From e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Thu, 23 Apr 2020 14:45:49 +0900 Subject: Imported Upstream version 1.4.0 --- .ctags | 2 +- .gitignore | 2 +- LICENSE | 426 +- Makefile.template | 4 +- README.md | 29 +- compiler/CMakeLists.txt | 78 + compiler/adtidas/CMakeLists.txt | 2 + compiler/adtidas/include/adtidas/SmallVector.h | 156 + compiler/angkor/CMakeLists.txt | 22 + compiler/angkor/README.md | 51 + compiler/angkor/include/angkor/TensorIndex.h | 29 + compiler/angkor/include/angkor/TensorShape.h | 29 + .../include/nncc/core/ADT/feature/Accessor.h | 43 + .../angkor/include/nncc/core/ADT/feature/Buffer.h | 59 + .../include/nncc/core/ADT/feature/CHWLayout.h | 41 + .../include/nncc/core/ADT/feature/HWCLayout.h | 41 + .../angkor/include/nncc/core/ADT/feature/Layout.h | 54 + .../angkor/include/nncc/core/ADT/feature/Overlay.h | 60 + .../angkor/include/nncc/core/ADT/feature/Reader.h | 43 + .../angkor/include/nncc/core/ADT/feature/Shape.h | 74 + .../angkor/include/nncc/core/ADT/feature/View.h | 71 + .../angkor/include/nncc/core/ADT/kernel/Accessor.h | 43 + .../angkor/include/nncc/core/ADT/kernel/Buffer.h | 72 + .../include/nncc/core/ADT/kernel/IndexEnumerator.h | 70 + .../angkor/include/nncc/core/ADT/kernel/Layout.h | 54 + .../include/nncc/core/ADT/kernel/NCHWLayout.h | 41 + .../include/nncc/core/ADT/kernel/NHWCLayout.h | 41 + .../angkor/include/nncc/core/ADT/kernel/Overlay.h | 81 + .../angkor/include/nncc/core/ADT/kernel/Reader.h | 43 + .../angkor/include/nncc/core/ADT/kernel/Shape.h | 73 + .../angkor/include/nncc/core/ADT/kernel/View.h | 43 + .../angkor/include/nncc/core/ADT/kernel/ViewImpl.h | 67 + .../angkor/include/nncc/core/ADT/tensor/Accessor.h | 43 + .../angkor/include/nncc/core/ADT/tensor/Buffer.h | 57 + .../angkor/include/nncc/core/ADT/tensor/Index.h | 65 + .../include/nncc/core/ADT/tensor/IndexEnumerator.h | 63 + .../angkor/include/nncc/core/ADT/tensor/Layout.h | 52 + .../include/nncc/core/ADT/tensor/LexicalLayout.h | 41 + .../angkor/include/nncc/core/ADT/tensor/Overlay.h | 58 + .../angkor/include/nncc/core/ADT/tensor/Reader.h | 43 + .../angkor/include/nncc/core/ADT/tensor/Shape.h | 70 + .../angkor/include/nncc/core/ADT/tensor/View.h | 70 + compiler/angkor/src/ADT/feature/Accessor.cpp | 21 + compiler/angkor/src/ADT/feature/Buffer.test.cpp | 48 + compiler/angkor/src/ADT/feature/CHWLayout.cpp | 43 + compiler/angkor/src/ADT/feature/CHWLayout.test.cpp | 45 + compiler/angkor/src/ADT/feature/HWCLayout.cpp | 43 + compiler/angkor/src/ADT/feature/HWCLayout.test.cpp | 57 + compiler/angkor/src/ADT/feature/Layout.cpp | 35 + compiler/angkor/src/ADT/feature/Layout.test.cpp | 56 + compiler/angkor/src/ADT/feature/Overlay.test.cpp | 72 + compiler/angkor/src/ADT/feature/Reader.cpp | 21 + compiler/angkor/src/ADT/feature/Shape.test.cpp | 56 + compiler/angkor/src/ADT/kernel/Buffer.test.cpp | 49 + compiler/angkor/src/ADT/kernel/IndexEnumerator.cpp | 84 + .../angkor/src/ADT/kernel/IndexEnumerator.test.cpp | 46 + compiler/angkor/src/ADT/kernel/Layout.cpp | 38 + compiler/angkor/src/ADT/kernel/Layout.test.cpp | 56 + compiler/angkor/src/ADT/kernel/NCHWLayout.cpp | 43 + compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp | 53 + compiler/angkor/src/ADT/kernel/NHWCLayout.cpp | 43 + compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp | 74 + compiler/angkor/src/ADT/kernel/Overlay.test.cpp | 73 + compiler/angkor/src/ADT/kernel/Reader.cpp | 20 + compiler/angkor/src/ADT/kernel/Shape.cpp | 37 + compiler/angkor/src/ADT/kernel/Shape.test.cpp | 58 + compiler/angkor/src/ADT/tensor/Buffer.test.cpp | 49 + compiler/angkor/src/ADT/tensor/Index.cpp | 81 + compiler/angkor/src/ADT/tensor/Index.test.cpp | 119 + compiler/angkor/src/ADT/tensor/IndexEnumerator.cpp | 100 + .../angkor/src/ADT/tensor/IndexEnumerator.test.cpp | 48 + compiler/angkor/src/ADT/tensor/Layout.cpp | 35 + compiler/angkor/src/ADT/tensor/Layout.test.cpp | 56 + compiler/angkor/src/ADT/tensor/LexicalLayout.cpp | 60 + .../angkor/src/ADT/tensor/LexicalLayout.test.cpp | 54 + compiler/angkor/src/ADT/tensor/Overlay.test.cpp | 75 + compiler/angkor/src/ADT/tensor/Reader.cpp | 21 + compiler/angkor/src/ADT/tensor/Shape.cpp | 91 + compiler/angkor/src/ADT/tensor/Shape.test.cpp | 185 + compiler/angkor/src/TensorIndex.test.cpp | 87 + compiler/angkor/src/TensorShape.test.cpp | 99 + compiler/ann-api/CMakeLists.txt | 2 + compiler/ann-api/include/.FORMATDENY | 0 compiler/ann-api/include/NeuralNetworks.h | 2075 ++ compiler/ann-ref/.FORMATDENY | 0 compiler/ann-ref/CMakeLists.txt | 32 + compiler/ann-ref/README.md | 7 + compiler/ann-ref/requires.cmake | 1 + compiler/ann-ref/src/Assert.h | 34 + compiler/ann-ref/src/CompilationBuilder.cpp | 52 + compiler/ann-ref/src/CompilationBuilder.h | 44 + compiler/ann-ref/src/ExecutionBuilder.cpp | 196 + compiler/ann-ref/src/ExecutionBuilder.h | 73 + compiler/ann-ref/src/Executor.cpp | 814 + compiler/ann-ref/src/Executor.h | 114 + compiler/ann-ref/src/Logging.cpp | 30 + compiler/ann-ref/src/Logging.h | 42 + compiler/ann-ref/src/Macro.h | 22 + compiler/ann-ref/src/Memory.cpp | 105 + compiler/ann-ref/src/Memory.h | 106 + compiler/ann-ref/src/MemoryTracker.cpp | 50 + compiler/ann-ref/src/MemoryTracker.h | 49 + compiler/ann-ref/src/Model.h | 39 + compiler/ann-ref/src/ModelArgumentInfo.cpp | 121 + compiler/ann-ref/src/ModelArgumentInfo.h | 58 + compiler/ann-ref/src/ModelBuilder.cpp | 483 + compiler/ann-ref/src/ModelBuilder.h | 142 + compiler/ann-ref/src/NeuralNetworks.cpp | 338 + compiler/ann-ref/src/Operand.h | 61 + compiler/ann-ref/src/OperandType.cpp | 55 + compiler/ann-ref/src/OperandType.h | 43 + compiler/ann-ref/src/OperandType.probe.cpp | 32 + compiler/ann-ref/src/Operation.h | 32 + compiler/ann-ref/src/OperationType.cpp | 67 + compiler/ann-ref/src/OperationType.h | 66 + compiler/ann-ref/src/OperationType.probe.cpp | 85 + compiler/ann-ref/src/Probe.cpp | 89 + compiler/ann-ref/src/Request.h | 35 + compiler/ann-ref/src/Shape.cpp | 68 + compiler/ann-ref/src/Shape.h | 47 + compiler/ann-ref/src/Validation.cpp | 263 + compiler/ann-ref/src/Validation.h | 34 + compiler/ann-ref/src/ops/Add.cpp | 57 + compiler/ann-ref/src/ops/Add.float.cpp | 122 + compiler/ann-ref/src/ops/Add.float.h | 28 + compiler/ann-ref/src/ops/Add.h | 25 + compiler/ann-ref/src/ops/AvgPool2D.cpp | 30 + compiler/ann-ref/src/ops/AvgPool2D.float.cpp | 123 + compiler/ann-ref/src/ops/AvgPool2D.float.h | 31 + compiler/ann-ref/src/ops/AvgPool2D.h | 30 + compiler/ann-ref/src/ops/Concatenation.cpp | 66 + compiler/ann-ref/src/ops/Concatenation.float.cpp | 84 + compiler/ann-ref/src/ops/Concatenation.float.h | 30 + compiler/ann-ref/src/ops/Concatenation.h | 28 + compiler/ann-ref/src/ops/Conv2D.cpp | 57 + compiler/ann-ref/src/ops/Conv2D.float.cpp | 256 + compiler/ann-ref/src/ops/Conv2D.float.h | 31 + compiler/ann-ref/src/ops/Conv2D.h | 29 + compiler/ann-ref/src/ops/DepthwiseConv2D.cpp | 57 + compiler/ann-ref/src/ops/DepthwiseConv2D.float.cpp | 311 + compiler/ann-ref/src/ops/DepthwiseConv2D.float.h | 32 + compiler/ann-ref/src/ops/DepthwiseConv2D.h | 30 + compiler/ann-ref/src/ops/Div.cpp | 57 + compiler/ann-ref/src/ops/Div.float.cpp | 122 + compiler/ann-ref/src/ops/Div.float.h | 28 + compiler/ann-ref/src/ops/Div.h | 25 + compiler/ann-ref/src/ops/FullyConnected.cpp | 70 + compiler/ann-ref/src/ops/FullyConnected.float.cpp | 65 + compiler/ann-ref/src/ops/FullyConnected.float.h | 29 + compiler/ann-ref/src/ops/FullyConnected.h | 28 + compiler/ann-ref/src/ops/MaxPool2D.cpp | 30 + compiler/ann-ref/src/ops/MaxPool2D.float.cpp | 118 + compiler/ann-ref/src/ops/MaxPool2D.float.h | 31 + compiler/ann-ref/src/ops/MaxPool2D.h | 30 + compiler/ann-ref/src/ops/Mul.cpp | 57 + compiler/ann-ref/src/ops/Mul.float.cpp | 122 + compiler/ann-ref/src/ops/Mul.float.h | 28 + compiler/ann-ref/src/ops/Mul.h | 25 + compiler/ann-ref/src/ops/Pad.cpp | 189 + compiler/ann-ref/src/ops/Pad.h | 31 + compiler/ann-ref/src/ops/ReLU.cpp | 25 + compiler/ann-ref/src/ops/ReLU.float.cpp | 31 + compiler/ann-ref/src/ops/ReLU.float.h | 26 + compiler/ann-ref/src/ops/ReLU.h | 25 + compiler/ann-ref/src/ops/ReLU6.cpp | 25 + compiler/ann-ref/src/ops/ReLU6.float.cpp | 31 + compiler/ann-ref/src/ops/ReLU6.float.h | 26 + compiler/ann-ref/src/ops/ReLU6.h | 25 + compiler/ann-ref/src/ops/Reshape.cpp | 73 + compiler/ann-ref/src/ops/Reshape.h | 31 + compiler/ann-ref/src/ops/Softmax.cpp | 25 + compiler/ann-ref/src/ops/Softmax.float.cpp | 71 + compiler/ann-ref/src/ops/Softmax.float.h | 28 + compiler/ann-ref/src/ops/Softmax.h | 27 + compiler/ann-ref/src/ops/Sub.cpp | 57 + compiler/ann-ref/src/ops/Sub.float.cpp | 122 + compiler/ann-ref/src/ops/Sub.float.h | 28 + compiler/ann-ref/src/ops/Sub.h | 25 + .../ann-ref/src/ops/internal/ActivationUtils.h | 59 + compiler/ann-ref/src/ops/internal/Array.h | 46 + compiler/ann-ref/src/ops/internal/Dims.h | 167 + compiler/ann-ref/src/ops/internal/Elementwise.cpp | 25 + compiler/ann-ref/src/ops/internal/Elementwise.h | 25 + compiler/ann-ref/src/ops/internal/FeatureMap.h | 26 + compiler/ann-ref/src/ops/internal/Fused.cpp | 28 + compiler/ann-ref/src/ops/internal/Fused.h | 84 + compiler/ann-ref/src/ops/internal/GEMM.h | 38 + compiler/ann-ref/src/ops/internal/Macro.h | 58 + compiler/ann-ref/src/ops/internal/Matrix.h | 127 + compiler/ann-ref/src/ops/internal/NDArray.h | 133 + compiler/ann-ref/src/ops/internal/Pooling.cpp | 43 + compiler/ann-ref/src/ops/internal/Pooling.h | 31 + compiler/ann-ref/src/ops/internal/Spatial.h | 29 + compiler/bino/CMakeLists.txt | 14 + compiler/bino/README.md | 5 + compiler/bino/include/bino.h | 57 + compiler/bino/tests/Functional.tests.cpp | 35 + compiler/caffe2circle/CMakeLists.txt | 16 + compiler/caffe2circle/README.md | 3 + compiler/caffe2circle/requires.cmake | 3 + compiler/caffe2circle/src/caffe2circle.cpp | 39 + compiler/caffegen/CMakeLists.txt | 14 + compiler/caffegen/README.md | 45 + compiler/caffegen/src/DecodeCommand.cpp | 46 + compiler/caffegen/src/DecodeCommand.h | 27 + compiler/caffegen/src/Driver.cpp | 42 + compiler/caffegen/src/EncodeCommand.cpp | 51 + compiler/caffegen/src/EncodeCommand.h | 27 + compiler/caffegen/src/InitCommand.cpp | 65 + compiler/caffegen/src/InitCommand.h | 27 + compiler/caffegen/src/MergeCommand.cpp | 58 + compiler/caffegen/src/MergeCommand.h | 33 + compiler/circle-inspect/CMakeLists.txt | 13 + compiler/circle-inspect/README.md | 22 + compiler/circle-inspect/driver/Driver.cpp | 96 + compiler/circle-inspect/requires.cmake | 3 + compiler/circle-inspect/src/Dump.cpp | 135 + compiler/circle-inspect/src/Dump.h | 56 + compiler/circle-inspect/src/Model.cpp | 143 + compiler/circle-inspect/src/Model.h | 43 + compiler/circle-inspect/src/Reader.cpp | 166 + compiler/circle-inspect/src/Reader.h | 91 + compiler/circle-verify/CMakeLists.txt | 12 + compiler/circle-verify/README.md | 23 + compiler/circle-verify/requires.cmake | 4 + compiler/circle-verify/src/Driver.cpp | 51 + compiler/circle-verify/src/Model.cpp | 90 + compiler/circle-verify/src/Model.h | 38 + compiler/circle-verify/src/VerifyFlatBuffers.cpp | 36 + compiler/circle-verify/src/VerifyFlatBuffers.h | 32 + compiler/circle2circle/CMakeLists.txt | 42 + compiler/circle2circle/README.md | 3 + compiler/circle2circle/include/CircleExpContract.h | 50 + compiler/circle2circle/include/Model.h | 43 + compiler/circle2circle/requires.cmake | 10 + compiler/circle2circle/src/Circle2Circle.cpp | 120 + compiler/circle2circle/src/Circle2Circle.test.cpp | 29 + compiler/circle2circle/src/CircleExpContract.cpp | 33 + compiler/circle2circle/src/Model.cpp | 78 + compiler/circle2circle/src/TestHelper.h | 55 + compiler/circledump/CMakeLists.txt | 14 + compiler/circledump/README.md | 71 + compiler/circledump/driver/Driver.cpp | 52 + compiler/circledump/include/circledump/Dump.h | 32 + compiler/circledump/include/circleread/Model.h | 43 + compiler/circledump/requires.cmake | 3 + compiler/circledump/src/Dump.cpp | 310 + compiler/circledump/src/Load.cpp | 133 + compiler/circledump/src/OpPrinter.cpp | 307 + compiler/circledump/src/OpPrinter.h | 61 + compiler/circledump/src/Read.cpp | 169 + compiler/circledump/src/Read.h | 101 + compiler/cli/CMakeLists.txt | 15 + compiler/cli/README.md | 13 + compiler/cli/include/cli/App.h | 50 + compiler/cli/include/cli/Command.h | 32 + compiler/cli/include/cli/FunctionCommand.h | 46 + compiler/cli/src/App.cpp | 74 + compiler/cli/src/App.test.cpp | 63 + compiler/coco/CMakeLists.txt | 2 + compiler/coco/README.md | 3 + compiler/coco/core/CMakeLists.txt | 25 + compiler/coco/core/include/coco/ADT/DLinkedList.h | 288 + compiler/coco/core/include/coco/ADT/PtrList.h | 54 + compiler/coco/core/include/coco/ADT/PtrManager.h | 67 + compiler/coco/core/include/coco/IR.h | 34 + compiler/coco/core/include/coco/IR/Arg.h | 80 + compiler/coco/core/include/coco/IR/Bag.h | 164 + compiler/coco/core/include/coco/IR/BagManager.h | 47 + compiler/coco/core/include/coco/IR/Block.forward.h | 27 + compiler/coco/core/include/coco/IR/Block.h | 78 + compiler/coco/core/include/coco/IR/BlockIndex.h | 63 + compiler/coco/core/include/coco/IR/BlockManager.h | 47 + compiler/coco/core/include/coco/IR/Def.forward.h | 27 + compiler/coco/core/include/coco/IR/Def.h | 52 + compiler/coco/core/include/coco/IR/Dep.forward.h | 27 + compiler/coco/core/include/coco/IR/Dep.h | 59 + compiler/coco/core/include/coco/IR/DepSet.h | 31 + compiler/coco/core/include/coco/IR/ElemID.h | 51 + compiler/coco/core/include/coco/IR/Entity.h | 51 + compiler/coco/core/include/coco/IR/EntityBuilder.h | 48 + compiler/coco/core/include/coco/IR/EntityManager.h | 67 + compiler/coco/core/include/coco/IR/FeatureLayout.h | 54 + .../coco/core/include/coco/IR/FeatureLayouts.h | 159 + .../core/include/coco/IR/FeatureObject.forward.h | 27 + compiler/coco/core/include/coco/IR/FeatureObject.h | 63 + compiler/coco/core/include/coco/IR/FeatureShape.h | 70 + compiler/coco/core/include/coco/IR/Input.forward.h | 27 + compiler/coco/core/include/coco/IR/Input.h | 44 + compiler/coco/core/include/coco/IR/InputList.h | 31 + compiler/coco/core/include/coco/IR/InputManager.h | 39 + compiler/coco/core/include/coco/IR/Instr.forward.h | 28 + compiler/coco/core/include/coco/IR/Instr.h | 161 + compiler/coco/core/include/coco/IR/Instr.lst | 9 + compiler/coco/core/include/coco/IR/InstrIndex.h | 63 + compiler/coco/core/include/coco/IR/InstrManager.h | 66 + compiler/coco/core/include/coco/IR/Instrs.h | 175 + compiler/coco/core/include/coco/IR/KernelLayout.h | 58 + compiler/coco/core/include/coco/IR/KernelLayouts.h | 117 + .../core/include/coco/IR/KernelObject.forward.h | 27 + compiler/coco/core/include/coco/IR/KernelObject.h | 65 + compiler/coco/core/include/coco/IR/Locatable.h | 37 + .../coco/core/include/coco/IR/Module.forward.h | 27 + compiler/coco/core/include/coco/IR/Module.h | 67 + .../coco/core/include/coco/IR/Object.forward.h | 27 + compiler/coco/core/include/coco/IR/Object.h | 144 + compiler/coco/core/include/coco/IR/ObjectManager.h | 53 + compiler/coco/core/include/coco/IR/ObjectSet.h | 31 + compiler/coco/core/include/coco/IR/Op.forward.h | 27 + compiler/coco/core/include/coco/IR/Op.h | 255 + compiler/coco/core/include/coco/IR/Op.lst | 19 + compiler/coco/core/include/coco/IR/OpManager.h | 63 + compiler/coco/core/include/coco/IR/Ops.h | 412 + .../coco/core/include/coco/IR/Output.forward.h | 27 + compiler/coco/core/include/coco/IR/Output.h | 44 + compiler/coco/core/include/coco/IR/OutputList.h | 31 + compiler/coco/core/include/coco/IR/OutputManager.h | 39 + compiler/coco/core/include/coco/IR/Padding2D.h | 65 + compiler/coco/core/include/coco/IR/Part.forward.h | 27 + compiler/coco/core/include/coco/IR/Part.h | 53 + compiler/coco/core/include/coco/IR/Read.forward.h | 27 + compiler/coco/core/include/coco/IR/Read.h | 55 + compiler/coco/core/include/coco/IR/ReadSet.h | 31 + compiler/coco/core/include/coco/IR/Step.forward.h | 27 + compiler/coco/core/include/coco/IR/Step.h | 54 + compiler/coco/core/include/coco/IR/Stride2D.h | 54 + .../coco/core/include/coco/IR/Update.forward.h | 27 + compiler/coco/core/include/coco/IR/Update.h | 51 + compiler/coco/core/include/coco/IR/UpdateSet.h | 31 + compiler/coco/core/include/coco/IR/Use.forward.h | 27 + compiler/coco/core/include/coco/IR/Use.h | 52 + compiler/coco/core/include/coco/IR/UseSet.h | 31 + compiler/coco/core/include/coco/IR/Window2D.h | 55 + compiler/coco/core/src/ADT/DLinkedList.test.cpp | 281 + compiler/coco/core/src/ADT/PtrList.cpp | 19 + compiler/coco/core/src/ADT/PtrList.test.cpp | 47 + compiler/coco/core/src/ADT/PtrManager.test.cpp | 99 + compiler/coco/core/src/IR.test.cpp | 303 + compiler/coco/core/src/IR/Arg.cpp | 78 + compiler/coco/core/src/IR/Arg.test.cpp | 100 + compiler/coco/core/src/IR/AvgPool2D.test.cpp | 113 + compiler/coco/core/src/IR/Bag.cpp | 147 + compiler/coco/core/src/IR/Bag.test.cpp | 30 + compiler/coco/core/src/IR/BagManager.cpp | 33 + compiler/coco/core/src/IR/BagManager.test.cpp | 38 + compiler/coco/core/src/IR/Block.cpp | 56 + compiler/coco/core/src/IR/Block.test.cpp | 28 + compiler/coco/core/src/IR/BlockIndex.cpp | 30 + compiler/coco/core/src/IR/BlockIndex.test.cpp | 50 + compiler/coco/core/src/IR/BlockManager.cpp | 41 + compiler/coco/core/src/IR/BlockManager.test.cpp | 60 + compiler/coco/core/src/IR/Consumer.mock.h | 33 + compiler/coco/core/src/IR/Conv2D.cpp | 75 + compiler/coco/core/src/IR/Conv2D.test.cpp | 154 + compiler/coco/core/src/IR/Def.cpp | 43 + compiler/coco/core/src/IR/Def.test.cpp | 82 + compiler/coco/core/src/IR/Dep.cpp | 53 + compiler/coco/core/src/IR/Dep.test.cpp | 73 + compiler/coco/core/src/IR/ElemID.cpp | 25 + compiler/coco/core/src/IR/ElemID.test.cpp | 62 + compiler/coco/core/src/IR/EntityManager.cpp | 20 + compiler/coco/core/src/IR/Eval.cpp | 28 + compiler/coco/core/src/IR/Eval.test.cpp | 60 + compiler/coco/core/src/IR/FeatureLayouts.cpp | 211 + compiler/coco/core/src/IR/FeatureLayouts.test.cpp | 66 + compiler/coco/core/src/IR/FeatureObject.cpp | 31 + compiler/coco/core/src/IR/FeatureObject.test.cpp | 122 + compiler/coco/core/src/IR/FeatureShape.test.cpp | 29 + compiler/coco/core/src/IR/Input.cpp | 41 + compiler/coco/core/src/IR/Input.test.cpp | 79 + compiler/coco/core/src/IR/InputManager.cpp | 31 + compiler/coco/core/src/IR/InputManager.test.cpp | 29 + compiler/coco/core/src/IR/Instr.cpp | 56 + compiler/coco/core/src/IR/InstrIndex.cpp | 30 + compiler/coco/core/src/IR/InstrIndex.test.cpp | 50 + compiler/coco/core/src/IR/InstrManager.cpp | 33 + compiler/coco/core/src/IR/InstrManager.test.cpp | 52 + compiler/coco/core/src/IR/KernelLayouts.cpp | 155 + compiler/coco/core/src/IR/KernelLayouts.test.cpp | 126 + compiler/coco/core/src/IR/KernelObject.cpp | 42 + compiler/coco/core/src/IR/KernelObject.test.cpp | 78 + compiler/coco/core/src/IR/Load.cpp | 53 + compiler/coco/core/src/IR/MaxPool2D.test.cpp | 101 + compiler/coco/core/src/IR/Module.cpp | 150 + compiler/coco/core/src/IR/Module.test.cpp | 196 + compiler/coco/core/src/IR/Object.cpp | 116 + compiler/coco/core/src/IR/Object.test.cpp | 110 + compiler/coco/core/src/IR/ObjectManager.cpp | 52 + compiler/coco/core/src/IR/ObjectManager.test.cpp | 57 + compiler/coco/core/src/IR/Op.cpp | 153 + compiler/coco/core/src/IR/OpManager.cpp | 99 + compiler/coco/core/src/IR/OpManager.test.cpp | 120 + compiler/coco/core/src/IR/Ops.cpp | 22 + compiler/coco/core/src/IR/Ops.test.cpp | 129 + compiler/coco/core/src/IR/Output.cpp | 41 + compiler/coco/core/src/IR/Output.test.cpp | 83 + compiler/coco/core/src/IR/OutputManager.cpp | 31 + compiler/coco/core/src/IR/OutputManager.test.cpp | 29 + compiler/coco/core/src/IR/PadF.test.cpp | 89 + compiler/coco/core/src/IR/Padding2D.cpp | 46 + compiler/coco/core/src/IR/Padding2D.test.cpp | 51 + compiler/coco/core/src/IR/Part.cpp | 45 + compiler/coco/core/src/IR/Part.test.cpp | 70 + compiler/coco/core/src/IR/Producer.mock.h | 33 + compiler/coco/core/src/IR/ReLU.test.cpp | 85 + compiler/coco/core/src/IR/ReLU6.test.cpp | 85 + compiler/coco/core/src/IR/Read.cpp | 49 + compiler/coco/core/src/IR/Read.test.cpp | 81 + compiler/coco/core/src/IR/Reader.mock.h | 33 + compiler/coco/core/src/IR/Shuffle.cpp | 41 + compiler/coco/core/src/IR/Shuffle.test.cpp | 95 + compiler/coco/core/src/IR/Sqrt.test.cpp | 85 + compiler/coco/core/src/IR/Step.cpp | 52 + compiler/coco/core/src/IR/Stride2D.cpp | 34 + compiler/coco/core/src/IR/Stride2D.test.cpp | 45 + compiler/coco/core/src/IR/Sub.test.cpp | 87 + compiler/coco/core/src/IR/Update.cpp | 49 + compiler/coco/core/src/IR/Update.test.cpp | 81 + compiler/coco/core/src/IR/Updater.mock.h | 33 + compiler/coco/core/src/IR/Use.cpp | 43 + compiler/coco/core/src/IR/Use.test.cpp | 86 + compiler/coco/core/src/IR/Window2D.test.cpp | 46 + compiler/coco/generic/CMakeLists.txt | 22 + compiler/coco/generic/include/coco/ADT/Span.h | 67 + compiler/coco/generic/include/coco/IR/Data.h | 54 + .../generic/include/coco/IR/PlainWeightContext.h | 66 + compiler/coco/generic/src/ADT/Span.test.cpp | 60 + compiler/coco/generic/src/IR/Data.cpp | 217 + compiler/coco/generic/src/IR/Data.test.cpp | 64 + compiler/coco/requires.cmake | 1 + compiler/cwrap/CMakeLists.txt | 22 + compiler/cwrap/README.md | 23 + compiler/cwrap/include/cwrap/Fildes.h | 57 + compiler/cwrap/src/Fildes.cpp | 100 + compiler/cwrap/src/Fildes.test.cpp | 97 + compiler/dredd-rule-lib/CMakeLists.txt | 21 + compiler/dredd-rule-lib/README.md | 112 + compiler/dredd-rule-lib/rule-lib.sh | 203 + compiler/enco-intf/CMakeLists.txt | 2 + compiler/enco-intf/cmdline/CMakeLists.txt | 2 + compiler/enco-intf/cmdline/include/cmdline/View.h | 35 + compiler/enco-intf/frontend/CMakeLists.txt | 4 + compiler/enco-intf/frontend/include/enco/Bundle.h | 48 + .../enco-intf/frontend/include/enco/Frontend.h | 34 + compiler/enco/CMakeLists.txt | 4 + compiler/enco/README.md | 25 + compiler/enco/cli/CMakeLists.txt | 11 + compiler/enco/cli/src/Driver.cpp | 221 + compiler/enco/core/CMakeLists.txt | 35 + compiler/enco/core/include/enco/Backend.h | 41 + compiler/enco/core/src/ANN/Binder.h | 219 + compiler/enco/core/src/ANN/Context.cpp | 31 + compiler/enco/core/src/ANN/Context.h | 57 + compiler/enco/core/src/ANN/Context.test.cpp | 73 + compiler/enco/core/src/ANN/IR/DType.cpp | 25 + compiler/enco/core/src/ANN/IR/DType.h | 36 + compiler/enco/core/src/ANN/IR/DType.test.cpp | 25 + compiler/enco/core/src/ANN/IR/InputList.h | 31 + compiler/enco/core/src/ANN/IR/Module.h | 60 + compiler/enco/core/src/ANN/IR/Module.test.cpp | 36 + compiler/enco/core/src/ANN/IR/Operand.h | 82 + compiler/enco/core/src/ANN/IR/Operand.test.cpp | 37 + compiler/enco/core/src/ANN/IR/OperandID.h | 48 + compiler/enco/core/src/ANN/IR/OperandID.test.cpp | 33 + compiler/enco/core/src/ANN/IR/OperandInventory.cpp | 57 + compiler/enco/core/src/ANN/IR/OperandInventory.h | 56 + .../enco/core/src/ANN/IR/OperandInventory.test.cpp | 30 + compiler/enco/core/src/ANN/IR/Operation.def | 17 + compiler/enco/core/src/ANN/IR/Operation.h | 59 + compiler/enco/core/src/ANN/IR/Operation.test.cpp | 28 + .../enco/core/src/ANN/IR/OperationInventory.cpp | 32 + compiler/enco/core/src/ANN/IR/OperationInventory.h | 48 + .../core/src/ANN/IR/OperationInventory.test.cpp | 40 + compiler/enco/core/src/ANN/IR/OutputList.h | 31 + compiler/enco/core/src/ANN/IR/Weight.h | 70 + compiler/enco/core/src/ANN/IR/Weight.test.cpp | 53 + compiler/enco/core/src/ANN/IR/WeightInventory.cpp | 34 + compiler/enco/core/src/ANN/IR/WeightInventory.h | 38 + .../enco/core/src/ANN/IR/WeightInventory.test.cpp | 29 + compiler/enco/core/src/AsmCode.cpp | 33 + compiler/enco/core/src/AsmCode.h | 51 + compiler/enco/core/src/Backend.cpp | 178 + compiler/enco/core/src/Code.h | 47 + compiler/enco/core/src/Code.test.cpp | 30 + compiler/enco/core/src/CodeIndex.h | 76 + compiler/enco/core/src/CppCode.cpp | 553 + compiler/enco/core/src/CppCode.h | 51 + compiler/enco/core/src/CppGen/Host.cpp | 306 + compiler/enco/core/src/CppGen/Host.h | 48 + compiler/enco/core/src/CppGen/MemoryContext.cpp | 40 + compiler/enco/core/src/CppGen/MemoryContext.h | 55 + compiler/enco/core/src/CppGen/Subnet.cpp | 422 + compiler/enco/core/src/CppGen/Subnet.h | 91 + compiler/enco/core/src/Dims.h | 34 + compiler/enco/core/src/IRUtils.cpp | 65 + compiler/enco/core/src/IRUtils.h | 41 + compiler/enco/core/src/IRValidator.cpp | 85 + compiler/enco/core/src/IRValidator.h | 29 + compiler/enco/core/src/IRValidator.test.cpp | 130 + compiler/enco/core/src/Pass.h | 78 + compiler/enco/core/src/Pass.test.cpp | 41 + compiler/enco/core/src/Pipeline.h | 46 + compiler/enco/core/src/Pipeline.test.cpp | 26 + compiler/enco/core/src/Session.cpp | 58 + compiler/enco/core/src/Session.h | 45 + compiler/enco/core/src/String.h | 57 + compiler/enco/core/src/Support/Debugging.cpp | 533 + compiler/enco/core/src/Support/Debugging.h | 110 + compiler/enco/core/src/Support/Debugging.test.cpp | 26 + .../enco/core/src/Transforms/AvgPoolLowering.cpp | 229 + .../enco/core/src/Transforms/AvgPoolLowering.h | 43 + .../enco/core/src/Transforms/ConcatLowering.cpp | 196 + compiler/enco/core/src/Transforms/ConcatLowering.h | 43 + .../enco/core/src/Transforms/ConstantFolding.cpp | 442 + .../enco/core/src/Transforms/ConstantFolding.h | 43 + .../core/src/Transforms/ConstantFolding.test.cpp | 327 + compiler/enco/core/src/Transforms/CopyLowering.cpp | 105 + compiler/enco/core/src/Transforms/CopyLowering.h | 43 + .../core/src/Transforms/DataLayoutConversion.cpp | 383 + .../core/src/Transforms/DataLayoutConversion.h | 43 + .../src/Transforms/DataLayoutConversion.test.cpp | 33 + .../core/src/Transforms/DeadBagElimination.cpp | 72 + .../enco/core/src/Transforms/DeadBagElimination.h | 48 + .../core/src/Transforms/DeadObjectElimination.cpp | 77 + .../core/src/Transforms/DeadObjectElimination.h | 47 + compiler/enco/core/src/Transforms/Duplicate.cpp | 135 + compiler/enco/core/src/Transforms/Duplicate.h | 43 + .../src/Transforms/DuplicatedObjectReduction.cpp | 119 + .../src/Transforms/DuplicatedObjectReduction.h | 73 + .../core/src/Transforms/FeatureUnification.cpp | 216 + .../enco/core/src/Transforms/FeatureUnification.h | 68 + .../core/src/Transforms/FreeInstrElimination.cpp | 65 + .../core/src/Transforms/FreeInstrElimination.h | 54 + .../src/Transforms/FreeInstrElimination.test.cpp | 34 + .../enco/core/src/Transforms/FreeOpElimination.cpp | 59 + .../enco/core/src/Transforms/FreeOpElimination.h | 54 + .../core/src/Transforms/FreeOpElimination.test.cpp | 34 + .../core/src/Transforms/GlobalDataGeneration.cpp | 181 + .../core/src/Transforms/GlobalDataGeneration.h | 54 + .../src/Transforms/IdenticalObjectReduction.cpp | 139 + .../core/src/Transforms/IdenticalObjectReduction.h | 69 + .../Transforms/IdenticalObjectReduction.test.cpp | 32 + .../src/Transforms/IndirectCopyElimination.cpp | 84 + .../core/src/Transforms/IndirectCopyElimination.h | 60 + .../core/src/Transforms/IntrinsicSelection.cpp | 100 + .../enco/core/src/Transforms/IntrinsicSelection.h | 47 + .../enco/core/src/Transforms/Optimizations.cpp | 257 + compiler/enco/core/src/Transforms/Optimizations.h | 123 + compiler/enco/core/src/Transforms/Split.cpp | 1233 + compiler/enco/core/src/Transforms/Split.h | 48 + compiler/enco/core/src/Usage.cpp | 58 + compiler/enco/core/src/Usage.h | 34 + compiler/enco/core/src/coex/IR.h | 109 + compiler/enco/core/src/coex/IR.test.cpp | 38 + compiler/enco/frontend/CMakeLists.txt | 1 + compiler/enco/frontend/caffe/CMakeLists.txt | 39 + compiler/enco/frontend/caffe/src/ConcatSpec.cpp | 40 + compiler/enco/frontend/caffe/src/ConcatSpec.h | 47 + .../enco/frontend/caffe/src/ConcatSpec.test.cpp | 42 + compiler/enco/frontend/caffe/src/Context.cpp | 21 + compiler/enco/frontend/caffe/src/Context.h | 112 + compiler/enco/frontend/caffe/src/Convert.cpp | 40 + compiler/enco/frontend/caffe/src/Convert.h | 36 + .../enco/frontend/caffe/src/ConvolutionSpec.cpp | 147 + compiler/enco/frontend/caffe/src/ConvolutionSpec.h | 59 + .../frontend/caffe/src/ConvolutionSpec.test.cpp | 405 + compiler/enco/frontend/caffe/src/Entry.cpp | 62 + compiler/enco/frontend/caffe/src/Frontend.cpp | 135 + compiler/enco/frontend/caffe/src/Frontend.h | 43 + compiler/enco/frontend/caffe/src/GraphBuilder.cpp | 21 + compiler/enco/frontend/caffe/src/GraphBuilder.h | 36 + .../frontend/caffe/src/GraphBuilderRegistry.cpp | 47 + .../enco/frontend/caffe/src/GraphBuilderRegistry.h | 54 + compiler/enco/frontend/caffe/src/IRBuilder.h | 180 + compiler/enco/frontend/caffe/src/Importer.cpp | 52 + compiler/enco/frontend/caffe/src/Importer.h | 29 + .../enco/frontend/caffe/src/Layer/BatchNorm.cpp | 254 + compiler/enco/frontend/caffe/src/Layer/BatchNorm.h | 35 + .../frontend/caffe/src/Layer/Concatenation.cpp | 138 + .../enco/frontend/caffe/src/Layer/Concatenation.h | 35 + .../enco/frontend/caffe/src/Layer/Convolution.cpp | 197 + .../enco/frontend/caffe/src/Layer/Convolution.h | 35 + compiler/enco/frontend/caffe/src/Layer/Eltwise.cpp | 134 + compiler/enco/frontend/caffe/src/Layer/Eltwise.h | 35 + compiler/enco/frontend/caffe/src/Layer/Input.cpp | 60 + compiler/enco/frontend/caffe/src/Layer/Input.h | 35 + compiler/enco/frontend/caffe/src/Layer/Pooling.cpp | 138 + compiler/enco/frontend/caffe/src/Layer/Pooling.h | 35 + compiler/enco/frontend/caffe/src/Layer/ReLU.cpp | 83 + compiler/enco/frontend/caffe/src/Layer/ReLU.h | 35 + compiler/enco/frontend/caffe/src/Layer/Scale.cpp | 160 + compiler/enco/frontend/caffe/src/Layer/Scale.h | 35 + compiler/enco/frontend/caffe/src/Padding.h | 69 + compiler/enco/frontend/caffe/src/Padding.test.cpp | 48 + compiler/enco/frontend/caffe/src/PaddingUtils.cpp | 131 + compiler/enco/frontend/caffe/src/PaddingUtils.h | 81 + compiler/enco/frontend/caffe/src/PoolingSpec.cpp | 148 + compiler/enco/frontend/caffe/src/PoolingSpec.h | 62 + .../enco/frontend/caffe/src/PoolingSpec.test.cpp | 294 + compiler/enco/frontend/caffe/src/ShapeQuery.cpp | 40 + compiler/enco/frontend/caffe/src/ShapeQuery.h | 75 + compiler/enco/frontend/tflite/CMakeLists.txt | 36 + compiler/enco/frontend/tflite/schema/schema.fbs | 734 + compiler/enco/frontend/tflite/schema/schema.meta | 2 + compiler/enco/frontend/tflite/src/Context.cpp | 116 + compiler/enco/frontend/tflite/src/Context.h | 169 + compiler/enco/frontend/tflite/src/Convert.cpp | 57 + compiler/enco/frontend/tflite/src/Convert.h | 43 + compiler/enco/frontend/tflite/src/Entry.cpp | 36 + compiler/enco/frontend/tflite/src/Frontend.cpp | 198 + compiler/enco/frontend/tflite/src/Frontend.h | 40 + .../enco/frontend/tflite/src/Frontend.test.cpp | 41 + compiler/enco/frontend/tflite/src/GraphBuilder.h | 46 + .../frontend/tflite/src/GraphBuilderRegistry.h | 88 + compiler/enco/frontend/tflite/src/IRBuilder.h | 178 + .../enco/frontend/tflite/src/Op/Activation.cpp | 96 + compiler/enco/frontend/tflite/src/Op/Activation.h | 37 + .../enco/frontend/tflite/src/Op/AveragePool2D.cpp | 126 + .../enco/frontend/tflite/src/Op/AveragePool2D.h | 39 + .../enco/frontend/tflite/src/Op/Concatenation.cpp | 252 + .../enco/frontend/tflite/src/Op/Concatenation.h | 38 + compiler/enco/frontend/tflite/src/Op/Conv2D.cpp | 181 + compiler/enco/frontend/tflite/src/Op/Conv2D.h | 39 + .../frontend/tflite/src/Op/DepthwiseConv2D.cpp | 230 + .../enco/frontend/tflite/src/Op/DepthwiseConv2D.h | 39 + compiler/enco/frontend/tflite/src/Op/Div.cpp | 116 + compiler/enco/frontend/tflite/src/Op/Div.h | 38 + compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp | 123 + compiler/enco/frontend/tflite/src/Op/MaxPool2D.h | 39 + compiler/enco/frontend/tflite/src/Op/Padding.cpp | 105 + compiler/enco/frontend/tflite/src/Op/Padding.h | 42 + compiler/enco/frontend/tflite/src/Op/ReLU.cpp | 89 + compiler/enco/frontend/tflite/src/Op/ReLU.h | 38 + compiler/enco/frontend/tflite/src/Op/ReLU6.cpp | 89 + compiler/enco/frontend/tflite/src/Op/ReLU6.h | 38 + compiler/enco/frontend/tflite/src/Op/Reshape.cpp | 89 + compiler/enco/frontend/tflite/src/Op/Reshape.h | 38 + compiler/enco/frontend/tflite/src/Op/Sub.cpp | 112 + compiler/enco/frontend/tflite/src/Op/Sub.h | 38 + compiler/enco/frontend/tflite/src/RawModel.h | 29 + .../enco/frontend/tflite/src/RawModelLoader.cpp | 89 + compiler/enco/frontend/tflite/src/RawModelLoader.h | 29 + compiler/enco/frontend/tflite/src/TensorBags.h | 65 + compiler/enco/requires.cmake | 8 + compiler/enco/test/CMakeLists.txt | 1 + compiler/enco/test/basic/000/CMakeLists.txt | 26 + compiler/enco/test/basic/000/enco.test.cpp | 81 + compiler/enco/test/basic/CMakeLists.txt | 1 + compiler/enco/test/binder.cpp | 188 + compiler/enco/test/caffe/CMakeLists.txt | 141 + compiler/enco/test/caffe/runall.sh | 85 + .../enco/test/tflite/AveragePool2D_000/INFERENCE | 0 .../enco/test/tflite/AveragePool2D_000/test.recipe | 24 + .../enco/test/tflite/AveragePool2D_001/INFERENCE | 0 .../enco/test/tflite/AveragePool2D_001/test.recipe | 24 + compiler/enco/test/tflite/CMakeLists.txt | 108 + compiler/enco/test/tflite/Concat_000/INFERENCE | 0 compiler/enco/test/tflite/Concat_000/test.recipe | 28 + compiler/enco/test/tflite/Concat_001/INFERENCE | 0 compiler/enco/test/tflite/Concat_001/test.recipe | 29 + compiler/enco/test/tflite/Concat_002/INFERENCE | 0 compiler/enco/test/tflite/Concat_002/test.recipe | 29 + compiler/enco/test/tflite/Concat_003/INFERENCE | 0 compiler/enco/test/tflite/Concat_003/test.recipe | 29 + compiler/enco/test/tflite/Conv2D_000/INFERENCE | 0 compiler/enco/test/tflite/Conv2D_000/test.recipe | 45 + compiler/enco/test/tflite/Conv2D_001/INFERENCE | 0 compiler/enco/test/tflite/Conv2D_001/test.recipe | 45 + compiler/enco/test/tflite/Conv2D_002/INFERENCE | 0 compiler/enco/test/tflite/Conv2D_002/test.recipe | 46 + compiler/enco/test/tflite/Conv2D_003/INFERENCE | 0 compiler/enco/test/tflite/Conv2D_003/test.recipe | 45 + compiler/enco/test/tflite/Conv2D_004/INFERENCE | 0 compiler/enco/test/tflite/Conv2D_004/test.recipe | 45 + .../enco/test/tflite/DepthwiseConv2D_000/INFERENCE | 0 .../test/tflite/DepthwiseConv2D_000/test.recipe | 48 + .../enco/test/tflite/DepthwiseConv2D_001/INFERENCE | 0 .../test/tflite/DepthwiseConv2D_001/test.recipe | 46 + compiler/enco/test/tflite/Div_000/INFERENCE | 0 compiler/enco/test/tflite/Div_000/test.recipe | 27 + compiler/enco/test/tflite/MaxPool2D_000/INFERENCE | 0 .../enco/test/tflite/MaxPool2D_000/test.recipe | 24 + compiler/enco/test/tflite/ReLU6_000/INFERENCE | 0 compiler/enco/test/tflite/ReLU6_000/test.recipe | 17 + compiler/enco/test/tflite/ReLU_000/INFERENCE | 0 compiler/enco/test/tflite/ReLU_000/test.recipe | 17 + .../enco/test/tflite/Regression_0000/INFERENCE | 0 .../enco/test/tflite/Regression_0000/test.recipe | 84 + .../enco/test/tflite/Regression_0001/INFERENCE | 0 .../enco/test/tflite/Regression_0001/test.recipe | 50 + .../enco/test/tflite/Regression_0002/INFERENCE | 0 .../enco/test/tflite/Regression_0002/test.recipe | 45 + .../enco/test/tflite/Regression_0003/INFERENCE | 0 .../enco/test/tflite/Regression_0003/test.recipe | 33 + .../enco/test/tflite/Regression_0004/INFERENCE | 0 .../enco/test/tflite/Regression_0004/test.recipe | 27 + compiler/enco/test/tflite/Reshape_000/INFERENCE | 0 compiler/enco/test/tflite/Reshape_000/test.recipe | 21 + compiler/enco/test/tflite/Sub_000/INFERENCE | 0 compiler/enco/test/tflite/Sub_000/test.recipe | 27 + compiler/enco/test/tflite/empty/INFERENCE | 0 compiler/enco/test/tflite/empty/test.recipe | 0 compiler/enco/test/tflite/runall.sh | 83 + compiler/encodump/CMakeLists.txt | 17 + compiler/encodump/README.md | 69 + compiler/encodump/requires.cmake | 1 + compiler/encodump/src/Driver.cpp | 207 + compiler/encodump/src/Dump.cpp | 371 + compiler/encodump/src/Dump.h | 24 + compiler/exo/CMakeLists.txt | 73 + compiler/exo/README.md | 12 + compiler/exo/include/exo/CircleExporter.h | 62 + compiler/exo/include/exo/LoggingContext.h | 35 + compiler/exo/include/exo/TFLExporter.h | 62 + compiler/exo/requires.cmake | 6 + compiler/exo/src/Check.h | 37 + compiler/exo/src/Circle/CircleExporter.cpp | 49 + compiler/exo/src/Circle/CircleExporterImpl.cpp | 181 + compiler/exo/src/Circle/CircleExporterImpl.h | 78 + compiler/exo/src/Circle/CircleExporterUtils.cpp | 163 + compiler/exo/src/Circle/CircleExporterUtils.h | 120 + .../exo/src/Circle/CircleOperationExporter.cpp | 1228 + compiler/exo/src/Circle/CircleOperationExporter.h | 39 + compiler/exo/src/Circle/CircleTensorExporter.cpp | 261 + compiler/exo/src/Circle/CircleTensorExporter.h | 42 + compiler/exo/src/Circle/CircleTypeInference.cpp | 85 + compiler/exo/src/Circle/CircleTypeInference.h | 45 + compiler/exo/src/Conversion/AvgPool2DConverter.cpp | 79 + compiler/exo/src/Conversion/AvgPool2DConverter.h | 41 + .../exo/src/Conversion/CanonicalNodeConverter.cpp | 19 + .../exo/src/Conversion/CanonicalNodeConverter.h | 71 + compiler/exo/src/Conversion/ConstGenConverter.cpp | 60 + compiler/exo/src/Conversion/ConstGenConverter.h | 38 + .../exo/src/Conversion/ConstGenConverter.test.cpp | 65 + compiler/exo/src/Conversion/Conv2DConverter.cpp | 97 + compiler/exo/src/Conversion/Conv2DConverter.h | 41 + .../src/Conversion/DepthwiseConv2DConverter.cpp | 114 + .../exo/src/Conversion/DepthwiseConv2DConverter.h | 61 + .../exo/src/Conversion/EltwiseAddConverter.cpp | 29 + compiler/exo/src/Conversion/EltwiseAddConverter.h | 41 + .../exo/src/Conversion/EltwiseBinaryConverter.h | 110 + .../exo/src/Conversion/EltwiseDivConverter.cpp | 29 + compiler/exo/src/Conversion/EltwiseDivConverter.h | 41 + .../exo/src/Conversion/EltwiseMaxConverter.cpp | 75 + compiler/exo/src/Conversion/EltwiseMaxConverter.h | 41 + .../exo/src/Conversion/EltwiseMulConverter.cpp | 29 + compiler/exo/src/Conversion/EltwiseMulConverter.h | 41 + .../exo/src/Conversion/EltwiseSqrtConverter.cpp | 68 + compiler/exo/src/Conversion/EltwiseSqrtConverter.h | 41 + .../exo/src/Conversion/EltwiseSubConverter.cpp | 29 + compiler/exo/src/Conversion/EltwiseSubConverter.h | 41 + .../exo/src/Conversion/FeatureBiasAddConverter.cpp | 91 + .../exo/src/Conversion/FeatureBiasAddConverter.h | 38 + .../Conversion/FeatureBiasAddConverter.test.cpp | 102 + compiler/exo/src/Conversion/MatMulConverter.cpp | 103 + compiler/exo/src/Conversion/MatMulConverter.h | 41 + compiler/exo/src/Conversion/MaxPool2DConverter.cpp | 67 + compiler/exo/src/Conversion/MaxPool2DConverter.h | 41 + compiler/exo/src/Conversion/Relu6Converter.cpp | 68 + compiler/exo/src/Conversion/Relu6Converter.h | 41 + compiler/exo/src/Conversion/ReluConverter.cpp | 68 + compiler/exo/src/Conversion/ReluConverter.h | 41 + compiler/exo/src/Conversion/ReluConverter.test.cpp | 97 + .../src/Conversion/TensorBroadcastConverter.cpp | 189 + .../exo/src/Conversion/TensorBroadcastConverter.h | 40 + .../exo/src/Conversion/TensorConcatConverter.cpp | 66 + .../exo/src/Conversion/TensorConcatConverter.h | 41 + .../exo/src/Conversion/TensorReduceConverter.cpp | 95 + .../exo/src/Conversion/TensorReduceConverter.h | 46 + .../src/Conversion/TensorTransposeConverter.cpp | 102 + .../exo/src/Conversion/TensorTransposeConverter.h | 41 + .../src/Conversion/TransposedConv2DConverter.cpp | 92 + .../exo/src/Conversion/TransposedConv2DConverter.h | 62 + compiler/exo/src/Conversions.h | 46 + compiler/exo/src/Convert.cpp | 97 + compiler/exo/src/Convert.h | 29 + compiler/exo/src/Dialect/IR/CircleDialect.cpp | 28 + compiler/exo/src/Dialect/IR/CircleDialect.h | 40 + compiler/exo/src/Dialect/IR/CircleDialect.test.cpp | 31 + compiler/exo/src/Dialect/IR/CircleNode.cpp | 26 + compiler/exo/src/Dialect/IR/CircleNode.h | 23 + compiler/exo/src/Dialect/IR/CircleNodeDecl.h | 50 + compiler/exo/src/Dialect/IR/CircleNodeImpl.h | 70 + .../exo/src/Dialect/IR/CircleNodeVisitor.forward.h | 30 + compiler/exo/src/Dialect/IR/CircleNodeVisitor.h | 86 + compiler/exo/src/Dialect/IR/CircleNodes.cpp | 18 + compiler/exo/src/Dialect/IR/CircleNodes.h | 79 + compiler/exo/src/Dialect/IR/CircleNodes.lst | 8 + compiler/exo/src/Dialect/IR/CircleNodes.test.cpp | 36 + compiler/exo/src/Dialect/IR/CircleOpcode.h | 32 + compiler/exo/src/Dialect/IR/FusedActFunc.h | 35 + compiler/exo/src/Dialect/IR/NodeMixins.cpp | 18 + compiler/exo/src/Dialect/IR/NodeMixins.h | 66 + compiler/exo/src/Dialect/IR/TFLDialect.cpp | 28 + compiler/exo/src/Dialect/IR/TFLDialect.h | 40 + compiler/exo/src/Dialect/IR/TFLDialect.test.cpp | 31 + compiler/exo/src/Dialect/IR/TFLNode.cpp | 26 + compiler/exo/src/Dialect/IR/TFLNode.h | 23 + compiler/exo/src/Dialect/IR/TFLNodeDecl.h | 50 + compiler/exo/src/Dialect/IR/TFLNodeImpl.h | 70 + .../exo/src/Dialect/IR/TFLNodeVisitor.forward.h | 30 + compiler/exo/src/Dialect/IR/TFLNodeVisitor.h | 86 + compiler/exo/src/Dialect/IR/TFLNodes.cpp | 91 + compiler/exo/src/Dialect/IR/TFLNodes.h | 551 + compiler/exo/src/Dialect/IR/TFLNodes.lst | 30 + compiler/exo/src/Dialect/IR/TFLNodes.test.cpp | 159 + compiler/exo/src/Dialect/IR/TFLOpcode.h | 32 + .../Dialect/Service/CircleShapeInferenceRule.cpp | 67 + .../src/Dialect/Service/CircleShapeInferenceRule.h | 33 + .../Dialect/Service/CircleTypeInferenceRule.cpp | 58 + .../src/Dialect/Service/CircleTypeInferenceRule.h | 36 + .../src/Dialect/Service/TFLShapeInferenceRule.cpp | 627 + .../src/Dialect/Service/TFLShapeInferenceRule.h | 33 + .../Dialect/Service/TFLShapeInferenceRule.test.cpp | 277 + .../src/Dialect/Service/TFLTypeInferenceRule.cpp | 141 + .../exo/src/Dialect/Service/TFLTypeInferenceRule.h | 37 + .../Dialect/Service/TFLTypeInferenceRule.test.cpp | 57 + compiler/exo/src/ExoFormattedGraph.cpp | 525 + compiler/exo/src/ExoFormattedGraph.h | 56 + compiler/exo/src/ExoOptimize.cpp | 74 + compiler/exo/src/ExoOptimize.h | 34 + compiler/exo/src/ExporterUtils.cpp | 139 + compiler/exo/src/ExporterUtils.h | 57 + compiler/exo/src/GraphBlock.cpp | 243 + compiler/exo/src/GraphBlock.h | 199 + compiler/exo/src/Knob.cpp | 122 + compiler/exo/src/Knob.h | 51 + compiler/exo/src/Knob.lst | 11 + compiler/exo/src/Log.cpp | 84 + compiler/exo/src/Log.h | 75 + compiler/exo/src/LogHelper.cpp | 79 + compiler/exo/src/LogHelper.h | 70 + compiler/exo/src/LoggingContext.cpp | 40 + compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp | 116 + compiler/exo/src/Pass/FoldReshapeOfConstPass.h | 46 + compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp | 154 + compiler/exo/src/Pass/FoldTransposeOfConstPass.h | 46 + compiler/exo/src/Pass/FuseBiasAddPass.cpp | 362 + compiler/exo/src/Pass/FuseBiasAddPass.h | 61 + compiler/exo/src/Pass/FuseBiasAddPass.test.cpp | 361 + compiler/exo/src/Pass/FuseInstanceNormPass.cpp | 402 + compiler/exo/src/Pass/FuseInstanceNormPass.h | 40 + compiler/exo/src/Pass/FuseReluPass.cpp | 115 + compiler/exo/src/Pass/FuseReluPass.h | 40 + compiler/exo/src/Pass/FuseReluPass.test.cpp | 115 + compiler/exo/src/Pass/FuseRsqrtPass.cpp | 95 + compiler/exo/src/Pass/FuseRsqrtPass.h | 47 + .../exo/src/Pass/FuseSquaredDifferencePass.cpp | 86 + compiler/exo/src/Pass/FuseSquaredDifferencePass.h | 49 + compiler/exo/src/Pass/MergeConcatNodesPass.cpp | 191 + compiler/exo/src/Pass/MergeConcatNodesPass.h | 41 + compiler/exo/src/Pass/ShapeInferencePass.cpp | 59 + compiler/exo/src/Pass/ShapeInferencePass.h | 40 + compiler/exo/src/Pass/TypeInferencePass.cpp | 57 + compiler/exo/src/Pass/TypeInferencePass.h | 42 + compiler/exo/src/Passes.cpp | 19 + compiler/exo/src/Passes.h | 38 + compiler/exo/src/ProgressReporter.cpp | 84 + compiler/exo/src/ProgressReporter.h | 53 + compiler/exo/src/ShapeInference.cpp | 44 + compiler/exo/src/ShapeInference.h | 41 + compiler/exo/src/TFLite/TFLExporter.cpp | 49 + compiler/exo/src/TFLite/TFLExporterImpl.cpp | 179 + compiler/exo/src/TFLite/TFLExporterImpl.h | 78 + compiler/exo/src/TFLite/TFLExporterImpl.test.cpp | 413 + compiler/exo/src/TFLite/TFLExporterUtils.cpp | 160 + compiler/exo/src/TFLite/TFLExporterUtils.h | 118 + compiler/exo/src/TFLite/TFLExporterUtils.test.cpp | 108 + compiler/exo/src/TFLite/TFLOperationExporter.cpp | 1199 + compiler/exo/src/TFLite/TFLOperationExporter.h | 39 + compiler/exo/src/TFLite/TFLTensorExporter.cpp | 249 + compiler/exo/src/TFLite/TFLTensorExporter.h | 42 + compiler/exo/src/TFLite/TFLTypeInference.cpp | 82 + compiler/exo/src/TFLite/TFLTypeInference.h | 42 + compiler/exo/src/TFLite/TFLTypeInference.test.cpp | 118 + compiler/exo/src/TestGraph.h | 315 + compiler/exo/src/TestHelper.h | 110 + compiler/fipe/CMakeLists.txt | 11 + compiler/fipe/fipe.test.cpp | 73 + compiler/fipe/include/fipe.h | 40 + compiler/gen-core/CMakeLists.txt | 17 + compiler/gen-core/README.md | 3 + compiler/gen-core/include/gencore/HDF5Common.h | 65 + compiler/gen-core/include/gencore/HDF5Exporter.h | 52 + compiler/gen-core/include/gencore/HDF5Importer.h | 55 + compiler/gen-core/requires.cmake | 2 + compiler/gen-core/src/HDF5Common.cpp | 43 + compiler/gen-core/src/HDF5Exporter.cpp | 95 + compiler/gen-core/src/HDF5Importer.cpp | 85 + compiler/gen-tf-input/CMakeLists.txt | 4 + compiler/gen-tf-input/README.md | 11 + compiler/gen-tf-input/src/Driver.cpp | 56 + compiler/gen-tf-output/CMakeLists.txt | 3 + compiler/gen-tf-output/README.md | 13 + compiler/gen-tf-output/src/Driver.cpp | 54 + compiler/gen-tflite-output/CMakeLists.txt | 3 + compiler/gen-tflite-output/README.md | 14 + compiler/gen-tflite-output/src/Driver.cpp | 54 + compiler/hermes-std/CMakeLists.txt | 27 + compiler/hermes-std/README.md | 3 + .../hermes-std/include/hermes/ConsoleReporter.h | 35 + compiler/hermes-std/include/hermes/EnvConfig.h | 55 + compiler/hermes-std/requires.cmake | 1 + compiler/hermes-std/src/ConsoleReporter.cpp | 32 + compiler/hermes-std/src/ConsoleReporter.test.cpp | 46 + compiler/hermes-std/src/EnvConfig.cpp | 44 + compiler/hermes/CMakeLists.txt | 28 + compiler/hermes/README.md | 3 + compiler/hermes/include/hermes.h | 25 + compiler/hermes/include/hermes/core/Config.h | 43 + compiler/hermes/include/hermes/core/Context.h | 78 + compiler/hermes/include/hermes/core/Message.h | 69 + .../hermes/include/hermes/core/MessageBuffer.h | 51 + compiler/hermes/include/hermes/core/MessageBus.h | 40 + compiler/hermes/include/hermes/core/Severity.h | 83 + compiler/hermes/include/hermes/core/Sink.h | 49 + compiler/hermes/include/hermes/core/Source.h | 118 + .../hermes/include/hermes/core/SourceSetting.h | 108 + compiler/hermes/requires.cmake | 1 + compiler/hermes/src/core/Context.cpp | 79 + compiler/hermes/src/core/Context.test.cpp | 28 + compiler/hermes/src/core/Message.cpp | 43 + compiler/hermes/src/core/Message.test.cpp | 41 + compiler/hermes/src/core/MessageBuffer.cpp | 40 + compiler/hermes/src/core/MessageBuffer.test.cpp | 66 + compiler/hermes/src/core/MessageBus.cpp | 19 + compiler/hermes/src/core/Severity.test.cpp | 59 + compiler/hermes/src/core/Sink.cpp | 19 + compiler/hermes/src/core/Source.cpp | 70 + compiler/hermes/src/core/Source.test.cpp | 101 + compiler/hermes/src/hermes.cpp | 19 + compiler/hermes/src/hermes.test.cpp | 24 + compiler/i5diff/CMakeLists.txt | 15 + compiler/i5diff/README.md | 20 + compiler/i5diff/requires.cmake | 2 + compiler/i5diff/src/entry.cpp | 313 + compiler/kuma/CMakeLists.txt | 19 + compiler/kuma/README.md | 7 + compiler/kuma/include/kuma.h | 98 + compiler/kuma/src/IntervalSet.cpp | 92 + compiler/kuma/src/IntervalSet.h | 78 + compiler/kuma/src/IntervalSet.test.cpp | 31 + compiler/kuma/src/kuma.cpp | 93 + compiler/kuma/src/kuma.test.cpp | 89 + compiler/loco/CMakeLists.txt | 28 + compiler/loco/README.md | 3 + compiler/loco/doc/LEP_000_Dialect_Service.md | 116 + compiler/loco/include/loco.h | 26 + compiler/loco/include/loco/ADT/AnnotatedItem.h | 82 + compiler/loco/include/loco/ADT/ObjectPool.h | 77 + compiler/loco/include/loco/IR/Algorithm.h | 48 + compiler/loco/include/loco/IR/BiasShape.h | 43 + compiler/loco/include/loco/IR/CanonicalDialect.h | 45 + compiler/loco/include/loco/IR/CanonicalNode.h | 23 + compiler/loco/include/loco/IR/CanonicalNodeDecl.h | 50 + compiler/loco/include/loco/IR/CanonicalNodeImpl.h | 64 + .../include/loco/IR/CanonicalNodeVisitor.forward.h | 29 + .../loco/include/loco/IR/CanonicalNodeVisitor.h | 79 + compiler/loco/include/loco/IR/CanonicalNodes.lst | 49 + compiler/loco/include/loco/IR/CanonicalOpcode.h | 37 + compiler/loco/include/loco/IR/DataType.h | 51 + compiler/loco/include/loco/IR/DataTypeTraits.h | 86 + .../loco/include/loco/IR/DepthwiseFilterAxis.h | 33 + .../loco/include/loco/IR/DepthwiseFilterCodec.h | 69 + .../loco/include/loco/IR/DepthwiseFilterIndex.h | 65 + .../loco/include/loco/IR/DepthwiseFilterShape.h | 63 + compiler/loco/include/loco/IR/Dialect.h | 66 + compiler/loco/include/loco/IR/DialectService.h | 35 + compiler/loco/include/loco/IR/Dimension.h | 85 + compiler/loco/include/loco/IR/Domain.h | 53 + compiler/loco/include/loco/IR/FeatureAxis.h | 33 + compiler/loco/include/loco/IR/FeatureCodec.h | 77 + compiler/loco/include/loco/IR/FeatureIndex.h | 65 + compiler/loco/include/loco/IR/FeatureShape.h | 66 + compiler/loco/include/loco/IR/FilterAxis.h | 33 + compiler/loco/include/loco/IR/FilterCodec.h | 61 + compiler/loco/include/loco/IR/FilterIndex.h | 65 + compiler/loco/include/loco/IR/FilterShape.h | 69 + compiler/loco/include/loco/IR/Graph.forward.h | 28 + compiler/loco/include/loco/IR/Graph.h | 284 + compiler/loco/include/loco/IR/GraphInputIndex.h | 29 + compiler/loco/include/loco/IR/GraphOutputIndex.h | 29 + compiler/loco/include/loco/IR/MatrixAxis.h | 31 + compiler/loco/include/loco/IR/MatrixCodec.h | 73 + compiler/loco/include/loco/IR/MatrixIndex.h | 55 + compiler/loco/include/loco/IR/MatrixShape.h | 56 + compiler/loco/include/loco/IR/Node.forward.h | 28 + compiler/loco/include/loco/IR/Node.h | 147 + compiler/loco/include/loco/IR/NodeMixins.h | 133 + compiler/loco/include/loco/IR/NodePool.forward.h | 28 + compiler/loco/include/loco/IR/NodePool.h | 62 + compiler/loco/include/loco/IR/NodeShape.h | 70 + compiler/loco/include/loco/IR/Nodes.h | 1123 + compiler/loco/include/loco/IR/Padding2D.h | 65 + compiler/loco/include/loco/IR/PaddingND.h | 56 + compiler/loco/include/loco/IR/PermutingCodec.h | 421 + compiler/loco/include/loco/IR/Stride.h | 50 + compiler/loco/include/loco/IR/TensorAxis.h | 29 + compiler/loco/include/loco/IR/TensorAxisSet.h | 42 + compiler/loco/include/loco/IR/TensorIndex.h | 30 + compiler/loco/include/loco/IR/TensorShape.h | 62 + compiler/loco/include/loco/IR/Use.h | 71 + compiler/loco/include/loco/IR/Verifier.h | 100 + compiler/loco/include/loco/IR/Window.h | 52 + .../loco/Service/CanonicalShapeInferenceRule.h | 38 + .../loco/Service/MultiDialectShapeInferenceRule.h | 45 + .../loco/include/loco/Service/ShapeInference.h | 66 + .../loco/include/loco/Service/ShapeInferenceRule.h | 97 + compiler/loco/include/loco/Service/TypeInference.h | 114 + compiler/loco/src/ADT/AnnotatedItem.test.cpp | 75 + compiler/loco/src/ADT/ObjectPool.cpp | 19 + compiler/loco/src/IR/Algorithm.cpp | 121 + compiler/loco/src/IR/Algorithm.test.cpp | 122 + compiler/loco/src/IR/BiasShape.test.cpp | 26 + compiler/loco/src/IR/CanonicalDialect.cpp | 67 + compiler/loco/src/IR/CanonicalDialect.test.cpp | 29 + compiler/loco/src/IR/CanonicalNode.cpp | 25 + compiler/loco/src/IR/CanonicalNode.test.cpp | 72 + compiler/loco/src/IR/CanonicalOpcode.cpp | 19 + compiler/loco/src/IR/DataType.cpp | 19 + compiler/loco/src/IR/DataTypeTraits.test.cpp | 29 + compiler/loco/src/IR/DepthwiseFilterAxis.cpp | 19 + compiler/loco/src/IR/DepthwiseFilterCodec.cpp | 19 + compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp | 67 + compiler/loco/src/IR/DepthwiseFilterShape.test.cpp | 80 + compiler/loco/src/IR/Dialect.cpp | 19 + compiler/loco/src/IR/Dialect.test.cpp | 41 + compiler/loco/src/IR/DialectService.cpp | 19 + compiler/loco/src/IR/Dimension.cpp | 32 + compiler/loco/src/IR/Dimension.test.cpp | 100 + compiler/loco/src/IR/Domain.cpp | 19 + compiler/loco/src/IR/FeatureAxis.cpp | 19 + compiler/loco/src/IR/FeatureCodec.cpp | 19 + compiler/loco/src/IR/FeatureIndex.test.cpp | 67 + compiler/loco/src/IR/FeatureShape.test.cpp | 80 + compiler/loco/src/IR/FilterAxis.cpp | 19 + compiler/loco/src/IR/FilterCodec.cpp | 19 + compiler/loco/src/IR/FilterIndex.test.cpp | 67 + compiler/loco/src/IR/FilterShape.test.cpp | 80 + compiler/loco/src/IR/Graph.cpp | 137 + compiler/loco/src/IR/Graph.test.cpp | 218 + compiler/loco/src/IR/GraphInputIndex.cpp | 19 + compiler/loco/src/IR/GraphOutputIndex.cpp | 19 + compiler/loco/src/IR/MatrixAxis.cpp | 19 + compiler/loco/src/IR/MatrixCodec.cpp | 19 + compiler/loco/src/IR/MockupNode.h | 58 + compiler/loco/src/IR/Node.cpp | 88 + compiler/loco/src/IR/Node.test.cpp | 102 + compiler/loco/src/IR/NodeMixins.cpp | 19 + compiler/loco/src/IR/NodePool.cpp | 31 + compiler/loco/src/IR/NodeShape.cpp | 284 + compiler/loco/src/IR/NodeShape.test.cpp | 125 + compiler/loco/src/IR/Nodes.cpp | 243 + compiler/loco/src/IR/Nodes.test.cpp | 588 + compiler/loco/src/IR/Padding2D.test.cpp | 29 + compiler/loco/src/IR/PaddingND.test.cpp | 32 + compiler/loco/src/IR/PermutingCodec.cpp | 630 + compiler/loco/src/IR/PermutingCodec.test.cpp | 553 + compiler/loco/src/IR/Stride.test.cpp | 42 + compiler/loco/src/IR/TensorAxis.cpp | 19 + compiler/loco/src/IR/TensorAxisSet.cpp | 19 + compiler/loco/src/IR/TensorIndex.cpp | 19 + compiler/loco/src/IR/TensorShape.cpp | 39 + compiler/loco/src/IR/TensorShape.test.cpp | 109 + compiler/loco/src/IR/Use.cpp | 45 + compiler/loco/src/IR/Use.test.cpp | 42 + compiler/loco/src/IR/Verifier.cpp | 119 + compiler/loco/src/IR/Verifier.test.cpp | 64 + compiler/loco/src/IR/Window.test.cpp | 42 + .../src/Service/CanonicalShapeInferenceRule.cpp | 774 + .../Service/CanonicalShapeInferenceRule.test.cpp | 400 + compiler/loco/src/Service/GraphBuilder.h | 547 + compiler/loco/src/Service/GraphBuilder.test.cpp | 47 + compiler/loco/src/Service/GraphTestcase.h | 541 + .../src/Service/MultiDialectShapeInferenceRule.cpp | 67 + .../MultiDialectShapeInferenceRule.test.cpp | 134 + compiler/loco/src/Service/ShapeInference.cpp | 105 + compiler/loco/src/Service/ShapeInference.test.cpp | 87 + compiler/loco/src/Service/ShapeInferenceRule.cpp | 31 + compiler/loco/src/Service/TypeInference.cpp | 228 + compiler/loco/src/Service/TypeInference.test.cpp | 282 + compiler/loco/src/loco.test.cpp | 108 + compiler/loco/src/tensorflow.test.cpp | 386 + compiler/locoex-customop/CMakeLists.txt | 18 + compiler/locoex-customop/README.md | 9 + .../locoex-customop/include/locoex/COpAttrTypes.h | 101 + compiler/locoex-customop/include/locoex/COpCall.h | 71 + .../locoex-customop/include/locoex/COpDialect.h | 43 + compiler/locoex-customop/include/locoex/COpNode.h | 37 + .../include/locoex/Service/COpFormattedGraph.h | 47 + .../include/locoex/Service/COpShapeInferenceRule.h | 41 + .../include/locoex/Service/COpTypeInference.h | 36 + .../include/locoex/VariadicArityNode.h | 77 + compiler/locoex-customop/requires.cmake | 4 + compiler/locoex-customop/src/COpCall.cpp | 67 + compiler/locoex-customop/src/COpCall.test.cpp | 90 + compiler/locoex-customop/src/COpDialect.cpp | 28 + compiler/locoex-customop/src/COpDialect.test.cpp | 29 + compiler/locoex-customop/src/COpNode.cpp | 25 + .../src/Service/COpFormattedGraph.cpp | 66 + .../src/Service/COpShapeInferenceRule.cpp | 59 + .../src/Service/COpShapeInferenceRule.test.cpp | 54 + .../src/Service/COpTypeInference.cpp | 47 + .../src/Service/COpTypeInference.test.cpp | 63 + .../locoex-customop/src/VariadicArityNode.test.cpp | 72 + compiler/locomotiv/CMakeLists.txt | 29 + compiler/locomotiv/README.md | 90 + compiler/locomotiv/include/locomotiv/NodeData.h | 61 + compiler/locomotiv/include/locomotiv/Session.h | 108 + compiler/locomotiv/requires.cmake | 2 + compiler/locomotiv/src/Node.lst | 40 + compiler/locomotiv/src/Node/AvgPool2D.cpp | 179 + compiler/locomotiv/src/Node/AvgPool2D.test.cpp | 176 + compiler/locomotiv/src/Node/BiasAdd.cpp | 121 + compiler/locomotiv/src/Node/BiasAdd.test.cpp | 204 + compiler/locomotiv/src/Node/BiasEncode.cpp | 63 + compiler/locomotiv/src/Node/BiasEncode.test.cpp | 95 + compiler/locomotiv/src/Node/ConstGen.cpp | 116 + compiler/locomotiv/src/Node/ConstGen.test.cpp | 100 + compiler/locomotiv/src/Node/Conv2D.cpp | 179 + compiler/locomotiv/src/Node/Conv2D.test.cpp | 231 + compiler/locomotiv/src/Node/DepthwiseConv2D.cpp | 185 + .../locomotiv/src/Node/DepthwiseConv2D.test.cpp | 164 + .../locomotiv/src/Node/DepthwiseFilterEncode.cpp | 113 + .../src/Node/DepthwiseFilterEncode.test.cpp | 90 + compiler/locomotiv/src/Node/EltwiseAdd.cpp | 34 + compiler/locomotiv/src/Node/EltwiseAdd.test.cpp | 121 + compiler/locomotiv/src/Node/EltwiseDiv.cpp | 34 + compiler/locomotiv/src/Node/EltwiseDiv.test.cpp | 121 + compiler/locomotiv/src/Node/EltwiseMax.cpp | 36 + compiler/locomotiv/src/Node/EltwiseMax.test.cpp | 121 + compiler/locomotiv/src/Node/EltwiseMul.cpp | 34 + compiler/locomotiv/src/Node/EltwiseMul.test.cpp | 124 + compiler/locomotiv/src/Node/EltwiseSqrt.cpp | 43 + compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp | 69 + compiler/locomotiv/src/Node/EltwiseSub.cpp | 34 + compiler/locomotiv/src/Node/EltwiseSub.test.cpp | 121 + compiler/locomotiv/src/Node/FeatureCodec.test.cpp | 223 + compiler/locomotiv/src/Node/FeatureDecode.cpp | 112 + compiler/locomotiv/src/Node/FeatureEncode.cpp | 114 + compiler/locomotiv/src/Node/FilterEncode.cpp | 114 + compiler/locomotiv/src/Node/FilterEncode.test.cpp | 144 + compiler/locomotiv/src/Node/Forward.cpp | 62 + compiler/locomotiv/src/Node/Forward.test.cpp | 88 + compiler/locomotiv/src/Node/MatMul.cpp | 133 + compiler/locomotiv/src/Node/MatMul.test.cpp | 188 + compiler/locomotiv/src/Node/MatrixCodec.test.cpp | 207 + compiler/locomotiv/src/Node/MatrixDecode.cpp | 109 + compiler/locomotiv/src/Node/MatrixEncode.cpp | 112 + compiler/locomotiv/src/Node/MaxPool2D.cpp | 167 + compiler/locomotiv/src/Node/MaxPool2D.test.cpp | 159 + compiler/locomotiv/src/Node/Pull.cpp | 72 + compiler/locomotiv/src/Node/Pull.test.cpp | 61 + compiler/locomotiv/src/Node/Push.cpp | 61 + compiler/locomotiv/src/Node/Push.test.cpp | 88 + compiler/locomotiv/src/Node/ReLU.cpp | 41 + compiler/locomotiv/src/Node/ReLU.test.cpp | 62 + compiler/locomotiv/src/Node/ReLU6.cpp | 96 + compiler/locomotiv/src/Node/ReLU6.test.cpp | 66 + compiler/locomotiv/src/Node/Reshape.cpp | 90 + compiler/locomotiv/src/Node/Reshape.test.cpp | 67 + compiler/locomotiv/src/Node/Softmax.cpp | 122 + compiler/locomotiv/src/Node/Softmax.test.cpp | 68 + compiler/locomotiv/src/Node/Tanh.cpp | 41 + compiler/locomotiv/src/Node/Tanh.test.cpp | 64 + compiler/locomotiv/src/Node/TensorBroadcast.cpp | 106 + .../locomotiv/src/Node/TensorBroadcast.test.cpp | 63 + compiler/locomotiv/src/Node/TensorConcat.cpp | 113 + compiler/locomotiv/src/Node/TensorConcat.test.cpp | 128 + compiler/locomotiv/src/Node/TensorConstantPad.cpp | 113 + .../locomotiv/src/Node/TensorConstantPad.test.cpp | 218 + compiler/locomotiv/src/Node/TensorReduce.cpp | 153 + compiler/locomotiv/src/Node/TensorReduce.test.cpp | 104 + compiler/locomotiv/src/Node/TransposedConv2D.cpp | 189 + .../locomotiv/src/Node/TransposedConv2D.test.cpp | 144 + compiler/locomotiv/src/NodeData.cpp | 33 + compiler/locomotiv/src/NodeData.test.cpp | 54 + compiler/locomotiv/src/NodeDataImpl.cpp | 77 + compiler/locomotiv/src/NodeDataImpl.h | 66 + compiler/locomotiv/src/NodeDataImpl.test.cpp | 58 + compiler/locomotiv/src/NodeDomain.cpp | 53 + compiler/locomotiv/src/NodeDomain.h | 37 + compiler/locomotiv/src/NodeDomain.test.cpp | 37 + compiler/locomotiv/src/NodeExecution.cpp | 158 + compiler/locomotiv/src/NodeExecution.h | 83 + compiler/locomotiv/src/Session.cpp | 93 + compiler/locomotiv/src/Session.test.cpp | 379 + compiler/locomotiv/src/UserData.cpp | 63 + compiler/locomotiv/src/UserData.h | 31 + compiler/locomotiv/src/Validation.h | 34 + compiler/locop/CMakeLists.txt | 27 + compiler/locop/README.md | 3 + .../include/locop/CanonicalNodeSummaryBuilder.h | 45 + compiler/locop/include/locop/FormattedGraph.h | 89 + .../locop/include/locop/FormattedTensorShape.h | 76 + .../include/locop/GenericNodeSummaryBuilder.h | 45 + compiler/locop/include/locop/Interfaces.h | 43 + compiler/locop/include/locop/NodeSummary.h | 114 + compiler/locop/include/locop/NodeSummaryBuilder.h | 47 + compiler/locop/include/locop/SymbolTable.h | 41 + compiler/locop/src/CanonicalNodeSummaryBuilder.cpp | 297 + compiler/locop/src/ExampleGraph.h | 69 + compiler/locop/src/FormattedGraph.cpp | 390 + compiler/locop/src/FormattedGraph.test.cpp | 143 + compiler/locop/src/FormattedTensorShape.cpp | 68 + compiler/locop/src/FormattedTensorShape.test.cpp | 33 + compiler/locop/src/GenericNodeSummaryBuilder.cpp | 38 + .../locop/src/GenericNodeSummaryBuilder.test.cpp | 58 + compiler/locop/src/Interfaces.cpp | 28 + compiler/locop/src/NodeSummary.cpp | 41 + compiler/locop/src/NodeSummaryBuilder.cpp | 21 + compiler/logo-core/CMakeLists.txt | 19 + compiler/logo-core/README.md | 3 + compiler/logo-core/include/logo/Pass.h | 48 + compiler/logo-core/include/logo/Phase.h | 192 + compiler/logo-core/requires.cmake | 1 + compiler/logo-core/src/Pass.cpp | 32 + compiler/logo-core/src/Pass.test.cpp | 46 + compiler/logo-core/src/Phase.cpp | 71 + compiler/logo/CMakeLists.txt | 23 + compiler/logo/README.md | 3 + compiler/logo/include/logo/ConstantFoldingPass.h | 41 + compiler/logo/include/logo/Passes.h | 30 + compiler/logo/include/logo/RemoveDeadNodePass.h | 34 + compiler/logo/include/logo/RemoveForwardNodePass.h | 46 + compiler/logo/include/logo/ReorderDecodePass.h | 51 + .../include/logo/ResolveDuplicateReshapePass.h | 41 + .../include/logo/ResolveRedundantReshapePass.h | 44 + .../include/logo/SimplifyDomainConversionPass.h | 47 + compiler/logo/requires.cmake | 4 + compiler/logo/src/Passes/ConstantFoldingPass.cpp | 174 + .../logo/src/Passes/ConstantFoldingPass.test.cpp | 179 + compiler/logo/src/Passes/RemoveDeadNodePass.cpp | 77 + compiler/logo/src/Passes/RemoveForwardNodePass.cpp | 64 + compiler/logo/src/Passes/ReorderDecodePass.cpp | 311 + .../src/Passes/ResolveDuplicateReshapePass.cpp | 108 + .../src/Passes/ResolveRedundantReshapePass.cpp | 103 + .../src/Passes/SimplifyDomainConversionPass.cpp | 445 + .../Passes/SimplifyDomainConversionPass.test.cpp | 234 + compiler/logo/src/TestHelper.h | 44 + compiler/luci/CMakeLists.txt | 10 + compiler/luci/README.md | 3 + compiler/luci/export/CMakeLists.txt | 29 + compiler/luci/export/README.md | 3 + compiler/luci/export/include/luci/CircleExporter.h | 64 + compiler/luci/export/src/Check.h | 35 + compiler/luci/export/src/CircleExporter.cpp | 64 + compiler/luci/export/src/CircleExporterImpl.cpp | 266 + compiler/luci/export/src/CircleExporterImpl.h | 82 + compiler/luci/export/src/CircleExporterUtils.cpp | 169 + compiler/luci/export/src/CircleExporterUtils.h | 51 + .../luci/export/src/CircleOperationExporter.cpp | 643 + compiler/luci/export/src/CircleOperationExporter.h | 37 + compiler/luci/export/src/CircleTensorExporter.cpp | 264 + compiler/luci/export/src/CircleTensorExporter.h | 44 + compiler/luci/export/src/Optimize.cpp | 48 + compiler/luci/export/src/Optimize.h | 33 + compiler/luci/export/src/ProgressReporter.cpp | 84 + compiler/luci/export/src/ProgressReporter.h | 53 + compiler/luci/export/src/SerializedData.h | 95 + compiler/luci/import/CMakeLists.txt | 26 + compiler/luci/import/README.md | 3 + .../luci/import/include/luci/Import/CircleReader.h | 87 + .../luci/import/include/luci/Import/GraphBuilder.h | 56 + .../include/luci/Import/GraphBuilderContext.h | 79 + .../include/luci/Import/GraphBuilderRegistry.h | 85 + compiler/luci/import/include/luci/Import/Nodes.h | 48 + .../import/include/luci/Import/Nodes/CircleAbs.h | 37 + .../import/include/luci/Import/Nodes/CircleAdd.h | 37 + .../include/luci/Import/Nodes/CircleArgMax.h | 37 + .../luci/Import/Nodes/CircleAveragePool2D.h | 37 + .../luci/Import/Nodes/CircleBatchToSpaceND.h | 37 + .../luci/Import/Nodes/CircleConcatenation.h | 37 + .../import/include/luci/Import/Nodes/CircleConst.h | 37 + .../include/luci/Import/Nodes/CircleConv2D.h | 37 + .../import/include/luci/Import/Nodes/CircleCos.h | 37 + .../luci/Import/Nodes/CircleDepthwiseConv2D.h | 37 + .../import/include/luci/Import/Nodes/CircleDiv.h | 36 + .../import/include/luci/Import/Nodes/CircleEqual.h | 37 + .../import/include/luci/Import/Nodes/CircleExp.h | 37 + .../luci/Import/Nodes/CircleFullyConnected.h | 37 + .../include/luci/Import/Nodes/CircleLogicalNot.h | 37 + .../include/luci/Import/Nodes/CircleLogicalOr.h | 37 + .../include/luci/Import/Nodes/CircleMaxPool2D.h | 37 + .../import/include/luci/Import/Nodes/CircleMean.h | 37 + .../import/include/luci/Import/Nodes/CircleMul.h | 37 + .../import/include/luci/Import/Nodes/CirclePack.h | 37 + .../import/include/luci/Import/Nodes/CirclePad.h | 37 + .../import/include/luci/Import/Nodes/CircleRelu.h | 37 + .../include/luci/Import/Nodes/CircleReshape.h | 37 + .../import/include/luci/Import/Nodes/CircleRsqrt.h | 37 + .../include/luci/Import/Nodes/CircleSoftmax.h | 37 + .../import/include/luci/Import/Nodes/CircleSub.h | 37 + .../include/luci/Import/Nodes/CircleTranspose.h | 37 + compiler/luci/import/include/luci/Importer.h | 54 + compiler/luci/import/src/CircleReader.cpp | 211 + compiler/luci/import/src/GraphBuilder.cpp | 61 + compiler/luci/import/src/GraphBuilderContext.cpp | 47 + compiler/luci/import/src/GraphBuilderRegistry.cpp | 163 + compiler/luci/import/src/Importer.cpp | 253 + compiler/luci/import/src/Importer.test.cpp | 23 + compiler/luci/import/src/Nodes/CircleAbs.cpp | 44 + compiler/luci/import/src/Nodes/CircleAdd.cpp | 48 + compiler/luci/import/src/Nodes/CircleArgMax.cpp | 48 + .../luci/import/src/Nodes/CircleAveragePool2D.cpp | 50 + .../luci/import/src/Nodes/CircleBatchToSpaceND.cpp | 80 + .../luci/import/src/Nodes/CircleConcatenation.cpp | 52 + compiler/luci/import/src/Nodes/CircleConst.cpp | 110 + compiler/luci/import/src/Nodes/CircleConv2D.cpp | 58 + compiler/luci/import/src/Nodes/CircleCos.cpp | 46 + .../import/src/Nodes/CircleDepthwiseConv2D.cpp | 60 + compiler/luci/import/src/Nodes/CircleDiv.cpp | 49 + compiler/luci/import/src/Nodes/CircleEqual.cpp | 51 + compiler/luci/import/src/Nodes/CircleExp.cpp | 59 + .../luci/import/src/Nodes/CircleFullyConnected.cpp | 56 + .../luci/import/src/Nodes/CircleLogicalNot.cpp | 51 + compiler/luci/import/src/Nodes/CircleLogicalOr.cpp | 55 + compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp | 52 + compiler/luci/import/src/Nodes/CircleMean.cpp | 46 + compiler/luci/import/src/Nodes/CircleMul.cpp | 49 + compiler/luci/import/src/Nodes/CirclePack.cpp | 61 + compiler/luci/import/src/Nodes/CirclePad.cpp | 50 + compiler/luci/import/src/Nodes/CircleRelu.cpp | 47 + compiler/luci/import/src/Nodes/CircleReshape.cpp | 82 + compiler/luci/import/src/Nodes/CircleRsqrt.cpp | 60 + compiler/luci/import/src/Nodes/CircleSoftmax.cpp | 49 + compiler/luci/import/src/Nodes/CircleSub.cpp | 51 + compiler/luci/import/src/Nodes/CircleTranspose.cpp | 51 + compiler/luci/lang/CMakeLists.txt | 22 + compiler/luci/lang/README.md | 3 + compiler/luci/lang/include/luci/IR/AttrFilter.h | 43 + .../luci/lang/include/luci/IR/AttrFusedActFunc.h | 36 + compiler/luci/lang/include/luci/IR/AttrPadding.h | 33 + compiler/luci/lang/include/luci/IR/AttrStride.h | 43 + compiler/luci/lang/include/luci/IR/CircleDialect.h | 43 + compiler/luci/lang/include/luci/IR/CircleNode.h | 23 + .../luci/lang/include/luci/IR/CircleNodeDecl.h | 68 + .../luci/lang/include/luci/IR/CircleNodeImpl.h | 70 + .../include/luci/IR/CircleNodeVisitor.forward.h | 30 + .../luci/lang/include/luci/IR/CircleNodeVisitor.h | 87 + compiler/luci/lang/include/luci/IR/CircleNodes.h | 73 + compiler/luci/lang/include/luci/IR/CircleNodes.lst | 52 + compiler/luci/lang/include/luci/IR/CircleOpcode.h | 32 + .../luci/lang/include/luci/IR/CircleQuantParam.h | 36 + .../luci/lang/include/luci/IR/LuciNodeMixins.h | 104 + compiler/luci/lang/include/luci/IR/Module.h | 70 + .../luci/lang/include/luci/IR/Nodes/CircleAbs.h | 40 + .../luci/lang/include/luci/IR/Nodes/CircleAdd.h | 45 + .../luci/lang/include/luci/IR/Nodes/CircleArgMax.h | 50 + .../include/luci/IR/Nodes/CircleAveragePool2D.h | 63 + .../include/luci/IR/Nodes/CircleBatchToSpaceND.h | 47 + .../include/luci/IR/Nodes/CircleConcatenation.h | 72 + .../luci/lang/include/luci/IR/Nodes/CircleConst.h | 57 + .../luci/lang/include/luci/IR/Nodes/CircleConv2D.h | 62 + .../luci/lang/include/luci/IR/Nodes/CircleCos.h | 40 + .../include/luci/IR/Nodes/CircleDepthwiseConv2D.h | 68 + .../luci/lang/include/luci/IR/Nodes/CircleDiv.h | 51 + .../luci/lang/include/luci/IR/Nodes/CircleEqual.h | 43 + .../luci/lang/include/luci/IR/Nodes/CircleExp.h | 40 + .../include/luci/IR/Nodes/CircleFullyConnected.h | 50 + .../luci/lang/include/luci/IR/Nodes/CircleGather.h | 51 + .../luci/lang/include/luci/IR/Nodes/CircleInput.h | 55 + .../include/luci/IR/Nodes/CircleInstanceNorm.h | 56 + .../lang/include/luci/IR/Nodes/CircleLogicalNot.h | 40 + .../lang/include/luci/IR/Nodes/CircleLogicalOr.h | 43 + .../lang/include/luci/IR/Nodes/CircleMaxPool2D.h | 62 + .../lang/include/luci/IR/Nodes/CircleMaximum.h | 44 + .../luci/lang/include/luci/IR/Nodes/CircleMean.h | 51 + .../luci/lang/include/luci/IR/Nodes/CircleMul.h | 45 + .../luci/lang/include/luci/IR/Nodes/CircleOutput.h | 55 + .../luci/lang/include/luci/IR/Nodes/CirclePack.h | 67 + .../luci/lang/include/luci/IR/Nodes/CirclePad.h | 46 + .../luci/lang/include/luci/IR/Nodes/CircleRelu.h | 44 + .../luci/lang/include/luci/IR/Nodes/CircleRelu6.h | 44 + .../lang/include/luci/IR/Nodes/CircleReshape.h | 69 + .../luci/lang/include/luci/IR/Nodes/CircleRsqrt.h | 44 + .../lang/include/luci/IR/Nodes/CircleSoftmax.h | 47 + .../luci/lang/include/luci/IR/Nodes/CircleSqrt.h | 44 + .../luci/IR/Nodes/CircleSquaredDifference.h | 48 + .../luci/lang/include/luci/IR/Nodes/CircleSub.h | 48 + .../lang/include/luci/IR/Nodes/CircleTranspose.h | 50 + .../include/luci/IR/Nodes/CircleTransposeConv.h | 64 + .../luci/lang/include/luci/IR/VariadicArityNode.h | 77 + compiler/luci/lang/src/Check.h | 35 + compiler/luci/lang/src/CircleDialect.cpp | 88 + compiler/luci/lang/src/CircleDialect.test.cpp | 34 + compiler/luci/lang/src/CircleNode.cpp | 25 + compiler/luci/lang/src/CircleNodes.cpp | 50 + compiler/luci/lang/src/LuciNodeMixins.cpp | 18 + compiler/luci/lang/src/Module.cpp | 46 + compiler/luci/lang/src/Module.test.cpp | 73 + compiler/luci/lang/src/Nodes/CircleAbs.test.cpp | 31 + compiler/luci/lang/src/Nodes/CircleAdd.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp | 32 + .../lang/src/Nodes/CircleBatchToSpaceND.test.cpp | 33 + .../lang/src/Nodes/CircleConcatenation.test.cpp | 35 + compiler/luci/lang/src/Nodes/CircleConst.cpp | 79 + compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp | 33 + compiler/luci/lang/src/Nodes/CircleCos.test.cpp | 31 + .../lang/src/Nodes/CircleDepthwiseConv2D.test.cpp | 38 + compiler/luci/lang/src/Nodes/CircleDiv.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleEqual.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleExp.test.cpp | 31 + .../lang/src/Nodes/CircleFullyConnected.test.cpp | 34 + compiler/luci/lang/src/Nodes/CircleGather.test.cpp | 33 + compiler/luci/lang/src/Nodes/CircleInput.cpp | 38 + .../lang/src/Nodes/CircleInstanceNorm.test.cpp | 35 + .../luci/lang/src/Nodes/CircleLogicalNot.test.cpp | 31 + .../luci/lang/src/Nodes/CircleLogicalOr.test.cpp | 32 + .../luci/lang/src/Nodes/CircleMaxPool2D.test.cpp | 33 + .../luci/lang/src/Nodes/CircleMaximum.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleMul.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleOutput.cpp | 38 + compiler/luci/lang/src/Nodes/CirclePack.test.cpp | 35 + compiler/luci/lang/src/Nodes/CirclePad.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleRelu.test.cpp | 31 + compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp | 31 + .../luci/lang/src/Nodes/CircleReshape.test.cpp | 48 + compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp | 31 + .../luci/lang/src/Nodes/CircleSoftmax.test.cpp | 31 + compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp | 31 + .../src/Nodes/CircleSquaredDifference.test.cpp | 32 + compiler/luci/lang/src/Nodes/CircleSub.test.cpp | 32 + .../luci/lang/src/Nodes/CircleTranspose.test.cpp | 32 + .../lang/src/Nodes/CircleTransposeConv.test.cpp | 33 + compiler/luci/log/CMakeLists.txt | 9 + compiler/luci/log/README.md | 3 + compiler/luci/log/include/luci/Log.h | 75 + compiler/luci/log/include/luci/LoggingContext.h | 35 + compiler/luci/log/src/Log.cpp | 87 + compiler/luci/log/src/LoggingContext.cpp | 41 + compiler/luci/logex/CMakeLists.txt | 13 + compiler/luci/logex/README.md | 3 + compiler/luci/logex/include/luci/FormattedGraph.h | 56 + compiler/luci/logex/include/luci/LogHelper.h | 36 + compiler/luci/logex/src/FormattedGraph.cpp | 606 + compiler/luci/logex/src/LogHelper.cpp | 29 + compiler/luci/pass/CMakeLists.txt | 29 + compiler/luci/pass/README.md | 3 + compiler/luci/pass/include/luci/CircleOptimizer.h | 55 + .../pass/include/luci/Pass/FuseInstanceNormPass.h | 40 + .../pass/include/luci/Pass/ShapeInferencePass.h | 41 + .../pass/include/luci/Pass/TypeInferencePass.h | 42 + compiler/luci/pass/src/CircleOptimizer.cpp | 96 + compiler/luci/pass/src/FuseInstanceNormPass.cpp | 401 + compiler/luci/pass/src/ProgressReporter.cpp | 84 + compiler/luci/pass/src/ProgressReporter.h | 53 + compiler/luci/pass/src/ShapeInferencePass.cpp | 44 + compiler/luci/pass/src/TypeInferencePass.cpp | 42 + compiler/luci/requires.cmake | 9 + compiler/luci/service/CMakeLists.txt | 25 + compiler/luci/service/README.md | 3 + .../include/luci/Service/CircleShapeInference.h | 41 + .../luci/Service/CircleShapeInferenceRule.h | 33 + .../include/luci/Service/CircleTypeInference.h | 42 + .../include/luci/Service/CircleTypeInferenceRule.h | 36 + .../include/luci/Service/ShapeDescription.h | 59 + .../luci/service/include/luci/Service/Validate.h | 29 + compiler/luci/service/src/Check.h | 35 + compiler/luci/service/src/CircleShapeInference.cpp | 37 + .../luci/service/src/CircleShapeInferenceRule.cpp | 907 + .../service/src/CircleShapeInferenceRule.test.cpp | 282 + compiler/luci/service/src/CircleTypeInference.cpp | 78 + .../luci/service/src/CircleTypeInferenceRule.cpp | 202 + .../service/src/CircleTypeInferenceRule.test.cpp | 57 + compiler/luci/service/src/GraphBlock.h | 201 + compiler/luci/service/src/GraphBlock.test.cpp | 246 + compiler/luci/service/src/ShapeDescription.cpp | 139 + compiler/luci/service/src/TestGraph.h | 315 + compiler/luci/service/src/Validate.cpp | 109 + compiler/luci/tester/CMakeLists.txt | 22 + compiler/luci/tester/src/Model.cpp | 62 + compiler/luci/tester/src/Model.h | 27 + compiler/luci/tester/src/ReadTester.cpp | 92 + compiler/luci/tester/src/WriteTester.cpp | 142 + compiler/luci/tests/.gitignore | 1 + compiler/luci/tests/CMakeLists.txt | 97 + compiler/luci/tests/readverify.sh | 53 + compiler/luci/tests/test.lst | 91 + compiler/luci/tests/writeverify.sh | 53 + compiler/mio-circle/CMakeLists.txt | 28 + compiler/mio-circle/README.md | 3 + compiler/mio-circle/example.cpp | 41 + compiler/mio-tf/CMakeLists.txt | 48 + compiler/mio-tf/README.md | 3 + compiler/mio-tf/src/mio_tf.test.cpp | 27 + compiler/mio-tflite/CMakeLists.txt | 37 + compiler/mio-tflite/README.md | 3 + compiler/mio-tflite/example.cpp | 41 + compiler/mir-caffe-importer/CMakeLists.txt | 17 + compiler/mir-caffe-importer/caffe_importer.cpp | 439 + compiler/mir-caffe-importer/caffe_importer.h | 35 + compiler/mir-caffe-importer/caffe_op_creator.cpp | 834 + compiler/mir-caffe-importer/caffe_op_creator.h | 146 + compiler/mir-caffe-importer/caffe_op_types.h | 89 + compiler/mir-caffe-importer/requires.cmake | 1 + compiler/mir-caffe2-importer/CMakeLists.txt | 29 + compiler/mir-caffe2-importer/caffe2_importer.cpp | 343 + compiler/mir-caffe2-importer/caffe2_importer.h | 34 + compiler/mir-caffe2-importer/caffe2_op_creator.cpp | 547 + compiler/mir-caffe2-importer/caffe2_op_creator.h | 117 + compiler/mir-caffe2-importer/caffe2_op_types.h | 48 + .../mir-caffe2-importer/caffe2_proto_helper.cpp | 62 + compiler/mir-caffe2-importer/caffe2_proto_helper.h | 40 + compiler/mir-caffe2-importer/requires.cmake | 1 + compiler/mir-interpreter/CMakeLists.txt | 4 + compiler/mir-interpreter/include/MirInterpreter.h | 101 + compiler/mir-interpreter/requires.cmake | 1 + compiler/mir-interpreter/src/MirInterpreter.cpp | 420 + compiler/mir-interpreter/src/ops/Abs.cpp | 55 + compiler/mir-interpreter/src/ops/Abs.h | 29 + compiler/mir-interpreter/src/ops/Add.cpp | 130 + compiler/mir-interpreter/src/ops/Add.h | 29 + compiler/mir-interpreter/src/ops/AvgPool2D.cpp | 173 + compiler/mir-interpreter/src/ops/AvgPool2D.h | 31 + compiler/mir-interpreter/src/ops/CappedReLU.cpp | 82 + compiler/mir-interpreter/src/ops/CappedReLU.h | 29 + compiler/mir-interpreter/src/ops/Common.cpp | 37 + compiler/mir-interpreter/src/ops/Common.h | 65 + compiler/mir-interpreter/src/ops/Concat.cpp | 172 + compiler/mir-interpreter/src/ops/Concat.h | 30 + compiler/mir-interpreter/src/ops/Conv2D.cpp | 261 + compiler/mir-interpreter/src/ops/Conv2D.h | 32 + compiler/mir-interpreter/src/ops/DeConv2D.cpp | 122 + compiler/mir-interpreter/src/ops/DeConv2D.h | 41 + .../mir-interpreter/src/ops/DepthwiseConv2D.cpp | 225 + compiler/mir-interpreter/src/ops/DepthwiseConv2D.h | 32 + compiler/mir-interpreter/src/ops/Div.cpp | 62 + compiler/mir-interpreter/src/ops/Div.h | 29 + compiler/mir-interpreter/src/ops/ELU.cpp | 51 + compiler/mir-interpreter/src/ops/ELU.h | 29 + compiler/mir-interpreter/src/ops/Equal.cpp | 58 + compiler/mir-interpreter/src/ops/Equal.h | 29 + compiler/mir-interpreter/src/ops/Fill.h | 48 + .../mir-interpreter/src/ops/FullyConnected.cpp | 214 + compiler/mir-interpreter/src/ops/FullyConnected.h | 32 + compiler/mir-interpreter/src/ops/Gather.cpp | 92 + compiler/mir-interpreter/src/ops/Gather.h | 31 + compiler/mir-interpreter/src/ops/Greater.cpp | 57 + compiler/mir-interpreter/src/ops/Greater.h | 29 + compiler/mir-interpreter/src/ops/HardSwish.cpp | 54 + compiler/mir-interpreter/src/ops/HardSwish.h | 29 + compiler/mir-interpreter/src/ops/LeakyReLU.cpp | 49 + compiler/mir-interpreter/src/ops/LeakyReLU.h | 29 + compiler/mir-interpreter/src/ops/Less.cpp | 57 + compiler/mir-interpreter/src/ops/Less.h | 29 + compiler/mir-interpreter/src/ops/Max.cpp | 66 + compiler/mir-interpreter/src/ops/Max.h | 29 + compiler/mir-interpreter/src/ops/MaxPool2D.cpp | 157 + compiler/mir-interpreter/src/ops/MaxPool2D.h | 31 + compiler/mir-interpreter/src/ops/Mul.cpp | 61 + compiler/mir-interpreter/src/ops/Mul.h | 29 + compiler/mir-interpreter/src/ops/Pad.cpp | 84 + compiler/mir-interpreter/src/ops/Pad.h | 36 + compiler/mir-interpreter/src/ops/Quantization.cpp | 71 + compiler/mir-interpreter/src/ops/Quantization.h | 32 + .../mir-interpreter/src/ops/QuantizationHelpers.h | 126 + compiler/mir-interpreter/src/ops/ReLU.cpp | 58 + compiler/mir-interpreter/src/ops/ReLU.h | 29 + compiler/mir-interpreter/src/ops/ReduceMean.cpp | 98 + compiler/mir-interpreter/src/ops/ReduceMean.h | 30 + compiler/mir-interpreter/src/ops/Reshape.cpp | 38 + compiler/mir-interpreter/src/ops/Reshape.h | 29 + compiler/mir-interpreter/src/ops/Sigmoid.cpp | 58 + compiler/mir-interpreter/src/ops/Sigmoid.h | 29 + compiler/mir-interpreter/src/ops/Slice.cpp | 52 + compiler/mir-interpreter/src/ops/Slice.h | 29 + compiler/mir-interpreter/src/ops/Softmax.cpp | 155 + compiler/mir-interpreter/src/ops/Softmax.h | 29 + compiler/mir-interpreter/src/ops/Sqrt.cpp | 58 + compiler/mir-interpreter/src/ops/Sqrt.h | 29 + compiler/mir-interpreter/src/ops/Sub.cpp | 61 + compiler/mir-interpreter/src/ops/Sub.h | 29 + compiler/mir-interpreter/src/ops/Tanh.cpp | 58 + compiler/mir-interpreter/src/ops/Tanh.h | 29 + compiler/mir-interpreter/src/ops/Transpose.cpp | 64 + compiler/mir-interpreter/src/ops/Transpose.h | 31 + compiler/mir-onnx-importer/AttributeHelpers.h | 105 + compiler/mir-onnx-importer/CMakeLists.txt | 119 + compiler/mir-onnx-importer/ConvPoolHelpers.cpp | 113 + compiler/mir-onnx-importer/ConvPoolHelpers.h | 44 + compiler/mir-onnx-importer/ONNXHelpers.cpp | 188 + compiler/mir-onnx-importer/ONNXHelpers.h | 50 + compiler/mir-onnx-importer/ONNXImporterImpl.cpp | 241 + compiler/mir-onnx-importer/ONNXImporterImpl.h | 35 + .../ONNXNodeConverterRegistry.cpp | 142 + .../mir-onnx-importer/ONNXNodeConverterRegistry.h | 80 + .../ONNXNodeConverterRegistry.test.cpp | 64 + compiler/mir-onnx-importer/ONNXOpRegistration.h | 256 + compiler/mir-onnx-importer/Op/Abs.cpp | 47 + compiler/mir-onnx-importer/Op/Abs.h | 30 + compiler/mir-onnx-importer/Op/Add.cpp | 53 + compiler/mir-onnx-importer/Op/Add.h | 31 + compiler/mir-onnx-importer/Op/AveragePool.cpp | 99 + compiler/mir-onnx-importer/Op/AveragePool.h | 31 + .../mir-onnx-importer/Op/BatchNormalization.cpp | 119 + compiler/mir-onnx-importer/Op/BatchNormalization.h | 32 + compiler/mir-onnx-importer/Op/Concat.cpp | 54 + compiler/mir-onnx-importer/Op/Concat.h | 30 + compiler/mir-onnx-importer/Op/Constant.cpp | 61 + compiler/mir-onnx-importer/Op/Constant.h | 31 + compiler/mir-onnx-importer/Op/Conv.cpp | 156 + compiler/mir-onnx-importer/Op/Conv.h | 29 + compiler/mir-onnx-importer/Op/ConvTranspose.cpp | 138 + compiler/mir-onnx-importer/Op/ConvTranspose.h | 29 + compiler/mir-onnx-importer/Op/Div.cpp | 38 + compiler/mir-onnx-importer/Op/Div.h | 29 + compiler/mir-onnx-importer/Op/Dropout.cpp | 54 + compiler/mir-onnx-importer/Op/Dropout.h | 32 + compiler/mir-onnx-importer/Op/Equal.cpp | 43 + compiler/mir-onnx-importer/Op/Equal.h | 30 + compiler/mir-onnx-importer/Op/Expand.cpp | 43 + compiler/mir-onnx-importer/Op/Expand.h | 29 + compiler/mir-onnx-importer/Op/Flatten.cpp | 58 + compiler/mir-onnx-importer/Op/Flatten.h | 30 + compiler/mir-onnx-importer/Op/Gather.cpp | 40 + compiler/mir-onnx-importer/Op/Gather.h | 29 + compiler/mir-onnx-importer/Op/Gemm.cpp | 120 + compiler/mir-onnx-importer/Op/Gemm.h | 33 + .../mir-onnx-importer/Op/GlobalAveragePool.cpp | 50 + compiler/mir-onnx-importer/Op/GlobalAveragePool.h | 29 + compiler/mir-onnx-importer/Op/Greater.cpp | 47 + compiler/mir-onnx-importer/Op/Greater.h | 30 + compiler/mir-onnx-importer/Op/Identity.cpp | 30 + compiler/mir-onnx-importer/Op/Identity.h | 29 + compiler/mir-onnx-importer/Op/Less.cpp | 47 + compiler/mir-onnx-importer/Op/Less.h | 30 + compiler/mir-onnx-importer/Op/MatMul.cpp | 50 + compiler/mir-onnx-importer/Op/MatMul.h | 30 + compiler/mir-onnx-importer/Op/Max.cpp | 54 + compiler/mir-onnx-importer/Op/Max.h | 31 + compiler/mir-onnx-importer/Op/MaxPool.cpp | 107 + compiler/mir-onnx-importer/Op/MaxPool.h | 31 + compiler/mir-onnx-importer/Op/Mul.cpp | 35 + compiler/mir-onnx-importer/Op/Mul.h | 29 + compiler/mir-onnx-importer/Op/Pad.cpp | 70 + compiler/mir-onnx-importer/Op/Pad.h | 30 + compiler/mir-onnx-importer/Op/Reciprocal.cpp | 53 + compiler/mir-onnx-importer/Op/Reciprocal.h | 30 + compiler/mir-onnx-importer/Op/ReduceMean.cpp | 60 + compiler/mir-onnx-importer/Op/ReduceMean.h | 29 + compiler/mir-onnx-importer/Op/Relu.cpp | 46 + compiler/mir-onnx-importer/Op/Relu.h | 30 + compiler/mir-onnx-importer/Op/Reshape.cpp | 97 + compiler/mir-onnx-importer/Op/Reshape.h | 30 + compiler/mir-onnx-importer/Op/Shape.cpp | 46 + compiler/mir-onnx-importer/Op/Shape.h | 29 + compiler/mir-onnx-importer/Op/Sigmoid.cpp | 46 + compiler/mir-onnx-importer/Op/Sigmoid.h | 30 + compiler/mir-onnx-importer/Op/Softmax.cpp | 40 + compiler/mir-onnx-importer/Op/Softmax.h | 29 + compiler/mir-onnx-importer/Op/Sqrt.cpp | 46 + compiler/mir-onnx-importer/Op/Sqrt.h | 30 + compiler/mir-onnx-importer/Op/Sub.cpp | 53 + compiler/mir-onnx-importer/Op/Sub.h | 31 + compiler/mir-onnx-importer/Op/Sum.cpp | 41 + compiler/mir-onnx-importer/Op/Sum.h | 29 + compiler/mir-onnx-importer/Op/Tanh.cpp | 46 + compiler/mir-onnx-importer/Op/Tanh.h | 30 + compiler/mir-onnx-importer/Op/Transpose.cpp | 57 + compiler/mir-onnx-importer/Op/Transpose.h | 29 + compiler/mir-onnx-importer/Op/Unsqueeze.cpp | 56 + compiler/mir-onnx-importer/Op/Unsqueeze.h | 29 + compiler/mir-onnx-importer/Op/Upsample.cpp | 124 + compiler/mir-onnx-importer/Op/Upsample.h | 31 + compiler/mir-onnx-importer/requires.cmake | 2 + compiler/mir-tflite-importer/CMakeLists.txt | 22 + compiler/mir-tflite-importer/requires.cmake | 1 + compiler/mir-tflite-importer/schema/schema.fbs | 937 + compiler/mir-tflite-importer/schema/schema.meta | 2 + compiler/mir-tflite-importer/schema/schema_v0.fbs | 247 + compiler/mir-tflite-importer/schema/schema_v0.meta | 2 + compiler/mir-tflite-importer/schema/schema_v1.fbs | 295 + compiler/mir-tflite-importer/schema/schema_v1.meta | 2 + compiler/mir-tflite-importer/schema/schema_v2.fbs | 303 + compiler/mir-tflite-importer/schema/schema_v2.meta | 2 + compiler/mir-tflite-importer/schema/schema_v3.fbs | 326 + compiler/mir-tflite-importer/schema/schema_v3.meta | 2 + compiler/mir-tflite-importer/tflite_importer.cpp | 428 + compiler/mir-tflite-importer/tflite_importer.h | 32 + compiler/mir-tflite-importer/tflite_op_creator.cpp | 649 + compiler/mir-tflite-importer/tflite_op_creator.h | 163 + compiler/mir/CMakeLists.txt | 38 + compiler/mir/Readme.md | 36 + compiler/mir/include/mir/Attributes.h | 83 + compiler/mir/include/mir/Common.h | 36 + compiler/mir/include/mir/DataFormat.h | 90 + compiler/mir/include/mir/DataType.h | 58 + compiler/mir/include/mir/ExternalRegion.h | 44 + compiler/mir/include/mir/Graph.h | 111 + compiler/mir/include/mir/GraphPatternMatcher.h | 55 + compiler/mir/include/mir/Index.h | 81 + compiler/mir/include/mir/IrDotDumper.h | 31 + compiler/mir/include/mir/OpDefs.h | 60 + compiler/mir/include/mir/Operation.h | 188 + compiler/mir/include/mir/Operations.inc | 59 + compiler/mir/include/mir/Quantization.h | 47 + compiler/mir/include/mir/Region.h | 37 + compiler/mir/include/mir/Shape.h | 75 + compiler/mir/include/mir/ShapeRange.h | 109 + compiler/mir/include/mir/Tensor.h | 62 + compiler/mir/include/mir/TensorType.h | 56 + compiler/mir/include/mir/TensorUtil.h | 59 + compiler/mir/include/mir/TensorVariant.h | 86 + compiler/mir/include/mir/Visitor.h | 63 + compiler/mir/include/mir/ops/AbsOp.h | 45 + compiler/mir/include/mir/ops/AddOp.h | 41 + compiler/mir/include/mir/ops/AvgPool2DOp.h | 68 + compiler/mir/include/mir/ops/BinaryElementwiseOp.h | 42 + compiler/mir/include/mir/ops/BroadcastOp.h | 47 + compiler/mir/include/mir/ops/CappedReluOp.h | 50 + compiler/mir/include/mir/ops/ConcatOp.h | 67 + compiler/mir/include/mir/ops/ConstantOp.h | 52 + compiler/mir/include/mir/ops/Conv2DOp.h | 73 + compiler/mir/include/mir/ops/Deconv2DOp.h | 84 + compiler/mir/include/mir/ops/DepthwiseConv2DOp.h | 72 + compiler/mir/include/mir/ops/DequantizeOp.h | 46 + compiler/mir/include/mir/ops/DivOp.h | 41 + compiler/mir/include/mir/ops/EluOp.h | 49 + compiler/mir/include/mir/ops/EqualOp.h | 44 + compiler/mir/include/mir/ops/FullyConnectedOp.h | 58 + compiler/mir/include/mir/ops/GatherOp.h | 57 + compiler/mir/include/mir/ops/GreaterOp.h | 44 + compiler/mir/include/mir/ops/HardSwishOp.h | 44 + compiler/mir/include/mir/ops/InputOp.h | 49 + compiler/mir/include/mir/ops/LeakyReluOp.h | 50 + compiler/mir/include/mir/ops/LessOp.h | 44 + compiler/mir/include/mir/ops/MaxOp.h | 41 + compiler/mir/include/mir/ops/MaxPool2DOp.h | 66 + compiler/mir/include/mir/ops/MulOp.h | 41 + compiler/mir/include/mir/ops/OutputOp.h | 41 + compiler/mir/include/mir/ops/PadOp.h | 58 + compiler/mir/include/mir/ops/PaddingType.h | 37 + compiler/mir/include/mir/ops/QuantizeOp.h | 44 + compiler/mir/include/mir/ops/ReduceMeanOp.h | 45 + compiler/mir/include/mir/ops/ReduceOp.h | 52 + compiler/mir/include/mir/ops/ReluOp.h | 45 + compiler/mir/include/mir/ops/ReshapeOp.h | 64 + compiler/mir/include/mir/ops/ResizeOp.h | 87 + compiler/mir/include/mir/ops/SigmoidOp.h | 45 + compiler/mir/include/mir/ops/SliceOp.h | 55 + compiler/mir/include/mir/ops/SoftmaxOp.h | 64 + compiler/mir/include/mir/ops/SqrtOp.h | 44 + compiler/mir/include/mir/ops/SqueezeOp.h | 56 + compiler/mir/include/mir/ops/SubOp.h | 41 + compiler/mir/include/mir/ops/TanhOp.h | 45 + compiler/mir/include/mir/ops/TransposeOp.h | 54 + compiler/mir/src/DotGraph.cpp | 41 + compiler/mir/src/DotGraph.h | 55 + compiler/mir/src/DotNodeBuilder.cpp | 205 + compiler/mir/src/DotNodeBuilder.h | 70 + compiler/mir/src/Graph.cpp | 136 + compiler/mir/src/GraphPatternMatcher.cpp | 76 + compiler/mir/src/Index.cpp | 50 + compiler/mir/src/IrDotDumper.cpp | 41 + compiler/mir/src/Operation.cpp | 87 + compiler/mir/src/Shape.cpp | 87 + compiler/mir/src/Tensor.cpp | 26 + compiler/mir/src/TensorVariant.cpp | 74 + compiler/mir/src/Visitor.cpp | 29 + compiler/mir/src/ops/AvgPool2DOp.cpp | 60 + compiler/mir/src/ops/BinaryElementwiseOp.cpp | 39 + compiler/mir/src/ops/BroadcastOp.cpp | 35 + compiler/mir/src/ops/ConcatOp.cpp | 40 + compiler/mir/src/ops/Conv2DOp.cpp | 67 + compiler/mir/src/ops/DeConv2DOp.cpp | 96 + compiler/mir/src/ops/DepthwiseConv2DOp.cpp | 60 + compiler/mir/src/ops/FullyConnectedOp.cpp | 46 + compiler/mir/src/ops/GatherOp.cpp | 52 + compiler/mir/src/ops/MaxPool2DOp.cpp | 60 + compiler/mir/src/ops/PadOp.cpp | 40 + compiler/mir/src/ops/ReduceOp.cpp | 61 + compiler/mir/src/ops/SliceOp.cpp | 45 + compiler/mir/src/ops/SqueezeOp.cpp | 81 + compiler/mir/src/ops/TransposeOp.cpp | 43 + compiler/mir/unittests/CMakeLists.txt | 16 + compiler/mir/unittests/Index.cpp | 63 + compiler/mir/unittests/NodeReplacer.cpp | 65 + compiler/mir/unittests/Operation.cpp | 93 + compiler/mir/unittests/ShapeInference.cpp | 203 + compiler/mir/unittests/ShapeRange.cpp | 78 + compiler/mir/unittests/TensorVariant.cpp | 39 + compiler/mir2loco/CMakeLists.txt | 19 + compiler/mir2loco/include/mir2loco.h | 58 + compiler/mir2loco/requires.cmake | 2 + compiler/mir2loco/src/mir2loco.cpp | 725 + compiler/mir2loco/src/mir2loco.test.cpp | 736 + compiler/moco-log/CMakeLists.txt | 9 + compiler/moco-log/README.md | 3 + compiler/moco-log/include/moco/Log.h | 75 + compiler/moco-log/include/moco/LoggingContext.h | 35 + compiler/moco-log/requires.cmake | 2 + compiler/moco-log/src/Log.cpp | 87 + compiler/moco-log/src/LoggingContext.cpp | 40 + compiler/moco-tf/CMakeLists.txt | 51 + compiler/moco-tf/README.md | 57 + compiler/moco-tf/doc/Conversion.md | 140 + compiler/moco-tf/include/moco/tf/Frontend.h | 54 + compiler/moco-tf/requires.cmake | 13 + compiler/moco-tf/src/BroadcastHelper.cpp | 226 + compiler/moco-tf/src/BroadcastHelper.h | 76 + compiler/moco-tf/src/BroadcastHelper.test.cpp | 88 + .../moco-tf/src/CanonicalEltwiseInputConnector.cpp | 49 + .../moco-tf/src/CanonicalEltwiseInputConnector.h | 60 + .../src/Canonicalization/AddCanonicalizer.cpp | 35 + .../src/Canonicalization/AddCanonicalizer.h | 47 + .../src/Canonicalization/AvgPoolCanonicalizer.cpp | 114 + .../src/Canonicalization/AvgPoolCanonicalizer.h | 47 + .../src/Canonicalization/BiasAddCanonicalizer.cpp | 109 + .../src/Canonicalization/BiasAddCanonicalizer.h | 47 + .../src/Canonicalization/ConcatV2Canonicalizer.cpp | 160 + .../src/Canonicalization/ConcatV2Canonicalizer.h | 47 + .../src/Canonicalization/ConstCanonicalizer.cpp | 127 + .../src/Canonicalization/ConstCanonicalizer.h | 47 + .../Conv2DBackpropInputCanonicalizer.cpp | 371 + .../Conv2DBackpropInputCanonicalizer.h | 45 + .../src/Canonicalization/Conv2DCanonicalizer.cpp | 132 + .../src/Canonicalization/Conv2DCanonicalizer.h | 47 + .../DepthwiseConv2dNativeCanonicalizer.cpp | 137 + .../DepthwiseConv2dNativeCanonicalizer.h | 45 + .../src/Canonicalization/IdentityCanonicalizer.cpp | 78 + .../src/Canonicalization/IdentityCanonicalizer.h | 47 + .../src/Canonicalization/MaxPoolCanonicalizer.cpp | 111 + .../src/Canonicalization/MaxPoolCanonicalizer.h | 47 + .../src/Canonicalization/MaximumCanonicalizer.cpp | 34 + .../src/Canonicalization/MaximumCanonicalizer.h | 47 + .../src/Canonicalization/MeanCanonicalizer.cpp | 31 + .../src/Canonicalization/MeanCanonicalizer.h | 47 + .../src/Canonicalization/MulCanonicalizer.cpp | 34 + .../src/Canonicalization/MulCanonicalizer.h | 47 + .../src/Canonicalization/PadCanonicalizer.cpp | 100 + .../src/Canonicalization/PadCanonicalizer.h | 45 + .../Canonicalization/PlaceholderCanonicalizer.cpp | 102 + .../Canonicalization/PlaceholderCanonicalizer.h | 47 + .../src/Canonicalization/RealDivCanonicalizer.cpp | 34 + .../src/Canonicalization/RealDivCanonicalizer.h | 47 + .../src/Canonicalization/Relu6Canonicalizer.cpp | 70 + .../src/Canonicalization/Relu6Canonicalizer.h | 47 + .../src/Canonicalization/ReluCanonicalizer.cpp | 70 + .../src/Canonicalization/ReluCanonicalizer.h | 47 + .../src/Canonicalization/ReshapeCanonicalizer.cpp | 169 + .../src/Canonicalization/ReshapeCanonicalizer.h | 47 + .../src/Canonicalization/RsqrtCanonicalizer.cpp | 150 + .../src/Canonicalization/RsqrtCanonicalizer.h | 47 + .../src/Canonicalization/SoftmaxCanonicalizer.cpp | 78 + .../src/Canonicalization/SoftmaxCanonicalizer.h | 47 + .../src/Canonicalization/SqrtCanonicalizer.cpp | 68 + .../src/Canonicalization/SqrtCanonicalizer.h | 47 + .../src/Canonicalization/SqueezeCanonicalizer.cpp | 86 + .../src/Canonicalization/SqueezeCanonicalizer.h | 49 + .../Canonicalization/StopGradientCanonicalizer.cpp | 71 + .../Canonicalization/StopGradientCanonicalizer.h | 47 + .../src/Canonicalization/SubCanonicalizer.cpp | 34 + .../src/Canonicalization/SubCanonicalizer.h | 47 + .../src/Canonicalization/TFPushCanonicalizer.cpp | 74 + .../src/Canonicalization/TFPushCanonicalizer.h | 47 + .../src/Canonicalization/TanhCanonicalizer.cpp | 70 + .../src/Canonicalization/TanhCanonicalizer.h | 47 + compiler/moco-tf/src/Canonicalizer.cpp | 142 + compiler/moco-tf/src/Canonicalizer.h | 36 + compiler/moco-tf/src/Canonicalizer.test.cpp | 33 + compiler/moco-tf/src/CodecHelper.h | 74 + compiler/moco-tf/src/Convert.cpp | 34 + compiler/moco-tf/src/Convert.h | 31 + compiler/moco-tf/src/Convert.test.cpp | 29 + compiler/moco-tf/src/Frontend.cpp | 277 + compiler/moco-tf/src/Frontend.test.cpp | 84 + compiler/moco-tf/src/Knob.cpp | 123 + compiler/moco-tf/src/Knob.h | 47 + compiler/moco-tf/src/Knob.lst | 39 + compiler/moco-tf/src/LogHelper.cpp | 82 + compiler/moco-tf/src/LogHelper.h | 73 + compiler/moco-tf/src/Op/COpCall.cpp | 126 + compiler/moco-tf/src/Op/COpCall.h | 46 + compiler/moco-tf/src/Op/COpCall.test.cpp | 121 + compiler/moco-tf/src/Optimizer.cpp | 90 + compiler/moco-tf/src/Optimizer.h | 36 + compiler/moco-tf/src/Optimizer.test.cpp | 87 + compiler/moco-tf/src/ProgressReporter.cpp | 88 + compiler/moco-tf/src/ProgressReporter.h | 56 + compiler/moco-tf/src/SimpleNodeTransform.h | 64 + compiler/moco-tf/src/SimpleNodeTransform.test.cpp | 56 + .../moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h | 117 + compiler/moco-tf/src/TFFormattedGraph.cpp | 400 + compiler/moco-tf/src/TFFormattedGraph.h | 59 + compiler/moco-tf/src/TFOptimizer.cpp | 81 + compiler/moco-tf/src/TFOptimizer.h | 36 + compiler/moco-tf/src/TFOptimizer.test.cpp | 33 + compiler/moco-tf/src/TFReduceCanonicalzeHelper.h | 118 + compiler/moco-tf/src/TestHelper.h | 113 + compiler/moco-tf/src/TestHelper.test.cpp | 121 + compiler/moco-tf/src/Transform.cpp | 35 + compiler/moco-tf/src/Transform.h | 44 + compiler/moco-tf/src/Transform.test.cpp | 46 + compiler/moco-tf/src/Transforms.h | 26 + .../moco-tf/src/Transforms/ShapeInferencePass.cpp | 56 + .../moco-tf/src/Transforms/ShapeInferencePass.h | 44 + .../moco-tf/src/Transforms/TypeInferencePass.cpp | 54 + .../moco-tf/src/Transforms/TypeInferencePass.h | 44 + compiler/moco-value-pbtxt-test/.gitignore | 1 + compiler/moco-value-pbtxt-test/CMakeLists.txt | 136 + compiler/moco-value-pbtxt-test/README.md | 1 + compiler/moco-value-pbtxt-test/requires.cmake | 2 + compiler/moco-value-pbtxt-test/runall.sh | 96 + compiler/moco-value-pbtxt-test/test.lst | 103 + compiler/moco/CMakeLists.txt | 5 + compiler/moco/README.md | 3 + compiler/moco/import/CMakeLists.txt | 26 + compiler/moco/import/README.md | 3 + compiler/moco/import/include/moco/GraphHelper.h | 59 + .../moco/import/include/moco/Import/GraphBuilder.h | 40 + .../include/moco/Import/GraphBuilderContext.h | 144 + .../include/moco/Import/GraphBuilderRegistry.h | 87 + .../import/include/moco/Import/ModelSignature.h | 80 + compiler/moco/import/include/moco/Import/Nodes.h | 53 + .../moco/import/include/moco/Import/Nodes/Add.h | 37 + .../import/include/moco/Import/Nodes/AvgPool.h | 34 + .../import/include/moco/Import/Nodes/BiasAdd.h | 34 + .../moco/import/include/moco/Import/Nodes/Concat.h | 34 + .../moco/import/include/moco/Import/Nodes/Const.h | 34 + .../moco/import/include/moco/Import/Nodes/Conv2D.h | 34 + .../moco/Import/Nodes/Conv2DBackpropInput.h | 37 + .../moco/Import/Nodes/DepthwiseConv2dNative.h | 37 + .../moco/Import/Nodes/FakeQuantWithMinMaxVars.h | 37 + .../include/moco/Import/Nodes/FusedBatchNorm.h | 37 + .../import/include/moco/Import/Nodes/Identity.h | 34 + .../import/include/moco/Import/Nodes/MaxPool.h | 34 + .../import/include/moco/Import/Nodes/Maximum.h | 37 + .../moco/import/include/moco/Import/Nodes/Mean.h | 37 + .../moco/import/include/moco/Import/Nodes/Mul.h | 37 + .../moco/import/include/moco/Import/Nodes/Pack.h | 34 + .../moco/import/include/moco/Import/Nodes/Pad.h | 37 + .../import/include/moco/Import/Nodes/Placeholder.h | 37 + .../import/include/moco/Import/Nodes/RealDiv.h | 37 + .../moco/import/include/moco/Import/Nodes/Relu.h | 37 + .../moco/import/include/moco/Import/Nodes/Relu6.h | 37 + .../import/include/moco/Import/Nodes/Reshape.h | 37 + .../moco/import/include/moco/Import/Nodes/Rsqrt.h | 37 + .../moco/import/include/moco/Import/Nodes/Shape.h | 37 + .../import/include/moco/Import/Nodes/Softmax.h | 37 + .../moco/import/include/moco/Import/Nodes/Sqrt.h | 37 + .../include/moco/Import/Nodes/SquaredDifference.h | 37 + .../import/include/moco/Import/Nodes/Squeeze.h | 37 + .../include/moco/Import/Nodes/StopGradient.h | 37 + .../include/moco/Import/Nodes/StridedSlice.h | 34 + .../moco/import/include/moco/Import/Nodes/Sub.h | 37 + .../moco/import/include/moco/Import/Nodes/Tanh.h | 37 + compiler/moco/import/include/moco/Importer.h | 54 + compiler/moco/import/src/Convert.cpp | 34 + compiler/moco/import/src/Convert.h | 31 + compiler/moco/import/src/GraphBuilderContext.cpp | 80 + .../moco/import/src/GraphBuilderContext.test.cpp | 77 + compiler/moco/import/src/GraphBuilderRegistry.cpp | 63 + compiler/moco/import/src/Importer.cpp | 197 + compiler/moco/import/src/Importer.test.cpp | 223 + compiler/moco/import/src/ModelSignature.cpp | 66 + compiler/moco/import/src/Nodes/Add.cpp | 85 + compiler/moco/import/src/Nodes/Add.test.cpp | 58 + compiler/moco/import/src/Nodes/AvgPool.cpp | 140 + compiler/moco/import/src/Nodes/AvgPool.test.cpp | 99 + compiler/moco/import/src/Nodes/BiasAdd.cpp | 122 + compiler/moco/import/src/Nodes/BiasAdd.test.cpp | 112 + compiler/moco/import/src/Nodes/Concat.cpp | 109 + compiler/moco/import/src/Nodes/Concat.test.cpp | 134 + compiler/moco/import/src/Nodes/Const.cpp | 242 + compiler/moco/import/src/Nodes/Const.test.cpp | 465 + compiler/moco/import/src/Nodes/Conv2D.cpp | 139 + compiler/moco/import/src/Nodes/Conv2D.test.cpp | 119 + .../moco/import/src/Nodes/Conv2DBackpropInput.cpp | 140 + .../import/src/Nodes/Conv2DBackpropInput.test.cpp | 98 + .../import/src/Nodes/DepthwiseConv2dNative.cpp | 148 + .../src/Nodes/DepthwiseConv2dNative.test.cpp | 97 + .../import/src/Nodes/FakeQuantWithMinMaxVars.cpp | 123 + .../src/Nodes/FakeQuantWithMinMaxVars.test.cpp | 65 + compiler/moco/import/src/Nodes/FusedBatchNorm.cpp | 102 + .../moco/import/src/Nodes/FusedBatchNorm.test.cpp | 88 + compiler/moco/import/src/Nodes/Identity.cpp | 95 + compiler/moco/import/src/Nodes/MaxPool.cpp | 145 + compiler/moco/import/src/Nodes/MaxPool.test.cpp | 98 + compiler/moco/import/src/Nodes/Maximum.cpp | 87 + compiler/moco/import/src/Nodes/Maximum.test.cpp | 58 + compiler/moco/import/src/Nodes/Mean.cpp | 99 + compiler/moco/import/src/Nodes/Mean.test.cpp | 120 + compiler/moco/import/src/Nodes/Mul.cpp | 85 + compiler/moco/import/src/Nodes/Mul.test.cpp | 58 + compiler/moco/import/src/Nodes/Pack.cpp | 102 + compiler/moco/import/src/Nodes/Pack.test.cpp | 84 + compiler/moco/import/src/Nodes/Pad.cpp | 91 + compiler/moco/import/src/Nodes/Pad.test.cpp | 65 + compiler/moco/import/src/Nodes/Placeholder.cpp | 90 + .../moco/import/src/Nodes/Placeholder.test.cpp | 71 + compiler/moco/import/src/Nodes/RealDiv.cpp | 86 + compiler/moco/import/src/Nodes/RealDiv.test.cpp | 58 + compiler/moco/import/src/Nodes/Relu.cpp | 86 + compiler/moco/import/src/Nodes/Relu.test.cpp | 58 + compiler/moco/import/src/Nodes/Relu6.cpp | 80 + compiler/moco/import/src/Nodes/Relu6.test.cpp | 58 + compiler/moco/import/src/Nodes/Reshape.cpp | 102 + compiler/moco/import/src/Nodes/Reshape.test.cpp | 61 + compiler/moco/import/src/Nodes/Rsqrt.cpp | 82 + compiler/moco/import/src/Nodes/Rsqrt.test.cpp | 57 + compiler/moco/import/src/Nodes/Shape.cpp | 100 + compiler/moco/import/src/Nodes/Shape.test.cpp | 65 + compiler/moco/import/src/Nodes/Softmax.cpp | 86 + compiler/moco/import/src/Nodes/Softmax.test.cpp | 58 + compiler/moco/import/src/Nodes/Sqrt.cpp | 81 + compiler/moco/import/src/Nodes/Sqrt.test.cpp | 57 + .../moco/import/src/Nodes/SquaredDifference.cpp | 92 + .../import/src/Nodes/SquaredDifference.test.cpp | 59 + compiler/moco/import/src/Nodes/Squeeze.cpp | 112 + compiler/moco/import/src/Nodes/Squeeze.test.cpp | 109 + compiler/moco/import/src/Nodes/StopGradient.cpp | 87 + .../moco/import/src/Nodes/StopGradient.test.cpp | 57 + compiler/moco/import/src/Nodes/StridedSlice.cpp | 187 + .../moco/import/src/Nodes/StridedSlice.test.cpp | 107 + compiler/moco/import/src/Nodes/Sub.cpp | 85 + compiler/moco/import/src/Nodes/Sub.test.cpp | 58 + compiler/moco/import/src/Nodes/Tanh.cpp | 81 + compiler/moco/import/src/Nodes/Tanh.test.cpp | 57 + compiler/moco/import/src/TestHelper.h | 83 + compiler/moco/import/src/TestHelper.test.cpp | 101 + compiler/moco/lang/CMakeLists.txt | 21 + compiler/moco/lang/README.md | 3 + compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h | 56 + .../moco/lang/include/moco/IR/Nodes/TFAvgPool.h | 101 + .../moco/lang/include/moco/IR/Nodes/TFBiasAdd.h | 68 + .../moco/lang/include/moco/IR/Nodes/TFConcatV2.h | 90 + compiler/moco/lang/include/moco/IR/Nodes/TFConst.h | 94 + .../moco/lang/include/moco/IR/Nodes/TFConv2D.h | 55 + .../include/moco/IR/Nodes/TFConv2DBackpropInput.h | 102 + .../moco/IR/Nodes/TFDepthwiseConv2dNative.h | 56 + .../moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h | 54 + .../lang/include/moco/IR/Nodes/TFFusedBatchNorm.h | 55 + .../moco/lang/include/moco/IR/Nodes/TFIdentity.h | 52 + .../moco/lang/include/moco/IR/Nodes/TFMaxPool.h | 101 + .../moco/lang/include/moco/IR/Nodes/TFMaximum.h | 56 + compiler/moco/lang/include/moco/IR/Nodes/TFMean.h | 71 + compiler/moco/lang/include/moco/IR/Nodes/TFMul.h | 56 + compiler/moco/lang/include/moco/IR/Nodes/TFPack.h | 86 + compiler/moco/lang/include/moco/IR/Nodes/TFPad.h | 61 + .../lang/include/moco/IR/Nodes/TFPlaceholder.h | 90 + compiler/moco/lang/include/moco/IR/Nodes/TFPush.h | 84 + .../moco/lang/include/moco/IR/Nodes/TFRealDiv.h | 56 + compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h | 52 + compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h | 50 + .../moco/lang/include/moco/IR/Nodes/TFReshape.h | 54 + compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h | 52 + compiler/moco/lang/include/moco/IR/Nodes/TFShape.h | 60 + .../moco/lang/include/moco/IR/Nodes/TFSoftmax.h | 37 + compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h | 52 + .../include/moco/IR/Nodes/TFSquaredDifference.h | 56 + .../moco/lang/include/moco/IR/Nodes/TFSqueeze.h | 71 + .../lang/include/moco/IR/Nodes/TFStopGradient.h | 52 + .../lang/include/moco/IR/Nodes/TFStridedSlice.h | 123 + compiler/moco/lang/include/moco/IR/Nodes/TFSub.h | 56 + compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h | 37 + compiler/moco/lang/include/moco/IR/TFDataLayout.h | 29 + compiler/moco/lang/include/moco/IR/TFDialect.h | 43 + compiler/moco/lang/include/moco/IR/TFNode.h | 23 + compiler/moco/lang/include/moco/IR/TFNodeDecl.h | 104 + compiler/moco/lang/include/moco/IR/TFNodeImpl.h | 68 + .../lang/include/moco/IR/TFNodeVisitor.forward.h | 30 + compiler/moco/lang/include/moco/IR/TFNodeVisitor.h | 83 + compiler/moco/lang/include/moco/IR/TFNodes.h | 55 + compiler/moco/lang/include/moco/IR/TFNodes.lst | 48 + compiler/moco/lang/include/moco/IR/TFOpcode.h | 35 + compiler/moco/lang/include/moco/IR/TFPadding.h | 29 + .../moco/lang/include/moco/IR/VariadicArityNode.h | 77 + compiler/moco/lang/include/moco/Names.h | 96 + compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp | 34 + compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp | 32 + .../moco/lang/src/IR/Nodes/TFConcatV2.test.cpp | 34 + compiler/moco/lang/src/IR/Nodes/TFConst.cpp | 113 + compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp | 95 + compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp | 34 + .../src/IR/Nodes/TFConv2DBackpropInput.test.cpp | 35 + .../src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp | 34 + .../IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp | 34 + .../lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp | 35 + .../moco/lang/src/IR/Nodes/TFIdentity.test.cpp | 30 + compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp | 34 + compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp | 32 + compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp | 34 + compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp | 31 + .../moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp | 46 + compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp | 30 + compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp | 30 + compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp | 30 + compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp | 30 + compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp | 30 + .../lang/src/IR/Nodes/TFSquaredDifference.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp | 31 + .../moco/lang/src/IR/Nodes/TFStopGradient.test.cpp | 30 + .../moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp | 38 + compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp | 31 + compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp | 30 + compiler/moco/lang/src/IR/TFDialect.cpp | 91 + compiler/moco/lang/src/IR/TFDialect.test.cpp | 29 + compiler/moco/lang/src/IR/TFNode.cpp | 137 + compiler/moco/lang/src/IR/TFNode.test.cpp | 44 + .../moco/lang/src/IR/VariadicArityNode.test.cpp | 55 + compiler/moco/pass/CMakeLists.txt | 26 + compiler/moco/pass/README.md | 3 + compiler/moco/pass/include/moco/Pass/Passes.h | 32 + .../include/moco/Pass/Passes/ConstantFoldAdd.h | 41 + .../include/moco/Pass/Passes/ConstantFoldMul.h | 41 + .../include/moco/Pass/Passes/ConstantFoldPack.h | 43 + .../moco/Pass/Passes/ConstantFoldStridedSlice.h | 41 + .../moco/Pass/Passes/FuseBinaryIntoPreceding.h | 41 + .../moco/Pass/Passes/RemoveTFIdentityNode.h | 49 + .../moco/Pass/Passes/ResolveConstantShape.h | 41 + .../moco/Pass/Passes/ResolveFusedBatchNorm.h | 41 + .../moco/Pass/Passes/ResolveReshapeWildcardDim.h | 42 + .../moco/Pass/Passes/ResolveSquaredDifference.h | 41 + .../include/moco/Pass/Passes/SqueezeReduceNode.h | 42 + compiler/moco/pass/src/ConstantFoldAdd.test.cpp | 109 + compiler/moco/pass/src/ConstantFoldHelper.cpp | 238 + compiler/moco/pass/src/ConstantFoldHelper.h | 64 + compiler/moco/pass/src/ConstantFoldMul.test.cpp | 109 + compiler/moco/pass/src/ConstantFoldPack.test.cpp | 90 + .../pass/src/ConstantFoldStridedSlice.test.cpp | 268 + compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp | 116 + compiler/moco/pass/src/Passes/ConstantFoldMul.cpp | 116 + compiler/moco/pass/src/Passes/ConstantFoldPack.cpp | 191 + .../pass/src/Passes/ConstantFoldStridedSlice.cpp | 292 + .../pass/src/Passes/FuseBinaryIntoPreceding.cpp | 539 + .../moco/pass/src/Passes/RemoveTFIdentityNode.cpp | 66 + .../moco/pass/src/Passes/ResolveConstantShape.cpp | 129 + .../moco/pass/src/Passes/ResolveFusedBatchNorm.cpp | 254 + .../pass/src/Passes/ResolveReshapeWildcardDim.cpp | 153 + .../pass/src/Passes/ResolveSquaredDifference.cpp | 97 + .../moco/pass/src/Passes/SqueezeReduceNode.cpp | 105 + compiler/moco/pass/src/TensorPackEnumerator.cpp | 134 + compiler/moco/pass/src/TensorPackEnumerator.h | 66 + compiler/moco/pass/src/TensorSliceEnumerator.cpp | 84 + compiler/moco/pass/src/TensorSliceEnumerator.h | 62 + .../moco/pass/src/TensorSliceEnumerator.test.cpp | 83 + compiler/moco/pass/src/TestHelper.h | 67 + compiler/moco/pass/src/TestHelper.test.cpp | 38 + compiler/moco/requires.cmake | 8 + compiler/moco/service/CMakeLists.txt | 24 + compiler/moco/service/README.md | 3 + .../include/moco/Service/TFShapeInferenceRule.h | 38 + .../include/moco/Service/TFTypeInferenceRule.h | 36 + .../service/src/Service/TFShapeInferenceRule.cpp | 891 + .../src/Service/TFShapeInferenceRule.test.cpp | 500 + .../service/src/Service/TFTypeInferenceRule.cpp | 113 + compiler/moco/service/src/TestHelper.h | 69 + compiler/moco/service/src/TestHelper.test.cpp | 38 + compiler/moco/support/CMakeLists.txt | 9 + compiler/moco/support/README.md | 3 + .../moco/support/include/moco/Support/NodeAs.h | 29 + .../include/moco/Support/TFShapeInferenceHelper.h | 221 + .../moco/support/src/TFShapeInferenceHelper.cpp | 354 + compiler/morph/CMakeLists.txt | 20 + compiler/morph/README.md | 3 + compiler/morph/include/morph/caffe.h | 38 + compiler/morph/include/morph/dims.h | 33 + compiler/morph/include/morph/nnapi.h | 38 + compiler/morph/include/morph/tflite.h | 38 + compiler/morph/requires.cmake | 1 + compiler/morph/src/caffe.cpp | 68 + compiler/morph/src/caffe.test.cpp | 65 + compiler/morph/src/dims.cpp | 36 + compiler/morph/src/dims.test.cpp | 32 + compiler/morph/src/nnapi.cpp | 68 + compiler/morph/src/nnapi.test.cpp | 65 + compiler/morph/src/tflite.cpp | 68 + compiler/morph/src/tflite.test.cpp | 65 + compiler/nest/CMakeLists.txt | 1 + compiler/nest/README.md | 8 + compiler/nest/core/CMakeLists.txt | 27 + compiler/nest/core/examples/conv2d.cpp | 152 + compiler/nest/core/include/nest/Block.h | 46 + compiler/nest/core/include/nest/Bound.h | 47 + compiler/nest/core/include/nest/Closure.h | 49 + compiler/nest/core/include/nest/Domain.h | 54 + compiler/nest/core/include/nest/DomainContext.h | 45 + compiler/nest/core/include/nest/DomainID.h | 54 + compiler/nest/core/include/nest/DomainInfo.h | 48 + compiler/nest/core/include/nest/Expr.h | 44 + compiler/nest/core/include/nest/FV.h | 39 + compiler/nest/core/include/nest/Level.h | 45 + compiler/nest/core/include/nest/Module.h | 70 + compiler/nest/core/include/nest/Ret.h | 49 + compiler/nest/core/include/nest/Schedule.h | 52 + compiler/nest/core/include/nest/Stmt.h | 34 + compiler/nest/core/include/nest/Var.h | 49 + compiler/nest/core/include/nest/VarContext.h | 46 + compiler/nest/core/include/nest/VarID.h | 54 + compiler/nest/core/include/nest/expr/AddNode.h | 53 + compiler/nest/core/include/nest/expr/DerefNode.h | 54 + compiler/nest/core/include/nest/expr/Forward.h | 34 + compiler/nest/core/include/nest/expr/Macro.h | 28 + compiler/nest/core/include/nest/expr/MulNode.h | 53 + compiler/nest/core/include/nest/expr/Node.def | 9 + compiler/nest/core/include/nest/expr/Node.h | 62 + compiler/nest/core/include/nest/expr/Subscript.h | 53 + compiler/nest/core/include/nest/expr/VarNode.h | 52 + compiler/nest/core/include/nest/expr/Visitor.h | 40 + compiler/nest/core/include/nest/stmt/Forward.h | 34 + compiler/nest/core/include/nest/stmt/Macro.h | 28 + compiler/nest/core/include/nest/stmt/Node.def | 6 + compiler/nest/core/include/nest/stmt/Node.h | 62 + compiler/nest/core/include/nest/stmt/PushNode.h | 49 + compiler/nest/core/include/nest/stmt/Visitor.h | 40 + compiler/nest/core/src/Block.test.cpp | 41 + compiler/nest/core/src/Bound.test.cpp | 27 + compiler/nest/core/src/Closure.cpp | 24 + compiler/nest/core/src/Closure.test.cpp | 45 + compiler/nest/core/src/Domain.test.cpp | 41 + compiler/nest/core/src/DomainContext.cpp | 38 + compiler/nest/core/src/DomainContext.test.cpp | 56 + compiler/nest/core/src/DomainID.cpp | 30 + compiler/nest/core/src/DomainID.test.cpp | 38 + compiler/nest/core/src/DomainInfo.test.cpp | 30 + compiler/nest/core/src/Expr.cpp | 27 + compiler/nest/core/src/Expr.test.cpp | 58 + compiler/nest/core/src/FV.cpp | 75 + compiler/nest/core/src/FV.test.cpp | 75 + compiler/nest/core/src/Level.cpp | 48 + compiler/nest/core/src/Level.test.cpp | 39 + compiler/nest/core/src/Module.cpp | 44 + compiler/nest/core/src/Module.test.cpp | 99 + compiler/nest/core/src/Ret.test.cpp | 58 + compiler/nest/core/src/Schedule.cpp | 51 + compiler/nest/core/src/Schedule.test.cpp | 44 + compiler/nest/core/src/Var.cpp | 24 + compiler/nest/core/src/Var.test.cpp | 38 + compiler/nest/core/src/VarContext.cpp | 36 + compiler/nest/core/src/VarContext.test.cpp | 82 + compiler/nest/core/src/VarID.cpp | 30 + compiler/nest/core/src/VarID.test.cpp | 38 + compiler/nest/core/src/expr/AddNode.test.cpp | 43 + compiler/nest/core/src/expr/DerefNode.test.cpp | 39 + compiler/nest/core/src/expr/Macro.cpp | 21 + compiler/nest/core/src/expr/MulNode.test.cpp | 43 + compiler/nest/core/src/expr/Node.cpp | 21 + compiler/nest/core/src/expr/Subscript.test.cpp | 37 + compiler/nest/core/src/expr/VarNode.test.cpp | 47 + compiler/nest/core/src/expr/Visitor.cpp | 21 + compiler/nest/core/src/stmt/Macro.cpp | 21 + compiler/nest/core/src/stmt/Node.cpp | 21 + compiler/nest/core/src/stmt/PushNode.test.cpp | 37 + compiler/nest/core/src/stmt/Visitor.cpp | 21 + compiler/nike/CMakeLists.txt | 15 + compiler/nike/README.md | 4 + compiler/nike/include/nike/AbsoluteEpsilonEqual.h | 50 + compiler/nike/include/nike/RelativeEpsilonEqual.h | 50 + compiler/nike/src/AbsoluteEpsilonEqual.cpp | 42 + compiler/nike/src/AbsoluteEpsilonEqual.test.cpp | 31 + compiler/nike/src/RelativeEpsilonEqual.cpp | 46 + compiler/nike/src/RelativeEpsilonEqual.test.cpp | 39 + compiler/nnc/CMakeLists.txt | 42 + compiler/nnc/README.md | 58 + compiler/nnc/backends/CMakeLists.txt | 3 + .../acl_soft_backend/AclArtifactUtilities.in | 29 + .../backends/acl_soft_backend/AclCppGenerator.cpp | 76 + .../acl_soft_backend/AclCppOpGenerator.cpp | 1000 + .../backends/acl_soft_backend/AclCppOpGenerator.h | 387 + .../acl_soft_backend/ArtifactGeneratorCppCode.cpp | 302 + .../acl_soft_backend/ArtifactGeneratorCppCode.h | 64 + .../acl_soft_backend/ArtifactGeneratorCppDecl.cpp | 173 + .../acl_soft_backend/ArtifactGeneratorCppDecl.h | 64 + .../nnc/backends/acl_soft_backend/ArtifactIndent.h | 63 + .../backends/acl_soft_backend/ArtifactModel.cpp | 119 + .../nnc/backends/acl_soft_backend/ArtifactModel.h | 859 + .../nnc/backends/acl_soft_backend/CMakeLists.txt | 14 + .../backends/acl_soft_backend/IArtifactGenerator.h | 80 + compiler/nnc/backends/interpreter/CMakeLists.txt | 11 + .../backends/interpreter/InterpreterBackend.cpp | 171 + compiler/nnc/backends/soft_backend/CMakeLists.txt | 14 + .../nnc/backends/soft_backend/CPPGenerator.cpp | 489 + compiler/nnc/backends/soft_backend/CommonData.def | 41 + .../nnc/backends/soft_backend/ModelAnalyzer.cpp | 452 + compiler/nnc/backends/soft_backend/ModelAnalyzer.h | 207 + .../nnc/backends/soft_backend/SBSerializer.cpp | 414 + compiler/nnc/backends/soft_backend/SBSerializer.h | 134 + compiler/nnc/backends/soft_backend/SequencedIR.cpp | 17 + compiler/nnc/backends/soft_backend/SequencedIR.h | 144 + .../soft_backend/code_snippets/cpp_broadcast.def | 73 + .../soft_backend/code_snippets/cpp_capped_relu.def | 24 + .../code_snippets/cpp_common_funcs.def | 750 + .../soft_backend/code_snippets/cpp_concat.def | 45 + .../soft_backend/code_snippets/cpp_conv.def | 237 + .../code_snippets/cpp_conv_transpose.def | 111 + .../code_snippets/cpp_depthwise_conv.def | 1029 + .../soft_backend/code_snippets/cpp_elementwise.def | 273 + .../soft_backend/code_snippets/cpp_elu.def | 29 + .../code_snippets/cpp_fully_connected.def | 27 + .../soft_backend/code_snippets/cpp_gather.def | 48 + .../code_snippets/cpp_header_types.def | 234 + .../soft_backend/code_snippets/cpp_leaky_relu.def | 25 + .../soft_backend/code_snippets/cpp_operations.def | 656 + .../soft_backend/code_snippets/cpp_pad.def | 96 + .../soft_backend/code_snippets/cpp_pool.def | 116 + .../soft_backend/code_snippets/cpp_reduce.def | 185 + .../soft_backend/code_snippets/cpp_relu.def | 23 + .../soft_backend/code_snippets/cpp_resize.def | 61 + .../soft_backend/code_snippets/cpp_sigmoid.def | 24 + .../soft_backend/code_snippets/cpp_slice.def | 56 + .../soft_backend/code_snippets/cpp_softmax.def | 33 + .../soft_backend/code_snippets/cpp_sqrt.def | 23 + .../soft_backend/code_snippets/cpp_tanh.def | 21 + .../soft_backend/code_snippets/cpp_transpose.def | 65 + .../backends/soft_backend/code_snippets/eigen.def | 29033 +++++++++++++++++++ compiler/nnc/cmake/config.cmake | 55 + compiler/nnc/cmake/utils.cmake | 65 + compiler/nnc/doxygen.config | 2427 ++ compiler/nnc/driver/Driver.cpp | 216 + compiler/nnc/driver/Driver.h | 67 + compiler/nnc/driver/Options.cpp | 137 + compiler/nnc/driver/Options.h | 68 + compiler/nnc/driver/main.cpp | 75 + compiler/nnc/include/Definitions.h.in | 44 + .../backends/acl_soft_backend/AclCppException.h | 39 + .../backends/acl_soft_backend/AclCppGenerator.h | 50 + .../backends/interpreter/InterpreterBackend.h | 41 + .../include/backends/soft_backend/CPPGenerator.h | 162 + compiler/nnc/include/pass/Pass.h | 53 + compiler/nnc/include/pass/PassData.h | 100 + compiler/nnc/include/pass/PassException.h | 44 + compiler/nnc/include/pass/PassManager.h | 57 + .../nnc/include/passes/dot_dumper/DumperPass.h | 42 + .../passes/optimizations/CombineTransposes.h | 41 + .../passes/optimizations/ConstantFoldTranspose.h | 39 + .../passes/optimizations/DeadCodeElimination.h | 40 + .../passes/optimizations/FuseArithmeticOps.h | 44 + .../passes/optimizations/OptimizationUtils.h | 40 + .../nnc/include/passes/optimizations/SinkRelu.h | 39 + .../include/passes/optimizations/SinkTranspose.h | 40 + .../passes/transformations/DataFormatSwitcher.h | 62 + .../include/passes/transformations/LowerConv2D.h | 41 + compiler/nnc/include/support/CommandLine.h | 556 + compiler/nnc/pass/CMakeLists.txt | 5 + compiler/nnc/pass/PassManager.cpp | 36 + compiler/nnc/passes/CMakeLists.txt | 12 + compiler/nnc/passes/dot_dumper/CMakeLists.txt | 6 + compiler/nnc/passes/dot_dumper/DumperPass.cpp | 38 + compiler/nnc/passes/optimizations/CMakeLists.txt | 13 + .../nnc/passes/optimizations/CombineTransposes.cpp | 102 + .../passes/optimizations/ConstantFoldTranspose.cpp | 87 + .../passes/optimizations/DeadCodeElimination.cpp | 48 + .../nnc/passes/optimizations/FuseArithmeticOps.cpp | 250 + .../nnc/passes/optimizations/OptimizationUtils.cpp | 55 + compiler/nnc/passes/optimizations/SinkRelu.cpp | 77 + .../nnc/passes/optimizations/SinkTranspose.cpp | 82 + compiler/nnc/passes/transformations/CMakeLists.txt | 7 + .../passes/transformations/DataFormatSwitcher.cpp | 248 + .../nnc/passes/transformations/LowerConv2D.cpp | 75 + compiler/nnc/requires.cmake | 7 + compiler/nnc/support/CLOptionChecker.cpp | 74 + compiler/nnc/support/CMakeLists.txt | 7 + compiler/nnc/support/CommandLine.cpp | 637 + compiler/nnc/tests/CMakeLists.txt | 3 + .../tests/acl_soft_backend/AclCppOperations.cpp | 193 + compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in | 22 + compiler/nnc/tests/acl_soft_backend/CMakeLists.txt | 125 + .../acl_soft_backend/artifact_cmake/CMakeLists.txt | 21 + .../tests/acl_soft_backend/artifact_cmake/main.cpp | 139 + .../acl_soft_backend/artifact_cmake/odroid.cmake | 17 + .../acl_soft_backend/models/concatenate.prototxt | 23 + .../acl_soft_backend/models/convolution.prototxt | 34 + .../models/convolution_with_bias.prototxt | 38 + .../models/depthwise_convolution.prototxt | 35 + .../models/fully_connected.prototxt | 29 + .../acl_soft_backend/models/pooling_avg.prototxt | 26 + .../acl_soft_backend/models/pooling_max.prototxt | 26 + .../tests/acl_soft_backend/models/relu.prototxt | 20 + .../tests/acl_soft_backend/models/reshape.prototxt | 26 + .../tests/acl_soft_backend/models/scale.prototxt | 30 + compiler/nnc/tests/import/CMakeLists.txt | 20 + compiler/nnc/tests/import/caffe.cpp | 41 + compiler/nnc/tests/import/tflite.cpp | 41 + compiler/nnc/tests/soft_backend/CMakeLists.txt | 9 + compiler/nnc/tests/soft_backend/CompileCPP.cpp | 120 + compiler/nnc/tests/soft_backend/test_main.def | 10 + compiler/nnc/unittests/CMakeLists.txt | 10 + compiler/nnc/unittests/acl_backend/CMakeLists.txt | 9 + compiler/nnc/unittests/acl_backend/DOMToText.cpp | 497 + compiler/nnc/unittests/acl_backend/MIRToDOM.cpp | 539 + .../test_data/unsupported.caffemodel | Bin 0 -> 803260 bytes .../nnc/unittests/optimizations/CMakeLists.txt | 7 + .../unittests/optimizations/CombineTransposes.cpp | 150 + .../optimizations/DeadCodeElimination.cpp | 89 + .../unittests/optimizations/FuseArithmeticOps.cpp | 70 + compiler/nnc/unittests/optimizations/SinkTest.cpp | 195 + compiler/nnc/unittests/optimizations/Util.h | 80 + compiler/nnc/unittests/pass/CMakeLists.txt | 4 + compiler/nnc/unittests/pass/PassExceptionTest.cpp | 58 + compiler/nnc/unittests/pass/PassManagerTest.cpp | 70 + compiler/nnc/unittests/soft_backend/CMakeLists.txt | 7 + .../nnc/unittests/soft_backend/CPPHeaderTypes.cpp | 125 + .../nnc/unittests/soft_backend/CPPOperations.cpp | 1007 + compiler/nnc/unittests/soft_backend/Generator.cpp | 112 + .../nnc/unittests/soft_backend/ModelAnalyzer.cpp | 83 + compiler/nnc/unittests/support/CMakeLists.txt | 4 + compiler/nnc/unittests/support/CommandLineTest.cpp | 271 + .../nnc/unittests/transformations/CMakeLists.txt | 4 + .../nnc/unittests/transformations/Switcher.cpp | 272 + compiler/nnc/utils/CMakeLists.txt | 7 + .../nnc/utils/caffe2_dot_dumper/CMakeLists.txt | 6 + .../nnc/utils/caffe2_dot_dumper/model_dump.cpp | 51 + compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt | 6 + compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp | 45 + compiler/nnc/utils/caffe_model_maker/AllFill.sh | 48 + compiler/nnc/utils/caffe_model_maker/Filler.sh | 28 + .../utils/caffe_model_maker/GenerateCaffeModels.py | 722 + compiler/nnc/utils/caffe_model_maker/Pyloss.py | 83 + compiler/nnc/utils/caffe_model_maker/README.md | 22 + compiler/nnc/utils/def2src.cpp | 105 + compiler/nnc/utils/infer_tests/README.md | 9 + compiler/nnc/utils/infer_tests/infer_testcases.py | 154 + compiler/nnc/utils/infer_tests/res2bin.py | 33 + compiler/nnc/utils/input_gen/CMakeLists.txt | 9 + compiler/nnc/utils/input_gen/tensor_gen.cpp | 215 + compiler/nnc/utils/model_runner/common_place.py | 70 + .../nnc/utils/model_runner/model_runner_caffe.py | 22 + .../nnc/utils/model_runner/model_runner_caffe2.py | 23 + .../nnc/utils/model_runner/model_runner_onnx.py | 27 + .../nnc/utils/model_runner/model_runner_tflite.py | 25 + compiler/nnc/utils/model_runner/readme.md | 39 + compiler/nnc/utils/prepare_inputs/README.md | 8 + compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py | 170 + .../nnc/utils/tflite_dot_dumper/CMakeLists.txt | 7 + .../nnc/utils/tflite_dot_dumper/model_dump.cpp | 45 + compiler/nnkit-caffe/CMakeLists.txt | 8 + compiler/nnkit-caffe/backend/CMakeLists.txt | 3 + compiler/nnkit-caffe/backend/Module.cpp | 34 + compiler/nnkit-caffe/requires.cmake | 1 + compiler/nnkit-caffe/support/CMakeLists.txt | 4 + .../support/include/nnkit/support/caffe/Backend.h | 73 + .../include/nnkit/support/caffe/BlobContext.h | 45 + .../include/nnkit/support/caffe/InputBlobContext.h | 57 + .../nnkit/support/caffe/OutputBlobContext.h | 57 + .../include/nnkit/support/caffe/TensorContext.h | 114 + compiler/nnkit-intf/CMakeLists.txt | 4 + compiler/nnkit-intf/README.md | 3 + compiler/nnkit-intf/action/CMakeLists.txt | 4 + compiler/nnkit-intf/action/include/nnkit/Action.h | 34 + compiler/nnkit-intf/backend/CMakeLists.txt | 4 + .../nnkit-intf/backend/include/nnkit/Backend.h | 38 + compiler/nnkit-intf/cmdline/CMakeLists.txt | 2 + .../cmdline/include/nnkit/CmdlineArguments.h | 36 + compiler/nnkit-intf/tensor/CMakeLists.txt | 3 + .../tensor/include/nnkit/TensorContext.h | 91 + compiler/nnkit-misc/CMakeLists.txt | 1 + compiler/nnkit-misc/README.md | 3 + compiler/nnkit-misc/backend/CMakeLists.txt | 14 + .../backend/include/nnkit/BackendPlugin.h | 60 + compiler/nnkit-misc/backend/src/BackendPlugin.cpp | 88 + compiler/nnkit-misc/cmdline/CMakeLists.txt | 5 + .../cmdline/include/nnkit/VectorArguments.h | 43 + .../nnkit-misc/cmdline/src/VectorArguments.cpp | 28 + compiler/nnkit-mocotf/CMakeLists.txt | 6 + compiler/nnkit-mocotf/backend/Backend.cpp | 31 + compiler/nnkit-mocotf/backend/CMakeLists.txt | 3 + compiler/nnkit-mocotf/requires.cmake | 7 + compiler/nnkit-mocotf/support/CMakeLists.txt | 13 + .../include/nnkit/support/moco/tf/Backend.h | 68 + compiler/nnkit-mocotf/support/src/Backend.cpp | 162 + .../support/src/InputTensorContext.cpp | 49 + .../nnkit-mocotf/support/src/InputTensorContext.h | 71 + .../support/src/OutputTensorContext.cpp | 47 + .../nnkit-mocotf/support/src/OutputTensorContext.h | 72 + compiler/nnkit-mocotf/support/src/TensorContext.h | 86 + compiler/nnkit-onnxrt/CMakeLists.txt | 8 + compiler/nnkit-onnxrt/backend/Backend.cpp | 29 + compiler/nnkit-onnxrt/backend/CMakeLists.txt | 3 + compiler/nnkit-onnxrt/requires.cmake | 2 + compiler/nnkit-onnxrt/support/CMakeLists.txt | 10 + .../support/include/nnkit/support/onnx/Allocator.h | 56 + .../support/include/nnkit/support/onnx/Backend.h | 53 + .../support/include/nnkit/support/onnx/Runner.h | 67 + .../support/include/nnkit/support/onnx/Status.h | 85 + .../include/nnkit/support/onnx/TensorContext.h | 118 + .../support/include/nnkit/support/onnx/TensorSet.h | 97 + compiler/nnkit-onnxrt/support/src/Allocator.cpp | 88 + compiler/nnkit-onnxrt/support/src/Backend.cpp | 47 + compiler/nnkit-onnxrt/support/src/Runner.cpp | 183 + compiler/nnkit-tf/CMakeLists.txt | 8 + compiler/nnkit-tf/backend/Backend.cpp | 31 + compiler/nnkit-tf/backend/CMakeLists.txt | 3 + compiler/nnkit-tf/requires.cmake | 3 + compiler/nnkit-tf/support/CMakeLists.txt | 9 + .../support/include/nnkit/support/tf/Backend.h | 67 + .../support/include/nnkit/support/tf/Runner.h | 105 + .../include/nnkit/support/tf/TensorContext.h | 80 + .../include/nnkit/support/tf/TensorDataMap.h | 81 + compiler/nnkit-tf/support/src/Backend.cpp | 113 + compiler/nnkit-tf/support/src/Runner.cpp | 323 + compiler/nnkit-tf/support/src/TensorContext.cpp | 60 + compiler/nnkit-tflite/CMakeLists.txt | 8 + compiler/nnkit-tflite/backend/Backend.cpp | 62 + compiler/nnkit-tflite/backend/CMakeLists.txt | 7 + compiler/nnkit-tflite/requires.cmake | 2 + compiler/nnkit-tflite/support/CMakeLists.txt | 10 + .../include/nnkit/support/tflite/AbstractBackend.h | 48 + .../include/nnkit/support/tflite/TensorContext.h | 63 + .../include/nnkit/support/tflite/TensorSet.h | 44 + .../include/nnkit/support/tflite/TensorSets.h | 71 + .../include/nnkit/support/tflite/TensorUtils.h | 36 + compiler/nnkit-tflite/support/src/Backend.cpp | 52 + .../nnkit-tflite/support/src/TensorContext.cpp | 65 + compiler/nnkit-tflite/support/src/TensorUtils.cpp | 43 + compiler/nnkit/CMakeLists.txt | 2 + compiler/nnkit/README.md | 179 + compiler/nnkit/actions/CMakeLists.txt | 1 + compiler/nnkit/actions/HDF5/CMakeLists.txt | 21 + compiler/nnkit/actions/HDF5/Common.cpp | 38 + compiler/nnkit/actions/HDF5/Common.h | 60 + compiler/nnkit/actions/HDF5/Export.cpp | 109 + compiler/nnkit/actions/HDF5/Import.cpp | 100 + compiler/nnkit/actions/builtin/CMakeLists.txt | 7 + compiler/nnkit/actions/builtin/Randomize.cpp | 60 + compiler/nnkit/actions/builtin/Show.cpp | 71 + compiler/nnkit/requires.cmake | 8 + compiler/nnkit/tools/CMakeLists.txt | 1 + compiler/nnkit/tools/benchmark/CMakeLists.txt | 14 + compiler/nnkit/tools/benchmark/src/Benchmark.cpp | 207 + compiler/nnkit/tools/run/CMakeLists.txt | 22 + compiler/nnkit/tools/run/nnkit-run.cpp | 266 + compiler/nnop/CMakeLists.txt | 16 + compiler/nnop/include/nnop/Conv2D.h | 87 + compiler/nnop/include/nnop/PadInfo.h | 49 + compiler/nnop/include/nnop/StrideInfo.h | 44 + compiler/nnop/requires.cmake | 1 + compiler/nnop/src/Conv2D.test.cpp | 50 + compiler/nnop/src/PadInfo.test.cpp | 34 + compiler/nnop/src/StrideInfo.test.cpp | 30 + compiler/nnsuite/CMakeLists.txt | 1 + compiler/nnsuite/conv/CMakeLists.txt | 3 + compiler/nnsuite/conv/model/CMakeLists.txt | 6 + .../conv/model/include/nnsuite/conv/Model.h | 53 + .../conv/model/include/nnsuite/conv/RandomModel.h | 70 + compiler/nnsuite/conv/model/src/RandomModel.cpp | 58 + compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt | 24 + compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp | 97 + compiler/nnsuite/conv/nnkit-caffe/ConvBackend.h | 30 + .../nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp | 126 + compiler/nnsuite/conv/nnkit-caffe/Entry.cpp | 40 + compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt | 23 + compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp | 145 + compiler/nnsuite/conv/nnkit-tflite/ConvBackend.h | 51 + .../nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp | 129 + compiler/nnsuite/conv/nnkit-tflite/Entry.cpp | 44 + compiler/nnsuite/requires.cmake | 1 + compiler/oneco-value-pbtxt-test/CMakeLists.txt | 53 + .../oneco-value-pbtxt-test/Const_000/test.pbtxt | 52 + .../oneco-value-pbtxt-test/Identity_000/test.pbtxt | 66 + compiler/oneco-value-pbtxt-test/requires.cmake | 1 + compiler/oneco/CMakeLists.txt | 36 + compiler/oneco/include/moco/onnx/Frontend.h | 48 + compiler/oneco/proto/CMakeLists.txt | 13 + compiler/oneco/requires.cmake | 3 + compiler/oneco/src/Convert.cpp | 113 + compiler/oneco/src/Convert.h | 38 + compiler/oneco/src/Frontend.cpp | 275 + compiler/oneco/src/Frontend.test.cpp | 21 + compiler/oneco/src/GraphBuilder.h | 46 + compiler/oneco/src/GraphBuilderContext.cpp | 92 + compiler/oneco/src/GraphBuilderContext.h | 100 + compiler/oneco/src/GraphBuilderRegistry.h | 78 + compiler/oneco/src/Onnxutil.cpp | 109 + compiler/oneco/src/Onnxutil.h | 46 + compiler/oneco/src/Op/Constant.cpp | 52 + compiler/oneco/src/Op/Constant.h | 59 + compiler/oneco/src/Op/Constant_V1.cpp | 71 + compiler/oneco/src/Op/Constant_V9.cpp | 67 + compiler/oneco/src/Op/Identity.cpp | 48 + compiler/oneco/src/Op/Identity.h | 47 + compiler/oneco/src/Op/Identity_V1.cpp | 51 + compiler/onnx2circle/CMakeLists.txt | 27 + compiler/onnx2circle/README.md | 3 + compiler/onnx2circle/requires.cmake | 9 + compiler/onnx2circle/src/onnx2circle.cpp | 111 + .../onnx2tflite-integration-test/CMakeLists.txt | 120 + .../onnx2tflite-integration-test/requires.cmake | 6 + compiler/onnx2tflite-integration-test/test.lst | 5 + compiler/onnx2tflite-integration-test/testall.sh | 112 + compiler/onnx2tflite/CMakeLists.txt | 8 + compiler/onnx2tflite/requires.cmake | 3 + compiler/onnx2tflite/src/Driver.cpp | 83 + compiler/onnxkit/CMakeLists.txt | 30 + compiler/onnxkit/README.md | 61 + compiler/onnxkit/src/DecodeCommand.cpp | 47 + compiler/onnxkit/src/DecodeCommand.hpp | 27 + compiler/onnxkit/src/EncodeCommand.cpp | 52 + compiler/onnxkit/src/EncodeCommand.hpp | 27 + compiler/onnxkit/src/Main.cpp | 31 + compiler/onnxkit/src/Support.cpp | 73 + compiler/onnxkit/src/Support.hpp | 59 + compiler/oops/CMakeLists.txt | 12 + compiler/oops/include/oops/InternalExn.h | 80 + compiler/oops/include/oops/UserExn.h | 89 + compiler/oops/test.cpp | 94 + compiler/pepper-assert/CMakeLists.txt | 2 + compiler/pepper-assert/include/pepper/assert.h | 38 + compiler/pepper-env/CMakeLists.txt | 19 + compiler/pepper-env/README.md | 3 + compiler/pepper-env/include/pepper/env.h | 101 + compiler/pepper-env/src/env.cpp | 47 + compiler/pepper-env/src/env.test.cpp | 40 + compiler/pepper-str/CMakeLists.txt | 12 + compiler/pepper-str/README.md | 15 + compiler/pepper-str/include/pepper/str.h | 65 + compiler/pepper-str/test.cpp | 51 + compiler/pepper-strcast/CMakeLists.txt | 19 + compiler/pepper-strcast/README.md | 3 + compiler/pepper-strcast/include/pepper/strcast.h | 34 + compiler/pepper-strcast/src/strcast.cpp | 29 + compiler/pepper-strcast/src/strcast.test.cpp | 26 + compiler/plier-tf/CMakeLists.txt | 28 + compiler/plier-tf/README.md | 3 + compiler/plier-tf/include/plier/tf/Convert.h | 78 + compiler/plier-tf/include/plier/tf/TestHelper.h | 38 + compiler/plier-tf/requires.cmake | 3 + compiler/plier-tf/src/Convert.cpp | 198 + compiler/plier-tf/src/Convert.test.cpp | 115 + compiler/plier-tf/src/TestHelper.cpp | 70 + compiler/pp/CMakeLists.txt | 20 + compiler/pp/README.md | 35 + compiler/pp/include/pp/EnclosedDocument.h | 53 + compiler/pp/include/pp/Format.h | 43 + compiler/pp/include/pp/IndentedStringBuilder.h | 52 + compiler/pp/include/pp/LinearDocument.h | 92 + compiler/pp/include/pp/MultiLineText.h | 37 + compiler/pp/include/pp/MultiLineTextUtils.h | 26 + compiler/pp/src/EnclosedDocument.cpp | 34 + compiler/pp/src/EnclosedDocument.test.cpp | 47 + compiler/pp/src/Format.test.cpp | 30 + compiler/pp/src/IndentedStringBuilder.cpp | 48 + compiler/pp/src/IndentedStringBuilder.test.cpp | 30 + compiler/pp/src/LinearDocument.cpp | 71 + compiler/pp/src/LinearDocument.test.cpp | 160 + compiler/pp/src/MultiLineTextUtils.cpp | 32 + compiler/pp/src/MultiLineTextUtils.test.cpp | 49 + compiler/safemain/CMakeLists.txt | 2 + compiler/safemain/SafeMain.cpp | 43 + compiler/stdex/CMakeLists.txt | 16 + compiler/stdex/README.md | 22 + compiler/stdex/include/stdex/Memory.h | 29 + compiler/stdex/include/stdex/Queue.h | 38 + compiler/stdex/include/stdex/Set.h | 55 + compiler/stdex/src/Memory.test.cpp | 60 + compiler/stdex/src/Queue.test.cpp | 32 + compiler/stdex/src/Set.test.cpp | 37 + compiler/tf2circle-conversion-test/.gitignore | 1 + compiler/tf2circle-conversion-test/CMakeLists.txt | 138 + compiler/tf2circle-conversion-test/README.md | 3 + compiler/tf2circle-conversion-test/requires.cmake | 2 + compiler/tf2circle-conversion-test/test.lst | 103 + compiler/tf2circle-conversion-test/testall.sh | 90 + compiler/tf2circle-dredd-pb-test/.gitignore | 1 + compiler/tf2circle-dredd-pb-test/CMakeLists.txt | 141 + compiler/tf2circle-dredd-pb-test/README.md | 3 + .../tf2circle-dredd-pb-test/contrib/.gitignore | 3 + compiler/tf2circle-dredd-pb-test/requires.cmake | 4 + compiler/tf2circle-dredd-pb-test/runner.sh | 121 + compiler/tf2circle-dredd-pbtxt-test/.gitignore | 1 + compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt | 184 + compiler/tf2circle-dredd-pbtxt-test/README.md | 3 + compiler/tf2circle-dredd-pbtxt-test/requires.cmake | 5 + compiler/tf2circle-dredd-pbtxt-test/runner.sh | 121 + compiler/tf2circle-dredd-pbtxt-test/test.lst | 4 + compiler/tf2circle-model-test/.gitignore | 1 + compiler/tf2circle-model-test/CMakeLists.txt | 110 + compiler/tf2circle-model-test/README.md | 1 + compiler/tf2circle-model-test/contrib/.gitignore | 3 + compiler/tf2circle-model-test/requires.cmake | 2 + compiler/tf2circle-model-test/runner.sh | 83 + compiler/tf2circle-ui-check/.gitignore | 1 + compiler/tf2circle-ui-check/CMakeLists.txt | 44 + compiler/tf2circle-ui-check/README.md | 21 + compiler/tf2circle-ui-check/checkall.sh | 57 + compiler/tf2circle-ui-check/requires.cmake | 2 + .../tf2circle-value-pbtxt-remote-test/.gitignore | 1 + .../CMakeLists.txt | 170 + .../tf2circle-value-pbtxt-remote-test/README.md | 138 + .../requires.cmake | 3 + .../tf2circle-value-pbtxt-remote-test/testall.sh | 161 + compiler/tf2circle/CMakeLists.txt | 47 + compiler/tf2circle/README.md | 3 + compiler/tf2circle/proto/CustomOpInfo.proto | 57 + compiler/tf2circle/requires.cmake | 8 + compiler/tf2circle/src/CustomopConfLoader.cpp | 138 + compiler/tf2circle/src/CustomopConfLoader.h | 32 + compiler/tf2circle/src/tf2circle.cpp | 225 + compiler/tf2nnpkg/CMakeLists.txt | 35 + compiler/tf2nnpkg/requires.cmake | 8 + compiler/tf2nnpkg/src/filesystem.h | 43 + compiler/tf2nnpkg/src/filesystem_common.cpp | 44 + compiler/tf2nnpkg/src/filesystem_linux.cpp | 46 + compiler/tf2nnpkg/src/filesystem_windows.cpp | 55 + compiler/tf2nnpkg/src/tf2nnpkg.cpp | 300 + compiler/tf2tflite-dredd-pb-test/.gitignore | 1 + compiler/tf2tflite-dredd-pb-test/CMakeLists.txt | 141 + compiler/tf2tflite-dredd-pb-test/README.md | 6 + .../tf2tflite-dredd-pb-test/contrib/.gitignore | 3 + compiler/tf2tflite-dredd-pb-test/requires.cmake | 4 + compiler/tf2tflite-dredd-pb-test/runner.sh | 121 + compiler/tf2tflite-dredd-pbtxt-test/.gitignore | 1 + compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt | 184 + compiler/tf2tflite-dredd-pbtxt-test/requires.cmake | 5 + compiler/tf2tflite-dredd-pbtxt-test/runner.sh | 121 + compiler/tf2tflite-dredd-pbtxt-test/test.lst | 1 + compiler/tf2tflite-value-pb-test/.gitignore | 1 + compiler/tf2tflite-value-pb-test/CMakeLists.txt | 131 + compiler/tf2tflite-value-pb-test/README.md | 1 + .../tf2tflite-value-pb-test/contrib/.gitignore | 3 + compiler/tf2tflite-value-pb-test/requires.cmake | 6 + compiler/tf2tflite-value-pb-test/runner.sh | 112 + compiler/tf2tflite-value-pbtxt-test/.gitignore | 1 + compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt | 159 + compiler/tf2tflite-value-pbtxt-test/README.md | 3 + compiler/tf2tflite-value-pbtxt-test/requires.cmake | 4 + compiler/tf2tflite-value-pbtxt-test/test.lst | 101 + compiler/tf2tflite-value-pbtxt-test/testall.sh | 106 + compiler/tf2tflite/.gitignore | 1 + compiler/tf2tflite/CMakeLists.txt | 44 + compiler/tf2tflite/README.md | 3 + compiler/tf2tflite/proto/CustomOpInfo.proto | 57 + compiler/tf2tflite/requires.cmake | 8 + compiler/tf2tflite/src/CustomopConfLoader.cpp | 137 + compiler/tf2tflite/src/CustomopConfLoader.h | 32 + compiler/tf2tflite/src/Driver.cpp | 167 + .../tf2tfliteV2-value-pbtxt-test/CMakeLists.txt | 183 + .../tf2tfliteV2-value-pbtxt-test/requirements.txt | 2 + .../tf2tfliteV2-value-pbtxt-test/requires.cmake | 4 + compiler/tf2tfliteV2-value-pbtxt-test/test.lst | 101 + compiler/tf2tfliteV2-value-pbtxt-test/testall.sh | 110 + compiler/tf2tfliteV2/CMakeLists.txt | 11 + compiler/tf2tfliteV2/README.md | 47 + compiler/tf2tfliteV2/tf2tfliteV2.py | 183 + compiler/tfgraph-xform/CMakeLists.txt | 328 + compiler/tfgraph-xform/README.md | 5 + compiler/tfinfo-v2/CMakeLists.txt | 36 + .../tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h | 43 + .../tfinfo-v2/include/tfinfo-v2/TensorSignature.h | 124 + compiler/tfinfo-v2/proto/tfinfo-v2.proto | 46 + compiler/tfinfo-v2/requires.cmake | 2 + compiler/tfinfo-v2/src/TFInfo_v2.test.cpp | 246 + compiler/tfinfo-v2/src/TensorInfoLoader.cpp | 179 + compiler/tfinfo-v2/src/TensorSignature.cpp | 17 + compiler/tfinfo/CMakeLists.txt | 20 + compiler/tfinfo/README.md | 12 + .../nnkit/support/tftestinfo/ParsedTensor.h | 121 + .../nnkit/support/tftestinfo/TensorInfoParser.h | 46 + compiler/tfinfo/requires.cmake | 3 + compiler/tfinfo/src/Compat.h | 27 + compiler/tfinfo/src/TensorInfoParser.cpp | 230 + compiler/tfinfo/src/TensorInfoParser.test.cpp | 121 + compiler/tfkit/CMakeLists.txt | 13 + compiler/tfkit/README.md | 73 + compiler/tfkit/src/ConvertCommand.cpp | 146 + compiler/tfkit/src/ConvertCommand.hpp | 32 + compiler/tfkit/src/DecodeCommand.cpp | 52 + compiler/tfkit/src/DecodeCommand.hpp | 32 + compiler/tfkit/src/EncodeCommand.cpp | 57 + compiler/tfkit/src/EncodeCommand.hpp | 32 + compiler/tfkit/src/Main.cpp | 37 + compiler/tfkit/src/PackCommand.cpp | 173 + compiler/tfkit/src/PackCommand.hpp | 32 + compiler/tfkit/src/Support.cpp | 125 + compiler/tfkit/src/Support.hpp | 75 + compiler/tfkit/src/UnpackCommand.cpp | 202 + compiler/tfkit/src/UnpackCommand.hpp | 32 + compiler/tfl-inspect/CMakeLists.txt | 13 + compiler/tfl-inspect/README.md | 51 + compiler/tfl-inspect/driver/Driver.cpp | 96 + compiler/tfl-inspect/requires.cmake | 3 + compiler/tfl-inspect/src/Dump.cpp | 137 + compiler/tfl-inspect/src/Dump.h | 56 + compiler/tfl-inspect/src/Model.cpp | 143 + compiler/tfl-inspect/src/Model.h | 43 + compiler/tfl-inspect/src/Reader.cpp | 166 + compiler/tfl-inspect/src/Reader.h | 91 + compiler/tfl-verify/CMakeLists.txt | 12 + compiler/tfl-verify/README.md | 23 + compiler/tfl-verify/requires.cmake | 4 + compiler/tfl-verify/src/Driver.cpp | 51 + compiler/tfl-verify/src/Model.cpp | 90 + compiler/tfl-verify/src/Model.h | 38 + compiler/tfl-verify/src/VerifyFlatBuffers.cpp | 36 + compiler/tfl-verify/src/VerifyFlatBuffers.h | 32 + compiler/tflchef/CMakeLists.txt | 19 + compiler/tflchef/README.md | 76 + compiler/tflchef/core/CMakeLists.txt | 7 + compiler/tflchef/core/include/tflchef/ModelChef.h | 56 + compiler/tflchef/core/src/Arguments.h | 34 + compiler/tflchef/core/src/Convert.cpp | 72 + compiler/tflchef/core/src/Convert.h | 31 + compiler/tflchef/core/src/Data/Constant.h | 62 + compiler/tflchef/core/src/Data/Explicit.h | 75 + compiler/tflchef/core/src/Data/Gaussian.cpp | 135 + compiler/tflchef/core/src/Data/Gaussian.h | 88 + compiler/tflchef/core/src/DataChef.def | 15 + compiler/tflchef/core/src/DataChef.h | 56 + compiler/tflchef/core/src/DataChefs.h | 24 + compiler/tflchef/core/src/Dataset.h | 57 + compiler/tflchef/core/src/LexicalCast.cpp | 36 + compiler/tflchef/core/src/LexicalCast.h | 32 + compiler/tflchef/core/src/ModelChef.cpp | 765 + compiler/tflchef/core/src/Op/Abs.cpp | 30 + compiler/tflchef/core/src/Op/Abs.h | 46 + compiler/tflchef/core/src/Op/Add.cpp | 39 + compiler/tflchef/core/src/Op/Add.h | 46 + compiler/tflchef/core/src/Op/ArgMax.cpp | 39 + compiler/tflchef/core/src/Op/ArgMax.h | 46 + compiler/tflchef/core/src/Op/AveragePool2D.cpp | 47 + compiler/tflchef/core/src/Op/AveragePool2D.h | 49 + compiler/tflchef/core/src/Op/BatchToSpaceND.cpp | 31 + compiler/tflchef/core/src/Op/BatchToSpaceND.h | 52 + compiler/tflchef/core/src/Op/Concatenation.cpp | 43 + compiler/tflchef/core/src/Op/Concatenation.h | 52 + compiler/tflchef/core/src/Op/Conv2D.cpp | 43 + compiler/tflchef/core/src/Op/Conv2D.h | 46 + compiler/tflchef/core/src/Op/Cos.cpp | 29 + compiler/tflchef/core/src/Op/Cos.h | 46 + compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp | 47 + compiler/tflchef/core/src/Op/DepthwiseConv2D.h | 52 + compiler/tflchef/core/src/Op/Div.cpp | 39 + compiler/tflchef/core/src/Op/Div.h | 46 + compiler/tflchef/core/src/Op/Equal.cpp | 29 + compiler/tflchef/core/src/Op/Equal.h | 46 + compiler/tflchef/core/src/Op/Exp.cpp | 30 + compiler/tflchef/core/src/Op/Exp.h | 46 + compiler/tflchef/core/src/Op/FloorDiv.cpp | 30 + compiler/tflchef/core/src/Op/FloorDiv.h | 49 + compiler/tflchef/core/src/Op/FullyConnected.cpp | 39 + compiler/tflchef/core/src/Op/FullyConnected.h | 52 + compiler/tflchef/core/src/Op/LogicalNot.cpp | 29 + compiler/tflchef/core/src/Op/LogicalNot.h | 49 + compiler/tflchef/core/src/Op/LogicalOr.cpp | 29 + compiler/tflchef/core/src/Op/LogicalOr.h | 49 + compiler/tflchef/core/src/Op/MaxPool2D.cpp | 47 + compiler/tflchef/core/src/Op/MaxPool2D.h | 46 + compiler/tflchef/core/src/Op/Mean.cpp | 39 + compiler/tflchef/core/src/Op/Mean.h | 46 + compiler/tflchef/core/src/Op/Mul.cpp | 39 + compiler/tflchef/core/src/Op/Mul.h | 46 + compiler/tflchef/core/src/Op/Pack.cpp | 38 + compiler/tflchef/core/src/Op/Pack.h | 46 + compiler/tflchef/core/src/Op/Pad.cpp | 28 + compiler/tflchef/core/src/Op/Pad.h | 46 + compiler/tflchef/core/src/Op/ReLU.cpp | 27 + compiler/tflchef/core/src/Op/ReLU.h | 46 + compiler/tflchef/core/src/Op/ReLU6.cpp | 27 + compiler/tflchef/core/src/Op/ReLU6.h | 46 + compiler/tflchef/core/src/Op/Reshape.cpp | 62 + compiler/tflchef/core/src/Op/Reshape.h | 46 + compiler/tflchef/core/src/Op/Rsqrt.cpp | 28 + compiler/tflchef/core/src/Op/Rsqrt.h | 46 + compiler/tflchef/core/src/Op/Shape.cpp | 39 + compiler/tflchef/core/src/Op/Shape.h | 46 + compiler/tflchef/core/src/Op/Softmax.cpp | 39 + compiler/tflchef/core/src/Op/Softmax.h | 46 + compiler/tflchef/core/src/Op/Sqrt.cpp | 27 + compiler/tflchef/core/src/Op/Sqrt.h | 46 + compiler/tflchef/core/src/Op/Sub.cpp | 39 + compiler/tflchef/core/src/Op/Sub.h | 46 + compiler/tflchef/core/src/Op/Tanh.cpp | 28 + compiler/tflchef/core/src/Op/Tanh.h | 46 + compiler/tflchef/core/src/Op/Transpose.cpp | 32 + compiler/tflchef/core/src/Op/Transpose.h | 49 + compiler/tflchef/core/src/OpChef.def | 37 + compiler/tflchef/core/src/OpChef.h | 41 + compiler/tflchef/core/src/OpChefs.h | 52 + compiler/tflchef/proto/CMakeLists.txt | 5 + compiler/tflchef/proto/tflchef.proto | 232 + compiler/tflchef/requires.cmake | 4 + compiler/tflchef/tests/CMakeLists.txt | 129 + .../tflchef/tests/explicit_datachef/test.recipe | 28 + .../tflchef/tests/explicit_datachef/test.reverse | 0 compiler/tflchef/tests/multisubgraph/test.recipe | 72 + compiler/tflchef/tests/readme/test.recipe | 44 + compiler/tflchef/tests/readme/test.reverse | 0 compiler/tflchef/tests/runall.sh | 60 + compiler/tflchef/tests/runvalidate.sh | 56 + compiler/tflchef/tflite/CMakeLists.txt | 9 + compiler/tflchef/tflite/include/tflchef/RawModel.h | 41 + .../tflchef/tflite/include/tflchef/RecipeChef.h | 41 + compiler/tflchef/tflite/src/Convert.cpp | 78 + compiler/tflchef/tflite/src/Convert.h | 55 + compiler/tflchef/tflite/src/Op/Abs.cpp | 40 + compiler/tflchef/tflite/src/Op/Abs.h | 39 + compiler/tflchef/tflite/src/Op/Add.cpp | 47 + compiler/tflchef/tflite/src/Op/Add.h | 39 + compiler/tflchef/tflite/src/Op/ArgMax.cpp | 54 + compiler/tflchef/tflite/src/Op/ArgMax.h | 39 + compiler/tflchef/tflite/src/Op/AveragePool2D.cpp | 52 + compiler/tflchef/tflite/src/Op/AveragePool2D.h | 39 + compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp | 53 + compiler/tflchef/tflite/src/Op/BatchToSpaceND.h | 39 + compiler/tflchef/tflite/src/Op/Concatenation.cpp | 48 + compiler/tflchef/tflite/src/Op/Concatenation.h | 39 + compiler/tflchef/tflite/src/Op/Conv2D.cpp | 58 + compiler/tflchef/tflite/src/Op/Conv2D.h | 39 + compiler/tflchef/tflite/src/Op/Cos.cpp | 40 + compiler/tflchef/tflite/src/Op/Cos.h | 39 + compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp | 61 + compiler/tflchef/tflite/src/Op/DepthwiseConv2D.h | 39 + compiler/tflchef/tflite/src/Op/Div.cpp | 47 + compiler/tflchef/tflite/src/Op/Div.h | 39 + compiler/tflchef/tflite/src/Op/Equal.cpp | 40 + compiler/tflchef/tflite/src/Op/Equal.h | 39 + compiler/tflchef/tflite/src/Op/Exp.cpp | 40 + compiler/tflchef/tflite/src/Op/Exp.h | 39 + compiler/tflchef/tflite/src/Op/FloorDiv.cpp | 40 + compiler/tflchef/tflite/src/Op/FloorDiv.h | 39 + compiler/tflchef/tflite/src/Op/FullyConnected.cpp | 47 + compiler/tflchef/tflite/src/Op/FullyConnected.h | 39 + compiler/tflchef/tflite/src/Op/LogicalNot.cpp | 40 + compiler/tflchef/tflite/src/Op/LogicalNot.h | 39 + compiler/tflchef/tflite/src/Op/LogicalOr.cpp | 40 + compiler/tflchef/tflite/src/Op/LogicalOr.h | 39 + compiler/tflchef/tflite/src/Op/MaxPool2D.cpp | 52 + compiler/tflchef/tflite/src/Op/MaxPool2D.h | 39 + compiler/tflchef/tflite/src/Op/Mean.cpp | 54 + compiler/tflchef/tflite/src/Op/Mean.h | 39 + compiler/tflchef/tflite/src/Op/Pack.cpp | 48 + compiler/tflchef/tflite/src/Op/Pack.h | 39 + compiler/tflchef/tflite/src/Op/Pad.cpp | 47 + compiler/tflchef/tflite/src/Op/Pad.h | 39 + compiler/tflchef/tflite/src/Op/ReLU.cpp | 40 + compiler/tflchef/tflite/src/Op/ReLU.h | 39 + compiler/tflchef/tflite/src/Op/ReLU6.cpp | 40 + compiler/tflchef/tflite/src/Op/ReLU6.h | 39 + compiler/tflchef/tflite/src/Op/Reshape.cpp | 62 + compiler/tflchef/tflite/src/Op/Reshape.h | 39 + compiler/tflchef/tflite/src/Op/Rsqrt.cpp | 39 + compiler/tflchef/tflite/src/Op/Rsqrt.h | 39 + compiler/tflchef/tflite/src/Op/Softmax.cpp | 47 + compiler/tflchef/tflite/src/Op/Softmax.h | 39 + compiler/tflchef/tflite/src/Op/Sqrt.cpp | 53 + compiler/tflchef/tflite/src/Op/Sqrt.h | 39 + compiler/tflchef/tflite/src/Op/Sub.cpp | 48 + compiler/tflchef/tflite/src/Op/Sub.h | 39 + compiler/tflchef/tflite/src/Op/Tanh.cpp | 39 + compiler/tflchef/tflite/src/Op/Tanh.h | 39 + compiler/tflchef/tflite/src/Op/Transpose.cpp | 53 + compiler/tflchef/tflite/src/Op/Transpose.h | 39 + compiler/tflchef/tflite/src/RawModelLoader.cpp | 94 + compiler/tflchef/tflite/src/RecipeChef.cpp | 241 + compiler/tflchef/tflite/src/TFliteImport.cpp | 145 + compiler/tflchef/tflite/src/TFliteImport.h | 140 + compiler/tflchef/tflite/src/TFliteOpChef.h | 44 + compiler/tflchef/tflite/src/TFliteOpChefs.h | 51 + compiler/tflchef/tflite/src/TFliteOpRegistry.h | 97 + compiler/tflchef/tools/CMakeLists.txt | 6 + compiler/tflchef/tools/console/CMakeLists.txt | 3 + compiler/tflchef/tools/console/Driver.cpp | 58 + compiler/tflchef/tools/file/CMakeLists.txt | 3 + compiler/tflchef/tools/file/Driver.cpp | 72 + compiler/tflchef/tools/reverse/CMakeLists.txt | 3 + compiler/tflchef/tools/reverse/Driver.cpp | 64 + compiler/tfldump/CMakeLists.txt | 14 + compiler/tfldump/README.md | 67 + compiler/tfldump/driver/Driver.cpp | 52 + compiler/tfldump/include/tfldump/Dump.h | 32 + compiler/tfldump/include/tflread/Model.h | 43 + compiler/tfldump/requires.cmake | 1 + compiler/tfldump/src/Dump.cpp | 297 + compiler/tfldump/src/Load.cpp | 133 + compiler/tfldump/src/OpPrinter.cpp | 306 + compiler/tfldump/src/OpPrinter.h | 61 + compiler/tfldump/src/Read.cpp | 168 + compiler/tfldump/src/Read.h | 99 + .../tflite2circle-conversion-test/CMakeLists.txt | 94 + compiler/tflite2circle-conversion-test/README.md | 3 + .../tflite2circle-conversion-test/requires.cmake | 2 + compiler/tflite2circle-conversion-test/test.lst | 20 + compiler/tflite2circle-conversion-test/testall.sh | 76 + compiler/tflite2circle/CMakeLists.txt | 16 + compiler/tflite2circle/README.md | 11 + compiler/tflite2circle/driver/Driver.cpp | 59 + compiler/tflite2circle/include/CircleModel.h | 102 + compiler/tflite2circle/include/TFLModel.h | 55 + compiler/tflite2circle/requires.cmake | 4 + compiler/tflite2circle/src/BuildBuiltinOptions.h | 54 + .../src/BuildBuiltinOptions/AbsOptions.cpp | 32 + .../src/BuildBuiltinOptions/AbsOptions.h | 31 + .../src/BuildBuiltinOptions/AddOptions.cpp | 36 + .../src/BuildBuiltinOptions/AddOptions.h | 31 + .../src/BuildBuiltinOptions/ArgMaxOptions.cpp | 36 + .../src/BuildBuiltinOptions/ArgMaxOptions.h | 31 + .../BuildBuiltinOptions/BatchToSpaceNDOptions.cpp | 32 + .../BuildBuiltinOptions/BatchToSpaceNDOptions.h | 31 + .../src/BuildBuiltinOptions/CastOptions.cpp | 38 + .../src/BuildBuiltinOptions/CastOptions.h | 31 + .../BuildBuiltinOptions/ConcatenationOptions.cpp | 37 + .../src/BuildBuiltinOptions/ConcatenationOptions.h | 31 + .../src/BuildBuiltinOptions/Conv2DOptions.cpp | 41 + .../src/BuildBuiltinOptions/Conv2DOptions.h | 31 + .../src/BuildBuiltinOptions/CosOptions.cpp | 32 + .../src/BuildBuiltinOptions/CosOptions.h | 31 + .../BuildBuiltinOptions/DepthwiseConv2DOptions.cpp | 42 + .../BuildBuiltinOptions/DepthwiseConv2DOptions.h | 31 + .../src/BuildBuiltinOptions/DivOptions.cpp | 36 + .../src/BuildBuiltinOptions/DivOptions.h | 31 + .../src/BuildBuiltinOptions/EqualOptions.cpp | 29 + .../src/BuildBuiltinOptions/EqualOptions.h | 31 + .../src/BuildBuiltinOptions/ExpOptions.cpp | 32 + .../src/BuildBuiltinOptions/ExpOptions.h | 31 + .../src/BuildBuiltinOptions/ExpandDimsOptions.cpp | 29 + .../src/BuildBuiltinOptions/ExpandDimsOptions.h | 31 + .../src/BuildBuiltinOptions/FillOptions.cpp | 29 + .../src/BuildBuiltinOptions/FillOptions.h | 31 + .../BuildBuiltinOptions/FullyConnectedOptions.cpp | 43 + .../BuildBuiltinOptions/FullyConnectedOptions.h | 31 + .../BuildBuiltinOptions/GreaterEqualOptions.cpp | 29 + .../src/BuildBuiltinOptions/GreaterEqualOptions.h | 31 + .../src/BuildBuiltinOptions/LogicalNotOptions.cpp | 32 + .../src/BuildBuiltinOptions/LogicalNotOptions.h | 31 + .../src/BuildBuiltinOptions/LogicalOrOptions.cpp | 32 + .../src/BuildBuiltinOptions/LogicalOrOptions.h | 31 + .../src/BuildBuiltinOptions/MulOptions.cpp | 36 + .../src/BuildBuiltinOptions/MulOptions.h | 31 + .../src/BuildBuiltinOptions/NotEqualOptions.cpp | 29 + .../src/BuildBuiltinOptions/NotEqualOptions.h | 31 + .../src/BuildBuiltinOptions/PackOptions.cpp | 35 + .../src/BuildBuiltinOptions/PackOptions.h | 31 + .../src/BuildBuiltinOptions/PadOptions.cpp | 31 + .../src/BuildBuiltinOptions/PadOptions.h | 31 + .../src/BuildBuiltinOptions/Pool2DOptions.cpp | 41 + .../src/BuildBuiltinOptions/Pool2DOptions.h | 31 + .../src/BuildBuiltinOptions/ReducerOptions.cpp | 34 + .../src/BuildBuiltinOptions/ReducerOptions.h | 31 + .../src/BuildBuiltinOptions/ReshapeOptions.cpp | 37 + .../src/BuildBuiltinOptions/ReshapeOptions.h | 31 + .../src/BuildBuiltinOptions/ShapeOptions.cpp | 35 + .../src/BuildBuiltinOptions/ShapeOptions.h | 31 + .../src/BuildBuiltinOptions/SoftmaxOptions.cpp | 35 + .../src/BuildBuiltinOptions/SoftmaxOptions.h | 31 + .../src/BuildBuiltinOptions/SplitOptions.cpp | 34 + .../src/BuildBuiltinOptions/SplitOptions.h | 31 + .../src/BuildBuiltinOptions/SqueezeOptions.cpp | 37 + .../src/BuildBuiltinOptions/SqueezeOptions.h | 31 + .../src/BuildBuiltinOptions/SubOptions.cpp | 36 + .../src/BuildBuiltinOptions/SubOptions.h | 31 + .../src/BuildBuiltinOptions/TransposeOptions.cpp | 31 + .../src/BuildBuiltinOptions/TransposeOptions.h | 31 + compiler/tflite2circle/src/CircleModel.cpp | 238 + compiler/tflite2circle/src/DataLookup.cpp | 113 + compiler/tflite2circle/src/DataLookup.h | 37 + .../src/TFLActivationFunctionType.lst | 12 + compiler/tflite2circle/src/TFLBuiltinOptions.lst | 103 + compiler/tflite2circle/src/TFLModel.cpp | 43 + compiler/tflite2circle/src/TFLOperator.lst | 128 + compiler/tflite2circle/src/TFLTensorType.lst | 16 + compiler/tfts/CMakeLists.txt | 30 + compiler/tfts/README.md | 3 + compiler/tfts/check_all.sh | 72 + compiler/tfts/requires.cmake | 2 + compiler/v4tf/README.md | 16 + compute/ARMComputeEx/CMakeLists.txt | 6 +- .../arm_compute/core/CL/CLKernelLibraryEx.h | 25 +- .../core/CL/kernels/CLArgOperationKernel.h | 25 +- .../core/CL/kernels/CLBinaryLogicalOpKernel.h | 26 +- .../arm_compute/core/CL/kernels/CLCastKernel.h | 25 +- .../core/CL/kernels/CLDepthToSpaceKernel.h | 26 +- .../core/CL/kernels/CLEmbeddingLookupKernel.h | 25 +- .../CL/kernels/CLGEMMLowpMatrixMultiplyKernelEx.h | 117 + .../arm_compute/core/CL/kernels/CLGatherExKernel.h | 25 +- .../core/CL/kernels/CLHashtableLookupKernel.h | 25 +- .../kernels/CLInstanceNormalizationLayerKernelEx.h | 16 + .../core/CL/kernels/CLMultiplyScaleFactorKernel.h | 106 + .../arm_compute/core/CL/kernels/CLNegKernel.h | 26 +- .../arm_compute/core/CL/kernels/CLPReLUKernel.h | 26 +- .../CL/kernels/CLQuantizationSymmetricKernel.h | 101 + .../core/CL/kernels/CLReduceOperationKernel.h | 25 +- .../core/CL/kernels/CLScaleFactorSymm8Kernel.h | 102 + .../core/CL/kernels/CLSpaceToBatchNDKernel.h | 26 +- .../core/CL/kernels/CLSpaceToDepthKernel.h | 26 +- .../arm_compute/core/CL/kernels/CLTopKV2Kernel.h | 25 +- .../kernels/CLTransposeConvLayerUpsampleKernel.h | 26 +- .../core/CPP/kernels/CPPOneHotKernelEx.h | 110 + .../core/CPP/kernels/CPPUpsampleKernelEx.h | 16 + .../core/NEON/NEElementwiseOperationFuncs.h | 15 + .../core/NEON/kernels/NEActivationLayerKernelEx.h | 135 + .../NEON/kernels/NEBinaryLogicalOperationKernel.h | 16 + .../arm_compute/core/NEON/kernels/NECastKernel.h | 16 + .../NEON/kernels/NEDepthToSpaceLayerKernelEx.h | 16 + .../core/NEON/kernels/NEElementwiseUnaryKernelEx.h | 16 + .../core/NEON/kernels/NEEmbeddingLookupKernel.h | 16 + .../core/NEON/kernels/NEGatherKernelEx.h | 15 + .../core/NEON/kernels/NEHashtableLookupKernel.h | 16 + .../kernels/NEInstanceNormalizationLayerKernelEx.h | 16 + .../core/NEON/kernels/NEMuliplyScaleFactorKernel.h | 16 + .../arm_compute/core/NEON/kernels/NEPReLUKernel.h | 16 + .../NEON/kernels/NEQuantizationSymmetricKernel.h | 16 + .../NEON/kernels/NEReductionOperationKernelEx.h | 16 + .../NEON/kernels/NESpaceToDepthLayerKernelEx.h | 16 + compute/ARMComputeEx/arm_compute/core/TypesEx.h | 26 +- compute/ARMComputeEx/arm_compute/core/UtilsEx.h | 26 +- .../core/utils/misc/ShapeCalculatorEx.h | 25 +- .../runtime/CL/functions/CLArgOperation.h | 25 +- .../runtime/CL/functions/CLBatchToSpaceND.h | 26 +- .../runtime/CL/functions/CLBinaryLogicalOp.h | 26 +- .../arm_compute/runtime/CL/functions/CLCast.h | 25 +- .../runtime/CL/functions/CLDepthToSpace.h | 26 +- .../runtime/CL/functions/CLEmbeddingLookup.h | 25 +- .../CL/functions/CLFullyConnectedHybridLayer.h | 186 + .../runtime/CL/functions/CLFullyConnectedLayerEx.h | 235 + .../CL/functions/CLFullyConnectedReshapingLayer.h | 22 +- .../CL/functions/CLGEMMLowpMatrixMultiplyCoreEx.h | 142 + .../arm_compute/runtime/CL/functions/CLGatherEx.h | 25 +- .../runtime/CL/functions/CLHashtableLookup.h | 25 +- .../CL/functions/CLInstanceNormalizationLayerEx.h | 16 + .../runtime/CL/functions/CLLogicalNot.h | 26 +- .../arm_compute/runtime/CL/functions/CLNeg.h | 26 +- .../arm_compute/runtime/CL/functions/CLPReLU.h | 26 +- .../runtime/CL/functions/CLPixelWiseDivision.h | 25 +- .../runtime/CL/functions/CLRNNLayerEx.h | 18 +- .../runtime/CL/functions/CLReduceOperation.h | 29 +- .../runtime/CL/functions/CLSpaceToBatchND.h | 26 +- .../runtime/CL/functions/CLSpaceToDepth.h | 26 +- .../runtime/CL/functions/CLStridedSliceEx.h | 25 +- .../arm_compute/runtime/CL/functions/CLTopKV2.h | 25 +- .../runtime/CL/functions/CLTransposeConvLayer.h | 19 +- .../CL/functions/CLTransposeConvLayerUpsample.h | 27 +- .../runtime/CPP/functions/CPPOneHotEx.h | 68 + .../runtime/CPP/functions/CPPUpsampleEx.h | 16 + .../arm_compute/runtime/NEON/NEFunctionsEx.h | 3 +- .../runtime/NEON/functions/NEActivationLayerEx.h | 103 + .../runtime/NEON/functions/NEArgMinMax.h | 81 - .../NEON/functions/NEBinaryLogicalOperation.h | 16 + .../arm_compute/runtime/NEON/functions/NECast.h | 16 + .../runtime/NEON/functions/NEDepthToSpaceLayerEx.h | 16 + .../NEON/functions/NEElementwiseUnaryLayerEx.h | 16 + .../runtime/NEON/functions/NEEmbeddingLookup.h | 25 +- .../NEON/functions/NEFullyConnectedHybridLayer.h | 16 + .../NEON/functions/NEFullyConnectedLayerEx.h | 16 + .../functions/NEGEMMLowpMatrixMultiplyCoreEx.h | 19 +- .../runtime/NEON/functions/NEGatherEx.h | 15 + .../runtime/NEON/functions/NEHashtableLookup.h | 25 +- .../functions/NEInstanceNormalizationLayerEx.h | 16 + .../arm_compute/runtime/NEON/functions/NEPReLU.h | 16 + .../runtime/NEON/functions/NERNNLayerEx.h | 16 + .../runtime/NEON/functions/NEReduceMeanEx.h | 16 + .../runtime/NEON/functions/NEReduceOperation.h | 17 + .../runtime/NEON/functions/NEReduceSum.h | 16 + .../NEON/functions/NEReductionOperationEx.h | 16 + .../runtime/NEON/functions/NESpaceToBatchLayerEx.h | 16 + .../runtime/NEON/functions/NESpaceToDepthLayerEx.h | 16 + .../runtime/NEON/functions/NETransposeConvLayer.h | 16 + .../arm_compute/runtime/misc/functions/Utils.h | 4 +- compute/ARMComputeEx/resolve_includes.py | 14 + .../ARMComputeEx/src/core/CL/CLKernelLibrary.cpp | 44 +- .../src/core/CL/cl_kernels/arg_operation.cl | 26 +- .../core/CL/cl_kernels/arithmetic_op_quantized.cl | 26 +- .../src/core/CL/cl_kernels/binary_logical_op.cl | 26 +- .../ARMComputeEx/src/core/CL/cl_kernels/cast.cl | 26 +- .../src/core/CL/cl_kernels/depth_to_space.cl | 26 +- .../src/core/CL/cl_kernels/embedding_lookup.cl | 26 +- .../src/core/CL/cl_kernels/gather_ex.cl | 26 +- .../src/core/CL/cl_kernels/gemmlowp_ex.cl | 354 + .../src/core/CL/cl_kernels/hashtable_lookup.cl | 26 +- .../ARMComputeEx/src/core/CL/cl_kernels/helpers.h | 17 + .../src/core/CL/cl_kernels/helpers_asymm.h | 19 +- .../CL/cl_kernels/instance_normalization_ex.cl | 16 + .../core/CL/cl_kernels/multiply_scale_factor.cl | 122 + .../src/core/CL/cl_kernels/neg_tensor.cl | 26 +- .../core/CL/cl_kernels/pixelwise_mul_quantized.cl | 26 +- .../ARMComputeEx/src/core/CL/cl_kernels/prelu.cl | 26 +- .../src/core/CL/cl_kernels/prelu_quantized.cl | 26 +- .../src/core/CL/cl_kernels/quantization_symm8.cl | 136 + .../src/core/CL/cl_kernels/reduce_operation.cl | 26 +- .../src/core/CL/cl_kernels/scale_factor.cl | 108 + .../src/core/CL/cl_kernels/space_to_batch.cl | 26 +- .../src/core/CL/cl_kernels/space_to_depth.cl | 26 +- .../ARMComputeEx/src/core/CL/cl_kernels/topkv2.cl | 25 +- .../src/core/CL/cl_kernels/topkv2_quicksort.cl | 25 +- .../src/core/CL/cl_kernels/topkv2_radixsort.cl | 25 +- .../src/core/CL/kernels/CLArgOperationKernel.cpp | 26 +- .../core/CL/kernels/CLBinaryLogicalOpKernel.cpp | 26 +- .../src/core/CL/kernels/CLCastKernel.cpp | 37 +- .../src/core/CL/kernels/CLDepthToSpaceKernel.cpp | 26 +- .../core/CL/kernels/CLEmbeddingLookupKernel.cpp | 26 +- .../kernels/CLGEMMLowpMatrixMultiplyKernelEx.cpp | 372 + .../src/core/CL/kernels/CLGatherExKernel.cpp | 26 +- .../core/CL/kernels/CLHashtableLookupKernel.cpp | 26 +- .../CLInstanceNormalizationLayerKernelEx.cpp | 16 + .../CL/kernels/CLMultiplyScaleFactorKernel.cpp | 173 + .../src/core/CL/kernels/CLNegKernel.cpp | 26 +- .../src/core/CL/kernels/CLPReLUKernel.cpp | 50 +- .../CL/kernels/CLQuantizationSymmetricKernel.cpp | 172 + .../core/CL/kernels/CLReduceOperationKernel.cpp | 26 +- .../core/CL/kernels/CLScaleFactorSymm8Kernel.cpp | 154 + .../src/core/CL/kernels/CLSpaceToBatchNDKernel.cpp | 30 +- .../src/core/CL/kernels/CLSpaceToDepthKernel.cpp | 26 +- .../src/core/CL/kernels/CLTopKV2Kernel.cpp | 26 +- .../kernels/CLTransposeConvLayerUpsampleKernel.cpp | 26 +- .../src/core/CPP/kernels/CPPOneHotKernelEx.cpp | 103 + .../src/core/CPP/kernels/CPPUpsampleKernelEx.cpp | 18 +- .../src/core/NEON/NEElementwiseOperationFuncs.cpp | 78 +- .../NEON/kernels/NEActivationLayerKernelEx.cpp | 730 + .../kernels/NEBinaryLogicalOperationKernel.cpp | 16 + .../src/core/NEON/kernels/NECastKernel.cpp | 30 +- .../NEON/kernels/NEDepthToSpaceLayerKernelEx.cpp | 16 + .../NEON/kernels/NEElementwiseUnaryKernelEx.cpp | 16 + .../core/NEON/kernels/NEEmbeddingLookupKernel.cpp | 16 + .../src/core/NEON/kernels/NEGatherKernelEx.cpp | 16 + .../core/NEON/kernels/NEHashtableLookupKernel.cpp | 18 +- .../NEInstanceNormalizationLayerKernelEx.cpp | 16 + .../NEON/kernels/NEMultiplyScaleFactorKernel.cpp | 22 +- .../src/core/NEON/kernels/NEPReLUKernel.cpp | 19 +- .../NEON/kernels/NEQuantizationSymmetricKernel.cpp | 16 + .../NEON/kernels/NEReductionOperationKernelEx.cpp | 16 + .../NEON/kernels/NESpaceToDepthLayerKernelEx.cpp | 16 + compute/ARMComputeEx/src/core/UtilsEx.cpp | 26 +- .../src/runtime/CL/functions/CLArgOperation.cpp | 26 +- .../src/runtime/CL/functions/CLBinaryLogicalOp.cpp | 26 +- .../src/runtime/CL/functions/CLCast.cpp | 26 +- .../src/runtime/CL/functions/CLDepthToSpace.cpp | 26 +- .../src/runtime/CL/functions/CLEmbeddingLookup.cpp | 26 +- .../CL/functions/CLFullyConnectedHybridLayer.cpp | 337 + .../CL/functions/CLFullyConnectedLayerEx.cpp | 583 + .../functions/CLFullyConnectedReshapingLayer.cpp | 50 +- .../functions/CLGEMMLowpMatrixMultiplyCoreEx.cpp | 180 + .../src/runtime/CL/functions/CLGatherEx.cpp | 26 +- .../src/runtime/CL/functions/CLHashtableLookup.cpp | 26 +- .../functions/CLInstanceNormalizationLayerEx.cpp | 16 + .../src/runtime/CL/functions/CLNeg.cpp | 26 +- .../src/runtime/CL/functions/CLPReLU.cpp | 26 +- .../src/runtime/CL/functions/CLRNNLayerEx.cpp | 16 + .../src/runtime/CL/functions/CLReduceOperation.cpp | 26 +- .../src/runtime/CL/functions/CLSpaceToBatchND.cpp | 26 +- .../src/runtime/CL/functions/CLSpaceToDepth.cpp | 26 +- .../src/runtime/CL/functions/CLTopKV2.cpp | 26 +- .../runtime/CL/functions/CLTransposeConvLayer.cpp | 16 + .../CL/functions/CLTransposeConvLayerUpsample.cpp | 29 +- .../src/runtime/CPP/functions/CPPOneHotEx.cpp | 54 + .../src/runtime/CPP/functions/CPPUpsampleEx.cpp | 16 + .../runtime/NEON/functions/NEActivationLayerEx.cpp | 66 + .../src/runtime/NEON/functions/NEArgMinMax.cpp | 109 - .../NEON/functions/NEBinaryLogicalOperation.cpp | 16 + .../src/runtime/NEON/functions/NECast.cpp | 15 + .../NEON/functions/NEDepthToSpaceLayerEx.cpp | 15 + .../NEON/functions/NEElementwiseUnaryLayerEx.cpp | 44 - .../runtime/NEON/functions/NEEmbeddingLookup.cpp | 26 +- .../NEON/functions/NEFullyConnectedHybridLayer.cpp | 20 +- .../NEON/functions/NEFullyConnectedLayerEx.cpp | 41 +- .../functions/NEGEMMLowpMatrixMultiplyCoreEx.cpp | 36 +- .../src/runtime/NEON/functions/NEGatherEx.cpp | 16 + .../runtime/NEON/functions/NEHashtableLookup.cpp | 26 +- .../functions/NEInstanceNormalizationLayerEx.cpp | 16 + .../src/runtime/NEON/functions/NEPReLU.cpp | 16 + .../src/runtime/NEON/functions/NERNNLayerEx.cpp | 15 + .../src/runtime/NEON/functions/NEReduceMeanEx.cpp | 16 + .../runtime/NEON/functions/NEReduceOperation.cpp | 18 + .../src/runtime/NEON/functions/NEReduceSum.cpp | 16 + .../NEON/functions/NEReductionOperationEx.cpp | 16 + .../NEON/functions/NESpaceToBatchLayerEx.cpp | 15 + .../NEON/functions/NESpaceToDepthLayerEx.cpp | 15 + .../NEON/functions/NETransposeConvLayer.cpp | 16 + compute/cker/CMakeLists.txt | 14 +- compute/cker/README.md | 4 +- compute/cker/include/cker/NeonTensorUtils.h | 319 + compute/cker/include/cker/PortableTensorUtils.h | 167 + compute/cker/include/cker/Shape.h | 45 +- compute/cker/include/cker/TensorUtils.h | 78 + compute/cker/include/cker/Types.h | 231 +- compute/cker/include/cker/Utils.h | 106 +- compute/cker/include/cker/eigen/EigenSupport.h | 122 + compute/cker/include/cker/eigen/Utils.h | 23 +- .../include/cker/eigen/eigen_convolution_helpers.h | 88 + .../cker/eigen/eigen_spatial_convolutions-inl.h | 1754 ++ .../cker/eigen/eigen_spatial_convolutions.h | 27 + .../eigen_tensor_reduced_instantiations_oss.h | 170 + compute/cker/include/cker/gemmlowp/FixedPoint.h | 289 - compute/cker/include/cker/gemmlowp/GEMMSupport.h | 67 + compute/cker/include/cker/neon/neon_check.h | 48 + compute/cker/include/cker/operation/AveragePool.h | 361 +- .../include/cker/operation/BinaryArithmeticOps.h | 128 +- compute/cker/include/cker/operation/Common.h | 103 + compute/cker/include/cker/operation/Comparison.h | 146 + .../cker/include/cker/operation/Concatenation.h | 88 +- compute/cker/include/cker/operation/Conv.h | 259 +- .../cker/include/cker/operation/DepthwiseConv.h | 126 +- compute/cker/include/cker/operation/Elementwise.h | 62 + compute/cker/include/cker/operation/Exp.h | 43 + .../cker/include/cker/operation/FullyConnected.h | 138 +- compute/cker/include/cker/operation/Gather.h | 5 - compute/cker/include/cker/operation/InstanceNorm.h | 7 - compute/cker/include/cker/operation/MaxMin.h | 104 + compute/cker/include/cker/operation/MaxPool.h | 152 +- compute/cker/include/cker/operation/OneHot.h | 64 + compute/cker/include/cker/operation/Pack.h | 62 + compute/cker/include/cker/operation/Reduce.h | 170 + compute/cker/include/cker/operation/Slice.h | 82 + compute/cker/include/cker/operation/SoftMax.h | 30 +- compute/cker/include/cker/operation/Split.h | 65 + compute/cker/include/cker/operation/StridedSlice.h | 308 + compute/cker/include/cker/operation/Tanh.h | 42 + compute/cker/include/cker/operation/Transpose.h | 580 + .../cker/include/cker/operation/TransposeConv.h | 24 - compute/cker/include/cker/operation/Unpack.h | 63 + .../include/cker/operation/optimized/AveragePool.h | 105 - .../cker/operation/optimized/BinaryArithmeticOps.h | 202 + .../cker/include/cker/operation/optimized/Conv.h | 290 + .../cker/operation/optimized/DepthwiseConvUint8.h | 2123 ++ .../include/cker/operation/optimized/MaxPool.h | 97 - .../cker/operation/optimized/OptimizedUtils.h | 176 + .../include/cker/operation/optimized/SoftMax.h | 59 - .../include/cker/operation/reference/AveragePool.h | 90 - .../cker/operation/reference/BinaryArithmeticOps.h | 68 + .../cker/include/cker/operation/reference/Conv.h | 197 + .../include/cker/operation/reference/MaxPool.h | 84 - .../include/cker/operation/reference/SoftMax.h | 70 - compute/ncnn/CMakeLists.txt | 34 - compute/ncnn/README.md | 9 - compute/ncnn/include/ncnn/layer/binaryop.h | 69 - compute/ncnn/include/ncnn/layer/instance_norm.h | 59 - compute/ncnn/include/ncnn/mat.h | 738 - compute/ncnn/include/ncnn/srcn/conv_type.h | 74 - compute/ncnn/include/ncnn/srcn/srcn_conv.h | 65 - compute/ncnn/src/layer/arm/neon_mathfun.h | 315 - compute/ncnn/src/layer/binaryop.cc | 1640 -- compute/ncnn/src/layer/instance_norm.cc | 371 - compute/ncnn/src/mat.cc | 940 - compute/ncnn/src/srcn/common.h | 162 - compute/ncnn/src/srcn/conv_sgemm_multithreads.cc | 483 - compute/ncnn/src/srcn/conv_sgemm_multithreads.h | 86 - compute/ncnn/src/srcn/conv_sgemm_singlethread.cc | 366 - compute/ncnn/src/srcn/conv_sgemm_singlethread.h | 73 - compute/ncnn/src/srcn/conv_sparse.cc | 271 - compute/ncnn/src/srcn/conv_sparse.h | 79 - compute/ncnn/src/srcn/conv_winograd.cc | 341 - compute/ncnn/src/srcn/conv_winograd.h | 72 - compute/ncnn/src/srcn/conv_winograd_batch.cc | 304 - compute/ncnn/src/srcn/conv_winograd_batch.h | 67 - compute/ncnn/src/srcn/deconv_sgemm_multithreads.cc | 387 - compute/ncnn/src/srcn/deconv_sgemm_multithreads.h | 85 - compute/ncnn/src/srcn/depthwise_conv.cc | 2684 -- compute/ncnn/src/srcn/direct_conv_colmajor.cc | 5872 ---- compute/ncnn/src/srcn/direct_conv_colmajor.h | 33 - compute/ncnn/src/srcn/sgemm_kernel.cc | 2508 -- compute/ncnn/src/srcn/sgemm_kernel.h | 52 - compute/ncnn/src/srcn/sgemm_pack.cc | 2316 -- compute/ncnn/src/srcn/sgemm_pack.h | 73 - compute/ncnn/src/srcn/sgemm_singlethread.cc | 689 - compute/ncnn/src/srcn/sgemm_singlethread.h | 88 - compute/ncnn/src/srcn/sgemm_test.cc | 1883 -- compute/ncnn/src/srcn/srcn_conv.cc | 614 - compute/ncnn/src/srcn/winograd.h | 148 - docs/HowToContribute.md | 72 - docs/UseDoxygen.md | 36 - docs/fig/compiler_flow.png | Bin 56456 -> 0 bytes docs/fig/nnfw_compiler_structure.png | Bin 75343 -> 0 bytes docs/fig/nnfw_compiler_structure.pptx | Bin 40532 -> 0 bytes docs/fig/nnfw_components.png | Bin 82620 -> 0 bytes docs/fig/nnfw_components.pptx | Bin 46596 -> 0 bytes docs/fig/nnfw_nativeapi_flow.png | Bin 105745 -> 0 bytes docs/fig/nnfw_nativeapi_flow.pptx | Bin 51156 -> 0 bytes docs/fig/nnfw_nnapi_flow.png | Bin 52314 -> 0 bytes docs/fig/nnfw_nnapi_flow.pptx | Bin 45988 -> 0 bytes docs/fig/nnfw_runtime_behavior.png | Bin 51473 -> 0 bytes docs/fig/nnfw_runtime_behavior.pptx | Bin 45204 -> 0 bytes docs/fig/nnfw_runtime_structure.png | Bin 64652 -> 0 bytes docs/fig/nnfw_runtime_structure.pptx | Bin 41044 -> 0 bytes docs/fig/runtime_nativeapi_flow.png | Bin 63638 -> 0 bytes docs/nncc/README.md | 56 - docs/nncc/design.md | 10 - docs/nncc/getting_started.md | 73 - docs/nncc/images/nncc_components.png | Bin 45359 -> 0 bytes docs/nncc/images/nncc_idef0_a0.png | Bin 50434 -> 0 bytes docs/nncc/images/nncc_idef0_a1.png | Bin 86576 -> 0 bytes docs/nncc/images/nncc_idef0_a12.png | Bin 42778 -> 0 bytes docs/nncc/project/detailed_level_design.md | 329 - docs/nncc/project/development_document.md | 257 - docs/nncc/project/high_level_design.md | 457 - docs/nncc/project/requirements_specification.md | 272 - docs/nncc/project/test_plan.md | 442 - docs/nncc/project_guide.md | 27 - docs/nncc/roadmap.md | 6 - docs/nncc/v1.0.0/getting_started.md | 59 - docs/nncc/v1.0.0/operation-list.md | 34 - docs/nncc/v1.0.0/tutorial.md | 49 - docs/nncc/v1.1.0/nncc_in_tizen_studio.md | 52 - docs/nncc/v1.1.0/nncc_in_visual_studio.md | 61 - docs/nnfw/2018/fig/nnfw_architecture.png | Bin 28876 -> 0 bytes docs/nnfw/2018/fig/nnfw_architecture.pptx | Bin 72036 -> 0 bytes docs/nnfw/2018/roadmap.md | 123 - docs/nnfw/HowToImplementOperatorKernel.md | 1 - docs/nnfw/fig/nnfw_architecture.png | Bin 280284 -> 0 bytes docs/nnfw/fig/nnfw_architecture.pptx | Bin 45709 -> 0 bytes docs/nnfw/fig/nnfw_behavior.png | Bin 14254 -> 0 bytes docs/nnfw/fig/nnfw_behavior.pptx | Bin 59844 -> 0 bytes docs/nnfw/howto.md | 38 - docs/nnfw/howto/BuildTFfromSource.md | 66 - docs/nnfw/howto/CrossBuildForAarch64.md | 77 - docs/nnfw/howto/CrossBuildForAndroid.md | 52 - docs/nnfw/howto/CrossBuildForArm.md | 118 - docs/nnfw/howto/HowToAddUnittest.md | 31 - docs/nnfw/howto/HowToRunNnpackge.md | 75 - docs/nnfw/howto/HowToTestManualy.md | 62 - docs/nnfw/howto/HowToUseDockerImage.md | 154 - docs/nnfw/howto/HowToUseNNFWAPI.md | 63 - docs/nnfw/howto/HowtoMakeSampleAppOnNnfw.md | 132 - docs/nnfw/howto/RemoteDebuggingForVSCode.md | 147 - docs/nnfw/howto/device/xu3-dip.png | Bin 262925 -> 0 bytes docs/nnfw/howto/device/xu3_tizen.md | 140 - docs/nnfw/howto/device/xu3_ubuntu.md | 114 - docs/nnfw/howto/device/xu4_tizen.md | 228 - docs/nnfw/howto/device/xu4_ubuntu.md | 99 - docs/nnfw/op_list.md | 71 - docs/nnfw/roadmap.md | 76 - docs/nnfw/tests/Convolution_manual_3x3.xlsx | Bin 19844 -> 0 bytes docs/nnfw/tests/Softmax_manual.xlsx | Bin 15940 -> 0 bytes docs/release/release_note_1.0.0.md | 65 - docs/release/release_note_1.1.0.md | 40 - docs/release/release_note_1.4.0.md | 23 + infra/cmake/modules/ExternalBuildTools.cmake | 7 +- infra/cmake/packages/ARMComputeConfig.cmake | 3 +- infra/cmake/packages/ARMComputeSourceConfig.cmake | 2 +- infra/cmake/packages/BoostConfig.cmake | 29 +- infra/cmake/packages/EigenConfig.cmake | 2 + infra/cmake/packages/NNPACKSourceConfig.cmake | 2 +- .../TensorFlowSourceConfig.cmake | 18 + .../TensorFlowSourceConfigVersion.cmake | 10 + infra/cmake/packages/TensorFlowVersionChecker.c | 16 + infra/command/format | 83 +- infra/command/gen-coverage-report | 4 + infra/command/install-githooks | 68 +- infra/docker/Dockerfile | 2 +- infra/docker/Dockerfile.1804 | 4 +- infra/doxygen/Doxyfile | 2 +- infra/git-hooks/pre-commit.sh | 32 + infra/git-hooks/pre-push.sh | 2 +- infra/nncc/CMakeLists.txt | 2 +- infra/nncc/command/utcount | 41 + infra/nnfw/CMakeLists.txt | 8 +- infra/nnfw/cmake/ApplyCompileFlags.cmake | 2 +- infra/nnfw/cmake/CfgOptionFlags.cmake | 9 +- .../buildtool/config/config_armv7l-linux.cmake | 2 +- .../cross/toolchain_aarch64-android.cmake | 3 +- .../cmake/options/options_aarch64-android.cmake | 2 - .../nnfw/cmake/options/options_aarch64-tizen.cmake | 2 +- .../nnfw/cmake/options/options_armv7l-tizen.cmake | 3 +- infra/nnfw/cmake/packages/EigenConfig.cmake | 2 + infra/nnfw/cmake/packages/GEMMLowpConfig.cmake | 20 + infra/nnfw/command/copyright-check | 5 +- infra/nnfw/command/gen-coverage-report | 59 - infra/nnfw/config/docker.configuration | 2 + infra/packaging/preset/20191231_windows | 3 + infra/packaging/preset/20200115_windows | 3 + infra/packaging/preset/20200220 | 44 + infra/packaging/res/tf2nnpkg.20200220 | 89 + infra/scripts/build_android_runtime_release.sh | 21 + infra/scripts/common.sh | 0 .../scripts/docker_build_cross_aarch64_runtime.sh | 48 + .../docker_build_cross_arm_benchmark_model.sh | 2 +- infra/scripts/docker_build_cross_arm_neurun.sh | 48 - .../docker_build_cross_arm_neurun_release.sh | 49 - infra/scripts/docker_build_cross_arm_runtime.sh | 48 + .../docker_build_cross_arm_runtime_release.sh | 49 + infra/scripts/docker_build_test_x64.sh | 5 +- infra/scripts/test_arm_neurun_acl_cl.sh | 31 - infra/scripts/test_arm_neurun_acl_neon.sh | 27 - infra/scripts/test_arm_neurun_cpu.sh | 23 - infra/scripts/test_arm_neurun_mixed.sh | 30 - infra/scripts/test_arm_nnpkg.sh | 2 +- infra/scripts/test_coverage.sh | 12 +- infra/scripts/test_neurun_interp.sh | 11 - infra/scripts/test_ubuntu_runtime.sh | 84 + infra/scripts/test_ubuntu_runtime_interp.sh | 12 + infra/scripts/test_ubuntu_runtime_mixed.sh | 37 + infra/scripts/test_x64_neurun_cpu.sh | 12 - infra/scripts/tizen_xu4_test.sh | 28 +- nnpackage/schema/circle_schema.fbs | 325 +- nnpackage/schema/circle_schema_v0.fbs | 811 + nnpackage/spec/30_custom_op.md | 2 +- packaging/eigen.tar.gz | Bin 0 -> 2502909 bytes packaging/gemmlowp.tar.gz | Bin 0 -> 830368 bytes packaging/nnfw.spec | 61 +- res/ONNXTests/UNIT_Gemm_000/test.pbtxt | 79 + res/ONNXTests/UNIT_Gemm_001/test.pbtxt | 70 + res/TensorFlowLiteRecipes/Abs_000/test.recipe | 17 + res/TensorFlowLiteRecipes/Abs_000/test.reverse | 0 res/TensorFlowLiteRecipes/Add_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Add_000/test.reverse | 0 res/TensorFlowLiteRecipes/Add_U8_000/test.recipe | 30 + res/TensorFlowLiteRecipes/Add_U8_000/test.reverse | 0 res/TensorFlowLiteRecipes/ArgMax_000/test.recipe | 30 + res/TensorFlowLiteRecipes/ArgMax_000/test.reverse | 0 res/TensorFlowLiteRecipes/ArgMax_001/test.recipe | 30 + res/TensorFlowLiteRecipes/ArgMax_001/test.reverse | 0 res/TensorFlowLiteRecipes/ArgMax_002/test.recipe | 30 + res/TensorFlowLiteRecipes/ArgMax_002/test.reverse | 0 res/TensorFlowLiteRecipes/ArgMax_003/test.recipe | 30 + res/TensorFlowLiteRecipes/ArgMax_003/test.reverse | 0 .../ArgMax_U8_000/test.recipe | 31 + .../ArgMax_U8_000/test.reverse | 0 .../ArgMax_U8_001/test.recipe | 31 + .../ArgMax_U8_001/test.reverse | 0 .../ArgMax_U8_002/test.recipe | 31 + .../ArgMax_U8_002/test.reverse | 0 .../ArgMax_U8_003/test.recipe | 31 + .../ArgMax_U8_003/test.reverse | 0 .../AveragePool2D_000/test.recipe | 24 + .../AveragePool2D_000/test.reverse | 0 .../BatchToSpaceND_000/test.recipe | 38 + .../BatchToSpaceND_000/test.reverse | 0 .../Concatenation_000/test.recipe | 28 + .../Concatenation_000/test.reverse | 0 .../Concatenation_U8_000/test.recipe | 31 + res/TensorFlowLiteRecipes/Conv2D_000/test.recipe | 44 + res/TensorFlowLiteRecipes/Conv2D_000/test.reverse | 0 res/TensorFlowLiteRecipes/Conv2D_001/test.recipe | 44 + res/TensorFlowLiteRecipes/Conv2D_001/test.reverse | 0 res/TensorFlowLiteRecipes/Conv2D_002/test.recipe | 45 + .../Conv2D_U8_000/test.recipe | 48 + res/TensorFlowLiteRecipes/Cos_000/test.recipe | 17 + res/TensorFlowLiteRecipes/Cos_000/test.reverse | 0 .../DepthwiseConv2D_000/test.recipe | 41 + .../DepthwiseConv2D_000/test.reverse | 0 .../DepthwiseConv2D_U8_000/test.recipe | 46 + .../DepthwiseConv2D_U8_000/test.reverse | 0 res/TensorFlowLiteRecipes/Div_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Div_000/test.reverse | 0 res/TensorFlowLiteRecipes/Equal_000/test.recipe | 26 + res/TensorFlowLiteRecipes/Equal_000/test.reverse | 0 res/TensorFlowLiteRecipes/Exp_000/test.recipe | 17 + res/TensorFlowLiteRecipes/Exp_000/test.reverse | 0 .../FullyConnected_000/test.recipe | 34 + .../FullyConnected_000/test.reverse | 0 .../FullyConnected_001/test.recipe | 34 + .../FullyConnected_001/test.reverse | 0 .../FullyConnected_U8_000/test.recipe | 35 + .../FullyConnected_U8_000/test.reverse | 0 .../LogicalNot_000/test.recipe | 17 + .../LogicalNot_000/test.reverse | 0 .../LogicalOr_000/test.recipe | 24 + .../LogicalOr_000/test.reverse | 0 .../MaxPool2D_000/test.recipe | 24 + .../MaxPool2D_000/test.reverse | 0 .../MaxPool2D_U8_000/test.recipe | 26 + res/TensorFlowLiteRecipes/Mean_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Mean_000/test.reverse | 0 res/TensorFlowLiteRecipes/Mul_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Mul_U8_000/test.recipe | 30 + res/TensorFlowLiteRecipes/Pack_000/test.recipe | 28 + res/TensorFlowLiteRecipes/Pack_000/test.reverse | 0 res/TensorFlowLiteRecipes/Pack_U8_000/test.recipe | 31 + res/TensorFlowLiteRecipes/Pack_U8_000/test.reverse | 0 res/TensorFlowLiteRecipes/Pad_000/test.recipe | 30 + res/TensorFlowLiteRecipes/Pad_000/test.reverse | 0 res/TensorFlowLiteRecipes/Pad_U8_000/test.recipe | 32 + res/TensorFlowLiteRecipes/Pad_U8_000/test.reverse | 0 .../Quantization_000/test.recipe | 46 + .../Quantization_000/test.reverse | 0 res/TensorFlowLiteRecipes/ReLU6_000/test.recipe | 17 + res/TensorFlowLiteRecipes/ReLU6_000/test.reverse | 0 res/TensorFlowLiteRecipes/ReLU_000/test.recipe | 17 + res/TensorFlowLiteRecipes/ReLU_000/test.reverse | 0 res/TensorFlowLiteRecipes/Reshape_000/test.recipe | 20 + res/TensorFlowLiteRecipes/Reshape_000/test.reverse | 0 res/TensorFlowLiteRecipes/Reshape_001/test.recipe | 28 + res/TensorFlowLiteRecipes/Reshape_001/test.reverse | 0 .../Reshape_U8_000/test.recipe | 22 + .../Reshape_U8_000/test.reverse | 0 res/TensorFlowLiteRecipes/Rsqrt_000/test.recipe | 17 + res/TensorFlowLiteRecipes/Rsqrt_000/test.reverse | 0 res/TensorFlowLiteRecipes/Softmax_000/test.recipe | 20 + res/TensorFlowLiteRecipes/Softmax_000/test.reverse | 0 .../Softmax_U8_000/test.recipe | 22 + .../Softmax_U8_000/test.reverse | 0 res/TensorFlowLiteRecipes/Sqrt_000/test.recipe | 18 + res/TensorFlowLiteRecipes/Sqrt_000/test.reverse | 0 res/TensorFlowLiteRecipes/Sub_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Sub_000/test.reverse | 0 res/TensorFlowLiteRecipes/Sub_001/test.recipe | 42 + res/TensorFlowLiteRecipes/Sub_001/test.reverse | 0 res/TensorFlowLiteRecipes/Sub_U8_000/test.recipe | 30 + res/TensorFlowLiteRecipes/Sub_U8_000/test.reverse | 0 .../Transpose_000/test.recipe | 27 + .../Transpose_000/test.reverse | 0 res/TensorFlowLiteSchema/1.13.1/schema.fbs | 794 + res/TensorFlowLiteSchema/1.14.0/schema.fbs | 873 + res/TensorFlowLiteSchema/1.15.2/schema.fbs | 922 + res/TensorFlowLiteSchema/2.1.0/schema.fbs | 940 + res/TensorFlowLiteSchema/README.md | 7 + res/TensorFlowLiteSchema/SCHEMA.lst | 5 + res/TensorFlowLiteSchema/download.sh | 9 + res/TensorFlowPythonExamples/.gitignore | 1 + res/TensorFlowPythonExamples/README.md | 31 + .../examples/abs/__init__.py | 4 + .../examples/add/__init__.py | 5 + .../examples/argmax/__init__.py | 4 + .../examples/biasadd/__init__.py | 4 + .../examples/cos/__init__.py | 4 + .../examples/div/__init__.py | 5 + .../examples/elu/__init__.py | 4 + .../examples/exp/__init__.py | 4 + .../examples/floor/__init__.py | 4 + .../examples/floordiv/__init__.py | 5 + .../examples/greater/__init__.py | 5 + .../examples/greater_equal/__init__.py | 5 + .../examples/leaky_relu/__init__.py | 4 + .../examples/less/__init__.py | 5 + .../examples/less_equal/__init__.py | 5 + .../examples/logical_not/__init__.py | 4 + .../examples/logical_or/__init__.py | 5 + .../examples/matmul/__init__.py | 5 + .../examples/multiply/__init__.py | 5 + .../examples/not_equal/__init__.py | 5 + .../examples/pack/__init__.py | 5 + .../examples/pad/__init__.py | 5 + .../examples/pow/__init__.py | 5 + .../examples/prelu/__init__.py | 7 + .../examples/relu/__init__.py | 4 + .../examples/relu6/__init__.py | 4 + .../examples/reshape/__init.py__ | 4 + .../examples/resize_bilinear/__init__.py | 4 + .../examples/resize_nearest_neighbor/__init__.py | 4 + .../examples/rsqrt/__init__.py | 4 + .../examples/sigmoid/__init__.py | 4 + .../examples/softmax/__init__.py | 4 + .../examples/sqrt/__init__.py | 4 + .../examples/subtract/__init__.py | 5 + .../examples/tanh/__init__.py | 4 + .../examples/yuv_to_rgb/__init__.py | 4 + res/TensorFlowPythonExamples/requirements.txt | 18 + res/TensorFlowPythonExamples/tfpem.py | 25 + res/TensorFlowTests/NET_0003/test.py | 0 res/TensorFlowTests/NET_0004/test.py | 0 res/TensorFlowTests/UNIT_Maximum_000/test.info | 3 + res/TensorFlowTests/UNIT_Maximum_000/test.pbtxt | 70 + res/TensorFlowTests/UNIT_Maximum_001/test.info | 3 + res/TensorFlowTests/UNIT_Maximum_001/test.pbtxt | 70 + res/TensorFlowTests/UNIT_Maximum_002/test.info | 3 + res/TensorFlowTests/UNIT_Maximum_002/test.pbtxt | 61 + runtime/contrib/README.md | 10 - .../TFLiteSharp/TFLiteNative/include/tflite_log.h | 2 +- .../TFLiteNative/include/tflite_nativewrapper.h | 2 +- .../TFLiteNative/src/tflite_nativewrapper.cpp | 2 +- .../contrib/android_benchmark_app/CMakeLists.txt | 10 +- runtime/contrib/android_benchmark_app/README.md | 4 +- .../contrib/android_benchmark_app/cpp/ndk_main.cpp | 16 + .../contrib/android_benchmark_app/cpp/ndk_main.h | 16 + runtime/contrib/android_tflite/builtin_ops_jni.cc | 31 +- runtime/contrib/custom_op/customOp-workflow.png | Bin 22082 -> 0 bytes runtime/contrib/heap_trace/CMakeLists.txt | 5 + .../contrib/heap_trace/src/aligned_alloc_stub.cc | 45 + runtime/contrib/heap_trace/src/calloc_stub.cc | 46 + .../heap_trace/src/cl_retain_mem_object_stub.cc | 43 + runtime/contrib/heap_trace/src/free_stub.cc | 7 + runtime/contrib/heap_trace/src/malloc_stub.cc | 6 + .../memory_pool_for_symbol_searcher_internals.cc | 21 + .../memory_pool_for_symbol_searcher_internals.h | 78 + .../contrib/heap_trace/src/posix_memalign_stub.cc | 46 + runtime/contrib/heap_trace/src/realloc_stub.cc | 6 + runtime/contrib/heap_trace/src/symbol_searcher.cc | 22 +- runtime/contrib/heap_trace/src/symbol_searcher.h | 4 + runtime/contrib/heap_trace/src/trace.cc | 32 +- runtime/contrib/heap_trace/src/trace.h | 15 +- runtime/contrib/heap_trace/src/valloc_stub.cc | 6 + runtime/contrib/heap_trace/tests/CMakeLists.txt | 5 + .../tests/src/aligned_alloc_interception_test.cc | 90 + .../tests/src/calloc_interception_test.cc | 91 + .../src/cl_release_mem_object_interception_test.cc | 23 + .../src/cl_retain_mem_object_interception_test.cc | 85 + .../heap_trace/tests/src/free_interception_test.cc | 17 + .../tests/src/malloc_interception_test.cc | 18 +- ...mory_pool_for_symbol_searcher_internals_test.cc | 69 + .../tests/src/posix_memalign_interception_test.cc | 101 + .../tests/src/realloc_interception_test.cc | 20 +- .../heap_trace/tests/src/symbol_searcher_test.cc | 28 +- .../tests/src/valloc_interception_test.cc | 18 +- runtime/contrib/hi_perf_cpu/CMakeLists.txt | 47 + .../hi_perf_cpu/HighPerformanceBackend.test.cc | 42 + runtime/contrib/hi_perf_cpu/KernelGenerator.cc | 18 + runtime/contrib/hi_perf_cpu/KernelGenerator.h | 47 + runtime/contrib/hi_perf_cpu/TensorBuilder.cc | 18 + runtime/contrib/hi_perf_cpu/TensorBuilder.h | 44 + runtime/contrib/labs/jniacl/src/jniacl_main.cc | 23 +- runtime/contrib/labs/tflite_examples/src/conv.cpp | 6 +- runtime/contrib/logging/src/nnapi_logging.cc | 18 +- runtime/contrib/mlapse/tfl/CMakeLists.txt | 1 - runtime/contrib/mlapse/tfl/driver.cc | 11 +- .../contrib/mlapse/tfl/mlapse/benchmark_observer.h | 2 +- .../contrib/mlapse/tfl/mlapse/benchmark_runner.h | 2 +- runtime/contrib/pure_arm_compute/CMakeLists.txt | 2 +- .../contrib/pure_arm_compute/src/compilation.cc | 159 +- .../pure_arm_compute/src/internal/MatrixSink.h | 4 +- .../pure_arm_compute/src/internal/MatrixSource.h | 4 +- .../contrib/pure_arm_compute/src/internal/Sinks.h | 2 +- .../pure_arm_compute/src/internal/Tensor3DSink.h | 4 +- .../pure_arm_compute/src/internal/Tensor3DSource.h | 4 +- .../pure_arm_compute/src/internal/arm_compute.cc | 6 +- .../pure_arm_compute/src/internal/arm_compute.h | 13 +- .../src/internal/arm_compute/Cast.h | 10 +- .../src/internal/op/BatchToSpaceNd.cc | 3 +- .../pure_arm_compute/src/internal/op/Mean.h | 12 +- runtime/contrib/pure_arm_compute/src/memory.cc | 4 +- runtime/contrib/pure_arm_compute/src/model.cc | 128 +- runtime/contrib/style_transfer_app/CMakeLists.txt | 40 + runtime/contrib/style_transfer_app/README.md | 23 + runtime/contrib/style_transfer_app/src/args.cc | 96 + runtime/contrib/style_transfer_app/src/args.h | 53 + .../style_transfer_app/src/bitmap_helper.cc | 236 + .../contrib/style_transfer_app/src/bitmap_helper.h | 61 + .../contrib/style_transfer_app/src/jpeg_helper.cc | 132 + .../contrib/style_transfer_app/src/jpeg_helper.h | 43 + .../style_transfer_app/src/style_transfer_app.cc | 301 + .../contrib/tflite_classify/src/tflite_classify.cc | 14 +- runtime/contrib/tflite_test/tflite_test.cpp | 7 +- .../libs/benchmark/include/benchmark/CsvWriter.h | 1 + runtime/libs/benchmark/include/benchmark/Phase.h | 14 + runtime/libs/benchmark/include/benchmark/Result.h | 2 +- runtime/libs/benchmark/include/benchmark/Util.h | 9 +- runtime/libs/benchmark/src/CsvWriter.cpp | 7 +- runtime/libs/benchmark/src/MemoryPoller.cpp | 2 +- runtime/libs/cpp14/CMakeLists.txt | 2 - runtime/libs/cpp14/include/cpp14/memory.h | 66 - runtime/libs/jsoncpp/.FORMATDENY | 0 runtime/libs/misc/CMakeLists.txt | 6 + .../libs/misc/examples/tensor_index_iterator.cpp | 2 +- runtime/libs/misc/include/misc/EventRecorder.h | 1 + runtime/libs/misc/include/misc/benchmark.h | 4 +- runtime/libs/misc/include/misc/string_helpers.h | 2 +- runtime/libs/misc/src/tensor/Comparator.cpp | 16 + .../libs/profiling/include/profiling/profiling.h | 2 +- runtime/libs/profiling/include/profiling/time.h | 30 +- runtime/libs/profiling/src/profiling/time.cpp | 30 +- runtime/libs/rua/core/include/rua/Service.h | 1 + runtime/libs/rua/dyn/include/rua/DynamicBinder.h | 2 +- runtime/libs/rua/dyn/src/DynamicBinder.cpp | 1 + runtime/libs/rua/shim/include/rua/Shim.h | 1 + runtime/libs/tflite/CMakeLists.txt | 10 + runtime/libs/tflite/include/tflite/Diff.h | 1 + runtime/libs/tflite/port/1.13.1/CMakeLists.txt | 2 - .../port/1.13.1/include/tflite/ext/kernels/Abs.h | 41 - .../1.13.1/include/tflite/ext/kernels/CustomOps.h | 6 - .../include/tflite/ext/kernels/TensorFlowMax.h | 75 - .../include/tflite/ext/kernels/TensorFlowSum.h | 41 - .../libs/tflite/port/1.13.1/src/kernels/Abs.cpp | 103 - .../port/1.13.1/src/kernels/TensorFlowMax.cpp | 405 - .../port/1.13.1/src/kernels/TensorFlowSum.cpp | 400 - .../tflite/port/1.13.1/src/kernels/register.cpp | 3 - .../libs/tflite/port/1.13.1/src/nnapi_delegate.cpp | 196 +- .../nnapi_delegate_ex_AddOpsAndParams_lambda.inc | 32 +- runtime/libs/tflite/src/Diff.cpp | 17 +- runtime/libs/tflite/src/TensorShapeUtils.cpp | 16 + runtime/neurun/CMakeLists.txt | 16 - runtime/neurun/api/CMakeLists.txt | 21 - runtime/neurun/api/include/nnfw.h | 378 - runtime/neurun/api/include/nnfw_debug.h | 24 - runtime/neurun/api/include/nnfw_dev.h | 65 - runtime/neurun/api/src/CustomKernel.cc | 98 - runtime/neurun/api/src/CustomKernel.h | 59 - runtime/neurun/api/src/CustomKernelRegistry.cc | 64 - runtime/neurun/api/src/CustomKernelRegistry.h | 64 - runtime/neurun/api/src/OpMap.lst | 89 - runtime/neurun/api/src/nnfw_api.cc | 267 - runtime/neurun/api/src/nnfw_api_internal.cc | 435 - runtime/neurun/api/src/nnfw_api_internal.h | 84 - runtime/neurun/api/src/nnfw_debug.cc | 24 - runtime/neurun/api/src/nnfw_debug_internal.cc | 25 - runtime/neurun/api/src/nnfw_debug_internal.h | 29 - runtime/neurun/backend/CMakeLists.txt | 10 - runtime/neurun/backend/acl_cl/Backend.h | 65 - runtime/neurun/backend/acl_cl/CLTimer.h | 108 - runtime/neurun/backend/acl_cl/CMakeLists.txt | 21 - runtime/neurun/backend/acl_cl/Config.cc | 50 - runtime/neurun/backend/acl_cl/Config.h | 45 - .../neurun/backend/acl_cl/ConstantInitializer.cc | 266 - .../neurun/backend/acl_cl/ConstantInitializer.h | 63 - runtime/neurun/backend/acl_cl/KernelGenerator.cc | 2151 -- runtime/neurun/backend/acl_cl/KernelGenerator.h | 112 - .../backend/acl_cl/PluginClassesAllocator.cc | 33 - runtime/neurun/backend/acl_cl/ShapeFixer.cc | 434 - runtime/neurun/backend/acl_cl/ShapeFixer.h | 110 - runtime/neurun/backend/acl_cl/TensorBuilder.h | 39 - runtime/neurun/backend/acl_cl/TensorManager.h | 80 - runtime/neurun/backend/acl_cl/TensorRegister.h | 51 - .../neurun/backend/acl_cl/operand/CLSubTensor.cc | 44 - .../neurun/backend/acl_cl/operand/CLSubTensor.h | 63 - runtime/neurun/backend/acl_cl/operand/CLTensor.cc | 62 - runtime/neurun/backend/acl_cl/operand/CLTensor.h | 75 - runtime/neurun/backend/acl_cl/operand/ICLTensor.cc | 45 - runtime/neurun/backend/acl_cl/operand/ICLTensor.h | 50 - runtime/neurun/backend/acl_common/AclFunction.h | 60 - .../backend/acl_common/AclInternalBufferManager.h | 97 - .../backend/acl_common/AclLinearMemoryManager.h | 110 - .../neurun/backend/acl_common/AclMemoryManager.h | 98 - .../neurun/backend/acl_common/AclTensorManager.h | 300 - .../neurun/backend/acl_common/AclTensorRegister.cc | 49 - .../neurun/backend/acl_common/AclTensorRegister.h | 56 - runtime/neurun/backend/acl_common/CMakeLists.txt | 19 - runtime/neurun/backend/acl_common/Convert.cc | 193 - runtime/neurun/backend/acl_common/Convert.h | 68 - runtime/neurun/backend/acl_common/IACLTensor.cc | 63 - runtime/neurun/backend/acl_common/IACLTensor.h | 62 - runtime/neurun/backend/acl_common/Swizzle.h | 160 - .../neurun/backend/acl_common/TemplTensorBuilder.h | 612 - runtime/neurun/backend/acl_neon/Backend.h | 65 - runtime/neurun/backend/acl_neon/CMakeLists.txt | 21 - runtime/neurun/backend/acl_neon/Config.cc | 30 - runtime/neurun/backend/acl_neon/Config.h | 49 - .../neurun/backend/acl_neon/ConstantInitializer.cc | 246 - .../neurun/backend/acl_neon/ConstantInitializer.h | 60 - runtime/neurun/backend/acl_neon/KernelGenerator.cc | 2152 -- runtime/neurun/backend/acl_neon/KernelGenerator.h | 111 - .../backend/acl_neon/PluginClassesAllocator.cc | 33 - runtime/neurun/backend/acl_neon/ShapeFixer.cc | 439 - runtime/neurun/backend/acl_neon/ShapeFixer.h | 109 - runtime/neurun/backend/acl_neon/TensorBuilder.h | 39 - runtime/neurun/backend/acl_neon/TensorManager.h | 78 - runtime/neurun/backend/acl_neon/TensorRegister.cc | 30 - runtime/neurun/backend/acl_neon/TensorRegister.h | 51 - .../neurun/backend/acl_neon/operand/INETensor.cc | 33 - .../neurun/backend/acl_neon/operand/INETensor.h | 46 - .../neurun/backend/acl_neon/operand/NESubTensor.cc | 44 - .../neurun/backend/acl_neon/operand/NESubTensor.h | 63 - .../neurun/backend/acl_neon/operand/NETensor.cc | 45 - runtime/neurun/backend/acl_neon/operand/NETensor.h | 64 - runtime/neurun/backend/cpu/Backend.h | 64 - runtime/neurun/backend/cpu/CMakeLists.txt | 16 - runtime/neurun/backend/cpu/Config.cc | 30 - runtime/neurun/backend/cpu/Config.h | 53 - runtime/neurun/backend/cpu/ConstantInitializer.cc | 68 - runtime/neurun/backend/cpu/ConstantInitializer.h | 55 - runtime/neurun/backend/cpu/KernelGenerator.cc | 624 - runtime/neurun/backend/cpu/KernelGenerator.h | 71 - runtime/neurun/backend/cpu/MemoryManager.cc | 91 - runtime/neurun/backend/cpu/MemoryManager.h | 63 - .../neurun/backend/cpu/PluginClassesAllocator.cc | 33 - runtime/neurun/backend/cpu/ShapeFixer.cc | 135 - runtime/neurun/backend/cpu/ShapeFixer.h | 65 - runtime/neurun/backend/cpu/TensorBuilder.cc | 104 - runtime/neurun/backend/cpu/TensorBuilder.h | 88 - runtime/neurun/backend/cpu/TensorManager.cc | 95 - runtime/neurun/backend/cpu/TensorManager.h | 64 - runtime/neurun/backend/cpu/TensorRegister.cc | 35 - runtime/neurun/backend/cpu/TensorRegister.h | 50 - runtime/neurun/backend/cpu/kernel/AddLayer.cc | 101 - runtime/neurun/backend/cpu/kernel/AddLayer.h | 77 - runtime/neurun/backend/cpu/kernel/AvgPoolLayer.cc | 116 - runtime/neurun/backend/cpu/kernel/AvgPoolLayer.h | 85 - runtime/neurun/backend/cpu/kernel/ConcatLayer.cc | 137 - runtime/neurun/backend/cpu/kernel/ConcatLayer.h | 73 - .../neurun/backend/cpu/kernel/ConvolutionLayer.cc | 140 - .../neurun/backend/cpu/kernel/ConvolutionLayer.h | 88 - .../cpu/kernel/DepthwiseConvolutionLayer.cc | 143 - .../backend/cpu/kernel/DepthwiseConvolutionLayer.h | 90 - .../backend/cpu/kernel/FullyConnectedLayer.cc | 119 - .../backend/cpu/kernel/FullyConnectedLayer.h | 77 - runtime/neurun/backend/cpu/kernel/GatherLayer.cc | 79 - runtime/neurun/backend/cpu/kernel/GatherLayer.h | 74 - runtime/neurun/backend/cpu/kernel/LogisticLayer.cc | 75 - runtime/neurun/backend/cpu/kernel/LogisticLayer.h | 69 - runtime/neurun/backend/cpu/kernel/MaxPoolLayer.cc | 116 - runtime/neurun/backend/cpu/kernel/MaxPoolLayer.h | 85 - runtime/neurun/backend/cpu/kernel/MulLayer.cc | 101 - runtime/neurun/backend/cpu/kernel/MulLayer.h | 77 - .../neurun/backend/cpu/kernel/OperationUtils.cc | 273 - runtime/neurun/backend/cpu/kernel/OperationUtils.h | 152 - runtime/neurun/backend/cpu/kernel/PadLayer.cc | 76 - runtime/neurun/backend/cpu/kernel/PadLayer.h | 75 - runtime/neurun/backend/cpu/kernel/PermuteLayer.cc | 71 - runtime/neurun/backend/cpu/kernel/PermuteLayer.h | 209 - runtime/neurun/backend/cpu/kernel/ReshapeLayer.cc | 54 - runtime/neurun/backend/cpu/kernel/ReshapeLayer.h | 65 - runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc | 172 - runtime/neurun/backend/cpu/kernel/SoftMaxLayer.h | 71 - runtime/neurun/backend/cpu/kernel/SubLayer.cc | 100 - runtime/neurun/backend/cpu/kernel/SubLayer.h | 77 - runtime/neurun/backend/cpu/operand/Tensor.cc | 45 - runtime/neurun/backend/cpu/operand/Tensor.h | 77 - runtime/neurun/backend/cpu_common/CMakeLists.txt | 28 - runtime/neurun/backend/cpu_common/MemoryPlanner.cc | 220 - runtime/neurun/backend/cpu_common/MemoryPlanner.h | 217 - .../backend/cpu_common/MemoryPlanner.test.cc | 193 - .../backend/cpu_common/MemoryPlannerFactory.cc | 51 - .../backend/cpu_common/MemoryPlannerFactory.h | 45 - runtime/neurun/backend/hi_perf_cpu/CMakeLists.txt | 44 - .../hi_perf_cpu/HighPerformanceBackend.test.cc | 42 - .../neurun/backend/hi_perf_cpu/KernelGenerator.cc | 18 - .../neurun/backend/hi_perf_cpu/KernelGenerator.h | 47 - .../neurun/backend/hi_perf_cpu/TensorBuilder.cc | 18 - runtime/neurun/backend/hi_perf_cpu/TensorBuilder.h | 44 - runtime/neurun/backend/srcn/Backend.h | 64 - runtime/neurun/backend/srcn/CMakeLists.txt | 21 - runtime/neurun/backend/srcn/Config.cc | 30 - runtime/neurun/backend/srcn/Config.h | 46 - runtime/neurun/backend/srcn/ConstantInitializer.cc | 191 - runtime/neurun/backend/srcn/ConstantInitializer.h | 60 - runtime/neurun/backend/srcn/Convert.cc | 75 - runtime/neurun/backend/srcn/Convert.h | 46 - runtime/neurun/backend/srcn/KernelGenerator.cc | 275 - runtime/neurun/backend/srcn/KernelGenerator.h | 59 - runtime/neurun/backend/srcn/MemoryManager.cc | 92 - runtime/neurun/backend/srcn/MemoryManager.h | 63 - .../neurun/backend/srcn/PluginClassesAllocator.cc | 33 - runtime/neurun/backend/srcn/ShapeFixer.cc | 47 - runtime/neurun/backend/srcn/ShapeFixer.h | 53 - runtime/neurun/backend/srcn/Swizzle.h | 84 - runtime/neurun/backend/srcn/TensorBuilder.cc | 107 - runtime/neurun/backend/srcn/TensorBuilder.h | 89 - runtime/neurun/backend/srcn/TensorManager.cc | 95 - runtime/neurun/backend/srcn/TensorManager.h | 65 - runtime/neurun/backend/srcn/TensorRegister.cc | 118 - runtime/neurun/backend/srcn/TensorRegister.h | 55 - runtime/neurun/backend/srcn/kernel/AddLayer.cc | 123 - runtime/neurun/backend/srcn/kernel/AddLayer.h | 80 - .../neurun/backend/srcn/kernel/ConvolutionLayer.cc | 233 - .../neurun/backend/srcn/kernel/ConvolutionLayer.h | 89 - .../srcn/kernel/DepthwiseConvolutionLayer.cc | 212 - .../srcn/kernel/DepthwiseConvolutionLayer.h | 85 - .../backend/srcn/kernel/InstanceNormLayer.cc | 155 - .../neurun/backend/srcn/kernel/InstanceNormLayer.h | 77 - .../neurun/backend/srcn/kernel/OperationUtils.cc | 139 - .../neurun/backend/srcn/kernel/OperationUtils.h | 84 - .../backend/srcn/kernel/TransposeConvLayer.cc | 136 - .../backend/srcn/kernel/TransposeConvLayer.h | 83 - runtime/neurun/backend/srcn/operand/Tensor.cc | 45 - runtime/neurun/backend/srcn/operand/Tensor.h | 79 - runtime/neurun/core/CMakeLists.txt | 18 - runtime/neurun/core/include/backend/Backend.h | 67 - .../core/include/backend/CustomKernelBuilder.h | 69 - runtime/neurun/core/include/backend/ExecTime.h | 111 - runtime/neurun/core/include/backend/IConfig.h | 47 - .../core/include/backend/IConstantInitializer.h | 288 - .../neurun/core/include/backend/IKernelGenerator.h | 63 - .../neurun/core/include/backend/IMemoryManager.h | 49 - runtime/neurun/core/include/backend/IShapeFixer.h | 55 - .../neurun/core/include/backend/ITensorBuilder.h | 89 - .../neurun/core/include/backend/ITensorManager.h | 56 - .../neurun/core/include/backend/ITensorRegister.h | 164 - runtime/neurun/core/include/backend/JSONExecTime.h | 96 - .../neurun/core/include/backend/operand/ITensor.h | 54 - runtime/neurun/core/include/compiler/Compiler.h | 91 - .../core/include/compiler/IExecutionBuilder.h | 39 - .../neurun/core/include/compiler/SubTensorInfo.h | 83 - runtime/neurun/core/include/exec/Execution.h | 144 - .../neurun/core/include/exec/ExecutionObservers.h | 83 - runtime/neurun/core/include/exec/IExecutor.h | 72 - runtime/neurun/core/include/exec/IFunction.h | 37 - runtime/neurun/core/include/exec/IODescription.h | 66 - runtime/neurun/core/include/exec/NopFunction.h | 54 - runtime/neurun/core/include/ir/BackendSet.h | 40 - runtime/neurun/core/include/ir/Data.h | 75 - runtime/neurun/core/include/ir/DataType.h | 62 - runtime/neurun/core/include/ir/Graph.h | 153 - runtime/neurun/core/include/ir/Index.h | 42 - runtime/neurun/core/include/ir/InternalType.h | 68 - runtime/neurun/core/include/ir/Layout.h | 67 - runtime/neurun/core/include/ir/LowerInfoMap.h | 42 - runtime/neurun/core/include/ir/OpCode.h | 56 - runtime/neurun/core/include/ir/OpSequence.h | 106 - runtime/neurun/core/include/ir/Operand.h | 130 - runtime/neurun/core/include/ir/OperandConstraint.h | 58 - runtime/neurun/core/include/ir/OperandIndexMap.h | 34 - .../neurun/core/include/ir/OperandIndexSequence.h | 60 - runtime/neurun/core/include/ir/OperandInfo.h | 90 - runtime/neurun/core/include/ir/Operands.h | 39 - runtime/neurun/core/include/ir/Operation.h | 71 - .../neurun/core/include/ir/OperationIndexList.h | 59 - runtime/neurun/core/include/ir/OperationIndexMap.h | 34 - runtime/neurun/core/include/ir/OperationVisitor.h | 52 - .../neurun/core/include/ir/Operations.Include.h | 83 - runtime/neurun/core/include/ir/Operations.h | 36 - runtime/neurun/core/include/ir/Operations.lst | 86 - runtime/neurun/core/include/ir/Shape.h | 84 - runtime/neurun/core/include/ir/Subgraphs.h | 87 - runtime/neurun/core/include/ir/TypeInfo.h | 59 - runtime/neurun/core/include/ir/operand/LowerInfo.h | 93 - .../neurun/core/include/ir/operand/ParentInfo.h | 77 - .../neurun/core/include/ir/operand/PermuteFactor.h | 130 - runtime/neurun/core/include/ir/operation/Abs.h | 49 - runtime/neurun/core/include/ir/operation/Add.h | 62 - runtime/neurun/core/include/ir/operation/ArgMax.h | 62 - .../neurun/core/include/ir/operation/AvgPool2D.h | 69 - .../core/include/ir/operation/BatchToSpaceND.h | 50 - runtime/neurun/core/include/ir/operation/Cast.h | 49 - .../neurun/core/include/ir/operation/Comparison.h | 72 - runtime/neurun/core/include/ir/operation/Concat.h | 59 - runtime/neurun/core/include/ir/operation/Conv2D.h | 68 - runtime/neurun/core/include/ir/operation/Custom.h | 66 - .../core/include/ir/operation/DepthToSpace.h | 63 - .../core/include/ir/operation/DepthwiseConv2D.h | 69 - .../neurun/core/include/ir/operation/Dequantize.h | 49 - runtime/neurun/core/include/ir/operation/Div.h | 62 - .../core/include/ir/operation/EmbeddingLookup.h | 50 - runtime/neurun/core/include/ir/operation/Exp.h | 49 - runtime/neurun/core/include/ir/operation/Floor.h | 51 - .../core/include/ir/operation/FullyConnected.h | 66 - runtime/neurun/core/include/ir/operation/Gather.h | 65 - .../core/include/ir/operation/HashtableLookup.h | 57 - .../core/include/ir/operation/InstanceNorm.h | 65 - .../core/include/ir/operation/L2Normalization.h | 62 - .../neurun/core/include/ir/operation/L2Pool2D.h | 68 - runtime/neurun/core/include/ir/operation/LSTM.h | 89 - .../ir/operation/LocalResponseNormalization.h | 66 - .../neurun/core/include/ir/operation/LogicalAnd.h | 50 - .../neurun/core/include/ir/operation/LogicalNot.h | 49 - .../neurun/core/include/ir/operation/LogicalOr.h | 50 - .../neurun/core/include/ir/operation/Logistic.h | 49 - .../neurun/core/include/ir/operation/LowerInfo.h | 54 - runtime/neurun/core/include/ir/operation/Max.h | 50 - .../neurun/core/include/ir/operation/MaxPool2D.h | 68 - runtime/neurun/core/include/ir/operation/Mean.h | 62 - runtime/neurun/core/include/ir/operation/Min.h | 50 - runtime/neurun/core/include/ir/operation/Mul.h | 62 - runtime/neurun/core/include/ir/operation/Neg.h | 49 - runtime/neurun/core/include/ir/operation/OneHot.h | 60 - runtime/neurun/core/include/ir/operation/PReLU.h | 50 - runtime/neurun/core/include/ir/operation/Pack.h | 53 - runtime/neurun/core/include/ir/operation/Pad.h | 63 - runtime/neurun/core/include/ir/operation/Permute.h | 78 - runtime/neurun/core/include/ir/operation/RNN.h | 70 - runtime/neurun/core/include/ir/operation/RSQRT.h | 49 - runtime/neurun/core/include/ir/operation/ReLU.h | 49 - runtime/neurun/core/include/ir/operation/ReLU1.h | 49 - runtime/neurun/core/include/ir/operation/ReLU6.h | 49 - .../neurun/core/include/ir/operation/ReduceMax.h | 65 - .../neurun/core/include/ir/operation/ReduceMin.h | 65 - .../neurun/core/include/ir/operation/ReduceSum.h | 63 - runtime/neurun/core/include/ir/operation/Reshape.h | 51 - .../core/include/ir/operation/ResizeBilinear.h | 64 - runtime/neurun/core/include/ir/operation/SQRT.h | 49 - runtime/neurun/core/include/ir/operation/Slice.h | 64 - runtime/neurun/core/include/ir/operation/Softmax.h | 63 - .../core/include/ir/operation/SpaceToBatchND.h | 53 - .../core/include/ir/operation/SpaceToDepth.h | 63 - runtime/neurun/core/include/ir/operation/Split.h | 59 - .../core/include/ir/operation/SquaredDifference.h | 50 - runtime/neurun/core/include/ir/operation/Squeeze.h | 62 - .../core/include/ir/operation/StridedSlice.h | 69 - runtime/neurun/core/include/ir/operation/Sub.h | 62 - runtime/neurun/core/include/ir/operation/Tanh.h | 49 - runtime/neurun/core/include/ir/operation/TopKV2.h | 69 - .../neurun/core/include/ir/operation/Transpose.h | 64 - .../core/include/ir/operation/TransposeConv.h | 67 - runtime/neurun/core/include/ir/operation/Unpack.h | 59 - runtime/neurun/core/include/util/Config.lst | 43 - runtime/neurun/core/include/util/ConfigSource.h | 55 - runtime/neurun/core/include/util/Coordinates.h | 103 - runtime/neurun/core/include/util/EnvConfigSource.h | 41 - .../core/include/util/EventCollectorGlobal.h | 155 - .../neurun/core/include/util/GeneralConfigSource.h | 44 - runtime/neurun/core/include/util/IConfigSource.h | 46 - runtime/neurun/core/include/util/ITimer.h | 59 - runtime/neurun/core/include/util/Index.h | 154 - runtime/neurun/core/include/util/ObjectManager.h | 144 - runtime/neurun/core/include/util/Padding.h | 41 - runtime/neurun/core/include/util/Set.h | 166 - runtime/neurun/core/include/util/ShapeInference.h | 59 - runtime/neurun/core/include/util/Utils.h | 51 - .../core/include/util/feature/Coordinate4D.h | 111 - .../neurun/core/include/util/feature/nchw/Reader.h | 120 - .../neurun/core/include/util/feature/nchw/View.h | 138 - .../neurun/core/include/util/feature/nhwc/Reader.h | 121 - .../neurun/core/include/util/feature/nhwc/View.h | 140 - runtime/neurun/core/include/util/logging.h | 63 - runtime/neurun/core/src/backend/Backend.cc | 30 - runtime/neurun/core/src/backend/BackendManager.cc | 150 - runtime/neurun/core/src/backend/BackendManager.h | 82 - runtime/neurun/core/src/backend/ExecTime.cc | 133 - runtime/neurun/core/src/backend/JSONExecTime.cc | 231 - .../neurun/core/src/compiler/BackendResolver.cc | 47 - runtime/neurun/core/src/compiler/BackendResolver.h | 102 - runtime/neurun/core/src/compiler/CodeWithInfo.h | 44 - runtime/neurun/core/src/compiler/Compiler.cc | 143 - .../neurun/core/src/compiler/ExecutorFactory.cc | 379 - runtime/neurun/core/src/compiler/ExecutorFactory.h | 52 - runtime/neurun/core/src/compiler/HEScheduler.cc | 628 - runtime/neurun/core/src/compiler/HEScheduler.h | 175 - runtime/neurun/core/src/compiler/IScheduler.h | 38 - runtime/neurun/core/src/compiler/Linear.cc | 317 - runtime/neurun/core/src/compiler/Linear.h | 81 - .../neurun/core/src/compiler/ManualScheduler.cc | 142 - runtime/neurun/core/src/compiler/ManualScheduler.h | 36 - runtime/neurun/core/src/compiler/OperandContext.cc | 45 - runtime/neurun/core/src/compiler/OperandContext.h | 60 - .../neurun/core/src/compiler/OperationValidator.cc | 985 - .../neurun/core/src/compiler/OperationValidator.h | 86 - runtime/neurun/core/src/compiler/ParamChecker.cc | 33 - runtime/neurun/core/src/compiler/ParamChecker.h | 73 - .../neurun/core/src/compiler/SubTensorAnalyzer.cc | 96 - .../neurun/core/src/compiler/SubTensorAnalyzer.h | 59 - runtime/neurun/core/src/dumper/dot/DotBuilder.cc | 83 - runtime/neurun/core/src/dumper/dot/DotBuilder.h | 62 - runtime/neurun/core/src/dumper/dot/DotDumper.cc | 199 - runtime/neurun/core/src/dumper/dot/DotDumper.h | 60 - .../neurun/core/src/dumper/dot/DotSubgraphInfo.cc | 56 - .../neurun/core/src/dumper/dot/DotSubgraphInfo.h | 59 - runtime/neurun/core/src/dumper/dot/Node.cc | 56 - runtime/neurun/core/src/dumper/dot/Node.h | 127 - runtime/neurun/core/src/dumper/dot/OperandNode.cc | 60 - runtime/neurun/core/src/dumper/dot/OperandNode.h | 79 - .../neurun/core/src/dumper/dot/OperationNode.cc | 46 - runtime/neurun/core/src/dumper/dot/OperationNode.h | 62 - runtime/neurun/core/src/exec/DataflowExecutor.cc | 176 - runtime/neurun/core/src/exec/DataflowExecutor.h | 97 - runtime/neurun/core/src/exec/Execution.cc | 135 - runtime/neurun/core/src/exec/ExecutionObservee.cc | 64 - runtime/neurun/core/src/exec/ExecutionObservee.h | 56 - runtime/neurun/core/src/exec/ExecutionObservers.cc | 130 - runtime/neurun/core/src/exec/ExecutorBase.cc | 145 - runtime/neurun/core/src/exec/ExecutorBase.h | 127 - runtime/neurun/core/src/exec/FunctionSequence.cc | 62 - runtime/neurun/core/src/exec/FunctionSequence.h | 56 - runtime/neurun/core/src/exec/Job.cc | 33 - runtime/neurun/core/src/exec/Job.h | 69 - runtime/neurun/core/src/exec/LinearExecutor.cc | 39 - runtime/neurun/core/src/exec/LinearExecutor.h | 64 - runtime/neurun/core/src/exec/ParallelExecutor.cc | 147 - runtime/neurun/core/src/exec/ParallelExecutor.h | 69 - runtime/neurun/core/src/exec/ParallelScheduler.cc | 55 - runtime/neurun/core/src/exec/ParallelScheduler.h | 60 - runtime/neurun/core/src/exec/Sink.h | 205 - runtime/neurun/core/src/exec/Source.h | 211 - runtime/neurun/core/src/exec/ThreadPool.cc | 65 - runtime/neurun/core/src/exec/ThreadPool.h | 73 - runtime/neurun/core/src/exec/WorkQueue.cc | 104 - runtime/neurun/core/src/exec/WorkQueue.h | 87 - runtime/neurun/core/src/exec/interp/Buffer.h | 94 - runtime/neurun/core/src/exec/interp/ExecEnv.h | 165 - runtime/neurun/core/src/exec/interp/ExecManager.cc | 125 - runtime/neurun/core/src/exec/interp/ExecManager.h | 71 - runtime/neurun/core/src/exec/interp/Interpreter.cc | 210 - runtime/neurun/core/src/exec/interp/Interpreter.h | 67 - runtime/neurun/core/src/exec/interp/Registration.h | 63 - runtime/neurun/core/src/exec/interp/Tensor.cc | 59 - runtime/neurun/core/src/exec/interp/Tensor.h | 180 - .../core/src/exec/interp/operations/AvgPool2D.cc | 129 - .../exec/interp/operations/BinaryArithmeticOps.cc | 202 - .../core/src/exec/interp/operations/Concat.cc | 150 - .../core/src/exec/interp/operations/Conv2D.cc | 152 - .../src/exec/interp/operations/DepthwiseConv.cc | 159 - .../src/exec/interp/operations/FullyConnected.cc | 137 - .../core/src/exec/interp/operations/Gather.cc | 141 - .../src/exec/interp/operations/InstanceNorm.cc | 124 - .../core/src/exec/interp/operations/Logistic.cc | 102 - .../core/src/exec/interp/operations/MaxPool2D.cc | 128 - .../src/exec/interp/operations/OperationUtil.h | 177 - .../neurun/core/src/exec/interp/operations/Pad.cc | 109 - .../core/src/exec/interp/operations/Reshape.cc | 66 - .../core/src/exec/interp/operations/SoftMax.cc | 163 - .../src/exec/interp/operations/TransposeConv.cc | 145 - .../src/exec/interp/operations/UnaryActivations.cc | 156 - runtime/neurun/core/src/ir/Graph.cc | 551 - runtime/neurun/core/src/ir/GraphIterator.cc | 84 - runtime/neurun/core/src/ir/GraphIterator.h | 74 - runtime/neurun/core/src/ir/LayoutSet.cc | 66 - runtime/neurun/core/src/ir/LayoutSet.h | 58 - runtime/neurun/core/src/ir/OpCode.cc | 37 - runtime/neurun/core/src/ir/OpSequence.cc | 83 - runtime/neurun/core/src/ir/Operand.cc | 70 - runtime/neurun/core/src/ir/OperandIndexSequence.cc | 58 - runtime/neurun/core/src/ir/Operation.cc | 55 - runtime/neurun/core/src/ir/OperationIndexList.cc | 37 - runtime/neurun/core/src/ir/Shape.cc | 85 - runtime/neurun/core/src/ir/Subgraphs.cc | 87 - runtime/neurun/core/src/ir/TypeInfo.cc | 47 - runtime/neurun/core/src/ir/dumper/Dumper.cc | 633 - runtime/neurun/core/src/ir/dumper/Dumper.h | 102 - .../neurun/core/src/ir/operand/Shape4DConvert.h | 57 - runtime/neurun/core/src/ir/operation/Abs.cc | 39 - runtime/neurun/core/src/ir/operation/Add.cc | 40 - runtime/neurun/core/src/ir/operation/ArgMax.cc | 40 - runtime/neurun/core/src/ir/operation/AvgPool2D.cc | 40 - .../neurun/core/src/ir/operation/BatchToSpaceND.cc | 40 - runtime/neurun/core/src/ir/operation/Cast.cc | 39 - runtime/neurun/core/src/ir/operation/Comparison.cc | 40 - runtime/neurun/core/src/ir/operation/Concat.cc | 40 - runtime/neurun/core/src/ir/operation/Conv2D.cc | 40 - runtime/neurun/core/src/ir/operation/Custom.cc | 46 - .../neurun/core/src/ir/operation/DepthToSpace.cc | 40 - .../core/src/ir/operation/DepthwiseConv2D.cc | 40 - runtime/neurun/core/src/ir/operation/Dequantize.cc | 39 - runtime/neurun/core/src/ir/operation/Div.cc | 40 - .../core/src/ir/operation/EmbeddingLookup.cc | 40 - runtime/neurun/core/src/ir/operation/Exp.cc | 39 - runtime/neurun/core/src/ir/operation/Floor.cc | 39 - .../neurun/core/src/ir/operation/FullyConnected.cc | 40 - runtime/neurun/core/src/ir/operation/Gather.cc | 40 - .../core/src/ir/operation/HashtableLookup.cc | 40 - .../neurun/core/src/ir/operation/InstanceNorm.cc | 40 - .../core/src/ir/operation/L2Normalization.cc | 40 - runtime/neurun/core/src/ir/operation/L2Pool2D.cc | 40 - runtime/neurun/core/src/ir/operation/LSTM.cc | 40 - .../src/ir/operation/LocalResponseNormalization.cc | 41 - runtime/neurun/core/src/ir/operation/LogicalAnd.cc | 39 - runtime/neurun/core/src/ir/operation/LogicalNot.cc | 39 - runtime/neurun/core/src/ir/operation/LogicalOr.cc | 39 - runtime/neurun/core/src/ir/operation/Logistic.cc | 39 - runtime/neurun/core/src/ir/operation/LowerInfo.cc | 34 - runtime/neurun/core/src/ir/operation/Max.cc | 39 - runtime/neurun/core/src/ir/operation/MaxPool2D.cc | 40 - runtime/neurun/core/src/ir/operation/Mean.cc | 40 - runtime/neurun/core/src/ir/operation/Min.cc | 39 - runtime/neurun/core/src/ir/operation/Mul.cc | 40 - runtime/neurun/core/src/ir/operation/Neg.cc | 39 - runtime/neurun/core/src/ir/operation/OneHot.cc | 37 - runtime/neurun/core/src/ir/operation/PReLU.cc | 39 - runtime/neurun/core/src/ir/operation/Pack.cc | 33 - runtime/neurun/core/src/ir/operation/Pad.cc | 38 - runtime/neurun/core/src/ir/operation/Permute.cc | 44 - runtime/neurun/core/src/ir/operation/RNN.cc | 40 - runtime/neurun/core/src/ir/operation/RSQRT.cc | 39 - runtime/neurun/core/src/ir/operation/ReLU.cc | 39 - runtime/neurun/core/src/ir/operation/ReLU1.cc | 39 - runtime/neurun/core/src/ir/operation/ReLU6.cc | 39 - runtime/neurun/core/src/ir/operation/ReduceMax.cc | 40 - runtime/neurun/core/src/ir/operation/ReduceMin.cc | 40 - runtime/neurun/core/src/ir/operation/ReduceSum.cc | 40 - runtime/neurun/core/src/ir/operation/Reshape.cc | 39 - .../neurun/core/src/ir/operation/ResizeBilinear.cc | 40 - runtime/neurun/core/src/ir/operation/SQRT.cc | 39 - runtime/neurun/core/src/ir/operation/Slice.cc | 37 - runtime/neurun/core/src/ir/operation/Softmax.cc | 40 - .../neurun/core/src/ir/operation/SpaceToBatchND.cc | 40 - .../neurun/core/src/ir/operation/SpaceToDepth.cc | 40 - runtime/neurun/core/src/ir/operation/Split.cc | 33 - .../core/src/ir/operation/SquaredDifference.cc | 40 - runtime/neurun/core/src/ir/operation/Squeeze.cc | 37 - .../neurun/core/src/ir/operation/StridedSlice.cc | 40 - runtime/neurun/core/src/ir/operation/Sub.cc | 40 - runtime/neurun/core/src/ir/operation/Tanh.cc | 39 - runtime/neurun/core/src/ir/operation/TopKV2.cc | 40 - runtime/neurun/core/src/ir/operation/Transpose.cc | 40 - .../neurun/core/src/ir/operation/TransposeConv.cc | 40 - runtime/neurun/core/src/ir/operation/Unpack.cc | 33 - .../core/src/ir/pass/ConstantInsertionPass.cc | 104 - .../core/src/ir/pass/ConstantInsertionPass.h | 75 - runtime/neurun/core/src/ir/pass/OperandPass.cc | 36 - runtime/neurun/core/src/ir/pass/OperandPass.h | 53 - runtime/neurun/core/src/ir/pass/OperationPass.cc | 38 - runtime/neurun/core/src/ir/pass/OperationPass.h | 76 - runtime/neurun/core/src/ir/pass/Pass.h | 55 - .../core/src/ir/pass/PermutationEliminationPass.cc | 195 - .../core/src/ir/pass/PermutationEliminationPass.h | 86 - .../core/src/ir/pass/PermutationInsertionPass.cc | 209 - .../core/src/ir/pass/PermutationInsertionPass.h | 59 - .../core/src/ir/pass/PermutationOperationPass.cc | 230 - .../core/src/ir/pass/PermutationOperationPass.h | 54 - runtime/neurun/core/src/ir/verifier/Verifier.cc | 96 - runtime/neurun/core/src/ir/verifier/Verifier.h | 68 - runtime/neurun/core/src/library_info.cc | 17 - runtime/neurun/core/src/util/ConfigSource.cc | 116 - runtime/neurun/core/src/util/EnvConfigSource.cc | 40 - .../neurun/core/src/util/EventCollectorGlobal.cc | 86 - .../neurun/core/src/util/GeneralConfigSource.cc | 45 - runtime/neurun/core/src/util/Padding.cc | 119 - runtime/neurun/core/src/util/ShapeInference.cc | 200 - runtime/neurun/core/src/util/Utils.cc | 68 - runtime/neurun/core/src/util/logging.cc | 7 - runtime/neurun/frontend/CMakeLists.txt | 1 - runtime/neurun/frontend/base_loader/CMakeLists.txt | 7 - .../frontend/base_loader/include/base_loader.h | 1278 - runtime/neurun/frontend/circle/CMakeLists.txt | 17 - .../neurun/frontend/circle/include/circle_loader.h | 32 - .../neurun/frontend/circle/src/circle_loader.cc | 116 - .../frontend/circle/src/circle_schema_generated.h | 7546 ----- .../frontend/nnapi/ANeuralNetworksModel.test.cc | 25 - runtime/neurun/frontend/nnapi/CMakeLists.txt | 23 - runtime/neurun/frontend/nnapi/compilation.cc | 110 - runtime/neurun/frontend/nnapi/event.cc | 36 - runtime/neurun/frontend/nnapi/execution.cc | 480 - runtime/neurun/frontend/nnapi/memory.cc | 42 - runtime/neurun/frontend/nnapi/model.cc | 411 - .../nnapi/wrapper/ANeuralNetworksCompilation.cc | 42 - .../nnapi/wrapper/ANeuralNetworksCompilation.h | 42 - .../frontend/nnapi/wrapper/ANeuralNetworksEvent.cc | 43 - .../frontend/nnapi/wrapper/ANeuralNetworksEvent.h | 44 - .../nnapi/wrapper/ANeuralNetworksExecution.cc | 289 - .../nnapi/wrapper/ANeuralNetworksExecution.h | 74 - .../nnapi/wrapper/ANeuralNetworksMemory.cc | 46 - .../frontend/nnapi/wrapper/ANeuralNetworksMemory.h | 39 - .../frontend/nnapi/wrapper/ANeuralNetworksModel.cc | 268 - .../frontend/nnapi/wrapper/ANeuralNetworksModel.h | 71 - .../neurun/frontend/nnapi/wrapper/NNAPIConvert.cc | 100 - .../neurun/frontend/nnapi/wrapper/NNAPIConvert.h | 78 - .../frontend/nnapi/wrapper/OperationFactory.cc | 1680 -- .../frontend/nnapi/wrapper/OperationFactory.h | 60 - runtime/neurun/frontend/tflite/CMakeLists.txt | 17 - .../neurun/frontend/tflite/include/tflite_loader.h | 34 - .../neurun/frontend/tflite/src/tflite_loader.cc | 105 - .../frontend/tflite/src/tflite_schema_generated.h | 7275 ----- runtime/neurun/frontend/tflite/tflite_schema.fbs | 795 - runtime/neurun/sample/CMakeLists.txt | 1 - runtime/neurun/sample/minimal/CMakeLists.txt | 10 - runtime/neurun/sample/minimal/README.md | 13 - runtime/neurun/sample/minimal/src/minimal.cc | 67 - runtime/neurun/test/CMakeLists.txt | 15 - runtime/neurun/test/core/backend/ExecTime.test.cc | 98 - runtime/neurun/test/core/compiler/Scheduler.cc | 550 - runtime/neurun/test/core/exec/ExecInstance.cc | 307 - .../neurun/test/core/exec/interp/ExecManager.cc | 334 - runtime/neurun/test/graph/Graph.cc | 52 - runtime/neurun/test/graph/Index.cc | 34 - runtime/neurun/test/graph/MockNode.h | 47 - runtime/neurun/test/graph/operand/IndexSet.cc | 52 - runtime/neurun/test/graph/operand/LayoutSet.cc | 43 - runtime/neurun/test/graph/operand/Set.cc | 45 - runtime/neurun/test/graph/operand/UseDef.cc | 85 - runtime/neurun/test/graph/operation/Set.cc | 33 - runtime/neurun/test/graph/operation/SetIO.cc | 99 - runtime/neurun/test/graph/verifier/Verifier.cc | 49 - runtime/neurun/test/util/ShapeInference.cc | 233 - runtime/nnapi-header/include/NeuralNetworksEx.h | 309 +- runtime/onert/CMakeLists.txt | 15 + runtime/onert/api/CMakeLists.txt | 21 + runtime/onert/api/include/nnfw.h | 409 + runtime/onert/api/include/nnfw_debug.h | 26 + runtime/onert/api/include/nnfw_dev.h | 65 + runtime/onert/api/include/nnfw_version.h | 26 + runtime/onert/api/src/CustomKernel.cc | 98 + runtime/onert/api/src/CustomKernel.h | 60 + runtime/onert/api/src/CustomKernelRegistry.cc | 64 + runtime/onert/api/src/CustomKernelRegistry.h | 64 + runtime/onert/api/src/OpMap.lst | 89 + runtime/onert/api/src/nnfw_api.cc | 299 + runtime/onert/api/src/nnfw_api_internal.cc | 518 + runtime/onert/api/src/nnfw_api_internal.h | 92 + runtime/onert/api/src/nnfw_debug.cc | 29 + runtime/onert/api/src/nnfw_debug_internal.cc | 25 + runtime/onert/api/src/nnfw_debug_internal.h | 29 + runtime/onert/backend/CMakeLists.txt | 8 + runtime/onert/backend/acl_cl/Backend.h | 68 + runtime/onert/backend/acl_cl/CLTimer.h | 108 + runtime/onert/backend/acl_cl/CMakeLists.txt | 19 + runtime/onert/backend/acl_cl/Config.cc | 50 + runtime/onert/backend/acl_cl/Config.h | 44 + .../onert/backend/acl_cl/ConstantInitializer.cc | 196 + runtime/onert/backend/acl_cl/ConstantInitializer.h | 63 + runtime/onert/backend/acl_cl/KernelGenerator.cc | 2023 ++ runtime/onert/backend/acl_cl/KernelGenerator.h | 112 + runtime/onert/backend/acl_cl/Optimizer.cc | 58 + runtime/onert/backend/acl_cl/Optimizer.h | 47 + runtime/onert/backend/acl_cl/ShapeFixer.cc | 431 + runtime/onert/backend/acl_cl/ShapeFixer.h | 110 + runtime/onert/backend/acl_cl/TensorBuilder.h | 39 + runtime/onert/backend/acl_cl/TensorManager.h | 78 + runtime/onert/backend/acl_cl/acl_cl.cc | 33 + .../onert/backend/acl_cl/operand/CLSubTensor.cc | 44 + runtime/onert/backend/acl_cl/operand/CLSubTensor.h | 62 + runtime/onert/backend/acl_cl/operand/CLTensor.cc | 62 + runtime/onert/backend/acl_cl/operand/CLTensor.h | 75 + runtime/onert/backend/acl_cl/operand/ICLTensor.cc | 45 + runtime/onert/backend/acl_cl/operand/ICLTensor.h | 50 + .../backend/acl_common/AclActivationBuilder.h | 125 + runtime/onert/backend/acl_common/AclFunction.h | 69 + .../backend/acl_common/AclInternalBufferManager.h | 97 + .../backend/acl_common/AclLinearMemoryManager.h | 110 + .../onert/backend/acl_common/AclMemoryManager.h | 98 + .../backend/acl_common/AclSubTensorAnalyzer.h | 105 + .../onert/backend/acl_common/AclTensorBuilder.h | 483 + .../onert/backend/acl_common/AclTensorManager.h | 301 + runtime/onert/backend/acl_common/CMakeLists.txt | 19 + runtime/onert/backend/acl_common/Convert.cc | 198 + runtime/onert/backend/acl_common/Convert.h | 74 + runtime/onert/backend/acl_common/IACLTensor.cc | 63 + runtime/onert/backend/acl_common/IACLTensor.h | 68 + runtime/onert/backend/acl_common/ParentInfo.h | 44 + runtime/onert/backend/acl_common/Swizzle.h | 160 + runtime/onert/backend/acl_neon/Backend.h | 69 + runtime/onert/backend/acl_neon/CMakeLists.txt | 19 + runtime/onert/backend/acl_neon/Config.cc | 30 + runtime/onert/backend/acl_neon/Config.h | 45 + .../onert/backend/acl_neon/ConstantInitializer.cc | 183 + .../onert/backend/acl_neon/ConstantInitializer.h | 60 + runtime/onert/backend/acl_neon/KernelGenerator.cc | 2030 ++ runtime/onert/backend/acl_neon/KernelGenerator.h | 112 + runtime/onert/backend/acl_neon/Optimizer.cc | 58 + runtime/onert/backend/acl_neon/Optimizer.h | 47 + runtime/onert/backend/acl_neon/ShapeFixer.cc | 437 + runtime/onert/backend/acl_neon/ShapeFixer.h | 110 + runtime/onert/backend/acl_neon/TensorBuilder.h | 39 + runtime/onert/backend/acl_neon/TensorManager.h | 77 + runtime/onert/backend/acl_neon/acl_neon.cc | 33 + .../onert/backend/acl_neon/operand/INETensor.cc | 33 + runtime/onert/backend/acl_neon/operand/INETensor.h | 46 + .../onert/backend/acl_neon/operand/NESubTensor.cc | 44 + .../onert/backend/acl_neon/operand/NESubTensor.h | 62 + runtime/onert/backend/acl_neon/operand/NETensor.cc | 45 + runtime/onert/backend/acl_neon/operand/NETensor.h | 64 + runtime/onert/backend/cpu/Backend.h | 67 + runtime/onert/backend/cpu/CMakeLists.txt | 14 + runtime/onert/backend/cpu/Config.cc | 30 + runtime/onert/backend/cpu/Config.h | 45 + runtime/onert/backend/cpu/ConstantInitializer.cc | 68 + runtime/onert/backend/cpu/ConstantInitializer.h | 54 + runtime/onert/backend/cpu/KernelGenerator.cc | 932 + runtime/onert/backend/cpu/KernelGenerator.h | 93 + runtime/onert/backend/cpu/ShapeFixer.cc | 181 + runtime/onert/backend/cpu/ShapeFixer.h | 85 + runtime/onert/backend/cpu/TensorBuilder.cc | 93 + runtime/onert/backend/cpu/TensorBuilder.h | 76 + runtime/onert/backend/cpu/TensorManager.cc | 114 + runtime/onert/backend/cpu/TensorManager.h | 65 + runtime/onert/backend/cpu/cpu.cc | 33 + runtime/onert/backend/cpu/kernel/AbsLayer.cc | 67 + runtime/onert/backend/cpu/kernel/AbsLayer.h | 63 + runtime/onert/backend/cpu/kernel/AddLayer.cc | 96 + runtime/onert/backend/cpu/kernel/AddLayer.h | 71 + runtime/onert/backend/cpu/kernel/AvgPoolLayer.cc | 116 + runtime/onert/backend/cpu/kernel/AvgPoolLayer.h | 81 + runtime/onert/backend/cpu/kernel/CastLayer.cc | 108 + runtime/onert/backend/cpu/kernel/CastLayer.h | 63 + runtime/onert/backend/cpu/kernel/CompareLayer.cc | 180 + runtime/onert/backend/cpu/kernel/CompareLayer.h | 65 + runtime/onert/backend/cpu/kernel/ConcatLayer.cc | 134 + runtime/onert/backend/cpu/kernel/ConcatLayer.h | 65 + .../onert/backend/cpu/kernel/ConvolutionLayer.cc | 159 + .../onert/backend/cpu/kernel/ConvolutionLayer.h | 96 + .../cpu/kernel/DepthwiseConvolutionLayer.cc | 136 + .../backend/cpu/kernel/DepthwiseConvolutionLayer.h | 83 + runtime/onert/backend/cpu/kernel/DivLayer.cc | 92 + runtime/onert/backend/cpu/kernel/DivLayer.h | 71 + runtime/onert/backend/cpu/kernel/ExpLayer.cc | 71 + runtime/onert/backend/cpu/kernel/ExpLayer.h | 63 + .../backend/cpu/kernel/FullyConnectedLayer.cc | 141 + .../onert/backend/cpu/kernel/FullyConnectedLayer.h | 81 + runtime/onert/backend/cpu/kernel/GatherLayer.cc | 77 + runtime/onert/backend/cpu/kernel/GatherLayer.h | 66 + runtime/onert/backend/cpu/kernel/LogisticLayer.cc | 71 + runtime/onert/backend/cpu/kernel/LogisticLayer.h | 63 + runtime/onert/backend/cpu/kernel/MaxLayer.cc | 78 + runtime/onert/backend/cpu/kernel/MaxLayer.h | 67 + runtime/onert/backend/cpu/kernel/MaxPoolLayer.cc | 113 + runtime/onert/backend/cpu/kernel/MaxPoolLayer.h | 81 + runtime/onert/backend/cpu/kernel/MinLayer.cc | 78 + runtime/onert/backend/cpu/kernel/MinLayer.h | 67 + runtime/onert/backend/cpu/kernel/MulLayer.cc | 92 + runtime/onert/backend/cpu/kernel/MulLayer.h | 71 + runtime/onert/backend/cpu/kernel/OneHotLayer.cc | 70 + runtime/onert/backend/cpu/kernel/OneHotLayer.h | 73 + runtime/onert/backend/cpu/kernel/OperationUtils.cc | 269 + runtime/onert/backend/cpu/kernel/OperationUtils.h | 162 + runtime/onert/backend/cpu/kernel/PackLayer.cc | 98 + runtime/onert/backend/cpu/kernel/PackLayer.h | 65 + runtime/onert/backend/cpu/kernel/PadLayer.cc | 70 + runtime/onert/backend/cpu/kernel/PadLayer.h | 71 + runtime/onert/backend/cpu/kernel/PermuteLayer.cc | 71 + runtime/onert/backend/cpu/kernel/PermuteLayer.h | 209 + runtime/onert/backend/cpu/kernel/ReduceLayer.cc | 137 + runtime/onert/backend/cpu/kernel/ReduceLayer.h | 84 + runtime/onert/backend/cpu/kernel/ReshapeLayer.cc | 55 + runtime/onert/backend/cpu/kernel/ReshapeLayer.h | 63 + runtime/onert/backend/cpu/kernel/RsqrtLayer.cc | 70 + runtime/onert/backend/cpu/kernel/RsqrtLayer.h | 59 + runtime/onert/backend/cpu/kernel/ShapeLayer.cc | 81 + runtime/onert/backend/cpu/kernel/ShapeLayer.h | 61 + runtime/onert/backend/cpu/kernel/SinLayer.cc | 69 + runtime/onert/backend/cpu/kernel/SinLayer.h | 60 + runtime/onert/backend/cpu/kernel/SliceLayer.cc | 111 + runtime/onert/backend/cpu/kernel/SliceLayer.h | 71 + runtime/onert/backend/cpu/kernel/SoftMaxLayer.cc | 173 + runtime/onert/backend/cpu/kernel/SoftMaxLayer.h | 65 + runtime/onert/backend/cpu/kernel/SplitLayer.cc | 98 + runtime/onert/backend/cpu/kernel/SplitLayer.h | 66 + .../onert/backend/cpu/kernel/StridedSliceLayer.cc | 96 + .../onert/backend/cpu/kernel/StridedSliceLayer.h | 78 + runtime/onert/backend/cpu/kernel/SubLayer.cc | 92 + runtime/onert/backend/cpu/kernel/SubLayer.h | 71 + runtime/onert/backend/cpu/kernel/TanhLayer.cc | 71 + runtime/onert/backend/cpu/kernel/TanhLayer.h | 63 + runtime/onert/backend/cpu/kernel/TransposeLayer.cc | 81 + runtime/onert/backend/cpu/kernel/TransposeLayer.h | 61 + runtime/onert/backend/cpu/kernel/UnpackLayer.cc | 104 + runtime/onert/backend/cpu/kernel/UnpackLayer.h | 66 + runtime/onert/backend/cpu/operand/Tensor.cc | 45 + runtime/onert/backend/cpu/operand/Tensor.h | 124 + runtime/onert/backend/cpu_common/Allocator.cc | 38 + runtime/onert/backend/cpu_common/Allocator.h | 56 + runtime/onert/backend/cpu_common/CMakeLists.txt | 35 + runtime/onert/backend/cpu_common/MemoryManager.cc | 91 + runtime/onert/backend/cpu_common/MemoryManager.h | 72 + runtime/onert/backend/cpu_common/MemoryPlanner.cc | 212 + runtime/onert/backend/cpu_common/MemoryPlanner.h | 200 + .../onert/backend/cpu_common/MemoryPlanner.test.cc | 193 + .../backend/cpu_common/MemoryPlannerFactory.cc | 51 + .../backend/cpu_common/MemoryPlannerFactory.h | 47 + runtime/onert/core/CMakeLists.txt | 21 + runtime/onert/core/include/backend/Backend.h | 50 + .../onert/core/include/backend/BackendContext.h | 91 + .../core/include/backend/CustomKernelBuilder.h | 77 + runtime/onert/core/include/backend/IConfig.h | 45 + .../core/include/backend/IConstantInitializer.h | 280 + .../onert/core/include/backend/IKernelGenerator.h | 76 + .../onert/core/include/backend/IMemoryManager.h | 49 + runtime/onert/core/include/backend/IOptimizer.h | 51 + runtime/onert/core/include/backend/IShapeFixer.h | 55 + runtime/onert/core/include/backend/ITensor.h | 58 + .../onert/core/include/backend/ITensorBuilder.h | 77 + .../onert/core/include/backend/ITensorManager.h | 51 + .../onert/core/include/backend/ITensorRegister.h | 97 + .../onert/core/include/compiler/BackendManager.h | 81 + .../onert/core/include/compiler/BackendResolver.h | 60 + runtime/onert/core/include/compiler/CodeMap.h | 45 + runtime/onert/core/include/compiler/Compiler.h | 118 + .../onert/core/include/compiler/ExecutionBuilder.h | 49 + runtime/onert/core/include/exec/ExecTime.h | 112 + runtime/onert/core/include/exec/Execution.h | 144 + .../onert/core/include/exec/ExecutionObservers.h | 83 + runtime/onert/core/include/exec/FunctionSequence.h | 72 + runtime/onert/core/include/exec/IExecutor.h | 72 + runtime/onert/core/include/exec/IFunction.h | 37 + runtime/onert/core/include/exec/IODescription.h | 66 + runtime/onert/core/include/exec/JSONExecTime.h | 97 + runtime/onert/core/include/exec/NopFunction.h | 54 + runtime/onert/core/include/interp/InterpExecutor.h | 70 + runtime/onert/core/include/ir/BackendSet.h | 40 + runtime/onert/core/include/ir/Coordinates.h | 113 + runtime/onert/core/include/ir/Data.h | 75 + runtime/onert/core/include/ir/DataType.h | 62 + runtime/onert/core/include/ir/Graph.h | 114 + runtime/onert/core/include/ir/Index.h | 45 + runtime/onert/core/include/ir/InternalType.h | 46 + runtime/onert/core/include/ir/Layout.h | 67 + runtime/onert/core/include/ir/LowerInfoMap.h | 42 + runtime/onert/core/include/ir/LoweredGraph.h | 78 + runtime/onert/core/include/ir/OpCode.h | 56 + runtime/onert/core/include/ir/OpSequence.h | 106 + runtime/onert/core/include/ir/OpSequences.h | 87 + runtime/onert/core/include/ir/Operand.h | 114 + runtime/onert/core/include/ir/OperandConstraint.h | 58 + runtime/onert/core/include/ir/OperandIndexMap.h | 34 + .../onert/core/include/ir/OperandIndexSequence.h | 63 + runtime/onert/core/include/ir/OperandInfo.h | 129 + runtime/onert/core/include/ir/Operands.h | 46 + runtime/onert/core/include/ir/Operation.h | 71 + runtime/onert/core/include/ir/OperationIndexList.h | 59 + runtime/onert/core/include/ir/OperationIndexMap.h | 34 + runtime/onert/core/include/ir/OperationVisitor.h | 52 + runtime/onert/core/include/ir/Operations.Include.h | 87 + runtime/onert/core/include/ir/Operations.h | 43 + runtime/onert/core/include/ir/Operations.lst | 90 + runtime/onert/core/include/ir/Padding.h | 73 + runtime/onert/core/include/ir/Shape.h | 86 + runtime/onert/core/include/ir/TypeInfo.h | 59 + runtime/onert/core/include/ir/operand/LowerInfo.h | 69 + .../onert/core/include/ir/operand/PermuteFactor.h | 130 + runtime/onert/core/include/ir/operation/Abs.h | 49 + runtime/onert/core/include/ir/operation/Add.h | 62 + runtime/onert/core/include/ir/operation/ArgMax.h | 62 + .../onert/core/include/ir/operation/AvgPool2D.h | 70 + .../core/include/ir/operation/BatchToSpaceND.h | 50 + runtime/onert/core/include/ir/operation/Cast.h | 49 + .../onert/core/include/ir/operation/Comparison.h | 72 + runtime/onert/core/include/ir/operation/Concat.h | 59 + runtime/onert/core/include/ir/operation/Conv2D.h | 69 + .../core/include/ir/operation/ConvertFp16ToFp32.h | 49 + .../core/include/ir/operation/ConvertFp32ToFp16.h | 49 + runtime/onert/core/include/ir/operation/Custom.h | 75 + .../onert/core/include/ir/operation/DepthToSpace.h | 63 + .../core/include/ir/operation/DepthwiseConv2D.h | 70 + .../onert/core/include/ir/operation/Dequantize.h | 49 + runtime/onert/core/include/ir/operation/Div.h | 62 + .../core/include/ir/operation/EmbeddingLookup.h | 50 + runtime/onert/core/include/ir/operation/Exp.h | 49 + runtime/onert/core/include/ir/operation/Floor.h | 51 + .../core/include/ir/operation/FullyConnected.h | 66 + runtime/onert/core/include/ir/operation/Gather.h | 65 + .../core/include/ir/operation/HashtableLookup.h | 57 + .../onert/core/include/ir/operation/InstanceNorm.h | 65 + .../core/include/ir/operation/L2Normalization.h | 62 + runtime/onert/core/include/ir/operation/L2Pool2D.h | 69 + runtime/onert/core/include/ir/operation/LSTM.h | 89 + .../ir/operation/LocalResponseNormalization.h | 66 + .../onert/core/include/ir/operation/LogicalAnd.h | 50 + .../onert/core/include/ir/operation/LogicalNot.h | 49 + .../onert/core/include/ir/operation/LogicalOr.h | 50 + runtime/onert/core/include/ir/operation/Logistic.h | 49 + .../onert/core/include/ir/operation/LowerInfo.h | 54 + runtime/onert/core/include/ir/operation/Max.h | 50 + .../onert/core/include/ir/operation/MaxPool2D.h | 69 + runtime/onert/core/include/ir/operation/Mean.h | 62 + runtime/onert/core/include/ir/operation/Min.h | 50 + runtime/onert/core/include/ir/operation/Mul.h | 62 + runtime/onert/core/include/ir/operation/Neg.h | 49 + runtime/onert/core/include/ir/operation/OneHot.h | 63 + runtime/onert/core/include/ir/operation/PReLU.h | 50 + runtime/onert/core/include/ir/operation/Pack.h | 53 + runtime/onert/core/include/ir/operation/Pad.h | 63 + runtime/onert/core/include/ir/operation/Permute.h | 78 + runtime/onert/core/include/ir/operation/RNN.h | 70 + runtime/onert/core/include/ir/operation/RSQRT.h | 49 + runtime/onert/core/include/ir/operation/ReLU.h | 49 + runtime/onert/core/include/ir/operation/ReLU1.h | 49 + runtime/onert/core/include/ir/operation/ReLU6.h | 49 + .../onert/core/include/ir/operation/ReduceMax.h | 65 + .../onert/core/include/ir/operation/ReduceMin.h | 65 + .../onert/core/include/ir/operation/ReduceSum.h | 63 + runtime/onert/core/include/ir/operation/Reshape.h | 52 + .../core/include/ir/operation/ResizeBilinear.h | 64 + runtime/onert/core/include/ir/operation/SQRT.h | 49 + runtime/onert/core/include/ir/operation/Shape.h | 51 + runtime/onert/core/include/ir/operation/Sin.h | 49 + runtime/onert/core/include/ir/operation/Slice.h | 64 + runtime/onert/core/include/ir/operation/Softmax.h | 63 + .../core/include/ir/operation/SpaceToBatchND.h | 53 + .../onert/core/include/ir/operation/SpaceToDepth.h | 63 + runtime/onert/core/include/ir/operation/Split.h | 59 + .../core/include/ir/operation/SquaredDifference.h | 50 + runtime/onert/core/include/ir/operation/Squeeze.h | 62 + .../onert/core/include/ir/operation/StridedSlice.h | 69 + runtime/onert/core/include/ir/operation/Sub.h | 62 + runtime/onert/core/include/ir/operation/Tanh.h | 49 + runtime/onert/core/include/ir/operation/TopKV2.h | 69 + .../onert/core/include/ir/operation/Transpose.h | 64 + .../core/include/ir/operation/TransposeConv.h | 68 + runtime/onert/core/include/ir/operation/Unpack.h | 59 + runtime/onert/core/include/util/Config.lst | 44 + runtime/onert/core/include/util/ConfigSource.h | 58 + runtime/onert/core/include/util/EnvConfigSource.h | 41 + .../onert/core/include/util/EventCollectorGlobal.h | 155 + .../onert/core/include/util/GeneralConfigSource.h | 44 + runtime/onert/core/include/util/IConfigSource.h | 46 + runtime/onert/core/include/util/ITimer.h | 59 + runtime/onert/core/include/util/Index.h | 154 + runtime/onert/core/include/util/ObjectManager.h | 148 + runtime/onert/core/include/util/Set.h | 166 + runtime/onert/core/include/util/ShapeInference.h | 90 + runtime/onert/core/include/util/Utils.h | 27 + .../onert/core/include/util/feature/nchw/Reader.h | 118 + .../onert/core/include/util/feature/nchw/View.h | 137 + .../onert/core/include/util/feature/nhwc/Reader.h | 120 + .../onert/core/include/util/feature/nhwc/View.h | 139 + runtime/onert/core/include/util/logging.h | 63 + runtime/onert/core/src/backend/BackendContext.cc | 64 + runtime/onert/core/src/compiler/BackendManager.cc | 140 + runtime/onert/core/src/compiler/BackendResolver.cc | 25 + .../onert/core/src/compiler/CachedDataDeleter.h | 103 + runtime/onert/core/src/compiler/Compiler.cc | 209 + runtime/onert/core/src/compiler/ExecutorFactory.cc | 379 + runtime/onert/core/src/compiler/ExecutorFactory.h | 62 + runtime/onert/core/src/compiler/HEScheduler.cc | 615 + runtime/onert/core/src/compiler/HEScheduler.h | 186 + runtime/onert/core/src/compiler/IScheduler.h | 38 + runtime/onert/core/src/compiler/Linear.cc | 280 + runtime/onert/core/src/compiler/Linear.h | 54 + runtime/onert/core/src/compiler/ManualScheduler.cc | 103 + runtime/onert/core/src/compiler/ManualScheduler.h | 41 + runtime/onert/core/src/compiler/OperandContext.cc | 45 + runtime/onert/core/src/compiler/OperandContext.h | 55 + .../onert/core/src/compiler/OperationValidator.cc | 1079 + .../onert/core/src/compiler/OperationValidator.h | 93 + runtime/onert/core/src/compiler/ParamChecker.cc | 33 + runtime/onert/core/src/compiler/ParamChecker.h | 73 + runtime/onert/core/src/dumper/dot/DotBuilder.cc | 83 + runtime/onert/core/src/dumper/dot/DotBuilder.h | 62 + runtime/onert/core/src/dumper/dot/DotDumper.cc | 199 + runtime/onert/core/src/dumper/dot/DotDumper.h | 69 + .../onert/core/src/dumper/dot/DotOpSequenceInfo.cc | 56 + .../onert/core/src/dumper/dot/DotOpSequenceInfo.h | 59 + runtime/onert/core/src/dumper/dot/Node.cc | 56 + runtime/onert/core/src/dumper/dot/Node.h | 127 + runtime/onert/core/src/dumper/dot/OperandNode.cc | 60 + runtime/onert/core/src/dumper/dot/OperandNode.h | 79 + runtime/onert/core/src/dumper/dot/OperationNode.cc | 46 + runtime/onert/core/src/dumper/dot/OperationNode.h | 62 + runtime/onert/core/src/exec/DataflowExecutor.cc | 175 + runtime/onert/core/src/exec/DataflowExecutor.h | 96 + runtime/onert/core/src/exec/ExecTime.cc | 137 + runtime/onert/core/src/exec/Execution.cc | 131 + runtime/onert/core/src/exec/ExecutionObservee.cc | 64 + runtime/onert/core/src/exec/ExecutionObservee.h | 56 + runtime/onert/core/src/exec/ExecutionObservers.cc | 126 + runtime/onert/core/src/exec/ExecutorBase.cc | 165 + runtime/onert/core/src/exec/ExecutorBase.h | 133 + runtime/onert/core/src/exec/FunctionSequence.cc | 62 + runtime/onert/core/src/exec/JSONExecTime.cc | 231 + runtime/onert/core/src/exec/Job.cc | 33 + runtime/onert/core/src/exec/Job.h | 69 + runtime/onert/core/src/exec/LinearExecutor.cc | 39 + runtime/onert/core/src/exec/LinearExecutor.h | 70 + runtime/onert/core/src/exec/ParallelExecutor.cc | 146 + runtime/onert/core/src/exec/ParallelExecutor.h | 67 + runtime/onert/core/src/exec/ParallelScheduler.cc | 55 + runtime/onert/core/src/exec/ParallelScheduler.h | 60 + runtime/onert/core/src/exec/Sink.h | 199 + runtime/onert/core/src/exec/Source.h | 208 + runtime/onert/core/src/exec/ThreadPool.cc | 65 + runtime/onert/core/src/exec/ThreadPool.h | 73 + runtime/onert/core/src/exec/WorkQueue.cc | 104 + runtime/onert/core/src/exec/WorkQueue.h | 87 + runtime/onert/core/src/interp/Buffer.h | 91 + runtime/onert/core/src/interp/ExecEnv.h | 162 + runtime/onert/core/src/interp/InterpExecutor.cc | 114 + runtime/onert/core/src/interp/InterpOps.lst | 89 + runtime/onert/core/src/interp/Interpreter.cc | 184 + runtime/onert/core/src/interp/Interpreter.h | 64 + runtime/onert/core/src/interp/Registration.h | 43 + runtime/onert/core/src/interp/Tensor.cc | 53 + runtime/onert/core/src/interp/Tensor.h | 177 + .../onert/core/src/interp/operations/AvgPool2D.cc | 125 + .../src/interp/operations/BinaryArithmeticOps.cc | 193 + runtime/onert/core/src/interp/operations/Concat.cc | 147 + runtime/onert/core/src/interp/operations/Conv2D.cc | 150 + .../core/src/interp/operations/DepthwiseConv2D.cc | 155 + .../core/src/interp/operations/FullyConnected.cc | 135 + runtime/onert/core/src/interp/operations/Gather.cc | 138 + .../core/src/interp/operations/InstanceNorm.cc | 121 + .../onert/core/src/interp/operations/Logistic.cc | 99 + .../onert/core/src/interp/operations/MaxPool2D.cc | 124 + .../core/src/interp/operations/OperationUtil.h | 210 + runtime/onert/core/src/interp/operations/Pad.cc | 106 + .../onert/core/src/interp/operations/Reshape.cc | 63 + .../onert/core/src/interp/operations/Softmax.cc | 160 + .../core/src/interp/operations/TransposeConv.cc | 141 + .../core/src/interp/operations/UnaryActivations.cc | 153 + runtime/onert/core/src/ir/Coordinates.cc | 50 + runtime/onert/core/src/ir/Graph.cc | 103 + runtime/onert/core/src/ir/GraphIterator.cc | 84 + runtime/onert/core/src/ir/GraphIterator.h | 74 + runtime/onert/core/src/ir/LayoutSet.cc | 66 + runtime/onert/core/src/ir/LayoutSet.h | 58 + runtime/onert/core/src/ir/LoweredGraph.cc | 496 + runtime/onert/core/src/ir/OpCode.cc | 37 + runtime/onert/core/src/ir/OpSequence.cc | 93 + runtime/onert/core/src/ir/OpSequences.cc | 88 + runtime/onert/core/src/ir/Operand.cc | 61 + runtime/onert/core/src/ir/OperandIndexSequence.cc | 65 + runtime/onert/core/src/ir/Operands.cc | 36 + runtime/onert/core/src/ir/Operation.cc | 55 + runtime/onert/core/src/ir/OperationCloner.cc | 42 + runtime/onert/core/src/ir/OperationCloner.h | 46 + runtime/onert/core/src/ir/OperationDumper.cc | 634 + runtime/onert/core/src/ir/OperationDumper.h | 99 + runtime/onert/core/src/ir/OperationIndexList.cc | 37 + runtime/onert/core/src/ir/Operations.cc | 37 + runtime/onert/core/src/ir/Padding.cc | 154 + runtime/onert/core/src/ir/Shape.cc | 104 + runtime/onert/core/src/ir/TypeInfo.cc | 47 + runtime/onert/core/src/ir/operation/Abs.cc | 39 + runtime/onert/core/src/ir/operation/Add.cc | 40 + runtime/onert/core/src/ir/operation/ArgMax.cc | 40 + runtime/onert/core/src/ir/operation/AvgPool2D.cc | 40 + .../onert/core/src/ir/operation/BatchToSpaceND.cc | 40 + runtime/onert/core/src/ir/operation/Cast.cc | 39 + runtime/onert/core/src/ir/operation/Comparison.cc | 40 + runtime/onert/core/src/ir/operation/Concat.cc | 40 + runtime/onert/core/src/ir/operation/Conv2D.cc | 40 + .../core/src/ir/operation/ConvertFp16ToFp32.cc | 40 + .../core/src/ir/operation/ConvertFp32ToFp16.cc | 40 + runtime/onert/core/src/ir/operation/Custom.cc | 44 + .../onert/core/src/ir/operation/DepthToSpace.cc | 40 + .../onert/core/src/ir/operation/DepthwiseConv2D.cc | 40 + runtime/onert/core/src/ir/operation/Dequantize.cc | 39 + runtime/onert/core/src/ir/operation/Div.cc | 40 + .../onert/core/src/ir/operation/EmbeddingLookup.cc | 40 + runtime/onert/core/src/ir/operation/Exp.cc | 39 + runtime/onert/core/src/ir/operation/Floor.cc | 39 + .../onert/core/src/ir/operation/FullyConnected.cc | 40 + runtime/onert/core/src/ir/operation/Gather.cc | 40 + .../onert/core/src/ir/operation/HashtableLookup.cc | 40 + .../onert/core/src/ir/operation/InstanceNorm.cc | 40 + .../onert/core/src/ir/operation/L2Normalization.cc | 40 + runtime/onert/core/src/ir/operation/L2Pool2D.cc | 40 + runtime/onert/core/src/ir/operation/LSTM.cc | 40 + .../src/ir/operation/LocalResponseNormalization.cc | 41 + runtime/onert/core/src/ir/operation/LogicalAnd.cc | 39 + runtime/onert/core/src/ir/operation/LogicalNot.cc | 39 + runtime/onert/core/src/ir/operation/LogicalOr.cc | 39 + runtime/onert/core/src/ir/operation/Logistic.cc | 39 + runtime/onert/core/src/ir/operation/LowerInfo.cc | 34 + runtime/onert/core/src/ir/operation/Max.cc | 39 + runtime/onert/core/src/ir/operation/MaxPool2D.cc | 40 + runtime/onert/core/src/ir/operation/Mean.cc | 40 + runtime/onert/core/src/ir/operation/Min.cc | 39 + runtime/onert/core/src/ir/operation/Mul.cc | 40 + runtime/onert/core/src/ir/operation/Neg.cc | 39 + runtime/onert/core/src/ir/operation/OneHot.cc | 37 + runtime/onert/core/src/ir/operation/PReLU.cc | 39 + runtime/onert/core/src/ir/operation/Pack.cc | 33 + runtime/onert/core/src/ir/operation/Pad.cc | 38 + runtime/onert/core/src/ir/operation/Permute.cc | 44 + runtime/onert/core/src/ir/operation/RNN.cc | 40 + runtime/onert/core/src/ir/operation/RSQRT.cc | 39 + runtime/onert/core/src/ir/operation/ReLU.cc | 39 + runtime/onert/core/src/ir/operation/ReLU1.cc | 39 + runtime/onert/core/src/ir/operation/ReLU6.cc | 39 + runtime/onert/core/src/ir/operation/ReduceMax.cc | 40 + runtime/onert/core/src/ir/operation/ReduceMin.cc | 40 + runtime/onert/core/src/ir/operation/ReduceSum.cc | 40 + runtime/onert/core/src/ir/operation/Reshape.cc | 39 + .../onert/core/src/ir/operation/ResizeBilinear.cc | 40 + runtime/onert/core/src/ir/operation/SQRT.cc | 39 + runtime/onert/core/src/ir/operation/Shape.cc | 39 + runtime/onert/core/src/ir/operation/Sin.cc | 39 + runtime/onert/core/src/ir/operation/Slice.cc | 37 + runtime/onert/core/src/ir/operation/Softmax.cc | 40 + .../onert/core/src/ir/operation/SpaceToBatchND.cc | 40 + .../onert/core/src/ir/operation/SpaceToDepth.cc | 40 + runtime/onert/core/src/ir/operation/Split.cc | 33 + .../core/src/ir/operation/SquaredDifference.cc | 40 + runtime/onert/core/src/ir/operation/Squeeze.cc | 37 + .../onert/core/src/ir/operation/StridedSlice.cc | 40 + runtime/onert/core/src/ir/operation/Sub.cc | 40 + runtime/onert/core/src/ir/operation/Tanh.cc | 39 + runtime/onert/core/src/ir/operation/TopKV2.cc | 40 + runtime/onert/core/src/ir/operation/Transpose.cc | 40 + .../onert/core/src/ir/operation/TransposeConv.cc | 40 + runtime/onert/core/src/ir/operation/Unpack.cc | 33 + .../core/src/ir/pass/ConstantInsertionPass.cc | 102 + .../onert/core/src/ir/pass/ConstantInsertionPass.h | 75 + .../onert/core/src/ir/pass/LoweredOperandPass.h | 52 + .../onert/core/src/ir/pass/LoweredOperationPass.h | 52 + runtime/onert/core/src/ir/pass/OperandPass.cc | 36 + runtime/onert/core/src/ir/pass/OperandPass.h | 54 + runtime/onert/core/src/ir/pass/OperationPass.cc | 38 + runtime/onert/core/src/ir/pass/OperationPass.h | 77 + runtime/onert/core/src/ir/pass/Pass.h | 55 + .../core/src/ir/pass/PermutationEliminationPass.cc | 195 + .../core/src/ir/pass/PermutationEliminationPass.h | 86 + .../core/src/ir/pass/PermutationInsertionPass.cc | 207 + .../core/src/ir/pass/PermutationInsertionPass.h | 59 + .../core/src/ir/pass/PermutationOperationPass.cc | 231 + .../core/src/ir/pass/PermutationOperationPass.h | 54 + runtime/onert/core/src/ir/verifier/Verifier.cc | 96 + runtime/onert/core/src/ir/verifier/Verifier.h | 68 + runtime/onert/core/src/library_info.cc | 17 + runtime/onert/core/src/util/ConfigSource.cc | 122 + runtime/onert/core/src/util/EnvConfigSource.cc | 40 + .../onert/core/src/util/EventCollectorGlobal.cc | 85 + runtime/onert/core/src/util/GeneralConfigSource.cc | 45 + runtime/onert/core/src/util/ShapeInference.cc | 241 + runtime/onert/core/src/util/logging.cc | 23 + runtime/onert/frontend/CMakeLists.txt | 1 + runtime/onert/frontend/base_loader/CMakeLists.txt | 7 + .../frontend/base_loader/include/base_loader.h | 1362 + runtime/onert/frontend/circle/CMakeLists.txt | 17 + .../onert/frontend/circle/include/circle_loader.h | 32 + runtime/onert/frontend/circle/src/circle_loader.cc | 134 + .../frontend/circle/src/circle_schema_generated.h | 9952 +++++++ .../frontend/nnapi/ANeuralNetworksModel.test.cc | 25 + runtime/onert/frontend/nnapi/CMakeLists.txt | 27 + runtime/onert/frontend/nnapi/compilation.cc | 110 + runtime/onert/frontend/nnapi/event.cc | 36 + runtime/onert/frontend/nnapi/execution.cc | 480 + runtime/onert/frontend/nnapi/memory.cc | 42 + runtime/onert/frontend/nnapi/model.cc | 411 + .../nnapi/wrapper/ANeuralNetworksCompilation.cc | 42 + .../nnapi/wrapper/ANeuralNetworksCompilation.h | 42 + .../frontend/nnapi/wrapper/ANeuralNetworksEvent.cc | 42 + .../frontend/nnapi/wrapper/ANeuralNetworksEvent.h | 44 + .../nnapi/wrapper/ANeuralNetworksExecution.cc | 288 + .../nnapi/wrapper/ANeuralNetworksExecution.h | 74 + .../nnapi/wrapper/ANeuralNetworksMemory.cc | 46 + .../frontend/nnapi/wrapper/ANeuralNetworksMemory.h | 39 + .../frontend/nnapi/wrapper/ANeuralNetworksModel.cc | 267 + .../frontend/nnapi/wrapper/ANeuralNetworksModel.h | 71 + .../onert/frontend/nnapi/wrapper/NNAPIConvert.cc | 100 + .../onert/frontend/nnapi/wrapper/NNAPIConvert.h | 79 + .../frontend/nnapi/wrapper/OperationFactory.cc | 1899 ++ .../frontend/nnapi/wrapper/OperationFactory.h | 60 + runtime/onert/frontend/tflite/CMakeLists.txt | 17 + .../onert/frontend/tflite/include/tflite_loader.h | 34 + runtime/onert/frontend/tflite/src/tflite_loader.cc | 110 + .../frontend/tflite/src/tflite_schema_generated.h | 9553 ++++++ .../onert/frontend/tflite/tflite_schema-1.13.1.fbs | 795 + runtime/onert/frontend/tflite/tflite_schema.fbs | 1095 + runtime/onert/sample/CMakeLists.txt | 1 + runtime/onert/sample/minimal/CMakeLists.txt | 10 + runtime/onert/sample/minimal/README.md | 13 + runtime/onert/sample/minimal/src/minimal.cc | 69 + runtime/onert/test/CMakeLists.txt | 15 + runtime/onert/test/core/compiler/Scheduler.cc | 569 + runtime/onert/test/core/exec/ExecInstance.cc | 306 + runtime/onert/test/core/exec/ExecTime.test.cc | 99 + runtime/onert/test/core/interp/ExecManager.cc | 333 + runtime/onert/test/graph/Graph.cc | 52 + runtime/onert/test/graph/Index.cc | 34 + runtime/onert/test/graph/MockNode.h | 47 + runtime/onert/test/graph/operand/IndexSet.cc | 52 + runtime/onert/test/graph/operand/LayoutSet.cc | 43 + runtime/onert/test/graph/operand/Set.cc | 45 + runtime/onert/test/graph/operand/UseDef.cc | 85 + runtime/onert/test/graph/operation/Set.cc | 33 + runtime/onert/test/graph/operation/SetIO.cc | 99 + runtime/onert/test/graph/verifier/Verifier.cc | 49 + runtime/onert/test/util/ObjectManager.cc | 97 + runtime/onert/test/util/ShapeInference.cc | 230 + tests/CMakeLists.txt | 8 +- tests/custom_op/CMakeLists.txt | 64 +- tests/custom_op/FillFrom/CMakeLists.txt | 7 +- tests/custom_op/FillFrom/FillFrom_runner.cc | 2 +- tests/framework/run_test.sh | 277 - .../tests/MODELS/inception_module/config.sh | 1 - .../tests/MODELS/inception_nonslim/config.sh | 2 - .../tests/MODELS/inception_slim/config.sh | 2 - tests/framework/tests/MODELS/mobilenet/config.sh | 2 - tests/framework/tests/add/1D/config.sh | 1 - tests/framework/tests/add/4D/config.sh | 1 - .../tests/average_pool_2d/avgpool1/config.sh | 1 - .../tests/average_pool_2d/avgpool2/config.sh | 1 - tests/framework/tests/batch_to_space_nd2/config.sh | 1 - tests/framework/tests/cast/config.sh | 1 - tests/framework/tests/concat/2D/config.sh | 1 - tests/framework/tests/concat/concat1/config.sh | 1 - tests/framework/tests/concat/concat2/config.sh | 1 - .../framework/tests/conv_2d/convolution1/config.sh | 1 - .../framework/tests/conv_2d/convolution2/config.sh | 1 - tests/framework/tests/custom/abs/config.sh | 1 - .../tests/custom/squared_difference/config.sh | 1 - .../framework/tests/custom/tensorflowmax/config.sh | 1 - .../framework/tests/custom/tensorflowsum/config.sh | 1 - .../tests/depthwise_conv_2d/depthconv1/config.sh | 1 - .../tests/depthwise_conv_2d/depthconv2/config.sh | 1 - tests/framework/tests/div/broadcast/config.sh | 1 - tests/framework/tests/embedding_lookup/config.sh | 1 - tests/framework/tests/exp/config.sh | 1 - tests/framework/tests/floor/floor1/config.sh | 1 - tests/framework/tests/floor/floor2/config.sh | 1 - tests/framework/tests/fullyconnected/fc1/config.sh | 1 - .../tests/fullyconnected/matmul2x2/config.sh | 6 - tests/framework/tests/gather/config.sh | 1 - tests/framework/tests/hashtable_lookup/config.sh | 1 - tests/framework/tests/l2_normalization/config.sh | 1 - tests/framework/tests/l2_pool_2d/config.sh | 1 - tests/framework/tests/logistic/config.sh | 1 - tests/framework/tests/max/config.sh | 1 - .../framework/tests/max_pool_2d/maxpool1/config.sh | 1 - .../framework/tests/max_pool_2d/maxpool2/config.sh | 1 - tests/framework/tests/mean/config.sh | 1 - tests/framework/tests/min/config.sh | 1 - tests/framework/tests/mul/broadcast/config.sh | 1 - tests/framework/tests/neg/config.sh | 1 - tests/framework/tests/pack/config.sh | 1 - tests/framework/tests/pad/4D_2D/config.sh | 1 - tests/framework/tests/pad/pad1/config.sh | 1 - tests/framework/tests/pad/pad2/config.sh | 1 - tests/framework/tests/reduce_max/config.sh | 1 - tests/framework/tests/reduce_mean/test1/config.sh | 1 - tests/framework/tests/reduce_mean/test2/config.sh | 1 - tests/framework/tests/reduce_sum/config.sh | 1 - tests/framework/tests/relu/config.sh | 1 - tests/framework/tests/relu6/config.sh | 1 - tests/framework/tests/reshape/3D/config.sh | 1 - tests/framework/tests/reshape/reshape1/config.sh | 1 - tests/framework/tests/reshape/reshape2/config.sh | 1 - tests/framework/tests/resize_bilinear/config.sh | 1 - tests/framework/tests/rnn/config.sh | 1 - tests/framework/tests/rsqrt/config.sh | 1 - tests/framework/tests/slice/config.sh | 1 - tests/framework/tests/softmax/config.sh | 1 - tests/framework/tests/space_to_batch_nd2/config.sh | 1 - tests/framework/tests/space_to_depth/config.sh | 1 - tests/framework/tests/sqrt/config.sh | 1 - tests/framework/tests/squeeze/config.sh | 1 - tests/framework/tests/strided_slice/config.sh | 1 - tests/framework/tests/sub/broadcast/config.sh | 1 - tests/framework/tests/tanh/config.sh | 1 - tests/framework/tests/topk_v2/config.sh | 1 - tests/framework/tests/transpose/config.sh | 1 - .../framework/tests/transpose_conv/same/config.sh | 1 - .../framework/tests/transpose_conv/valid/config.sh | 1 - tests/nnapi/CMakeLists.txt | 7 +- tests/nnapi/include/NeuralNetworksWrapper.h | 10 +- tests/nnapi/include/TestHarness.h | 72 +- tests/nnapi/nnapi_gtest.skip.aarch64-linux.acl_cl | 36 + .../nnapi/nnapi_gtest.skip.aarch64-linux.acl_neon | 51 + tests/nnapi/nnapi_gtest.skip.aarch64-linux.cpu | 84 + tests/nnapi/nnapi_gtest.skip.aarch64-linux.srcn | 79 + tests/nnapi/nnapi_gtest.skip.armv7l-linux | 31 - tests/nnapi/nnapi_gtest.skip.armv7l-linux.acl_cl | 36 + tests/nnapi/nnapi_gtest.skip.armv7l-linux.acl_neon | 58 +- tests/nnapi/nnapi_gtest.skip.armv7l-linux.cpu | 84 +- tests/nnapi/nnapi_gtest.skip.armv7l-linux.ncnn | 77 - tests/nnapi/nnapi_gtest.skip.armv7l-linux.srcn | 79 + tests/nnapi/nnapi_gtest.skip.armv7l-tizen | 35 - tests/nnapi/nnapi_gtest.skip.armv7l-tizen.acl_cl | 41 + tests/nnapi/nnapi_gtest.skip.noarch.interp | 72 +- tests/nnapi/nnapi_gtest.skip.x86_64-linux | 68 - tests/nnapi/nnapi_gtest.skip.x86_64-linux.cpu | 74 + .../nnapi_test_generator/android-10/README.md | 6 +- .../android-10/cts_generator.py | 36 +- .../android-10/test_generator.py | 15 +- tests/nnapi/specs/Ex/argmax_ex_float_1.mod.py | 18 - tests/nnapi/specs/Ex/argmax_ex_float_2.mod.py | 18 - tests/nnapi/specs/Ex/argmax_ex_int32.mod.py | 18 - .../nnapi/specs/Ex/argmax_ex_neg_axis_float.mod.py | 17 - .../nnapi/specs/Ex/argmax_ex_neg_axis_int32.mod.py | 17 - tests/nnapi/specs/Ex/argmax_ex_quant8.mod.py | 18 - .../specs/Ex/argmax_ex_quant8_neg_axis.mod.py | 17 - tests/nnapi/specs/Ex/equal_ex_1D_float.mod.py | 18 - tests/nnapi/specs/Ex/equal_ex_4D_float.mod.py | 18 - .../specs/Ex/equal_ex_broadcast_4D_2D_float.mod.py | 30 - .../nnapi/specs/Ex/equal_ex_broadcast_float.mod.py | 19 - tests/nnapi/specs/Ex/equal_ex_quant8.mod.py | 18 - ...ully_connected_float_2_weights_as_inputs.mod.py | 0 tests/nnapi/specs/Ex/greater_equal_ex.mod.py | 35 - tests/nnapi/specs/Ex/less_ex.mod.py | 35 - tests/nnapi/specs/Ex/logical_and_ex_1D.mod.py | 19 - tests/nnapi/specs/Ex/logical_and_ex_2D.mod.py | 19 - tests/nnapi/specs/Ex/logical_and_ex_3D.mod.py | 19 - tests/nnapi/specs/Ex/logical_and_ex_4D.mod.py | 19 - .../nnapi/specs/Ex/logical_and_ex_broadcast.mod.py | 19 - .../specs/Ex/logical_and_ex_broadcast_4D_2D.mod.py | 25 - tests/nnapi/specs/Ex/logical_not_ex_1D.mod.py | 16 - tests/nnapi/specs/Ex/logical_not_ex_4D.mod.py | 16 - tests/nnapi/specs/Ex/logical_or_ex_1D.mod.py | 19 - tests/nnapi/specs/Ex/logical_or_ex_2D.mod.py | 19 - tests/nnapi/specs/Ex/logical_or_ex_3D.mod.py | 19 - tests/nnapi/specs/Ex/logical_or_ex_4D.mod.py | 19 - .../nnapi/specs/Ex/logical_or_ex_broadcast.mod.py | 19 - .../specs/Ex/logical_or_ex_broadcast_4D_2D.mod.py | 25 - .../Ex/notequal_ex_broadcast_4D_2D_float.mod.py | 30 - .../specs/Ex/notequal_ex_broadcast_float.mod.py | 19 - tests/nnapi/specs/Ex/notequal_ex_float.mod.py | 18 - tests/nnapi/specs/Ex/notequal_ex_quant8.mod.py | 18 - tests/nnapi/specs/Ex/pack_ex_2D_float_1.mod.py | 0 tests/nnapi/specs/Ex/pack_ex_2D_float_2.mod.py | 0 tests/nnapi/specs/Ex/pack_ex_2D_int_1.mod.py | 0 tests/nnapi/specs/Ex/pack_ex_2D_int_2.mod.py | 0 .../specs/Ex/prelu_ex_broadcast_float_1.mod.py | 23 - .../specs/Ex/prelu_ex_broadcast_quant8_1.mod.py | 24 - tests/nnapi/specs/Ex/prelu_ex_float_1.mod.py | 22 - tests/nnapi/specs/Ex/prelu_ex_quant8_1.mod.py | 23 - tests/nnapi/specs/Ex/reduce_min_ex_float.mod.py | 19 - tests/nnapi/specs/Ex/reduce_min_ex_float_1.mod.py | 18 - tests/nnapi/specs/Ex/reduce_min_ex_float_2.mod.py | 18 - tests/nnapi/specs/Ex/reduce_sum_ex_2D_float.mod.py | 19 - tests/nnapi/specs/Ex/reduce_sum_ex_4D_float.mod.py | 19 - .../Ex/reduce_sum_ex_4D_float_reducing_C.mod.py | 33 - .../Ex/reduce_sum_ex_4D_float_reducing_HW.mod.py | 33 - tests/nnapi/specs/Ex/split_ex_1D_float.mod.py | 40 - tests/nnapi/specs/Ex/split_ex_1D_int32.mod.py | 40 - tests/nnapi/specs/Ex/split_ex_4D_float_1.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_float_2.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_float_3.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_int32_1.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_int32_2.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_int32_3.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_int32_4.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_int32_5.mod.py | 21 - tests/nnapi/specs/Ex/split_ex_4D_quant8.mod.py | 21 - .../specs/Ex/squared_difference_ex_1D_float.mod.py | 0 .../specs/Ex/squared_difference_ex_2D_float.mod.py | 0 .../specs/Ex/squared_difference_ex_3D_float.mod.py | 0 .../specs/Ex/squared_difference_ex_4D_float.mod.py | 0 ...ared_difference_ex_broadcast_4D_2D_float.mod.py | 0 .../squared_difference_ex_broadcast_float.mod.py | 0 .../specs/Ex/transpose_conv_ex_float_1.mod.py | 0 .../specs/Ex/transpose_conv_ex_float_2.mod.py | 0 .../specs/Ex/transpose_conv_ex_float_3.mod.py | 0 .../specs/Ex/transpose_conv_ex_float_4.mod.py | 0 tests/nnapi/specs/Ex/unpack_ex_3D_float_1.mod.py | 0 tests/nnapi/specs/Ex/unpack_ex_3D_float_2.mod.py | 0 tests/nnapi/specs/Ex/unpack_ex_3D_int_1.mod.py | 0 tests/nnapi/specs/Ex/unpack_ex_3D_int_2.mod.py | 0 tests/nnapi/specs/V1_0/add.mod.py | 0 ...dd_broadcast_4D_2D_after_nops_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_0/add_broadcast_quant8.mod.py | 0 tests/nnapi/specs/V1_0/add_quant8.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_float_1.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_float_2.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_float_3.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_float_4.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_float_5.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_quant8_3.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_quant8_4.mod.py | 0 tests/nnapi/specs/V1_0/avg_pool_quant8_5.mod.py | 0 tests/nnapi/specs/V1_0/concat_float_1.mod.py | 0 tests/nnapi/specs/V1_0/concat_float_2.mod.py | 0 tests/nnapi/specs/V1_0/concat_float_3.mod.py | 0 .../specs/V1_0/concat_float_4D_axis3_1_nnfw.mod.py | 0 tests/nnapi/specs/V1_0/concat_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/concat_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/concat_quant8_3.mod.py | 0 tests/nnapi/specs/V1_0/conv_1_h3_w2_SAME.mod.py | 0 tests/nnapi/specs/V1_0/conv_1_h3_w2_VALID.mod.py | 0 tests/nnapi/specs/V1_0/conv_3_h3_w2_SAME.mod.py | 0 tests/nnapi/specs/V1_0/conv_3_h3_w2_VALID.mod.py | 0 tests/nnapi/specs/V1_0/conv_float.mod.py | 0 tests/nnapi/specs/V1_0/conv_float_2.mod.py | 0 tests/nnapi/specs/V1_0/conv_float_channels.mod.py | 0 .../conv_float_channels_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/conv_float_large.mod.py | 0 .../V1_0/conv_float_large_weights_as_inputs.mod.py | 0 .../specs/V1_0/conv_float_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/conv_quant8.mod.py | 0 tests/nnapi/specs/V1_0/conv_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/conv_quant8_channels.mod.py | 0 .../conv_quant8_channels_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/conv_quant8_large.mod.py | 0 .../conv_quant8_large_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/conv_quant8_overflow.mod.py | 0 .../conv_quant8_overflow_weights_as_inputs.mod.py | 0 .../V1_0/conv_quant8_weights_as_inputs.mod.py | 0 .../nnapi/specs/V1_0/depth_to_space_float_1.mod.py | 0 .../nnapi/specs/V1_0/depth_to_space_float_2.mod.py | 0 .../nnapi/specs/V1_0/depth_to_space_float_3.mod.py | 0 .../specs/V1_0/depth_to_space_quant8_1.mod.py | 0 .../specs/V1_0/depth_to_space_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/depthwise_conv.mod.py | 0 .../nnapi/specs/V1_0/depthwise_conv2d_float.mod.py | 0 .../specs/V1_0/depthwise_conv2d_float_2.mod.py | 0 .../specs/V1_0/depthwise_conv2d_float_large.mod.py | 0 .../V1_0/depthwise_conv2d_float_large_2.mod.py | 0 ...e_conv2d_float_large_2_weights_as_inputs.mod.py | 0 ...ise_conv2d_float_large_weights_as_inputs.mod.py | 0 ...depthwise_conv2d_float_weights_as_inputs.mod.py | 0 .../specs/V1_0/depthwise_conv2d_quant8.mod.py | 0 .../specs/V1_0/depthwise_conv2d_quant8_2.mod.py | 0 .../V1_0/depthwise_conv2d_quant8_large.mod.py | 0 ...se_conv2d_quant8_large_weights_as_inputs.mod.py | 0 ...epthwise_conv2d_quant8_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/dequantize.mod.py | 0 tests/nnapi/specs/V1_0/embedding_lookup.mod.py | 0 .../specs/V1_0/embedding_lookup_2d_nnfw.mod.py | 0 .../specs/V1_0/embedding_lookup_4d_nnfw.mod.py | 0 tests/nnapi/specs/V1_0/floor_.mod.py | 0 .../nnapi/specs/V1_0/fully_connected_float.mod.py | 0 .../specs/V1_0/fully_connected_float_1_nnfw.mod.py | 0 .../specs/V1_0/fully_connected_float_2.mod.py | 0 .../specs/V1_0/fully_connected_float_3.mod.py | 0 .../specs/V1_0/fully_connected_float_large.mod.py | 0 ..._connected_float_large_weights_as_inputs.mod.py | 0 .../fully_connected_float_weights_as_inputs.mod.py | 0 .../V1_0/fully_connected_hybrid_1_nnfw.mod.py | 0 .../V1_0/fully_connected_hybrid_2_nnfw.mod.py | 0 .../nnapi/specs/V1_0/fully_connected_quant8.mod.py | 0 .../specs/V1_0/fully_connected_quant8_2.mod.py | 0 .../specs/V1_0/fully_connected_quant8_large.mod.py | 0 ...connected_quant8_large_weights_as_inputs.mod.py | 0 ...fully_connected_quant8_weights_as_inputs.mod.py | 0 .../nnapi/specs/V1_0/hashtable_lookup_float.mod.py | 0 .../V1_0/hashtable_lookup_float_4D_nnfw.mod.py | 0 .../specs/V1_0/hashtable_lookup_quant8.mod.py | 0 tests/nnapi/specs/V1_0/l2_normalization.mod.py | 0 tests/nnapi/specs/V1_0/l2_normalization_2.mod.py | 0 .../nnapi/specs/V1_0/l2_normalization_large.mod.py | 0 tests/nnapi/specs/V1_0/l2_pool_float.mod.py | 0 tests/nnapi/specs/V1_0/l2_pool_float_2.mod.py | 0 tests/nnapi/specs/V1_0/l2_pool_float_large.mod.py | 0 .../specs/V1_0/local_response_norm_float_1.mod.py | 0 .../specs/V1_0/local_response_norm_float_2.mod.py | 0 .../specs/V1_0/local_response_norm_float_3.mod.py | 0 .../specs/V1_0/local_response_norm_float_4.mod.py | 0 tests/nnapi/specs/V1_0/logistic_float_1.mod.py | 0 tests/nnapi/specs/V1_0/logistic_float_2.mod.py | 0 tests/nnapi/specs/V1_0/logistic_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/logistic_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/lsh_projection.mod.py | 0 tests/nnapi/specs/V1_0/lsh_projection_2.mod.py | 0 .../V1_0/lsh_projection_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/lstm.mod.py | 0 tests/nnapi/specs/V1_0/lstm2.mod.py | 0 tests/nnapi/specs/V1_0/lstm2_state.mod.py | 0 tests/nnapi/specs/V1_0/lstm2_state2.mod.py | 0 tests/nnapi/specs/V1_0/lstm3.mod.py | 0 tests/nnapi/specs/V1_0/lstm3_state.mod.py | 0 tests/nnapi/specs/V1_0/lstm3_state2.mod.py | 0 tests/nnapi/specs/V1_0/lstm3_state3.mod.py | 0 tests/nnapi/specs/V1_0/lstm_state.mod.py | 0 tests/nnapi/specs/V1_0/lstm_state2.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_float_1.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_float_2.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_float_3.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_float_4.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_quant8_3.mod.py | 0 tests/nnapi/specs/V1_0/max_pool_quant8_4.mod.py | 0 tests/nnapi/specs/V1_0/mul.mod.py | 0 tests/nnapi/specs/V1_0/mul_4D_nnfw.mod.py | 0 .../specs/V1_0/mul_broadcast_3D_1D_1_nnfw.mod.py | 0 .../specs/V1_0/mul_broadcast_3D_1D_2_nnfw.mod.py | 0 tests/nnapi/specs/V1_0/mul_broadcast_quant8.mod.py | 0 .../nnapi/specs/V1_0/mul_float_square_nnfw.mod.py | 0 tests/nnapi/specs/V1_0/mul_quant8.mod.py | 0 tests/nnapi/specs/V1_0/mul_relu.mod.py | 0 tests/nnapi/specs/V1_0/relu1_float_1.mod.py | 0 tests/nnapi/specs/V1_0/relu1_float_2.mod.py | 0 tests/nnapi/specs/V1_0/relu1_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/relu1_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/relu6_float_1.mod.py | 0 tests/nnapi/specs/V1_0/relu6_float_2.mod.py | 0 tests/nnapi/specs/V1_0/relu6_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/relu6_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/relu_float_1.mod.py | 0 tests/nnapi/specs/V1_0/relu_float_2.mod.py | 0 tests/nnapi/specs/V1_0/relu_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/relu_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/reshape.mod.py | 0 tests/nnapi/specs/V1_0/reshape_quant8.mod.py | 0 .../V1_0/reshape_quant8_weights_as_inputs.mod.py | 0 .../specs/V1_0/reshape_weights_as_inputs.mod.py | 0 tests/nnapi/specs/V1_0/resize_bilinear.mod.py | 0 tests/nnapi/specs/V1_0/resize_bilinear_2.mod.py | 0 tests/nnapi/specs/V1_0/rnn.mod.py | 0 tests/nnapi/specs/V1_0/rnn_state.mod.py | 0 tests/nnapi/specs/V1_0/softmax_float_1.mod.py | 0 tests/nnapi/specs/V1_0/softmax_float_2.mod.py | 0 tests/nnapi/specs/V1_0/softmax_quant8_1.mod.py | 0 tests/nnapi/specs/V1_0/softmax_quant8_2.mod.py | 0 .../nnapi/specs/V1_0/space_to_depth_float_1.mod.py | 0 .../nnapi/specs/V1_0/space_to_depth_float_2.mod.py | 0 .../nnapi/specs/V1_0/space_to_depth_float_3.mod.py | 0 .../specs/V1_0/space_to_depth_quant8_1.mod.py | 0 .../specs/V1_0/space_to_depth_quant8_2.mod.py | 0 tests/nnapi/specs/V1_0/svdf.mod.py | 0 tests/nnapi/specs/V1_0/svdf2.mod.py | 0 tests/nnapi/specs/V1_0/svdf_bias_present.mod.py | 0 tests/nnapi/specs/V1_0/svdf_state.mod.py | 0 tests/nnapi/specs/V1_0/tanh_.mod.py | 0 tests/nnapi/specs/V1_1/batch_to_space.mod.py | 0 .../nnapi/specs/V1_1/batch_to_space_float_1.mod.py | 2 +- .../specs/V1_1/batch_to_space_quant8_1.mod.py | 2 +- tests/nnapi/specs/V1_1/div_.mod.py | 0 tests/nnapi/specs/V1_1/div_broadcast_float.mod.py | 0 .../V1_1/div_broadcast_float_4D_2D_nnfw.mod.py | 0 .../V1_1/fully_connected_float_4d_simple.mod.py | 0 tests/nnapi/specs/V1_1/mean.mod.py | 0 .../V1_1/mean_4D_float_reducing_C_nnfw.mod.py | 0 .../V1_1/mean_4D_float_reducing_HW_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/mean_axis01_1_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/mean_axis01_2_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/mean_float_1.mod.py | 0 tests/nnapi/specs/V1_1/mean_float_2.mod.py | 0 tests/nnapi/specs/V1_1/mean_quant8_1.mod.py | 0 tests/nnapi/specs/V1_1/mean_quant8_2.mod.py | 0 tests/nnapi/specs/V1_1/pad.mod.py | 0 tests/nnapi/specs/V1_1/pad_2D_HW_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/pad_3D_HWC_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/pad_BHWC_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/pad_BHW_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/pad_HWD_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/pad_float_1.mod.py | 0 tests/nnapi/specs/V1_1/pad_quant8_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/space_to_batch.mod.py | 0 .../nnapi/specs/V1_1/space_to_batch_float_1.mod.py | 0 .../specs/V1_1/space_to_batch_float_1_nnfw.mod.py | 0 .../nnapi/specs/V1_1/space_to_batch_float_2.mod.py | 0 .../nnapi/specs/V1_1/space_to_batch_float_3.mod.py | 0 .../specs/V1_1/space_to_batch_quant8_1.mod.py | 0 .../specs/V1_1/space_to_batch_quant8_1_nnfw.mod.py | 0 .../specs/V1_1/space_to_batch_quant8_2.mod.py | 0 .../specs/V1_1/space_to_batch_quant8_2_nnfw.mod.py | 0 .../specs/V1_1/space_to_batch_quant8_3.mod.py | 0 tests/nnapi/specs/V1_1/squeeze.mod.py | 0 .../specs/V1_1/squeeze_2D_float_1_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/squeeze_float_1.mod.py | 0 tests/nnapi/specs/V1_1/squeeze_quant8_1.mod.py | 0 tests/nnapi/specs/V1_1/strided_slice.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_1.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_10.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_11.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_2.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_3.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_4.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_5.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_6.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_7.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_8.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_float_9.mod.py | 0 .../specs/V1_1/strided_slice_qaunt8_10.mod.py | 0 .../specs/V1_1/strided_slice_qaunt8_11.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_1.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_2.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_3.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_4.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_5.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_6.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_7.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_8.mod.py | 0 .../nnapi/specs/V1_1/strided_slice_quant8_9.mod.py | 0 tests/nnapi/specs/V1_1/sub.mod.py | 0 .../V1_1/sub_broadcast_4D_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/sub_broadcast_float.mod.py | 0 tests/nnapi/specs/V1_1/transpose.mod.py | 0 tests/nnapi/specs/V1_1/transpose_2D_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/transpose_3D_nnfw.mod.py | 0 tests/nnapi/specs/V1_1/transpose_float_1.mod.py | 0 tests/nnapi/specs/V1_1/transpose_quant8_1.mod.py | 0 tests/nnapi/specs/V1_2/abs_.mod.py | 0 tests/nnapi/specs/V1_2/abs_1D_float_nnfw.mod.py | 20 + tests/nnapi/specs/V1_2/abs_2D_float_nnfw.mod.py | 20 + tests/nnapi/specs/V1_2/abs_3D_float_nnfw.mod.py | 20 + tests/nnapi/specs/V1_2/abs_4D_float_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/argmax_1.mod.py | 31 + tests/nnapi/specs/V1_2/argmax_2.mod.py | 31 + tests/nnapi/specs/V1_2/argmax_3.mod.py | 33 + tests/nnapi/specs/V1_2/argmax_float_1_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/argmax_float_2_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/argmax_int32_nnfw.mod.py | 18 + .../specs/V1_2/argmax_neg_axis_float_nnfw.mod.py | 17 + .../specs/V1_2/argmax_neg_axis_int32_nnfw.mod.py | 17 + .../specs/V1_2/argmax_quant8_neg_axis_nnfw.mod.py | 17 + tests/nnapi/specs/V1_2/argmax_quant8_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/cast.mod.py | 0 .../specs/V1_2/cast_float32_to_int32_nnfw.mod.py | 0 .../specs/V1_2/cast_int32_to_float32_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/equal.mod.py | 99 + tests/nnapi/specs/V1_2/equal_1D_float_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/equal_4D_float_nnfw.mod.py | 18 + .../V1_2/equal_broadcast_4D_2D_float_nnfw.mod.py | 30 + .../specs/V1_2/equal_broadcast_float_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/equal_quant8_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/exp_.mod.py | 0 tests/nnapi/specs/V1_2/exp_1D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/exp_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/exp_3D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/exp_4D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather.mod.py | 0 tests/nnapi/specs/V1_2/gather_1D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather_1D_int32_nnfw.mod.py | 0 .../nnapi/specs/V1_2/gather_1D_quant8_nnfw.mod.py | 0 .../specs/V1_2/gather_2D_2D_float_1_nnfw.mod.py | 0 .../specs/V1_2/gather_2D_2D_float_2_nnfw.mod.py | 0 .../specs/V1_2/gather_2D_3D_float_1_nnfw.mod.py | 0 .../specs/V1_2/gather_2D_3D_float_2_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather_2D_int32_nnfw.mod.py | 0 .../nnapi/specs/V1_2/gather_2D_quant8_nnfw.mod.py | 0 .../specs/V1_2/gather_3D_2D_float_1_nnfw.mod.py | 0 .../specs/V1_2/gather_3D_2D_float_2_nnfw.mod.py | 0 .../specs/V1_2/gather_3D_2D_float_3_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather_4D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/gather_higher_rank.mod.py | 0 tests/nnapi/specs/V1_2/greater_equal.mod.py | 99 + tests/nnapi/specs/V1_2/greater_equal_nnfw.mod.py | 35 + tests/nnapi/specs/V1_2/less.mod.py | 99 + tests/nnapi/specs/V1_2/less_nnfw.mod.py | 35 + tests/nnapi/specs/V1_2/logical_and.mod.py | 43 + tests/nnapi/specs/V1_2/logical_and_1D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_and_2D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_and_3D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_and_4D_nnfw.mod.py | 19 + .../V1_2/logical_and_broadcast_4D_2D_nnfw.mod.py | 25 + .../specs/V1_2/logical_and_broadcast_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_not.mod.py | 25 + tests/nnapi/specs/V1_2/logical_not_1D_nnfw.mod.py | 16 + tests/nnapi/specs/V1_2/logical_not_4D_nnfw.mod.py | 16 + tests/nnapi/specs/V1_2/logical_or.mod.py | 43 + tests/nnapi/specs/V1_2/logical_or_1D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_or_2D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_or_3D_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/logical_or_4D_nnfw.mod.py | 19 + .../V1_2/logical_or_broadcast_4D_2D_nnfw.mod.py | 25 + .../specs/V1_2/logical_or_broadcast_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/maximum.mod.py | 64 + tests/nnapi/specs/V1_2/minimum.mod.py | 64 + tests/nnapi/specs/V1_2/neg.mod.py | 0 tests/nnapi/specs/V1_2/neg_1D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/neg_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/neg_3D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/neg_3D_int_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/neg_4D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/neg_4D_int_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/not_equal.mod.py | 99 + .../not_equal_broadcast_4D_2D_float_nnfw.mod.py | 30 + .../V1_2/not_equal_broadcast_float_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/not_equal_float_nnfw.mod.py | 18 + .../nnapi/specs/V1_2/not_equal_quant8_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/prelu.mod.py | 61 + .../specs/V1_2/prelu_broadcast_float_1_nnfw.mod.py | 23 + .../V1_2/prelu_broadcast_quant8_1_nnfw.mod.py | 24 + tests/nnapi/specs/V1_2/prelu_float_1_nnfw.mod.py | 22 + tests/nnapi/specs/V1_2/prelu_quant8_1_nnfw.mod.py | 23 + tests/nnapi/specs/V1_2/reduce_max.mod.py | 0 .../specs/V1_2/reduce_max_2D_float_nnfw.mod.py | 0 .../specs/V1_2/reduce_max_2D_int32_nnfw.mod.py | 0 .../reduce_max_4D_float_reducing_C_nnfw.mod.py | 0 .../reduce_max_4D_float_reducing_HW_nnfw.mod.py | 0 .../specs/V1_2/reduce_max_float_1_nnfw.mod.py | 0 .../specs/V1_2/reduce_max_float_2_nnfw.mod.py | 0 .../nnapi/specs/V1_2/reduce_max_float_nnfw.mod.py | 0 .../specs/V1_2/reduce_max_quant8_1_nnfw.mod.py | 0 .../specs/V1_2/reduce_max_quant8_2_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/reduce_min.mod.py | 70 + .../specs/V1_2/reduce_min_float_1_nnfw.mod.py | 18 + .../specs/V1_2/reduce_min_float_2_nnfw.mod.py | 18 + .../nnapi/specs/V1_2/reduce_min_float_nnfw.mod.py | 19 + tests/nnapi/specs/V1_2/reduce_sum.mod.py | 66 + .../specs/V1_2/reduce_sum_2D_float_nnfw.mod.py | 19 + .../specs/V1_2/reduce_sum_4D_float_nnfw.mod.py | 19 + .../reduce_sum_4D_float_reducing_C_nnfw.mod.py | 33 + .../reduce_sum_4D_float_reducing_HW_nnfw.mod.py | 33 + tests/nnapi/specs/V1_2/rsqrt.mod.py | 0 tests/nnapi/specs/V1_2/rsqrt_1D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/rsqrt_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/rsqrt_3D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/rsqrt_4D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/sin_1D_float_nnfw.mod.py | 13 + tests/nnapi/specs/V1_2/sin_4D_float_nnfw.mod.py | 18 + tests/nnapi/specs/V1_2/slice.mod.py | 147 + tests/nnapi/specs/V1_2/split_1D_float_nnfw.mod.py | 40 + tests/nnapi/specs/V1_2/split_1D_int32_nnfw.mod.py | 40 + .../nnapi/specs/V1_2/split_4D_float_1_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_float_2_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_float_3_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_int32_1_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_int32_2_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_int32_3_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_int32_4_nnfw.mod.py | 21 + .../nnapi/specs/V1_2/split_4D_int32_5_nnfw.mod.py | 21 + tests/nnapi/specs/V1_2/split_4D_quant8_nnfw.mod.py | 21 + tests/nnapi/specs/V1_2/split_float_1.mod.py | 38 + tests/nnapi/specs/V1_2/split_float_2.mod.py | 37 + tests/nnapi/specs/V1_2/split_float_3.mod.py | 39 + tests/nnapi/specs/V1_2/split_float_4.mod.py | 36 + tests/nnapi/specs/V1_2/split_float_5.mod.py | 36 + tests/nnapi/specs/V1_2/split_int32_1.mod.py | 38 + tests/nnapi/specs/V1_2/split_int32_2.mod.py | 37 + tests/nnapi/specs/V1_2/split_int32_3.mod.py | 39 + tests/nnapi/specs/V1_2/split_int32_4.mod.py | 36 + tests/nnapi/specs/V1_2/split_quant8_1.mod.py | 38 + tests/nnapi/specs/V1_2/split_quant8_2.mod.py | 37 + tests/nnapi/specs/V1_2/split_quant8_3.mod.py | 39 + tests/nnapi/specs/V1_2/split_quant8_4.mod.py | 36 + tests/nnapi/specs/V1_2/sqrt_.mod.py | 0 tests/nnapi/specs/V1_2/sqrt_1D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/sqrt_2D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/sqrt_3D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/sqrt_4D_float_nnfw.mod.py | 0 tests/nnapi/specs/V1_2/sub_v1_2.mod.py | 99 + tests/nnapi/specs/V1_2/sub_v1_2_broadcast.mod.py | 60 + tests/nnapi/specs/V1_2/tanh_v1_2.mod.py | 89 + tests/nnapi/specs/V1_2/topk_v2.mod.py | 0 .../nnapi/specs/V1_2/topk_v2_1D_float_nnfw.mod.py | 2 +- .../nnapi/specs/V1_2/topk_v2_1D_int32_nnfw.mod.py | 2 +- .../nnapi/specs/V1_2/topk_v2_1D_quant8_nnfw.mod.py | 2 +- .../nnapi/specs/V1_2/topk_v2_2D_float_nnfw.mod.py | 4 +- .../nnapi/specs/V1_2/topk_v2_2D_int32_nnfw.mod.py | 2 +- .../nnapi/specs/V1_2/topk_v2_2D_quant8_nnfw.mod.py | 2 +- tests/nnapi/specs/V1_2/transpose_v1_2.mod.py | 81 + .../V1_0/mobilenet_224_gender_basic_fixed.mod.py | 0 .../specs/skip/V1_0/mobilenet_quantized.mod.py | 0 tests/nnapi/specs/skip/V1_1/add_relaxed.mod.py | 0 .../skip/V1_1/avg_pool_float_1_relaxed.mod.py | 0 .../skip/V1_1/avg_pool_float_2_relaxed.mod.py | 0 .../skip/V1_1/avg_pool_float_3_relaxed.mod.py | 0 .../skip/V1_1/avg_pool_float_4_relaxed.mod.py | 0 .../skip/V1_1/avg_pool_float_5_relaxed.mod.py | 0 .../V1_1/batch_to_space_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/batch_to_space_relaxed.mod.py | 0 .../specs/skip/V1_1/concat_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/concat_float_2_relaxed.mod.py | 0 .../specs/skip/V1_1/concat_float_3_relaxed.mod.py | 0 .../skip/V1_1/conv_1_h3_w2_SAME_relaxed.mod.py | 0 .../skip/V1_1/conv_1_h3_w2_VALID_relaxed.mod.py | 0 .../skip/V1_1/conv_3_h3_w2_SAME_relaxed.mod.py | 0 .../skip/V1_1/conv_3_h3_w2_VALID_relaxed.mod.py | 0 .../specs/skip/V1_1/conv_float_2_relaxed.mod.py | 0 .../skip/V1_1/conv_float_channels_relaxed.mod.py | 0 ...float_channels_weights_as_inputs_relaxed.mod.py | 0 .../skip/V1_1/conv_float_large_relaxed.mod.py | 0 ...nv_float_large_weights_as_inputs_relaxed.mod.py | 0 .../specs/skip/V1_1/conv_float_relaxed.mod.py | 0 .../conv_float_weights_as_inputs_relaxed.mod.py | 0 .../V1_1/depth_to_space_float_1_relaxed.mod.py | 0 .../V1_1/depth_to_space_float_2_relaxed.mod.py | 0 .../V1_1/depth_to_space_float_3_relaxed.mod.py | 0 .../V1_1/depthwise_conv2d_float_2_relaxed.mod.py | 0 .../depthwise_conv2d_float_large_2_relaxed.mod.py | 0 ..._float_large_2_weights_as_inputs_relaxed.mod.py | 0 .../depthwise_conv2d_float_large_relaxed.mod.py | 0 ...2d_float_large_weights_as_inputs_relaxed.mod.py | 0 .../V1_1/depthwise_conv2d_float_relaxed.mod.py | 0 ...e_conv2d_float_weights_as_inputs_relaxed.mod.py | 0 .../specs/skip/V1_1/depthwise_conv_relaxed.mod.py | 0 .../specs/skip/V1_1/dequantize_relaxed.mod.py | 0 .../skip/V1_1/div_broadcast_float_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/div_relaxed.mod.py | 0 .../skip/V1_1/embedding_lookup_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/floor_relaxed.mod.py | 0 .../V1_1/fully_connected_float_2_relaxed.mod.py | 0 .../fully_connected_float_4d_simple_relaxed.mod.py | 0 .../fully_connected_float_large_relaxed.mod.py | 0 ...ed_float_large_weights_as_inputs_relaxed.mod.py | 0 .../skip/V1_1/fully_connected_float_relaxed.mod.py | 0 ...onnected_float_weights_as_inputs_relaxed.mod.py | 0 .../V1_1/hashtable_lookup_float_relaxed.mod.py | 0 .../skip/V1_1/l2_normalization_2_relaxed.mod.py | 0 .../V1_1/l2_normalization_large_relaxed.mod.py | 0 .../skip/V1_1/l2_normalization_relaxed.mod.py | 0 .../specs/skip/V1_1/l2_pool_float_2_relaxed.mod.py | 0 .../skip/V1_1/l2_pool_float_large_relaxed.mod.py | 0 .../specs/skip/V1_1/l2_pool_float_relaxed.mod.py | 0 .../local_response_norm_float_1_relaxed.mod.py | 0 .../local_response_norm_float_2_relaxed.mod.py | 0 .../local_response_norm_float_3_relaxed.mod.py | 0 .../local_response_norm_float_4_relaxed.mod.py | 0 .../skip/V1_1/logistic_float_1_relaxed.mod.py | 0 .../skip/V1_1/logistic_float_2_relaxed.mod.py | 0 .../skip/V1_1/lsh_projection_2_relaxed.mod.py | 0 .../specs/skip/V1_1/lsh_projection_relaxed.mod.py | 0 ...lsh_projection_weights_as_inputs_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/lstm2_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm2_state2_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm2_state_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/lstm3_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm3_state2_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm3_state3_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm3_state_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/lstm_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm_state2_relaxed.mod.py | 0 .../specs/skip/V1_1/lstm_state_relaxed.mod.py | 0 .../skip/V1_1/max_pool_float_1_relaxed.mod.py | 0 .../skip/V1_1/max_pool_float_2_relaxed.mod.py | 0 .../skip/V1_1/max_pool_float_3_relaxed.mod.py | 0 .../skip/V1_1/max_pool_float_4_relaxed.mod.py | 0 .../specs/skip/V1_1/mean_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/mean_float_2_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/mean_relaxed.mod.py | 0 ...mobilenet_224_gender_basic_fixed_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/mul_relaxed.mod.py | 0 .../nnapi/specs/skip/V1_1/mul_relu_relaxed.mod.py | 0 .../specs/skip/V1_1/pad_float_1_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/pad_relaxed.mod.py | 0 .../specs/skip/V1_1/relu1_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/relu1_float_2_relaxed.mod.py | 0 .../specs/skip/V1_1/relu6_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/relu6_float_2_relaxed.mod.py | 0 .../specs/skip/V1_1/relu_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/relu_float_2_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/reshape_relaxed.mod.py | 0 .../V1_1/reshape_weights_as_inputs_relaxed.mod.py | 0 .../skip/V1_1/resize_bilinear_2_relaxed.mod.py | 0 .../specs/skip/V1_1/resize_bilinear_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/rnn_relaxed.mod.py | 0 .../nnapi/specs/skip/V1_1/rnn_state_relaxed.mod.py | 0 .../specs/skip/V1_1/softmax_float_1_relaxed.mod.py | 0 .../specs/skip/V1_1/softmax_float_2_relaxed.mod.py | 0 .../V1_1/space_to_batch_float_1_relaxed.mod.py | 0 .../V1_1/space_to_batch_float_2_relaxed.mod.py | 0 .../V1_1/space_to_batch_float_3_relaxed.mod.py | 0 .../specs/skip/V1_1/space_to_batch_relaxed.mod.py | 0 .../V1_1/space_to_depth_float_1_relaxed.mod.py | 0 .../V1_1/space_to_depth_float_2_relaxed.mod.py | 0 .../V1_1/space_to_depth_float_3_relaxed.mod.py | 0 .../specs/skip/V1_1/squeeze_float_1_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/squeeze_relaxed.mod.py | 0 .../V1_1/strided_slice_float_10_relaxed.mod.py | 0 .../V1_1/strided_slice_float_11_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_1_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_2_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_3_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_4_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_5_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_6_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_7_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_8_relaxed.mod.py | 0 .../skip/V1_1/strided_slice_float_9_relaxed.mod.py | 0 .../specs/skip/V1_1/strided_slice_relaxed.mod.py | 0 .../skip/V1_1/sub_broadcast_float_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/sub_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/svdf2_relaxed.mod.py | 0 .../skip/V1_1/svdf_bias_present_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/svdf_relaxed.mod.py | 0 .../specs/skip/V1_1/svdf_state_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_1/tanh_relaxed.mod.py | 0 .../skip/V1_1/transpose_float_1_relaxed.mod.py | 0 .../nnapi/specs/skip/V1_1/transpose_relaxed.mod.py | 0 tests/nnapi/specs/skip/V1_2/add_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/argmax_1.mod.py | 31 - tests/nnapi/specs/skip/V1_2/argmax_2.mod.py | 31 - tests/nnapi/specs/skip/V1_2/argmax_3.mod.py | 33 - tests/nnapi/specs/skip/V1_2/argmin_1.mod.py | 0 tests/nnapi/specs/skip/V1_2/argmin_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/argmin_3.mod.py | 0 tests/nnapi/specs/skip/V1_2/avg_pool_v1_2.mod.py | 0 .../skip/V1_2/axis_aligned_bbox_transform.mod.py | 0 .../specs/skip/V1_2/batch_to_space_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/bbox_graph.mod.py | 0 .../skip/V1_2/bidirectional_sequence_lstm.mod.py | 0 .../bidirectional_sequence_lstm_aux_input.mod.py | 0 ...idirectional_sequence_lstm_cifg_peephole.mod.py | 0 ...tional_sequence_lstm_float16_batch_major.mod.py | 0 ...uence_lstm_float16_batch_major_aux_input.mod.py | 0 ...e_lstm_float16_batch_major_merge_outputs.mod.py | 0 ...idirectional_sequence_lstm_merge_outputs.mod.py | 0 ...directional_sequence_lstm_norm_fw_output.mod.py | 0 .../skip/V1_2/bidirectional_sequence_rnn.mod.py | 0 .../skip/V1_2/box_with_nms_limit_gaussian.mod.py | 0 .../specs/skip/V1_2/box_with_nms_limit_hard.mod.py | 0 .../skip/V1_2/box_with_nms_limit_linear.mod.py | 0 tests/nnapi/specs/skip/V1_2/channel_shuffle.mod.py | 0 .../nnapi/specs/skip/V1_2/concat_float16_1.mod.py | 0 .../nnapi/specs/skip/V1_2/concat_float16_2.mod.py | 0 .../nnapi/specs/skip/V1_2/concat_float16_3.mod.py | 0 .../specs/skip/V1_2/concat_mixed_quant.mod.py | 0 .../nnapi/specs/skip/V1_2/concat_zero_sized.mod.py | 0 tests/nnapi/specs/skip/V1_2/conv2d_dilation.mod.py | 0 .../specs/skip/V1_2/conv2d_per_channel.mod.py | 0 tests/nnapi/specs/skip/V1_2/conv2d_v1_2.mod.py | 0 .../specs/skip/V1_2/depth_to_space_v1_2.mod.py | 0 .../skip/V1_2/depthwise_conv2d_dilation.mod.py | 0 .../skip/V1_2/depthwise_conv2d_per_channel.mod.py | 0 .../specs/skip/V1_2/depthwise_conv2d_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/dequantize_v1_2.mod.py | 0 .../specs/skip/V1_2/detection_postprocess.mod.py | 0 tests/nnapi/specs/skip/V1_2/div_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/equal.mod.py | 99 - tests/nnapi/specs/skip/V1_2/expand_dims.mod.py | 0 tests/nnapi/specs/skip/V1_2/floor_float16.mod.py | 0 .../specs/skip/V1_2/fully_connected_v1_2.mod.py | 0 .../specs/skip/V1_2/generate_proposals.mod.py | 0 tests/nnapi/specs/skip/V1_2/greater.mod.py | 0 tests/nnapi/specs/skip/V1_2/greater_equal.mod.py | 99 - tests/nnapi/specs/skip/V1_2/grouped_conv2d.mod.py | 0 .../specs/skip/V1_2/heatmap_max_keypoint.mod.py | 0 .../specs/skip/V1_2/instance_normalization.mod.py | 0 .../specs/skip/V1_2/l2_normalization_axis.mod.py | 0 .../specs/skip/V1_2/l2_normalization_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/l2_pool_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/layer_norm_lstm.mod.py | 0 tests/nnapi/specs/skip/V1_2/less.mod.py | 99 - tests/nnapi/specs/skip/V1_2/less_equal.mod.py | 0 .../V1_2/local_response_normalization_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/log.mod.py | 0 tests/nnapi/specs/skip/V1_2/log_softmax.mod.py | 0 tests/nnapi/specs/skip/V1_2/logical_and.mod.py | 43 - tests/nnapi/specs/skip/V1_2/logical_not.mod.py | 25 - tests/nnapi/specs/skip/V1_2/logical_or.mod.py | 43 - tests/nnapi/specs/skip/V1_2/logistic_v1_2.mod.py | 0 .../skip/V1_2/lsh_projection_3_relaxed.mod.py | 0 .../skip/V1_2/lsh_projection_4_relaxed.mod.py | 0 .../skip/V1_2/lsh_projection_deprecated.mod.py | 0 .../specs/skip/V1_2/lsh_projection_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/lstm2_float16.mod.py | 0 .../specs/skip/V1_2/lstm2_state2_float16.mod.py | 0 .../specs/skip/V1_2/lstm2_state_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/lstm3_float16.mod.py | 0 .../specs/skip/V1_2/lstm3_state2_float16.mod.py | 0 .../specs/skip/V1_2/lstm3_state3_float16.mod.py | 0 .../specs/skip/V1_2/lstm3_state_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/lstm_float16.mod.py | 0 .../specs/skip/V1_2/lstm_state2_float16.mod.py | 0 .../specs/skip/V1_2/lstm_state_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/max_pool_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/maximum.mod.py | 64 - tests/nnapi/specs/skip/V1_2/mean_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/minimum.mod.py | 64 - tests/nnapi/specs/skip/V1_2/mul_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/not_equal.mod.py | 99 - tests/nnapi/specs/skip/V1_2/pad_all_dims.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_low_rank.mod.py | 0 .../specs/skip/V1_2/pad_low_rank_quant8.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_quant8.mod.py | 0 .../specs/skip/V1_2/pad_quant8_nonzero.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_v2_1_float.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_v2_1_quant8.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_v2_all_dims.mod.py | 0 .../specs/skip/V1_2/pad_v2_all_dims_quant8.mod.py | 0 tests/nnapi/specs/skip/V1_2/pad_v2_low_rank.mod.py | 0 .../specs/skip/V1_2/pad_v2_low_rank_quant8.mod.py | 0 tests/nnapi/specs/skip/V1_2/pow.mod.py | 0 tests/nnapi/specs/skip/V1_2/prelu.mod.py | 61 - tests/nnapi/specs/skip/V1_2/quantize.mod.py | 0 tests/nnapi/specs/skip/V1_2/quantized_lstm.mod.py | 0 .../specs/skip/V1_2/random_multinomial.mod.py | 0 .../skip/V1_2/random_multinomial_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/reduce_all.mod.py | 0 tests/nnapi/specs/skip/V1_2/reduce_any.mod.py | 0 tests/nnapi/specs/skip/V1_2/reduce_min.mod.py | 70 - tests/nnapi/specs/skip/V1_2/reduce_prod.mod.py | 0 tests/nnapi/specs/skip/V1_2/reduce_sum.mod.py | 66 - tests/nnapi/specs/skip/V1_2/relu1_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/relu6_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/relu_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/reshape_float16.mod.py | 0 .../specs/skip/V1_2/resize_bilinear_v1_2.mod.py | 0 .../specs/skip/V1_2/resize_nearest_neighbor.mod.py | 0 tests/nnapi/specs/skip/V1_2/rnn_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/roi_align.mod.py | 0 tests/nnapi/specs/skip/V1_2/roi_pooling.mod.py | 0 tests/nnapi/specs/skip/V1_2/select_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/sin.mod.py | 0 tests/nnapi/specs/skip/V1_2/slice.mod.py | 147 - tests/nnapi/specs/skip/V1_2/softmax_v1_2.mod.py | 0 .../skip/V1_2/space_to_batch_quant8_nonzero.mod.py | 0 .../specs/skip/V1_2/space_to_batch_v1_2.mod.py | 0 .../specs/skip/V1_2/space_to_depth_v1_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/split_float_1.mod.py | 38 - tests/nnapi/specs/skip/V1_2/split_float_2.mod.py | 37 - tests/nnapi/specs/skip/V1_2/split_float_3.mod.py | 39 - tests/nnapi/specs/skip/V1_2/split_float_4.mod.py | 36 - tests/nnapi/specs/skip/V1_2/split_float_5.mod.py | 36 - tests/nnapi/specs/skip/V1_2/split_int32_1.mod.py | 38 - tests/nnapi/specs/skip/V1_2/split_int32_2.mod.py | 37 - tests/nnapi/specs/skip/V1_2/split_int32_3.mod.py | 39 - tests/nnapi/specs/skip/V1_2/split_int32_4.mod.py | 36 - tests/nnapi/specs/skip/V1_2/split_quant8_1.mod.py | 38 - tests/nnapi/specs/skip/V1_2/split_quant8_2.mod.py | 37 - tests/nnapi/specs/skip/V1_2/split_quant8_3.mod.py | 39 - tests/nnapi/specs/skip/V1_2/split_quant8_4.mod.py | 36 - tests/nnapi/specs/skip/V1_2/squeeze_float16.mod.py | 0 .../specs/skip/V1_2/strided_slice_float16.mod.py | 0 .../V1_2/sub_quantized_different_scales.mod.py | 0 tests/nnapi/specs/skip/V1_2/sub_v1_2.mod.py | 99 - .../specs/skip/V1_2/sub_v1_2_broadcast.mod.py | 60 - .../skip/V1_2/svdf_bias_present_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/svdf_float16.mod.py | 0 .../specs/skip/V1_2/svdf_state_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/tanh_v1_2.mod.py | 89 - tests/nnapi/specs/skip/V1_2/tile_1.mod.py | 0 tests/nnapi/specs/skip/V1_2/tile_2.mod.py | 0 tests/nnapi/specs/skip/V1_2/tile_3.mod.py | 0 .../nnapi/specs/skip/V1_2/transpose_conv2d.mod.py | 0 .../specs/skip/V1_2/transpose_conv2d_large.mod.py | 0 .../nnapi/specs/skip/V1_2/transpose_float16.mod.py | 0 tests/nnapi/specs/skip/V1_2/transpose_v1_2.mod.py | 81 - .../V1_2/unidirectional_sequence_lstm_1step.mod.py | 0 ...stm_batch_major_norm_peephole_projection.mod.py | 0 ...stm_batch_major_peephole_projection_bias.mod.py | 0 ...idirectional_sequence_lstm_cifg_peephole.mod.py | 0 ...irectional_sequence_lstm_f16_batch_major.mod.py | 0 ...quence_lstm_f16_norm_peephole_projection.mod.py | 0 ...l_sequence_lstm_layer_norm_cifg_peephole.mod.py | 0 ...l_sequence_lstm_norm_peephole_projection.mod.py | 0 .../skip/V1_2/unidirectional_sequence_rnn.mod.py | 0 tests/nnapi/src/TestGenerated.cpp | 28 +- tests/nnapi/src/TestMain.cpp | 8 +- tests/nnapi/src/TestNeuralNetworksWrapper.h | 8 +- tests/nnapi/src/TestValidation.cpp | 6 +- tests/nnfw_api/CMakeLists.txt | 25 + tests/nnfw_api/src/FourOneOpModelSetInput.cc | 36 + tests/nnfw_api/src/create_session.cc | 30 + tests/nnfw_api/src/fixtures.h | 132 + tests/nnfw_api/src/load_model.cc | 47 + tests/nnfw_api/src/main.cc | 55 + tests/nnfw_api/src/model_path.cc | 58 + tests/nnfw_api/src/model_path.h | 62 + tests/nnfw_api/src/prepare.cc | 27 + tests/scripts/CMakeLists.txt | 19 + tests/scripts/README.md | 6 +- tests/scripts/benchmark_nnapi.sh | 28 +- tests/scripts/benchmark_nnpkg.sh | 6 +- tests/scripts/framework/run_test.sh | 277 + .../tests/MODELS/inception_module/config.sh | 1 + .../tests/MODELS/inception_nonslim/config.sh | 2 + .../tests/MODELS/inception_slim/config.sh | 2 + .../framework/tests/MODELS/mobilenet/config.sh | 2 + tests/scripts/framework/tests/abs/config.sh | 1 + tests/scripts/framework/tests/add/1D/config.sh | 1 + tests/scripts/framework/tests/add/4D/config.sh | 1 + .../tests/average_pool_2d/aligned/config.sh | 1 + .../tests/average_pool_2d/avgpool1/config.sh | 1 + .../tests/average_pool_2d/avgpool2/config.sh | 1 + .../framework/tests/batch_to_space_nd2/config.sh | 1 + tests/scripts/framework/tests/cast/config.sh | 1 + tests/scripts/framework/tests/concat/2D/config.sh | 1 + .../framework/tests/concat/concat1/config.sh | 1 + .../framework/tests/concat/concat2/config.sh | 1 + .../framework/tests/conv_2d/convolution1/config.sh | 1 + .../framework/tests/conv_2d/convolution2/config.sh | 1 + .../tests/custom/squared_difference/config.sh | 1 + .../tests/depthwise_conv_2d/depthconv1/config.sh | 1 + .../tests/depthwise_conv_2d/depthconv2/config.sh | 1 + .../tests/depthwise_conv_2d_no_fuse/config.sh | 1 + .../framework/tests/div/broadcast/config.sh | 1 + .../framework/tests/embedding_lookup/config.sh | 1 + tests/scripts/framework/tests/equal/config.sh | 1 + tests/scripts/framework/tests/exp/config.sh | 1 + .../scripts/framework/tests/floor/floor1/config.sh | 1 + .../scripts/framework/tests/floor/floor2/config.sh | 1 + .../framework/tests/fullyconnected/fc1/config.sh | 1 + .../tests/fullyconnected/hybrid/config.sh | 1 + .../tests/fullyconnected/matmul2x2/config.sh | 6 + .../fullyconnected/weights_as_input/config.sh | 1 + tests/scripts/framework/tests/gather/config.sh | 1 + tests/scripts/framework/tests/greater/config.sh | 1 + .../framework/tests/greater_equal/config.sh | 1 + .../framework/tests/hashtable_lookup/config.sh | 1 + .../framework/tests/l2_normalization/config.sh | 1 + tests/scripts/framework/tests/l2_pool_2d/config.sh | 1 + tests/scripts/framework/tests/less/config.sh | 1 + tests/scripts/framework/tests/less_equal/config.sh | 1 + tests/scripts/framework/tests/logistic/config.sh | 1 + tests/scripts/framework/tests/max/config.sh | 1 + .../framework/tests/max_pool_2d/maxpool1/config.sh | 1 + .../framework/tests/max_pool_2d/maxpool2/config.sh | 1 + tests/scripts/framework/tests/mean/config.sh | 1 + tests/scripts/framework/tests/min/config.sh | 1 + .../framework/tests/mul/broadcast/config.sh | 1 + tests/scripts/framework/tests/neg/config.sh | 1 + tests/scripts/framework/tests/not_equal/config.sh | 1 + tests/scripts/framework/tests/one_hot/config.sh | 1 + tests/scripts/framework/tests/pack/config.sh | 1 + tests/scripts/framework/tests/pad/4D_2D/config.sh | 1 + tests/scripts/framework/tests/pad/pad1/config.sh | 1 + tests/scripts/framework/tests/pad/pad2/config.sh | 1 + tests/scripts/framework/tests/reduce_max/config.sh | 1 + .../framework/tests/reduce_mean/test1/config.sh | 1 + .../framework/tests/reduce_mean/test2/config.sh | 1 + tests/scripts/framework/tests/reduce_sum/config.sh | 1 + tests/scripts/framework/tests/relu/config.sh | 1 + tests/scripts/framework/tests/relu6/config.sh | 1 + tests/scripts/framework/tests/reshape/3D/config.sh | 1 + .../framework/tests/reshape/reshape1/config.sh | 1 + .../framework/tests/reshape/reshape2/config.sh | 1 + .../framework/tests/resize_bilinear/config.sh | 1 + tests/scripts/framework/tests/rnn/config.sh | 1 + tests/scripts/framework/tests/rsqrt/config.sh | 1 + tests/scripts/framework/tests/shape/config.sh | 1 + tests/scripts/framework/tests/sin/config.sh | 1 + tests/scripts/framework/tests/slice/config.sh | 1 + tests/scripts/framework/tests/softmax/config.sh | 1 + .../framework/tests/space_to_batch_nd2/config.sh | 1 + .../framework/tests/space_to_depth/config.sh | 1 + tests/scripts/framework/tests/sqrt/config.sh | 1 + tests/scripts/framework/tests/squeeze/config.sh | 1 + .../framework/tests/strided_slice/config.sh | 1 + .../framework/tests/sub/broadcast/config.sh | 1 + tests/scripts/framework/tests/tanh/config.sh | 1 + tests/scripts/framework/tests/topk_v2/config.sh | 1 + tests/scripts/framework/tests/transpose/config.sh | 1 + .../framework/tests/transpose_conv/same/config.sh | 1 + .../framework/tests/transpose_conv/valid/config.sh | 1 + tests/scripts/list/benchmark_nnpkg_model_list.txt | 8 +- .../list/frameworktest_list.aarch64.acl_cl.txt | 46 + .../list/frameworktest_list.aarch64.acl_neon.txt | 41 + .../list/frameworktest_list.aarch64.cpu.txt | 33 + .../list/frameworktest_list.armv7l.acl_cl.txt | 46 + .../list/frameworktest_list.armv7l.acl_neon.txt | 42 + .../scripts/list/frameworktest_list.armv7l.cpu.txt | 38 + .../list/frameworktest_list.noarch.interp.txt | 16 + .../scripts/list/frameworktest_list.x86_64.cpu.txt | 15 + .../neurun_frameworktest_list.armv7l.acl_cl.txt | 46 - .../neurun_frameworktest_list.armv7l.acl_neon.txt | 41 - .../list/neurun_frameworktest_list.armv7l.cpu.txt | 13 - .../list/neurun_frameworktest_list.armv7l.ncnn.txt | 2 - .../neurun_frameworktest_list.noarch.interp.txt | 16 - .../list/neurun_frameworktest_list.x86-64.cpu.txt | 12 - tests/scripts/list/tflite_loader_list.aarch64.txt | 35 + tests/scripts/list/tflite_loader_list.armv7l.txt | 44 +- tests/scripts/merge_result_of_benchmark_nnpkg.py | 32 +- .../oneapi_test/install_oneapi_test_nnpackages.sh | 142 + tests/scripts/oneapi_test/models/add/config.sh | 1 + tests/scripts/test-driver.sh | 20 +- tests/scripts/test_framework.sh | 4 +- tests/scripts/test_scheduler_with_profiling.sh | 6 +- tests/scripts/unittest.sh | 22 +- tests/tools/nnapi_test/src/nnapi_test.cc | 6 + tests/tools/nnpackage_run/CMakeLists.txt | 10 +- tests/tools/nnpackage_run/src/args.cc | 20 +- tests/tools/nnpackage_run/src/args.h | 2 + tests/tools/nnpackage_run/src/nnpackage_run.cc | 43 +- .../tools/tflite_benchmark/src/tflite_benchmark.cc | 44 +- tests/tools/tflite_loader/CMakeLists.txt | 8 +- tests/tools/tflite_loader/src/tflite_loader.cc | 22 +- tests/tools/tflite_run/CMakeLists.txt | 2 +- tests/tools/tflite_run/src/args.cc | 7 +- tests/tools/tflite_run/src/tensor_loader.cc | 16 + tests/tools/tflite_run/src/tensor_loader.h | 16 + tests/tools/tflite_run/src/tflite_run.cc | 32 +- tools/cross/build_android_ndk.sh | 2 +- tools/cross/install_android_sdk.sh | 167 + tools/kernel_report/kernel_report.py | 171 + .../nnpkg_test/list.armv7l-linux.acl_cl | 2 +- .../nnpkg_test/list.armv7l-linux.acl_neon | 4 +- .../nnpkg_test/list.armv7l-linux.cpu | 16 +- tools/nnpackage_tool/nnpkg_test/list.noarch.interp | 6 +- tools/nnpackage_tool/sth2nnpkgtc/tflite2nnpkgtc.md | 2 +- tools/nnpackage_tool/tf2tfliteV2/README.md | 47 + tools/nnpackage_tool/tf2tfliteV2/tf2tfliteV2.py | 173 + .../tflite2circle/tflitejson2circlejson.py | 1 + tools/pbfile_tool/convert_ckpt_to_pb.py | 0 tools/pbfile_tool/convert_pb_to_pbtxt.py | 0 tools/pbfile_tool/extract_subgraph.py | 0 tools/tensorflow_model_freezer/__init__.py | 0 tools/tensorflow_model_freezer/base_freezer.py | 0 .../tensorflow_model_freezer/model_freezer_util.py | 0 .../sample/Operation_gen.py | 0 .../tensorflow_model_freezer/sample/UNSTACK_gen.py | 0 tools/tensorflow_model_freezer/sample/__init__.py | 0 tools/tflite_accuracy/README.md | 2 +- tools/tflitefile_tool/config_saver.py | 5 +- tools/tflitefile_tool/graph_stats.py | 84 + tools/tflitefile_tool/model_parser.py | 31 +- tools/tflitefile_tool/model_printer.py | 146 - tools/tflitefile_tool/model_saver.py | 0 tools/tflitefile_tool/operator_parser.py | 6 +- tools/tflitefile_tool/operator_printer.py | 15 +- tools/tflitefile_tool/option_printer.py | 0 tools/tflitefile_tool/perf_predictor.py | 28 - tools/tflitefile_tool/select_operator.py | 27 +- tools/tflitefile_tool/subgraph_printer.py | 92 + tools/tflitefile_tool/tensor_printer.py | 0 tools/tflitefile_tool/tensor_wrapping.py | 8 +- tools/tflitefile_tool/tflite/AbsOptions.py | 0 .../tflite/ActivationFunctionType.py | 0 tools/tflitefile_tool/tflite/AddNOptions.py | 28 + tools/tflitefile_tool/tflite/AddOptions.py | 0 tools/tflitefile_tool/tflite/ArgMaxOptions.py | 0 tools/tflitefile_tool/tflite/ArgMinOptions.py | 0 .../tflite/BatchToSpaceNDOptions.py | 0 .../tflite/BidirectionalSequenceLSTMOptions.py | 14 +- .../tflite/BidirectionalSequenceRNNOptions.py | 0 tools/tflitefile_tool/tflite/Buffer.py | 0 tools/tflitefile_tool/tflite/BuiltinOperator.py | 21 + tools/tflitefile_tool/tflite/BuiltinOptions.py | 18 + tools/tflitefile_tool/tflite/CallOptions.py | 0 tools/tflitefile_tool/tflite/CastOptions.py | 0 tools/tflitefile_tool/tflite/CombinerType.py | 0 .../tflite/ConcatEmbeddingsOptions.py | 0 .../tflitefile_tool/tflite/ConcatenationOptions.py | 0 tools/tflitefile_tool/tflite/Conv2DOptions.py | 0 tools/tflitefile_tool/tflite/CosOptions.py | 28 + .../tflitefile_tool/tflite/CustomOptionsFormat.py | 0 tools/tflitefile_tool/tflite/CustomQuantization.py | 0 .../tflitefile_tool/tflite/DepthToSpaceOptions.py | 39 + .../tflite/DepthwiseConv2DOptions.py | 0 tools/tflitefile_tool/tflite/DequantizeOptions.py | 0 tools/tflitefile_tool/tflite/DivOptions.py | 0 .../tflite/EmbeddingLookupSparseOptions.py | 0 tools/tflitefile_tool/tflite/EqualOptions.py | 0 tools/tflitefile_tool/tflite/ExpOptions.py | 0 tools/tflitefile_tool/tflite/ExpandDimsOptions.py | 0 tools/tflitefile_tool/tflite/FakeQuantOptions.py | 8 +- tools/tflitefile_tool/tflite/FillOptions.py | 0 tools/tflitefile_tool/tflite/FloorDivOptions.py | 0 tools/tflitefile_tool/tflite/FloorModOptions.py | 0 .../tflite/FullyConnectedOptions.py | 14 +- .../tflite/FullyConnectedOptionsWeightsFormat.py | 0 tools/tflitefile_tool/tflite/GatherNdOptions.py | 28 + tools/tflitefile_tool/tflite/GatherOptions.py | 0 .../tflitefile_tool/tflite/GreaterEqualOptions.py | 0 tools/tflitefile_tool/tflite/GreaterOptions.py | 0 tools/tflitefile_tool/tflite/HardSwishOptions.py | 28 + tools/tflitefile_tool/tflite/IfOptions.py | 50 + tools/tflitefile_tool/tflite/L2NormOptions.py | 0 .../tflitefile_tool/tflite/LSHProjectionOptions.py | 4 +- tools/tflitefile_tool/tflite/LSHProjectionType.py | 0 tools/tflitefile_tool/tflite/LSTMKernelType.py | 0 tools/tflitefile_tool/tflite/LSTMOptions.py | 0 tools/tflitefile_tool/tflite/LeakyReluOptions.py | 0 tools/tflitefile_tool/tflite/LessEqualOptions.py | 0 tools/tflitefile_tool/tflite/LessOptions.py | 0 .../tflite/LocalResponseNormalizationOptions.py | 0 tools/tflitefile_tool/tflite/LogSoftmaxOptions.py | 0 tools/tflitefile_tool/tflite/LogicalAndOptions.py | 0 tools/tflitefile_tool/tflite/LogicalNotOptions.py | 0 tools/tflitefile_tool/tflite/LogicalOrOptions.py | 0 tools/tflitefile_tool/tflite/MatrixDiagOptions.py | 28 + .../tflitefile_tool/tflite/MatrixSetDiagOptions.py | 28 + .../tflite/MaximumMinimumOptions.py | 0 tools/tflitefile_tool/tflite/MeanOptions.py | 39 - tools/tflitefile_tool/tflite/Metadata.py | 51 + tools/tflitefile_tool/tflite/MirrorPadMode.py | 0 tools/tflitefile_tool/tflite/MirrorPadOptions.py | 0 tools/tflitefile_tool/tflite/Model.py | 31 +- tools/tflitefile_tool/tflite/MulOptions.py | 0 tools/tflitefile_tool/tflite/NegOptions.py | 0 .../tflite/NonMaxSuppressionV4Options.py | 28 + .../tflite/NonMaxSuppressionV5Options.py | 28 + tools/tflitefile_tool/tflite/NotEqualOptions.py | 0 tools/tflitefile_tool/tflite/OneHotOptions.py | 0 tools/tflitefile_tool/tflite/Operator.py | 35 +- tools/tflitefile_tool/tflite/OperatorCode.py | 0 tools/tflitefile_tool/tflite/PackOptions.py | 0 tools/tflitefile_tool/tflite/PadOptions.py | 0 tools/tflitefile_tool/tflite/PadV2Options.py | 0 tools/tflitefile_tool/tflite/Padding.py | 0 tools/tflitefile_tool/tflite/Pool2DOptions.py | 0 tools/tflitefile_tool/tflite/PowOptions.py | 0 .../tflitefile_tool/tflite/QuantizationDetails.py | 0 .../tflite/QuantizationParameters.py | 21 +- tools/tflitefile_tool/tflite/QuantizeOptions.py | 28 + tools/tflitefile_tool/tflite/RNNOptions.py | 0 tools/tflitefile_tool/tflite/RangeOptions.py | 0 tools/tflitefile_tool/tflite/RankOptions.py | 28 + tools/tflitefile_tool/tflite/ReducerOptions.py | 0 tools/tflitefile_tool/tflite/ReshapeOptions.py | 0 .../tflite/ResizeBilinearOptions.py | 0 .../tflite/ResizeNearestNeighborOptions.py | 0 .../tflite/ReverseSequenceOptions.py | 50 + tools/tflitefile_tool/tflite/ReverseV2Options.py | 28 + tools/tflitefile_tool/tflite/SVDFOptions.py | 0 tools/tflitefile_tool/tflite/ScatterNdOptions.py | 28 + tools/tflitefile_tool/tflite/SelectOptions.py | 0 tools/tflitefile_tool/tflite/SequenceRNNOptions.py | 0 tools/tflitefile_tool/tflite/ShapeOptions.py | 0 tools/tflitefile_tool/tflite/SkipGramOptions.py | 0 tools/tflitefile_tool/tflite/SliceOptions.py | 0 tools/tflitefile_tool/tflite/SoftmaxOptions.py | 0 .../tflite/SpaceToBatchNDOptions.py | 0 .../tflitefile_tool/tflite/SpaceToDepthOptions.py | 0 .../tflitefile_tool/tflite/SparseToDenseOptions.py | 0 tools/tflitefile_tool/tflite/SplitOptions.py | 0 tools/tflitefile_tool/tflite/SplitVOptions.py | 0 tools/tflitefile_tool/tflite/SquareOptions.py | 0 .../tflite/SquaredDifferenceOptions.py | 0 tools/tflitefile_tool/tflite/SqueezeOptions.py | 0 .../tflitefile_tool/tflite/StridedSliceOptions.py | 0 tools/tflitefile_tool/tflite/SubGraph.py | 0 tools/tflitefile_tool/tflite/SubOptions.py | 0 tools/tflitefile_tool/tflite/Tensor.py | 4 +- tools/tflitefile_tool/tflite/TensorType.py | 0 tools/tflitefile_tool/tflite/TileOptions.py | 0 tools/tflitefile_tool/tflite/TopKV2Options.py | 0 .../tflitefile_tool/tflite/TransposeConvOptions.py | 0 tools/tflitefile_tool/tflite/TransposeOptions.py | 0 .../tflite/UnidirectionalSequenceLSTMOptions.py | 0 tools/tflitefile_tool/tflite/UniqueOptions.py | 39 + tools/tflitefile_tool/tflite/UnpackOptions.py | 0 tools/tflitefile_tool/tflite/WhereOptions.py | 28 + tools/tflitefile_tool/tflite/WhileOptions.py | 50 + tools/tflitefile_tool/tflite/ZerosLikeOptions.py | 0 tools/tflitefile_tool/tflite/__init__.py | 0 tools/tflkit/README.md | 2 +- tools/tflkit/summarize_pb.py | 0 6010 files changed, 371936 insertions(+), 108668 deletions(-) create mode 100644 compiler/CMakeLists.txt create mode 100644 compiler/adtidas/CMakeLists.txt create mode 100644 compiler/adtidas/include/adtidas/SmallVector.h create mode 100644 compiler/angkor/CMakeLists.txt create mode 100644 compiler/angkor/README.md create mode 100644 compiler/angkor/include/angkor/TensorIndex.h create mode 100644 compiler/angkor/include/angkor/TensorShape.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Accessor.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Buffer.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/CHWLayout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/HWCLayout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Layout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Overlay.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Reader.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/Shape.h create mode 100644 compiler/angkor/include/nncc/core/ADT/feature/View.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Accessor.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Buffer.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/IndexEnumerator.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Layout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/NCHWLayout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/NHWCLayout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Reader.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/Shape.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/View.h create mode 100644 compiler/angkor/include/nncc/core/ADT/kernel/ViewImpl.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Accessor.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Buffer.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Index.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/IndexEnumerator.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Layout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/LexicalLayout.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Reader.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/Shape.h create mode 100644 compiler/angkor/include/nncc/core/ADT/tensor/View.h create mode 100644 compiler/angkor/src/ADT/feature/Accessor.cpp create mode 100644 compiler/angkor/src/ADT/feature/Buffer.test.cpp create mode 100644 compiler/angkor/src/ADT/feature/CHWLayout.cpp create mode 100644 compiler/angkor/src/ADT/feature/CHWLayout.test.cpp create mode 100644 compiler/angkor/src/ADT/feature/HWCLayout.cpp create mode 100644 compiler/angkor/src/ADT/feature/HWCLayout.test.cpp create mode 100644 compiler/angkor/src/ADT/feature/Layout.cpp create mode 100644 compiler/angkor/src/ADT/feature/Layout.test.cpp create mode 100644 compiler/angkor/src/ADT/feature/Overlay.test.cpp create mode 100644 compiler/angkor/src/ADT/feature/Reader.cpp create mode 100644 compiler/angkor/src/ADT/feature/Shape.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Buffer.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/IndexEnumerator.cpp create mode 100644 compiler/angkor/src/ADT/kernel/IndexEnumerator.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Layout.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Layout.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/NCHWLayout.cpp create mode 100644 compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/NHWCLayout.cpp create mode 100644 compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Overlay.test.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Reader.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Shape.cpp create mode 100644 compiler/angkor/src/ADT/kernel/Shape.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Buffer.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Index.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Index.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/IndexEnumerator.cpp create mode 100644 compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Layout.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Layout.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/LexicalLayout.cpp create mode 100644 compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Overlay.test.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Reader.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Shape.cpp create mode 100644 compiler/angkor/src/ADT/tensor/Shape.test.cpp create mode 100644 compiler/angkor/src/TensorIndex.test.cpp create mode 100644 compiler/angkor/src/TensorShape.test.cpp create mode 100644 compiler/ann-api/CMakeLists.txt create mode 100644 compiler/ann-api/include/.FORMATDENY create mode 100644 compiler/ann-api/include/NeuralNetworks.h create mode 100644 compiler/ann-ref/.FORMATDENY create mode 100644 compiler/ann-ref/CMakeLists.txt create mode 100644 compiler/ann-ref/README.md create mode 100644 compiler/ann-ref/requires.cmake create mode 100644 compiler/ann-ref/src/Assert.h create mode 100644 compiler/ann-ref/src/CompilationBuilder.cpp create mode 100644 compiler/ann-ref/src/CompilationBuilder.h create mode 100644 compiler/ann-ref/src/ExecutionBuilder.cpp create mode 100644 compiler/ann-ref/src/ExecutionBuilder.h create mode 100644 compiler/ann-ref/src/Executor.cpp create mode 100644 compiler/ann-ref/src/Executor.h create mode 100644 compiler/ann-ref/src/Logging.cpp create mode 100644 compiler/ann-ref/src/Logging.h create mode 100644 compiler/ann-ref/src/Macro.h create mode 100644 compiler/ann-ref/src/Memory.cpp create mode 100644 compiler/ann-ref/src/Memory.h create mode 100644 compiler/ann-ref/src/MemoryTracker.cpp create mode 100644 compiler/ann-ref/src/MemoryTracker.h create mode 100644 compiler/ann-ref/src/Model.h create mode 100644 compiler/ann-ref/src/ModelArgumentInfo.cpp create mode 100644 compiler/ann-ref/src/ModelArgumentInfo.h create mode 100644 compiler/ann-ref/src/ModelBuilder.cpp create mode 100644 compiler/ann-ref/src/ModelBuilder.h create mode 100644 compiler/ann-ref/src/NeuralNetworks.cpp create mode 100644 compiler/ann-ref/src/Operand.h create mode 100644 compiler/ann-ref/src/OperandType.cpp create mode 100644 compiler/ann-ref/src/OperandType.h create mode 100644 compiler/ann-ref/src/OperandType.probe.cpp create mode 100644 compiler/ann-ref/src/Operation.h create mode 100644 compiler/ann-ref/src/OperationType.cpp create mode 100644 compiler/ann-ref/src/OperationType.h create mode 100644 compiler/ann-ref/src/OperationType.probe.cpp create mode 100644 compiler/ann-ref/src/Probe.cpp create mode 100644 compiler/ann-ref/src/Request.h create mode 100644 compiler/ann-ref/src/Shape.cpp create mode 100644 compiler/ann-ref/src/Shape.h create mode 100644 compiler/ann-ref/src/Validation.cpp create mode 100644 compiler/ann-ref/src/Validation.h create mode 100644 compiler/ann-ref/src/ops/Add.cpp create mode 100644 compiler/ann-ref/src/ops/Add.float.cpp create mode 100644 compiler/ann-ref/src/ops/Add.float.h create mode 100644 compiler/ann-ref/src/ops/Add.h create mode 100644 compiler/ann-ref/src/ops/AvgPool2D.cpp create mode 100644 compiler/ann-ref/src/ops/AvgPool2D.float.cpp create mode 100644 compiler/ann-ref/src/ops/AvgPool2D.float.h create mode 100644 compiler/ann-ref/src/ops/AvgPool2D.h create mode 100644 compiler/ann-ref/src/ops/Concatenation.cpp create mode 100644 compiler/ann-ref/src/ops/Concatenation.float.cpp create mode 100644 compiler/ann-ref/src/ops/Concatenation.float.h create mode 100644 compiler/ann-ref/src/ops/Concatenation.h create mode 100644 compiler/ann-ref/src/ops/Conv2D.cpp create mode 100644 compiler/ann-ref/src/ops/Conv2D.float.cpp create mode 100644 compiler/ann-ref/src/ops/Conv2D.float.h create mode 100644 compiler/ann-ref/src/ops/Conv2D.h create mode 100644 compiler/ann-ref/src/ops/DepthwiseConv2D.cpp create mode 100644 compiler/ann-ref/src/ops/DepthwiseConv2D.float.cpp create mode 100644 compiler/ann-ref/src/ops/DepthwiseConv2D.float.h create mode 100644 compiler/ann-ref/src/ops/DepthwiseConv2D.h create mode 100644 compiler/ann-ref/src/ops/Div.cpp create mode 100644 compiler/ann-ref/src/ops/Div.float.cpp create mode 100644 compiler/ann-ref/src/ops/Div.float.h create mode 100644 compiler/ann-ref/src/ops/Div.h create mode 100644 compiler/ann-ref/src/ops/FullyConnected.cpp create mode 100644 compiler/ann-ref/src/ops/FullyConnected.float.cpp create mode 100644 compiler/ann-ref/src/ops/FullyConnected.float.h create mode 100644 compiler/ann-ref/src/ops/FullyConnected.h create mode 100644 compiler/ann-ref/src/ops/MaxPool2D.cpp create mode 100644 compiler/ann-ref/src/ops/MaxPool2D.float.cpp create mode 100644 compiler/ann-ref/src/ops/MaxPool2D.float.h create mode 100644 compiler/ann-ref/src/ops/MaxPool2D.h create mode 100644 compiler/ann-ref/src/ops/Mul.cpp create mode 100644 compiler/ann-ref/src/ops/Mul.float.cpp create mode 100644 compiler/ann-ref/src/ops/Mul.float.h create mode 100644 compiler/ann-ref/src/ops/Mul.h create mode 100644 compiler/ann-ref/src/ops/Pad.cpp create mode 100644 compiler/ann-ref/src/ops/Pad.h create mode 100644 compiler/ann-ref/src/ops/ReLU.cpp create mode 100644 compiler/ann-ref/src/ops/ReLU.float.cpp create mode 100644 compiler/ann-ref/src/ops/ReLU.float.h create mode 100644 compiler/ann-ref/src/ops/ReLU.h create mode 100644 compiler/ann-ref/src/ops/ReLU6.cpp create mode 100644 compiler/ann-ref/src/ops/ReLU6.float.cpp create mode 100644 compiler/ann-ref/src/ops/ReLU6.float.h create mode 100644 compiler/ann-ref/src/ops/ReLU6.h create mode 100644 compiler/ann-ref/src/ops/Reshape.cpp create mode 100644 compiler/ann-ref/src/ops/Reshape.h create mode 100644 compiler/ann-ref/src/ops/Softmax.cpp create mode 100644 compiler/ann-ref/src/ops/Softmax.float.cpp create mode 100644 compiler/ann-ref/src/ops/Softmax.float.h create mode 100644 compiler/ann-ref/src/ops/Softmax.h create mode 100644 compiler/ann-ref/src/ops/Sub.cpp create mode 100644 compiler/ann-ref/src/ops/Sub.float.cpp create mode 100644 compiler/ann-ref/src/ops/Sub.float.h create mode 100644 compiler/ann-ref/src/ops/Sub.h create mode 100644 compiler/ann-ref/src/ops/internal/ActivationUtils.h create mode 100644 compiler/ann-ref/src/ops/internal/Array.h create mode 100644 compiler/ann-ref/src/ops/internal/Dims.h create mode 100644 compiler/ann-ref/src/ops/internal/Elementwise.cpp create mode 100644 compiler/ann-ref/src/ops/internal/Elementwise.h create mode 100644 compiler/ann-ref/src/ops/internal/FeatureMap.h create mode 100644 compiler/ann-ref/src/ops/internal/Fused.cpp create mode 100644 compiler/ann-ref/src/ops/internal/Fused.h create mode 100644 compiler/ann-ref/src/ops/internal/GEMM.h create mode 100644 compiler/ann-ref/src/ops/internal/Macro.h create mode 100644 compiler/ann-ref/src/ops/internal/Matrix.h create mode 100644 compiler/ann-ref/src/ops/internal/NDArray.h create mode 100644 compiler/ann-ref/src/ops/internal/Pooling.cpp create mode 100644 compiler/ann-ref/src/ops/internal/Pooling.h create mode 100644 compiler/ann-ref/src/ops/internal/Spatial.h create mode 100644 compiler/bino/CMakeLists.txt create mode 100644 compiler/bino/README.md create mode 100644 compiler/bino/include/bino.h create mode 100644 compiler/bino/tests/Functional.tests.cpp create mode 100644 compiler/caffe2circle/CMakeLists.txt create mode 100644 compiler/caffe2circle/README.md create mode 100644 compiler/caffe2circle/requires.cmake create mode 100644 compiler/caffe2circle/src/caffe2circle.cpp create mode 100644 compiler/caffegen/CMakeLists.txt create mode 100644 compiler/caffegen/README.md create mode 100644 compiler/caffegen/src/DecodeCommand.cpp create mode 100644 compiler/caffegen/src/DecodeCommand.h create mode 100644 compiler/caffegen/src/Driver.cpp create mode 100644 compiler/caffegen/src/EncodeCommand.cpp create mode 100644 compiler/caffegen/src/EncodeCommand.h create mode 100644 compiler/caffegen/src/InitCommand.cpp create mode 100644 compiler/caffegen/src/InitCommand.h create mode 100644 compiler/caffegen/src/MergeCommand.cpp create mode 100644 compiler/caffegen/src/MergeCommand.h create mode 100644 compiler/circle-inspect/CMakeLists.txt create mode 100644 compiler/circle-inspect/README.md create mode 100644 compiler/circle-inspect/driver/Driver.cpp create mode 100644 compiler/circle-inspect/requires.cmake create mode 100644 compiler/circle-inspect/src/Dump.cpp create mode 100644 compiler/circle-inspect/src/Dump.h create mode 100644 compiler/circle-inspect/src/Model.cpp create mode 100644 compiler/circle-inspect/src/Model.h create mode 100644 compiler/circle-inspect/src/Reader.cpp create mode 100644 compiler/circle-inspect/src/Reader.h create mode 100644 compiler/circle-verify/CMakeLists.txt create mode 100644 compiler/circle-verify/README.md create mode 100644 compiler/circle-verify/requires.cmake create mode 100644 compiler/circle-verify/src/Driver.cpp create mode 100644 compiler/circle-verify/src/Model.cpp create mode 100644 compiler/circle-verify/src/Model.h create mode 100644 compiler/circle-verify/src/VerifyFlatBuffers.cpp create mode 100644 compiler/circle-verify/src/VerifyFlatBuffers.h create mode 100644 compiler/circle2circle/CMakeLists.txt create mode 100644 compiler/circle2circle/README.md create mode 100644 compiler/circle2circle/include/CircleExpContract.h create mode 100644 compiler/circle2circle/include/Model.h create mode 100644 compiler/circle2circle/requires.cmake create mode 100644 compiler/circle2circle/src/Circle2Circle.cpp create mode 100644 compiler/circle2circle/src/Circle2Circle.test.cpp create mode 100644 compiler/circle2circle/src/CircleExpContract.cpp create mode 100644 compiler/circle2circle/src/Model.cpp create mode 100644 compiler/circle2circle/src/TestHelper.h create mode 100644 compiler/circledump/CMakeLists.txt create mode 100644 compiler/circledump/README.md create mode 100644 compiler/circledump/driver/Driver.cpp create mode 100644 compiler/circledump/include/circledump/Dump.h create mode 100644 compiler/circledump/include/circleread/Model.h create mode 100644 compiler/circledump/requires.cmake create mode 100644 compiler/circledump/src/Dump.cpp create mode 100644 compiler/circledump/src/Load.cpp create mode 100644 compiler/circledump/src/OpPrinter.cpp create mode 100644 compiler/circledump/src/OpPrinter.h create mode 100644 compiler/circledump/src/Read.cpp create mode 100644 compiler/circledump/src/Read.h create mode 100644 compiler/cli/CMakeLists.txt create mode 100644 compiler/cli/README.md create mode 100644 compiler/cli/include/cli/App.h create mode 100644 compiler/cli/include/cli/Command.h create mode 100644 compiler/cli/include/cli/FunctionCommand.h create mode 100644 compiler/cli/src/App.cpp create mode 100644 compiler/cli/src/App.test.cpp create mode 100644 compiler/coco/CMakeLists.txt create mode 100644 compiler/coco/README.md create mode 100644 compiler/coco/core/CMakeLists.txt create mode 100644 compiler/coco/core/include/coco/ADT/DLinkedList.h create mode 100644 compiler/coco/core/include/coco/ADT/PtrList.h create mode 100644 compiler/coco/core/include/coco/ADT/PtrManager.h create mode 100644 compiler/coco/core/include/coco/IR.h create mode 100644 compiler/coco/core/include/coco/IR/Arg.h create mode 100644 compiler/coco/core/include/coco/IR/Bag.h create mode 100644 compiler/coco/core/include/coco/IR/BagManager.h create mode 100644 compiler/coco/core/include/coco/IR/Block.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Block.h create mode 100644 compiler/coco/core/include/coco/IR/BlockIndex.h create mode 100644 compiler/coco/core/include/coco/IR/BlockManager.h create mode 100644 compiler/coco/core/include/coco/IR/Def.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Def.h create mode 100644 compiler/coco/core/include/coco/IR/Dep.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Dep.h create mode 100644 compiler/coco/core/include/coco/IR/DepSet.h create mode 100644 compiler/coco/core/include/coco/IR/ElemID.h create mode 100644 compiler/coco/core/include/coco/IR/Entity.h create mode 100644 compiler/coco/core/include/coco/IR/EntityBuilder.h create mode 100644 compiler/coco/core/include/coco/IR/EntityManager.h create mode 100644 compiler/coco/core/include/coco/IR/FeatureLayout.h create mode 100644 compiler/coco/core/include/coco/IR/FeatureLayouts.h create mode 100644 compiler/coco/core/include/coco/IR/FeatureObject.forward.h create mode 100644 compiler/coco/core/include/coco/IR/FeatureObject.h create mode 100644 compiler/coco/core/include/coco/IR/FeatureShape.h create mode 100644 compiler/coco/core/include/coco/IR/Input.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Input.h create mode 100644 compiler/coco/core/include/coco/IR/InputList.h create mode 100644 compiler/coco/core/include/coco/IR/InputManager.h create mode 100644 compiler/coco/core/include/coco/IR/Instr.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Instr.h create mode 100644 compiler/coco/core/include/coco/IR/Instr.lst create mode 100644 compiler/coco/core/include/coco/IR/InstrIndex.h create mode 100644 compiler/coco/core/include/coco/IR/InstrManager.h create mode 100644 compiler/coco/core/include/coco/IR/Instrs.h create mode 100644 compiler/coco/core/include/coco/IR/KernelLayout.h create mode 100644 compiler/coco/core/include/coco/IR/KernelLayouts.h create mode 100644 compiler/coco/core/include/coco/IR/KernelObject.forward.h create mode 100644 compiler/coco/core/include/coco/IR/KernelObject.h create mode 100644 compiler/coco/core/include/coco/IR/Locatable.h create mode 100644 compiler/coco/core/include/coco/IR/Module.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Module.h create mode 100644 compiler/coco/core/include/coco/IR/Object.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Object.h create mode 100644 compiler/coco/core/include/coco/IR/ObjectManager.h create mode 100644 compiler/coco/core/include/coco/IR/ObjectSet.h create mode 100644 compiler/coco/core/include/coco/IR/Op.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Op.h create mode 100644 compiler/coco/core/include/coco/IR/Op.lst create mode 100644 compiler/coco/core/include/coco/IR/OpManager.h create mode 100644 compiler/coco/core/include/coco/IR/Ops.h create mode 100644 compiler/coco/core/include/coco/IR/Output.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Output.h create mode 100644 compiler/coco/core/include/coco/IR/OutputList.h create mode 100644 compiler/coco/core/include/coco/IR/OutputManager.h create mode 100644 compiler/coco/core/include/coco/IR/Padding2D.h create mode 100644 compiler/coco/core/include/coco/IR/Part.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Part.h create mode 100644 compiler/coco/core/include/coco/IR/Read.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Read.h create mode 100644 compiler/coco/core/include/coco/IR/ReadSet.h create mode 100644 compiler/coco/core/include/coco/IR/Step.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Step.h create mode 100644 compiler/coco/core/include/coco/IR/Stride2D.h create mode 100644 compiler/coco/core/include/coco/IR/Update.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Update.h create mode 100644 compiler/coco/core/include/coco/IR/UpdateSet.h create mode 100644 compiler/coco/core/include/coco/IR/Use.forward.h create mode 100644 compiler/coco/core/include/coco/IR/Use.h create mode 100644 compiler/coco/core/include/coco/IR/UseSet.h create mode 100644 compiler/coco/core/include/coco/IR/Window2D.h create mode 100644 compiler/coco/core/src/ADT/DLinkedList.test.cpp create mode 100644 compiler/coco/core/src/ADT/PtrList.cpp create mode 100644 compiler/coco/core/src/ADT/PtrList.test.cpp create mode 100644 compiler/coco/core/src/ADT/PtrManager.test.cpp create mode 100644 compiler/coco/core/src/IR.test.cpp create mode 100644 compiler/coco/core/src/IR/Arg.cpp create mode 100644 compiler/coco/core/src/IR/Arg.test.cpp create mode 100644 compiler/coco/core/src/IR/AvgPool2D.test.cpp create mode 100644 compiler/coco/core/src/IR/Bag.cpp create mode 100644 compiler/coco/core/src/IR/Bag.test.cpp create mode 100644 compiler/coco/core/src/IR/BagManager.cpp create mode 100644 compiler/coco/core/src/IR/BagManager.test.cpp create mode 100644 compiler/coco/core/src/IR/Block.cpp create mode 100644 compiler/coco/core/src/IR/Block.test.cpp create mode 100644 compiler/coco/core/src/IR/BlockIndex.cpp create mode 100644 compiler/coco/core/src/IR/BlockIndex.test.cpp create mode 100644 compiler/coco/core/src/IR/BlockManager.cpp create mode 100644 compiler/coco/core/src/IR/BlockManager.test.cpp create mode 100644 compiler/coco/core/src/IR/Consumer.mock.h create mode 100644 compiler/coco/core/src/IR/Conv2D.cpp create mode 100644 compiler/coco/core/src/IR/Conv2D.test.cpp create mode 100644 compiler/coco/core/src/IR/Def.cpp create mode 100644 compiler/coco/core/src/IR/Def.test.cpp create mode 100644 compiler/coco/core/src/IR/Dep.cpp create mode 100644 compiler/coco/core/src/IR/Dep.test.cpp create mode 100644 compiler/coco/core/src/IR/ElemID.cpp create mode 100644 compiler/coco/core/src/IR/ElemID.test.cpp create mode 100644 compiler/coco/core/src/IR/EntityManager.cpp create mode 100644 compiler/coco/core/src/IR/Eval.cpp create mode 100644 compiler/coco/core/src/IR/Eval.test.cpp create mode 100644 compiler/coco/core/src/IR/FeatureLayouts.cpp create mode 100644 compiler/coco/core/src/IR/FeatureLayouts.test.cpp create mode 100644 compiler/coco/core/src/IR/FeatureObject.cpp create mode 100644 compiler/coco/core/src/IR/FeatureObject.test.cpp create mode 100644 compiler/coco/core/src/IR/FeatureShape.test.cpp create mode 100644 compiler/coco/core/src/IR/Input.cpp create mode 100644 compiler/coco/core/src/IR/Input.test.cpp create mode 100644 compiler/coco/core/src/IR/InputManager.cpp create mode 100644 compiler/coco/core/src/IR/InputManager.test.cpp create mode 100644 compiler/coco/core/src/IR/Instr.cpp create mode 100644 compiler/coco/core/src/IR/InstrIndex.cpp create mode 100644 compiler/coco/core/src/IR/InstrIndex.test.cpp create mode 100644 compiler/coco/core/src/IR/InstrManager.cpp create mode 100644 compiler/coco/core/src/IR/InstrManager.test.cpp create mode 100644 compiler/coco/core/src/IR/KernelLayouts.cpp create mode 100644 compiler/coco/core/src/IR/KernelLayouts.test.cpp create mode 100644 compiler/coco/core/src/IR/KernelObject.cpp create mode 100644 compiler/coco/core/src/IR/KernelObject.test.cpp create mode 100644 compiler/coco/core/src/IR/Load.cpp create mode 100644 compiler/coco/core/src/IR/MaxPool2D.test.cpp create mode 100644 compiler/coco/core/src/IR/Module.cpp create mode 100644 compiler/coco/core/src/IR/Module.test.cpp create mode 100644 compiler/coco/core/src/IR/Object.cpp create mode 100644 compiler/coco/core/src/IR/Object.test.cpp create mode 100644 compiler/coco/core/src/IR/ObjectManager.cpp create mode 100644 compiler/coco/core/src/IR/ObjectManager.test.cpp create mode 100644 compiler/coco/core/src/IR/Op.cpp create mode 100644 compiler/coco/core/src/IR/OpManager.cpp create mode 100644 compiler/coco/core/src/IR/OpManager.test.cpp create mode 100644 compiler/coco/core/src/IR/Ops.cpp create mode 100644 compiler/coco/core/src/IR/Ops.test.cpp create mode 100644 compiler/coco/core/src/IR/Output.cpp create mode 100644 compiler/coco/core/src/IR/Output.test.cpp create mode 100644 compiler/coco/core/src/IR/OutputManager.cpp create mode 100644 compiler/coco/core/src/IR/OutputManager.test.cpp create mode 100644 compiler/coco/core/src/IR/PadF.test.cpp create mode 100644 compiler/coco/core/src/IR/Padding2D.cpp create mode 100644 compiler/coco/core/src/IR/Padding2D.test.cpp create mode 100644 compiler/coco/core/src/IR/Part.cpp create mode 100644 compiler/coco/core/src/IR/Part.test.cpp create mode 100644 compiler/coco/core/src/IR/Producer.mock.h create mode 100644 compiler/coco/core/src/IR/ReLU.test.cpp create mode 100644 compiler/coco/core/src/IR/ReLU6.test.cpp create mode 100644 compiler/coco/core/src/IR/Read.cpp create mode 100644 compiler/coco/core/src/IR/Read.test.cpp create mode 100644 compiler/coco/core/src/IR/Reader.mock.h create mode 100644 compiler/coco/core/src/IR/Shuffle.cpp create mode 100644 compiler/coco/core/src/IR/Shuffle.test.cpp create mode 100644 compiler/coco/core/src/IR/Sqrt.test.cpp create mode 100644 compiler/coco/core/src/IR/Step.cpp create mode 100644 compiler/coco/core/src/IR/Stride2D.cpp create mode 100644 compiler/coco/core/src/IR/Stride2D.test.cpp create mode 100644 compiler/coco/core/src/IR/Sub.test.cpp create mode 100644 compiler/coco/core/src/IR/Update.cpp create mode 100644 compiler/coco/core/src/IR/Update.test.cpp create mode 100644 compiler/coco/core/src/IR/Updater.mock.h create mode 100644 compiler/coco/core/src/IR/Use.cpp create mode 100644 compiler/coco/core/src/IR/Use.test.cpp create mode 100644 compiler/coco/core/src/IR/Window2D.test.cpp create mode 100644 compiler/coco/generic/CMakeLists.txt create mode 100644 compiler/coco/generic/include/coco/ADT/Span.h create mode 100644 compiler/coco/generic/include/coco/IR/Data.h create mode 100644 compiler/coco/generic/include/coco/IR/PlainWeightContext.h create mode 100644 compiler/coco/generic/src/ADT/Span.test.cpp create mode 100644 compiler/coco/generic/src/IR/Data.cpp create mode 100644 compiler/coco/generic/src/IR/Data.test.cpp create mode 100644 compiler/coco/requires.cmake create mode 100644 compiler/cwrap/CMakeLists.txt create mode 100644 compiler/cwrap/README.md create mode 100644 compiler/cwrap/include/cwrap/Fildes.h create mode 100644 compiler/cwrap/src/Fildes.cpp create mode 100644 compiler/cwrap/src/Fildes.test.cpp create mode 100644 compiler/dredd-rule-lib/CMakeLists.txt create mode 100644 compiler/dredd-rule-lib/README.md create mode 100755 compiler/dredd-rule-lib/rule-lib.sh create mode 100644 compiler/enco-intf/CMakeLists.txt create mode 100644 compiler/enco-intf/cmdline/CMakeLists.txt create mode 100644 compiler/enco-intf/cmdline/include/cmdline/View.h create mode 100644 compiler/enco-intf/frontend/CMakeLists.txt create mode 100644 compiler/enco-intf/frontend/include/enco/Bundle.h create mode 100644 compiler/enco-intf/frontend/include/enco/Frontend.h create mode 100644 compiler/enco/CMakeLists.txt create mode 100644 compiler/enco/README.md create mode 100644 compiler/enco/cli/CMakeLists.txt create mode 100644 compiler/enco/cli/src/Driver.cpp create mode 100644 compiler/enco/core/CMakeLists.txt create mode 100644 compiler/enco/core/include/enco/Backend.h create mode 100644 compiler/enco/core/src/ANN/Binder.h create mode 100644 compiler/enco/core/src/ANN/Context.cpp create mode 100644 compiler/enco/core/src/ANN/Context.h create mode 100644 compiler/enco/core/src/ANN/Context.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/DType.cpp create mode 100644 compiler/enco/core/src/ANN/IR/DType.h create mode 100644 compiler/enco/core/src/ANN/IR/DType.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/InputList.h create mode 100644 compiler/enco/core/src/ANN/IR/Module.h create mode 100644 compiler/enco/core/src/ANN/IR/Module.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/Operand.h create mode 100644 compiler/enco/core/src/ANN/IR/Operand.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OperandID.h create mode 100644 compiler/enco/core/src/ANN/IR/OperandID.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OperandInventory.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OperandInventory.h create mode 100644 compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/Operation.def create mode 100644 compiler/enco/core/src/ANN/IR/Operation.h create mode 100644 compiler/enco/core/src/ANN/IR/Operation.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OperationInventory.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OperationInventory.h create mode 100644 compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/OutputList.h create mode 100644 compiler/enco/core/src/ANN/IR/Weight.h create mode 100644 compiler/enco/core/src/ANN/IR/Weight.test.cpp create mode 100644 compiler/enco/core/src/ANN/IR/WeightInventory.cpp create mode 100644 compiler/enco/core/src/ANN/IR/WeightInventory.h create mode 100644 compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp create mode 100644 compiler/enco/core/src/AsmCode.cpp create mode 100644 compiler/enco/core/src/AsmCode.h create mode 100644 compiler/enco/core/src/Backend.cpp create mode 100644 compiler/enco/core/src/Code.h create mode 100644 compiler/enco/core/src/Code.test.cpp create mode 100644 compiler/enco/core/src/CodeIndex.h create mode 100644 compiler/enco/core/src/CppCode.cpp create mode 100644 compiler/enco/core/src/CppCode.h create mode 100644 compiler/enco/core/src/CppGen/Host.cpp create mode 100644 compiler/enco/core/src/CppGen/Host.h create mode 100644 compiler/enco/core/src/CppGen/MemoryContext.cpp create mode 100644 compiler/enco/core/src/CppGen/MemoryContext.h create mode 100644 compiler/enco/core/src/CppGen/Subnet.cpp create mode 100644 compiler/enco/core/src/CppGen/Subnet.h create mode 100644 compiler/enco/core/src/Dims.h create mode 100644 compiler/enco/core/src/IRUtils.cpp create mode 100644 compiler/enco/core/src/IRUtils.h create mode 100644 compiler/enco/core/src/IRValidator.cpp create mode 100644 compiler/enco/core/src/IRValidator.h create mode 100644 compiler/enco/core/src/IRValidator.test.cpp create mode 100644 compiler/enco/core/src/Pass.h create mode 100644 compiler/enco/core/src/Pass.test.cpp create mode 100644 compiler/enco/core/src/Pipeline.h create mode 100644 compiler/enco/core/src/Pipeline.test.cpp create mode 100644 compiler/enco/core/src/Session.cpp create mode 100644 compiler/enco/core/src/Session.h create mode 100644 compiler/enco/core/src/String.h create mode 100644 compiler/enco/core/src/Support/Debugging.cpp create mode 100644 compiler/enco/core/src/Support/Debugging.h create mode 100644 compiler/enco/core/src/Support/Debugging.test.cpp create mode 100644 compiler/enco/core/src/Transforms/AvgPoolLowering.cpp create mode 100644 compiler/enco/core/src/Transforms/AvgPoolLowering.h create mode 100644 compiler/enco/core/src/Transforms/ConcatLowering.cpp create mode 100644 compiler/enco/core/src/Transforms/ConcatLowering.h create mode 100644 compiler/enco/core/src/Transforms/ConstantFolding.cpp create mode 100644 compiler/enco/core/src/Transforms/ConstantFolding.h create mode 100644 compiler/enco/core/src/Transforms/ConstantFolding.test.cpp create mode 100644 compiler/enco/core/src/Transforms/CopyLowering.cpp create mode 100644 compiler/enco/core/src/Transforms/CopyLowering.h create mode 100644 compiler/enco/core/src/Transforms/DataLayoutConversion.cpp create mode 100644 compiler/enco/core/src/Transforms/DataLayoutConversion.h create mode 100644 compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp create mode 100644 compiler/enco/core/src/Transforms/DeadBagElimination.cpp create mode 100644 compiler/enco/core/src/Transforms/DeadBagElimination.h create mode 100644 compiler/enco/core/src/Transforms/DeadObjectElimination.cpp create mode 100644 compiler/enco/core/src/Transforms/DeadObjectElimination.h create mode 100644 compiler/enco/core/src/Transforms/Duplicate.cpp create mode 100644 compiler/enco/core/src/Transforms/Duplicate.h create mode 100644 compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp create mode 100644 compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h create mode 100644 compiler/enco/core/src/Transforms/FeatureUnification.cpp create mode 100644 compiler/enco/core/src/Transforms/FeatureUnification.h create mode 100644 compiler/enco/core/src/Transforms/FreeInstrElimination.cpp create mode 100644 compiler/enco/core/src/Transforms/FreeInstrElimination.h create mode 100644 compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp create mode 100644 compiler/enco/core/src/Transforms/FreeOpElimination.cpp create mode 100644 compiler/enco/core/src/Transforms/FreeOpElimination.h create mode 100644 compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp create mode 100644 compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp create mode 100644 compiler/enco/core/src/Transforms/GlobalDataGeneration.h create mode 100644 compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp create mode 100644 compiler/enco/core/src/Transforms/IdenticalObjectReduction.h create mode 100644 compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp create mode 100644 compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp create mode 100644 compiler/enco/core/src/Transforms/IndirectCopyElimination.h create mode 100644 compiler/enco/core/src/Transforms/IntrinsicSelection.cpp create mode 100644 compiler/enco/core/src/Transforms/IntrinsicSelection.h create mode 100644 compiler/enco/core/src/Transforms/Optimizations.cpp create mode 100644 compiler/enco/core/src/Transforms/Optimizations.h create mode 100644 compiler/enco/core/src/Transforms/Split.cpp create mode 100644 compiler/enco/core/src/Transforms/Split.h create mode 100644 compiler/enco/core/src/Usage.cpp create mode 100644 compiler/enco/core/src/Usage.h create mode 100644 compiler/enco/core/src/coex/IR.h create mode 100644 compiler/enco/core/src/coex/IR.test.cpp create mode 100644 compiler/enco/frontend/CMakeLists.txt create mode 100644 compiler/enco/frontend/caffe/CMakeLists.txt create mode 100644 compiler/enco/frontend/caffe/src/ConcatSpec.cpp create mode 100644 compiler/enco/frontend/caffe/src/ConcatSpec.h create mode 100644 compiler/enco/frontend/caffe/src/ConcatSpec.test.cpp create mode 100644 compiler/enco/frontend/caffe/src/Context.cpp create mode 100644 compiler/enco/frontend/caffe/src/Context.h create mode 100644 compiler/enco/frontend/caffe/src/Convert.cpp create mode 100644 compiler/enco/frontend/caffe/src/Convert.h create mode 100644 compiler/enco/frontend/caffe/src/ConvolutionSpec.cpp create mode 100644 compiler/enco/frontend/caffe/src/ConvolutionSpec.h create mode 100644 compiler/enco/frontend/caffe/src/ConvolutionSpec.test.cpp create mode 100644 compiler/enco/frontend/caffe/src/Entry.cpp create mode 100644 compiler/enco/frontend/caffe/src/Frontend.cpp create mode 100644 compiler/enco/frontend/caffe/src/Frontend.h create mode 100644 compiler/enco/frontend/caffe/src/GraphBuilder.cpp create mode 100644 compiler/enco/frontend/caffe/src/GraphBuilder.h create mode 100644 compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp create mode 100644 compiler/enco/frontend/caffe/src/GraphBuilderRegistry.h create mode 100644 compiler/enco/frontend/caffe/src/IRBuilder.h create mode 100644 compiler/enco/frontend/caffe/src/Importer.cpp create mode 100644 compiler/enco/frontend/caffe/src/Importer.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/BatchNorm.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/BatchNorm.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Concatenation.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Concatenation.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Convolution.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Convolution.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Eltwise.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Eltwise.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Input.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Input.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Pooling.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Pooling.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/ReLU.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/ReLU.h create mode 100644 compiler/enco/frontend/caffe/src/Layer/Scale.cpp create mode 100644 compiler/enco/frontend/caffe/src/Layer/Scale.h create mode 100644 compiler/enco/frontend/caffe/src/Padding.h create mode 100644 compiler/enco/frontend/caffe/src/Padding.test.cpp create mode 100644 compiler/enco/frontend/caffe/src/PaddingUtils.cpp create mode 100644 compiler/enco/frontend/caffe/src/PaddingUtils.h create mode 100644 compiler/enco/frontend/caffe/src/PoolingSpec.cpp create mode 100644 compiler/enco/frontend/caffe/src/PoolingSpec.h create mode 100644 compiler/enco/frontend/caffe/src/PoolingSpec.test.cpp create mode 100644 compiler/enco/frontend/caffe/src/ShapeQuery.cpp create mode 100644 compiler/enco/frontend/caffe/src/ShapeQuery.h create mode 100644 compiler/enco/frontend/tflite/CMakeLists.txt create mode 100644 compiler/enco/frontend/tflite/schema/schema.fbs create mode 100644 compiler/enco/frontend/tflite/schema/schema.meta create mode 100644 compiler/enco/frontend/tflite/src/Context.cpp create mode 100644 compiler/enco/frontend/tflite/src/Context.h create mode 100644 compiler/enco/frontend/tflite/src/Convert.cpp create mode 100644 compiler/enco/frontend/tflite/src/Convert.h create mode 100644 compiler/enco/frontend/tflite/src/Entry.cpp create mode 100644 compiler/enco/frontend/tflite/src/Frontend.cpp create mode 100644 compiler/enco/frontend/tflite/src/Frontend.h create mode 100644 compiler/enco/frontend/tflite/src/Frontend.test.cpp create mode 100644 compiler/enco/frontend/tflite/src/GraphBuilder.h create mode 100644 compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h create mode 100644 compiler/enco/frontend/tflite/src/IRBuilder.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Activation.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Activation.h create mode 100644 compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/AveragePool2D.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Concatenation.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Concatenation.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Conv2D.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Conv2D.h create mode 100644 compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Div.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Div.h create mode 100644 compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/MaxPool2D.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Padding.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Padding.h create mode 100644 compiler/enco/frontend/tflite/src/Op/ReLU.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/ReLU.h create mode 100644 compiler/enco/frontend/tflite/src/Op/ReLU6.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/ReLU6.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Reshape.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Reshape.h create mode 100644 compiler/enco/frontend/tflite/src/Op/Sub.cpp create mode 100644 compiler/enco/frontend/tflite/src/Op/Sub.h create mode 100644 compiler/enco/frontend/tflite/src/RawModel.h create mode 100644 compiler/enco/frontend/tflite/src/RawModelLoader.cpp create mode 100644 compiler/enco/frontend/tflite/src/RawModelLoader.h create mode 100644 compiler/enco/frontend/tflite/src/TensorBags.h create mode 100644 compiler/enco/requires.cmake create mode 100644 compiler/enco/test/CMakeLists.txt create mode 100644 compiler/enco/test/basic/000/CMakeLists.txt create mode 100644 compiler/enco/test/basic/000/enco.test.cpp create mode 100644 compiler/enco/test/basic/CMakeLists.txt create mode 100644 compiler/enco/test/binder.cpp create mode 100644 compiler/enco/test/caffe/CMakeLists.txt create mode 100755 compiler/enco/test/caffe/runall.sh create mode 100644 compiler/enco/test/tflite/AveragePool2D_000/INFERENCE create mode 100644 compiler/enco/test/tflite/AveragePool2D_000/test.recipe create mode 100644 compiler/enco/test/tflite/AveragePool2D_001/INFERENCE create mode 100644 compiler/enco/test/tflite/AveragePool2D_001/test.recipe create mode 100644 compiler/enco/test/tflite/CMakeLists.txt create mode 100644 compiler/enco/test/tflite/Concat_000/INFERENCE create mode 100644 compiler/enco/test/tflite/Concat_000/test.recipe create mode 100644 compiler/enco/test/tflite/Concat_001/INFERENCE create mode 100644 compiler/enco/test/tflite/Concat_001/test.recipe create mode 100644 compiler/enco/test/tflite/Concat_002/INFERENCE create mode 100644 compiler/enco/test/tflite/Concat_002/test.recipe create mode 100644 compiler/enco/test/tflite/Concat_003/INFERENCE create mode 100644 compiler/enco/test/tflite/Concat_003/test.recipe create mode 100644 compiler/enco/test/tflite/Conv2D_000/INFERENCE create mode 100644 compiler/enco/test/tflite/Conv2D_000/test.recipe create mode 100644 compiler/enco/test/tflite/Conv2D_001/INFERENCE create mode 100644 compiler/enco/test/tflite/Conv2D_001/test.recipe create mode 100644 compiler/enco/test/tflite/Conv2D_002/INFERENCE create mode 100644 compiler/enco/test/tflite/Conv2D_002/test.recipe create mode 100644 compiler/enco/test/tflite/Conv2D_003/INFERENCE create mode 100644 compiler/enco/test/tflite/Conv2D_003/test.recipe create mode 100644 compiler/enco/test/tflite/Conv2D_004/INFERENCE create mode 100644 compiler/enco/test/tflite/Conv2D_004/test.recipe create mode 100644 compiler/enco/test/tflite/DepthwiseConv2D_000/INFERENCE create mode 100644 compiler/enco/test/tflite/DepthwiseConv2D_000/test.recipe create mode 100644 compiler/enco/test/tflite/DepthwiseConv2D_001/INFERENCE create mode 100644 compiler/enco/test/tflite/DepthwiseConv2D_001/test.recipe create mode 100644 compiler/enco/test/tflite/Div_000/INFERENCE create mode 100644 compiler/enco/test/tflite/Div_000/test.recipe create mode 100644 compiler/enco/test/tflite/MaxPool2D_000/INFERENCE create mode 100644 compiler/enco/test/tflite/MaxPool2D_000/test.recipe create mode 100644 compiler/enco/test/tflite/ReLU6_000/INFERENCE create mode 100644 compiler/enco/test/tflite/ReLU6_000/test.recipe create mode 100644 compiler/enco/test/tflite/ReLU_000/INFERENCE create mode 100644 compiler/enco/test/tflite/ReLU_000/test.recipe create mode 100644 compiler/enco/test/tflite/Regression_0000/INFERENCE create mode 100644 compiler/enco/test/tflite/Regression_0000/test.recipe create mode 100644 compiler/enco/test/tflite/Regression_0001/INFERENCE create mode 100644 compiler/enco/test/tflite/Regression_0001/test.recipe create mode 100644 compiler/enco/test/tflite/Regression_0002/INFERENCE create mode 100644 compiler/enco/test/tflite/Regression_0002/test.recipe create mode 100644 compiler/enco/test/tflite/Regression_0003/INFERENCE create mode 100644 compiler/enco/test/tflite/Regression_0003/test.recipe create mode 100644 compiler/enco/test/tflite/Regression_0004/INFERENCE create mode 100644 compiler/enco/test/tflite/Regression_0004/test.recipe create mode 100644 compiler/enco/test/tflite/Reshape_000/INFERENCE create mode 100644 compiler/enco/test/tflite/Reshape_000/test.recipe create mode 100644 compiler/enco/test/tflite/Sub_000/INFERENCE create mode 100644 compiler/enco/test/tflite/Sub_000/test.recipe create mode 100644 compiler/enco/test/tflite/empty/INFERENCE create mode 100644 compiler/enco/test/tflite/empty/test.recipe create mode 100755 compiler/enco/test/tflite/runall.sh create mode 100644 compiler/encodump/CMakeLists.txt create mode 100644 compiler/encodump/README.md create mode 100644 compiler/encodump/requires.cmake create mode 100644 compiler/encodump/src/Driver.cpp create mode 100644 compiler/encodump/src/Dump.cpp create mode 100644 compiler/encodump/src/Dump.h create mode 100644 compiler/exo/CMakeLists.txt create mode 100644 compiler/exo/README.md create mode 100644 compiler/exo/include/exo/CircleExporter.h create mode 100644 compiler/exo/include/exo/LoggingContext.h create mode 100644 compiler/exo/include/exo/TFLExporter.h create mode 100644 compiler/exo/requires.cmake create mode 100644 compiler/exo/src/Check.h create mode 100644 compiler/exo/src/Circle/CircleExporter.cpp create mode 100644 compiler/exo/src/Circle/CircleExporterImpl.cpp create mode 100644 compiler/exo/src/Circle/CircleExporterImpl.h create mode 100644 compiler/exo/src/Circle/CircleExporterUtils.cpp create mode 100644 compiler/exo/src/Circle/CircleExporterUtils.h create mode 100644 compiler/exo/src/Circle/CircleOperationExporter.cpp create mode 100644 compiler/exo/src/Circle/CircleOperationExporter.h create mode 100644 compiler/exo/src/Circle/CircleTensorExporter.cpp create mode 100644 compiler/exo/src/Circle/CircleTensorExporter.h create mode 100644 compiler/exo/src/Circle/CircleTypeInference.cpp create mode 100644 compiler/exo/src/Circle/CircleTypeInference.h create mode 100644 compiler/exo/src/Conversion/AvgPool2DConverter.cpp create mode 100644 compiler/exo/src/Conversion/AvgPool2DConverter.h create mode 100644 compiler/exo/src/Conversion/CanonicalNodeConverter.cpp create mode 100644 compiler/exo/src/Conversion/CanonicalNodeConverter.h create mode 100644 compiler/exo/src/Conversion/ConstGenConverter.cpp create mode 100644 compiler/exo/src/Conversion/ConstGenConverter.h create mode 100644 compiler/exo/src/Conversion/ConstGenConverter.test.cpp create mode 100644 compiler/exo/src/Conversion/Conv2DConverter.cpp create mode 100644 compiler/exo/src/Conversion/Conv2DConverter.h create mode 100644 compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp create mode 100644 compiler/exo/src/Conversion/DepthwiseConv2DConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseAddConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseAddConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseBinaryConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseDivConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseDivConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseMaxConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseMaxConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseMulConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseMulConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseSqrtConverter.h create mode 100644 compiler/exo/src/Conversion/EltwiseSubConverter.cpp create mode 100644 compiler/exo/src/Conversion/EltwiseSubConverter.h create mode 100644 compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp create mode 100644 compiler/exo/src/Conversion/FeatureBiasAddConverter.h create mode 100644 compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp create mode 100644 compiler/exo/src/Conversion/MatMulConverter.cpp create mode 100644 compiler/exo/src/Conversion/MatMulConverter.h create mode 100644 compiler/exo/src/Conversion/MaxPool2DConverter.cpp create mode 100644 compiler/exo/src/Conversion/MaxPool2DConverter.h create mode 100644 compiler/exo/src/Conversion/Relu6Converter.cpp create mode 100644 compiler/exo/src/Conversion/Relu6Converter.h create mode 100644 compiler/exo/src/Conversion/ReluConverter.cpp create mode 100644 compiler/exo/src/Conversion/ReluConverter.h create mode 100644 compiler/exo/src/Conversion/ReluConverter.test.cpp create mode 100644 compiler/exo/src/Conversion/TensorBroadcastConverter.cpp create mode 100644 compiler/exo/src/Conversion/TensorBroadcastConverter.h create mode 100644 compiler/exo/src/Conversion/TensorConcatConverter.cpp create mode 100644 compiler/exo/src/Conversion/TensorConcatConverter.h create mode 100644 compiler/exo/src/Conversion/TensorReduceConverter.cpp create mode 100644 compiler/exo/src/Conversion/TensorReduceConverter.h create mode 100644 compiler/exo/src/Conversion/TensorTransposeConverter.cpp create mode 100644 compiler/exo/src/Conversion/TensorTransposeConverter.h create mode 100644 compiler/exo/src/Conversion/TransposedConv2DConverter.cpp create mode 100644 compiler/exo/src/Conversion/TransposedConv2DConverter.h create mode 100644 compiler/exo/src/Conversions.h create mode 100644 compiler/exo/src/Convert.cpp create mode 100644 compiler/exo/src/Convert.h create mode 100644 compiler/exo/src/Dialect/IR/CircleDialect.cpp create mode 100644 compiler/exo/src/Dialect/IR/CircleDialect.h create mode 100644 compiler/exo/src/Dialect/IR/CircleDialect.test.cpp create mode 100644 compiler/exo/src/Dialect/IR/CircleNode.cpp create mode 100644 compiler/exo/src/Dialect/IR/CircleNode.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodeDecl.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodeImpl.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodeVisitor.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodes.cpp create mode 100644 compiler/exo/src/Dialect/IR/CircleNodes.h create mode 100644 compiler/exo/src/Dialect/IR/CircleNodes.lst create mode 100644 compiler/exo/src/Dialect/IR/CircleNodes.test.cpp create mode 100644 compiler/exo/src/Dialect/IR/CircleOpcode.h create mode 100644 compiler/exo/src/Dialect/IR/FusedActFunc.h create mode 100644 compiler/exo/src/Dialect/IR/NodeMixins.cpp create mode 100644 compiler/exo/src/Dialect/IR/NodeMixins.h create mode 100644 compiler/exo/src/Dialect/IR/TFLDialect.cpp create mode 100644 compiler/exo/src/Dialect/IR/TFLDialect.h create mode 100644 compiler/exo/src/Dialect/IR/TFLDialect.test.cpp create mode 100644 compiler/exo/src/Dialect/IR/TFLNode.cpp create mode 100644 compiler/exo/src/Dialect/IR/TFLNode.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodeDecl.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodeImpl.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodeVisitor.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodes.cpp create mode 100644 compiler/exo/src/Dialect/IR/TFLNodes.h create mode 100644 compiler/exo/src/Dialect/IR/TFLNodes.lst create mode 100644 compiler/exo/src/Dialect/IR/TFLNodes.test.cpp create mode 100644 compiler/exo/src/Dialect/IR/TFLOpcode.h create mode 100644 compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp create mode 100644 compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h create mode 100644 compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp create mode 100644 compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h create mode 100644 compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp create mode 100644 compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h create mode 100644 compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp create mode 100644 compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp create mode 100644 compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h create mode 100644 compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp create mode 100644 compiler/exo/src/ExoFormattedGraph.cpp create mode 100644 compiler/exo/src/ExoFormattedGraph.h create mode 100644 compiler/exo/src/ExoOptimize.cpp create mode 100644 compiler/exo/src/ExoOptimize.h create mode 100644 compiler/exo/src/ExporterUtils.cpp create mode 100644 compiler/exo/src/ExporterUtils.h create mode 100644 compiler/exo/src/GraphBlock.cpp create mode 100644 compiler/exo/src/GraphBlock.h create mode 100644 compiler/exo/src/Knob.cpp create mode 100644 compiler/exo/src/Knob.h create mode 100644 compiler/exo/src/Knob.lst create mode 100644 compiler/exo/src/Log.cpp create mode 100644 compiler/exo/src/Log.h create mode 100644 compiler/exo/src/LogHelper.cpp create mode 100644 compiler/exo/src/LogHelper.h create mode 100644 compiler/exo/src/LoggingContext.cpp create mode 100644 compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp create mode 100644 compiler/exo/src/Pass/FoldReshapeOfConstPass.h create mode 100644 compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp create mode 100644 compiler/exo/src/Pass/FoldTransposeOfConstPass.h create mode 100644 compiler/exo/src/Pass/FuseBiasAddPass.cpp create mode 100644 compiler/exo/src/Pass/FuseBiasAddPass.h create mode 100644 compiler/exo/src/Pass/FuseBiasAddPass.test.cpp create mode 100644 compiler/exo/src/Pass/FuseInstanceNormPass.cpp create mode 100644 compiler/exo/src/Pass/FuseInstanceNormPass.h create mode 100644 compiler/exo/src/Pass/FuseReluPass.cpp create mode 100644 compiler/exo/src/Pass/FuseReluPass.h create mode 100644 compiler/exo/src/Pass/FuseReluPass.test.cpp create mode 100644 compiler/exo/src/Pass/FuseRsqrtPass.cpp create mode 100644 compiler/exo/src/Pass/FuseRsqrtPass.h create mode 100644 compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp create mode 100644 compiler/exo/src/Pass/FuseSquaredDifferencePass.h create mode 100644 compiler/exo/src/Pass/MergeConcatNodesPass.cpp create mode 100644 compiler/exo/src/Pass/MergeConcatNodesPass.h create mode 100644 compiler/exo/src/Pass/ShapeInferencePass.cpp create mode 100644 compiler/exo/src/Pass/ShapeInferencePass.h create mode 100644 compiler/exo/src/Pass/TypeInferencePass.cpp create mode 100644 compiler/exo/src/Pass/TypeInferencePass.h create mode 100644 compiler/exo/src/Passes.cpp create mode 100644 compiler/exo/src/Passes.h create mode 100644 compiler/exo/src/ProgressReporter.cpp create mode 100644 compiler/exo/src/ProgressReporter.h create mode 100644 compiler/exo/src/ShapeInference.cpp create mode 100644 compiler/exo/src/ShapeInference.h create mode 100644 compiler/exo/src/TFLite/TFLExporter.cpp create mode 100644 compiler/exo/src/TFLite/TFLExporterImpl.cpp create mode 100644 compiler/exo/src/TFLite/TFLExporterImpl.h create mode 100644 compiler/exo/src/TFLite/TFLExporterImpl.test.cpp create mode 100644 compiler/exo/src/TFLite/TFLExporterUtils.cpp create mode 100644 compiler/exo/src/TFLite/TFLExporterUtils.h create mode 100644 compiler/exo/src/TFLite/TFLExporterUtils.test.cpp create mode 100644 compiler/exo/src/TFLite/TFLOperationExporter.cpp create mode 100644 compiler/exo/src/TFLite/TFLOperationExporter.h create mode 100644 compiler/exo/src/TFLite/TFLTensorExporter.cpp create mode 100644 compiler/exo/src/TFLite/TFLTensorExporter.h create mode 100644 compiler/exo/src/TFLite/TFLTypeInference.cpp create mode 100644 compiler/exo/src/TFLite/TFLTypeInference.h create mode 100644 compiler/exo/src/TFLite/TFLTypeInference.test.cpp create mode 100644 compiler/exo/src/TestGraph.h create mode 100644 compiler/exo/src/TestHelper.h create mode 100644 compiler/fipe/CMakeLists.txt create mode 100644 compiler/fipe/fipe.test.cpp create mode 100644 compiler/fipe/include/fipe.h create mode 100644 compiler/gen-core/CMakeLists.txt create mode 100644 compiler/gen-core/README.md create mode 100644 compiler/gen-core/include/gencore/HDF5Common.h create mode 100644 compiler/gen-core/include/gencore/HDF5Exporter.h create mode 100644 compiler/gen-core/include/gencore/HDF5Importer.h create mode 100644 compiler/gen-core/requires.cmake create mode 100644 compiler/gen-core/src/HDF5Common.cpp create mode 100644 compiler/gen-core/src/HDF5Exporter.cpp create mode 100644 compiler/gen-core/src/HDF5Importer.cpp create mode 100644 compiler/gen-tf-input/CMakeLists.txt create mode 100644 compiler/gen-tf-input/README.md create mode 100644 compiler/gen-tf-input/src/Driver.cpp create mode 100644 compiler/gen-tf-output/CMakeLists.txt create mode 100644 compiler/gen-tf-output/README.md create mode 100644 compiler/gen-tf-output/src/Driver.cpp create mode 100644 compiler/gen-tflite-output/CMakeLists.txt create mode 100644 compiler/gen-tflite-output/README.md create mode 100644 compiler/gen-tflite-output/src/Driver.cpp create mode 100644 compiler/hermes-std/CMakeLists.txt create mode 100644 compiler/hermes-std/README.md create mode 100644 compiler/hermes-std/include/hermes/ConsoleReporter.h create mode 100644 compiler/hermes-std/include/hermes/EnvConfig.h create mode 100644 compiler/hermes-std/requires.cmake create mode 100644 compiler/hermes-std/src/ConsoleReporter.cpp create mode 100644 compiler/hermes-std/src/ConsoleReporter.test.cpp create mode 100644 compiler/hermes-std/src/EnvConfig.cpp create mode 100644 compiler/hermes/CMakeLists.txt create mode 100644 compiler/hermes/README.md create mode 100644 compiler/hermes/include/hermes.h create mode 100644 compiler/hermes/include/hermes/core/Config.h create mode 100644 compiler/hermes/include/hermes/core/Context.h create mode 100644 compiler/hermes/include/hermes/core/Message.h create mode 100644 compiler/hermes/include/hermes/core/MessageBuffer.h create mode 100644 compiler/hermes/include/hermes/core/MessageBus.h create mode 100644 compiler/hermes/include/hermes/core/Severity.h create mode 100644 compiler/hermes/include/hermes/core/Sink.h create mode 100644 compiler/hermes/include/hermes/core/Source.h create mode 100644 compiler/hermes/include/hermes/core/SourceSetting.h create mode 100644 compiler/hermes/requires.cmake create mode 100644 compiler/hermes/src/core/Context.cpp create mode 100644 compiler/hermes/src/core/Context.test.cpp create mode 100644 compiler/hermes/src/core/Message.cpp create mode 100644 compiler/hermes/src/core/Message.test.cpp create mode 100644 compiler/hermes/src/core/MessageBuffer.cpp create mode 100644 compiler/hermes/src/core/MessageBuffer.test.cpp create mode 100644 compiler/hermes/src/core/MessageBus.cpp create mode 100644 compiler/hermes/src/core/Severity.test.cpp create mode 100644 compiler/hermes/src/core/Sink.cpp create mode 100644 compiler/hermes/src/core/Source.cpp create mode 100644 compiler/hermes/src/core/Source.test.cpp create mode 100644 compiler/hermes/src/hermes.cpp create mode 100644 compiler/hermes/src/hermes.test.cpp create mode 100644 compiler/i5diff/CMakeLists.txt create mode 100644 compiler/i5diff/README.md create mode 100644 compiler/i5diff/requires.cmake create mode 100644 compiler/i5diff/src/entry.cpp create mode 100644 compiler/kuma/CMakeLists.txt create mode 100644 compiler/kuma/README.md create mode 100644 compiler/kuma/include/kuma.h create mode 100644 compiler/kuma/src/IntervalSet.cpp create mode 100644 compiler/kuma/src/IntervalSet.h create mode 100644 compiler/kuma/src/IntervalSet.test.cpp create mode 100644 compiler/kuma/src/kuma.cpp create mode 100644 compiler/kuma/src/kuma.test.cpp create mode 100644 compiler/loco/CMakeLists.txt create mode 100644 compiler/loco/README.md create mode 100644 compiler/loco/doc/LEP_000_Dialect_Service.md create mode 100644 compiler/loco/include/loco.h create mode 100644 compiler/loco/include/loco/ADT/AnnotatedItem.h create mode 100644 compiler/loco/include/loco/ADT/ObjectPool.h create mode 100644 compiler/loco/include/loco/IR/Algorithm.h create mode 100644 compiler/loco/include/loco/IR/BiasShape.h create mode 100644 compiler/loco/include/loco/IR/CanonicalDialect.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNode.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNodeDecl.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNodeImpl.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNodeVisitor.h create mode 100644 compiler/loco/include/loco/IR/CanonicalNodes.lst create mode 100644 compiler/loco/include/loco/IR/CanonicalOpcode.h create mode 100644 compiler/loco/include/loco/IR/DataType.h create mode 100644 compiler/loco/include/loco/IR/DataTypeTraits.h create mode 100644 compiler/loco/include/loco/IR/DepthwiseFilterAxis.h create mode 100644 compiler/loco/include/loco/IR/DepthwiseFilterCodec.h create mode 100644 compiler/loco/include/loco/IR/DepthwiseFilterIndex.h create mode 100644 compiler/loco/include/loco/IR/DepthwiseFilterShape.h create mode 100644 compiler/loco/include/loco/IR/Dialect.h create mode 100644 compiler/loco/include/loco/IR/DialectService.h create mode 100644 compiler/loco/include/loco/IR/Dimension.h create mode 100644 compiler/loco/include/loco/IR/Domain.h create mode 100644 compiler/loco/include/loco/IR/FeatureAxis.h create mode 100644 compiler/loco/include/loco/IR/FeatureCodec.h create mode 100644 compiler/loco/include/loco/IR/FeatureIndex.h create mode 100644 compiler/loco/include/loco/IR/FeatureShape.h create mode 100644 compiler/loco/include/loco/IR/FilterAxis.h create mode 100644 compiler/loco/include/loco/IR/FilterCodec.h create mode 100644 compiler/loco/include/loco/IR/FilterIndex.h create mode 100644 compiler/loco/include/loco/IR/FilterShape.h create mode 100644 compiler/loco/include/loco/IR/Graph.forward.h create mode 100644 compiler/loco/include/loco/IR/Graph.h create mode 100644 compiler/loco/include/loco/IR/GraphInputIndex.h create mode 100644 compiler/loco/include/loco/IR/GraphOutputIndex.h create mode 100644 compiler/loco/include/loco/IR/MatrixAxis.h create mode 100644 compiler/loco/include/loco/IR/MatrixCodec.h create mode 100644 compiler/loco/include/loco/IR/MatrixIndex.h create mode 100644 compiler/loco/include/loco/IR/MatrixShape.h create mode 100644 compiler/loco/include/loco/IR/Node.forward.h create mode 100644 compiler/loco/include/loco/IR/Node.h create mode 100644 compiler/loco/include/loco/IR/NodeMixins.h create mode 100644 compiler/loco/include/loco/IR/NodePool.forward.h create mode 100644 compiler/loco/include/loco/IR/NodePool.h create mode 100644 compiler/loco/include/loco/IR/NodeShape.h create mode 100644 compiler/loco/include/loco/IR/Nodes.h create mode 100644 compiler/loco/include/loco/IR/Padding2D.h create mode 100644 compiler/loco/include/loco/IR/PaddingND.h create mode 100644 compiler/loco/include/loco/IR/PermutingCodec.h create mode 100644 compiler/loco/include/loco/IR/Stride.h create mode 100644 compiler/loco/include/loco/IR/TensorAxis.h create mode 100644 compiler/loco/include/loco/IR/TensorAxisSet.h create mode 100644 compiler/loco/include/loco/IR/TensorIndex.h create mode 100644 compiler/loco/include/loco/IR/TensorShape.h create mode 100644 compiler/loco/include/loco/IR/Use.h create mode 100644 compiler/loco/include/loco/IR/Verifier.h create mode 100644 compiler/loco/include/loco/IR/Window.h create mode 100644 compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h create mode 100644 compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h create mode 100644 compiler/loco/include/loco/Service/ShapeInference.h create mode 100644 compiler/loco/include/loco/Service/ShapeInferenceRule.h create mode 100644 compiler/loco/include/loco/Service/TypeInference.h create mode 100644 compiler/loco/src/ADT/AnnotatedItem.test.cpp create mode 100644 compiler/loco/src/ADT/ObjectPool.cpp create mode 100644 compiler/loco/src/IR/Algorithm.cpp create mode 100644 compiler/loco/src/IR/Algorithm.test.cpp create mode 100644 compiler/loco/src/IR/BiasShape.test.cpp create mode 100644 compiler/loco/src/IR/CanonicalDialect.cpp create mode 100644 compiler/loco/src/IR/CanonicalDialect.test.cpp create mode 100644 compiler/loco/src/IR/CanonicalNode.cpp create mode 100644 compiler/loco/src/IR/CanonicalNode.test.cpp create mode 100644 compiler/loco/src/IR/CanonicalOpcode.cpp create mode 100644 compiler/loco/src/IR/DataType.cpp create mode 100644 compiler/loco/src/IR/DataTypeTraits.test.cpp create mode 100644 compiler/loco/src/IR/DepthwiseFilterAxis.cpp create mode 100644 compiler/loco/src/IR/DepthwiseFilterCodec.cpp create mode 100644 compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp create mode 100644 compiler/loco/src/IR/DepthwiseFilterShape.test.cpp create mode 100644 compiler/loco/src/IR/Dialect.cpp create mode 100644 compiler/loco/src/IR/Dialect.test.cpp create mode 100644 compiler/loco/src/IR/DialectService.cpp create mode 100644 compiler/loco/src/IR/Dimension.cpp create mode 100644 compiler/loco/src/IR/Dimension.test.cpp create mode 100644 compiler/loco/src/IR/Domain.cpp create mode 100644 compiler/loco/src/IR/FeatureAxis.cpp create mode 100644 compiler/loco/src/IR/FeatureCodec.cpp create mode 100644 compiler/loco/src/IR/FeatureIndex.test.cpp create mode 100644 compiler/loco/src/IR/FeatureShape.test.cpp create mode 100644 compiler/loco/src/IR/FilterAxis.cpp create mode 100644 compiler/loco/src/IR/FilterCodec.cpp create mode 100644 compiler/loco/src/IR/FilterIndex.test.cpp create mode 100644 compiler/loco/src/IR/FilterShape.test.cpp create mode 100644 compiler/loco/src/IR/Graph.cpp create mode 100644 compiler/loco/src/IR/Graph.test.cpp create mode 100644 compiler/loco/src/IR/GraphInputIndex.cpp create mode 100644 compiler/loco/src/IR/GraphOutputIndex.cpp create mode 100644 compiler/loco/src/IR/MatrixAxis.cpp create mode 100644 compiler/loco/src/IR/MatrixCodec.cpp create mode 100644 compiler/loco/src/IR/MockupNode.h create mode 100644 compiler/loco/src/IR/Node.cpp create mode 100644 compiler/loco/src/IR/Node.test.cpp create mode 100644 compiler/loco/src/IR/NodeMixins.cpp create mode 100644 compiler/loco/src/IR/NodePool.cpp create mode 100644 compiler/loco/src/IR/NodeShape.cpp create mode 100644 compiler/loco/src/IR/NodeShape.test.cpp create mode 100644 compiler/loco/src/IR/Nodes.cpp create mode 100644 compiler/loco/src/IR/Nodes.test.cpp create mode 100644 compiler/loco/src/IR/Padding2D.test.cpp create mode 100644 compiler/loco/src/IR/PaddingND.test.cpp create mode 100644 compiler/loco/src/IR/PermutingCodec.cpp create mode 100644 compiler/loco/src/IR/PermutingCodec.test.cpp create mode 100644 compiler/loco/src/IR/Stride.test.cpp create mode 100644 compiler/loco/src/IR/TensorAxis.cpp create mode 100644 compiler/loco/src/IR/TensorAxisSet.cpp create mode 100644 compiler/loco/src/IR/TensorIndex.cpp create mode 100644 compiler/loco/src/IR/TensorShape.cpp create mode 100644 compiler/loco/src/IR/TensorShape.test.cpp create mode 100644 compiler/loco/src/IR/Use.cpp create mode 100644 compiler/loco/src/IR/Use.test.cpp create mode 100644 compiler/loco/src/IR/Verifier.cpp create mode 100644 compiler/loco/src/IR/Verifier.test.cpp create mode 100644 compiler/loco/src/IR/Window.test.cpp create mode 100644 compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp create mode 100644 compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp create mode 100644 compiler/loco/src/Service/GraphBuilder.h create mode 100644 compiler/loco/src/Service/GraphBuilder.test.cpp create mode 100644 compiler/loco/src/Service/GraphTestcase.h create mode 100644 compiler/loco/src/Service/MultiDialectShapeInferenceRule.cpp create mode 100644 compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp create mode 100644 compiler/loco/src/Service/ShapeInference.cpp create mode 100644 compiler/loco/src/Service/ShapeInference.test.cpp create mode 100644 compiler/loco/src/Service/ShapeInferenceRule.cpp create mode 100644 compiler/loco/src/Service/TypeInference.cpp create mode 100644 compiler/loco/src/Service/TypeInference.test.cpp create mode 100644 compiler/loco/src/loco.test.cpp create mode 100644 compiler/loco/src/tensorflow.test.cpp create mode 100644 compiler/locoex-customop/CMakeLists.txt create mode 100644 compiler/locoex-customop/README.md create mode 100644 compiler/locoex-customop/include/locoex/COpAttrTypes.h create mode 100644 compiler/locoex-customop/include/locoex/COpCall.h create mode 100644 compiler/locoex-customop/include/locoex/COpDialect.h create mode 100644 compiler/locoex-customop/include/locoex/COpNode.h create mode 100644 compiler/locoex-customop/include/locoex/Service/COpFormattedGraph.h create mode 100644 compiler/locoex-customop/include/locoex/Service/COpShapeInferenceRule.h create mode 100644 compiler/locoex-customop/include/locoex/Service/COpTypeInference.h create mode 100644 compiler/locoex-customop/include/locoex/VariadicArityNode.h create mode 100644 compiler/locoex-customop/requires.cmake create mode 100644 compiler/locoex-customop/src/COpCall.cpp create mode 100644 compiler/locoex-customop/src/COpCall.test.cpp create mode 100644 compiler/locoex-customop/src/COpDialect.cpp create mode 100644 compiler/locoex-customop/src/COpDialect.test.cpp create mode 100644 compiler/locoex-customop/src/COpNode.cpp create mode 100644 compiler/locoex-customop/src/Service/COpFormattedGraph.cpp create mode 100644 compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp create mode 100644 compiler/locoex-customop/src/Service/COpShapeInferenceRule.test.cpp create mode 100644 compiler/locoex-customop/src/Service/COpTypeInference.cpp create mode 100644 compiler/locoex-customop/src/Service/COpTypeInference.test.cpp create mode 100644 compiler/locoex-customop/src/VariadicArityNode.test.cpp create mode 100644 compiler/locomotiv/CMakeLists.txt create mode 100644 compiler/locomotiv/README.md create mode 100644 compiler/locomotiv/include/locomotiv/NodeData.h create mode 100644 compiler/locomotiv/include/locomotiv/Session.h create mode 100644 compiler/locomotiv/requires.cmake create mode 100644 compiler/locomotiv/src/Node.lst create mode 100644 compiler/locomotiv/src/Node/AvgPool2D.cpp create mode 100644 compiler/locomotiv/src/Node/AvgPool2D.test.cpp create mode 100644 compiler/locomotiv/src/Node/BiasAdd.cpp create mode 100644 compiler/locomotiv/src/Node/BiasAdd.test.cpp create mode 100644 compiler/locomotiv/src/Node/BiasEncode.cpp create mode 100644 compiler/locomotiv/src/Node/BiasEncode.test.cpp create mode 100644 compiler/locomotiv/src/Node/ConstGen.cpp create mode 100644 compiler/locomotiv/src/Node/ConstGen.test.cpp create mode 100644 compiler/locomotiv/src/Node/Conv2D.cpp create mode 100644 compiler/locomotiv/src/Node/Conv2D.test.cpp create mode 100644 compiler/locomotiv/src/Node/DepthwiseConv2D.cpp create mode 100644 compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp create mode 100644 compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp create mode 100644 compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseAdd.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseAdd.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseDiv.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseDiv.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseMax.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseMax.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseMul.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseMul.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseSqrt.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseSub.cpp create mode 100644 compiler/locomotiv/src/Node/EltwiseSub.test.cpp create mode 100644 compiler/locomotiv/src/Node/FeatureCodec.test.cpp create mode 100644 compiler/locomotiv/src/Node/FeatureDecode.cpp create mode 100644 compiler/locomotiv/src/Node/FeatureEncode.cpp create mode 100644 compiler/locomotiv/src/Node/FilterEncode.cpp create mode 100644 compiler/locomotiv/src/Node/FilterEncode.test.cpp create mode 100644 compiler/locomotiv/src/Node/Forward.cpp create mode 100644 compiler/locomotiv/src/Node/Forward.test.cpp create mode 100644 compiler/locomotiv/src/Node/MatMul.cpp create mode 100644 compiler/locomotiv/src/Node/MatMul.test.cpp create mode 100644 compiler/locomotiv/src/Node/MatrixCodec.test.cpp create mode 100644 compiler/locomotiv/src/Node/MatrixDecode.cpp create mode 100644 compiler/locomotiv/src/Node/MatrixEncode.cpp create mode 100644 compiler/locomotiv/src/Node/MaxPool2D.cpp create mode 100644 compiler/locomotiv/src/Node/MaxPool2D.test.cpp create mode 100644 compiler/locomotiv/src/Node/Pull.cpp create mode 100644 compiler/locomotiv/src/Node/Pull.test.cpp create mode 100644 compiler/locomotiv/src/Node/Push.cpp create mode 100644 compiler/locomotiv/src/Node/Push.test.cpp create mode 100644 compiler/locomotiv/src/Node/ReLU.cpp create mode 100644 compiler/locomotiv/src/Node/ReLU.test.cpp create mode 100644 compiler/locomotiv/src/Node/ReLU6.cpp create mode 100644 compiler/locomotiv/src/Node/ReLU6.test.cpp create mode 100644 compiler/locomotiv/src/Node/Reshape.cpp create mode 100644 compiler/locomotiv/src/Node/Reshape.test.cpp create mode 100644 compiler/locomotiv/src/Node/Softmax.cpp create mode 100644 compiler/locomotiv/src/Node/Softmax.test.cpp create mode 100644 compiler/locomotiv/src/Node/Tanh.cpp create mode 100644 compiler/locomotiv/src/Node/Tanh.test.cpp create mode 100644 compiler/locomotiv/src/Node/TensorBroadcast.cpp create mode 100644 compiler/locomotiv/src/Node/TensorBroadcast.test.cpp create mode 100644 compiler/locomotiv/src/Node/TensorConcat.cpp create mode 100644 compiler/locomotiv/src/Node/TensorConcat.test.cpp create mode 100644 compiler/locomotiv/src/Node/TensorConstantPad.cpp create mode 100644 compiler/locomotiv/src/Node/TensorConstantPad.test.cpp create mode 100644 compiler/locomotiv/src/Node/TensorReduce.cpp create mode 100644 compiler/locomotiv/src/Node/TensorReduce.test.cpp create mode 100644 compiler/locomotiv/src/Node/TransposedConv2D.cpp create mode 100644 compiler/locomotiv/src/Node/TransposedConv2D.test.cpp create mode 100644 compiler/locomotiv/src/NodeData.cpp create mode 100644 compiler/locomotiv/src/NodeData.test.cpp create mode 100644 compiler/locomotiv/src/NodeDataImpl.cpp create mode 100644 compiler/locomotiv/src/NodeDataImpl.h create mode 100644 compiler/locomotiv/src/NodeDataImpl.test.cpp create mode 100644 compiler/locomotiv/src/NodeDomain.cpp create mode 100644 compiler/locomotiv/src/NodeDomain.h create mode 100644 compiler/locomotiv/src/NodeDomain.test.cpp create mode 100644 compiler/locomotiv/src/NodeExecution.cpp create mode 100644 compiler/locomotiv/src/NodeExecution.h create mode 100644 compiler/locomotiv/src/Session.cpp create mode 100644 compiler/locomotiv/src/Session.test.cpp create mode 100644 compiler/locomotiv/src/UserData.cpp create mode 100644 compiler/locomotiv/src/UserData.h create mode 100644 compiler/locomotiv/src/Validation.h create mode 100644 compiler/locop/CMakeLists.txt create mode 100644 compiler/locop/README.md create mode 100644 compiler/locop/include/locop/CanonicalNodeSummaryBuilder.h create mode 100644 compiler/locop/include/locop/FormattedGraph.h create mode 100644 compiler/locop/include/locop/FormattedTensorShape.h create mode 100644 compiler/locop/include/locop/GenericNodeSummaryBuilder.h create mode 100644 compiler/locop/include/locop/Interfaces.h create mode 100644 compiler/locop/include/locop/NodeSummary.h create mode 100644 compiler/locop/include/locop/NodeSummaryBuilder.h create mode 100644 compiler/locop/include/locop/SymbolTable.h create mode 100644 compiler/locop/src/CanonicalNodeSummaryBuilder.cpp create mode 100644 compiler/locop/src/ExampleGraph.h create mode 100644 compiler/locop/src/FormattedGraph.cpp create mode 100644 compiler/locop/src/FormattedGraph.test.cpp create mode 100644 compiler/locop/src/FormattedTensorShape.cpp create mode 100644 compiler/locop/src/FormattedTensorShape.test.cpp create mode 100644 compiler/locop/src/GenericNodeSummaryBuilder.cpp create mode 100644 compiler/locop/src/GenericNodeSummaryBuilder.test.cpp create mode 100644 compiler/locop/src/Interfaces.cpp create mode 100644 compiler/locop/src/NodeSummary.cpp create mode 100644 compiler/locop/src/NodeSummaryBuilder.cpp create mode 100644 compiler/logo-core/CMakeLists.txt create mode 100644 compiler/logo-core/README.md create mode 100644 compiler/logo-core/include/logo/Pass.h create mode 100644 compiler/logo-core/include/logo/Phase.h create mode 100644 compiler/logo-core/requires.cmake create mode 100644 compiler/logo-core/src/Pass.cpp create mode 100644 compiler/logo-core/src/Pass.test.cpp create mode 100644 compiler/logo-core/src/Phase.cpp create mode 100644 compiler/logo/CMakeLists.txt create mode 100644 compiler/logo/README.md create mode 100644 compiler/logo/include/logo/ConstantFoldingPass.h create mode 100644 compiler/logo/include/logo/Passes.h create mode 100644 compiler/logo/include/logo/RemoveDeadNodePass.h create mode 100644 compiler/logo/include/logo/RemoveForwardNodePass.h create mode 100644 compiler/logo/include/logo/ReorderDecodePass.h create mode 100644 compiler/logo/include/logo/ResolveDuplicateReshapePass.h create mode 100644 compiler/logo/include/logo/ResolveRedundantReshapePass.h create mode 100644 compiler/logo/include/logo/SimplifyDomainConversionPass.h create mode 100644 compiler/logo/requires.cmake create mode 100644 compiler/logo/src/Passes/ConstantFoldingPass.cpp create mode 100644 compiler/logo/src/Passes/ConstantFoldingPass.test.cpp create mode 100644 compiler/logo/src/Passes/RemoveDeadNodePass.cpp create mode 100644 compiler/logo/src/Passes/RemoveForwardNodePass.cpp create mode 100644 compiler/logo/src/Passes/ReorderDecodePass.cpp create mode 100644 compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp create mode 100644 compiler/logo/src/Passes/ResolveRedundantReshapePass.cpp create mode 100644 compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp create mode 100644 compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp create mode 100644 compiler/logo/src/TestHelper.h create mode 100644 compiler/luci/CMakeLists.txt create mode 100644 compiler/luci/README.md create mode 100644 compiler/luci/export/CMakeLists.txt create mode 100644 compiler/luci/export/README.md create mode 100644 compiler/luci/export/include/luci/CircleExporter.h create mode 100644 compiler/luci/export/src/Check.h create mode 100644 compiler/luci/export/src/CircleExporter.cpp create mode 100644 compiler/luci/export/src/CircleExporterImpl.cpp create mode 100644 compiler/luci/export/src/CircleExporterImpl.h create mode 100644 compiler/luci/export/src/CircleExporterUtils.cpp create mode 100644 compiler/luci/export/src/CircleExporterUtils.h create mode 100644 compiler/luci/export/src/CircleOperationExporter.cpp create mode 100644 compiler/luci/export/src/CircleOperationExporter.h create mode 100644 compiler/luci/export/src/CircleTensorExporter.cpp create mode 100644 compiler/luci/export/src/CircleTensorExporter.h create mode 100644 compiler/luci/export/src/Optimize.cpp create mode 100644 compiler/luci/export/src/Optimize.h create mode 100644 compiler/luci/export/src/ProgressReporter.cpp create mode 100644 compiler/luci/export/src/ProgressReporter.h create mode 100644 compiler/luci/export/src/SerializedData.h create mode 100644 compiler/luci/import/CMakeLists.txt create mode 100644 compiler/luci/import/README.md create mode 100644 compiler/luci/import/include/luci/Import/CircleReader.h create mode 100644 compiler/luci/import/include/luci/Import/GraphBuilder.h create mode 100644 compiler/luci/import/include/luci/Import/GraphBuilderContext.h create mode 100644 compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleConst.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleCos.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleExp.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleMean.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleMul.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CirclePack.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CirclePad.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleSub.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h create mode 100644 compiler/luci/import/include/luci/Importer.h create mode 100644 compiler/luci/import/src/CircleReader.cpp create mode 100644 compiler/luci/import/src/GraphBuilder.cpp create mode 100644 compiler/luci/import/src/GraphBuilderContext.cpp create mode 100644 compiler/luci/import/src/GraphBuilderRegistry.cpp create mode 100644 compiler/luci/import/src/Importer.cpp create mode 100644 compiler/luci/import/src/Importer.test.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleAbs.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleAdd.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleArgMax.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleConcatenation.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleConst.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleConv2D.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleCos.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleDiv.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleEqual.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleExp.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleFullyConnected.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleLogicalNot.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleLogicalOr.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleMean.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleMul.cpp create mode 100644 compiler/luci/import/src/Nodes/CirclePack.cpp create mode 100644 compiler/luci/import/src/Nodes/CirclePad.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleRelu.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleReshape.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleRsqrt.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleSoftmax.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleSub.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleTranspose.cpp create mode 100644 compiler/luci/lang/CMakeLists.txt create mode 100644 compiler/luci/lang/README.md create mode 100644 compiler/luci/lang/include/luci/IR/AttrFilter.h create mode 100644 compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h create mode 100644 compiler/luci/lang/include/luci/IR/AttrPadding.h create mode 100644 compiler/luci/lang/include/luci/IR/AttrStride.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleDialect.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNode.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodeDecl.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodeImpl.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodes.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleNodes.lst create mode 100644 compiler/luci/lang/include/luci/IR/CircleOpcode.h create mode 100644 compiler/luci/lang/include/luci/IR/CircleQuantParam.h create mode 100644 compiler/luci/lang/include/luci/IR/LuciNodeMixins.h create mode 100644 compiler/luci/lang/include/luci/IR/Module.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h create mode 100644 compiler/luci/lang/include/luci/IR/VariadicArityNode.h create mode 100644 compiler/luci/lang/src/Check.h create mode 100644 compiler/luci/lang/src/CircleDialect.cpp create mode 100644 compiler/luci/lang/src/CircleDialect.test.cpp create mode 100644 compiler/luci/lang/src/CircleNode.cpp create mode 100644 compiler/luci/lang/src/CircleNodes.cpp create mode 100644 compiler/luci/lang/src/LuciNodeMixins.cpp create mode 100644 compiler/luci/lang/src/Module.cpp create mode 100644 compiler/luci/lang/src/Module.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleAbs.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleAdd.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleConst.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleCos.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleDiv.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleEqual.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleExp.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleGather.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleInput.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleMul.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleOutput.cpp create mode 100644 compiler/luci/lang/src/Nodes/CirclePack.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CirclePad.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleRelu.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleReshape.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleSub.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp create mode 100644 compiler/luci/log/CMakeLists.txt create mode 100644 compiler/luci/log/README.md create mode 100644 compiler/luci/log/include/luci/Log.h create mode 100644 compiler/luci/log/include/luci/LoggingContext.h create mode 100644 compiler/luci/log/src/Log.cpp create mode 100644 compiler/luci/log/src/LoggingContext.cpp create mode 100644 compiler/luci/logex/CMakeLists.txt create mode 100644 compiler/luci/logex/README.md create mode 100644 compiler/luci/logex/include/luci/FormattedGraph.h create mode 100644 compiler/luci/logex/include/luci/LogHelper.h create mode 100644 compiler/luci/logex/src/FormattedGraph.cpp create mode 100644 compiler/luci/logex/src/LogHelper.cpp create mode 100644 compiler/luci/pass/CMakeLists.txt create mode 100644 compiler/luci/pass/README.md create mode 100644 compiler/luci/pass/include/luci/CircleOptimizer.h create mode 100644 compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h create mode 100644 compiler/luci/pass/include/luci/Pass/TypeInferencePass.h create mode 100644 compiler/luci/pass/src/CircleOptimizer.cpp create mode 100644 compiler/luci/pass/src/FuseInstanceNormPass.cpp create mode 100644 compiler/luci/pass/src/ProgressReporter.cpp create mode 100644 compiler/luci/pass/src/ProgressReporter.h create mode 100644 compiler/luci/pass/src/ShapeInferencePass.cpp create mode 100644 compiler/luci/pass/src/TypeInferencePass.cpp create mode 100644 compiler/luci/requires.cmake create mode 100644 compiler/luci/service/CMakeLists.txt create mode 100644 compiler/luci/service/README.md create mode 100644 compiler/luci/service/include/luci/Service/CircleShapeInference.h create mode 100644 compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h create mode 100644 compiler/luci/service/include/luci/Service/CircleTypeInference.h create mode 100644 compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h create mode 100644 compiler/luci/service/include/luci/Service/ShapeDescription.h create mode 100644 compiler/luci/service/include/luci/Service/Validate.h create mode 100644 compiler/luci/service/src/Check.h create mode 100644 compiler/luci/service/src/CircleShapeInference.cpp create mode 100644 compiler/luci/service/src/CircleShapeInferenceRule.cpp create mode 100644 compiler/luci/service/src/CircleShapeInferenceRule.test.cpp create mode 100644 compiler/luci/service/src/CircleTypeInference.cpp create mode 100644 compiler/luci/service/src/CircleTypeInferenceRule.cpp create mode 100644 compiler/luci/service/src/CircleTypeInferenceRule.test.cpp create mode 100644 compiler/luci/service/src/GraphBlock.h create mode 100644 compiler/luci/service/src/GraphBlock.test.cpp create mode 100644 compiler/luci/service/src/ShapeDescription.cpp create mode 100644 compiler/luci/service/src/TestGraph.h create mode 100644 compiler/luci/service/src/Validate.cpp create mode 100644 compiler/luci/tester/CMakeLists.txt create mode 100644 compiler/luci/tester/src/Model.cpp create mode 100644 compiler/luci/tester/src/Model.h create mode 100644 compiler/luci/tester/src/ReadTester.cpp create mode 100644 compiler/luci/tester/src/WriteTester.cpp create mode 100644 compiler/luci/tests/.gitignore create mode 100644 compiler/luci/tests/CMakeLists.txt create mode 100755 compiler/luci/tests/readverify.sh create mode 100644 compiler/luci/tests/test.lst create mode 100755 compiler/luci/tests/writeverify.sh create mode 100644 compiler/mio-circle/CMakeLists.txt create mode 100644 compiler/mio-circle/README.md create mode 100644 compiler/mio-circle/example.cpp create mode 100644 compiler/mio-tf/CMakeLists.txt create mode 100644 compiler/mio-tf/README.md create mode 100644 compiler/mio-tf/src/mio_tf.test.cpp create mode 100644 compiler/mio-tflite/CMakeLists.txt create mode 100644 compiler/mio-tflite/README.md create mode 100644 compiler/mio-tflite/example.cpp create mode 100644 compiler/mir-caffe-importer/CMakeLists.txt create mode 100644 compiler/mir-caffe-importer/caffe_importer.cpp create mode 100644 compiler/mir-caffe-importer/caffe_importer.h create mode 100644 compiler/mir-caffe-importer/caffe_op_creator.cpp create mode 100644 compiler/mir-caffe-importer/caffe_op_creator.h create mode 100644 compiler/mir-caffe-importer/caffe_op_types.h create mode 100644 compiler/mir-caffe-importer/requires.cmake create mode 100644 compiler/mir-caffe2-importer/CMakeLists.txt create mode 100644 compiler/mir-caffe2-importer/caffe2_importer.cpp create mode 100644 compiler/mir-caffe2-importer/caffe2_importer.h create mode 100644 compiler/mir-caffe2-importer/caffe2_op_creator.cpp create mode 100644 compiler/mir-caffe2-importer/caffe2_op_creator.h create mode 100644 compiler/mir-caffe2-importer/caffe2_op_types.h create mode 100644 compiler/mir-caffe2-importer/caffe2_proto_helper.cpp create mode 100644 compiler/mir-caffe2-importer/caffe2_proto_helper.h create mode 100644 compiler/mir-caffe2-importer/requires.cmake create mode 100644 compiler/mir-interpreter/CMakeLists.txt create mode 100644 compiler/mir-interpreter/include/MirInterpreter.h create mode 100644 compiler/mir-interpreter/requires.cmake create mode 100644 compiler/mir-interpreter/src/MirInterpreter.cpp create mode 100644 compiler/mir-interpreter/src/ops/Abs.cpp create mode 100644 compiler/mir-interpreter/src/ops/Abs.h create mode 100644 compiler/mir-interpreter/src/ops/Add.cpp create mode 100644 compiler/mir-interpreter/src/ops/Add.h create mode 100644 compiler/mir-interpreter/src/ops/AvgPool2D.cpp create mode 100644 compiler/mir-interpreter/src/ops/AvgPool2D.h create mode 100644 compiler/mir-interpreter/src/ops/CappedReLU.cpp create mode 100644 compiler/mir-interpreter/src/ops/CappedReLU.h create mode 100644 compiler/mir-interpreter/src/ops/Common.cpp create mode 100644 compiler/mir-interpreter/src/ops/Common.h create mode 100644 compiler/mir-interpreter/src/ops/Concat.cpp create mode 100644 compiler/mir-interpreter/src/ops/Concat.h create mode 100644 compiler/mir-interpreter/src/ops/Conv2D.cpp create mode 100644 compiler/mir-interpreter/src/ops/Conv2D.h create mode 100644 compiler/mir-interpreter/src/ops/DeConv2D.cpp create mode 100644 compiler/mir-interpreter/src/ops/DeConv2D.h create mode 100644 compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp create mode 100644 compiler/mir-interpreter/src/ops/DepthwiseConv2D.h create mode 100644 compiler/mir-interpreter/src/ops/Div.cpp create mode 100644 compiler/mir-interpreter/src/ops/Div.h create mode 100644 compiler/mir-interpreter/src/ops/ELU.cpp create mode 100644 compiler/mir-interpreter/src/ops/ELU.h create mode 100644 compiler/mir-interpreter/src/ops/Equal.cpp create mode 100644 compiler/mir-interpreter/src/ops/Equal.h create mode 100644 compiler/mir-interpreter/src/ops/Fill.h create mode 100644 compiler/mir-interpreter/src/ops/FullyConnected.cpp create mode 100644 compiler/mir-interpreter/src/ops/FullyConnected.h create mode 100644 compiler/mir-interpreter/src/ops/Gather.cpp create mode 100644 compiler/mir-interpreter/src/ops/Gather.h create mode 100644 compiler/mir-interpreter/src/ops/Greater.cpp create mode 100644 compiler/mir-interpreter/src/ops/Greater.h create mode 100644 compiler/mir-interpreter/src/ops/HardSwish.cpp create mode 100644 compiler/mir-interpreter/src/ops/HardSwish.h create mode 100644 compiler/mir-interpreter/src/ops/LeakyReLU.cpp create mode 100644 compiler/mir-interpreter/src/ops/LeakyReLU.h create mode 100644 compiler/mir-interpreter/src/ops/Less.cpp create mode 100644 compiler/mir-interpreter/src/ops/Less.h create mode 100644 compiler/mir-interpreter/src/ops/Max.cpp create mode 100644 compiler/mir-interpreter/src/ops/Max.h create mode 100644 compiler/mir-interpreter/src/ops/MaxPool2D.cpp create mode 100644 compiler/mir-interpreter/src/ops/MaxPool2D.h create mode 100644 compiler/mir-interpreter/src/ops/Mul.cpp create mode 100644 compiler/mir-interpreter/src/ops/Mul.h create mode 100644 compiler/mir-interpreter/src/ops/Pad.cpp create mode 100644 compiler/mir-interpreter/src/ops/Pad.h create mode 100644 compiler/mir-interpreter/src/ops/Quantization.cpp create mode 100644 compiler/mir-interpreter/src/ops/Quantization.h create mode 100644 compiler/mir-interpreter/src/ops/QuantizationHelpers.h create mode 100644 compiler/mir-interpreter/src/ops/ReLU.cpp create mode 100644 compiler/mir-interpreter/src/ops/ReLU.h create mode 100644 compiler/mir-interpreter/src/ops/ReduceMean.cpp create mode 100644 compiler/mir-interpreter/src/ops/ReduceMean.h create mode 100644 compiler/mir-interpreter/src/ops/Reshape.cpp create mode 100644 compiler/mir-interpreter/src/ops/Reshape.h create mode 100644 compiler/mir-interpreter/src/ops/Sigmoid.cpp create mode 100644 compiler/mir-interpreter/src/ops/Sigmoid.h create mode 100644 compiler/mir-interpreter/src/ops/Slice.cpp create mode 100644 compiler/mir-interpreter/src/ops/Slice.h create mode 100644 compiler/mir-interpreter/src/ops/Softmax.cpp create mode 100644 compiler/mir-interpreter/src/ops/Softmax.h create mode 100644 compiler/mir-interpreter/src/ops/Sqrt.cpp create mode 100644 compiler/mir-interpreter/src/ops/Sqrt.h create mode 100644 compiler/mir-interpreter/src/ops/Sub.cpp create mode 100644 compiler/mir-interpreter/src/ops/Sub.h create mode 100644 compiler/mir-interpreter/src/ops/Tanh.cpp create mode 100644 compiler/mir-interpreter/src/ops/Tanh.h create mode 100644 compiler/mir-interpreter/src/ops/Transpose.cpp create mode 100644 compiler/mir-interpreter/src/ops/Transpose.h create mode 100644 compiler/mir-onnx-importer/AttributeHelpers.h create mode 100644 compiler/mir-onnx-importer/CMakeLists.txt create mode 100644 compiler/mir-onnx-importer/ConvPoolHelpers.cpp create mode 100644 compiler/mir-onnx-importer/ConvPoolHelpers.h create mode 100644 compiler/mir-onnx-importer/ONNXHelpers.cpp create mode 100644 compiler/mir-onnx-importer/ONNXHelpers.h create mode 100644 compiler/mir-onnx-importer/ONNXImporterImpl.cpp create mode 100644 compiler/mir-onnx-importer/ONNXImporterImpl.h create mode 100644 compiler/mir-onnx-importer/ONNXNodeConverterRegistry.cpp create mode 100644 compiler/mir-onnx-importer/ONNXNodeConverterRegistry.h create mode 100644 compiler/mir-onnx-importer/ONNXNodeConverterRegistry.test.cpp create mode 100644 compiler/mir-onnx-importer/ONNXOpRegistration.h create mode 100644 compiler/mir-onnx-importer/Op/Abs.cpp create mode 100644 compiler/mir-onnx-importer/Op/Abs.h create mode 100644 compiler/mir-onnx-importer/Op/Add.cpp create mode 100644 compiler/mir-onnx-importer/Op/Add.h create mode 100644 compiler/mir-onnx-importer/Op/AveragePool.cpp create mode 100644 compiler/mir-onnx-importer/Op/AveragePool.h create mode 100644 compiler/mir-onnx-importer/Op/BatchNormalization.cpp create mode 100644 compiler/mir-onnx-importer/Op/BatchNormalization.h create mode 100644 compiler/mir-onnx-importer/Op/Concat.cpp create mode 100644 compiler/mir-onnx-importer/Op/Concat.h create mode 100644 compiler/mir-onnx-importer/Op/Constant.cpp create mode 100644 compiler/mir-onnx-importer/Op/Constant.h create mode 100644 compiler/mir-onnx-importer/Op/Conv.cpp create mode 100644 compiler/mir-onnx-importer/Op/Conv.h create mode 100644 compiler/mir-onnx-importer/Op/ConvTranspose.cpp create mode 100644 compiler/mir-onnx-importer/Op/ConvTranspose.h create mode 100644 compiler/mir-onnx-importer/Op/Div.cpp create mode 100644 compiler/mir-onnx-importer/Op/Div.h create mode 100644 compiler/mir-onnx-importer/Op/Dropout.cpp create mode 100644 compiler/mir-onnx-importer/Op/Dropout.h create mode 100644 compiler/mir-onnx-importer/Op/Equal.cpp create mode 100644 compiler/mir-onnx-importer/Op/Equal.h create mode 100644 compiler/mir-onnx-importer/Op/Expand.cpp create mode 100644 compiler/mir-onnx-importer/Op/Expand.h create mode 100644 compiler/mir-onnx-importer/Op/Flatten.cpp create mode 100644 compiler/mir-onnx-importer/Op/Flatten.h create mode 100644 compiler/mir-onnx-importer/Op/Gather.cpp create mode 100644 compiler/mir-onnx-importer/Op/Gather.h create mode 100644 compiler/mir-onnx-importer/Op/Gemm.cpp create mode 100644 compiler/mir-onnx-importer/Op/Gemm.h create mode 100644 compiler/mir-onnx-importer/Op/GlobalAveragePool.cpp create mode 100644 compiler/mir-onnx-importer/Op/GlobalAveragePool.h create mode 100644 compiler/mir-onnx-importer/Op/Greater.cpp create mode 100644 compiler/mir-onnx-importer/Op/Greater.h create mode 100644 compiler/mir-onnx-importer/Op/Identity.cpp create mode 100644 compiler/mir-onnx-importer/Op/Identity.h create mode 100644 compiler/mir-onnx-importer/Op/Less.cpp create mode 100644 compiler/mir-onnx-importer/Op/Less.h create mode 100644 compiler/mir-onnx-importer/Op/MatMul.cpp create mode 100644 compiler/mir-onnx-importer/Op/MatMul.h create mode 100644 compiler/mir-onnx-importer/Op/Max.cpp create mode 100644 compiler/mir-onnx-importer/Op/Max.h create mode 100644 compiler/mir-onnx-importer/Op/MaxPool.cpp create mode 100644 compiler/mir-onnx-importer/Op/MaxPool.h create mode 100644 compiler/mir-onnx-importer/Op/Mul.cpp create mode 100644 compiler/mir-onnx-importer/Op/Mul.h create mode 100644 compiler/mir-onnx-importer/Op/Pad.cpp create mode 100644 compiler/mir-onnx-importer/Op/Pad.h create mode 100644 compiler/mir-onnx-importer/Op/Reciprocal.cpp create mode 100644 compiler/mir-onnx-importer/Op/Reciprocal.h create mode 100644 compiler/mir-onnx-importer/Op/ReduceMean.cpp create mode 100644 compiler/mir-onnx-importer/Op/ReduceMean.h create mode 100644 compiler/mir-onnx-importer/Op/Relu.cpp create mode 100644 compiler/mir-onnx-importer/Op/Relu.h create mode 100644 compiler/mir-onnx-importer/Op/Reshape.cpp create mode 100644 compiler/mir-onnx-importer/Op/Reshape.h create mode 100644 compiler/mir-onnx-importer/Op/Shape.cpp create mode 100644 compiler/mir-onnx-importer/Op/Shape.h create mode 100644 compiler/mir-onnx-importer/Op/Sigmoid.cpp create mode 100644 compiler/mir-onnx-importer/Op/Sigmoid.h create mode 100644 compiler/mir-onnx-importer/Op/Softmax.cpp create mode 100644 compiler/mir-onnx-importer/Op/Softmax.h create mode 100644 compiler/mir-onnx-importer/Op/Sqrt.cpp create mode 100644 compiler/mir-onnx-importer/Op/Sqrt.h create mode 100644 compiler/mir-onnx-importer/Op/Sub.cpp create mode 100644 compiler/mir-onnx-importer/Op/Sub.h create mode 100644 compiler/mir-onnx-importer/Op/Sum.cpp create mode 100644 compiler/mir-onnx-importer/Op/Sum.h create mode 100644 compiler/mir-onnx-importer/Op/Tanh.cpp create mode 100644 compiler/mir-onnx-importer/Op/Tanh.h create mode 100644 compiler/mir-onnx-importer/Op/Transpose.cpp create mode 100644 compiler/mir-onnx-importer/Op/Transpose.h create mode 100644 compiler/mir-onnx-importer/Op/Unsqueeze.cpp create mode 100644 compiler/mir-onnx-importer/Op/Unsqueeze.h create mode 100644 compiler/mir-onnx-importer/Op/Upsample.cpp create mode 100644 compiler/mir-onnx-importer/Op/Upsample.h create mode 100644 compiler/mir-onnx-importer/requires.cmake create mode 100644 compiler/mir-tflite-importer/CMakeLists.txt create mode 100644 compiler/mir-tflite-importer/requires.cmake create mode 100644 compiler/mir-tflite-importer/schema/schema.fbs create mode 100644 compiler/mir-tflite-importer/schema/schema.meta create mode 100644 compiler/mir-tflite-importer/schema/schema_v0.fbs create mode 100644 compiler/mir-tflite-importer/schema/schema_v0.meta create mode 100644 compiler/mir-tflite-importer/schema/schema_v1.fbs create mode 100644 compiler/mir-tflite-importer/schema/schema_v1.meta create mode 100644 compiler/mir-tflite-importer/schema/schema_v2.fbs create mode 100644 compiler/mir-tflite-importer/schema/schema_v2.meta create mode 100644 compiler/mir-tflite-importer/schema/schema_v3.fbs create mode 100644 compiler/mir-tflite-importer/schema/schema_v3.meta create mode 100644 compiler/mir-tflite-importer/tflite_importer.cpp create mode 100644 compiler/mir-tflite-importer/tflite_importer.h create mode 100644 compiler/mir-tflite-importer/tflite_op_creator.cpp create mode 100644 compiler/mir-tflite-importer/tflite_op_creator.h create mode 100644 compiler/mir/CMakeLists.txt create mode 100644 compiler/mir/Readme.md create mode 100644 compiler/mir/include/mir/Attributes.h create mode 100644 compiler/mir/include/mir/Common.h create mode 100644 compiler/mir/include/mir/DataFormat.h create mode 100644 compiler/mir/include/mir/DataType.h create mode 100644 compiler/mir/include/mir/ExternalRegion.h create mode 100644 compiler/mir/include/mir/Graph.h create mode 100644 compiler/mir/include/mir/GraphPatternMatcher.h create mode 100644 compiler/mir/include/mir/Index.h create mode 100644 compiler/mir/include/mir/IrDotDumper.h create mode 100644 compiler/mir/include/mir/OpDefs.h create mode 100644 compiler/mir/include/mir/Operation.h create mode 100644 compiler/mir/include/mir/Operations.inc create mode 100644 compiler/mir/include/mir/Quantization.h create mode 100644 compiler/mir/include/mir/Region.h create mode 100644 compiler/mir/include/mir/Shape.h create mode 100644 compiler/mir/include/mir/ShapeRange.h create mode 100644 compiler/mir/include/mir/Tensor.h create mode 100644 compiler/mir/include/mir/TensorType.h create mode 100644 compiler/mir/include/mir/TensorUtil.h create mode 100644 compiler/mir/include/mir/TensorVariant.h create mode 100644 compiler/mir/include/mir/Visitor.h create mode 100644 compiler/mir/include/mir/ops/AbsOp.h create mode 100644 compiler/mir/include/mir/ops/AddOp.h create mode 100644 compiler/mir/include/mir/ops/AvgPool2DOp.h create mode 100644 compiler/mir/include/mir/ops/BinaryElementwiseOp.h create mode 100644 compiler/mir/include/mir/ops/BroadcastOp.h create mode 100644 compiler/mir/include/mir/ops/CappedReluOp.h create mode 100644 compiler/mir/include/mir/ops/ConcatOp.h create mode 100644 compiler/mir/include/mir/ops/ConstantOp.h create mode 100644 compiler/mir/include/mir/ops/Conv2DOp.h create mode 100644 compiler/mir/include/mir/ops/Deconv2DOp.h create mode 100644 compiler/mir/include/mir/ops/DepthwiseConv2DOp.h create mode 100644 compiler/mir/include/mir/ops/DequantizeOp.h create mode 100644 compiler/mir/include/mir/ops/DivOp.h create mode 100644 compiler/mir/include/mir/ops/EluOp.h create mode 100644 compiler/mir/include/mir/ops/EqualOp.h create mode 100644 compiler/mir/include/mir/ops/FullyConnectedOp.h create mode 100644 compiler/mir/include/mir/ops/GatherOp.h create mode 100644 compiler/mir/include/mir/ops/GreaterOp.h create mode 100644 compiler/mir/include/mir/ops/HardSwishOp.h create mode 100644 compiler/mir/include/mir/ops/InputOp.h create mode 100644 compiler/mir/include/mir/ops/LeakyReluOp.h create mode 100644 compiler/mir/include/mir/ops/LessOp.h create mode 100644 compiler/mir/include/mir/ops/MaxOp.h create mode 100644 compiler/mir/include/mir/ops/MaxPool2DOp.h create mode 100644 compiler/mir/include/mir/ops/MulOp.h create mode 100644 compiler/mir/include/mir/ops/OutputOp.h create mode 100644 compiler/mir/include/mir/ops/PadOp.h create mode 100644 compiler/mir/include/mir/ops/PaddingType.h create mode 100644 compiler/mir/include/mir/ops/QuantizeOp.h create mode 100644 compiler/mir/include/mir/ops/ReduceMeanOp.h create mode 100644 compiler/mir/include/mir/ops/ReduceOp.h create mode 100644 compiler/mir/include/mir/ops/ReluOp.h create mode 100644 compiler/mir/include/mir/ops/ReshapeOp.h create mode 100644 compiler/mir/include/mir/ops/ResizeOp.h create mode 100644 compiler/mir/include/mir/ops/SigmoidOp.h create mode 100644 compiler/mir/include/mir/ops/SliceOp.h create mode 100644 compiler/mir/include/mir/ops/SoftmaxOp.h create mode 100644 compiler/mir/include/mir/ops/SqrtOp.h create mode 100644 compiler/mir/include/mir/ops/SqueezeOp.h create mode 100644 compiler/mir/include/mir/ops/SubOp.h create mode 100644 compiler/mir/include/mir/ops/TanhOp.h create mode 100644 compiler/mir/include/mir/ops/TransposeOp.h create mode 100644 compiler/mir/src/DotGraph.cpp create mode 100644 compiler/mir/src/DotGraph.h create mode 100644 compiler/mir/src/DotNodeBuilder.cpp create mode 100644 compiler/mir/src/DotNodeBuilder.h create mode 100644 compiler/mir/src/Graph.cpp create mode 100644 compiler/mir/src/GraphPatternMatcher.cpp create mode 100644 compiler/mir/src/Index.cpp create mode 100644 compiler/mir/src/IrDotDumper.cpp create mode 100644 compiler/mir/src/Operation.cpp create mode 100644 compiler/mir/src/Shape.cpp create mode 100644 compiler/mir/src/Tensor.cpp create mode 100644 compiler/mir/src/TensorVariant.cpp create mode 100644 compiler/mir/src/Visitor.cpp create mode 100644 compiler/mir/src/ops/AvgPool2DOp.cpp create mode 100644 compiler/mir/src/ops/BinaryElementwiseOp.cpp create mode 100644 compiler/mir/src/ops/BroadcastOp.cpp create mode 100644 compiler/mir/src/ops/ConcatOp.cpp create mode 100644 compiler/mir/src/ops/Conv2DOp.cpp create mode 100644 compiler/mir/src/ops/DeConv2DOp.cpp create mode 100644 compiler/mir/src/ops/DepthwiseConv2DOp.cpp create mode 100644 compiler/mir/src/ops/FullyConnectedOp.cpp create mode 100644 compiler/mir/src/ops/GatherOp.cpp create mode 100644 compiler/mir/src/ops/MaxPool2DOp.cpp create mode 100644 compiler/mir/src/ops/PadOp.cpp create mode 100644 compiler/mir/src/ops/ReduceOp.cpp create mode 100644 compiler/mir/src/ops/SliceOp.cpp create mode 100644 compiler/mir/src/ops/SqueezeOp.cpp create mode 100644 compiler/mir/src/ops/TransposeOp.cpp create mode 100644 compiler/mir/unittests/CMakeLists.txt create mode 100644 compiler/mir/unittests/Index.cpp create mode 100644 compiler/mir/unittests/NodeReplacer.cpp create mode 100644 compiler/mir/unittests/Operation.cpp create mode 100644 compiler/mir/unittests/ShapeInference.cpp create mode 100644 compiler/mir/unittests/ShapeRange.cpp create mode 100644 compiler/mir/unittests/TensorVariant.cpp create mode 100644 compiler/mir2loco/CMakeLists.txt create mode 100644 compiler/mir2loco/include/mir2loco.h create mode 100644 compiler/mir2loco/requires.cmake create mode 100644 compiler/mir2loco/src/mir2loco.cpp create mode 100644 compiler/mir2loco/src/mir2loco.test.cpp create mode 100644 compiler/moco-log/CMakeLists.txt create mode 100644 compiler/moco-log/README.md create mode 100644 compiler/moco-log/include/moco/Log.h create mode 100644 compiler/moco-log/include/moco/LoggingContext.h create mode 100644 compiler/moco-log/requires.cmake create mode 100644 compiler/moco-log/src/Log.cpp create mode 100644 compiler/moco-log/src/LoggingContext.cpp create mode 100644 compiler/moco-tf/CMakeLists.txt create mode 100644 compiler/moco-tf/README.md create mode 100644 compiler/moco-tf/doc/Conversion.md create mode 100644 compiler/moco-tf/include/moco/tf/Frontend.h create mode 100644 compiler/moco-tf/requires.cmake create mode 100644 compiler/moco-tf/src/BroadcastHelper.cpp create mode 100644 compiler/moco-tf/src/BroadcastHelper.h create mode 100644 compiler/moco-tf/src/BroadcastHelper.test.cpp create mode 100644 compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp create mode 100644 compiler/moco-tf/src/CanonicalEltwiseInputConnector.h create mode 100644 compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalizer.cpp create mode 100644 compiler/moco-tf/src/Canonicalizer.h create mode 100644 compiler/moco-tf/src/Canonicalizer.test.cpp create mode 100644 compiler/moco-tf/src/CodecHelper.h create mode 100644 compiler/moco-tf/src/Convert.cpp create mode 100644 compiler/moco-tf/src/Convert.h create mode 100644 compiler/moco-tf/src/Convert.test.cpp create mode 100644 compiler/moco-tf/src/Frontend.cpp create mode 100644 compiler/moco-tf/src/Frontend.test.cpp create mode 100644 compiler/moco-tf/src/Knob.cpp create mode 100644 compiler/moco-tf/src/Knob.h create mode 100644 compiler/moco-tf/src/Knob.lst create mode 100644 compiler/moco-tf/src/LogHelper.cpp create mode 100644 compiler/moco-tf/src/LogHelper.h create mode 100644 compiler/moco-tf/src/Op/COpCall.cpp create mode 100644 compiler/moco-tf/src/Op/COpCall.h create mode 100644 compiler/moco-tf/src/Op/COpCall.test.cpp create mode 100644 compiler/moco-tf/src/Optimizer.cpp create mode 100644 compiler/moco-tf/src/Optimizer.h create mode 100644 compiler/moco-tf/src/Optimizer.test.cpp create mode 100644 compiler/moco-tf/src/ProgressReporter.cpp create mode 100644 compiler/moco-tf/src/ProgressReporter.h create mode 100644 compiler/moco-tf/src/SimpleNodeTransform.h create mode 100644 compiler/moco-tf/src/SimpleNodeTransform.test.cpp create mode 100644 compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h create mode 100644 compiler/moco-tf/src/TFFormattedGraph.cpp create mode 100644 compiler/moco-tf/src/TFFormattedGraph.h create mode 100644 compiler/moco-tf/src/TFOptimizer.cpp create mode 100644 compiler/moco-tf/src/TFOptimizer.h create mode 100644 compiler/moco-tf/src/TFOptimizer.test.cpp create mode 100644 compiler/moco-tf/src/TFReduceCanonicalzeHelper.h create mode 100644 compiler/moco-tf/src/TestHelper.h create mode 100644 compiler/moco-tf/src/TestHelper.test.cpp create mode 100644 compiler/moco-tf/src/Transform.cpp create mode 100644 compiler/moco-tf/src/Transform.h create mode 100644 compiler/moco-tf/src/Transform.test.cpp create mode 100644 compiler/moco-tf/src/Transforms.h create mode 100644 compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp create mode 100644 compiler/moco-tf/src/Transforms/ShapeInferencePass.h create mode 100644 compiler/moco-tf/src/Transforms/TypeInferencePass.cpp create mode 100644 compiler/moco-tf/src/Transforms/TypeInferencePass.h create mode 100644 compiler/moco-value-pbtxt-test/.gitignore create mode 100644 compiler/moco-value-pbtxt-test/CMakeLists.txt create mode 100644 compiler/moco-value-pbtxt-test/README.md create mode 100644 compiler/moco-value-pbtxt-test/requires.cmake create mode 100755 compiler/moco-value-pbtxt-test/runall.sh create mode 100644 compiler/moco-value-pbtxt-test/test.lst create mode 100644 compiler/moco/CMakeLists.txt create mode 100644 compiler/moco/README.md create mode 100644 compiler/moco/import/CMakeLists.txt create mode 100644 compiler/moco/import/README.md create mode 100644 compiler/moco/import/include/moco/GraphHelper.h create mode 100644 compiler/moco/import/include/moco/Import/GraphBuilder.h create mode 100644 compiler/moco/import/include/moco/Import/GraphBuilderContext.h create mode 100644 compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h create mode 100644 compiler/moco/import/include/moco/Import/ModelSignature.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Add.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/AvgPool.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Concat.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Const.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Conv2D.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Identity.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/MaxPool.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Maximum.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Mean.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Mul.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Pack.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Pad.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Placeholder.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/RealDiv.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Relu.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Relu6.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Reshape.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Shape.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Softmax.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Sqrt.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Squeeze.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/StopGradient.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Sub.h create mode 100644 compiler/moco/import/include/moco/Import/Nodes/Tanh.h create mode 100644 compiler/moco/import/include/moco/Importer.h create mode 100644 compiler/moco/import/src/Convert.cpp create mode 100644 compiler/moco/import/src/Convert.h create mode 100644 compiler/moco/import/src/GraphBuilderContext.cpp create mode 100644 compiler/moco/import/src/GraphBuilderContext.test.cpp create mode 100644 compiler/moco/import/src/GraphBuilderRegistry.cpp create mode 100644 compiler/moco/import/src/Importer.cpp create mode 100644 compiler/moco/import/src/Importer.test.cpp create mode 100644 compiler/moco/import/src/ModelSignature.cpp create mode 100644 compiler/moco/import/src/Nodes/Add.cpp create mode 100644 compiler/moco/import/src/Nodes/Add.test.cpp create mode 100644 compiler/moco/import/src/Nodes/AvgPool.cpp create mode 100644 compiler/moco/import/src/Nodes/AvgPool.test.cpp create mode 100644 compiler/moco/import/src/Nodes/BiasAdd.cpp create mode 100644 compiler/moco/import/src/Nodes/BiasAdd.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Concat.cpp create mode 100644 compiler/moco/import/src/Nodes/Concat.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Const.cpp create mode 100644 compiler/moco/import/src/Nodes/Const.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Conv2D.cpp create mode 100644 compiler/moco/import/src/Nodes/Conv2D.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp create mode 100644 compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp create mode 100644 compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp create mode 100644 compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp create mode 100644 compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp create mode 100644 compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp create mode 100644 compiler/moco/import/src/Nodes/FusedBatchNorm.cpp create mode 100644 compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Identity.cpp create mode 100644 compiler/moco/import/src/Nodes/MaxPool.cpp create mode 100644 compiler/moco/import/src/Nodes/MaxPool.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Maximum.cpp create mode 100644 compiler/moco/import/src/Nodes/Maximum.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Mean.cpp create mode 100644 compiler/moco/import/src/Nodes/Mean.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Mul.cpp create mode 100644 compiler/moco/import/src/Nodes/Mul.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Pack.cpp create mode 100644 compiler/moco/import/src/Nodes/Pack.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Pad.cpp create mode 100644 compiler/moco/import/src/Nodes/Pad.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Placeholder.cpp create mode 100644 compiler/moco/import/src/Nodes/Placeholder.test.cpp create mode 100644 compiler/moco/import/src/Nodes/RealDiv.cpp create mode 100644 compiler/moco/import/src/Nodes/RealDiv.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Relu.cpp create mode 100644 compiler/moco/import/src/Nodes/Relu.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Relu6.cpp create mode 100644 compiler/moco/import/src/Nodes/Relu6.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Reshape.cpp create mode 100644 compiler/moco/import/src/Nodes/Reshape.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Rsqrt.cpp create mode 100644 compiler/moco/import/src/Nodes/Rsqrt.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Shape.cpp create mode 100644 compiler/moco/import/src/Nodes/Shape.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Softmax.cpp create mode 100644 compiler/moco/import/src/Nodes/Softmax.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Sqrt.cpp create mode 100644 compiler/moco/import/src/Nodes/Sqrt.test.cpp create mode 100644 compiler/moco/import/src/Nodes/SquaredDifference.cpp create mode 100644 compiler/moco/import/src/Nodes/SquaredDifference.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Squeeze.cpp create mode 100644 compiler/moco/import/src/Nodes/Squeeze.test.cpp create mode 100644 compiler/moco/import/src/Nodes/StopGradient.cpp create mode 100644 compiler/moco/import/src/Nodes/StopGradient.test.cpp create mode 100644 compiler/moco/import/src/Nodes/StridedSlice.cpp create mode 100644 compiler/moco/import/src/Nodes/StridedSlice.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Sub.cpp create mode 100644 compiler/moco/import/src/Nodes/Sub.test.cpp create mode 100644 compiler/moco/import/src/Nodes/Tanh.cpp create mode 100644 compiler/moco/import/src/Nodes/Tanh.test.cpp create mode 100644 compiler/moco/import/src/TestHelper.h create mode 100644 compiler/moco/import/src/TestHelper.test.cpp create mode 100644 compiler/moco/lang/CMakeLists.txt create mode 100644 compiler/moco/lang/README.md create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFConst.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFMean.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFMul.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFPack.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFPad.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFPush.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFShape.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFSub.h create mode 100644 compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h create mode 100644 compiler/moco/lang/include/moco/IR/TFDataLayout.h create mode 100644 compiler/moco/lang/include/moco/IR/TFDialect.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNode.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodeDecl.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodeImpl.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodeVisitor.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodes.h create mode 100644 compiler/moco/lang/include/moco/IR/TFNodes.lst create mode 100644 compiler/moco/lang/include/moco/IR/TFOpcode.h create mode 100644 compiler/moco/lang/include/moco/IR/TFPadding.h create mode 100644 compiler/moco/lang/include/moco/IR/VariadicArityNode.h create mode 100644 compiler/moco/lang/include/moco/Names.h create mode 100644 compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFConst.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp create mode 100644 compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp create mode 100644 compiler/moco/lang/src/IR/TFDialect.cpp create mode 100644 compiler/moco/lang/src/IR/TFDialect.test.cpp create mode 100644 compiler/moco/lang/src/IR/TFNode.cpp create mode 100644 compiler/moco/lang/src/IR/TFNode.test.cpp create mode 100644 compiler/moco/lang/src/IR/VariadicArityNode.test.cpp create mode 100644 compiler/moco/pass/CMakeLists.txt create mode 100644 compiler/moco/pass/README.md create mode 100644 compiler/moco/pass/include/moco/Pass/Passes.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h create mode 100644 compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h create mode 100644 compiler/moco/pass/src/ConstantFoldAdd.test.cpp create mode 100644 compiler/moco/pass/src/ConstantFoldHelper.cpp create mode 100644 compiler/moco/pass/src/ConstantFoldHelper.h create mode 100644 compiler/moco/pass/src/ConstantFoldMul.test.cpp create mode 100644 compiler/moco/pass/src/ConstantFoldPack.test.cpp create mode 100644 compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp create mode 100644 compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp create mode 100644 compiler/moco/pass/src/Passes/ConstantFoldMul.cpp create mode 100644 compiler/moco/pass/src/Passes/ConstantFoldPack.cpp create mode 100644 compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp create mode 100644 compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp create mode 100644 compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp create mode 100644 compiler/moco/pass/src/Passes/ResolveConstantShape.cpp create mode 100644 compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp create mode 100644 compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp create mode 100644 compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp create mode 100644 compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp create mode 100644 compiler/moco/pass/src/TensorPackEnumerator.cpp create mode 100644 compiler/moco/pass/src/TensorPackEnumerator.h create mode 100644 compiler/moco/pass/src/TensorSliceEnumerator.cpp create mode 100644 compiler/moco/pass/src/TensorSliceEnumerator.h create mode 100644 compiler/moco/pass/src/TensorSliceEnumerator.test.cpp create mode 100644 compiler/moco/pass/src/TestHelper.h create mode 100644 compiler/moco/pass/src/TestHelper.test.cpp create mode 100644 compiler/moco/requires.cmake create mode 100644 compiler/moco/service/CMakeLists.txt create mode 100644 compiler/moco/service/README.md create mode 100644 compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h create mode 100644 compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h create mode 100644 compiler/moco/service/src/Service/TFShapeInferenceRule.cpp create mode 100644 compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp create mode 100644 compiler/moco/service/src/Service/TFTypeInferenceRule.cpp create mode 100644 compiler/moco/service/src/TestHelper.h create mode 100644 compiler/moco/service/src/TestHelper.test.cpp create mode 100644 compiler/moco/support/CMakeLists.txt create mode 100644 compiler/moco/support/README.md create mode 100644 compiler/moco/support/include/moco/Support/NodeAs.h create mode 100644 compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h create mode 100644 compiler/moco/support/src/TFShapeInferenceHelper.cpp create mode 100644 compiler/morph/CMakeLists.txt create mode 100644 compiler/morph/README.md create mode 100644 compiler/morph/include/morph/caffe.h create mode 100644 compiler/morph/include/morph/dims.h create mode 100644 compiler/morph/include/morph/nnapi.h create mode 100644 compiler/morph/include/morph/tflite.h create mode 100644 compiler/morph/requires.cmake create mode 100644 compiler/morph/src/caffe.cpp create mode 100644 compiler/morph/src/caffe.test.cpp create mode 100644 compiler/morph/src/dims.cpp create mode 100644 compiler/morph/src/dims.test.cpp create mode 100644 compiler/morph/src/nnapi.cpp create mode 100644 compiler/morph/src/nnapi.test.cpp create mode 100644 compiler/morph/src/tflite.cpp create mode 100644 compiler/morph/src/tflite.test.cpp create mode 100644 compiler/nest/CMakeLists.txt create mode 100644 compiler/nest/README.md create mode 100644 compiler/nest/core/CMakeLists.txt create mode 100644 compiler/nest/core/examples/conv2d.cpp create mode 100644 compiler/nest/core/include/nest/Block.h create mode 100644 compiler/nest/core/include/nest/Bound.h create mode 100644 compiler/nest/core/include/nest/Closure.h create mode 100644 compiler/nest/core/include/nest/Domain.h create mode 100644 compiler/nest/core/include/nest/DomainContext.h create mode 100644 compiler/nest/core/include/nest/DomainID.h create mode 100644 compiler/nest/core/include/nest/DomainInfo.h create mode 100644 compiler/nest/core/include/nest/Expr.h create mode 100644 compiler/nest/core/include/nest/FV.h create mode 100644 compiler/nest/core/include/nest/Level.h create mode 100644 compiler/nest/core/include/nest/Module.h create mode 100644 compiler/nest/core/include/nest/Ret.h create mode 100644 compiler/nest/core/include/nest/Schedule.h create mode 100644 compiler/nest/core/include/nest/Stmt.h create mode 100644 compiler/nest/core/include/nest/Var.h create mode 100644 compiler/nest/core/include/nest/VarContext.h create mode 100644 compiler/nest/core/include/nest/VarID.h create mode 100644 compiler/nest/core/include/nest/expr/AddNode.h create mode 100644 compiler/nest/core/include/nest/expr/DerefNode.h create mode 100644 compiler/nest/core/include/nest/expr/Forward.h create mode 100644 compiler/nest/core/include/nest/expr/Macro.h create mode 100644 compiler/nest/core/include/nest/expr/MulNode.h create mode 100644 compiler/nest/core/include/nest/expr/Node.def create mode 100644 compiler/nest/core/include/nest/expr/Node.h create mode 100644 compiler/nest/core/include/nest/expr/Subscript.h create mode 100644 compiler/nest/core/include/nest/expr/VarNode.h create mode 100644 compiler/nest/core/include/nest/expr/Visitor.h create mode 100644 compiler/nest/core/include/nest/stmt/Forward.h create mode 100644 compiler/nest/core/include/nest/stmt/Macro.h create mode 100644 compiler/nest/core/include/nest/stmt/Node.def create mode 100644 compiler/nest/core/include/nest/stmt/Node.h create mode 100644 compiler/nest/core/include/nest/stmt/PushNode.h create mode 100644 compiler/nest/core/include/nest/stmt/Visitor.h create mode 100644 compiler/nest/core/src/Block.test.cpp create mode 100644 compiler/nest/core/src/Bound.test.cpp create mode 100644 compiler/nest/core/src/Closure.cpp create mode 100644 compiler/nest/core/src/Closure.test.cpp create mode 100644 compiler/nest/core/src/Domain.test.cpp create mode 100644 compiler/nest/core/src/DomainContext.cpp create mode 100644 compiler/nest/core/src/DomainContext.test.cpp create mode 100644 compiler/nest/core/src/DomainID.cpp create mode 100644 compiler/nest/core/src/DomainID.test.cpp create mode 100644 compiler/nest/core/src/DomainInfo.test.cpp create mode 100644 compiler/nest/core/src/Expr.cpp create mode 100644 compiler/nest/core/src/Expr.test.cpp create mode 100644 compiler/nest/core/src/FV.cpp create mode 100644 compiler/nest/core/src/FV.test.cpp create mode 100644 compiler/nest/core/src/Level.cpp create mode 100644 compiler/nest/core/src/Level.test.cpp create mode 100644 compiler/nest/core/src/Module.cpp create mode 100644 compiler/nest/core/src/Module.test.cpp create mode 100644 compiler/nest/core/src/Ret.test.cpp create mode 100644 compiler/nest/core/src/Schedule.cpp create mode 100644 compiler/nest/core/src/Schedule.test.cpp create mode 100644 compiler/nest/core/src/Var.cpp create mode 100644 compiler/nest/core/src/Var.test.cpp create mode 100644 compiler/nest/core/src/VarContext.cpp create mode 100644 compiler/nest/core/src/VarContext.test.cpp create mode 100644 compiler/nest/core/src/VarID.cpp create mode 100644 compiler/nest/core/src/VarID.test.cpp create mode 100644 compiler/nest/core/src/expr/AddNode.test.cpp create mode 100644 compiler/nest/core/src/expr/DerefNode.test.cpp create mode 100644 compiler/nest/core/src/expr/Macro.cpp create mode 100644 compiler/nest/core/src/expr/MulNode.test.cpp create mode 100644 compiler/nest/core/src/expr/Node.cpp create mode 100644 compiler/nest/core/src/expr/Subscript.test.cpp create mode 100644 compiler/nest/core/src/expr/VarNode.test.cpp create mode 100644 compiler/nest/core/src/expr/Visitor.cpp create mode 100644 compiler/nest/core/src/stmt/Macro.cpp create mode 100644 compiler/nest/core/src/stmt/Node.cpp create mode 100644 compiler/nest/core/src/stmt/PushNode.test.cpp create mode 100644 compiler/nest/core/src/stmt/Visitor.cpp create mode 100644 compiler/nike/CMakeLists.txt create mode 100644 compiler/nike/README.md create mode 100644 compiler/nike/include/nike/AbsoluteEpsilonEqual.h create mode 100644 compiler/nike/include/nike/RelativeEpsilonEqual.h create mode 100644 compiler/nike/src/AbsoluteEpsilonEqual.cpp create mode 100644 compiler/nike/src/AbsoluteEpsilonEqual.test.cpp create mode 100644 compiler/nike/src/RelativeEpsilonEqual.cpp create mode 100644 compiler/nike/src/RelativeEpsilonEqual.test.cpp create mode 100644 compiler/nnc/CMakeLists.txt create mode 100644 compiler/nnc/README.md create mode 100644 compiler/nnc/backends/CMakeLists.txt create mode 100644 compiler/nnc/backends/acl_soft_backend/AclArtifactUtilities.in create mode 100644 compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp create mode 100644 compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp create mode 100644 compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.h create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactGeneratorCppCode.cpp create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactGeneratorCppCode.h create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactGeneratorCppDecl.cpp create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactGeneratorCppDecl.h create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactIndent.h create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactModel.cpp create mode 100644 compiler/nnc/backends/acl_soft_backend/ArtifactModel.h create mode 100644 compiler/nnc/backends/acl_soft_backend/CMakeLists.txt create mode 100644 compiler/nnc/backends/acl_soft_backend/IArtifactGenerator.h create mode 100644 compiler/nnc/backends/interpreter/CMakeLists.txt create mode 100644 compiler/nnc/backends/interpreter/InterpreterBackend.cpp create mode 100644 compiler/nnc/backends/soft_backend/CMakeLists.txt create mode 100644 compiler/nnc/backends/soft_backend/CPPGenerator.cpp create mode 100644 compiler/nnc/backends/soft_backend/CommonData.def create mode 100644 compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp create mode 100644 compiler/nnc/backends/soft_backend/ModelAnalyzer.h create mode 100644 compiler/nnc/backends/soft_backend/SBSerializer.cpp create mode 100644 compiler/nnc/backends/soft_backend/SBSerializer.h create mode 100644 compiler/nnc/backends/soft_backend/SequencedIR.cpp create mode 100644 compiler/nnc/backends/soft_backend/SequencedIR.h create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_broadcast.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_capped_relu.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_common_funcs.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_concat.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_conv.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_depthwise_conv.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_elu.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_fully_connected.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_gather.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_header_types.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_leaky_relu.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_pad.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_pool.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_relu.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_resize.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_sigmoid.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_slice.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_softmax.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_sqrt.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_tanh.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/cpp_transpose.def create mode 100644 compiler/nnc/backends/soft_backend/code_snippets/eigen.def create mode 100644 compiler/nnc/cmake/config.cmake create mode 100644 compiler/nnc/cmake/utils.cmake create mode 100644 compiler/nnc/doxygen.config create mode 100644 compiler/nnc/driver/Driver.cpp create mode 100644 compiler/nnc/driver/Driver.h create mode 100644 compiler/nnc/driver/Options.cpp create mode 100644 compiler/nnc/driver/Options.h create mode 100644 compiler/nnc/driver/main.cpp create mode 100644 compiler/nnc/include/Definitions.h.in create mode 100644 compiler/nnc/include/backends/acl_soft_backend/AclCppException.h create mode 100644 compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h create mode 100644 compiler/nnc/include/backends/interpreter/InterpreterBackend.h create mode 100644 compiler/nnc/include/backends/soft_backend/CPPGenerator.h create mode 100644 compiler/nnc/include/pass/Pass.h create mode 100644 compiler/nnc/include/pass/PassData.h create mode 100644 compiler/nnc/include/pass/PassException.h create mode 100644 compiler/nnc/include/pass/PassManager.h create mode 100644 compiler/nnc/include/passes/dot_dumper/DumperPass.h create mode 100644 compiler/nnc/include/passes/optimizations/CombineTransposes.h create mode 100644 compiler/nnc/include/passes/optimizations/ConstantFoldTranspose.h create mode 100644 compiler/nnc/include/passes/optimizations/DeadCodeElimination.h create mode 100644 compiler/nnc/include/passes/optimizations/FuseArithmeticOps.h create mode 100644 compiler/nnc/include/passes/optimizations/OptimizationUtils.h create mode 100644 compiler/nnc/include/passes/optimizations/SinkRelu.h create mode 100644 compiler/nnc/include/passes/optimizations/SinkTranspose.h create mode 100644 compiler/nnc/include/passes/transformations/DataFormatSwitcher.h create mode 100644 compiler/nnc/include/passes/transformations/LowerConv2D.h create mode 100644 compiler/nnc/include/support/CommandLine.h create mode 100644 compiler/nnc/pass/CMakeLists.txt create mode 100644 compiler/nnc/pass/PassManager.cpp create mode 100644 compiler/nnc/passes/CMakeLists.txt create mode 100644 compiler/nnc/passes/dot_dumper/CMakeLists.txt create mode 100644 compiler/nnc/passes/dot_dumper/DumperPass.cpp create mode 100644 compiler/nnc/passes/optimizations/CMakeLists.txt create mode 100644 compiler/nnc/passes/optimizations/CombineTransposes.cpp create mode 100644 compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp create mode 100644 compiler/nnc/passes/optimizations/DeadCodeElimination.cpp create mode 100644 compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp create mode 100644 compiler/nnc/passes/optimizations/OptimizationUtils.cpp create mode 100644 compiler/nnc/passes/optimizations/SinkRelu.cpp create mode 100644 compiler/nnc/passes/optimizations/SinkTranspose.cpp create mode 100644 compiler/nnc/passes/transformations/CMakeLists.txt create mode 100644 compiler/nnc/passes/transformations/DataFormatSwitcher.cpp create mode 100644 compiler/nnc/passes/transformations/LowerConv2D.cpp create mode 100644 compiler/nnc/requires.cmake create mode 100644 compiler/nnc/support/CLOptionChecker.cpp create mode 100644 compiler/nnc/support/CMakeLists.txt create mode 100644 compiler/nnc/support/CommandLine.cpp create mode 100644 compiler/nnc/tests/CMakeLists.txt create mode 100644 compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp create mode 100644 compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in create mode 100644 compiler/nnc/tests/acl_soft_backend/CMakeLists.txt create mode 100644 compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt create mode 100644 compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp create mode 100644 compiler/nnc/tests/acl_soft_backend/artifact_cmake/odroid.cmake create mode 100644 compiler/nnc/tests/acl_soft_backend/models/concatenate.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/convolution.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/convolution_with_bias.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/depthwise_convolution.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/fully_connected.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/pooling_avg.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/pooling_max.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/relu.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/reshape.prototxt create mode 100644 compiler/nnc/tests/acl_soft_backend/models/scale.prototxt create mode 100644 compiler/nnc/tests/import/CMakeLists.txt create mode 100644 compiler/nnc/tests/import/caffe.cpp create mode 100644 compiler/nnc/tests/import/tflite.cpp create mode 100644 compiler/nnc/tests/soft_backend/CMakeLists.txt create mode 100644 compiler/nnc/tests/soft_backend/CompileCPP.cpp create mode 100644 compiler/nnc/tests/soft_backend/test_main.def create mode 100644 compiler/nnc/unittests/CMakeLists.txt create mode 100644 compiler/nnc/unittests/acl_backend/CMakeLists.txt create mode 100644 compiler/nnc/unittests/acl_backend/DOMToText.cpp create mode 100644 compiler/nnc/unittests/acl_backend/MIRToDOM.cpp create mode 100644 compiler/nnc/unittests/caffe_frontend/test_data/unsupported.caffemodel create mode 100644 compiler/nnc/unittests/optimizations/CMakeLists.txt create mode 100644 compiler/nnc/unittests/optimizations/CombineTransposes.cpp create mode 100644 compiler/nnc/unittests/optimizations/DeadCodeElimination.cpp create mode 100644 compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp create mode 100644 compiler/nnc/unittests/optimizations/SinkTest.cpp create mode 100644 compiler/nnc/unittests/optimizations/Util.h create mode 100644 compiler/nnc/unittests/pass/CMakeLists.txt create mode 100644 compiler/nnc/unittests/pass/PassExceptionTest.cpp create mode 100644 compiler/nnc/unittests/pass/PassManagerTest.cpp create mode 100644 compiler/nnc/unittests/soft_backend/CMakeLists.txt create mode 100644 compiler/nnc/unittests/soft_backend/CPPHeaderTypes.cpp create mode 100644 compiler/nnc/unittests/soft_backend/CPPOperations.cpp create mode 100644 compiler/nnc/unittests/soft_backend/Generator.cpp create mode 100644 compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp create mode 100644 compiler/nnc/unittests/support/CMakeLists.txt create mode 100644 compiler/nnc/unittests/support/CommandLineTest.cpp create mode 100644 compiler/nnc/unittests/transformations/CMakeLists.txt create mode 100644 compiler/nnc/unittests/transformations/Switcher.cpp create mode 100644 compiler/nnc/utils/CMakeLists.txt create mode 100644 compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt create mode 100644 compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp create mode 100644 compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt create mode 100644 compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp create mode 100755 compiler/nnc/utils/caffe_model_maker/AllFill.sh create mode 100755 compiler/nnc/utils/caffe_model_maker/Filler.sh create mode 100755 compiler/nnc/utils/caffe_model_maker/GenerateCaffeModels.py create mode 100755 compiler/nnc/utils/caffe_model_maker/Pyloss.py create mode 100644 compiler/nnc/utils/caffe_model_maker/README.md create mode 100644 compiler/nnc/utils/def2src.cpp create mode 100644 compiler/nnc/utils/infer_tests/README.md create mode 100755 compiler/nnc/utils/infer_tests/infer_testcases.py create mode 100755 compiler/nnc/utils/infer_tests/res2bin.py create mode 100644 compiler/nnc/utils/input_gen/CMakeLists.txt create mode 100644 compiler/nnc/utils/input_gen/tensor_gen.cpp create mode 100755 compiler/nnc/utils/model_runner/common_place.py create mode 100755 compiler/nnc/utils/model_runner/model_runner_caffe.py create mode 100755 compiler/nnc/utils/model_runner/model_runner_caffe2.py create mode 100755 compiler/nnc/utils/model_runner/model_runner_onnx.py create mode 100755 compiler/nnc/utils/model_runner/model_runner_tflite.py create mode 100644 compiler/nnc/utils/model_runner/readme.md create mode 100644 compiler/nnc/utils/prepare_inputs/README.md create mode 100755 compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py create mode 100644 compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt create mode 100644 compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp create mode 100644 compiler/nnkit-caffe/CMakeLists.txt create mode 100644 compiler/nnkit-caffe/backend/CMakeLists.txt create mode 100644 compiler/nnkit-caffe/backend/Module.cpp create mode 100644 compiler/nnkit-caffe/requires.cmake create mode 100644 compiler/nnkit-caffe/support/CMakeLists.txt create mode 100644 compiler/nnkit-caffe/support/include/nnkit/support/caffe/Backend.h create mode 100644 compiler/nnkit-caffe/support/include/nnkit/support/caffe/BlobContext.h create mode 100644 compiler/nnkit-caffe/support/include/nnkit/support/caffe/InputBlobContext.h create mode 100644 compiler/nnkit-caffe/support/include/nnkit/support/caffe/OutputBlobContext.h create mode 100644 compiler/nnkit-caffe/support/include/nnkit/support/caffe/TensorContext.h create mode 100644 compiler/nnkit-intf/CMakeLists.txt create mode 100644 compiler/nnkit-intf/README.md create mode 100644 compiler/nnkit-intf/action/CMakeLists.txt create mode 100644 compiler/nnkit-intf/action/include/nnkit/Action.h create mode 100644 compiler/nnkit-intf/backend/CMakeLists.txt create mode 100644 compiler/nnkit-intf/backend/include/nnkit/Backend.h create mode 100644 compiler/nnkit-intf/cmdline/CMakeLists.txt create mode 100644 compiler/nnkit-intf/cmdline/include/nnkit/CmdlineArguments.h create mode 100644 compiler/nnkit-intf/tensor/CMakeLists.txt create mode 100644 compiler/nnkit-intf/tensor/include/nnkit/TensorContext.h create mode 100644 compiler/nnkit-misc/CMakeLists.txt create mode 100644 compiler/nnkit-misc/README.md create mode 100644 compiler/nnkit-misc/backend/CMakeLists.txt create mode 100644 compiler/nnkit-misc/backend/include/nnkit/BackendPlugin.h create mode 100644 compiler/nnkit-misc/backend/src/BackendPlugin.cpp create mode 100644 compiler/nnkit-misc/cmdline/CMakeLists.txt create mode 100644 compiler/nnkit-misc/cmdline/include/nnkit/VectorArguments.h create mode 100644 compiler/nnkit-misc/cmdline/src/VectorArguments.cpp create mode 100644 compiler/nnkit-mocotf/CMakeLists.txt create mode 100644 compiler/nnkit-mocotf/backend/Backend.cpp create mode 100644 compiler/nnkit-mocotf/backend/CMakeLists.txt create mode 100644 compiler/nnkit-mocotf/requires.cmake create mode 100644 compiler/nnkit-mocotf/support/CMakeLists.txt create mode 100644 compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h create mode 100644 compiler/nnkit-mocotf/support/src/Backend.cpp create mode 100644 compiler/nnkit-mocotf/support/src/InputTensorContext.cpp create mode 100644 compiler/nnkit-mocotf/support/src/InputTensorContext.h create mode 100644 compiler/nnkit-mocotf/support/src/OutputTensorContext.cpp create mode 100644 compiler/nnkit-mocotf/support/src/OutputTensorContext.h create mode 100644 compiler/nnkit-mocotf/support/src/TensorContext.h create mode 100644 compiler/nnkit-onnxrt/CMakeLists.txt create mode 100644 compiler/nnkit-onnxrt/backend/Backend.cpp create mode 100644 compiler/nnkit-onnxrt/backend/CMakeLists.txt create mode 100644 compiler/nnkit-onnxrt/requires.cmake create mode 100644 compiler/nnkit-onnxrt/support/CMakeLists.txt create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/Allocator.h create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/Backend.h create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/Runner.h create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/Status.h create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorContext.h create mode 100644 compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorSet.h create mode 100644 compiler/nnkit-onnxrt/support/src/Allocator.cpp create mode 100644 compiler/nnkit-onnxrt/support/src/Backend.cpp create mode 100644 compiler/nnkit-onnxrt/support/src/Runner.cpp create mode 100644 compiler/nnkit-tf/CMakeLists.txt create mode 100644 compiler/nnkit-tf/backend/Backend.cpp create mode 100644 compiler/nnkit-tf/backend/CMakeLists.txt create mode 100644 compiler/nnkit-tf/requires.cmake create mode 100644 compiler/nnkit-tf/support/CMakeLists.txt create mode 100644 compiler/nnkit-tf/support/include/nnkit/support/tf/Backend.h create mode 100644 compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h create mode 100644 compiler/nnkit-tf/support/include/nnkit/support/tf/TensorContext.h create mode 100644 compiler/nnkit-tf/support/include/nnkit/support/tf/TensorDataMap.h create mode 100644 compiler/nnkit-tf/support/src/Backend.cpp create mode 100644 compiler/nnkit-tf/support/src/Runner.cpp create mode 100644 compiler/nnkit-tf/support/src/TensorContext.cpp create mode 100644 compiler/nnkit-tflite/CMakeLists.txt create mode 100644 compiler/nnkit-tflite/backend/Backend.cpp create mode 100644 compiler/nnkit-tflite/backend/CMakeLists.txt create mode 100644 compiler/nnkit-tflite/requires.cmake create mode 100644 compiler/nnkit-tflite/support/CMakeLists.txt create mode 100644 compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h create mode 100644 compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorContext.h create mode 100644 compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h create mode 100644 compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h create mode 100644 compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h create mode 100644 compiler/nnkit-tflite/support/src/Backend.cpp create mode 100644 compiler/nnkit-tflite/support/src/TensorContext.cpp create mode 100644 compiler/nnkit-tflite/support/src/TensorUtils.cpp create mode 100644 compiler/nnkit/CMakeLists.txt create mode 100644 compiler/nnkit/README.md create mode 100644 compiler/nnkit/actions/CMakeLists.txt create mode 100644 compiler/nnkit/actions/HDF5/CMakeLists.txt create mode 100644 compiler/nnkit/actions/HDF5/Common.cpp create mode 100644 compiler/nnkit/actions/HDF5/Common.h create mode 100644 compiler/nnkit/actions/HDF5/Export.cpp create mode 100644 compiler/nnkit/actions/HDF5/Import.cpp create mode 100644 compiler/nnkit/actions/builtin/CMakeLists.txt create mode 100644 compiler/nnkit/actions/builtin/Randomize.cpp create mode 100644 compiler/nnkit/actions/builtin/Show.cpp create mode 100644 compiler/nnkit/requires.cmake create mode 100644 compiler/nnkit/tools/CMakeLists.txt create mode 100644 compiler/nnkit/tools/benchmark/CMakeLists.txt create mode 100644 compiler/nnkit/tools/benchmark/src/Benchmark.cpp create mode 100644 compiler/nnkit/tools/run/CMakeLists.txt create mode 100644 compiler/nnkit/tools/run/nnkit-run.cpp create mode 100644 compiler/nnop/CMakeLists.txt create mode 100644 compiler/nnop/include/nnop/Conv2D.h create mode 100644 compiler/nnop/include/nnop/PadInfo.h create mode 100644 compiler/nnop/include/nnop/StrideInfo.h create mode 100644 compiler/nnop/requires.cmake create mode 100644 compiler/nnop/src/Conv2D.test.cpp create mode 100644 compiler/nnop/src/PadInfo.test.cpp create mode 100644 compiler/nnop/src/StrideInfo.test.cpp create mode 100644 compiler/nnsuite/CMakeLists.txt create mode 100644 compiler/nnsuite/conv/CMakeLists.txt create mode 100644 compiler/nnsuite/conv/model/CMakeLists.txt create mode 100644 compiler/nnsuite/conv/model/include/nnsuite/conv/Model.h create mode 100644 compiler/nnsuite/conv/model/include/nnsuite/conv/RandomModel.h create mode 100644 compiler/nnsuite/conv/model/src/RandomModel.cpp create mode 100644 compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt create mode 100644 compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp create mode 100644 compiler/nnsuite/conv/nnkit-caffe/ConvBackend.h create mode 100644 compiler/nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp create mode 100644 compiler/nnsuite/conv/nnkit-caffe/Entry.cpp create mode 100644 compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt create mode 100644 compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp create mode 100644 compiler/nnsuite/conv/nnkit-tflite/ConvBackend.h create mode 100644 compiler/nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp create mode 100644 compiler/nnsuite/conv/nnkit-tflite/Entry.cpp create mode 100644 compiler/nnsuite/requires.cmake create mode 100644 compiler/oneco-value-pbtxt-test/CMakeLists.txt create mode 100644 compiler/oneco-value-pbtxt-test/Const_000/test.pbtxt create mode 100644 compiler/oneco-value-pbtxt-test/Identity_000/test.pbtxt create mode 100644 compiler/oneco-value-pbtxt-test/requires.cmake create mode 100644 compiler/oneco/CMakeLists.txt create mode 100644 compiler/oneco/include/moco/onnx/Frontend.h create mode 100644 compiler/oneco/proto/CMakeLists.txt create mode 100644 compiler/oneco/requires.cmake create mode 100644 compiler/oneco/src/Convert.cpp create mode 100644 compiler/oneco/src/Convert.h create mode 100644 compiler/oneco/src/Frontend.cpp create mode 100644 compiler/oneco/src/Frontend.test.cpp create mode 100644 compiler/oneco/src/GraphBuilder.h create mode 100644 compiler/oneco/src/GraphBuilderContext.cpp create mode 100644 compiler/oneco/src/GraphBuilderContext.h create mode 100644 compiler/oneco/src/GraphBuilderRegistry.h create mode 100644 compiler/oneco/src/Onnxutil.cpp create mode 100644 compiler/oneco/src/Onnxutil.h create mode 100644 compiler/oneco/src/Op/Constant.cpp create mode 100644 compiler/oneco/src/Op/Constant.h create mode 100644 compiler/oneco/src/Op/Constant_V1.cpp create mode 100644 compiler/oneco/src/Op/Constant_V9.cpp create mode 100644 compiler/oneco/src/Op/Identity.cpp create mode 100644 compiler/oneco/src/Op/Identity.h create mode 100644 compiler/oneco/src/Op/Identity_V1.cpp create mode 100644 compiler/onnx2circle/CMakeLists.txt create mode 100644 compiler/onnx2circle/README.md create mode 100644 compiler/onnx2circle/requires.cmake create mode 100644 compiler/onnx2circle/src/onnx2circle.cpp create mode 100644 compiler/onnx2tflite-integration-test/CMakeLists.txt create mode 100644 compiler/onnx2tflite-integration-test/requires.cmake create mode 100644 compiler/onnx2tflite-integration-test/test.lst create mode 100755 compiler/onnx2tflite-integration-test/testall.sh create mode 100644 compiler/onnx2tflite/CMakeLists.txt create mode 100644 compiler/onnx2tflite/requires.cmake create mode 100644 compiler/onnx2tflite/src/Driver.cpp create mode 100644 compiler/onnxkit/CMakeLists.txt create mode 100644 compiler/onnxkit/README.md create mode 100644 compiler/onnxkit/src/DecodeCommand.cpp create mode 100644 compiler/onnxkit/src/DecodeCommand.hpp create mode 100644 compiler/onnxkit/src/EncodeCommand.cpp create mode 100644 compiler/onnxkit/src/EncodeCommand.hpp create mode 100644 compiler/onnxkit/src/Main.cpp create mode 100644 compiler/onnxkit/src/Support.cpp create mode 100644 compiler/onnxkit/src/Support.hpp create mode 100644 compiler/oops/CMakeLists.txt create mode 100644 compiler/oops/include/oops/InternalExn.h create mode 100644 compiler/oops/include/oops/UserExn.h create mode 100644 compiler/oops/test.cpp create mode 100644 compiler/pepper-assert/CMakeLists.txt create mode 100644 compiler/pepper-assert/include/pepper/assert.h create mode 100644 compiler/pepper-env/CMakeLists.txt create mode 100644 compiler/pepper-env/README.md create mode 100644 compiler/pepper-env/include/pepper/env.h create mode 100644 compiler/pepper-env/src/env.cpp create mode 100644 compiler/pepper-env/src/env.test.cpp create mode 100644 compiler/pepper-str/CMakeLists.txt create mode 100644 compiler/pepper-str/README.md create mode 100644 compiler/pepper-str/include/pepper/str.h create mode 100644 compiler/pepper-str/test.cpp create mode 100644 compiler/pepper-strcast/CMakeLists.txt create mode 100644 compiler/pepper-strcast/README.md create mode 100644 compiler/pepper-strcast/include/pepper/strcast.h create mode 100644 compiler/pepper-strcast/src/strcast.cpp create mode 100644 compiler/pepper-strcast/src/strcast.test.cpp create mode 100644 compiler/plier-tf/CMakeLists.txt create mode 100644 compiler/plier-tf/README.md create mode 100644 compiler/plier-tf/include/plier/tf/Convert.h create mode 100644 compiler/plier-tf/include/plier/tf/TestHelper.h create mode 100644 compiler/plier-tf/requires.cmake create mode 100644 compiler/plier-tf/src/Convert.cpp create mode 100644 compiler/plier-tf/src/Convert.test.cpp create mode 100644 compiler/plier-tf/src/TestHelper.cpp create mode 100644 compiler/pp/CMakeLists.txt create mode 100644 compiler/pp/README.md create mode 100644 compiler/pp/include/pp/EnclosedDocument.h create mode 100644 compiler/pp/include/pp/Format.h create mode 100644 compiler/pp/include/pp/IndentedStringBuilder.h create mode 100644 compiler/pp/include/pp/LinearDocument.h create mode 100644 compiler/pp/include/pp/MultiLineText.h create mode 100644 compiler/pp/include/pp/MultiLineTextUtils.h create mode 100644 compiler/pp/src/EnclosedDocument.cpp create mode 100644 compiler/pp/src/EnclosedDocument.test.cpp create mode 100644 compiler/pp/src/Format.test.cpp create mode 100644 compiler/pp/src/IndentedStringBuilder.cpp create mode 100644 compiler/pp/src/IndentedStringBuilder.test.cpp create mode 100644 compiler/pp/src/LinearDocument.cpp create mode 100644 compiler/pp/src/LinearDocument.test.cpp create mode 100644 compiler/pp/src/MultiLineTextUtils.cpp create mode 100644 compiler/pp/src/MultiLineTextUtils.test.cpp create mode 100644 compiler/safemain/CMakeLists.txt create mode 100644 compiler/safemain/SafeMain.cpp create mode 100644 compiler/stdex/CMakeLists.txt create mode 100644 compiler/stdex/README.md create mode 100644 compiler/stdex/include/stdex/Memory.h create mode 100644 compiler/stdex/include/stdex/Queue.h create mode 100644 compiler/stdex/include/stdex/Set.h create mode 100644 compiler/stdex/src/Memory.test.cpp create mode 100644 compiler/stdex/src/Queue.test.cpp create mode 100644 compiler/stdex/src/Set.test.cpp create mode 100644 compiler/tf2circle-conversion-test/.gitignore create mode 100644 compiler/tf2circle-conversion-test/CMakeLists.txt create mode 100644 compiler/tf2circle-conversion-test/README.md create mode 100644 compiler/tf2circle-conversion-test/requires.cmake create mode 100644 compiler/tf2circle-conversion-test/test.lst create mode 100755 compiler/tf2circle-conversion-test/testall.sh create mode 100644 compiler/tf2circle-dredd-pb-test/.gitignore create mode 100644 compiler/tf2circle-dredd-pb-test/CMakeLists.txt create mode 100644 compiler/tf2circle-dredd-pb-test/README.md create mode 100644 compiler/tf2circle-dredd-pb-test/contrib/.gitignore create mode 100644 compiler/tf2circle-dredd-pb-test/requires.cmake create mode 100755 compiler/tf2circle-dredd-pb-test/runner.sh create mode 100644 compiler/tf2circle-dredd-pbtxt-test/.gitignore create mode 100644 compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt create mode 100644 compiler/tf2circle-dredd-pbtxt-test/README.md create mode 100644 compiler/tf2circle-dredd-pbtxt-test/requires.cmake create mode 100755 compiler/tf2circle-dredd-pbtxt-test/runner.sh create mode 100644 compiler/tf2circle-dredd-pbtxt-test/test.lst create mode 100644 compiler/tf2circle-model-test/.gitignore create mode 100644 compiler/tf2circle-model-test/CMakeLists.txt create mode 100644 compiler/tf2circle-model-test/README.md create mode 100644 compiler/tf2circle-model-test/contrib/.gitignore create mode 100644 compiler/tf2circle-model-test/requires.cmake create mode 100755 compiler/tf2circle-model-test/runner.sh create mode 100644 compiler/tf2circle-ui-check/.gitignore create mode 100644 compiler/tf2circle-ui-check/CMakeLists.txt create mode 100644 compiler/tf2circle-ui-check/README.md create mode 100755 compiler/tf2circle-ui-check/checkall.sh create mode 100644 compiler/tf2circle-ui-check/requires.cmake create mode 100644 compiler/tf2circle-value-pbtxt-remote-test/.gitignore create mode 100644 compiler/tf2circle-value-pbtxt-remote-test/CMakeLists.txt create mode 100644 compiler/tf2circle-value-pbtxt-remote-test/README.md create mode 100644 compiler/tf2circle-value-pbtxt-remote-test/requires.cmake create mode 100755 compiler/tf2circle-value-pbtxt-remote-test/testall.sh create mode 100644 compiler/tf2circle/CMakeLists.txt create mode 100644 compiler/tf2circle/README.md create mode 100644 compiler/tf2circle/proto/CustomOpInfo.proto create mode 100644 compiler/tf2circle/requires.cmake create mode 100644 compiler/tf2circle/src/CustomopConfLoader.cpp create mode 100644 compiler/tf2circle/src/CustomopConfLoader.h create mode 100644 compiler/tf2circle/src/tf2circle.cpp create mode 100644 compiler/tf2nnpkg/CMakeLists.txt create mode 100644 compiler/tf2nnpkg/requires.cmake create mode 100644 compiler/tf2nnpkg/src/filesystem.h create mode 100644 compiler/tf2nnpkg/src/filesystem_common.cpp create mode 100644 compiler/tf2nnpkg/src/filesystem_linux.cpp create mode 100644 compiler/tf2nnpkg/src/filesystem_windows.cpp create mode 100644 compiler/tf2nnpkg/src/tf2nnpkg.cpp create mode 100644 compiler/tf2tflite-dredd-pb-test/.gitignore create mode 100644 compiler/tf2tflite-dredd-pb-test/CMakeLists.txt create mode 100644 compiler/tf2tflite-dredd-pb-test/README.md create mode 100644 compiler/tf2tflite-dredd-pb-test/contrib/.gitignore create mode 100644 compiler/tf2tflite-dredd-pb-test/requires.cmake create mode 100755 compiler/tf2tflite-dredd-pb-test/runner.sh create mode 100644 compiler/tf2tflite-dredd-pbtxt-test/.gitignore create mode 100644 compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt create mode 100644 compiler/tf2tflite-dredd-pbtxt-test/requires.cmake create mode 100755 compiler/tf2tflite-dredd-pbtxt-test/runner.sh create mode 100644 compiler/tf2tflite-dredd-pbtxt-test/test.lst create mode 100644 compiler/tf2tflite-value-pb-test/.gitignore create mode 100644 compiler/tf2tflite-value-pb-test/CMakeLists.txt create mode 100644 compiler/tf2tflite-value-pb-test/README.md create mode 100644 compiler/tf2tflite-value-pb-test/contrib/.gitignore create mode 100644 compiler/tf2tflite-value-pb-test/requires.cmake create mode 100755 compiler/tf2tflite-value-pb-test/runner.sh create mode 100644 compiler/tf2tflite-value-pbtxt-test/.gitignore create mode 100644 compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt create mode 100644 compiler/tf2tflite-value-pbtxt-test/README.md create mode 100644 compiler/tf2tflite-value-pbtxt-test/requires.cmake create mode 100644 compiler/tf2tflite-value-pbtxt-test/test.lst create mode 100755 compiler/tf2tflite-value-pbtxt-test/testall.sh create mode 100644 compiler/tf2tflite/.gitignore create mode 100644 compiler/tf2tflite/CMakeLists.txt create mode 100644 compiler/tf2tflite/README.md create mode 100644 compiler/tf2tflite/proto/CustomOpInfo.proto create mode 100644 compiler/tf2tflite/requires.cmake create mode 100644 compiler/tf2tflite/src/CustomopConfLoader.cpp create mode 100644 compiler/tf2tflite/src/CustomopConfLoader.h create mode 100644 compiler/tf2tflite/src/Driver.cpp create mode 100644 compiler/tf2tfliteV2-value-pbtxt-test/CMakeLists.txt create mode 100644 compiler/tf2tfliteV2-value-pbtxt-test/requirements.txt create mode 100644 compiler/tf2tfliteV2-value-pbtxt-test/requires.cmake create mode 100644 compiler/tf2tfliteV2-value-pbtxt-test/test.lst create mode 100755 compiler/tf2tfliteV2-value-pbtxt-test/testall.sh create mode 100644 compiler/tf2tfliteV2/CMakeLists.txt create mode 100644 compiler/tf2tfliteV2/README.md create mode 100755 compiler/tf2tfliteV2/tf2tfliteV2.py create mode 100644 compiler/tfgraph-xform/CMakeLists.txt create mode 100644 compiler/tfgraph-xform/README.md create mode 100644 compiler/tfinfo-v2/CMakeLists.txt create mode 100644 compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h create mode 100644 compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h create mode 100644 compiler/tfinfo-v2/proto/tfinfo-v2.proto create mode 100644 compiler/tfinfo-v2/requires.cmake create mode 100644 compiler/tfinfo-v2/src/TFInfo_v2.test.cpp create mode 100644 compiler/tfinfo-v2/src/TensorInfoLoader.cpp create mode 100644 compiler/tfinfo-v2/src/TensorSignature.cpp create mode 100644 compiler/tfinfo/CMakeLists.txt create mode 100644 compiler/tfinfo/README.md create mode 100644 compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h create mode 100644 compiler/tfinfo/include/nnkit/support/tftestinfo/TensorInfoParser.h create mode 100644 compiler/tfinfo/requires.cmake create mode 100644 compiler/tfinfo/src/Compat.h create mode 100644 compiler/tfinfo/src/TensorInfoParser.cpp create mode 100644 compiler/tfinfo/src/TensorInfoParser.test.cpp create mode 100644 compiler/tfkit/CMakeLists.txt create mode 100644 compiler/tfkit/README.md create mode 100644 compiler/tfkit/src/ConvertCommand.cpp create mode 100644 compiler/tfkit/src/ConvertCommand.hpp create mode 100644 compiler/tfkit/src/DecodeCommand.cpp create mode 100644 compiler/tfkit/src/DecodeCommand.hpp create mode 100644 compiler/tfkit/src/EncodeCommand.cpp create mode 100644 compiler/tfkit/src/EncodeCommand.hpp create mode 100644 compiler/tfkit/src/Main.cpp create mode 100644 compiler/tfkit/src/PackCommand.cpp create mode 100644 compiler/tfkit/src/PackCommand.hpp create mode 100644 compiler/tfkit/src/Support.cpp create mode 100644 compiler/tfkit/src/Support.hpp create mode 100644 compiler/tfkit/src/UnpackCommand.cpp create mode 100644 compiler/tfkit/src/UnpackCommand.hpp create mode 100644 compiler/tfl-inspect/CMakeLists.txt create mode 100644 compiler/tfl-inspect/README.md create mode 100644 compiler/tfl-inspect/driver/Driver.cpp create mode 100644 compiler/tfl-inspect/requires.cmake create mode 100644 compiler/tfl-inspect/src/Dump.cpp create mode 100644 compiler/tfl-inspect/src/Dump.h create mode 100644 compiler/tfl-inspect/src/Model.cpp create mode 100644 compiler/tfl-inspect/src/Model.h create mode 100644 compiler/tfl-inspect/src/Reader.cpp create mode 100644 compiler/tfl-inspect/src/Reader.h create mode 100644 compiler/tfl-verify/CMakeLists.txt create mode 100644 compiler/tfl-verify/README.md create mode 100644 compiler/tfl-verify/requires.cmake create mode 100644 compiler/tfl-verify/src/Driver.cpp create mode 100644 compiler/tfl-verify/src/Model.cpp create mode 100644 compiler/tfl-verify/src/Model.h create mode 100644 compiler/tfl-verify/src/VerifyFlatBuffers.cpp create mode 100644 compiler/tfl-verify/src/VerifyFlatBuffers.h create mode 100644 compiler/tflchef/CMakeLists.txt create mode 100644 compiler/tflchef/README.md create mode 100644 compiler/tflchef/core/CMakeLists.txt create mode 100644 compiler/tflchef/core/include/tflchef/ModelChef.h create mode 100644 compiler/tflchef/core/src/Arguments.h create mode 100644 compiler/tflchef/core/src/Convert.cpp create mode 100644 compiler/tflchef/core/src/Convert.h create mode 100644 compiler/tflchef/core/src/Data/Constant.h create mode 100644 compiler/tflchef/core/src/Data/Explicit.h create mode 100644 compiler/tflchef/core/src/Data/Gaussian.cpp create mode 100644 compiler/tflchef/core/src/Data/Gaussian.h create mode 100644 compiler/tflchef/core/src/DataChef.def create mode 100644 compiler/tflchef/core/src/DataChef.h create mode 100644 compiler/tflchef/core/src/DataChefs.h create mode 100644 compiler/tflchef/core/src/Dataset.h create mode 100644 compiler/tflchef/core/src/LexicalCast.cpp create mode 100644 compiler/tflchef/core/src/LexicalCast.h create mode 100644 compiler/tflchef/core/src/ModelChef.cpp create mode 100644 compiler/tflchef/core/src/Op/Abs.cpp create mode 100644 compiler/tflchef/core/src/Op/Abs.h create mode 100644 compiler/tflchef/core/src/Op/Add.cpp create mode 100644 compiler/tflchef/core/src/Op/Add.h create mode 100644 compiler/tflchef/core/src/Op/ArgMax.cpp create mode 100644 compiler/tflchef/core/src/Op/ArgMax.h create mode 100644 compiler/tflchef/core/src/Op/AveragePool2D.cpp create mode 100644 compiler/tflchef/core/src/Op/AveragePool2D.h create mode 100644 compiler/tflchef/core/src/Op/BatchToSpaceND.cpp create mode 100644 compiler/tflchef/core/src/Op/BatchToSpaceND.h create mode 100644 compiler/tflchef/core/src/Op/Concatenation.cpp create mode 100644 compiler/tflchef/core/src/Op/Concatenation.h create mode 100644 compiler/tflchef/core/src/Op/Conv2D.cpp create mode 100644 compiler/tflchef/core/src/Op/Conv2D.h create mode 100644 compiler/tflchef/core/src/Op/Cos.cpp create mode 100644 compiler/tflchef/core/src/Op/Cos.h create mode 100644 compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp create mode 100644 compiler/tflchef/core/src/Op/DepthwiseConv2D.h create mode 100644 compiler/tflchef/core/src/Op/Div.cpp create mode 100644 compiler/tflchef/core/src/Op/Div.h create mode 100644 compiler/tflchef/core/src/Op/Equal.cpp create mode 100644 compiler/tflchef/core/src/Op/Equal.h create mode 100644 compiler/tflchef/core/src/Op/Exp.cpp create mode 100644 compiler/tflchef/core/src/Op/Exp.h create mode 100644 compiler/tflchef/core/src/Op/FloorDiv.cpp create mode 100644 compiler/tflchef/core/src/Op/FloorDiv.h create mode 100644 compiler/tflchef/core/src/Op/FullyConnected.cpp create mode 100644 compiler/tflchef/core/src/Op/FullyConnected.h create mode 100644 compiler/tflchef/core/src/Op/LogicalNot.cpp create mode 100644 compiler/tflchef/core/src/Op/LogicalNot.h create mode 100644 compiler/tflchef/core/src/Op/LogicalOr.cpp create mode 100644 compiler/tflchef/core/src/Op/LogicalOr.h create mode 100644 compiler/tflchef/core/src/Op/MaxPool2D.cpp create mode 100644 compiler/tflchef/core/src/Op/MaxPool2D.h create mode 100644 compiler/tflchef/core/src/Op/Mean.cpp create mode 100644 compiler/tflchef/core/src/Op/Mean.h create mode 100644 compiler/tflchef/core/src/Op/Mul.cpp create mode 100644 compiler/tflchef/core/src/Op/Mul.h create mode 100644 compiler/tflchef/core/src/Op/Pack.cpp create mode 100644 compiler/tflchef/core/src/Op/Pack.h create mode 100644 compiler/tflchef/core/src/Op/Pad.cpp create mode 100644 compiler/tflchef/core/src/Op/Pad.h create mode 100644 compiler/tflchef/core/src/Op/ReLU.cpp create mode 100644 compiler/tflchef/core/src/Op/ReLU.h create mode 100644 compiler/tflchef/core/src/Op/ReLU6.cpp create mode 100644 compiler/tflchef/core/src/Op/ReLU6.h create mode 100644 compiler/tflchef/core/src/Op/Reshape.cpp create mode 100644 compiler/tflchef/core/src/Op/Reshape.h create mode 100644 compiler/tflchef/core/src/Op/Rsqrt.cpp create mode 100644 compiler/tflchef/core/src/Op/Rsqrt.h create mode 100644 compiler/tflchef/core/src/Op/Shape.cpp create mode 100644 compiler/tflchef/core/src/Op/Shape.h create mode 100644 compiler/tflchef/core/src/Op/Softmax.cpp create mode 100644 compiler/tflchef/core/src/Op/Softmax.h create mode 100644 compiler/tflchef/core/src/Op/Sqrt.cpp create mode 100644 compiler/tflchef/core/src/Op/Sqrt.h create mode 100644 compiler/tflchef/core/src/Op/Sub.cpp create mode 100644 compiler/tflchef/core/src/Op/Sub.h create mode 100644 compiler/tflchef/core/src/Op/Tanh.cpp create mode 100644 compiler/tflchef/core/src/Op/Tanh.h create mode 100644 compiler/tflchef/core/src/Op/Transpose.cpp create mode 100644 compiler/tflchef/core/src/Op/Transpose.h create mode 100644 compiler/tflchef/core/src/OpChef.def create mode 100644 compiler/tflchef/core/src/OpChef.h create mode 100644 compiler/tflchef/core/src/OpChefs.h create mode 100644 compiler/tflchef/proto/CMakeLists.txt create mode 100644 compiler/tflchef/proto/tflchef.proto create mode 100644 compiler/tflchef/requires.cmake create mode 100644 compiler/tflchef/tests/CMakeLists.txt create mode 100644 compiler/tflchef/tests/explicit_datachef/test.recipe create mode 100644 compiler/tflchef/tests/explicit_datachef/test.reverse create mode 100644 compiler/tflchef/tests/multisubgraph/test.recipe create mode 100644 compiler/tflchef/tests/readme/test.recipe create mode 100644 compiler/tflchef/tests/readme/test.reverse create mode 100755 compiler/tflchef/tests/runall.sh create mode 100755 compiler/tflchef/tests/runvalidate.sh create mode 100644 compiler/tflchef/tflite/CMakeLists.txt create mode 100644 compiler/tflchef/tflite/include/tflchef/RawModel.h create mode 100644 compiler/tflchef/tflite/include/tflchef/RecipeChef.h create mode 100644 compiler/tflchef/tflite/src/Convert.cpp create mode 100644 compiler/tflchef/tflite/src/Convert.h create mode 100644 compiler/tflchef/tflite/src/Op/Abs.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Abs.h create mode 100644 compiler/tflchef/tflite/src/Op/Add.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Add.h create mode 100644 compiler/tflchef/tflite/src/Op/ArgMax.cpp create mode 100644 compiler/tflchef/tflite/src/Op/ArgMax.h create mode 100644 compiler/tflchef/tflite/src/Op/AveragePool2D.cpp create mode 100644 compiler/tflchef/tflite/src/Op/AveragePool2D.h create mode 100644 compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp create mode 100644 compiler/tflchef/tflite/src/Op/BatchToSpaceND.h create mode 100644 compiler/tflchef/tflite/src/Op/Concatenation.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Concatenation.h create mode 100644 compiler/tflchef/tflite/src/Op/Conv2D.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Conv2D.h create mode 100644 compiler/tflchef/tflite/src/Op/Cos.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Cos.h create mode 100644 compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp create mode 100644 compiler/tflchef/tflite/src/Op/DepthwiseConv2D.h create mode 100644 compiler/tflchef/tflite/src/Op/Div.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Div.h create mode 100644 compiler/tflchef/tflite/src/Op/Equal.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Equal.h create mode 100644 compiler/tflchef/tflite/src/Op/Exp.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Exp.h create mode 100644 compiler/tflchef/tflite/src/Op/FloorDiv.cpp create mode 100644 compiler/tflchef/tflite/src/Op/FloorDiv.h create mode 100644 compiler/tflchef/tflite/src/Op/FullyConnected.cpp create mode 100644 compiler/tflchef/tflite/src/Op/FullyConnected.h create mode 100644 compiler/tflchef/tflite/src/Op/LogicalNot.cpp create mode 100644 compiler/tflchef/tflite/src/Op/LogicalNot.h create mode 100644 compiler/tflchef/tflite/src/Op/LogicalOr.cpp create mode 100644 compiler/tflchef/tflite/src/Op/LogicalOr.h create mode 100644 compiler/tflchef/tflite/src/Op/MaxPool2D.cpp create mode 100644 compiler/tflchef/tflite/src/Op/MaxPool2D.h create mode 100644 compiler/tflchef/tflite/src/Op/Mean.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Mean.h create mode 100644 compiler/tflchef/tflite/src/Op/Pack.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Pack.h create mode 100644 compiler/tflchef/tflite/src/Op/Pad.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Pad.h create mode 100644 compiler/tflchef/tflite/src/Op/ReLU.cpp create mode 100644 compiler/tflchef/tflite/src/Op/ReLU.h create mode 100644 compiler/tflchef/tflite/src/Op/ReLU6.cpp create mode 100644 compiler/tflchef/tflite/src/Op/ReLU6.h create mode 100644 compiler/tflchef/tflite/src/Op/Reshape.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Reshape.h create mode 100644 compiler/tflchef/tflite/src/Op/Rsqrt.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Rsqrt.h create mode 100644 compiler/tflchef/tflite/src/Op/Softmax.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Softmax.h create mode 100644 compiler/tflchef/tflite/src/Op/Sqrt.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Sqrt.h create mode 100644 compiler/tflchef/tflite/src/Op/Sub.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Sub.h create mode 100644 compiler/tflchef/tflite/src/Op/Tanh.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Tanh.h create mode 100644 compiler/tflchef/tflite/src/Op/Transpose.cpp create mode 100644 compiler/tflchef/tflite/src/Op/Transpose.h create mode 100644 compiler/tflchef/tflite/src/RawModelLoader.cpp create mode 100644 compiler/tflchef/tflite/src/RecipeChef.cpp create mode 100644 compiler/tflchef/tflite/src/TFliteImport.cpp create mode 100644 compiler/tflchef/tflite/src/TFliteImport.h create mode 100644 compiler/tflchef/tflite/src/TFliteOpChef.h create mode 100644 compiler/tflchef/tflite/src/TFliteOpChefs.h create mode 100644 compiler/tflchef/tflite/src/TFliteOpRegistry.h create mode 100644 compiler/tflchef/tools/CMakeLists.txt create mode 100644 compiler/tflchef/tools/console/CMakeLists.txt create mode 100644 compiler/tflchef/tools/console/Driver.cpp create mode 100644 compiler/tflchef/tools/file/CMakeLists.txt create mode 100644 compiler/tflchef/tools/file/Driver.cpp create mode 100644 compiler/tflchef/tools/reverse/CMakeLists.txt create mode 100644 compiler/tflchef/tools/reverse/Driver.cpp create mode 100644 compiler/tfldump/CMakeLists.txt create mode 100644 compiler/tfldump/README.md create mode 100644 compiler/tfldump/driver/Driver.cpp create mode 100644 compiler/tfldump/include/tfldump/Dump.h create mode 100644 compiler/tfldump/include/tflread/Model.h create mode 100644 compiler/tfldump/requires.cmake create mode 100644 compiler/tfldump/src/Dump.cpp create mode 100644 compiler/tfldump/src/Load.cpp create mode 100644 compiler/tfldump/src/OpPrinter.cpp create mode 100644 compiler/tfldump/src/OpPrinter.h create mode 100644 compiler/tfldump/src/Read.cpp create mode 100644 compiler/tfldump/src/Read.h create mode 100644 compiler/tflite2circle-conversion-test/CMakeLists.txt create mode 100644 compiler/tflite2circle-conversion-test/README.md create mode 100644 compiler/tflite2circle-conversion-test/requires.cmake create mode 100644 compiler/tflite2circle-conversion-test/test.lst create mode 100755 compiler/tflite2circle-conversion-test/testall.sh create mode 100644 compiler/tflite2circle/CMakeLists.txt create mode 100644 compiler/tflite2circle/README.md create mode 100644 compiler/tflite2circle/driver/Driver.cpp create mode 100644 compiler/tflite2circle/include/CircleModel.h create mode 100644 compiler/tflite2circle/include/TFLModel.h create mode 100644 compiler/tflite2circle/requires.cmake create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.h create mode 100644 compiler/tflite2circle/src/CircleModel.cpp create mode 100644 compiler/tflite2circle/src/DataLookup.cpp create mode 100644 compiler/tflite2circle/src/DataLookup.h create mode 100644 compiler/tflite2circle/src/TFLActivationFunctionType.lst create mode 100644 compiler/tflite2circle/src/TFLBuiltinOptions.lst create mode 100644 compiler/tflite2circle/src/TFLModel.cpp create mode 100644 compiler/tflite2circle/src/TFLOperator.lst create mode 100644 compiler/tflite2circle/src/TFLTensorType.lst create mode 100644 compiler/tfts/CMakeLists.txt create mode 100644 compiler/tfts/README.md create mode 100755 compiler/tfts/check_all.sh create mode 100644 compiler/tfts/requires.cmake create mode 100644 compiler/v4tf/README.md create mode 100644 compute/ARMComputeEx/arm_compute/core/CL/kernels/CLGEMMLowpMatrixMultiplyKernelEx.h create mode 100644 compute/ARMComputeEx/arm_compute/core/CL/kernels/CLMultiplyScaleFactorKernel.h create mode 100644 compute/ARMComputeEx/arm_compute/core/CL/kernels/CLQuantizationSymmetricKernel.h create mode 100644 compute/ARMComputeEx/arm_compute/core/CL/kernels/CLScaleFactorSymm8Kernel.h create mode 100644 compute/ARMComputeEx/arm_compute/core/CPP/kernels/CPPOneHotKernelEx.h create mode 100644 compute/ARMComputeEx/arm_compute/core/NEON/kernels/NEActivationLayerKernelEx.h create mode 100644 compute/ARMComputeEx/arm_compute/runtime/CL/functions/CLFullyConnectedHybridLayer.h create mode 100644 compute/ARMComputeEx/arm_compute/runtime/CL/functions/CLFullyConnectedLayerEx.h create mode 100644 compute/ARMComputeEx/arm_compute/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCoreEx.h create mode 100644 compute/ARMComputeEx/arm_compute/runtime/CPP/functions/CPPOneHotEx.h create mode 100644 compute/ARMComputeEx/arm_compute/runtime/NEON/functions/NEActivationLayerEx.h delete mode 100644 compute/ARMComputeEx/arm_compute/runtime/NEON/functions/NEArgMinMax.h mode change 100644 => 100755 compute/ARMComputeEx/resolve_includes.py create mode 100644 compute/ARMComputeEx/src/core/CL/cl_kernels/gemmlowp_ex.cl create mode 100644 compute/ARMComputeEx/src/core/CL/cl_kernels/multiply_scale_factor.cl create mode 100644 compute/ARMComputeEx/src/core/CL/cl_kernels/quantization_symm8.cl create mode 100644 compute/ARMComputeEx/src/core/CL/cl_kernels/scale_factor.cl create mode 100644 compute/ARMComputeEx/src/core/CL/kernels/CLGEMMLowpMatrixMultiplyKernelEx.cpp create mode 100644 compute/ARMComputeEx/src/core/CL/kernels/CLMultiplyScaleFactorKernel.cpp create mode 100644 compute/ARMComputeEx/src/core/CL/kernels/CLQuantizationSymmetricKernel.cpp create mode 100644 compute/ARMComputeEx/src/core/CL/kernels/CLScaleFactorSymm8Kernel.cpp create mode 100644 compute/ARMComputeEx/src/core/CPP/kernels/CPPOneHotKernelEx.cpp create mode 100644 compute/ARMComputeEx/src/core/NEON/kernels/NEActivationLayerKernelEx.cpp create mode 100644 compute/ARMComputeEx/src/runtime/CL/functions/CLFullyConnectedHybridLayer.cpp create mode 100644 compute/ARMComputeEx/src/runtime/CL/functions/CLFullyConnectedLayerEx.cpp create mode 100644 compute/ARMComputeEx/src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCoreEx.cpp create mode 100644 compute/ARMComputeEx/src/runtime/CPP/functions/CPPOneHotEx.cpp create mode 100644 compute/ARMComputeEx/src/runtime/NEON/functions/NEActivationLayerEx.cpp delete mode 100644 compute/ARMComputeEx/src/runtime/NEON/functions/NEArgMinMax.cpp delete mode 100644 compute/ARMComputeEx/src/runtime/NEON/functions/NEElementwiseUnaryLayerEx.cpp create mode 100644 compute/cker/include/cker/NeonTensorUtils.h create mode 100644 compute/cker/include/cker/PortableTensorUtils.h create mode 100644 compute/cker/include/cker/TensorUtils.h create mode 100644 compute/cker/include/cker/eigen/EigenSupport.h create mode 100644 compute/cker/include/cker/eigen/eigen_convolution_helpers.h create mode 100644 compute/cker/include/cker/eigen/eigen_spatial_convolutions-inl.h create mode 100644 compute/cker/include/cker/eigen/eigen_spatial_convolutions.h create mode 100644 compute/cker/include/cker/eigen/eigen_tensor_reduced_instantiations_oss.h delete mode 100644 compute/cker/include/cker/gemmlowp/FixedPoint.h create mode 100644 compute/cker/include/cker/gemmlowp/GEMMSupport.h create mode 100644 compute/cker/include/cker/neon/neon_check.h create mode 100644 compute/cker/include/cker/operation/Common.h create mode 100644 compute/cker/include/cker/operation/Comparison.h create mode 100644 compute/cker/include/cker/operation/Elementwise.h create mode 100644 compute/cker/include/cker/operation/Exp.h create mode 100644 compute/cker/include/cker/operation/MaxMin.h create mode 100644 compute/cker/include/cker/operation/OneHot.h create mode 100644 compute/cker/include/cker/operation/Pack.h create mode 100644 compute/cker/include/cker/operation/Reduce.h create mode 100644 compute/cker/include/cker/operation/Slice.h create mode 100644 compute/cker/include/cker/operation/Split.h create mode 100644 compute/cker/include/cker/operation/StridedSlice.h create mode 100644 compute/cker/include/cker/operation/Tanh.h create mode 100644 compute/cker/include/cker/operation/Transpose.h create mode 100644 compute/cker/include/cker/operation/Unpack.h delete mode 100644 compute/cker/include/cker/operation/optimized/AveragePool.h create mode 100644 compute/cker/include/cker/operation/optimized/BinaryArithmeticOps.h create mode 100644 compute/cker/include/cker/operation/optimized/Conv.h create mode 100644 compute/cker/include/cker/operation/optimized/DepthwiseConvUint8.h delete mode 100644 compute/cker/include/cker/operation/optimized/MaxPool.h create mode 100644 compute/cker/include/cker/operation/optimized/OptimizedUtils.h delete mode 100644 compute/cker/include/cker/operation/optimized/SoftMax.h delete mode 100644 compute/cker/include/cker/operation/reference/AveragePool.h create mode 100644 compute/cker/include/cker/operation/reference/BinaryArithmeticOps.h create mode 100644 compute/cker/include/cker/operation/reference/Conv.h delete mode 100644 compute/cker/include/cker/operation/reference/MaxPool.h delete mode 100644 compute/cker/include/cker/operation/reference/SoftMax.h delete mode 100644 compute/ncnn/CMakeLists.txt delete mode 100644 compute/ncnn/README.md delete mode 100644 compute/ncnn/include/ncnn/layer/binaryop.h delete mode 100644 compute/ncnn/include/ncnn/layer/instance_norm.h delete mode 100644 compute/ncnn/include/ncnn/mat.h delete mode 100644 compute/ncnn/include/ncnn/srcn/conv_type.h delete mode 100644 compute/ncnn/include/ncnn/srcn/srcn_conv.h delete mode 100644 compute/ncnn/src/layer/arm/neon_mathfun.h delete mode 100644 compute/ncnn/src/layer/binaryop.cc delete mode 100644 compute/ncnn/src/layer/instance_norm.cc delete mode 100644 compute/ncnn/src/mat.cc delete mode 100644 compute/ncnn/src/srcn/common.h delete mode 100644 compute/ncnn/src/srcn/conv_sgemm_multithreads.cc delete mode 100644 compute/ncnn/src/srcn/conv_sgemm_multithreads.h delete mode 100644 compute/ncnn/src/srcn/conv_sgemm_singlethread.cc delete mode 100644 compute/ncnn/src/srcn/conv_sgemm_singlethread.h delete mode 100644 compute/ncnn/src/srcn/conv_sparse.cc delete mode 100644 compute/ncnn/src/srcn/conv_sparse.h delete mode 100644 compute/ncnn/src/srcn/conv_winograd.cc delete mode 100644 compute/ncnn/src/srcn/conv_winograd.h delete mode 100644 compute/ncnn/src/srcn/conv_winograd_batch.cc delete mode 100644 compute/ncnn/src/srcn/conv_winograd_batch.h delete mode 100644 compute/ncnn/src/srcn/deconv_sgemm_multithreads.cc delete mode 100644 compute/ncnn/src/srcn/deconv_sgemm_multithreads.h delete mode 100644 compute/ncnn/src/srcn/depthwise_conv.cc delete mode 100644 compute/ncnn/src/srcn/direct_conv_colmajor.cc delete mode 100644 compute/ncnn/src/srcn/direct_conv_colmajor.h delete mode 100644 compute/ncnn/src/srcn/sgemm_kernel.cc delete mode 100644 compute/ncnn/src/srcn/sgemm_kernel.h delete mode 100644 compute/ncnn/src/srcn/sgemm_pack.cc delete mode 100644 compute/ncnn/src/srcn/sgemm_pack.h delete mode 100644 compute/ncnn/src/srcn/sgemm_singlethread.cc delete mode 100644 compute/ncnn/src/srcn/sgemm_singlethread.h delete mode 100644 compute/ncnn/src/srcn/sgemm_test.cc delete mode 100644 compute/ncnn/src/srcn/srcn_conv.cc delete mode 100644 compute/ncnn/src/srcn/winograd.h delete mode 100644 docs/HowToContribute.md delete mode 100644 docs/UseDoxygen.md delete mode 100644 docs/fig/compiler_flow.png delete mode 100644 docs/fig/nnfw_compiler_structure.png delete mode 100644 docs/fig/nnfw_compiler_structure.pptx delete mode 100644 docs/fig/nnfw_components.png delete mode 100644 docs/fig/nnfw_components.pptx delete mode 100644 docs/fig/nnfw_nativeapi_flow.png delete mode 100644 docs/fig/nnfw_nativeapi_flow.pptx delete mode 100644 docs/fig/nnfw_nnapi_flow.png delete mode 100644 docs/fig/nnfw_nnapi_flow.pptx delete mode 100644 docs/fig/nnfw_runtime_behavior.png delete mode 100644 docs/fig/nnfw_runtime_behavior.pptx delete mode 100644 docs/fig/nnfw_runtime_structure.png delete mode 100644 docs/fig/nnfw_runtime_structure.pptx delete mode 100644 docs/fig/runtime_nativeapi_flow.png delete mode 100644 docs/nncc/README.md delete mode 100644 docs/nncc/design.md delete mode 100644 docs/nncc/getting_started.md delete mode 100644 docs/nncc/images/nncc_components.png delete mode 100644 docs/nncc/images/nncc_idef0_a0.png delete mode 100644 docs/nncc/images/nncc_idef0_a1.png delete mode 100644 docs/nncc/images/nncc_idef0_a12.png delete mode 100644 docs/nncc/project/detailed_level_design.md delete mode 100644 docs/nncc/project/development_document.md delete mode 100644 docs/nncc/project/high_level_design.md delete mode 100644 docs/nncc/project/requirements_specification.md delete mode 100644 docs/nncc/project/test_plan.md delete mode 100644 docs/nncc/project_guide.md delete mode 100644 docs/nncc/roadmap.md delete mode 100644 docs/nncc/v1.0.0/getting_started.md delete mode 100644 docs/nncc/v1.0.0/operation-list.md delete mode 100644 docs/nncc/v1.0.0/tutorial.md delete mode 100644 docs/nncc/v1.1.0/nncc_in_tizen_studio.md delete mode 100644 docs/nncc/v1.1.0/nncc_in_visual_studio.md delete mode 100644 docs/nnfw/2018/fig/nnfw_architecture.png delete mode 100644 docs/nnfw/2018/fig/nnfw_architecture.pptx delete mode 100644 docs/nnfw/2018/roadmap.md delete mode 100644 docs/nnfw/HowToImplementOperatorKernel.md delete mode 100644 docs/nnfw/fig/nnfw_architecture.png delete mode 100644 docs/nnfw/fig/nnfw_architecture.pptx delete mode 100644 docs/nnfw/fig/nnfw_behavior.png delete mode 100644 docs/nnfw/fig/nnfw_behavior.pptx delete mode 100644 docs/nnfw/howto.md delete mode 100644 docs/nnfw/howto/BuildTFfromSource.md delete mode 100644 docs/nnfw/howto/CrossBuildForAarch64.md delete mode 100644 docs/nnfw/howto/CrossBuildForAndroid.md delete mode 100644 docs/nnfw/howto/CrossBuildForArm.md delete mode 100644 docs/nnfw/howto/HowToAddUnittest.md delete mode 100644 docs/nnfw/howto/HowToRunNnpackge.md delete mode 100644 docs/nnfw/howto/HowToTestManualy.md delete mode 100644 docs/nnfw/howto/HowToUseDockerImage.md delete mode 100644 docs/nnfw/howto/HowToUseNNFWAPI.md delete mode 100644 docs/nnfw/howto/HowtoMakeSampleAppOnNnfw.md delete mode 100644 docs/nnfw/howto/RemoteDebuggingForVSCode.md delete mode 100644 docs/nnfw/howto/device/xu3-dip.png delete mode 100644 docs/nnfw/howto/device/xu3_tizen.md delete mode 100644 docs/nnfw/howto/device/xu3_ubuntu.md delete mode 100644 docs/nnfw/howto/device/xu4_tizen.md delete mode 100644 docs/nnfw/howto/device/xu4_ubuntu.md delete mode 100644 docs/nnfw/op_list.md delete mode 100644 docs/nnfw/roadmap.md delete mode 100644 docs/nnfw/tests/Convolution_manual_3x3.xlsx delete mode 100644 docs/nnfw/tests/Softmax_manual.xlsx delete mode 100644 docs/release/release_note_1.0.0.md delete mode 100644 docs/release/release_note_1.1.0.md create mode 100644 docs/release/release_note_1.4.0.md create mode 100644 infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfig.cmake create mode 100644 infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfigVersion.cmake create mode 100755 infra/git-hooks/pre-commit.sh create mode 100644 infra/nncc/command/utcount create mode 100644 infra/nnfw/cmake/packages/GEMMLowpConfig.cmake delete mode 100644 infra/nnfw/command/gen-coverage-report create mode 100644 infra/packaging/preset/20200220 create mode 100644 infra/packaging/res/tf2nnpkg.20200220 create mode 100755 infra/scripts/build_android_runtime_release.sh mode change 100644 => 100755 infra/scripts/common.sh create mode 100755 infra/scripts/docker_build_cross_aarch64_runtime.sh delete mode 100755 infra/scripts/docker_build_cross_arm_neurun.sh delete mode 100755 infra/scripts/docker_build_cross_arm_neurun_release.sh create mode 100755 infra/scripts/docker_build_cross_arm_runtime.sh create mode 100755 infra/scripts/docker_build_cross_arm_runtime_release.sh delete mode 100755 infra/scripts/test_arm_neurun_acl_cl.sh delete mode 100755 infra/scripts/test_arm_neurun_acl_neon.sh delete mode 100755 infra/scripts/test_arm_neurun_cpu.sh delete mode 100755 infra/scripts/test_arm_neurun_mixed.sh delete mode 100755 infra/scripts/test_neurun_interp.sh create mode 100755 infra/scripts/test_ubuntu_runtime.sh create mode 100755 infra/scripts/test_ubuntu_runtime_interp.sh create mode 100755 infra/scripts/test_ubuntu_runtime_mixed.sh delete mode 100755 infra/scripts/test_x64_neurun_cpu.sh create mode 100644 nnpackage/schema/circle_schema_v0.fbs create mode 100644 packaging/eigen.tar.gz create mode 100644 packaging/gemmlowp.tar.gz create mode 100644 res/ONNXTests/UNIT_Gemm_000/test.pbtxt create mode 100644 res/ONNXTests/UNIT_Gemm_001/test.pbtxt create mode 100644 res/TensorFlowLiteRecipes/Abs_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Abs_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Add_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Add_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Add_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Add_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_002/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_003/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_003/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_002/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_003/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_U8_003/test.reverse create mode 100644 res/TensorFlowLiteRecipes/AveragePool2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/AveragePool2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/BatchToSpaceND_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/BatchToSpaceND_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Concatenation_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Concatenation_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Concatenation_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Conv2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Conv2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Conv2D_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Conv2D_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Conv2D_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Conv2D_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Cos_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Cos_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/DepthwiseConv2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/DepthwiseConv2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/DepthwiseConv2D_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/DepthwiseConv2D_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Div_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Div_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Equal_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Equal_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Exp_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Exp_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/LogicalNot_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/LogicalNot_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/LogicalOr_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/LogicalOr_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/MaxPool2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/MaxPool2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/MaxPool2D_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Mean_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Mean_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Mul_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Mul_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Pack_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Pack_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Pack_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Pack_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Pad_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Pad_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Pad_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Pad_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quantization_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quantization_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ReLU6_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ReLU6_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ReLU_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ReLU_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Reshape_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Reshape_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Reshape_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Reshape_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Reshape_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Reshape_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Rsqrt_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Rsqrt_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Softmax_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Softmax_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Softmax_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Softmax_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Sqrt_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Sqrt_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Sub_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Sub_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Sub_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Sub_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Sub_U8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Sub_U8_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Transpose_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Transpose_000/test.reverse create mode 100644 res/TensorFlowLiteSchema/1.13.1/schema.fbs create mode 100644 res/TensorFlowLiteSchema/1.14.0/schema.fbs create mode 100644 res/TensorFlowLiteSchema/1.15.2/schema.fbs create mode 100644 res/TensorFlowLiteSchema/2.1.0/schema.fbs create mode 100644 res/TensorFlowLiteSchema/README.md create mode 100644 res/TensorFlowLiteSchema/SCHEMA.lst create mode 100755 res/TensorFlowLiteSchema/download.sh create mode 100644 res/TensorFlowPythonExamples/.gitignore create mode 100644 res/TensorFlowPythonExamples/README.md create mode 100755 res/TensorFlowPythonExamples/examples/abs/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/add/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/argmax/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/biasadd/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/cos/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/div/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/elu/__init__.py create mode 100644 res/TensorFlowPythonExamples/examples/exp/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/floor/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/floordiv/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/greater/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/greater_equal/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/leaky_relu/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/less/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/less_equal/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/logical_not/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/logical_or/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/matmul/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/multiply/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/not_equal/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/pack/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/pad/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/pow/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/prelu/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/relu/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/relu6/__init__.py create mode 100644 res/TensorFlowPythonExamples/examples/reshape/__init.py__ create mode 100755 res/TensorFlowPythonExamples/examples/resize_bilinear/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/resize_nearest_neighbor/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/rsqrt/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/sigmoid/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/softmax/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/sqrt/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/subtract/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/tanh/__init__.py create mode 100755 res/TensorFlowPythonExamples/examples/yuv_to_rgb/__init__.py create mode 100644 res/TensorFlowPythonExamples/requirements.txt create mode 100755 res/TensorFlowPythonExamples/tfpem.py mode change 100644 => 100755 res/TensorFlowTests/NET_0003/test.py mode change 100644 => 100755 res/TensorFlowTests/NET_0004/test.py create mode 100644 res/TensorFlowTests/UNIT_Maximum_000/test.info create mode 100644 res/TensorFlowTests/UNIT_Maximum_000/test.pbtxt create mode 100644 res/TensorFlowTests/UNIT_Maximum_001/test.info create mode 100644 res/TensorFlowTests/UNIT_Maximum_001/test.pbtxt create mode 100644 res/TensorFlowTests/UNIT_Maximum_002/test.info create mode 100644 res/TensorFlowTests/UNIT_Maximum_002/test.pbtxt delete mode 100644 runtime/contrib/README.md delete mode 100644 runtime/contrib/custom_op/customOp-workflow.png create mode 100644 runtime/contrib/heap_trace/src/aligned_alloc_stub.cc create mode 100644 runtime/contrib/heap_trace/src/calloc_stub.cc create mode 100644 runtime/contrib/heap_trace/src/cl_retain_mem_object_stub.cc create mode 100644 runtime/contrib/heap_trace/src/memory_pool_for_symbol_searcher_internals.cc create mode 100644 runtime/contrib/heap_trace/src/memory_pool_for_symbol_searcher_internals.h create mode 100644 runtime/contrib/heap_trace/src/posix_memalign_stub.cc create mode 100644 runtime/contrib/heap_trace/tests/src/aligned_alloc_interception_test.cc create mode 100644 runtime/contrib/heap_trace/tests/src/calloc_interception_test.cc create mode 100644 runtime/contrib/heap_trace/tests/src/cl_retain_mem_object_interception_test.cc create mode 100644 runtime/contrib/heap_trace/tests/src/memory_pool_for_symbol_searcher_internals_test.cc create mode 100644 runtime/contrib/heap_trace/tests/src/posix_memalign_interception_test.cc create mode 100644 runtime/contrib/hi_perf_cpu/CMakeLists.txt create mode 100644 runtime/contrib/hi_perf_cpu/HighPerformanceBackend.test.cc create mode 100644 runtime/contrib/hi_perf_cpu/KernelGenerator.cc create mode 100644 runtime/contrib/hi_perf_cpu/KernelGenerator.h create mode 100644 runtime/contrib/hi_perf_cpu/TensorBuilder.cc create mode 100644 runtime/contrib/hi_perf_cpu/TensorBuilder.h create mode 100644 runtime/contrib/style_transfer_app/CMakeLists.txt create mode 100644 runtime/contrib/style_transfer_app/README.md create mode 100644 runtime/contrib/style_transfer_app/src/args.cc create mode 100644 runtime/contrib/style_transfer_app/src/args.h create mode 100644 runtime/contrib/style_transfer_app/src/bitmap_helper.cc create mode 100644 runtime/contrib/style_transfer_app/src/bitmap_helper.h create mode 100644 runtime/contrib/style_transfer_app/src/jpeg_helper.cc create mode 100644 runtime/contrib/style_transfer_app/src/jpeg_helper.h create mode 100644 runtime/contrib/style_transfer_app/src/style_transfer_app.cc delete mode 100644 runtime/libs/cpp14/CMakeLists.txt delete mode 100644 runtime/libs/cpp14/include/cpp14/memory.h create mode 100644 runtime/libs/jsoncpp/.FORMATDENY delete mode 100644 runtime/libs/tflite/port/1.13.1/include/tflite/ext/kernels/Abs.h delete mode 100644 runtime/libs/tflite/port/1.13.1/include/tflite/ext/kernels/TensorFlowMax.h delete mode 100644 runtime/libs/tflite/port/1.13.1/include/tflite/ext/kernels/TensorFlowSum.h delete mode 100644 runtime/libs/tflite/port/1.13.1/src/kernels/Abs.cpp delete mode 100644 runtime/libs/tflite/port/1.13.1/src/kernels/TensorFlowMax.cpp delete mode 100644 runtime/libs/tflite/port/1.13.1/src/kernels/TensorFlowSum.cpp delete mode 100644 runtime/neurun/CMakeLists.txt delete mode 100644 runtime/neurun/api/CMakeLists.txt delete mode 100644 runtime/neurun/api/include/nnfw.h delete mode 100644 runtime/neurun/api/include/nnfw_debug.h delete mode 100644 runtime/neurun/api/include/nnfw_dev.h delete mode 100644 runtime/neurun/api/src/CustomKernel.cc delete mode 100644 runtime/neurun/api/src/CustomKernel.h delete mode 100644 runtime/neurun/api/src/CustomKernelRegistry.cc delete mode 100644 runtime/neurun/api/src/CustomKernelRegistry.h delete mode 100644 runtime/neurun/api/src/OpMap.lst delete mode 100644 runtime/neurun/api/src/nnfw_api.cc delete mode 100644 runtime/neurun/api/src/nnfw_api_internal.cc delete mode 100644 runtime/neurun/api/src/nnfw_api_internal.h delete mode 100644 runtime/neurun/api/src/nnfw_debug.cc delete mode 100644 runtime/neurun/api/src/nnfw_debug_internal.cc delete mode 100644 runtime/neurun/api/src/nnfw_debug_internal.h delete mode 100644 runtime/neurun/backend/CMakeLists.txt delete mode 100644 runtime/neurun/backend/acl_cl/Backend.h delete mode 100644 runtime/neurun/backend/acl_cl/CLTimer.h delete mode 100644 runtime/neurun/backend/acl_cl/CMakeLists.txt delete mode 100644 runtime/neurun/backend/acl_cl/Config.cc delete mode 100644 runtime/neurun/backend/acl_cl/Config.h delete mode 100644 runtime/neurun/backend/acl_cl/ConstantInitializer.cc delete mode 100644 runtime/neurun/backend/acl_cl/ConstantInitializer.h delete mode 100644 runtime/neurun/backend/acl_cl/KernelGenerator.cc delete mode 100644 runtime/neurun/backend/acl_cl/KernelGenerator.h delete mode 100644 runtime/neurun/backend/acl_cl/PluginClassesAllocator.cc delete mode 100644 runtime/neurun/backend/acl_cl/ShapeFixer.cc delete mode 100644 runtime/neurun/backend/acl_cl/ShapeFixer.h delete mode 100644 runtime/neurun/backend/acl_cl/TensorBuilder.h delete mode 100644 runtime/neurun/backend/acl_cl/TensorManager.h delete mode 100644 runtime/neurun/backend/acl_cl/TensorRegister.h delete mode 100644 runtime/neurun/backend/acl_cl/operand/CLSubTensor.cc delete mode 100644 runtime/neurun/backend/acl_cl/operand/CLSubTensor.h delete mode 100644 runtime/neurun/backend/acl_cl/operand/CLTensor.cc delete mode 100644 runtime/neurun/backend/acl_cl/operand/CLTensor.h delete mode 100644 runtime/neurun/backend/acl_cl/operand/ICLTensor.cc delete mode 100644 runtime/neurun/backend/acl_cl/operand/ICLTensor.h delete mode 100644 runtime/neurun/backend/acl_common/AclFunction.h delete mode 100644 runtime/neurun/backend/acl_common/AclInternalBufferManager.h delete mode 100644 runtime/neurun/backend/acl_common/AclLinearMemoryManager.h delete mode 100644 runtime/neurun/backend/acl_common/AclMemoryManager.h delete mode 100644 runtime/neurun/backend/acl_common/AclTensorManager.h delete mode 100644 runtime/neurun/backend/acl_common/AclTensorRegister.cc delete mode 100644 runtime/neurun/backend/acl_common/AclTensorRegister.h delete mode 100644 runtime/neurun/backend/acl_common/CMakeLists.txt delete mode 100644 runtime/neurun/backend/acl_common/Convert.cc delete mode 100644 runtime/neurun/backend/acl_common/Convert.h delete mode 100644 runtime/neurun/backend/acl_common/IACLTensor.cc delete mode 100644 runtime/neurun/backend/acl_common/IACLTensor.h delete mode 100644 runtime/neurun/backend/acl_common/Swizzle.h delete mode 100644 runtime/neurun/backend/acl_common/TemplTensorBuilder.h delete mode 100644 runtime/neurun/backend/acl_neon/Backend.h delete mode 100644 runtime/neurun/backend/acl_neon/CMakeLists.txt delete mode 100644 runtime/neurun/backend/acl_neon/Config.cc delete mode 100644 runtime/neurun/backend/acl_neon/Config.h delete mode 100644 runtime/neurun/backend/acl_neon/ConstantInitializer.cc delete mode 100644 runtime/neurun/backend/acl_neon/ConstantInitializer.h delete mode 100644 runtime/neurun/backend/acl_neon/KernelGenerator.cc delete mode 100644 runtime/neurun/backend/acl_neon/KernelGenerator.h delete mode 100644 runtime/neurun/backend/acl_neon/PluginClassesAllocator.cc delete mode 100644 runtime/neurun/backend/acl_neon/ShapeFixer.cc delete mode 100644 runtime/neurun/backend/acl_neon/ShapeFixer.h delete mode 100644 runtime/neurun/backend/acl_neon/TensorBuilder.h delete mode 100644 runtime/neurun/backend/acl_neon/TensorManager.h delete mode 100644 runtime/neurun/backend/acl_neon/TensorRegister.cc delete mode 100644 runtime/neurun/backend/acl_neon/TensorRegister.h delete mode 100644 runtime/neurun/backend/acl_neon/operand/INETensor.cc delete mode 100644 runtime/neurun/backend/acl_neon/operand/INETensor.h delete mode 100644 runtime/neurun/backend/acl_neon/operand/NESubTensor.cc delete mode 100644 runtime/neurun/backend/acl_neon/operand/NESubTensor.h delete mode 100644 runtime/neurun/backend/acl_neon/operand/NETensor.cc delete mode 100644 runtime/neurun/backend/acl_neon/operand/NETensor.h delete mode 100644 runtime/neurun/backend/cpu/Backend.h delete mode 100644 runtime/neurun/backend/cpu/CMakeLists.txt delete mode 100644 runtime/neurun/backend/cpu/Config.cc delete mode 100644 runtime/neurun/backend/cpu/Config.h delete mode 100644 runtime/neurun/backend/cpu/ConstantInitializer.cc delete mode 100644 runtime/neurun/backend/cpu/ConstantInitializer.h delete mode 100644 runtime/neurun/backend/cpu/KernelGenerator.cc delete mode 100644 runtime/neurun/backend/cpu/KernelGenerator.h delete mode 100644 runtime/neurun/backend/cpu/MemoryManager.cc delete mode 100644 runtime/neurun/backend/cpu/MemoryManager.h delete mode 100644 runtime/neurun/backend/cpu/PluginClassesAllocator.cc delete mode 100644 runtime/neurun/backend/cpu/ShapeFixer.cc delete mode 100644 runtime/neurun/backend/cpu/ShapeFixer.h delete mode 100644 runtime/neurun/backend/cpu/TensorBuilder.cc delete mode 100644 runtime/neurun/backend/cpu/TensorBuilder.h delete mode 100644 runtime/neurun/backend/cpu/TensorManager.cc delete mode 100644 runtime/neurun/backend/cpu/TensorManager.h delete mode 100644 runtime/neurun/backend/cpu/TensorRegister.cc delete mode 100644 runtime/neurun/backend/cpu/TensorRegister.h delete mode 100644 runtime/neurun/backend/cpu/kernel/AddLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/AddLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/AvgPoolLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/AvgPoolLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/ConcatLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/ConcatLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/ConvolutionLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/ConvolutionLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/DepthwiseConvolutionLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/DepthwiseConvolutionLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/FullyConnectedLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/FullyConnectedLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/GatherLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/GatherLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/LogisticLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/LogisticLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/MaxPoolLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/MaxPoolLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/MulLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/MulLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/OperationUtils.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/OperationUtils.h delete mode 100644 runtime/neurun/backend/cpu/kernel/PadLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/PadLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/PermuteLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/PermuteLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/ReshapeLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/ReshapeLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/SoftMaxLayer.h delete mode 100644 runtime/neurun/backend/cpu/kernel/SubLayer.cc delete mode 100644 runtime/neurun/backend/cpu/kernel/SubLayer.h delete mode 100644 runtime/neurun/backend/cpu/operand/Tensor.cc delete mode 100644 runtime/neurun/backend/cpu/operand/Tensor.h delete mode 100644 runtime/neurun/backend/cpu_common/CMakeLists.txt delete mode 100644 runtime/neurun/backend/cpu_common/MemoryPlanner.cc delete mode 100644 runtime/neurun/backend/cpu_common/MemoryPlanner.h delete mode 100644 runtime/neurun/backend/cpu_common/MemoryPlanner.test.cc delete mode 100644 runtime/neurun/backend/cpu_common/MemoryPlannerFactory.cc delete mode 100644 runtime/neurun/backend/cpu_common/MemoryPlannerFactory.h delete mode 100644 runtime/neurun/backend/hi_perf_cpu/CMakeLists.txt delete mode 100644 runtime/neurun/backend/hi_perf_cpu/HighPerformanceBackend.test.cc delete mode 100644 runtime/neurun/backend/hi_perf_cpu/KernelGenerator.cc delete mode 100644 runtime/neurun/backend/hi_perf_cpu/KernelGenerator.h delete mode 100644 runtime/neurun/backend/hi_perf_cpu/TensorBuilder.cc delete mode 100644 runtime/neurun/backend/hi_perf_cpu/TensorBuilder.h delete mode 100644 runtime/neurun/backend/srcn/Backend.h delete mode 100644 runtime/neurun/backend/srcn/CMakeLists.txt delete mode 100644 runtime/neurun/backend/srcn/Config.cc delete mode 100644 runtime/neurun/backend/srcn/Config.h delete mode 100644 runtime/neurun/backend/srcn/ConstantInitializer.cc delete mode 100644 runtime/neurun/backend/srcn/ConstantInitializer.h delete mode 100644 runtime/neurun/backend/srcn/Convert.cc delete mode 100644 runtime/neurun/backend/srcn/Convert.h delete mode 100644 runtime/neurun/backend/srcn/KernelGenerator.cc delete mode 100644 runtime/neurun/backend/srcn/KernelGenerator.h delete mode 100644 runtime/neurun/backend/srcn/MemoryManager.cc delete mode 100644 runtime/neurun/backend/srcn/MemoryManager.h delete mode 100644 runtime/neurun/backend/srcn/PluginClassesAllocator.cc delete mode 100644 runtime/neurun/backend/srcn/ShapeFixer.cc delete mode 100644 runtime/neurun/backend/srcn/ShapeFixer.h delete mode 100644 runtime/neurun/backend/srcn/Swizzle.h delete mode 100644 runtime/neurun/backend/srcn/TensorBuilder.cc delete mode 100644 runtime/neurun/backend/srcn/TensorBuilder.h delete mode 100644 runtime/neurun/backend/srcn/TensorManager.cc delete mode 100644 runtime/neurun/backend/srcn/TensorManager.h delete mode 100644 runtime/neurun/backend/srcn/TensorRegister.cc delete mode 100644 runtime/neurun/backend/srcn/TensorRegister.h delete mode 100644 runtime/neurun/backend/srcn/kernel/AddLayer.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/AddLayer.h delete mode 100644 runtime/neurun/backend/srcn/kernel/ConvolutionLayer.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/ConvolutionLayer.h delete mode 100644 runtime/neurun/backend/srcn/kernel/DepthwiseConvolutionLayer.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/DepthwiseConvolutionLayer.h delete mode 100644 runtime/neurun/backend/srcn/kernel/InstanceNormLayer.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/InstanceNormLayer.h delete mode 100644 runtime/neurun/backend/srcn/kernel/OperationUtils.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/OperationUtils.h delete mode 100644 runtime/neurun/backend/srcn/kernel/TransposeConvLayer.cc delete mode 100644 runtime/neurun/backend/srcn/kernel/TransposeConvLayer.h delete mode 100644 runtime/neurun/backend/srcn/operand/Tensor.cc delete mode 100644 runtime/neurun/backend/srcn/operand/Tensor.h delete mode 100644 runtime/neurun/core/CMakeLists.txt delete mode 100644 runtime/neurun/core/include/backend/Backend.h delete mode 100644 runtime/neurun/core/include/backend/CustomKernelBuilder.h delete mode 100644 runtime/neurun/core/include/backend/ExecTime.h delete mode 100644 runtime/neurun/core/include/backend/IConfig.h delete mode 100644 runtime/neurun/core/include/backend/IConstantInitializer.h delete mode 100644 runtime/neurun/core/include/backend/IKernelGenerator.h delete mode 100644 runtime/neurun/core/include/backend/IMemoryManager.h delete mode 100644 runtime/neurun/core/include/backend/IShapeFixer.h delete mode 100644 runtime/neurun/core/include/backend/ITensorBuilder.h delete mode 100644 runtime/neurun/core/include/backend/ITensorManager.h delete mode 100644 runtime/neurun/core/include/backend/ITensorRegister.h delete mode 100644 runtime/neurun/core/include/backend/JSONExecTime.h delete mode 100644 runtime/neurun/core/include/backend/operand/ITensor.h delete mode 100644 runtime/neurun/core/include/compiler/Compiler.h delete mode 100644 runtime/neurun/core/include/compiler/IExecutionBuilder.h delete mode 100644 runtime/neurun/core/include/compiler/SubTensorInfo.h delete mode 100644 runtime/neurun/core/include/exec/Execution.h delete mode 100644 runtime/neurun/core/include/exec/ExecutionObservers.h delete mode 100644 runtime/neurun/core/include/exec/IExecutor.h delete mode 100644 runtime/neurun/core/include/exec/IFunction.h delete mode 100644 runtime/neurun/core/include/exec/IODescription.h delete mode 100644 runtime/neurun/core/include/exec/NopFunction.h delete mode 100644 runtime/neurun/core/include/ir/BackendSet.h delete mode 100644 runtime/neurun/core/include/ir/Data.h delete mode 100644 runtime/neurun/core/include/ir/DataType.h delete mode 100644 runtime/neurun/core/include/ir/Graph.h delete mode 100644 runtime/neurun/core/include/ir/Index.h delete mode 100644 runtime/neurun/core/include/ir/InternalType.h delete mode 100644 runtime/neurun/core/include/ir/Layout.h delete mode 100644 runtime/neurun/core/include/ir/LowerInfoMap.h delete mode 100644 runtime/neurun/core/include/ir/OpCode.h delete mode 100644 runtime/neurun/core/include/ir/OpSequence.h delete mode 100644 runtime/neurun/core/include/ir/Operand.h delete mode 100644 runtime/neurun/core/include/ir/OperandConstraint.h delete mode 100644 runtime/neurun/core/include/ir/OperandIndexMap.h delete mode 100644 runtime/neurun/core/include/ir/OperandIndexSequence.h delete mode 100644 runtime/neurun/core/include/ir/OperandInfo.h delete mode 100644 runtime/neurun/core/include/ir/Operands.h delete mode 100644 runtime/neurun/core/include/ir/Operation.h delete mode 100644 runtime/neurun/core/include/ir/OperationIndexList.h delete mode 100644 runtime/neurun/core/include/ir/OperationIndexMap.h delete mode 100644 runtime/neurun/core/include/ir/OperationVisitor.h delete mode 100644 runtime/neurun/core/include/ir/Operations.Include.h delete mode 100644 runtime/neurun/core/include/ir/Operations.h delete mode 100644 runtime/neurun/core/include/ir/Operations.lst delete mode 100644 runtime/neurun/core/include/ir/Shape.h delete mode 100644 runtime/neurun/core/include/ir/Subgraphs.h delete mode 100644 runtime/neurun/core/include/ir/TypeInfo.h delete mode 100644 runtime/neurun/core/include/ir/operand/LowerInfo.h delete mode 100644 runtime/neurun/core/include/ir/operand/ParentInfo.h delete mode 100644 runtime/neurun/core/include/ir/operand/PermuteFactor.h delete mode 100644 runtime/neurun/core/include/ir/operation/Abs.h delete mode 100644 runtime/neurun/core/include/ir/operation/Add.h delete mode 100644 runtime/neurun/core/include/ir/operation/ArgMax.h delete mode 100644 runtime/neurun/core/include/ir/operation/AvgPool2D.h delete mode 100644 runtime/neurun/core/include/ir/operation/BatchToSpaceND.h delete mode 100644 runtime/neurun/core/include/ir/operation/Cast.h delete mode 100644 runtime/neurun/core/include/ir/operation/Comparison.h delete mode 100644 runtime/neurun/core/include/ir/operation/Concat.h delete mode 100644 runtime/neurun/core/include/ir/operation/Conv2D.h delete mode 100644 runtime/neurun/core/include/ir/operation/Custom.h delete mode 100644 runtime/neurun/core/include/ir/operation/DepthToSpace.h delete mode 100644 runtime/neurun/core/include/ir/operation/DepthwiseConv2D.h delete mode 100644 runtime/neurun/core/include/ir/operation/Dequantize.h delete mode 100644 runtime/neurun/core/include/ir/operation/Div.h delete mode 100644 runtime/neurun/core/include/ir/operation/EmbeddingLookup.h delete mode 100644 runtime/neurun/core/include/ir/operation/Exp.h delete mode 100644 runtime/neurun/core/include/ir/operation/Floor.h delete mode 100644 runtime/neurun/core/include/ir/operation/FullyConnected.h delete mode 100644 runtime/neurun/core/include/ir/operation/Gather.h delete mode 100644 runtime/neurun/core/include/ir/operation/HashtableLookup.h delete mode 100644 runtime/neurun/core/include/ir/operation/InstanceNorm.h delete mode 100644 runtime/neurun/core/include/ir/operation/L2Normalization.h delete mode 100644 runtime/neurun/core/include/ir/operation/L2Pool2D.h delete mode 100644 runtime/neurun/core/include/ir/operation/LSTM.h delete mode 100644 runtime/neurun/core/include/ir/operation/LocalResponseNormalization.h delete mode 100644 runtime/neurun/core/include/ir/operation/LogicalAnd.h delete mode 100644 runtime/neurun/core/include/ir/operation/LogicalNot.h delete mode 100644 runtime/neurun/core/include/ir/operation/LogicalOr.h delete mode 100644 runtime/neurun/core/include/ir/operation/Logistic.h delete mode 100644 runtime/neurun/core/include/ir/operation/LowerInfo.h delete mode 100644 runtime/neurun/core/include/ir/operation/Max.h delete mode 100644 runtime/neurun/core/include/ir/operation/MaxPool2D.h delete mode 100644 runtime/neurun/core/include/ir/operation/Mean.h delete mode 100644 runtime/neurun/core/include/ir/operation/Min.h delete mode 100644 runtime/neurun/core/include/ir/operation/Mul.h delete mode 100644 runtime/neurun/core/include/ir/operation/Neg.h delete mode 100644 runtime/neurun/core/include/ir/operation/OneHot.h delete mode 100644 runtime/neurun/core/include/ir/operation/PReLU.h delete mode 100644 runtime/neurun/core/include/ir/operation/Pack.h delete mode 100644 runtime/neurun/core/include/ir/operation/Pad.h delete mode 100644 runtime/neurun/core/include/ir/operation/Permute.h delete mode 100644 runtime/neurun/core/include/ir/operation/RNN.h delete mode 100644 runtime/neurun/core/include/ir/operation/RSQRT.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReLU.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReLU1.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReLU6.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReduceMax.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReduceMin.h delete mode 100644 runtime/neurun/core/include/ir/operation/ReduceSum.h delete mode 100644 runtime/neurun/core/include/ir/operation/Reshape.h delete mode 100644 runtime/neurun/core/include/ir/operation/ResizeBilinear.h delete mode 100644 runtime/neurun/core/include/ir/operation/SQRT.h delete mode 100644 runtime/neurun/core/include/ir/operation/Slice.h delete mode 100644 runtime/neurun/core/include/ir/operation/Softmax.h delete mode 100644 runtime/neurun/core/include/ir/operation/SpaceToBatchND.h delete mode 100644 runtime/neurun/core/include/ir/operation/SpaceToDepth.h delete mode 100644 runtime/neurun/core/include/ir/operation/Split.h delete mode 100644 runtime/neurun/core/include/ir/operation/SquaredDifference.h delete mode 100644 runtime/neurun/core/include/ir/operation/Squeeze.h delete mode 100644 runtime/neurun/core/include/ir/operation/StridedSlice.h delete mode 100644 runtime/neurun/core/include/ir/operation/Sub.h delete mode 100644 runtime/neurun/core/include/ir/operation/Tanh.h delete mode 100644 runtime/neurun/core/include/ir/operation/TopKV2.h delete mode 100644 runtime/neurun/core/include/ir/operation/Transpose.h delete mode 100644 runtime/neurun/core/include/ir/operation/TransposeConv.h delete mode 100644 runtime/neurun/core/include/ir/operation/Unpack.h delete mode 100644 runtime/neurun/core/include/util/Config.lst delete mode 100644 runtime/neurun/core/include/util/ConfigSource.h delete mode 100644 runtime/neurun/core/include/util/Coordinates.h delete mode 100644 runtime/neurun/core/include/util/EnvConfigSource.h delete mode 100644 runtime/neurun/core/include/util/EventCollectorGlobal.h delete mode 100644 runtime/neurun/core/include/util/GeneralConfigSource.h delete mode 100644 runtime/neurun/core/include/util/IConfigSource.h delete mode 100644 runtime/neurun/core/include/util/ITimer.h delete mode 100644 runtime/neurun/core/include/util/Index.h delete mode 100644 runtime/neurun/core/include/util/ObjectManager.h delete mode 100644 runtime/neurun/core/include/util/Padding.h delete mode 100644 runtime/neurun/core/include/util/Set.h delete mode 100644 runtime/neurun/core/include/util/ShapeInference.h delete mode 100644 runtime/neurun/core/include/util/Utils.h delete mode 100644 runtime/neurun/core/include/util/feature/Coordinate4D.h delete mode 100644 runtime/neurun/core/include/util/feature/nchw/Reader.h delete mode 100644 runtime/neurun/core/include/util/feature/nchw/View.h delete mode 100644 runtime/neurun/core/include/util/feature/nhwc/Reader.h delete mode 100644 runtime/neurun/core/include/util/feature/nhwc/View.h delete mode 100644 runtime/neurun/core/include/util/logging.h delete mode 100644 runtime/neurun/core/src/backend/Backend.cc delete mode 100644 runtime/neurun/core/src/backend/BackendManager.cc delete mode 100644 runtime/neurun/core/src/backend/BackendManager.h delete mode 100644 runtime/neurun/core/src/backend/ExecTime.cc delete mode 100644 runtime/neurun/core/src/backend/JSONExecTime.cc delete mode 100644 runtime/neurun/core/src/compiler/BackendResolver.cc delete mode 100644 runtime/neurun/core/src/compiler/BackendResolver.h delete mode 100644 runtime/neurun/core/src/compiler/CodeWithInfo.h delete mode 100644 runtime/neurun/core/src/compiler/Compiler.cc delete mode 100644 runtime/neurun/core/src/compiler/ExecutorFactory.cc delete mode 100644 runtime/neurun/core/src/compiler/ExecutorFactory.h delete mode 100644 runtime/neurun/core/src/compiler/HEScheduler.cc delete mode 100644 runtime/neurun/core/src/compiler/HEScheduler.h delete mode 100644 runtime/neurun/core/src/compiler/IScheduler.h delete mode 100644 runtime/neurun/core/src/compiler/Linear.cc delete mode 100644 runtime/neurun/core/src/compiler/Linear.h delete mode 100644 runtime/neurun/core/src/compiler/ManualScheduler.cc delete mode 100644 runtime/neurun/core/src/compiler/ManualScheduler.h delete mode 100644 runtime/neurun/core/src/compiler/OperandContext.cc delete mode 100644 runtime/neurun/core/src/compiler/OperandContext.h delete mode 100644 runtime/neurun/core/src/compiler/OperationValidator.cc delete mode 100644 runtime/neurun/core/src/compiler/OperationValidator.h delete mode 100644 runtime/neurun/core/src/compiler/ParamChecker.cc delete mode 100644 runtime/neurun/core/src/compiler/ParamChecker.h delete mode 100644 runtime/neurun/core/src/compiler/SubTensorAnalyzer.cc delete mode 100644 runtime/neurun/core/src/compiler/SubTensorAnalyzer.h delete mode 100644 runtime/neurun/core/src/dumper/dot/DotBuilder.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/DotBuilder.h delete mode 100644 runtime/neurun/core/src/dumper/dot/DotDumper.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/DotDumper.h delete mode 100644 runtime/neurun/core/src/dumper/dot/DotSubgraphInfo.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/DotSubgraphInfo.h delete mode 100644 runtime/neurun/core/src/dumper/dot/Node.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/Node.h delete mode 100644 runtime/neurun/core/src/dumper/dot/OperandNode.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/OperandNode.h delete mode 100644 runtime/neurun/core/src/dumper/dot/OperationNode.cc delete mode 100644 runtime/neurun/core/src/dumper/dot/OperationNode.h delete mode 100644 runtime/neurun/core/src/exec/DataflowExecutor.cc delete mode 100644 runtime/neurun/core/src/exec/DataflowExecutor.h delete mode 100644 runtime/neurun/core/src/exec/Execution.cc delete mode 100644 runtime/neurun/core/src/exec/ExecutionObservee.cc delete mode 100644 runtime/neurun/core/src/exec/ExecutionObservee.h delete mode 100644 runtime/neurun/core/src/exec/ExecutionObservers.cc delete mode 100644 runtime/neurun/core/src/exec/ExecutorBase.cc delete mode 100644 runtime/neurun/core/src/exec/ExecutorBase.h delete mode 100644 runtime/neurun/core/src/exec/FunctionSequence.cc delete mode 100644 runtime/neurun/core/src/exec/FunctionSequence.h delete mode 100644 runtime/neurun/core/src/exec/Job.cc delete mode 100644 runtime/neurun/core/src/exec/Job.h delete mode 100644 runtime/neurun/core/src/exec/LinearExecutor.cc delete mode 100644 runtime/neurun/core/src/exec/LinearExecutor.h delete mode 100644 runtime/neurun/core/src/exec/ParallelExecutor.cc delete mode 100644 runtime/neurun/core/src/exec/ParallelExecutor.h delete mode 100644 runtime/neurun/core/src/exec/ParallelScheduler.cc delete mode 100644 runtime/neurun/core/src/exec/ParallelScheduler.h delete mode 100644 runtime/neurun/core/src/exec/Sink.h delete mode 100644 runtime/neurun/core/src/exec/Source.h delete mode 100644 runtime/neurun/core/src/exec/ThreadPool.cc delete mode 100644 runtime/neurun/core/src/exec/ThreadPool.h delete mode 100644 runtime/neurun/core/src/exec/WorkQueue.cc delete mode 100644 runtime/neurun/core/src/exec/WorkQueue.h delete mode 100644 runtime/neurun/core/src/exec/interp/Buffer.h delete mode 100644 runtime/neurun/core/src/exec/interp/ExecEnv.h delete mode 100644 runtime/neurun/core/src/exec/interp/ExecManager.cc delete mode 100644 runtime/neurun/core/src/exec/interp/ExecManager.h delete mode 100644 runtime/neurun/core/src/exec/interp/Interpreter.cc delete mode 100644 runtime/neurun/core/src/exec/interp/Interpreter.h delete mode 100644 runtime/neurun/core/src/exec/interp/Registration.h delete mode 100644 runtime/neurun/core/src/exec/interp/Tensor.cc delete mode 100644 runtime/neurun/core/src/exec/interp/Tensor.h delete mode 100644 runtime/neurun/core/src/exec/interp/operations/AvgPool2D.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/BinaryArithmeticOps.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Concat.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Conv2D.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/DepthwiseConv.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/FullyConnected.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Gather.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/InstanceNorm.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Logistic.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/MaxPool2D.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/OperationUtil.h delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Pad.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/Reshape.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/SoftMax.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/TransposeConv.cc delete mode 100644 runtime/neurun/core/src/exec/interp/operations/UnaryActivations.cc delete mode 100644 runtime/neurun/core/src/ir/Graph.cc delete mode 100644 runtime/neurun/core/src/ir/GraphIterator.cc delete mode 100644 runtime/neurun/core/src/ir/GraphIterator.h delete mode 100644 runtime/neurun/core/src/ir/LayoutSet.cc delete mode 100644 runtime/neurun/core/src/ir/LayoutSet.h delete mode 100644 runtime/neurun/core/src/ir/OpCode.cc delete mode 100644 runtime/neurun/core/src/ir/OpSequence.cc delete mode 100644 runtime/neurun/core/src/ir/Operand.cc delete mode 100644 runtime/neurun/core/src/ir/OperandIndexSequence.cc delete mode 100644 runtime/neurun/core/src/ir/Operation.cc delete mode 100644 runtime/neurun/core/src/ir/OperationIndexList.cc delete mode 100644 runtime/neurun/core/src/ir/Shape.cc delete mode 100644 runtime/neurun/core/src/ir/Subgraphs.cc delete mode 100644 runtime/neurun/core/src/ir/TypeInfo.cc delete mode 100644 runtime/neurun/core/src/ir/dumper/Dumper.cc delete mode 100644 runtime/neurun/core/src/ir/dumper/Dumper.h delete mode 100644 runtime/neurun/core/src/ir/operand/Shape4DConvert.h delete mode 100644 runtime/neurun/core/src/ir/operation/Abs.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Add.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ArgMax.cc delete mode 100644 runtime/neurun/core/src/ir/operation/AvgPool2D.cc delete mode 100644 runtime/neurun/core/src/ir/operation/BatchToSpaceND.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Cast.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Comparison.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Concat.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Conv2D.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Custom.cc delete mode 100644 runtime/neurun/core/src/ir/operation/DepthToSpace.cc delete mode 100644 runtime/neurun/core/src/ir/operation/DepthwiseConv2D.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Dequantize.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Div.cc delete mode 100644 runtime/neurun/core/src/ir/operation/EmbeddingLookup.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Exp.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Floor.cc delete mode 100644 runtime/neurun/core/src/ir/operation/FullyConnected.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Gather.cc delete mode 100644 runtime/neurun/core/src/ir/operation/HashtableLookup.cc delete mode 100644 runtime/neurun/core/src/ir/operation/InstanceNorm.cc delete mode 100644 runtime/neurun/core/src/ir/operation/L2Normalization.cc delete mode 100644 runtime/neurun/core/src/ir/operation/L2Pool2D.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LSTM.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LocalResponseNormalization.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LogicalAnd.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LogicalNot.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LogicalOr.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Logistic.cc delete mode 100644 runtime/neurun/core/src/ir/operation/LowerInfo.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Max.cc delete mode 100644 runtime/neurun/core/src/ir/operation/MaxPool2D.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Mean.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Min.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Mul.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Neg.cc delete mode 100644 runtime/neurun/core/src/ir/operation/OneHot.cc delete mode 100644 runtime/neurun/core/src/ir/operation/PReLU.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Pack.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Pad.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Permute.cc delete mode 100644 runtime/neurun/core/src/ir/operation/RNN.cc delete mode 100644 runtime/neurun/core/src/ir/operation/RSQRT.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReLU.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReLU1.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReLU6.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReduceMax.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReduceMin.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ReduceSum.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Reshape.cc delete mode 100644 runtime/neurun/core/src/ir/operation/ResizeBilinear.cc delete mode 100644 runtime/neurun/core/src/ir/operation/SQRT.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Slice.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Softmax.cc delete mode 100644 runtime/neurun/core/src/ir/operation/SpaceToBatchND.cc delete mode 100644 runtime/neurun/core/src/ir/operation/SpaceToDepth.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Split.cc delete mode 100644 runtime/neurun/core/src/ir/operation/SquaredDifference.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Squeeze.cc delete mode 100644 runtime/neurun/core/src/ir/operation/StridedSlice.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Sub.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Tanh.cc delete mode 100644 runtime/neurun/core/src/ir/operation/TopKV2.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Transpose.cc delete mode 100644 runtime/neurun/core/src/ir/operation/TransposeConv.cc delete mode 100644 runtime/neurun/core/src/ir/operation/Unpack.cc delete mode 100644 runtime/neurun/core/src/ir/pass/ConstantInsertionPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/ConstantInsertionPass.h delete mode 100644 runtime/neurun/core/src/ir/pass/OperandPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/OperandPass.h delete mode 100644 runtime/neurun/core/src/ir/pass/OperationPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/OperationPass.h delete mode 100644 runtime/neurun/core/src/ir/pass/Pass.h delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationEliminationPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationEliminationPass.h delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationInsertionPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationInsertionPass.h delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationOperationPass.cc delete mode 100644 runtime/neurun/core/src/ir/pass/PermutationOperationPass.h delete mode 100644 runtime/neurun/core/src/ir/verifier/Verifier.cc delete mode 100644 runtime/neurun/core/src/ir/verifier/Verifier.h delete mode 100644 runtime/neurun/core/src/library_info.cc delete mode 100644 runtime/neurun/core/src/util/ConfigSource.cc delete mode 100644 runtime/neurun/core/src/util/EnvConfigSource.cc delete mode 100644 runtime/neurun/core/src/util/EventCollectorGlobal.cc delete mode 100644 runtime/neurun/core/src/util/GeneralConfigSource.cc delete mode 100644 runtime/neurun/core/src/util/Padding.cc delete mode 100644 runtime/neurun/core/src/util/ShapeInference.cc delete mode 100644 runtime/neurun/core/src/util/Utils.cc delete mode 100644 runtime/neurun/core/src/util/logging.cc delete mode 100644 runtime/neurun/frontend/CMakeLists.txt delete mode 100644 runtime/neurun/frontend/base_loader/CMakeLists.txt delete mode 100644 runtime/neurun/frontend/base_loader/include/base_loader.h delete mode 100644 runtime/neurun/frontend/circle/CMakeLists.txt delete mode 100644 runtime/neurun/frontend/circle/include/circle_loader.h delete mode 100644 runtime/neurun/frontend/circle/src/circle_loader.cc delete mode 100644 runtime/neurun/frontend/circle/src/circle_schema_generated.h delete mode 100644 runtime/neurun/frontend/nnapi/ANeuralNetworksModel.test.cc delete mode 100644 runtime/neurun/frontend/nnapi/CMakeLists.txt delete mode 100644 runtime/neurun/frontend/nnapi/compilation.cc delete mode 100644 runtime/neurun/frontend/nnapi/event.cc delete mode 100644 runtime/neurun/frontend/nnapi/execution.cc delete mode 100644 runtime/neurun/frontend/nnapi/memory.cc delete mode 100644 runtime/neurun/frontend/nnapi/model.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksEvent.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksExecution.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksMemory.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksModel.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/ANeuralNetworksModel.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/NNAPIConvert.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/NNAPIConvert.h delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/OperationFactory.cc delete mode 100644 runtime/neurun/frontend/nnapi/wrapper/OperationFactory.h delete mode 100644 runtime/neurun/frontend/tflite/CMakeLists.txt delete mode 100644 runtime/neurun/frontend/tflite/include/tflite_loader.h delete mode 100644 runtime/neurun/frontend/tflite/src/tflite_loader.cc delete mode 100644 runtime/neurun/frontend/tflite/src/tflite_schema_generated.h delete mode 100644 runtime/neurun/frontend/tflite/tflite_schema.fbs delete mode 100644 runtime/neurun/sample/CMakeLists.txt delete mode 100644 runtime/neurun/sample/minimal/CMakeLists.txt delete mode 100644 runtime/neurun/sample/minimal/README.md delete mode 100644 runtime/neurun/sample/minimal/src/minimal.cc delete mode 100644 runtime/neurun/test/CMakeLists.txt delete mode 100644 runtime/neurun/test/core/backend/ExecTime.test.cc delete mode 100644 runtime/neurun/test/core/compiler/Scheduler.cc delete mode 100644 runtime/neurun/test/core/exec/ExecInstance.cc delete mode 100644 runtime/neurun/test/core/exec/interp/ExecManager.cc delete mode 100644 runtime/neurun/test/graph/Graph.cc delete mode 100644 runtime/neurun/test/graph/Index.cc delete mode 100644 runtime/neurun/test/graph/MockNode.h delete mode 100644 runtime/neurun/test/graph/operand/IndexSet.cc delete mode 100644 runtime/neurun/test/graph/operand/LayoutSet.cc delete mode 100644 runtime/neurun/test/graph/operand/Set.cc delete mode 100644 runtime/neurun/test/graph/operand/UseDef.cc delete mode 100644 runtime/neurun/test/graph/operation/Set.cc delete mode 100644 runtime/neurun/test/graph/operation/SetIO.cc delete mode 100644 runtime/neurun/test/graph/verifier/Verifier.cc delete mode 100644 runtime/neurun/test/util/ShapeInference.cc create mode 100644 runtime/onert/CMakeLists.txt create mode 100644 runtime/onert/api/CMakeLists.txt create mode 100644 runtime/onert/api/include/nnfw.h create mode 100644 runtime/onert/api/include/nnfw_debug.h create mode 100644 runtime/onert/api/include/nnfw_dev.h create mode 100644 runtime/onert/api/include/nnfw_version.h create mode 100644 runtime/onert/api/src/CustomKernel.cc create mode 100644 runtime/onert/api/src/CustomKernel.h create mode 100644 runtime/onert/api/src/CustomKernelRegistry.cc create mode 100644 runtime/onert/api/src/CustomKernelRegistry.h create mode 100644 runtime/onert/api/src/OpMap.lst create mode 100644 runtime/onert/api/src/nnfw_api.cc create mode 100644 runtime/onert/api/src/nnfw_api_internal.cc create mode 100644 runtime/onert/api/src/nnfw_api_internal.h create mode 100644 runtime/onert/api/src/nnfw_debug.cc create mode 100644 runtime/onert/api/src/nnfw_debug_internal.cc create mode 100644 runtime/onert/api/src/nnfw_debug_internal.h create mode 100644 runtime/onert/backend/CMakeLists.txt create mode 100644 runtime/onert/backend/acl_cl/Backend.h create mode 100644 runtime/onert/backend/acl_cl/CLTimer.h create mode 100644 runtime/onert/backend/acl_cl/CMakeLists.txt create mode 100644 runtime/onert/backend/acl_cl/Config.cc create mode 100644 runtime/onert/backend/acl_cl/Config.h create mode 100644 runtime/onert/backend/acl_cl/ConstantInitializer.cc create mode 100644 runtime/onert/backend/acl_cl/ConstantInitializer.h create mode 100644 runtime/onert/backend/acl_cl/KernelGenerator.cc create mode 100644 runtime/onert/backend/acl_cl/KernelGenerator.h create mode 100644 runtime/onert/backend/acl_cl/Optimizer.cc create mode 100644 runtime/onert/backend/acl_cl/Optimizer.h create mode 100644 runtime/onert/backend/acl_cl/ShapeFixer.cc create mode 100644 runtime/onert/backend/acl_cl/ShapeFixer.h create mode 100644 runtime/onert/backend/acl_cl/TensorBuilder.h create mode 100644 runtime/onert/backend/acl_cl/TensorManager.h create mode 100644 runtime/onert/backend/acl_cl/acl_cl.cc create mode 100644 runtime/onert/backend/acl_cl/operand/CLSubTensor.cc create mode 100644 runtime/onert/backend/acl_cl/operand/CLSubTensor.h create mode 100644 runtime/onert/backend/acl_cl/operand/CLTensor.cc create mode 100644 runtime/onert/backend/acl_cl/operand/CLTensor.h create mode 100644 runtime/onert/backend/acl_cl/operand/ICLTensor.cc create mode 100644 runtime/onert/backend/acl_cl/operand/ICLTensor.h create mode 100644 runtime/onert/backend/acl_common/AclActivationBuilder.h create mode 100644 runtime/onert/backend/acl_common/AclFunction.h create mode 100644 runtime/onert/backend/acl_common/AclInternalBufferManager.h create mode 100644 runtime/onert/backend/acl_common/AclLinearMemoryManager.h create mode 100644 runtime/onert/backend/acl_common/AclMemoryManager.h create mode 100644 runtime/onert/backend/acl_common/AclSubTensorAnalyzer.h create mode 100644 runtime/onert/backend/acl_common/AclTensorBuilder.h create mode 100644 runtime/onert/backend/acl_common/AclTensorManager.h create mode 100644 runtime/onert/backend/acl_common/CMakeLists.txt create mode 100644 runtime/onert/backend/acl_common/Convert.cc create mode 100644 runtime/onert/backend/acl_common/Convert.h create mode 100644 runtime/onert/backend/acl_common/IACLTensor.cc create mode 100644 runtime/onert/backend/acl_common/IACLTensor.h create mode 100644 runtime/onert/backend/acl_common/ParentInfo.h create mode 100644 runtime/onert/backend/acl_common/Swizzle.h create mode 100644 runtime/onert/backend/acl_neon/Backend.h create mode 100644 runtime/onert/backend/acl_neon/CMakeLists.txt create mode 100644 runtime/onert/backend/acl_neon/Config.cc create mode 100644 runtime/onert/backend/acl_neon/Config.h create mode 100644 runtime/onert/backend/acl_neon/ConstantInitializer.cc create mode 100644 runtime/onert/backend/acl_neon/ConstantInitializer.h create mode 100644 runtime/onert/backend/acl_neon/KernelGenerator.cc create mode 100644 runtime/onert/backend/acl_neon/KernelGenerator.h create mode 100644 runtime/onert/backend/acl_neon/Optimizer.cc create mode 100644 runtime/onert/backend/acl_neon/Optimizer.h create mode 100644 runtime/onert/backend/acl_neon/ShapeFixer.cc create mode 100644 runtime/onert/backend/acl_neon/ShapeFixer.h create mode 100644 runtime/onert/backend/acl_neon/TensorBuilder.h create mode 100644 runtime/onert/backend/acl_neon/TensorManager.h create mode 100644 runtime/onert/backend/acl_neon/acl_neon.cc create mode 100644 runtime/onert/backend/acl_neon/operand/INETensor.cc create mode 100644 runtime/onert/backend/acl_neon/operand/INETensor.h create mode 100644 runtime/onert/backend/acl_neon/operand/NESubTensor.cc create mode 100644 runtime/onert/backend/acl_neon/operand/NESubTensor.h create mode 100644 runtime/onert/backend/acl_neon/operand/NETensor.cc create mode 100644 runtime/onert/backend/acl_neon/operand/NETensor.h create mode 100644 runtime/onert/backend/cpu/Backend.h create mode 100644 runtime/onert/backend/cpu/CMakeLists.txt create mode 100644 runtime/onert/backend/cpu/Config.cc create mode 100644 runtime/onert/backend/cpu/Config.h create mode 100644 runtime/onert/backend/cpu/ConstantInitializer.cc create mode 100644 runtime/onert/backend/cpu/ConstantInitializer.h create mode 100644 runtime/onert/backend/cpu/KernelGenerator.cc create mode 100644 runtime/onert/backend/cpu/KernelGenerator.h create mode 100644 runtime/onert/backend/cpu/ShapeFixer.cc create mode 100644 runtime/onert/backend/cpu/ShapeFixer.h create mode 100644 runtime/onert/backend/cpu/TensorBuilder.cc create mode 100644 runtime/onert/backend/cpu/TensorBuilder.h create mode 100644 runtime/onert/backend/cpu/TensorManager.cc create mode 100644 runtime/onert/backend/cpu/TensorManager.h create mode 100644 runtime/onert/backend/cpu/cpu.cc create mode 100644 runtime/onert/backend/cpu/kernel/AbsLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/AbsLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/AddLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/AddLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/AvgPoolLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/AvgPoolLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/CastLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/CastLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/CompareLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/CompareLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ConcatLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ConcatLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ConvolutionLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ConvolutionLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/DepthwiseConvolutionLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/DepthwiseConvolutionLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/DivLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/DivLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ExpLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ExpLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/FullyConnectedLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/FullyConnectedLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/GatherLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/GatherLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/LogisticLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/LogisticLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/MaxLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/MaxLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/MaxPoolLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/MaxPoolLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/MinLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/MinLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/MulLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/MulLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/OneHotLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/OneHotLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/OperationUtils.cc create mode 100644 runtime/onert/backend/cpu/kernel/OperationUtils.h create mode 100644 runtime/onert/backend/cpu/kernel/PackLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/PackLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/PadLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/PadLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/PermuteLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/PermuteLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ReduceLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ReduceLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ReshapeLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ReshapeLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/RsqrtLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/RsqrtLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/ShapeLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/ShapeLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/SinLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/SinLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/SliceLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/SliceLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/SoftMaxLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/SoftMaxLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/SplitLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/SplitLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/StridedSliceLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/StridedSliceLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/SubLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/SubLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/TanhLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/TanhLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/TransposeLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/TransposeLayer.h create mode 100644 runtime/onert/backend/cpu/kernel/UnpackLayer.cc create mode 100644 runtime/onert/backend/cpu/kernel/UnpackLayer.h create mode 100644 runtime/onert/backend/cpu/operand/Tensor.cc create mode 100644 runtime/onert/backend/cpu/operand/Tensor.h create mode 100644 runtime/onert/backend/cpu_common/Allocator.cc create mode 100644 runtime/onert/backend/cpu_common/Allocator.h create mode 100644 runtime/onert/backend/cpu_common/CMakeLists.txt create mode 100644 runtime/onert/backend/cpu_common/MemoryManager.cc create mode 100644 runtime/onert/backend/cpu_common/MemoryManager.h create mode 100644 runtime/onert/backend/cpu_common/MemoryPlanner.cc create mode 100644 runtime/onert/backend/cpu_common/MemoryPlanner.h create mode 100644 runtime/onert/backend/cpu_common/MemoryPlanner.test.cc create mode 100644 runtime/onert/backend/cpu_common/MemoryPlannerFactory.cc create mode 100644 runtime/onert/backend/cpu_common/MemoryPlannerFactory.h create mode 100644 runtime/onert/core/CMakeLists.txt create mode 100644 runtime/onert/core/include/backend/Backend.h create mode 100644 runtime/onert/core/include/backend/BackendContext.h create mode 100644 runtime/onert/core/include/backend/CustomKernelBuilder.h create mode 100644 runtime/onert/core/include/backend/IConfig.h create mode 100644 runtime/onert/core/include/backend/IConstantInitializer.h create mode 100644 runtime/onert/core/include/backend/IKernelGenerator.h create mode 100644 runtime/onert/core/include/backend/IMemoryManager.h create mode 100644 runtime/onert/core/include/backend/IOptimizer.h create mode 100644 runtime/onert/core/include/backend/IShapeFixer.h create mode 100644 runtime/onert/core/include/backend/ITensor.h create mode 100644 runtime/onert/core/include/backend/ITensorBuilder.h create mode 100644 runtime/onert/core/include/backend/ITensorManager.h create mode 100644 runtime/onert/core/include/backend/ITensorRegister.h create mode 100644 runtime/onert/core/include/compiler/BackendManager.h create mode 100644 runtime/onert/core/include/compiler/BackendResolver.h create mode 100644 runtime/onert/core/include/compiler/CodeMap.h create mode 100644 runtime/onert/core/include/compiler/Compiler.h create mode 100644 runtime/onert/core/include/compiler/ExecutionBuilder.h create mode 100644 runtime/onert/core/include/exec/ExecTime.h create mode 100644 runtime/onert/core/include/exec/Execution.h create mode 100644 runtime/onert/core/include/exec/ExecutionObservers.h create mode 100644 runtime/onert/core/include/exec/FunctionSequence.h create mode 100644 runtime/onert/core/include/exec/IExecutor.h create mode 100644 runtime/onert/core/include/exec/IFunction.h create mode 100644 runtime/onert/core/include/exec/IODescription.h create mode 100644 runtime/onert/core/include/exec/JSONExecTime.h create mode 100644 runtime/onert/core/include/exec/NopFunction.h create mode 100644 runtime/onert/core/include/interp/InterpExecutor.h create mode 100644 runtime/onert/core/include/ir/BackendSet.h create mode 100644 runtime/onert/core/include/ir/Coordinates.h create mode 100644 runtime/onert/core/include/ir/Data.h create mode 100644 runtime/onert/core/include/ir/DataType.h create mode 100644 runtime/onert/core/include/ir/Graph.h create mode 100644 runtime/onert/core/include/ir/Index.h create mode 100644 runtime/onert/core/include/ir/InternalType.h create mode 100644 runtime/onert/core/include/ir/Layout.h create mode 100644 runtime/onert/core/include/ir/LowerInfoMap.h create mode 100644 runtime/onert/core/include/ir/LoweredGraph.h create mode 100644 runtime/onert/core/include/ir/OpCode.h create mode 100644 runtime/onert/core/include/ir/OpSequence.h create mode 100644 runtime/onert/core/include/ir/OpSequences.h create mode 100644 runtime/onert/core/include/ir/Operand.h create mode 100644 runtime/onert/core/include/ir/OperandConstraint.h create mode 100644 runtime/onert/core/include/ir/OperandIndexMap.h create mode 100644 runtime/onert/core/include/ir/OperandIndexSequence.h create mode 100644 runtime/onert/core/include/ir/OperandInfo.h create mode 100644 runtime/onert/core/include/ir/Operands.h create mode 100644 runtime/onert/core/include/ir/Operation.h create mode 100644 runtime/onert/core/include/ir/OperationIndexList.h create mode 100644 runtime/onert/core/include/ir/OperationIndexMap.h create mode 100644 runtime/onert/core/include/ir/OperationVisitor.h create mode 100644 runtime/onert/core/include/ir/Operations.Include.h create mode 100644 runtime/onert/core/include/ir/Operations.h create mode 100644 runtime/onert/core/include/ir/Operations.lst create mode 100644 runtime/onert/core/include/ir/Padding.h create mode 100644 runtime/onert/core/include/ir/Shape.h create mode 100644 runtime/onert/core/include/ir/TypeInfo.h create mode 100644 runtime/onert/core/include/ir/operand/LowerInfo.h create mode 100644 runtime/onert/core/include/ir/operand/PermuteFactor.h create mode 100644 runtime/onert/core/include/ir/operation/Abs.h create mode 100644 runtime/onert/core/include/ir/operation/Add.h create mode 100644 runtime/onert/core/include/ir/operation/ArgMax.h create mode 100644 runtime/onert/core/include/ir/operation/AvgPool2D.h create mode 100644 runtime/onert/core/include/ir/operation/BatchToSpaceND.h create mode 100644 runtime/onert/core/include/ir/operation/Cast.h create mode 100644 runtime/onert/core/include/ir/operation/Comparison.h create mode 100644 runtime/onert/core/include/ir/operation/Concat.h create mode 100644 runtime/onert/core/include/ir/operation/Conv2D.h create mode 100644 runtime/onert/core/include/ir/operation/ConvertFp16ToFp32.h create mode 100644 runtime/onert/core/include/ir/operation/ConvertFp32ToFp16.h create mode 100644 runtime/onert/core/include/ir/operation/Custom.h create mode 100644 runtime/onert/core/include/ir/operation/DepthToSpace.h create mode 100644 runtime/onert/core/include/ir/operation/DepthwiseConv2D.h create mode 100644 runtime/onert/core/include/ir/operation/Dequantize.h create mode 100644 runtime/onert/core/include/ir/operation/Div.h create mode 100644 runtime/onert/core/include/ir/operation/EmbeddingLookup.h create mode 100644 runtime/onert/core/include/ir/operation/Exp.h create mode 100644 runtime/onert/core/include/ir/operation/Floor.h create mode 100644 runtime/onert/core/include/ir/operation/FullyConnected.h create mode 100644 runtime/onert/core/include/ir/operation/Gather.h create mode 100644 runtime/onert/core/include/ir/operation/HashtableLookup.h create mode 100644 runtime/onert/core/include/ir/operation/InstanceNorm.h create mode 100644 runtime/onert/core/include/ir/operation/L2Normalization.h create mode 100644 runtime/onert/core/include/ir/operation/L2Pool2D.h create mode 100644 runtime/onert/core/include/ir/operation/LSTM.h create mode 100644 runtime/onert/core/include/ir/operation/LocalResponseNormalization.h create mode 100644 runtime/onert/core/include/ir/operation/LogicalAnd.h create mode 100644 runtime/onert/core/include/ir/operation/LogicalNot.h create mode 100644 runtime/onert/core/include/ir/operation/LogicalOr.h create mode 100644 runtime/onert/core/include/ir/operation/Logistic.h create mode 100644 runtime/onert/core/include/ir/operation/LowerInfo.h create mode 100644 runtime/onert/core/include/ir/operation/Max.h create mode 100644 runtime/onert/core/include/ir/operation/MaxPool2D.h create mode 100644 runtime/onert/core/include/ir/operation/Mean.h create mode 100644 runtime/onert/core/include/ir/operation/Min.h create mode 100644 runtime/onert/core/include/ir/operation/Mul.h create mode 100644 runtime/onert/core/include/ir/operation/Neg.h create mode 100644 runtime/onert/core/include/ir/operation/OneHot.h create mode 100644 runtime/onert/core/include/ir/operation/PReLU.h create mode 100644 runtime/onert/core/include/ir/operation/Pack.h create mode 100644 runtime/onert/core/include/ir/operation/Pad.h create mode 100644 runtime/onert/core/include/ir/operation/Permute.h create mode 100644 runtime/onert/core/include/ir/operation/RNN.h create mode 100644 runtime/onert/core/include/ir/operation/RSQRT.h create mode 100644 runtime/onert/core/include/ir/operation/ReLU.h create mode 100644 runtime/onert/core/include/ir/operation/ReLU1.h create mode 100644 runtime/onert/core/include/ir/operation/ReLU6.h create mode 100644 runtime/onert/core/include/ir/operation/ReduceMax.h create mode 100644 runtime/onert/core/include/ir/operation/ReduceMin.h create mode 100644 runtime/onert/core/include/ir/operation/ReduceSum.h create mode 100644 runtime/onert/core/include/ir/operation/Reshape.h create mode 100644 runtime/onert/core/include/ir/operation/ResizeBilinear.h create mode 100644 runtime/onert/core/include/ir/operation/SQRT.h create mode 100644 runtime/onert/core/include/ir/operation/Shape.h create mode 100644 runtime/onert/core/include/ir/operation/Sin.h create mode 100644 runtime/onert/core/include/ir/operation/Slice.h create mode 100644 runtime/onert/core/include/ir/operation/Softmax.h create mode 100644 runtime/onert/core/include/ir/operation/SpaceToBatchND.h create mode 100644 runtime/onert/core/include/ir/operation/SpaceToDepth.h create mode 100644 runtime/onert/core/include/ir/operation/Split.h create mode 100644 runtime/onert/core/include/ir/operation/SquaredDifference.h create mode 100644 runtime/onert/core/include/ir/operation/Squeeze.h create mode 100644 runtime/onert/core/include/ir/operation/StridedSlice.h create mode 100644 runtime/onert/core/include/ir/operation/Sub.h create mode 100644 runtime/onert/core/include/ir/operation/Tanh.h create mode 100644 runtime/onert/core/include/ir/operation/TopKV2.h create mode 100644 runtime/onert/core/include/ir/operation/Transpose.h create mode 100644 runtime/onert/core/include/ir/operation/TransposeConv.h create mode 100644 runtime/onert/core/include/ir/operation/Unpack.h create mode 100644 runtime/onert/core/include/util/Config.lst create mode 100644 runtime/onert/core/include/util/ConfigSource.h create mode 100644 runtime/onert/core/include/util/EnvConfigSource.h create mode 100644 runtime/onert/core/include/util/EventCollectorGlobal.h create mode 100644 runtime/onert/core/include/util/GeneralConfigSource.h create mode 100644 runtime/onert/core/include/util/IConfigSource.h create mode 100644 runtime/onert/core/include/util/ITimer.h create mode 100644 runtime/onert/core/include/util/Index.h create mode 100644 runtime/onert/core/include/util/ObjectManager.h create mode 100644 runtime/onert/core/include/util/Set.h create mode 100644 runtime/onert/core/include/util/ShapeInference.h create mode 100644 runtime/onert/core/include/util/Utils.h create mode 100644 runtime/onert/core/include/util/feature/nchw/Reader.h create mode 100644 runtime/onert/core/include/util/feature/nchw/View.h create mode 100644 runtime/onert/core/include/util/feature/nhwc/Reader.h create mode 100644 runtime/onert/core/include/util/feature/nhwc/View.h create mode 100644 runtime/onert/core/include/util/logging.h create mode 100644 runtime/onert/core/src/backend/BackendContext.cc create mode 100644 runtime/onert/core/src/compiler/BackendManager.cc create mode 100644 runtime/onert/core/src/compiler/BackendResolver.cc create mode 100644 runtime/onert/core/src/compiler/CachedDataDeleter.h create mode 100644 runtime/onert/core/src/compiler/Compiler.cc create mode 100644 runtime/onert/core/src/compiler/ExecutorFactory.cc create mode 100644 runtime/onert/core/src/compiler/ExecutorFactory.h create mode 100644 runtime/onert/core/src/compiler/HEScheduler.cc create mode 100644 runtime/onert/core/src/compiler/HEScheduler.h create mode 100644 runtime/onert/core/src/compiler/IScheduler.h create mode 100644 runtime/onert/core/src/compiler/Linear.cc create mode 100644 runtime/onert/core/src/compiler/Linear.h create mode 100644 runtime/onert/core/src/compiler/ManualScheduler.cc create mode 100644 runtime/onert/core/src/compiler/ManualScheduler.h create mode 100644 runtime/onert/core/src/compiler/OperandContext.cc create mode 100644 runtime/onert/core/src/compiler/OperandContext.h create mode 100644 runtime/onert/core/src/compiler/OperationValidator.cc create mode 100644 runtime/onert/core/src/compiler/OperationValidator.h create mode 100644 runtime/onert/core/src/compiler/ParamChecker.cc create mode 100644 runtime/onert/core/src/compiler/ParamChecker.h create mode 100644 runtime/onert/core/src/dumper/dot/DotBuilder.cc create mode 100644 runtime/onert/core/src/dumper/dot/DotBuilder.h create mode 100644 runtime/onert/core/src/dumper/dot/DotDumper.cc create mode 100644 runtime/onert/core/src/dumper/dot/DotDumper.h create mode 100644 runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.cc create mode 100644 runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.h create mode 100644 runtime/onert/core/src/dumper/dot/Node.cc create mode 100644 runtime/onert/core/src/dumper/dot/Node.h create mode 100644 runtime/onert/core/src/dumper/dot/OperandNode.cc create mode 100644 runtime/onert/core/src/dumper/dot/OperandNode.h create mode 100644 runtime/onert/core/src/dumper/dot/OperationNode.cc create mode 100644 runtime/onert/core/src/dumper/dot/OperationNode.h create mode 100644 runtime/onert/core/src/exec/DataflowExecutor.cc create mode 100644 runtime/onert/core/src/exec/DataflowExecutor.h create mode 100644 runtime/onert/core/src/exec/ExecTime.cc create mode 100644 runtime/onert/core/src/exec/Execution.cc create mode 100644 runtime/onert/core/src/exec/ExecutionObservee.cc create mode 100644 runtime/onert/core/src/exec/ExecutionObservee.h create mode 100644 runtime/onert/core/src/exec/ExecutionObservers.cc create mode 100644 runtime/onert/core/src/exec/ExecutorBase.cc create mode 100644 runtime/onert/core/src/exec/ExecutorBase.h create mode 100644 runtime/onert/core/src/exec/FunctionSequence.cc create mode 100644 runtime/onert/core/src/exec/JSONExecTime.cc create mode 100644 runtime/onert/core/src/exec/Job.cc create mode 100644 runtime/onert/core/src/exec/Job.h create mode 100644 runtime/onert/core/src/exec/LinearExecutor.cc create mode 100644 runtime/onert/core/src/exec/LinearExecutor.h create mode 100644 runtime/onert/core/src/exec/ParallelExecutor.cc create mode 100644 runtime/onert/core/src/exec/ParallelExecutor.h create mode 100644 runtime/onert/core/src/exec/ParallelScheduler.cc create mode 100644 runtime/onert/core/src/exec/ParallelScheduler.h create mode 100644 runtime/onert/core/src/exec/Sink.h create mode 100644 runtime/onert/core/src/exec/Source.h create mode 100644 runtime/onert/core/src/exec/ThreadPool.cc create mode 100644 runtime/onert/core/src/exec/ThreadPool.h create mode 100644 runtime/onert/core/src/exec/WorkQueue.cc create mode 100644 runtime/onert/core/src/exec/WorkQueue.h create mode 100644 runtime/onert/core/src/interp/Buffer.h create mode 100644 runtime/onert/core/src/interp/ExecEnv.h create mode 100644 runtime/onert/core/src/interp/InterpExecutor.cc create mode 100644 runtime/onert/core/src/interp/InterpOps.lst create mode 100644 runtime/onert/core/src/interp/Interpreter.cc create mode 100644 runtime/onert/core/src/interp/Interpreter.h create mode 100644 runtime/onert/core/src/interp/Registration.h create mode 100644 runtime/onert/core/src/interp/Tensor.cc create mode 100644 runtime/onert/core/src/interp/Tensor.h create mode 100644 runtime/onert/core/src/interp/operations/AvgPool2D.cc create mode 100644 runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc create mode 100644 runtime/onert/core/src/interp/operations/Concat.cc create mode 100644 runtime/onert/core/src/interp/operations/Conv2D.cc create mode 100644 runtime/onert/core/src/interp/operations/DepthwiseConv2D.cc create mode 100644 runtime/onert/core/src/interp/operations/FullyConnected.cc create mode 100644 runtime/onert/core/src/interp/operations/Gather.cc create mode 100644 runtime/onert/core/src/interp/operations/InstanceNorm.cc create mode 100644 runtime/onert/core/src/interp/operations/Logistic.cc create mode 100644 runtime/onert/core/src/interp/operations/MaxPool2D.cc create mode 100644 runtime/onert/core/src/interp/operations/OperationUtil.h create mode 100644 runtime/onert/core/src/interp/operations/Pad.cc create mode 100644 runtime/onert/core/src/interp/operations/Reshape.cc create mode 100644 runtime/onert/core/src/interp/operations/Softmax.cc create mode 100644 runtime/onert/core/src/interp/operations/TransposeConv.cc create mode 100644 runtime/onert/core/src/interp/operations/UnaryActivations.cc create mode 100644 runtime/onert/core/src/ir/Coordinates.cc create mode 100644 runtime/onert/core/src/ir/Graph.cc create mode 100644 runtime/onert/core/src/ir/GraphIterator.cc create mode 100644 runtime/onert/core/src/ir/GraphIterator.h create mode 100644 runtime/onert/core/src/ir/LayoutSet.cc create mode 100644 runtime/onert/core/src/ir/LayoutSet.h create mode 100644 runtime/onert/core/src/ir/LoweredGraph.cc create mode 100644 runtime/onert/core/src/ir/OpCode.cc create mode 100644 runtime/onert/core/src/ir/OpSequence.cc create mode 100644 runtime/onert/core/src/ir/OpSequences.cc create mode 100644 runtime/onert/core/src/ir/Operand.cc create mode 100644 runtime/onert/core/src/ir/OperandIndexSequence.cc create mode 100644 runtime/onert/core/src/ir/Operands.cc create mode 100644 runtime/onert/core/src/ir/Operation.cc create mode 100644 runtime/onert/core/src/ir/OperationCloner.cc create mode 100644 runtime/onert/core/src/ir/OperationCloner.h create mode 100644 runtime/onert/core/src/ir/OperationDumper.cc create mode 100644 runtime/onert/core/src/ir/OperationDumper.h create mode 100644 runtime/onert/core/src/ir/OperationIndexList.cc create mode 100644 runtime/onert/core/src/ir/Operations.cc create mode 100644 runtime/onert/core/src/ir/Padding.cc create mode 100644 runtime/onert/core/src/ir/Shape.cc create mode 100644 runtime/onert/core/src/ir/TypeInfo.cc create mode 100644 runtime/onert/core/src/ir/operation/Abs.cc create mode 100644 runtime/onert/core/src/ir/operation/Add.cc create mode 100644 runtime/onert/core/src/ir/operation/ArgMax.cc create mode 100644 runtime/onert/core/src/ir/operation/AvgPool2D.cc create mode 100644 runtime/onert/core/src/ir/operation/BatchToSpaceND.cc create mode 100644 runtime/onert/core/src/ir/operation/Cast.cc create mode 100644 runtime/onert/core/src/ir/operation/Comparison.cc create mode 100644 runtime/onert/core/src/ir/operation/Concat.cc create mode 100644 runtime/onert/core/src/ir/operation/Conv2D.cc create mode 100644 runtime/onert/core/src/ir/operation/ConvertFp16ToFp32.cc create mode 100644 runtime/onert/core/src/ir/operation/ConvertFp32ToFp16.cc create mode 100644 runtime/onert/core/src/ir/operation/Custom.cc create mode 100644 runtime/onert/core/src/ir/operation/DepthToSpace.cc create mode 100644 runtime/onert/core/src/ir/operation/DepthwiseConv2D.cc create mode 100644 runtime/onert/core/src/ir/operation/Dequantize.cc create mode 100644 runtime/onert/core/src/ir/operation/Div.cc create mode 100644 runtime/onert/core/src/ir/operation/EmbeddingLookup.cc create mode 100644 runtime/onert/core/src/ir/operation/Exp.cc create mode 100644 runtime/onert/core/src/ir/operation/Floor.cc create mode 100644 runtime/onert/core/src/ir/operation/FullyConnected.cc create mode 100644 runtime/onert/core/src/ir/operation/Gather.cc create mode 100644 runtime/onert/core/src/ir/operation/HashtableLookup.cc create mode 100644 runtime/onert/core/src/ir/operation/InstanceNorm.cc create mode 100644 runtime/onert/core/src/ir/operation/L2Normalization.cc create mode 100644 runtime/onert/core/src/ir/operation/L2Pool2D.cc create mode 100644 runtime/onert/core/src/ir/operation/LSTM.cc create mode 100644 runtime/onert/core/src/ir/operation/LocalResponseNormalization.cc create mode 100644 runtime/onert/core/src/ir/operation/LogicalAnd.cc create mode 100644 runtime/onert/core/src/ir/operation/LogicalNot.cc create mode 100644 runtime/onert/core/src/ir/operation/LogicalOr.cc create mode 100644 runtime/onert/core/src/ir/operation/Logistic.cc create mode 100644 runtime/onert/core/src/ir/operation/LowerInfo.cc create mode 100644 runtime/onert/core/src/ir/operation/Max.cc create mode 100644 runtime/onert/core/src/ir/operation/MaxPool2D.cc create mode 100644 runtime/onert/core/src/ir/operation/Mean.cc create mode 100644 runtime/onert/core/src/ir/operation/Min.cc create mode 100644 runtime/onert/core/src/ir/operation/Mul.cc create mode 100644 runtime/onert/core/src/ir/operation/Neg.cc create mode 100644 runtime/onert/core/src/ir/operation/OneHot.cc create mode 100644 runtime/onert/core/src/ir/operation/PReLU.cc create mode 100644 runtime/onert/core/src/ir/operation/Pack.cc create mode 100644 runtime/onert/core/src/ir/operation/Pad.cc create mode 100644 runtime/onert/core/src/ir/operation/Permute.cc create mode 100644 runtime/onert/core/src/ir/operation/RNN.cc create mode 100644 runtime/onert/core/src/ir/operation/RSQRT.cc create mode 100644 runtime/onert/core/src/ir/operation/ReLU.cc create mode 100644 runtime/onert/core/src/ir/operation/ReLU1.cc create mode 100644 runtime/onert/core/src/ir/operation/ReLU6.cc create mode 100644 runtime/onert/core/src/ir/operation/ReduceMax.cc create mode 100644 runtime/onert/core/src/ir/operation/ReduceMin.cc create mode 100644 runtime/onert/core/src/ir/operation/ReduceSum.cc create mode 100644 runtime/onert/core/src/ir/operation/Reshape.cc create mode 100644 runtime/onert/core/src/ir/operation/ResizeBilinear.cc create mode 100644 runtime/onert/core/src/ir/operation/SQRT.cc create mode 100644 runtime/onert/core/src/ir/operation/Shape.cc create mode 100644 runtime/onert/core/src/ir/operation/Sin.cc create mode 100644 runtime/onert/core/src/ir/operation/Slice.cc create mode 100644 runtime/onert/core/src/ir/operation/Softmax.cc create mode 100644 runtime/onert/core/src/ir/operation/SpaceToBatchND.cc create mode 100644 runtime/onert/core/src/ir/operation/SpaceToDepth.cc create mode 100644 runtime/onert/core/src/ir/operation/Split.cc create mode 100644 runtime/onert/core/src/ir/operation/SquaredDifference.cc create mode 100644 runtime/onert/core/src/ir/operation/Squeeze.cc create mode 100644 runtime/onert/core/src/ir/operation/StridedSlice.cc create mode 100644 runtime/onert/core/src/ir/operation/Sub.cc create mode 100644 runtime/onert/core/src/ir/operation/Tanh.cc create mode 100644 runtime/onert/core/src/ir/operation/TopKV2.cc create mode 100644 runtime/onert/core/src/ir/operation/Transpose.cc create mode 100644 runtime/onert/core/src/ir/operation/TransposeConv.cc create mode 100644 runtime/onert/core/src/ir/operation/Unpack.cc create mode 100644 runtime/onert/core/src/ir/pass/ConstantInsertionPass.cc create mode 100644 runtime/onert/core/src/ir/pass/ConstantInsertionPass.h create mode 100644 runtime/onert/core/src/ir/pass/LoweredOperandPass.h create mode 100644 runtime/onert/core/src/ir/pass/LoweredOperationPass.h create mode 100644 runtime/onert/core/src/ir/pass/OperandPass.cc create mode 100644 runtime/onert/core/src/ir/pass/OperandPass.h create mode 100644 runtime/onert/core/src/ir/pass/OperationPass.cc create mode 100644 runtime/onert/core/src/ir/pass/OperationPass.h create mode 100644 runtime/onert/core/src/ir/pass/Pass.h create mode 100644 runtime/onert/core/src/ir/pass/PermutationEliminationPass.cc create mode 100644 runtime/onert/core/src/ir/pass/PermutationEliminationPass.h create mode 100644 runtime/onert/core/src/ir/pass/PermutationInsertionPass.cc create mode 100644 runtime/onert/core/src/ir/pass/PermutationInsertionPass.h create mode 100644 runtime/onert/core/src/ir/pass/PermutationOperationPass.cc create mode 100644 runtime/onert/core/src/ir/pass/PermutationOperationPass.h create mode 100644 runtime/onert/core/src/ir/verifier/Verifier.cc create mode 100644 runtime/onert/core/src/ir/verifier/Verifier.h create mode 100644 runtime/onert/core/src/library_info.cc create mode 100644 runtime/onert/core/src/util/ConfigSource.cc create mode 100644 runtime/onert/core/src/util/EnvConfigSource.cc create mode 100644 runtime/onert/core/src/util/EventCollectorGlobal.cc create mode 100644 runtime/onert/core/src/util/GeneralConfigSource.cc create mode 100644 runtime/onert/core/src/util/ShapeInference.cc create mode 100644 runtime/onert/core/src/util/logging.cc create mode 100644 runtime/onert/frontend/CMakeLists.txt create mode 100644 runtime/onert/frontend/base_loader/CMakeLists.txt create mode 100644 runtime/onert/frontend/base_loader/include/base_loader.h create mode 100644 runtime/onert/frontend/circle/CMakeLists.txt create mode 100644 runtime/onert/frontend/circle/include/circle_loader.h create mode 100644 runtime/onert/frontend/circle/src/circle_loader.cc create mode 100644 runtime/onert/frontend/circle/src/circle_schema_generated.h create mode 100644 runtime/onert/frontend/nnapi/ANeuralNetworksModel.test.cc create mode 100644 runtime/onert/frontend/nnapi/CMakeLists.txt create mode 100644 runtime/onert/frontend/nnapi/compilation.cc create mode 100644 runtime/onert/frontend/nnapi/event.cc create mode 100644 runtime/onert/frontend/nnapi/execution.cc create mode 100644 runtime/onert/frontend/nnapi/memory.cc create mode 100644 runtime/onert/frontend/nnapi/model.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.h create mode 100644 runtime/onert/frontend/nnapi/wrapper/OperationFactory.cc create mode 100644 runtime/onert/frontend/nnapi/wrapper/OperationFactory.h create mode 100644 runtime/onert/frontend/tflite/CMakeLists.txt create mode 100644 runtime/onert/frontend/tflite/include/tflite_loader.h create mode 100644 runtime/onert/frontend/tflite/src/tflite_loader.cc create mode 100644 runtime/onert/frontend/tflite/src/tflite_schema_generated.h create mode 100644 runtime/onert/frontend/tflite/tflite_schema-1.13.1.fbs create mode 100644 runtime/onert/frontend/tflite/tflite_schema.fbs create mode 100644 runtime/onert/sample/CMakeLists.txt create mode 100644 runtime/onert/sample/minimal/CMakeLists.txt create mode 100644 runtime/onert/sample/minimal/README.md create mode 100644 runtime/onert/sample/minimal/src/minimal.cc create mode 100644 runtime/onert/test/CMakeLists.txt create mode 100644 runtime/onert/test/core/compiler/Scheduler.cc create mode 100644 runtime/onert/test/core/exec/ExecInstance.cc create mode 100644 runtime/onert/test/core/exec/ExecTime.test.cc create mode 100644 runtime/onert/test/core/interp/ExecManager.cc create mode 100644 runtime/onert/test/graph/Graph.cc create mode 100644 runtime/onert/test/graph/Index.cc create mode 100644 runtime/onert/test/graph/MockNode.h create mode 100644 runtime/onert/test/graph/operand/IndexSet.cc create mode 100644 runtime/onert/test/graph/operand/LayoutSet.cc create mode 100644 runtime/onert/test/graph/operand/Set.cc create mode 100644 runtime/onert/test/graph/operand/UseDef.cc create mode 100644 runtime/onert/test/graph/operation/Set.cc create mode 100644 runtime/onert/test/graph/operation/SetIO.cc create mode 100644 runtime/onert/test/graph/verifier/Verifier.cc create mode 100644 runtime/onert/test/util/ObjectManager.cc create mode 100644 runtime/onert/test/util/ShapeInference.cc delete mode 100755 tests/framework/run_test.sh delete mode 100644 tests/framework/tests/MODELS/inception_module/config.sh delete mode 100644 tests/framework/tests/MODELS/inception_nonslim/config.sh delete mode 100644 tests/framework/tests/MODELS/inception_slim/config.sh delete mode 100644 tests/framework/tests/MODELS/mobilenet/config.sh delete mode 100644 tests/framework/tests/add/1D/config.sh delete mode 100644 tests/framework/tests/add/4D/config.sh delete mode 100644 tests/framework/tests/average_pool_2d/avgpool1/config.sh delete mode 100644 tests/framework/tests/average_pool_2d/avgpool2/config.sh delete mode 100644 tests/framework/tests/batch_to_space_nd2/config.sh delete mode 100644 tests/framework/tests/cast/config.sh delete mode 100644 tests/framework/tests/concat/2D/config.sh delete mode 100644 tests/framework/tests/concat/concat1/config.sh delete mode 100644 tests/framework/tests/concat/concat2/config.sh delete mode 100644 tests/framework/tests/conv_2d/convolution1/config.sh delete mode 100644 tests/framework/tests/conv_2d/convolution2/config.sh delete mode 100644 tests/framework/tests/custom/abs/config.sh delete mode 100644 tests/framework/tests/custom/squared_difference/config.sh delete mode 100644 tests/framework/tests/custom/tensorflowmax/config.sh delete mode 100644 tests/framework/tests/custom/tensorflowsum/config.sh delete mode 100644 tests/framework/tests/depthwise_conv_2d/depthconv1/config.sh delete mode 100644 tests/framework/tests/depthwise_conv_2d/depthconv2/config.sh delete mode 100644 tests/framework/tests/div/broadcast/config.sh delete mode 100644 tests/framework/tests/embedding_lookup/config.sh delete mode 100644 tests/framework/tests/exp/config.sh delete mode 100644 tests/framework/tests/floor/floor1/config.sh delete mode 100644 tests/framework/tests/floor/floor2/config.sh delete mode 100644 tests/framework/tests/fullyconnected/fc1/config.sh delete mode 100644 tests/framework/tests/fullyconnected/matmul2x2/config.sh delete mode 100644 tests/framework/tests/gather/config.sh delete mode 100644 tests/framework/tests/hashtable_lookup/config.sh delete mode 100644 tests/framework/tests/l2_normalization/config.sh delete mode 100644 tests/framework/tests/l2_pool_2d/config.sh delete mode 100644 tests/framework/tests/logistic/config.sh delete mode 100644 tests/framework/tests/max/config.sh delete mode 100644 tests/framework/tests/max_pool_2d/maxpool1/config.sh delete mode 100644 tests/framework/tests/max_pool_2d/maxpool2/config.sh delete mode 100644 tests/framework/tests/mean/config.sh delete mode 100644 tests/framework/tests/min/config.sh delete mode 100644 tests/framework/tests/mul/broadcast/config.sh delete mode 100644 tests/framework/tests/neg/config.sh delete mode 100644 tests/framework/tests/pack/config.sh delete mode 100644 tests/framework/tests/pad/4D_2D/config.sh delete mode 100644 tests/framework/tests/pad/pad1/config.sh delete mode 100644 tests/framework/tests/pad/pad2/config.sh delete mode 100644 tests/framework/tests/reduce_max/config.sh delete mode 100644 tests/framework/tests/reduce_mean/test1/config.sh delete mode 100644 tests/framework/tests/reduce_mean/test2/config.sh delete mode 100644 tests/framework/tests/reduce_sum/config.sh delete mode 100644 tests/framework/tests/relu/config.sh delete mode 100644 tests/framework/tests/relu6/config.sh delete mode 100644 tests/framework/tests/reshape/3D/config.sh delete mode 100644 tests/framework/tests/reshape/reshape1/config.sh delete mode 100644 tests/framework/tests/reshape/reshape2/config.sh delete mode 100644 tests/framework/tests/resize_bilinear/config.sh delete mode 100644 tests/framework/tests/rnn/config.sh delete mode 100644 tests/framework/tests/rsqrt/config.sh delete mode 100644 tests/framework/tests/slice/config.sh delete mode 100644 tests/framework/tests/softmax/config.sh delete mode 100644 tests/framework/tests/space_to_batch_nd2/config.sh delete mode 100644 tests/framework/tests/space_to_depth/config.sh delete mode 100644 tests/framework/tests/sqrt/config.sh delete mode 100644 tests/framework/tests/squeeze/config.sh delete mode 100644 tests/framework/tests/strided_slice/config.sh delete mode 100644 tests/framework/tests/sub/broadcast/config.sh delete mode 100644 tests/framework/tests/tanh/config.sh delete mode 100644 tests/framework/tests/topk_v2/config.sh delete mode 100644 tests/framework/tests/transpose/config.sh delete mode 100644 tests/framework/tests/transpose_conv/same/config.sh delete mode 100644 tests/framework/tests/transpose_conv/valid/config.sh create mode 100644 tests/nnapi/nnapi_gtest.skip.aarch64-linux.acl_cl create mode 100644 tests/nnapi/nnapi_gtest.skip.aarch64-linux.acl_neon create mode 100644 tests/nnapi/nnapi_gtest.skip.aarch64-linux.cpu create mode 100644 tests/nnapi/nnapi_gtest.skip.aarch64-linux.srcn delete mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-linux create mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-linux.acl_cl delete mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-linux.ncnn create mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-linux.srcn delete mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-tizen create mode 100644 tests/nnapi/nnapi_gtest.skip.armv7l-tizen.acl_cl delete mode 100644 tests/nnapi/nnapi_gtest.skip.x86_64-linux create mode 100644 tests/nnapi/nnapi_gtest.skip.x86_64-linux.cpu delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_float_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_float_2.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_int32.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_neg_axis_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_neg_axis_int32.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_quant8.mod.py delete mode 100644 tests/nnapi/specs/Ex/argmax_ex_quant8_neg_axis.mod.py delete mode 100644 tests/nnapi/specs/Ex/equal_ex_1D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/equal_ex_4D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/equal_ex_broadcast_4D_2D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/equal_ex_broadcast_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/equal_ex_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/fully_connected_float_2_weights_as_inputs.mod.py delete mode 100644 tests/nnapi/specs/Ex/greater_equal_ex.mod.py delete mode 100644 tests/nnapi/specs/Ex/less_ex.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_1D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_2D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_3D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_4D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_broadcast.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_and_ex_broadcast_4D_2D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_not_ex_1D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_not_ex_4D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_1D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_2D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_3D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_4D.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_broadcast.mod.py delete mode 100644 tests/nnapi/specs/Ex/logical_or_ex_broadcast_4D_2D.mod.py delete mode 100644 tests/nnapi/specs/Ex/notequal_ex_broadcast_4D_2D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/notequal_ex_broadcast_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/notequal_ex_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/notequal_ex_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/pack_ex_2D_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/pack_ex_2D_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/pack_ex_2D_int_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/pack_ex_2D_int_2.mod.py delete mode 100644 tests/nnapi/specs/Ex/prelu_ex_broadcast_float_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/prelu_ex_broadcast_quant8_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/prelu_ex_float_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/prelu_ex_quant8_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_min_ex_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_min_ex_float_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_min_ex_float_2.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_sum_ex_2D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_sum_ex_4D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_sum_ex_4D_float_reducing_C.mod.py delete mode 100644 tests/nnapi/specs/Ex/reduce_sum_ex_4D_float_reducing_HW.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_1D_float.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_1D_int32.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_float_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_float_2.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_float_3.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_int32_1.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_int32_2.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_int32_3.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_int32_4.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_int32_5.mod.py delete mode 100644 tests/nnapi/specs/Ex/split_ex_4D_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_1D_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_2D_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_3D_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_4D_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_broadcast_4D_2D_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/squared_difference_ex_broadcast_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/transpose_conv_ex_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/transpose_conv_ex_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/transpose_conv_ex_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/transpose_conv_ex_float_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/unpack_ex_3D_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/unpack_ex_3D_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/unpack_ex_3D_int_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/Ex/unpack_ex_3D_int_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/add.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/add_broadcast_4D_2D_after_nops_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/add_broadcast_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/add_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_float_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_float_5.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_quant8_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_quant8_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/avg_pool_quant8_5.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_float_4D_axis3_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/concat_quant8_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_1_h3_w2_SAME.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_1_h3_w2_VALID.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_3_h3_w2_SAME.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_3_h3_w2_VALID.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_channels.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_channels_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_float_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_channels.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_channels_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_overflow.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_overflow_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/conv_quant8_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depth_to_space_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depth_to_space_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depth_to_space_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depth_to_space_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depth_to_space_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_large_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_large_2_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_float_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_quant8_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_quant8_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/depthwise_conv2d_quant8_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/dequantize.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/embedding_lookup.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/embedding_lookup_2d_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/embedding_lookup_4d_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/floor_.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_float_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_hybrid_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_hybrid_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_quant8_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_quant8_large_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/fully_connected_quant8_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/hashtable_lookup_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/hashtable_lookup_float_4D_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/hashtable_lookup_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_normalization.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_normalization_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_normalization_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_pool_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_pool_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/l2_pool_float_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/local_response_norm_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/local_response_norm_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/local_response_norm_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/local_response_norm_float_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/logistic_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/logistic_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/logistic_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/logistic_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lsh_projection.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lsh_projection_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lsh_projection_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm2_state.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm2_state2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm3_state.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm3_state2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm3_state3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm_state.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/lstm_state2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_float_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_quant8_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/max_pool_quant8_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_4D_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_broadcast_3D_1D_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_broadcast_3D_1D_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_broadcast_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_float_square_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/mul_relu.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu1_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu1_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu1_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu1_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu6_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu6_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu6_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu6_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/relu_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/reshape.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/reshape_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/reshape_quant8_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/reshape_weights_as_inputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/resize_bilinear.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/resize_bilinear_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/rnn.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/rnn_state.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/softmax_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/softmax_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/softmax_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/softmax_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/space_to_depth_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/space_to_depth_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/space_to_depth_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/space_to_depth_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/space_to_depth_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/svdf.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/svdf2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/svdf_bias_present.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/svdf_state.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_0/tanh_.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/batch_to_space.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/batch_to_space_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/batch_to_space_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/div_.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/div_broadcast_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/div_broadcast_float_4D_2D_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/fully_connected_float_4d_simple.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_4D_float_reducing_C_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_4D_float_reducing_HW_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_axis01_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_axis01_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/mean_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_2D_HW_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_3D_HWC_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_BHWC_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_BHW_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_HWD_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/pad_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_quant8_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_quant8_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/space_to_batch_quant8_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/squeeze.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/squeeze_2D_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/squeeze_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/squeeze_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_10.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_11.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_5.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_6.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_7.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_float_9.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_qaunt8_10.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_qaunt8_11.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_5.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_6.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_7.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_8.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/strided_slice_quant8_9.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/sub.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/sub_broadcast_4D_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/sub_broadcast_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/transpose.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/transpose_2D_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/transpose_3D_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/transpose_float_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_1/transpose_quant8_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/abs_.mod.py create mode 100755 tests/nnapi/specs/V1_2/abs_1D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/abs_2D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/abs_3D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/abs_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_1.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_2.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_3.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_float_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_float_2_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_int32_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_neg_axis_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_neg_axis_int32_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_quant8_neg_axis_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/argmax_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/cast.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/cast_float32_to_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/cast_int32_to_float32_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal_1D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal_broadcast_4D_2D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal_broadcast_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/equal_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/exp_.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/exp_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/exp_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/exp_3D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/exp_4D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_1D_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_1D_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_2D_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_2D_float_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_3D_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_3D_float_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_2D_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_3D_2D_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_3D_2D_float_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_3D_2D_float_3_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_4D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/gather_higher_rank.mod.py create mode 100755 tests/nnapi/specs/V1_2/greater_equal.mod.py create mode 100755 tests/nnapi/specs/V1_2/greater_equal_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/less.mod.py create mode 100755 tests/nnapi/specs/V1_2/less_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_1D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_2D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_3D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_4D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_broadcast_4D_2D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_and_broadcast_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_not.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_not_1D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_not_4D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_1D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_2D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_3D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_4D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_broadcast_4D_2D_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/logical_or_broadcast_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/maximum.mod.py create mode 100755 tests/nnapi/specs/V1_2/minimum.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_3D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_3D_int_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_4D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/neg_4D_int_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/not_equal.mod.py create mode 100755 tests/nnapi/specs/V1_2/not_equal_broadcast_4D_2D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/not_equal_broadcast_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/not_equal_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/not_equal_quant8_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/prelu.mod.py create mode 100755 tests/nnapi/specs/V1_2/prelu_broadcast_float_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/prelu_broadcast_quant8_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/prelu_float_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/prelu_quant8_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_2D_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_4D_float_reducing_C_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_4D_float_reducing_HW_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_float_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_float_2_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_quant8_1_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/reduce_max_quant8_2_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_min.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_min_float_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_min_float_2_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_min_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_sum.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_sum_2D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_sum_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_sum_4D_float_reducing_C_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/reduce_sum_4D_float_reducing_HW_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/rsqrt.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/rsqrt_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/rsqrt_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/rsqrt_3D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/rsqrt_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/sin_1D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/sin_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/slice.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_1D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_1D_int32_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_float_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_float_2_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_float_3_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_int32_1_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_int32_2_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_int32_3_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_int32_4_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_int32_5_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_4D_quant8_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_float_1.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_float_2.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_float_3.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_float_4.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_float_5.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_int32_1.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_int32_2.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_int32_3.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_int32_4.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_quant8_1.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_quant8_2.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_quant8_3.mod.py create mode 100755 tests/nnapi/specs/V1_2/split_quant8_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/sqrt_.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/sqrt_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/sqrt_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/sqrt_3D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/sqrt_4D_float_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/sub_v1_2.mod.py create mode 100755 tests/nnapi/specs/V1_2/sub_v1_2_broadcast.mod.py create mode 100755 tests/nnapi/specs/V1_2/tanh_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_1D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_1D_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_1D_quant8_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_2D_float_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_2D_int32_nnfw.mod.py mode change 100644 => 100755 tests/nnapi/specs/V1_2/topk_v2_2D_quant8_nnfw.mod.py create mode 100755 tests/nnapi/specs/V1_2/transpose_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_0/mobilenet_224_gender_basic_fixed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_0/mobilenet_quantized.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/add_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/avg_pool_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/avg_pool_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/avg_pool_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/avg_pool_float_4_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/avg_pool_float_5_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/batch_to_space_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/batch_to_space_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/concat_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/concat_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/concat_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_1_h3_w2_SAME_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_1_h3_w2_VALID_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_3_h3_w2_SAME_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_3_h3_w2_VALID_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_channels_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_channels_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_large_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_large_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/conv_float_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depth_to_space_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depth_to_space_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depth_to_space_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_large_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_large_2_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_large_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_large_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv2d_float_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/depthwise_conv_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/dequantize_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/div_broadcast_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/div_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/embedding_lookup_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/floor_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_4d_simple_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_large_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_large_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/fully_connected_float_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/hashtable_lookup_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_normalization_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_normalization_large_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_normalization_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_pool_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_pool_float_large_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/l2_pool_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/local_response_norm_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/local_response_norm_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/local_response_norm_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/local_response_norm_float_4_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/logistic_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/logistic_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lsh_projection_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lsh_projection_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lsh_projection_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm2_state2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm2_state_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm3_state2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm3_state3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm3_state_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm_state2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/lstm_state_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/max_pool_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/max_pool_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/max_pool_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/max_pool_float_4_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mean_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mean_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mean_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mobilenet_224_gender_basic_fixed_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mul_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/mul_relu_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/pad_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/pad_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu1_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu1_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu6_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu6_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/relu_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/reshape_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/reshape_weights_as_inputs_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/resize_bilinear_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/resize_bilinear_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/rnn_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/rnn_state_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/softmax_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/softmax_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_batch_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_batch_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_batch_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_batch_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_depth_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_depth_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/space_to_depth_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/squeeze_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/squeeze_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_10_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_11_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_4_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_5_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_6_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_7_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_8_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_float_9_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/strided_slice_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/sub_broadcast_float_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/sub_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/svdf2_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/svdf_bias_present_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/svdf_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/svdf_state_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/tanh_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/transpose_float_1_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_1/transpose_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/add_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/argmax_1.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/argmax_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/argmax_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/argmin_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/argmin_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/argmin_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/avg_pool_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/axis_aligned_bbox_transform.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/batch_to_space_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bbox_graph.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_aux_input.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_cifg_peephole.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_float16_batch_major.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_float16_batch_major_aux_input.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_float16_batch_major_merge_outputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_merge_outputs.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_lstm_norm_fw_output.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/bidirectional_sequence_rnn.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/box_with_nms_limit_gaussian.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/box_with_nms_limit_hard.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/box_with_nms_limit_linear.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/channel_shuffle.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/concat_float16_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/concat_float16_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/concat_float16_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/concat_mixed_quant.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/concat_zero_sized.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/conv2d_dilation.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/conv2d_per_channel.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/conv2d_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/depth_to_space_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/depthwise_conv2d_dilation.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/depthwise_conv2d_per_channel.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/depthwise_conv2d_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/dequantize_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/detection_postprocess.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/div_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/equal.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/expand_dims.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/floor_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/fully_connected_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/generate_proposals.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/greater.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/greater_equal.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/grouped_conv2d.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/heatmap_max_keypoint.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/instance_normalization.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/l2_normalization_axis.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/l2_normalization_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/l2_pool_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/layer_norm_lstm.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/less.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/less_equal.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/local_response_normalization_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/log.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/log_softmax.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/logical_and.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/logical_not.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/logical_or.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/logistic_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lsh_projection_3_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lsh_projection_4_relaxed.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lsh_projection_deprecated.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lsh_projection_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm2_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm2_state2_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm2_state_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm3_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm3_state2_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm3_state3_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm3_state_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm_state2_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/lstm_state_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/max_pool_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/maximum.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/mean_float16.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/minimum.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/mul_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/not_equal.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_all_dims.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_low_rank.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_low_rank_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_quant8_nonzero.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_1_float.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_1_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_all_dims.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_all_dims_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_low_rank.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pad_v2_low_rank_quant8.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/pow.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/prelu.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/quantize.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/quantized_lstm.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/random_multinomial.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/random_multinomial_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/reduce_all.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/reduce_any.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/reduce_min.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/reduce_prod.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/reduce_sum.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/relu1_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/relu6_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/relu_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/reshape_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/resize_bilinear_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/resize_nearest_neighbor.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/rnn_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/roi_align.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/roi_pooling.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/select_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/sin.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/slice.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/softmax_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/space_to_batch_quant8_nonzero.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/space_to_batch_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/space_to_depth_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_float_1.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_float_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_float_3.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_float_4.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_float_5.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_int32_1.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_int32_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_int32_3.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_int32_4.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_quant8_1.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_quant8_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_quant8_3.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/split_quant8_4.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/squeeze_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/strided_slice_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/sub_quantized_different_scales.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/sub_v1_2.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/sub_v1_2_broadcast.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/svdf_bias_present_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/svdf_float16.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/svdf_state_float16.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/tanh_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/tile_1.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/tile_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/tile_3.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/transpose_conv2d.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/transpose_conv2d_large.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/transpose_float16.mod.py delete mode 100644 tests/nnapi/specs/skip/V1_2/transpose_v1_2.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_1step.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_batch_major_norm_peephole_projection.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_batch_major_peephole_projection_bias.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_cifg_peephole.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_f16_batch_major.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_f16_norm_peephole_projection.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_layer_norm_cifg_peephole.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_lstm_norm_peephole_projection.mod.py mode change 100644 => 100755 tests/nnapi/specs/skip/V1_2/unidirectional_sequence_rnn.mod.py create mode 100644 tests/nnfw_api/CMakeLists.txt create mode 100644 tests/nnfw_api/src/FourOneOpModelSetInput.cc create mode 100644 tests/nnfw_api/src/create_session.cc create mode 100644 tests/nnfw_api/src/fixtures.h create mode 100644 tests/nnfw_api/src/load_model.cc create mode 100644 tests/nnfw_api/src/main.cc create mode 100644 tests/nnfw_api/src/model_path.cc create mode 100644 tests/nnfw_api/src/model_path.h create mode 100644 tests/nnfw_api/src/prepare.cc create mode 100644 tests/scripts/CMakeLists.txt create mode 100755 tests/scripts/framework/run_test.sh create mode 100755 tests/scripts/framework/tests/MODELS/inception_module/config.sh create mode 100755 tests/scripts/framework/tests/MODELS/inception_nonslim/config.sh create mode 100755 tests/scripts/framework/tests/MODELS/inception_slim/config.sh create mode 100755 tests/scripts/framework/tests/MODELS/mobilenet/config.sh create mode 100755 tests/scripts/framework/tests/abs/config.sh create mode 100755 tests/scripts/framework/tests/add/1D/config.sh create mode 100755 tests/scripts/framework/tests/add/4D/config.sh create mode 100755 tests/scripts/framework/tests/average_pool_2d/aligned/config.sh create mode 100755 tests/scripts/framework/tests/average_pool_2d/avgpool1/config.sh create mode 100755 tests/scripts/framework/tests/average_pool_2d/avgpool2/config.sh create mode 100755 tests/scripts/framework/tests/batch_to_space_nd2/config.sh create mode 100755 tests/scripts/framework/tests/cast/config.sh create mode 100755 tests/scripts/framework/tests/concat/2D/config.sh create mode 100755 tests/scripts/framework/tests/concat/concat1/config.sh create mode 100755 tests/scripts/framework/tests/concat/concat2/config.sh create mode 100755 tests/scripts/framework/tests/conv_2d/convolution1/config.sh create mode 100755 tests/scripts/framework/tests/conv_2d/convolution2/config.sh create mode 100755 tests/scripts/framework/tests/custom/squared_difference/config.sh create mode 100755 tests/scripts/framework/tests/depthwise_conv_2d/depthconv1/config.sh create mode 100755 tests/scripts/framework/tests/depthwise_conv_2d/depthconv2/config.sh create mode 100755 tests/scripts/framework/tests/depthwise_conv_2d_no_fuse/config.sh create mode 100755 tests/scripts/framework/tests/div/broadcast/config.sh create mode 100755 tests/scripts/framework/tests/embedding_lookup/config.sh create mode 100755 tests/scripts/framework/tests/equal/config.sh create mode 100755 tests/scripts/framework/tests/exp/config.sh create mode 100755 tests/scripts/framework/tests/floor/floor1/config.sh create mode 100755 tests/scripts/framework/tests/floor/floor2/config.sh create mode 100755 tests/scripts/framework/tests/fullyconnected/fc1/config.sh create mode 100755 tests/scripts/framework/tests/fullyconnected/hybrid/config.sh create mode 100755 tests/scripts/framework/tests/fullyconnected/matmul2x2/config.sh create mode 100755 tests/scripts/framework/tests/fullyconnected/weights_as_input/config.sh create mode 100755 tests/scripts/framework/tests/gather/config.sh create mode 100755 tests/scripts/framework/tests/greater/config.sh create mode 100755 tests/scripts/framework/tests/greater_equal/config.sh create mode 100755 tests/scripts/framework/tests/hashtable_lookup/config.sh create mode 100755 tests/scripts/framework/tests/l2_normalization/config.sh create mode 100755 tests/scripts/framework/tests/l2_pool_2d/config.sh create mode 100755 tests/scripts/framework/tests/less/config.sh create mode 100755 tests/scripts/framework/tests/less_equal/config.sh create mode 100755 tests/scripts/framework/tests/logistic/config.sh create mode 100755 tests/scripts/framework/tests/max/config.sh create mode 100755 tests/scripts/framework/tests/max_pool_2d/maxpool1/config.sh create mode 100755 tests/scripts/framework/tests/max_pool_2d/maxpool2/config.sh create mode 100755 tests/scripts/framework/tests/mean/config.sh create mode 100755 tests/scripts/framework/tests/min/config.sh create mode 100755 tests/scripts/framework/tests/mul/broadcast/config.sh create mode 100755 tests/scripts/framework/tests/neg/config.sh create mode 100755 tests/scripts/framework/tests/not_equal/config.sh create mode 100755 tests/scripts/framework/tests/one_hot/config.sh create mode 100755 tests/scripts/framework/tests/pack/config.sh create mode 100755 tests/scripts/framework/tests/pad/4D_2D/config.sh create mode 100755 tests/scripts/framework/tests/pad/pad1/config.sh create mode 100755 tests/scripts/framework/tests/pad/pad2/config.sh create mode 100755 tests/scripts/framework/tests/reduce_max/config.sh create mode 100755 tests/scripts/framework/tests/reduce_mean/test1/config.sh create mode 100755 tests/scripts/framework/tests/reduce_mean/test2/config.sh create mode 100755 tests/scripts/framework/tests/reduce_sum/config.sh create mode 100755 tests/scripts/framework/tests/relu/config.sh create mode 100755 tests/scripts/framework/tests/relu6/config.sh create mode 100755 tests/scripts/framework/tests/reshape/3D/config.sh create mode 100755 tests/scripts/framework/tests/reshape/reshape1/config.sh create mode 100755 tests/scripts/framework/tests/reshape/reshape2/config.sh create mode 100755 tests/scripts/framework/tests/resize_bilinear/config.sh create mode 100755 tests/scripts/framework/tests/rnn/config.sh create mode 100755 tests/scripts/framework/tests/rsqrt/config.sh create mode 100644 tests/scripts/framework/tests/shape/config.sh create mode 100755 tests/scripts/framework/tests/sin/config.sh create mode 100755 tests/scripts/framework/tests/slice/config.sh create mode 100755 tests/scripts/framework/tests/softmax/config.sh create mode 100755 tests/scripts/framework/tests/space_to_batch_nd2/config.sh create mode 100755 tests/scripts/framework/tests/space_to_depth/config.sh create mode 100755 tests/scripts/framework/tests/sqrt/config.sh create mode 100755 tests/scripts/framework/tests/squeeze/config.sh create mode 100755 tests/scripts/framework/tests/strided_slice/config.sh create mode 100755 tests/scripts/framework/tests/sub/broadcast/config.sh create mode 100755 tests/scripts/framework/tests/tanh/config.sh create mode 100755 tests/scripts/framework/tests/topk_v2/config.sh create mode 100755 tests/scripts/framework/tests/transpose/config.sh create mode 100755 tests/scripts/framework/tests/transpose_conv/same/config.sh create mode 100755 tests/scripts/framework/tests/transpose_conv/valid/config.sh create mode 100644 tests/scripts/list/frameworktest_list.aarch64.acl_cl.txt create mode 100644 tests/scripts/list/frameworktest_list.aarch64.acl_neon.txt create mode 100644 tests/scripts/list/frameworktest_list.aarch64.cpu.txt create mode 100644 tests/scripts/list/frameworktest_list.armv7l.acl_cl.txt create mode 100644 tests/scripts/list/frameworktest_list.armv7l.acl_neon.txt create mode 100644 tests/scripts/list/frameworktest_list.armv7l.cpu.txt create mode 100644 tests/scripts/list/frameworktest_list.noarch.interp.txt create mode 100644 tests/scripts/list/frameworktest_list.x86_64.cpu.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.armv7l.acl_cl.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.armv7l.acl_neon.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.armv7l.cpu.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.armv7l.ncnn.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.noarch.interp.txt delete mode 100644 tests/scripts/list/neurun_frameworktest_list.x86-64.cpu.txt create mode 100644 tests/scripts/list/tflite_loader_list.aarch64.txt create mode 100755 tests/scripts/oneapi_test/install_oneapi_test_nnpackages.sh create mode 100644 tests/scripts/oneapi_test/models/add/config.sh create mode 100755 tools/cross/install_android_sdk.sh create mode 100755 tools/kernel_report/kernel_report.py create mode 100644 tools/nnpackage_tool/tf2tfliteV2/README.md create mode 100755 tools/nnpackage_tool/tf2tfliteV2/tf2tfliteV2.py mode change 100644 => 100755 tools/pbfile_tool/convert_ckpt_to_pb.py mode change 100644 => 100755 tools/pbfile_tool/convert_pb_to_pbtxt.py mode change 100644 => 100755 tools/pbfile_tool/extract_subgraph.py mode change 100644 => 100755 tools/tensorflow_model_freezer/__init__.py mode change 100644 => 100755 tools/tensorflow_model_freezer/base_freezer.py mode change 100644 => 100755 tools/tensorflow_model_freezer/model_freezer_util.py mode change 100644 => 100755 tools/tensorflow_model_freezer/sample/Operation_gen.py mode change 100644 => 100755 tools/tensorflow_model_freezer/sample/UNSTACK_gen.py mode change 100644 => 100755 tools/tensorflow_model_freezer/sample/__init__.py mode change 100644 => 100755 tools/tflitefile_tool/config_saver.py create mode 100755 tools/tflitefile_tool/graph_stats.py delete mode 100644 tools/tflitefile_tool/model_printer.py mode change 100644 => 100755 tools/tflitefile_tool/model_saver.py mode change 100644 => 100755 tools/tflitefile_tool/operator_printer.py mode change 100644 => 100755 tools/tflitefile_tool/option_printer.py delete mode 100755 tools/tflitefile_tool/perf_predictor.py mode change 100644 => 100755 tools/tflitefile_tool/select_operator.py create mode 100755 tools/tflitefile_tool/subgraph_printer.py mode change 100644 => 100755 tools/tflitefile_tool/tensor_printer.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/AbsOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ActivationFunctionType.py create mode 100755 tools/tflitefile_tool/tflite/AddNOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/AddOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ArgMaxOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ArgMinOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/BatchToSpaceNDOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/BidirectionalSequenceLSTMOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/BidirectionalSequenceRNNOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Buffer.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/BuiltinOperator.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/BuiltinOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/CallOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/CastOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/CombinerType.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ConcatEmbeddingsOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ConcatenationOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Conv2DOptions.py create mode 100755 tools/tflitefile_tool/tflite/CosOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/CustomOptionsFormat.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/CustomQuantization.py create mode 100755 tools/tflitefile_tool/tflite/DepthToSpaceOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/DepthwiseConv2DOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/DequantizeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/DivOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/EmbeddingLookupSparseOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/EqualOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ExpOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ExpandDimsOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FakeQuantOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FillOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FloorDivOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FloorModOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FullyConnectedOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/FullyConnectedOptionsWeightsFormat.py create mode 100755 tools/tflitefile_tool/tflite/GatherNdOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/GatherOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/GreaterEqualOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/GreaterOptions.py create mode 100755 tools/tflitefile_tool/tflite/HardSwishOptions.py create mode 100755 tools/tflitefile_tool/tflite/IfOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/L2NormOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LSHProjectionOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LSHProjectionType.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LSTMKernelType.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LSTMOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LeakyReluOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LessEqualOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LessOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LocalResponseNormalizationOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LogSoftmaxOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LogicalAndOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LogicalNotOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/LogicalOrOptions.py create mode 100755 tools/tflitefile_tool/tflite/MatrixDiagOptions.py create mode 100755 tools/tflitefile_tool/tflite/MatrixSetDiagOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/MaximumMinimumOptions.py delete mode 100644 tools/tflitefile_tool/tflite/MeanOptions.py create mode 100755 tools/tflitefile_tool/tflite/Metadata.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/MirrorPadMode.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/MirrorPadOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Model.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/MulOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/NegOptions.py create mode 100755 tools/tflitefile_tool/tflite/NonMaxSuppressionV4Options.py create mode 100755 tools/tflitefile_tool/tflite/NonMaxSuppressionV5Options.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/NotEqualOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/OneHotOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Operator.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/OperatorCode.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/PackOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/PadOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/PadV2Options.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Padding.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Pool2DOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/PowOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/QuantizationDetails.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/QuantizationParameters.py create mode 100755 tools/tflitefile_tool/tflite/QuantizeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/RNNOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/RangeOptions.py create mode 100755 tools/tflitefile_tool/tflite/RankOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ReducerOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ReshapeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ResizeBilinearOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ResizeNearestNeighborOptions.py create mode 100755 tools/tflitefile_tool/tflite/ReverseSequenceOptions.py create mode 100755 tools/tflitefile_tool/tflite/ReverseV2Options.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SVDFOptions.py create mode 100755 tools/tflitefile_tool/tflite/ScatterNdOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SelectOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SequenceRNNOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ShapeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SkipGramOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SliceOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SoftmaxOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SpaceToBatchNDOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SpaceToDepthOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SparseToDenseOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SplitOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SplitVOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SquareOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SquaredDifferenceOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SqueezeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/StridedSliceOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SubGraph.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/SubOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/Tensor.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/TensorType.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/TileOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/TopKV2Options.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/TransposeConvOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/TransposeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/UnidirectionalSequenceLSTMOptions.py create mode 100755 tools/tflitefile_tool/tflite/UniqueOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/UnpackOptions.py create mode 100755 tools/tflitefile_tool/tflite/WhereOptions.py create mode 100755 tools/tflitefile_tool/tflite/WhileOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/ZerosLikeOptions.py mode change 100644 => 100755 tools/tflitefile_tool/tflite/__init__.py mode change 100644 => 100755 tools/tflkit/summarize_pb.py diff --git a/.ctags b/.ctags index 6f33a26d3..13c27abbe 100644 --- a/.ctags +++ b/.ctags @@ -2,6 +2,6 @@ --exclude=Product --exclude=build --exclude=tags ---exclude=tests/framework/cache +--exclude=tests/scripts/framework/cache --exclude=tools/cross/rootfs --exclude=doxygen diff --git a/.gitignore b/.gitignore index 32c33603f..d0931912a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ *.pyc # Test cache for model download -/tests/framework/cache +/tests/scripts/framework/cache # Test report /report diff --git a/LICENSE b/LICENSE index 2411d90dc..c1507bf42 100644 --- a/LICENSE +++ b/LICENSE @@ -3,6 +3,8 @@ This file provides full text of licenses used in this project - Apache Licence 2.0 - MIT - BSD-2-Clause +- BSD 3-Clause +- Mozilla Public License 2.0 ............................................................................... @@ -211,9 +213,9 @@ limitations under the License. ............................................................................... -Copyright (c) 2016-2018 ARM Limited. +The MIT License -SPDX-License-Identifier: MIT +Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -234,33 +236,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ............................................................................. -COPYRIGHT -All contributions by the University of California: -Copyright (c) 2014-2017 The Regents of the University of California (Regents) -All rights reserved. +The BSD 2-Clause License -All other contributions: -Copyright (c) 2014-2017, the respective contributors -All rights reserved. - -Caffe uses a shared copyright model: each contributor holds copyright over -their contributions to Caffe. The project versioning records all such -contribution and copyright details. If a contributor wants to further mark -their specific copyright on a particular contribution, they should indicate -their copyright solely in the commit message of the change when it is -committed. - -LICENSE +Copyright Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -273,8 +261,396 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -CONTRIBUTION AGREEMENT +............................................................................. + +The BSD 3-Clause License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +............................................................................. + +Mozilla Public License Version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has + attached the notice in Exhibit A, the Executable Form of such Source + Code Form, and Modifications of such Source Code Form, in each + case including portions thereof. + +1.5. “Incompatible With Secondary Licenses” + + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms + of version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, + in a separate file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and all of + the rights conveyed by this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + b. any new file in Source Code Form that contains any Covered + Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of + this definition, “control” means (a) the power, direct or indirect, to + cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. License Grants and Conditions + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, non- +exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under this +License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + + a. for any code that a Contributor has removed from Covered Software; + or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence + of its Contributions. + +This License does not grant any rights in the trademarks, service marks, or +logos of any Contributor (except as may be necessary to comply with the +notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this License +(see Section 10.2) or under the terms of a Secondary License (if permitted +under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its Contributions +are its original creation(s) or it has sufficient rights to grant the rights to its +Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under applicable +copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in +Section 2.1. + +3. Responsibilities -By contributing to the BVLC/caffe repository through pull-request, comment, -or otherwise, the contributor releases their content to the -license and copyright terms herein. \ No newline at end of file +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source Code +Form of the Covered Software is governed by the terms of this License, +and how they can obtain a copy of this License. You may not attempt to +alter or restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source + Code Form by reasonable means in a timely manner, at a charge no + more than the cost of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients’ rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and +the Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of the +Larger Work may, at their option, further distribute the Covered Software +under the terms of either this License or such Secondary License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, or +limitations of liability) contained within the Source Code Form of the +Covered Software, except that You may alter any license notices to the +extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any such +warranty, support, indemnity, or liability obligation is offered by You +alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + +If it is impossible for You to comply with any of the terms of this License +with respect to some or all of the Covered Software due to statute, judicial +order, or regulation then You must: (a) comply with the terms of this +License to the maximum extent possible; and (b) describe the limitations +and the code they affect. Such description must be placed in a text file +included with all distributions of the Covered Software under this License. +Except to the extent prohibited by statute or regulation, such description +must be sufficiently detailed for a recipient of ordinary skill to be able to +understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if +You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the non- +compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor notifies +You of the non-compliance by some reasonable means, this is the first +time You have received notice of non-compliance with this License from +such Contributor, and You become compliant prior to 30 days after Your +receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, counter- +claims, and cross-claims) alleging that a Contributor Version directly or +indirectly infringes any patent, then the rights granted to You by any and +all Contributors for the Covered Software under Section 2.1 of this +License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end +user license agreements (excluding distributors and resellers) which have +been validly granted by You or Your distributors under this License prior +to termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” + basis, without warranty of any kind, either expressed, implied, + or statutory, including, without limitation, warranties that the + Covered Software is free of defects, merchantable, fit for a + particular purpose or non-infringing. The entire risk as to the + quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary + servicing, repair, or correction. This disclaimer of warranty + constitutes an essential part of this License. No use of any + Covered Software is authorized under this License except + under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort + (including negligence), contract, or otherwise, shall any + Contributor, or anyone who distributes Covered Software as + permitted above, be liable to You for any direct, indirect, + special, incidental, or consequential damages of any character + including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or + any and all other commercial damages or losses, even if such + party shall have been informed of the possibility of such + damages. This limitation of liability shall not apply to liability + for death or personal injury resulting from such party’s + negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or + limitation of incidental or consequential damages, so this + exclusion and limitation may not apply to You. + +8. Litigation + +Any litigation relating to this License may be brought only in the courts of +a jurisdiction where the defendant maintains its principal place of business +and such litigation shall be governed by laws of that jurisdiction, without +reference to its conflict-of-law provisions. Nothing in this Section shall +prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be unenforceable, +such provision shall be reformed only to the extent necessary to make it +enforceable. Any law or regulation which provides that the language of a +contract shall be construed against the drafter shall not be used to construe +this License against a Contributor. + +10. Versions of the License + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in +Section 10.3, no one other than the license steward has the right to modify +or publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version of +the License under which You originally received the Covered Software, or +under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a modified +version of this License if you rename the license and remove any +references to the name of the license steward (except to note that such +modified license differs from this License). + +10.4. Distributing Source Code Form that isIncompatible With Secondary Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the terms of the Mozilla + Public License, v. 2.0. If a copy of the MPL was not distributed + with this file, You can obtain one at + https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible With Secondary + Licenses”, as defined by the Mozilla Public License, v. 2.0. diff --git a/Makefile.template b/Makefile.template index ef3a44c15..588a30052 100644 --- a/Makefile.template +++ b/Makefile.template @@ -155,7 +155,7 @@ build_test_suite: install_internal install_internal_acl @echo "packaging test suite" @rm -rf $(INSTALL_PATH)/test-suite.tar.gz # TODO Divide runtime package, external library package, and test suite - @tar -zcf test-suite.tar.gz tests/scripts tests/framework infra Product/out --dereference + @tar -zcf test-suite.tar.gz tests/scripts infra Product/out --dereference @mv test-suite.tar.gz $(INSTALL_PATH)/. build_coverage_suite: install_internal install_internal_acl @@ -163,7 +163,7 @@ build_coverage_suite: install_internal install_internal_acl @rm -rf $(INSTALL_PATH)/coverage-suite.tar.gz @find Product -name "*.gcno" > include_lists.txt @pwd | grep -o '/' | wc -l > tests/scripts/build_path_depth.txt - @tar -zcf coverage-suite.tar.gz tests/scripts tests/framework infra Product/out --dereference -T include_lists.txt + @tar -zcf coverage-suite.tar.gz tests/scripts infra Product/out --dereference -T include_lists.txt @rm -rf include_lists.txt tests/scripts/build_path_depth.txt @mv coverage-suite.tar.gz $(INSTALL_PATH)/. diff --git a/README.md b/README.md index 8772bb120..dfa91afa8 100644 --- a/README.md +++ b/README.md @@ -34,18 +34,18 @@ the target platform, such as the Linux kernel based OS including Tizen. You can suggest development of nnfw's features that are not yet available. -The functions requested so far can be checked in the [popular feature request](https://github.sec.samsung.net/STAR/nnfw/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AFEATURE_REQUEST+sort%3Areactions-%2B1-desc) list. +The functions requested so far can be checked in the [popular feature request](https://github.com/Samsung/ONE/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AFEATURE_REQUEST+sort%3Areactions-%2B1-desc) list. - If the feature you want is on the list, :+1: to the body of the issue. The feature with the most :+1: is placed at the top of the list. When adding new features, we will prioritize them with this reference. Of course, it is good to add an additional comment which describes your request in detail. -- For features not listed, [create a new issue](https://github.sec.samsung.net/STAR/nnfw/issues/new). +- For features not listed, [create a new issue](https://github.com/Samsung/ONE/issues/new). Sooner or later, the maintainer will tag the `FEATURE_REQUEST` label and appear on the list. We expect one of the most frequent feature requests would be the operator kernel implementation. It is good to make a request, but it is better if you contribute by yourself. See the following guide, -[How to Implement Operator Kernel](docs/nnfw/HowToImplementOperatorKernel.md), for help. +[How to Implement Operator Kernel](docs/nnfw/howto/HowToAddNewOperation.md), for help. We are looking forward to your participation. Thank you in advance! @@ -71,25 +71,4 @@ NPU. ## How to Contact -- Please post questions, issues, or suggestions into [Issues](https://github.sec.samsung.net/STAR/nnfw/issues). - ----- - -## Notice - -### 22/07/2019 - -Congratulations! On July 22nd, 2019, _nnfw_ repo and -[_nncc_](https://github.sec.samsung.net/STAR/nncc) repo are finally integrated into single one. Now -all activities related to the development of _nnas(Neural Network Acceleration Solution)_ will -proceed in this integrated _nnfw_ repo. The old _nncc_ repo will only be maintained for follow up on -remaining issues and for preserving development history. The following notice will remain in place -until the update of documents in integrated repo is complete. - -### 02/05/2019 - -~~We are currently working on [_nncc_](https://github.sec.samsung.net/STAR/nncc) as a sibling project. -In our plan, the two projects will soon be integrated into one, and focusing on their roles as -front-end(_nncc_) and back-end(_nnfw_), respectively. It will accompany the physical combination of -the github repo.~~ You can find the latest roadmap of the integrated project -[here](https://github.sec.samsung.net/orgs/STAR/projects/1). +- Please post questions, issues, or suggestions into [Issues](https://github.com/Samsung/ONE/issues). diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt new file mode 100644 index 000000000..7cf12f164 --- /dev/null +++ b/compiler/CMakeLists.txt @@ -0,0 +1,78 @@ +# TODO Validate the argument of "requires" +function(get_project_build_order VAR) + # This file will describe the dependencies among projects + set(DEPS_FILE "${CMAKE_CURRENT_BINARY_DIR}/compiler.deps") + + # Remove .deps file + file(REMOVE "${DEPS_FILE}") + + # Let's create .deps file + list_subdirectories(PROJECT_DIRS) + + foreach(PROJECT_DIR IN ITEMS ${PROJECT_DIRS}) + set(SUCC "${PROJECT_DIR}") + set(REQUIRES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_DIR}/requires.cmake") + + macro(require PRED) + file(APPEND "${DEPS_FILE}" "${PRED} ${SUCC} ") + endmacro(require) + + file(APPEND "${DEPS_FILE}" "${SUCC} ${SUCC} ") + if(EXISTS "${REQUIRES_FILE}") + include(${REQUIRES_FILE}) + endif(EXISTS "${REQUIRES_FILE}") + endforeach(PROJECT_DIR) + + # NOTE "tsort" is a part of the POSIX.1 standard. + # + # Reference: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/tsort.html + execute_process(COMMAND tsort "${DEPS_FILE}" + OUTPUT_VARIABLE ORDER + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Remove newline characters + # TODO Check which one (UNIX_COMMAND or WINDOWS_COMMAND) is correct + separate_arguments(ORDER UNIX_COMMAND ${ORDER}) + + set(${VAR} "${ORDER}" PARENT_SCOPE) +endfunction(get_project_build_order) + +function(add_compiler_directory DIR) + string(TOUPPER ${DIR} PREFIX) + + option(BUILD_COMPILER_${PREFIX} "Build compiler/${dir}" ON) + set(BUILD_WHITELIST "" CACHE STRING "Set modules to be built") + + if(NOT BUILD_WHITELIST STREQUAL "") + set(ENABLE OFF) + set(CURRENT_DIR ${DIR}) + foreach(ACCEPTED_DIR IN ITEMS ${BUILD_WHITELIST}) + if(ACCEPTED_DIR STREQUAL CURRENT_DIR) + set(ENABLE ON) + endif() + endforeach(ACCEPTED_DIR) + else() + set(ENABLE ${BUILD_COMPILER_${PREFIX}}) + endif() + + # This line prevents some errors in this CMakeLists.txt + if(NOT DEFINED ENABLE) + message(FATAL_ERROR "Undefined ENABLE! Please check CMakeLists.txt") + endif() + + if(ENABLE) + message(STATUS "Configure ${PREFIX}") + add_subdirectory(${DIR}) + message(STATUS "Configure ${PREFIX} - Done") + endif(ENABLE) +endfunction(add_compiler_directory) + +function(add_compiler_directories) + get_project_build_order(PROJECT_DIRS) + + foreach(PROJECT_DIR IN ITEMS ${PROJECT_DIRS}) + add_compiler_directory(${PROJECT_DIR}) + endforeach(PROJECT_DIR) +endfunction(add_compiler_directories) + +add_compiler_directories() diff --git a/compiler/adtidas/CMakeLists.txt b/compiler/adtidas/CMakeLists.txt new file mode 100644 index 000000000..0d84740b7 --- /dev/null +++ b/compiler/adtidas/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(adtidas INTERFACE) +target_include_directories(adtidas INTERFACE include) diff --git a/compiler/adtidas/include/adtidas/SmallVector.h b/compiler/adtidas/include/adtidas/SmallVector.h new file mode 100644 index 000000000..1ad630c63 --- /dev/null +++ b/compiler/adtidas/include/adtidas/SmallVector.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ADTIDAS_SMALL_VECTOR_H_ +#define _ADTIDAS_SMALL_VECTOR_H_ + +#include +#include +#include + +namespace adt +{ + +/** + * @brief vector with cheap memory allocation + * @tparam T type of elements + * @tparam Capacity maximum number of elements + * @note much like std::array, but tracks number of used elements. Stored in stack + */ +template class small_vector +{ +public: + using value_type = T; + using reference = T &; + using iterator = T *; + using const_iterator = const T *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = size_t; + + template small_vector(It begin, It end) : _size(std::distance(begin, end)) + { + assert(_size <= Capacity); + std::copy(begin, end, this->begin()); + } + + explicit small_vector(size_t size, value_type initializer = value_type()) : _size(size) + { + assert(_size <= Capacity); + std::fill(begin(), end(), initializer); + } + + explicit small_vector() : _size(0) {} + + small_vector(std::initializer_list l) : _size(l.size()) + { + assert(_size <= Capacity); + std::copy(std::begin(l), std::end(l), begin()); + } + + /** + * @return current size + */ + inline size_t size() const noexcept { return _size; } + + /** + * @return maximum number of elements this vector can hold + */ + constexpr size_t capacity() const { return Capacity; } + + /** + * @brief resize to given new size + * @note if new size is greater than current size, new elements are default-initialized + */ + void resize(size_t new_size) noexcept + { + assert(new_size <= Capacity); + if (new_size > _size) + { + std::fill(_storage + _size, _storage + new_size, T()); + } + _size = new_size; + } + + /** + * @return reference to the element at position idx + */ + inline reference operator[](size_t idx) noexcept + { + assert(idx < _size); + return _storage[idx]; + } + + /** + * @return value of element at position idx + */ + inline constexpr value_type operator[](size_t idx) const noexcept + { + // assert on the same line since c++11 does not allow multi-line constexpr functions + return assert(idx < _size), _storage[idx]; + } + + inline iterator begin() noexcept { return std::begin(_storage); } + inline iterator end() noexcept { return _storage + _size; } + + inline reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; } + inline reverse_iterator rend() noexcept { return reverse_iterator{begin()}; } + + // const overloads + inline const_iterator begin() const noexcept { return std::begin(_storage); } + inline const_iterator end() const noexcept { return _storage + _size; } + + inline const_reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + inline const_reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + inline void push_back(const value_type &e) noexcept + { + assert(_size < Capacity); + _storage[_size++] = e; + } + + inline void push_back(value_type &&e) noexcept + { + assert(_size < Capacity); + _storage[_size++] = std::move(e); + } + +private: + size_t _size; + value_type _storage[Capacity]{}; +}; + +template +bool operator==(const small_vector &lhs, const small_vector &rhs) +{ + if (lhs.size() != rhs.size()) + { + return false; + } + + bool equal = true; + size_t end = lhs.size(); + for (size_t i = 0; i < end; ++i) + { + equal &= (lhs[i] == rhs[i]); + } + + return equal; +} + +} // namespace adt + +#endif //_ADTIDAS_SMALL_VECTOR_H_ diff --git a/compiler/angkor/CMakeLists.txt b/compiler/angkor/CMakeLists.txt new file mode 100644 index 000000000..44b5e9058 --- /dev/null +++ b/compiler/angkor/CMakeLists.txt @@ -0,0 +1,22 @@ +file(GLOB_RECURSE HEADERS "include/*.h") +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +# NOTE STATIC is deliberately used here to allow clients to use 'angkor' without installation +add_library(angkor STATIC ${HEADERS} ${SOURCES}) +set_target_properties(angkor PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(angkor PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(angkor PUBLIC include) +target_link_libraries(angkor PRIVATE nncc_common) +target_link_libraries(angkor PUBLIC nncc_coverage) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for test +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(angkor_test ${TESTS}) +target_link_libraries(angkor_test angkor) diff --git a/compiler/angkor/README.md b/compiler/angkor/README.md new file mode 100644 index 000000000..f761b8740 --- /dev/null +++ b/compiler/angkor/README.md @@ -0,0 +1,51 @@ +# angkor + +## Purpose + +_angkor_ is a `nncc` core library + +## How to use + +_angkor_ implements abstract data type(ADT) for feature, kernel, tensor. +There are layout, shape information and enumerator and so on. + +To use some of these things, just insert `include`! +```cpp +#include +#include +#include +``` + +## Example + +- `compiler/coco/core/CMakeLists.txt` + +```cmake +target_link_libraries(coco_core PUBLIC angkor) +``` + +- `compiler/coco/core/src/IR/Arg.cpp` + +```cpp +#include "coco/IR/Arg.h" + +#include +#include + +namespace +{ +const nncc::core::ADT::tensor::LexicalLayout l; +} + +namespace coco +{ + +Arg::Arg(const nncc::core::ADT::tensor::Shape &shape) : _shape{shape}, _bag{nullptr} +{ + _map.resize(nncc::core::ADT::tensor::num_elements(shape)); +} + +// .... + +} +``` diff --git a/compiler/angkor/include/angkor/TensorIndex.h b/compiler/angkor/include/angkor/TensorIndex.h new file mode 100644 index 000000000..2fc10509e --- /dev/null +++ b/compiler/angkor/include/angkor/TensorIndex.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANGKOR_TENSOR_INDEX_H__ +#define __ANGKOR_TENSOR_INDEX_H__ + +#include "nncc/core/ADT/tensor/Index.h" + +namespace angkor +{ + +using TensorIndex = ::nncc::core::ADT::tensor::Index; + +} // namespace angkor + +#endif // __ANGKOR_TENSOR_INDEX_H__ diff --git a/compiler/angkor/include/angkor/TensorShape.h b/compiler/angkor/include/angkor/TensorShape.h new file mode 100644 index 000000000..ab62bd8d9 --- /dev/null +++ b/compiler/angkor/include/angkor/TensorShape.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANGKOR_TENSOR_SHAPE_H__ +#define __ANGKOR_TENSOR_SHAPE_H__ + +#include "nncc/core/ADT/tensor/Shape.h" + +namespace angkor +{ + +using TensorShape = ::nncc::core::ADT::tensor::Shape; + +} // namespace angkor + +#endif // __ANGKOR_TENSOR_SHAPE_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Accessor.h b/compiler/angkor/include/nncc/core/ADT/feature/Accessor.h new file mode 100644 index 000000000..aa4621851 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Accessor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_ACCESSOR_H__ +#define __NNCC_CORE_ADT_FEATURE_ACCESSOR_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +template struct Accessor +{ + virtual ~Accessor() = default; + + virtual T &at(uint32_t ch, uint32_t row, uint32_t col) = 0; +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_ACCESSOR_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Buffer.h b/compiler/angkor/include/nncc/core/ADT/feature/Buffer.h new file mode 100644 index 000000000..86fd60295 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Buffer.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_BUFFER_H__ +#define __NNCC_CORE_ADT_FEATURE_BUFFER_H__ + +#include "nncc/core/ADT/feature/View.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +template class Buffer final : public View +{ +public: + explicit Buffer(const Shape &shape, const Layout &layout) : View{shape, layout} + { + _buffer.resize(num_elements(shape)); + } + +public: + virtual T *base(void) { return _buffer.data(); } + virtual const T *base(void) const { return _buffer.data(); } + +private: + std::vector _buffer; +}; + +template Buffer make_buffer(const Shape &shape) +{ + return Buffer{shape, LayoutImpl{}}; +} + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_BUFFER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/CHWLayout.h b/compiler/angkor/include/nncc/core/ADT/feature/CHWLayout.h new file mode 100644 index 000000000..d84841d10 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/CHWLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_CHW_LAYOUT_H__ +#define __NNCC_CORE_ADT_FEATURE_CHW_LAYOUT_H__ + +#include "nncc/core/ADT/feature/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +struct CHWLayout final : public Layout +{ + CHWLayout(); +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_CHW_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/HWCLayout.h b/compiler/angkor/include/nncc/core/ADT/feature/HWCLayout.h new file mode 100644 index 000000000..df885ad82 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/HWCLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_HWC_LAYOUT_H__ +#define __NNCC_CORE_ADT_FEATURE_HWC_LAYOUT_H__ + +#include "nncc/core/ADT/feature/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +struct HWCLayout final : public Layout +{ + HWCLayout(); +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_HWC_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Layout.h b/compiler/angkor/include/nncc/core/ADT/feature/Layout.h new file mode 100644 index 000000000..762545a84 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Layout.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_LAYOUT_H__ +#define __NNCC_CORE_ADT_FEATURE_LAYOUT_H__ + +#include "nncc/core/ADT/feature/Shape.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +class Layout +{ +public: + using Func = uint32_t (*)(const Shape &, uint32_t ch, uint32_t row, uint32_t col); + +public: + explicit Layout(const Func &func); + +public: + uint32_t offset(const Shape &shape, uint32_t ch, uint32_t row, uint32_t col) const + { + return _func(shape, ch, row, col); + } + +private: + Func _func; +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h b/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h new file mode 100644 index 000000000..93d86f56b --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_OVERLAY_H__ +#define __NNCC_CORE_ADT_FEATURE_OVERLAY_H__ + +#include "nncc/core/ADT/feature/View.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +template class Overlay final : public View +{ +public: + explicit Overlay(const Shape &shape, const Layout &layout, T *base) + : View{shape, layout}, _base{base} + { + // DO NOTHING + } + +public: + virtual T *base(void) { return _base; } + virtual const T *base(void) const { return _base; } + +private: + T *const _base; +}; + +template Overlay make_overlay(const Shape &shape, T *base) +{ + return Overlay{shape, LayoutImpl{}, base}; +} + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_OVERLAY_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Reader.h b/compiler/angkor/include/nncc/core/ADT/feature/Reader.h new file mode 100644 index 000000000..9a6fb724b --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Reader.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_READER_H__ +#define __NNCC_CORE_ADT_FEATURE_READER_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +template struct Reader +{ + virtual ~Reader() = default; + + virtual T at(uint32_t ch, uint32_t row, uint32_t col) const = 0; +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_READER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Shape.h b/compiler/angkor/include/nncc/core/ADT/feature/Shape.h new file mode 100644 index 000000000..319326308 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/Shape.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_SHAPE_H__ +#define __NNCC_CORE_ADT_FEATURE_SHAPE_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +// +// Shape of Feature Map for Convolution +// +class Shape +{ +public: + Shape(uint32_t depth, uint32_t height, uint32_t width) + : _depth{depth}, _height{height}, _width{width} + { + // DO NOTHING + } + +public: + uint32_t depth(void) const { return _depth; } + uint32_t height(void) const { return _height; } + uint32_t width(void) const { return _width; } + +private: + uint32_t _depth; + uint32_t _height; + uint32_t _width; +}; + +/** + * @brief The number of elements of a feature map of a given shape + * + * WARN The result is valid only when the expected value is less than 2^32 - 1 + */ +inline uint32_t num_elements(const Shape &shape) +{ + return shape.depth() * shape.height() * shape.width(); +} + +inline bool operator==(const Shape &l, const Shape &r) +{ + return (l.depth() == r.depth()) && (l.height() == r.height()) && (l.width() == r.width()); +} + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_SHAPE_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/feature/View.h b/compiler/angkor/include/nncc/core/ADT/feature/View.h new file mode 100644 index 000000000..856e22b4b --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/feature/View.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_FEATURE_VIEW_H__ +#define __NNCC_CORE_ADT_FEATURE_VIEW_H__ + +#include "nncc/core/ADT/feature/Shape.h" +#include "nncc/core/ADT/feature/Reader.h" +#include "nncc/core/ADT/feature/Accessor.h" +#include "nncc/core/ADT/feature/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +template class View : public Reader, public Accessor +{ +public: + explicit View(const Shape &shape, const Layout &layout) : _shape{shape}, _layout{layout} + { + // DO NOTHING + } + +public: + virtual T *base(void) = 0; + virtual const T *base(void) const = 0; + +public: + T at(uint32_t ch, uint32_t row, uint32_t col) const override final + { + return *(base() + _layout.offset(_shape, ch, row, col)); + } + +public: + T &at(uint32_t ch, uint32_t row, uint32_t col) override final + { + return *(base() + _layout.offset(_shape, ch, row, col)); + } + +public: + const Shape &shape(void) const { return _shape; } + +private: + const Shape _shape; + const Layout _layout; +}; + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_FEATURE_VIEW_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Accessor.h b/compiler/angkor/include/nncc/core/ADT/kernel/Accessor.h new file mode 100644 index 000000000..5bc46de36 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Accessor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_ACCESSOR_H__ +#define __NNCC_CORE_ADT_KERNEL_ACCESSOR_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template struct Accessor +{ + virtual ~Accessor() = default; + + virtual T &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) = 0; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_ACCESSOR_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Buffer.h b/compiler/angkor/include/nncc/core/ADT/kernel/Buffer.h new file mode 100644 index 000000000..3497d4829 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Buffer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_BUFFER_H__ +#define __NNCC_CORE_ADT_KERNEL_BUFFER_H__ + +#include "nncc/core/ADT/kernel/View.h" +#include "nncc/core/ADT/kernel/ViewImpl.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template class Buffer final : public View +{ +public: + explicit Buffer(const Shape &shape, const Layout &layout) : _impl{shape, layout} + { + _buffer.resize(num_elements(shape)); + } + +public: + T at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override + { + return _impl.at(_buffer.begin(), nth, ch, row, col); + } + +public: + T &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) override + { + return _impl.at(_buffer.begin(), nth, ch, row, col); + } + +public: + const Shape &shape(void) const override { return _impl.shape(); } + +private: + std::vector _buffer; + ViewImpl _impl; +}; + +template Buffer make_buffer(const Shape &shape) +{ + return Buffer{shape, LayoutImpl{}}; +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_BUFFER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/IndexEnumerator.h b/compiler/angkor/include/nncc/core/ADT/kernel/IndexEnumerator.h new file mode 100644 index 000000000..4167ef972 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/IndexEnumerator.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__ +#define __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__ + +#include "nncc/core/ADT/kernel/Shape.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +class IndexEnumerator +{ +public: + explicit IndexEnumerator(const Shape &shape); + +public: + IndexEnumerator(IndexEnumerator &&) = delete; + IndexEnumerator(const IndexEnumerator &) = delete; + +public: + bool valid(void) const; + +public: + uint32_t count(void) const; + uint32_t depth(void) const; + uint32_t height(void) const; + uint32_t width(void) const; + +public: + void advance(void); + +private: + // Store max and current offset for count/depth/height/width + // + // NOTE Here explicit array is used instead of kernel::Shape to make + // a room for improvement such as enumeration order (NHWC, NCHW) + // support + uint32_t _max[4]; + uint32_t _cur[4]; + +private: + uint32_t _cursor; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Layout.h b/compiler/angkor/include/nncc/core/ADT/kernel/Layout.h new file mode 100644 index 000000000..1e85e1ed4 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Layout.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_LAYOUT_H__ +#define __NNCC_CORE_ADT_KERNEL_LAYOUT_H__ + +#include "nncc/core/ADT/kernel/Shape.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +class Layout +{ +public: + using Func = uint32_t (*)(const Shape &, uint32_t n, uint32_t ch, uint32_t row, uint32_t col); + +public: + Layout(const Func &func); + +public: + uint32_t offset(const Shape &shape, uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const + { + return _func(shape, n, ch, row, col); + } + +private: + Func _func; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/NCHWLayout.h b/compiler/angkor/include/nncc/core/ADT/kernel/NCHWLayout.h new file mode 100644 index 000000000..72bd89fb9 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/NCHWLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_NCHW_LAYOUT_H__ +#define __NNCC_CORE_ADT_KERNEL_NCHW_LAYOUT_H__ + +#include "nncc/core/ADT/kernel/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +struct NCHWLayout final : public Layout +{ + NCHWLayout(); +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_NCHW_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/NHWCLayout.h b/compiler/angkor/include/nncc/core/ADT/kernel/NHWCLayout.h new file mode 100644 index 000000000..bb239b91f --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/NHWCLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_NHWC_LAYOUT_H__ +#define __NNCC_CORE_ADT_KERNEL_NHWC_LAYOUT_H__ + +#include "nncc/core/ADT/kernel/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +struct NHWCLayout final : public Layout +{ + NHWCLayout(); +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_NHWC_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h b/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h new file mode 100644 index 000000000..e348a8769 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_OVERLAY_H__ +#define __NNCC_CORE_ADT_KERNEL_OVERLAY_H__ + +#include "nncc/core/ADT/kernel/View.h" +#include "nncc/core/ADT/kernel/ViewImpl.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template class Overlay final : public View +{ +public: + explicit Overlay(const Shape &shape, const Layout &layout, InputIt it) + : _impl{shape, layout}, _it{it} + { + // DO NOTHING + } + +public: + T at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override + { + return _impl.at(_it, nth, ch, row, col); + } + +public: + T &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) override + { + return _impl.at(_it, nth, ch, row, col); + } + +public: + const Shape &shape(void) const override { return _impl.shape(); } + +private: + InputIt const _it; + ViewImpl _impl; +}; + +template struct OverlayFactory +{ + template static Overlay make(const Shape &shape, InputIt it) + { + return Overlay{shape, LayoutImpl{}, it}; + } +}; + +template Overlay make_overlay(const Shape &shape, T *base) +{ + return OverlayFactory::make(shape, base); +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_OVERLAY_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Reader.h b/compiler/angkor/include/nncc/core/ADT/kernel/Reader.h new file mode 100644 index 000000000..af0267745 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Reader.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_READER_H__ +#define __NNCC_CORE_ADT_KERNEL_READER_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template struct Reader +{ + virtual ~Reader() = default; + + virtual T at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const = 0; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_READER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h b/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h new file mode 100644 index 000000000..d485d526b --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_SHAPE_H__ +#define __NNCC_CORE_ADT_KERNEL_SHAPE_H__ + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +// +// Shape of Convolution Kernel +// +class Shape +{ +public: + Shape(uint32_t count, uint32_t depth, uint32_t height, uint32_t width) + : _count{count}, _depth{depth}, _height{height}, _width{width} + { + // DO NOTHING + } + +public: + uint32_t count(void) const { return _count; } + uint32_t depth(void) const { return _depth; } + uint32_t height(void) const { return _height; } + uint32_t width(void) const { return _width; } + +private: + uint32_t _count; + uint32_t _depth; + uint32_t _height; + uint32_t _width; +}; + +/** + * @brief Return the number of elements in a kernel of a given shape + * + * WARN The result is valid only when the expected value is less than 2^32 - 1 + */ +inline uint32_t num_elements(const Shape &shape) +{ + return shape.count() * shape.depth() * shape.height() * shape.width(); +} + +bool operator==(const Shape &lhs, const Shape &rhs); + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_SHAPE_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/View.h b/compiler/angkor/include/nncc/core/ADT/kernel/View.h new file mode 100644 index 000000000..2ed682a51 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/View.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_VIEW_H__ +#define __NNCC_CORE_ADT_KERNEL_VIEW_H__ + +#include "nncc/core/ADT/kernel/Shape.h" +#include "nncc/core/ADT/kernel/Reader.h" +#include "nncc/core/ADT/kernel/Accessor.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template struct View : public Reader, public Accessor +{ + virtual const Shape &shape(void) const = 0; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_VIEW_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/ViewImpl.h b/compiler/angkor/include/nncc/core/ADT/kernel/ViewImpl.h new file mode 100644 index 000000000..f4e8ed5e2 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/kernel/ViewImpl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_KERNEL_VIEW_IMPL_H__ +#define __NNCC_CORE_ADT_KERNEL_VIEW_IMPL_H__ + +#include "nncc/core/ADT/kernel/Shape.h" +#include "nncc/core/ADT/kernel/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +template class ViewImpl +{ +public: + explicit ViewImpl(const Shape &shape, const Layout &layout) : _shape{shape}, _layout{layout} + { + // DO NOTHING + } + +public: + template + T at(InputIt it, uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const + { + return *(it + _layout.offset(_shape, nth, ch, row, col)); + } + +public: + template + T &at(InputIt it, uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) + { + return *(it + _layout.offset(_shape, nth, ch, row, col)); + } + +public: + const Shape &shape(void) const { return _shape; } + +private: + const Shape _shape; + const Layout _layout; +}; + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_KERNEL_VIEW_IMPL_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Accessor.h b/compiler/angkor/include/nncc/core/ADT/tensor/Accessor.h new file mode 100644 index 000000000..6a60b4b34 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Accessor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_ACCESSOR_H__ +#define __NNCC_CORE_ADT_TENSOR_ACCESSOR_H__ + +#include "nncc/core/ADT/tensor/Index.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +template struct Accessor +{ + virtual ~Accessor() = default; + + virtual T &at(const Index &) = 0; +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_ACCESSOR_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Buffer.h b/compiler/angkor/include/nncc/core/ADT/tensor/Buffer.h new file mode 100644 index 000000000..f62f3040f --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Buffer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_BUFFER_H__ +#define __NNCC_CORE_ADT_TENSOR_BUFFER_H__ + +#include "nncc/core/ADT/tensor/View.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +template class Buffer final : public View +{ +public: + explicit Buffer(const Shape &shape, const Layout &layout) : View{shape, layout} + { + _buffer.resize(num_elements(shape)); + } + +public: + T *base(void) override { return _buffer.data(); } + const T *base(void) const override { return _buffer.data(); } + +private: + std::vector _buffer; +}; + +template Buffer make_buffer(const Shape &shape) +{ + return Buffer{shape, LayoutImpl{}}; +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_BUFFER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Index.h b/compiler/angkor/include/nncc/core/ADT/tensor/Index.h new file mode 100644 index 000000000..19beafafc --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Index.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_INDEX_H__ +#define __NNCC_CORE_ADT_TENSOR_INDEX_H__ + +#include +#include +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +class Index +{ +public: + Index() = default; + Index(std::initializer_list &&l); + +public: + uint32_t rank(void) const; + +public: + Index &resize(uint32_t size); + +public: + Index &fill(uint32_t index); + +public: + uint32_t &at(uint32_t axis); + uint32_t at(uint32_t axis) const; + +private: + std::vector _indices; +}; + +// It throws an exception when rank of inputs does not match. +Index operator+(const Index &lhs, const Index &rhs); +bool operator==(const Index &lhs, const Index &rhs); + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_INDEX_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/IndexEnumerator.h b/compiler/angkor/include/nncc/core/ADT/tensor/IndexEnumerator.h new file mode 100644 index 000000000..ef85b2c10 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/IndexEnumerator.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_INDEX_ENUMERATOR_H__ +#define __NNCC_CORE_ADT_TENSOR_INDEX_ENUMERATOR_H__ + +#include "nncc/core/ADT/tensor/Index.h" +#include "nncc/core/ADT/tensor/Shape.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +class IndexEnumerator +{ +public: + explicit IndexEnumerator(const Shape &shape); + +public: + IndexEnumerator(IndexEnumerator &&) = delete; + IndexEnumerator(const IndexEnumerator &) = delete; + +public: + bool valid(void) const { return _cursor < _shape.rank(); } + +public: + const Index ¤t(void) const { return _index; } + +public: + void advance(void); + +private: + const Shape _shape; + Index _index; + +private: + uint32_t _cursor; +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_INDEX_ENUMERATOR_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Layout.h b/compiler/angkor/include/nncc/core/ADT/tensor/Layout.h new file mode 100644 index 000000000..0e410ff01 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Layout.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_LAYOUT_H__ +#define __NNCC_CORE_ADT_TENSOR_LAYOUT_H__ + +#include "nncc/core/ADT/tensor/Shape.h" +#include "nncc/core/ADT/tensor/Index.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +class Layout +{ +public: + using Func = uint32_t (*)(const Shape &, const Index &); + +public: + explicit Layout(const Func &func); + +public: + uint32_t offset(const Shape &shape, const Index &index) const { return _func(shape, index); } + +private: + Func _func; +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/LexicalLayout.h b/compiler/angkor/include/nncc/core/ADT/tensor/LexicalLayout.h new file mode 100644 index 000000000..b497ad844 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/LexicalLayout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_LEXICAL_LAYOUT_H__ +#define __NNCC_CORE_ADT_TENSOR_LEXICAL_LAYOUT_H__ + +#include "nncc/core/ADT/tensor/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +struct LexicalLayout final : public Layout +{ + LexicalLayout(); +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_LEXICAL_LAYOUT_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h b/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h new file mode 100644 index 000000000..11ee5350c --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_OVERLAY_H__ +#define __NNCC_CORE_ADT_TENSOR_OVERLAY_H__ + +#include "nncc/core/ADT/tensor/View.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +template class Overlay final : public View +{ +public: + explicit Overlay(const Shape &shape, const Layout &layout, T *base) + : View{shape, layout}, _base{base} + { + // DO NOTHING + } + +public: + T *base(void) override { return _base; } + const T *base(void) const override { return _base; } + +private: + T *const _base; +}; + +template Overlay make_overlay(const Shape &shape, T *base) +{ + return Overlay{shape, LayoutImpl{}, base}; +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_OVERLAY_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Reader.h b/compiler/angkor/include/nncc/core/ADT/tensor/Reader.h new file mode 100644 index 000000000..49f1287d2 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Reader.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_READER_H__ +#define __NNCC_CORE_ADT_TENSOR_READER_H__ + +#include "nncc/core/ADT/tensor/Index.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +template struct Reader +{ + virtual ~Reader() = default; + + virtual T at(const Index &) const = 0; +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_READER_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h b/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h new file mode 100644 index 000000000..3eaab0e54 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_SHAPE_H__ +#define __NNCC_CORE_ADT_TENSOR_SHAPE_H__ + +#include +#include +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +class Shape +{ +public: + Shape() = default; + Shape(std::initializer_list &&l); + +public: + uint32_t rank(void) const; + +public: + Shape &resize(uint32_t size); + +public: + uint32_t &dim(uint32_t axis); + uint32_t dim(uint32_t axis) const; + +public: + Shape &squeeze(void); + +private: + std::vector _dims; +}; + +/** + * NOTE num_elements returns 1 for rank-0 tensors + */ +uint64_t num_elements(const Shape &); + +Shape squeeze(const Shape &); + +bool operator==(const Shape &, const Shape &); + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_SHAPE_H__ diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/View.h b/compiler/angkor/include/nncc/core/ADT/tensor/View.h new file mode 100644 index 000000000..4c9a91539 --- /dev/null +++ b/compiler/angkor/include/nncc/core/ADT/tensor/View.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNCC_CORE_ADT_TENSOR_VIEW_H__ +#define __NNCC_CORE_ADT_TENSOR_VIEW_H__ + +#include "nncc/core/ADT/tensor/Shape.h" +#include "nncc/core/ADT/tensor/Index.h" +#include "nncc/core/ADT/tensor/Reader.h" +#include "nncc/core/ADT/tensor/Accessor.h" +#include "nncc/core/ADT/tensor/Layout.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +template class View : public Reader, public Accessor +{ +public: + explicit View(const Shape &shape, const Layout &layout) + : _shape{shape}, _layout{std::move(layout)} + { + // DO NOTHING + } + +public: + virtual ~View() = default; + +public: + virtual T *base(void) = 0; + virtual const T *base(void) const = 0; + +public: + T at(const Index &index) const override { return *(base() + _layout.offset(_shape, index)); } + +public: + T &at(const Index &index) override { return *(base() + _layout.offset(_shape, index)); } + +public: + const Shape &shape(void) const { return _shape; } + +private: + const Shape _shape; + const Layout _layout; +}; + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc + +#endif // __NNCC_CORE_ADT_TENSOR_VIEW_H__ diff --git a/compiler/angkor/src/ADT/feature/Accessor.cpp b/compiler/angkor/src/ADT/feature/Accessor.cpp new file mode 100644 index 000000000..03ff9a31e --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Accessor.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Accessor.h" + +// DO NOT REMOVE THIS FILE +// +// This file is introduced to check the self-completeness of 'Accessor.h' diff --git a/compiler/angkor/src/ADT/feature/Buffer.test.cpp b/compiler/angkor/src/ADT/feature/Buffer.test.cpp new file mode 100644 index 000000000..1e4430251 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Buffer.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Buffer.h" +#include "nncc/core/ADT/feature/CHWLayout.h" + +#include + +using nncc::core::ADT::feature::Shape; +using nncc::core::ADT::feature::CHWLayout; +using nncc::core::ADT::feature::Buffer; + +using nncc::core::ADT::feature::make_buffer; + +TEST(ADT_FEATURE_BUFFER, ctor) +{ + const Shape shape{4, 6, 3}; + auto buffer = make_buffer(shape); + + ASSERT_EQ(buffer.shape().depth(), shape.depth()); + ASSERT_EQ(buffer.shape().height(), shape.height()); + ASSERT_EQ(buffer.shape().width(), shape.width()); +} + +TEST(ADT_FEATURE_BUFFER, access) +{ + const Shape shape{4, 6, 3}; + auto buffer = make_buffer(shape); + + ASSERT_EQ(buffer.at(3, 5, 2), 0); + buffer.at(3, 5, 2) = 4; + + // Casting is introduced to use 'const T &at(...) const' method + ASSERT_EQ(static_cast &>(buffer).at(3, 5, 2), 4); +} diff --git a/compiler/angkor/src/ADT/feature/CHWLayout.cpp b/compiler/angkor/src/ADT/feature/CHWLayout.cpp new file mode 100644 index 000000000..31415a1bd --- /dev/null +++ b/compiler/angkor/src/ADT/feature/CHWLayout.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/CHWLayout.h" + +using nncc::core::ADT::feature::Shape; + +static uint32_t CHW_offset(const Shape &shape, uint32_t ch, uint32_t row, uint32_t col) +{ + return (ch * shape.height() + row) * shape.width() + col; +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +CHWLayout::CHWLayout() : Layout{CHW_offset} +{ + // DO NOTHING +} + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp b/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp new file mode 100644 index 000000000..5610df8f3 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/CHWLayout.h" + +#include + +using namespace nncc::core::ADT::feature; + +TEST(ADT_FEATURE_CHW_LAYOUT, col_increase) +{ + const Shape shape{4, 3, 6}; + const CHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 2, 1) + 1, l.offset(shape, 1, 2, 2)); +} + +TEST(ADT_FEATURE_CHW_LAYOUT, row_increase) +{ + const Shape shape{4, 3, 6}; + const CHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1) + 6, l.offset(shape, 1, 2, 1)); +} + +TEST(ADT_FEATURE_CHW_LAYOUT, ch_increase) +{ + const Shape shape{4, 3, 6}; + const CHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1) + 6 * 3, l.offset(shape, 2, 1, 1)); +} diff --git a/compiler/angkor/src/ADT/feature/HWCLayout.cpp b/compiler/angkor/src/ADT/feature/HWCLayout.cpp new file mode 100644 index 000000000..016535625 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/HWCLayout.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/HWCLayout.h" + +using nncc::core::ADT::feature::Shape; + +static uint32_t HWC_offset(const Shape &shape, uint32_t ch, uint32_t row, uint32_t col) +{ + return (row * shape.width() + col) * shape.depth() + ch; +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +HWCLayout::HWCLayout() : Layout{HWC_offset} +{ + // DO NOTHING +} + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp b/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp new file mode 100644 index 000000000..d1f359753 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/HWCLayout.h" + +#include + +using namespace nncc::core::ADT::feature; + +TEST(ADT_FEATURE_HWC_LAYOUT, C_increase) +{ + const uint32_t C = 4; + const uint32_t H = 3; + const uint32_t W = 6; + + const Shape shape{C, H, W}; + const HWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1) + 1, l.offset(shape, 2, 1, 1)); +} + +TEST(ADT_FEATURE_HWC_LAYOUT, W_increase) +{ + const uint32_t C = 4; + const uint32_t H = 3; + const uint32_t W = 6; + + const Shape shape{C, H, W}; + const HWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 2, 1) + C, l.offset(shape, 1, 2, 2)); +} + +TEST(ADT_FEATURE_HWC_LAYOUT, H_increase) +{ + const uint32_t C = 4; + const uint32_t H = 3; + const uint32_t W = 6; + + const Shape shape{C, H, W}; + const HWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1) + W * C, l.offset(shape, 1, 2, 1)); +} diff --git a/compiler/angkor/src/ADT/feature/Layout.cpp b/compiler/angkor/src/ADT/feature/Layout.cpp new file mode 100644 index 000000000..49ab7cbf9 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Layout.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Layout.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace feature +{ + +Layout::Layout(const Func &func) : _func{func} { assert(_func != nullptr); } + +} // namespace feature +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/feature/Layout.test.cpp b/compiler/angkor/src/ADT/feature/Layout.test.cpp new file mode 100644 index 000000000..023594e16 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Layout.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Layout.h" + +#include + +using nncc::core::ADT::feature::Shape; +using nncc::core::ADT::feature::Layout; + +static uint32_t offset_0(const Shape &, uint32_t, uint32_t, uint32_t) { return 0; } +static uint32_t offset_1(const Shape &, uint32_t, uint32_t, uint32_t) { return 1; } + +TEST(ADT_FEATURE_LAYOUT, ctor) +{ + Layout l{offset_0}; + + ASSERT_EQ(l.offset(Shape{4, 3, 6}, 1, 1, 1), 0); +} + +TEST(ADT_FEATURE_LAYOUT, copy) +{ + Layout orig{offset_0}; + Layout copy{offset_1}; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6}, 1, 1, 1), 1); + + copy = orig; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6}, 1, 1, 1), 0); +} + +TEST(ADT_FEATURE_LAYOUT, move) +{ + Layout orig{offset_0}; + Layout move{offset_1}; + + ASSERT_EQ(move.offset(Shape{4, 3, 6}, 1, 1, 1), 1); + + move = std::move(orig); + + ASSERT_EQ(move.offset(Shape{4, 3, 6}, 1, 1, 1), 0); +} diff --git a/compiler/angkor/src/ADT/feature/Overlay.test.cpp b/compiler/angkor/src/ADT/feature/Overlay.test.cpp new file mode 100644 index 000000000..c8e2943f8 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Overlay.test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Overlay.h" +#include "nncc/core/ADT/feature/CHWLayout.h" + +#include + +using nncc::core::ADT::feature::Shape; +using nncc::core::ADT::feature::CHWLayout; +using nncc::core::ADT::feature::Overlay; + +using nncc::core::ADT::feature::make_overlay; + +TEST(ADT_FEATURE_OVERLAY, ctor) +{ + const Shape shape{4, 6, 3}; + + int data[4 * 6 * 3] = { + 0, + }; + auto overlay = make_overlay(shape, data); + + ASSERT_EQ(overlay.shape().depth(), shape.depth()); + ASSERT_EQ(overlay.shape().height(), shape.height()); + ASSERT_EQ(overlay.shape().width(), shape.width()); +} + +TEST(ADT_FEATURE_OVERLAY, read) +{ + const Shape shape{4, 6, 3}; + + int data[4 * 6 * 3] = { + 0, + }; + const auto overlay = make_overlay(shape, data); + + CHWLayout layout{}; + + ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 0); + data[layout.offset(shape, 3, 5, 2)] = 2; + ASSERT_EQ(overlay.at(3, 5, 2), 2); +} + +TEST(ADT_FEATURE_OVERLAY, access) +{ + const Shape shape{4, 6, 3}; + + int data[4 * 6 * 3] = { + 0, + }; + auto overlay = make_overlay(shape, data); + + CHWLayout layout{}; + + ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 0); + overlay.at(3, 5, 2) = 4; + ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 4); +} diff --git a/compiler/angkor/src/ADT/feature/Reader.cpp b/compiler/angkor/src/ADT/feature/Reader.cpp new file mode 100644 index 000000000..5f1c0d22b --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Reader.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/feature/Reader.h" + +// DO NOT REMOVE THIS FILE +// +// This file is introduced to check the self-completeness of 'Reader.h' diff --git a/compiler/angkor/src/ADT/feature/Shape.test.cpp b/compiler/angkor/src/ADT/feature/Shape.test.cpp new file mode 100644 index 000000000..9216182f0 --- /dev/null +++ b/compiler/angkor/src/ADT/feature/Shape.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +TEST(ADT_FEATURE_SHAPE, ctor) +{ + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + nncc::core::ADT::feature::Shape shape{C, H, W}; + + ASSERT_EQ(shape.depth(), C); + ASSERT_EQ(shape.height(), H); + ASSERT_EQ(shape.width(), W); +} + +TEST(ADT_FEATURE_SHAPE, num_elements) +{ + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + using nncc::core::ADT::feature::Shape; + using nncc::core::ADT::feature::num_elements; + + ASSERT_EQ(num_elements(Shape{C, H, W}), C * H * W); +} + +TEST(ADT_FEATURE_SHAPE, operator_eq) +{ + using nncc::core::ADT::feature::Shape; + + // NOTE We use ASSERT_TRUE/ASSERT_FALSE instead of ASSERT_EQ/ASSERT_NE as it is impossible to + // introduce negative tests with ASSERT_NE (it uses operator!= instead of operator==). + ASSERT_TRUE(Shape(1, 1, 1) == Shape(1, 1, 1)); + ASSERT_FALSE(Shape(1, 1, 1) == Shape(2, 1, 1)); + ASSERT_FALSE(Shape(1, 1, 1) == Shape(1, 2, 1)); + ASSERT_FALSE(Shape(1, 1, 1) == Shape(1, 1, 2)); +} diff --git a/compiler/angkor/src/ADT/kernel/Buffer.test.cpp b/compiler/angkor/src/ADT/kernel/Buffer.test.cpp new file mode 100644 index 000000000..da344593e --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Buffer.test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Buffer.h" +#include "nncc/core/ADT/kernel/NCHWLayout.h" + +#include + +using nncc::core::ADT::kernel::Shape; +using nncc::core::ADT::kernel::NCHWLayout; +using nncc::core::ADT::kernel::Buffer; + +using nncc::core::ADT::kernel::make_buffer; + +TEST(ADT_KERNEL_BUFFER, ctor) +{ + const Shape shape{2, 4, 6, 3}; + auto buffer = make_buffer(shape); + + ASSERT_EQ(buffer.shape().count(), shape.count()); + ASSERT_EQ(buffer.shape().depth(), shape.depth()); + ASSERT_EQ(buffer.shape().height(), shape.height()); + ASSERT_EQ(buffer.shape().width(), shape.width()); +} + +TEST(ADT_KERNEL_BUFFER, access) +{ + const Shape shape{2, 4, 6, 3}; + auto buffer = make_buffer(shape); + + ASSERT_EQ(buffer.at(1, 3, 5, 2), 0); + buffer.at(1, 3, 5, 2) = 4; + + // Casting is introduced to use 'const T &at(...) const' method + ASSERT_EQ(static_cast &>(buffer).at(1, 3, 5, 2), 4); +} diff --git a/compiler/angkor/src/ADT/kernel/IndexEnumerator.cpp b/compiler/angkor/src/ADT/kernel/IndexEnumerator.cpp new file mode 100644 index 000000000..0b1db090d --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/IndexEnumerator.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/IndexEnumerator.h" + +#include +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +IndexEnumerator::IndexEnumerator(const Shape &shape) : _cursor(0) +{ + _max[0] = shape.width(); + _max[1] = shape.height(); + _max[2] = shape.depth(); + _max[3] = shape.count(); + + std::fill(_cur, _cur + 4, 0); + + // NOTE Null dimension should NOT exist + assert(std::find(_max, _max + 4, 0) == (_max + 4)); +} + +bool IndexEnumerator::valid(void) const { return _cursor < 4; } + +uint32_t IndexEnumerator::count(void) const { return _cur[3]; } +uint32_t IndexEnumerator::depth(void) const { return _cur[2]; } +uint32_t IndexEnumerator::height(void) const { return _cur[1]; } +uint32_t IndexEnumerator::width(void) const { return _cur[0]; } + +void IndexEnumerator::advance(void) +{ + while (_cursor < 4) + { + if (_cur[_cursor] + 1 < _max[_cursor]) + { + break; + } + + ++_cursor; + } + + if (_cursor == 4) + { + return; + } + + // Increment index + _cur[_cursor] += 1; + + // Reset indices for lower dimensions + for (uint32_t head = 0; head < _cursor; ++head) + { + _cur[head] = 0; + } + + // Reset cursor + _cursor = 0; +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/kernel/IndexEnumerator.test.cpp b/compiler/angkor/src/ADT/kernel/IndexEnumerator.test.cpp new file mode 100644 index 000000000..21ba19209 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/IndexEnumerator.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/IndexEnumerator.h" + +#include +#include + +#include + +using nncc::core::ADT::kernel::Shape; +using nncc::core::ADT::kernel::IndexEnumerator; + +TEST(ADT_KERNEL_INDEX_ENUMERATOR, iterate_full_range) +{ + const uint32_t N = 2; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + const Shape shape{N, C, H, W}; + + std::vector count; + count.resize(N * C * H * W, 0); + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const uint32_t offset = ((e.count() * C + e.depth()) * H + e.height()) * W + e.width(); + count.at(offset) += 1; + } + + ASSERT_TRUE(std::all_of(count.begin(), count.end(), [](uint32_t n) { return n == 1; })); +} diff --git a/compiler/angkor/src/ADT/kernel/Layout.cpp b/compiler/angkor/src/ADT/kernel/Layout.cpp new file mode 100644 index 000000000..acadd2448 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Layout.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Layout.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +Layout::Layout(const Func &func) : _func{func} +{ + // DO NOTHING +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/kernel/Layout.test.cpp b/compiler/angkor/src/ADT/kernel/Layout.test.cpp new file mode 100644 index 000000000..94885cd4e --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Layout.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Layout.h" + +#include + +using nncc::core::ADT::kernel::Shape; +using nncc::core::ADT::kernel::Layout; + +static uint32_t offset_0(const Shape &, uint32_t, uint32_t, uint32_t, uint32_t) { return 0; } +static uint32_t offset_1(const Shape &, uint32_t, uint32_t, uint32_t, uint32_t) { return 1; } + +TEST(ADT_KERNEL_LAYOUT, ctor) +{ + Layout l{offset_0}; + + ASSERT_EQ(l.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0); +} + +TEST(ADT_KERNEL_LAYOUT, copy) +{ + Layout orig{offset_0}; + Layout copy{offset_1}; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 1); + + copy = orig; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0); +} + +TEST(ADT_KERNEL_LAYOUT, move) +{ + Layout orig{offset_0}; + Layout move{offset_1}; + + ASSERT_EQ(move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 1); + + move = std::move(orig); + + ASSERT_EQ(move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0); +} diff --git a/compiler/angkor/src/ADT/kernel/NCHWLayout.cpp b/compiler/angkor/src/ADT/kernel/NCHWLayout.cpp new file mode 100644 index 000000000..be7551182 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/NCHWLayout.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/NCHWLayout.h" + +using nncc::core::ADT::kernel::Shape; + +static uint32_t NCHW_offset(const Shape &shape, uint32_t n, uint32_t ch, uint32_t row, uint32_t col) +{ + return (((n * shape.depth() + ch) * shape.height() + row) * shape.width() + col); +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +NCHWLayout::NCHWLayout() : Layout{NCHW_offset} +{ + // DO NOTHING +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp b/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp new file mode 100644 index 000000000..ba03b7b04 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/NCHWLayout.h" + +#include + +using namespace nncc::core::ADT::kernel; + +TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, col_increment) +{ + const Shape shape{4, 3, 6, 5}; + const NCHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 1, l.offset(shape, 1, 1, 1, 2)); +} + +TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, row_increment) +{ + const Shape shape{4, 3, 6, 5}; + const NCHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 5, l.offset(shape, 1, 1, 2, 1)); +} + +TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, ch_increment) +{ + const Shape shape{4, 3, 6, 5}; + const NCHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 6 * 5, l.offset(shape, 1, 2, 1, 1)); +} + +TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, n_increment) +{ + const Shape shape{4, 3, 6, 5}; + const NCHWLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 3 * 6 * 5, l.offset(shape, 2, 1, 1, 1)); +} diff --git a/compiler/angkor/src/ADT/kernel/NHWCLayout.cpp b/compiler/angkor/src/ADT/kernel/NHWCLayout.cpp new file mode 100644 index 000000000..8e0524425 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/NHWCLayout.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/NHWCLayout.h" + +using nncc::core::ADT::kernel::Shape; + +static uint32_t NHWC_offset(const Shape &shape, uint32_t n, uint32_t ch, uint32_t row, uint32_t col) +{ + return ((n * shape.height() + row) * shape.width() + col) * shape.depth() + ch; +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +NHWCLayout::NHWCLayout() : Layout{NHWC_offset} +{ + // DO NOTHING +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp b/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp new file mode 100644 index 000000000..2c5df7d89 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/NHWCLayout.h" + +#include + +using nncc::core::ADT::kernel::Shape; +using nncc::core::ADT::kernel::NHWCLayout; + +TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, ch_increment) +{ + const uint32_t N = 4; + const uint32_t C = 3; + const uint32_t H = 6; + const uint32_t W = 5; + + const Shape shape{N, C, H, W}; + const NHWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 1, l.offset(shape, 1, 2, 1, 1)); +} + +TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, col_increment) +{ + const uint32_t N = 4; + const uint32_t C = 3; + const uint32_t H = 6; + const uint32_t W = 5; + + const Shape shape{N, C, H, W}; + const NHWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + C, l.offset(shape, 1, 1, 1, 2)); +} + +TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, row_increment) +{ + const uint32_t N = 4; + const uint32_t C = 3; + const uint32_t H = 6; + const uint32_t W = 5; + + const Shape shape{N, C, H, W}; + const NHWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + C * W, l.offset(shape, 1, 1, 2, 1)); +} + +TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, n_increment) +{ + const uint32_t N = 4; + const uint32_t C = 3; + const uint32_t H = 6; + const uint32_t W = 5; + + const Shape shape{N, C, H, W}; + const NHWCLayout l; + + ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + H * W * C, l.offset(shape, 2, 1, 1, 1)); +} diff --git a/compiler/angkor/src/ADT/kernel/Overlay.test.cpp b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp new file mode 100644 index 000000000..e80ebbc30 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Overlay.h" +#include "nncc/core/ADT/kernel/NCHWLayout.h" + +#include + +using nncc::core::ADT::kernel::Shape; +using nncc::core::ADT::kernel::NCHWLayout; +using nncc::core::ADT::kernel::Overlay; + +using nncc::core::ADT::kernel::make_overlay; + +TEST(ADT_KERNEL_OVERLAY, ctor) +{ + const Shape shape{2, 4, 6, 3}; + + int data[2 * 4 * 6 * 3] = { + 0, + }; + auto overlay = make_overlay(shape, data); + + ASSERT_EQ(overlay.shape().count(), shape.count()); + ASSERT_EQ(overlay.shape().depth(), shape.depth()); + ASSERT_EQ(overlay.shape().height(), shape.height()); + ASSERT_EQ(overlay.shape().width(), shape.width()); +} + +TEST(ADT_KERNEL_OVERLAY, read) +{ + const Shape shape{2, 4, 6, 3}; + + int data[2 * 4 * 6 * 3] = { + 0, + }; + const auto overlay = make_overlay(shape, data); + + NCHWLayout layout{}; + + ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 0); + data[layout.offset(shape, 1, 3, 5, 2)] = 2; + ASSERT_EQ(overlay.at(1, 3, 5, 2), 2); +} + +TEST(ADT_KERNEL_OVERLAY, access) +{ + const Shape shape{2, 4, 6, 3}; + + int data[2 * 4 * 6 * 3] = { + 0, + }; + auto overlay = make_overlay(shape, data); + + NCHWLayout layout{}; + + ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 0); + overlay.at(1, 3, 5, 2) = 4; + ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 4); +} diff --git a/compiler/angkor/src/ADT/kernel/Reader.cpp b/compiler/angkor/src/ADT/kernel/Reader.cpp new file mode 100644 index 000000000..9e34167c8 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Reader.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Reader.h" + +// DO NOT REMOVE THIS FILE +// This file is introduced to test the self-completeness of 'Reader.h' diff --git a/compiler/angkor/src/ADT/kernel/Shape.cpp b/compiler/angkor/src/ADT/kernel/Shape.cpp new file mode 100644 index 000000000..8ad1edb67 --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Shape.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/kernel/Shape.h" + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace kernel +{ + +bool operator==(const Shape &l, const Shape &r) +{ + return (l.count() == r.count()) && (l.depth() == r.depth()) && (l.height() == r.height()) && + (l.width() == r.width()); +} + +} // namespace kernel +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/kernel/Shape.test.cpp b/compiler/angkor/src/ADT/kernel/Shape.test.cpp new file mode 100644 index 000000000..da608fb7f --- /dev/null +++ b/compiler/angkor/src/ADT/kernel/Shape.test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +TEST(ADT_KERNEL_SHAPE, ctor) +{ + const uint32_t N = 1; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + nncc::core::ADT::kernel::Shape shape{N, C, H, W}; + + ASSERT_EQ(shape.count(), N); + ASSERT_EQ(shape.depth(), C); + ASSERT_EQ(shape.height(), H); + ASSERT_EQ(shape.width(), W); +} + +TEST(ADT_KERNEL_SHAPE, num_elements) +{ + const uint32_t N = 1; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + using nncc::core::ADT::kernel::Shape; + using nncc::core::ADT::kernel::num_elements; + + ASSERT_EQ(num_elements(Shape{N, C, H, W}), N * C * H * W); +} + +TEST(ADT_KERNEL_SHAPE, operator_eq) +{ + using nncc::core::ADT::kernel::Shape; + + EXPECT_TRUE(Shape(1, 1, 1, 1) == Shape(1, 1, 1, 1)); + EXPECT_FALSE(Shape(1, 1, 1, 1) == Shape(1, 1, 1, 2)); + EXPECT_FALSE(Shape(1, 1, 1, 1) == Shape(1, 1, 2, 1)); + EXPECT_FALSE(Shape(1, 1, 1, 1) == Shape(1, 2, 1, 1)); + EXPECT_FALSE(Shape(1, 1, 1, 1) == Shape(2, 1, 1, 1)); +} diff --git a/compiler/angkor/src/ADT/tensor/Buffer.test.cpp b/compiler/angkor/src/ADT/tensor/Buffer.test.cpp new file mode 100644 index 000000000..c2b6a9983 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Buffer.test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Buffer.h" +#include "nncc/core/ADT/tensor/LexicalLayout.h" + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Buffer; + +using nncc::core::ADT::tensor::make_buffer; + +TEST(ADT_TENSOR_BUFFER, ctor) +{ + const Shape shape{2, 3}; + auto buffer = make_buffer(shape); + + ASSERT_EQ(buffer.shape(), shape); +} + +TEST(ADT_TENSOR_BUFFER, access) +{ + const Shape shape{2, 3}; + auto buffer = make_buffer(shape); + + const Index index{1, 2}; + + ASSERT_EQ(buffer.at(index), 0); + buffer.at(index) = 4; + + // Casting is introduced to use 'const T &at(...) const' method + ASSERT_EQ(static_cast &>(buffer).at(index), 4); +} diff --git a/compiler/angkor/src/ADT/tensor/Index.cpp b/compiler/angkor/src/ADT/tensor/Index.cpp new file mode 100644 index 000000000..61f0a7106 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Index.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Index.h" + +#include +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +Index::Index(std::initializer_list &&l) : _indices{l} +{ + // DO NOTHING +} + +uint32_t Index::rank(void) const { return _indices.size(); } +Index &Index::resize(uint32_t size) +{ + _indices.resize(size); + return *this; +} + +Index &Index::fill(uint32_t index) +{ + std::fill(_indices.begin(), _indices.end(), index); + return (*this); +} + +uint32_t &Index::at(uint32_t axis) { return _indices.at(axis); } +uint32_t Index::at(uint32_t axis) const { return _indices.at(axis); } + +Index operator+(const Index &lhs, const Index &rhs) +{ + if (lhs.rank() != rhs.rank()) + throw std::runtime_error("Two tensors should have same rank"); + + Index ret; + ret.resize(lhs.rank()); + for (uint32_t axis = 0; axis < lhs.rank(); axis++) + { + ret.at(axis) = lhs.at(axis) + rhs.at(axis); + } + return ret; +} + +bool operator==(const Index &lhs, const Index &rhs) +{ + if (lhs.rank() != rhs.rank()) + return false; + for (uint32_t axis = 0; axis < lhs.rank(); axis++) + { + if (lhs.at(axis) != rhs.at(axis)) + return false; + } + return true; +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/tensor/Index.test.cpp b/compiler/angkor/src/ADT/tensor/Index.test.cpp new file mode 100644 index 000000000..230602816 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Index.test.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Index.h" + +#include + +TEST(ADT_TENSOR_INDEX, ctor) +{ + nncc::core::ADT::tensor::Index index; + + ASSERT_EQ(index.rank(), 0); +} + +TEST(ADT_TENSOR_INDEX, ctor_initializer_list) +{ + const nncc::core::ADT::tensor::Index index{1, 3, 5, 7}; + + ASSERT_EQ(index.rank(), 4); + + ASSERT_EQ(index.at(0), 1); + ASSERT_EQ(index.at(1), 3); + ASSERT_EQ(index.at(2), 5); + ASSERT_EQ(index.at(3), 7); +} + +TEST(ADT_TENSOR_INDEX, operator_add) +{ + nncc::core::ADT::tensor::Index index1{1, 2, 3, 4}; + nncc::core::ADT::tensor::Index index2{5, 6, 7, 8}; + nncc::core::ADT::tensor::Index result{index1 + index2}; + + ASSERT_EQ(result.at(0), 6); + ASSERT_EQ(result.at(1), 8); + ASSERT_EQ(result.at(2), 10); + ASSERT_EQ(result.at(3), 12); +} + +TEST(ADT_TENSOR_INDEX, operator_eqaul) +{ + nncc::core::ADT::tensor::Index index1{1, 2, 3, 4}; + nncc::core::ADT::tensor::Index index2{1, 2, 3, 4}; + nncc::core::ADT::tensor::Index index3{5, 6, 7, 8}; + nncc::core::ADT::tensor::Index index4{1, 2}; + + ASSERT_TRUE(index1 == index2); + ASSERT_FALSE(index1 == index3); + ASSERT_FALSE(index1 == index4); +} + +TEST(ADT_TENSOR_INDEX, operator_add_different_size) +{ + nncc::core::ADT::tensor::Index index1{1, 2, 3, 4}; + nncc::core::ADT::tensor::Index index2{5, 6}; + + EXPECT_THROW(index1 + index2, std::runtime_error); +} + +TEST(ADT_TENSOR_INDEX, resize) +{ + nncc::core::ADT::tensor::Index index; + + index.resize(4); + + ASSERT_EQ(index.rank(), 4); +} + +TEST(ADT_TENSOR_INDEX, at) +{ + nncc::core::ADT::tensor::Index index; + + index.resize(4); + + uint32_t indices[4] = {3, 5, 2, 7}; + + for (uint32_t axis = 0; axis < 4; ++axis) + { + index.at(axis) = indices[axis]; + ASSERT_EQ(index.at(axis), indices[axis]); + } +} + +TEST(ADT_TENSOR_INDEX, copy) +{ + const nncc::core::ADT::tensor::Index original{3, 5, 2, 7}; + const nncc::core::ADT::tensor::Index copied{original}; + + ASSERT_EQ(original.rank(), copied.rank()); + + for (uint32_t axis = 0; axis < 4; ++axis) + { + ASSERT_EQ(original.at(axis), copied.at(axis)); + } +} + +TEST(ADT_TENSOR_INDEX, fill) +{ + nncc::core::ADT::tensor::Index index{1, 6}; + + index.fill(3); + + ASSERT_EQ(index.rank(), 2); + + ASSERT_EQ(index.at(0), 3); + ASSERT_EQ(index.at(1), 3); +} diff --git a/compiler/angkor/src/ADT/tensor/IndexEnumerator.cpp b/compiler/angkor/src/ADT/tensor/IndexEnumerator.cpp new file mode 100644 index 000000000..623313a2e --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/IndexEnumerator.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +using nncc::core::ADT::tensor::Shape; + +inline uint32_t axis_of(const Shape &shape, uint32_t cursor) +{ + const uint32_t rank = shape.rank(); + assert(cursor < rank); + return rank - cursor - 1; +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +IndexEnumerator::IndexEnumerator(const Shape &shape) : _shape{shape}, _cursor(0) +{ + const uint32_t rank = _shape.rank(); + + // Initialize _index + _index.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + _index.at(axis) = 0; + } + + // Initialize _cursor + for (_cursor = 0; _cursor < rank; ++_cursor) + { + const auto axis = axis_of(_shape, _cursor); + + if (_index.at(axis) < _shape.dim(axis)) + { + break; + } + } +} + +void IndexEnumerator::advance(void) +{ + const uint32_t rank = _shape.rank(); + + // Find axis to be updated + while (_cursor < rank) + { + const auto axis = axis_of(_shape, _cursor); + + if ((_index.at(axis)) + 1 < _shape.dim(axis)) + { + break; + } + + ++_cursor; + } + + if (_cursor == rank) + { + return; + } + + // Update index + _index.at(axis_of(_shape, _cursor)) += 1; + + for (uint32_t pos = 0; pos < _cursor; ++pos) + { + const auto axis = axis_of(_shape, pos); + _index.at(axis) = 0; + } + + // Reset cursor + _cursor = 0; +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp b/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp new file mode 100644 index 000000000..204a8aa21 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; + +TEST(ADT_TENSOR_INDEX_ENUMERATOR, iterate_full_range) +{ + const uint32_t H = 3; + const uint32_t W = 4; + + const Shape shape{H, W}; + + std::vector count; + + count.resize(H * W, 0); + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + + ASSERT_EQ(ind.rank(), 2); + count.at(ind.at(0) * W + ind.at(1)) += 1; + } + + ASSERT_TRUE(std::all_of(count.begin(), count.end(), [](uint32_t n) { return n == 1; })); +} diff --git a/compiler/angkor/src/ADT/tensor/Layout.cpp b/compiler/angkor/src/ADT/tensor/Layout.cpp new file mode 100644 index 000000000..7faf7507d --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Layout.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Layout.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +Layout::Layout(const Func &func) : _func{func} { assert(_func != nullptr); } + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/tensor/Layout.test.cpp b/compiler/angkor/src/ADT/tensor/Layout.test.cpp new file mode 100644 index 000000000..145adfecc --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Layout.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Layout.h" + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; + +static uint32_t offset_0(const Shape &, const Index &) { return 0; } +static uint32_t offset_1(const Shape &, const Index &) { return 1; } + +TEST(ADT_TENSOR_LAYOUT, ctor) +{ + nncc::core::ADT::tensor::Layout l{offset_0}; + + ASSERT_EQ(l.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0); +} + +TEST(ADT_TENSOR_LAYOUT, copy) +{ + nncc::core::ADT::tensor::Layout orig{offset_0}; + nncc::core::ADT::tensor::Layout copy{offset_1}; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 1); + + copy = orig; + + ASSERT_EQ(copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0); +} + +TEST(ADT_TENSOR_LAYOUT, move) +{ + nncc::core::ADT::tensor::Layout orig{offset_0}; + nncc::core::ADT::tensor::Layout move{offset_1}; + + ASSERT_EQ(move.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 1); + + move = std::move(orig); + + ASSERT_EQ(move.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0); +} diff --git a/compiler/angkor/src/ADT/tensor/LexicalLayout.cpp b/compiler/angkor/src/ADT/tensor/LexicalLayout.cpp new file mode 100644 index 000000000..671c60cec --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/LexicalLayout.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/LexicalLayout.h" + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; + +// NOTE This forward declaration is introduced to minimize code diff +static uint32_t lexical_offset(const Shape &shape, const Index &index) +{ + assert(shape.rank() > 0); + assert(shape.rank() == index.rank()); + + const uint32_t rank = shape.rank(); + + uint32_t res = index.at(0); + + for (uint32_t axis = 1; axis < rank; ++axis) + { + res *= shape.dim(axis); + res += index.at(axis); + } + + return res; +} + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +LexicalLayout::LexicalLayout() : Layout(lexical_offset) +{ + // DO NOTHING +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp b/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp new file mode 100644 index 000000000..8f9b7296f --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/LexicalLayout.h" + +#include + +#include + +TEST(ADT_TENSOR_LEXICAL_LAYOUT, last) +{ + const nncc::core::ADT::tensor::Shape shape{4, 3, 6}; + const nncc::core::ADT::tensor::Index curr{1, 1, 1}; + const nncc::core::ADT::tensor::Index next{1, 1, 2}; + + const nncc::core::ADT::tensor::LexicalLayout l; + + ASSERT_EQ(l.offset(shape, curr) + 1, l.offset(shape, next)); +} + +TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_middle) +{ + const nncc::core::ADT::tensor::Shape shape{4, 3, 6}; + const nncc::core::ADT::tensor::Index curr{1, 1, 1}; + const nncc::core::ADT::tensor::Index next{1, 2, 1}; + + const nncc::core::ADT::tensor::LexicalLayout l; + + ASSERT_EQ(l.offset(shape, curr) + 6, l.offset(shape, next)); +} + +TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_first) +{ + const nncc::core::ADT::tensor::Shape shape{4, 3, 6}; + const nncc::core::ADT::tensor::Index curr{1, 1, 1}; + const nncc::core::ADT::tensor::Index next{2, 1, 1}; + + const nncc::core::ADT::tensor::LexicalLayout l; + + ASSERT_EQ(l.offset(shape, curr) + 6 * 3, l.offset(shape, next)); +} diff --git a/compiler/angkor/src/ADT/tensor/Overlay.test.cpp b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp new file mode 100644 index 000000000..aacb5a9a1 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Overlay.h" +#include "nncc/core/ADT/tensor/LexicalLayout.h" + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Overlay; + +using nncc::core::ADT::tensor::make_overlay; + +TEST(ADT_TENSOR_OVERLAY, ctor) +{ + const Shape shape{2, 3}; + + int data[2 * 3] = { + 0, + }; + auto view = make_overlay(shape, data); + + ASSERT_EQ(view.shape(), shape); +} + +TEST(ADT_TENSOR_OVERLAY, read) +{ + const Shape shape{2, 3}; + + int data[2 * 3] = { + 0, + }; + const auto view = make_overlay(shape, data); + + LexicalLayout layout{}; + + const Index index{1, 2}; + + ASSERT_EQ(data[layout.offset(shape, index)], 0); + data[layout.offset(shape, index)] = 2; + ASSERT_EQ(view.at(index), 2); +} + +TEST(ADT_TENSOR_OVERLAY, access) +{ + const Shape shape{2, 3}; + + int data[2 * 3] = { + 0, + }; + auto view = make_overlay(shape, data); + + LexicalLayout layout{}; + + const Index index{1, 2}; + + ASSERT_EQ(data[layout.offset(shape, index)], 0); + view.at(index) = 4; + ASSERT_EQ(data[layout.offset(shape, index)], 4); +} diff --git a/compiler/angkor/src/ADT/tensor/Reader.cpp b/compiler/angkor/src/ADT/tensor/Reader.cpp new file mode 100644 index 000000000..d79e66dac --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Reader.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Reader.h" + +// DO NOT REMOVE THIS FILE +// +// This file is introduced to check the self-completeness of 'Reader.h' diff --git a/compiler/angkor/src/ADT/tensor/Shape.cpp b/compiler/angkor/src/ADT/tensor/Shape.cpp new file mode 100644 index 000000000..fb39ba192 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Shape.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Shape.h" + +#include + +namespace nncc +{ +namespace core +{ +namespace ADT +{ +namespace tensor +{ + +Shape::Shape(std::initializer_list &&l) : _dims{l} +{ + // DO NOTHING +} + +uint32_t Shape::rank(void) const { return _dims.size(); } +Shape &Shape::resize(uint32_t size) +{ + _dims.resize(size); + return *this; +} + +uint32_t &Shape::dim(uint32_t axis) { return _dims.at(axis); } +uint32_t Shape::dim(uint32_t axis) const { return _dims.at(axis); } + +Shape &Shape::squeeze(void) +{ + _dims.erase(std::remove(_dims.begin(), _dims.end(), 1), _dims.end()); + return *this; +} + +uint64_t num_elements(const Shape &shape) +{ + uint64_t res = 1; + + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + res *= shape.dim(axis); + } + + return res; +} + +Shape squeeze(const Shape &shape) +{ + Shape res{shape}; + res.squeeze(); + return res; +} + +bool operator==(const Shape &lhs, const Shape &rhs) +{ + if (lhs.rank() != rhs.rank()) + { + return false; + } + + for (uint32_t axis = 0; axis < lhs.rank(); ++axis) + { + if (lhs.dim(axis) != rhs.dim(axis)) + { + return false; + } + } + + return true; +} + +} // namespace tensor +} // namespace ADT +} // namespace core +} // namespace nncc diff --git a/compiler/angkor/src/ADT/tensor/Shape.test.cpp b/compiler/angkor/src/ADT/tensor/Shape.test.cpp new file mode 100644 index 000000000..711ae3d40 --- /dev/null +++ b/compiler/angkor/src/ADT/tensor/Shape.test.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nncc/core/ADT/tensor/Shape.h" + +#include + +TEST(ADT_TENSOR_SHAPE, ctor) +{ + nncc::core::ADT::tensor::Shape shape; + + ASSERT_EQ(shape.rank(), 0); +} + +TEST(ADT_TENSOR_SHAPE, ctor_initializer_list) +{ + nncc::core::ADT::tensor::Shape shape{1, 3, 5, 7}; + + ASSERT_EQ(shape.rank(), 4); + + ASSERT_EQ(shape.dim(0), 1); + ASSERT_EQ(shape.dim(1), 3); + ASSERT_EQ(shape.dim(2), 5); + ASSERT_EQ(shape.dim(3), 7); +} + +TEST(ADT_TENSOR_SHAPE, resize) +{ + nncc::core::ADT::tensor::Shape shape; + + shape.resize(4); + + ASSERT_EQ(shape.rank(), 4); +} + +TEST(ADT_TENSOR_SHAPE, dim) +{ + nncc::core::ADT::tensor::Shape shape; + + shape.resize(4); + + uint32_t dims[4] = {3, 5, 2, 7}; + + for (uint32_t axis = 0; axis < 4; ++axis) + { + shape.dim(axis) = dims[axis]; + ASSERT_EQ(shape.dim(axis), dims[axis]); + } +} + +TEST(ADT_TENSOR_SHAPE, copy) +{ + const nncc::core::ADT::tensor::Shape original{3, 5, 2, 7}; + const nncc::core::ADT::tensor::Shape copied{original}; + + ASSERT_EQ(original.rank(), copied.rank()); + + for (uint32_t axis = 0; axis < 4; ++axis) + { + ASSERT_EQ(original.dim(axis), copied.dim(axis)); + } +} + +TEST(ADT_TENSOR_SHAPE, num_elements_rank_0) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::num_elements; + + Shape rank_0_shape; + + ASSERT_EQ(num_elements(rank_0_shape), 1); +} + +TEST(ADT_TENSOR_SHAPE, num_elements_zero) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::num_elements; + + ASSERT_EQ(num_elements(Shape{0, 0, 0, 0}), 0); +} + +TEST(ADT_TENSOR_SHAPE, num_elements_nonzero) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::num_elements; + + ASSERT_EQ(num_elements(Shape{2, 3}), 6); +} + +TEST(ADT_TENSOR_SHAPE, num_elements_nulldim) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::num_elements; + + ASSERT_EQ(num_elements(Shape{2, 0, 3}), 0); +} + +TEST(ADT_TENSOR_SHAPE, squeeze_neg) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::squeeze; + + auto squeezed = squeeze(Shape{3, 5, 2}); + + ASSERT_EQ(squeezed.rank(), 3); + ASSERT_EQ(squeezed.dim(0), 3); + ASSERT_EQ(squeezed.dim(1), 5); + ASSERT_EQ(squeezed.dim(2), 2); +} + +TEST(ADT_TENSOR_SHAPE, squeeze_neg_0) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::squeeze; + + auto squeezed = squeeze(Shape{3, 0, 2}); + + ASSERT_EQ(squeezed.rank(), 3); + ASSERT_EQ(squeezed.dim(0), 3); + ASSERT_EQ(squeezed.dim(1), 0); + ASSERT_EQ(squeezed.dim(2), 2); +} + +TEST(ADT_TENSOR_SHAPE, squeeze_pos) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::squeeze; + + auto squeezed = squeeze(Shape{3, 1, 2}); + + ASSERT_EQ(squeezed.rank(), 2); + ASSERT_EQ(squeezed.dim(0), 3); + ASSERT_EQ(squeezed.dim(1), 2); +} + +TEST(ADT_TENSOR_SHAPE, squeeze_nested) +{ + using nncc::core::ADT::tensor::Shape; + using nncc::core::ADT::tensor::squeeze; + + Shape shape{3, 1, 2}; + + shape.squeeze().squeeze(); + + ASSERT_EQ(shape.rank(), 2); + ASSERT_EQ(shape.dim(0), 3); + ASSERT_EQ(shape.dim(1), 2); +} + +TEST(ADT_TENSOR_SHAPE, eq_negative_on_unmatched_rank) +{ + const nncc::core::ADT::tensor::Shape left{1, 1, 1}; + const nncc::core::ADT::tensor::Shape right{1, 1, 1, 1}; + + ASSERT_FALSE(left == right); +} + +TEST(ADT_TENSOR_SHAPE, eq_negative_on_unmatched_dim) +{ + const nncc::core::ADT::tensor::Shape left{2, 3}; + const nncc::core::ADT::tensor::Shape right{2, 4}; + + ASSERT_FALSE(left == right); +} + +TEST(ADT_TENSOR_SHAPE, eq_positive) +{ + const nncc::core::ADT::tensor::Shape left{2, 3}; + const nncc::core::ADT::tensor::Shape right{2, 3}; + + ASSERT_TRUE(left == right); +} diff --git a/compiler/angkor/src/TensorIndex.test.cpp b/compiler/angkor/src/TensorIndex.test.cpp new file mode 100644 index 000000000..68cf3917a --- /dev/null +++ b/compiler/angkor/src/TensorIndex.test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "angkor/TensorIndex.h" + +#include + +TEST(TensorIndexTest, ctor) +{ + angkor::TensorIndex index; + + ASSERT_EQ(index.rank(), 0); +} + +TEST(TensorIndexTest, ctor_initializer_list) +{ + const angkor::TensorIndex index{1, 3, 5, 7}; + + ASSERT_EQ(index.rank(), 4); + + ASSERT_EQ(index.at(0), 1); + ASSERT_EQ(index.at(1), 3); + ASSERT_EQ(index.at(2), 5); + ASSERT_EQ(index.at(3), 7); +} + +TEST(TensorIndexTest, resize) +{ + angkor::TensorIndex index; + + index.resize(4); + + ASSERT_EQ(index.rank(), 4); +} + +TEST(TensorIndexTest, at) +{ + angkor::TensorIndex index; + + index.resize(4); + + uint32_t indices[4] = {3, 5, 2, 7}; + + for (uint32_t axis = 0; axis < 4; ++axis) + { + index.at(axis) = indices[axis]; + ASSERT_EQ(index.at(axis), indices[axis]); + } +} + +TEST(TensorIndexTest, copy) +{ + const angkor::TensorIndex original{3, 5, 2, 7}; + const angkor::TensorIndex copied{original}; + + ASSERT_EQ(original.rank(), copied.rank()); + + for (uint32_t axis = 0; axis < 4; ++axis) + { + ASSERT_EQ(original.at(axis), copied.at(axis)); + } +} + +TEST(TensorIndexTest, fill) +{ + angkor::TensorIndex index{1, 6}; + + index.fill(3); + + ASSERT_EQ(index.rank(), 2); + + ASSERT_EQ(index.at(0), 3); + ASSERT_EQ(index.at(1), 3); +} diff --git a/compiler/angkor/src/TensorShape.test.cpp b/compiler/angkor/src/TensorShape.test.cpp new file mode 100644 index 000000000..5e6766a96 --- /dev/null +++ b/compiler/angkor/src/TensorShape.test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "angkor/TensorShape.h" + +#include + +TEST(TensorShapeTest, ctor) +{ + angkor::TensorShape shape; + + ASSERT_EQ(shape.rank(), 0); +} + +TEST(TensorShapeTest, ctor_initializer_list) +{ + angkor::TensorShape shape{1, 3, 5, 7}; + + ASSERT_EQ(shape.rank(), 4); + + ASSERT_EQ(shape.dim(0), 1); + ASSERT_EQ(shape.dim(1), 3); + ASSERT_EQ(shape.dim(2), 5); + ASSERT_EQ(shape.dim(3), 7); +} + +TEST(TensorShapeTest, resize) +{ + angkor::TensorShape shape; + + shape.resize(4); + + ASSERT_EQ(shape.rank(), 4); +} + +TEST(TensorShapeTest, dim) +{ + angkor::TensorShape shape; + + shape.resize(4); + + uint32_t dims[4] = {3, 5, 2, 7}; + + for (uint32_t axis = 0; axis < 4; ++axis) + { + shape.dim(axis) = dims[axis]; + ASSERT_EQ(shape.dim(axis), dims[axis]); + } +} + +TEST(TensorShapeTest, copy) +{ + const angkor::TensorShape original{3, 5, 2, 7}; + const angkor::TensorShape copied{original}; + + ASSERT_EQ(original.rank(), copied.rank()); + + for (uint32_t axis = 0; axis < 4; ++axis) + { + ASSERT_EQ(original.dim(axis), copied.dim(axis)); + } +} + +TEST(TensorShapeTest, eq_negative_on_unmatched_rank) +{ + const angkor::TensorShape left{1, 1, 1}; + const angkor::TensorShape right{1, 1, 1, 1}; + + ASSERT_FALSE(left == right); +} + +TEST(TensorShapeTest, eq_negative_on_unmatched_dim) +{ + const angkor::TensorShape left{2, 3}; + const angkor::TensorShape right{2, 4}; + + ASSERT_FALSE(left == right); +} + +TEST(TensorShapeTest, eq_positive) +{ + const angkor::TensorShape left{2, 3}; + const angkor::TensorShape right{2, 3}; + + ASSERT_TRUE(left == right); +} diff --git a/compiler/ann-api/CMakeLists.txt b/compiler/ann-api/CMakeLists.txt new file mode 100644 index 000000000..d2c45f9f0 --- /dev/null +++ b/compiler/ann-api/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(ann_api INTERFACE) +target_include_directories(ann_api INTERFACE include) diff --git a/compiler/ann-api/include/.FORMATDENY b/compiler/ann-api/include/.FORMATDENY new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/ann-api/include/NeuralNetworks.h b/compiler/ann-api/include/NeuralNetworks.h new file mode 100644 index 000000000..606156927 --- /dev/null +++ b/compiler/ann-api/include/NeuralNetworks.h @@ -0,0 +1,2075 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NeuralNetworks + * @{ + */ + +/** + * @file NeuralNetworks.h + */ + +#ifndef ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_H +#define ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_H + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +#if __ANDROID_API__ >= __ANDROID_API_O_MR1__ + +#include +#include +#include + +__BEGIN_DECLS + +/** + * Operand types. + * + * The type of operands that can be added to a model. + * + * Although we define many types, most operators accept just a few + * types. Most used are {@link ANEURALNETWORKS_TENSOR_FLOAT32}, + * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM}, + * and {@link ANEURALNETWORKS_INT32}. + */ +typedef enum { + /** The following entries are used to declare scalars. */ + + /** A 32 bit floating point scalar value. */ + ANEURALNETWORKS_FLOAT32 = 0, + /** A signed 32 bit integer scalar value. */ + ANEURALNETWORKS_INT32 = 1, + /** An unsigned 32 bit integer scalar value. */ + ANEURALNETWORKS_UINT32 = 2, + + /** The following entries are used to declare tensors. */ + + /** A tensor of 32 bit floating point values. */ + ANEURALNETWORKS_TENSOR_FLOAT32 = 3, + /** A tensor of 32 bit integer values. */ + ANEURALNETWORKS_TENSOR_INT32 = 4, + /** A tensor of 8 bit integers that represent real numbers. + * + * Attached to this tensor are two numbers that can be used to convert + * the 8 bit integer to the real value and vice versa. These two numbers are: + * - scale: a 32 bit non-negative floating point value. + * - zeroPoint: an 32 bit integer, in range [0, 255]. + * + * The formula is: + * real_value = (integer_value - zeroPoint) * scale. + */ + ANEURALNETWORKS_TENSOR_QUANT8_ASYMM = 5, +} OperandCode; + +/** + * Operation types. + * + * The type of operations that can be added to a model. + */ +typedef enum { + /** Adds two tensors, element-wise. + * + * Takes two input tensors of identical type and compatible dimensions. The output + * is the sum of both input tensors, optionally modified by an activation function. + * + * Two dimensions are compatible when: + * 1. they are equal, or + * 2. one of them is 1 + * + * The size of the output is the maximum size along each dimension of the input operands. + * It starts with the trailing dimensions, and works its way forward. + * + * Example: + * + * input1.dimension = {4, 1, 2} + * input2.dimension = {5, 4, 3, 1} + * output.dimension = {5, 4, 3, 2} + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: A tensor. + * * 1: A tensor of the same type, and compatible dimensions as input0. + * * 2: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The sum, a tensor of the same type as input0. + */ + ANEURALNETWORKS_ADD = 0, + + /** Performs a 2-D average pooling operation. + * + * The output dimensions are functions of the filter dimensions, stride, and padding. + * + * The values in the output tensor are computed as: + * + * output[batch, row, col, channel] = + * sum_{i, j}(input[batch, row + i, col + j, channel]) / sum(1) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" (i.e., Num_samples, Height, Width, and Channels) + * data layout. + * + * Both explicit padding and implicit padding are supported. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the padding on the left, in the ‘width’ dimension. + * * 2: An INT32 value, specifying the padding on the right,in the ‘width’ dimension. + * * 3: An INT32 value, specifying the padding on the top, in the ‘height’ dimension. + * * 4: An INT32 value, specifying the padding on the bottom, in the ‘height’ dimension. + * * 5: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 6: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 7: An INT32 value, specifying the filter width. + * * 8: An INT32 value, specifying the filter height. + * * 9: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Inputs (implicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the implicit padding scheme, has to be one of the + * {@link PaddingCode} values. + * * 2: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 3: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 4: An INT32 value, specifying the filter width. + * * 5: An INT32 value, specifying the filter height. + * * 6: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth]. + */ + ANEURALNETWORKS_AVERAGE_POOL_2D = 1, + + /** Concatenates the input tensors along the given dimension. + * + * The input tensors must have identical type and the same dimensions except the + * dimension along the concatenation axis. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0 ~ n-1: The list of n input tensors, of shape [D0, D1, ..., Daxis(i), ..., Dm]. + * For inputs of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, all + * input tensors must have the same scale and zeroPoint. + * * n: An INT32 value, specifying the concatenation axis. + * + * Outputs: + * * 0: The output, a tensor of the same type as the input tensors. + * The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm]. + */ + ANEURALNETWORKS_CONCATENATION = 2, + + /** Performs an 2-D convolution operation. + * + * The CONV_2D op sweeps a 2-D filter that can mix channels together over a batch of + * images, applying the filter to each window of each image of the appropriate size. + * + * The output dimensions are functions of the filter dimensions, stride, and padding. + * + * The values in the output tensor are computed as: + * + * output[batch, row, col, channel] = + * sum_{i, j} ( + * input[batch, row + i, col + j, k] * + * filter[channel, row + i, col + j, k] + + * bias[channel] + * ) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Both explicit padding and implicit padding are supported. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: A 4-D tensor, of shape [depth_out, filter_height, filter_width, depth_in], + * specifying the filter. + * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_FLOAT32} type, the bias should + * also be of {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the bias + * should be of {@link ANEURALNETWORKS_TENSOR_INT32}, with zeroPoint of 0 and + * bias_scale == input_scale * filter_scale. + * * 3: An INT32 value, specifying the padding on the left, in the ‘width’ dimension. + * * 4: An INT32 value, specifying the padding on the right,in the ‘width’ dimension. + * * 5: An INT32 value, specifying the padding on the top, in the ‘height’ dimension. + * * 6: An INT32 value, specifying the padding on the bottom, in the ‘height’ dimension. + * * 7: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 8: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 9: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Inputs (implicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: A 4-D tensor, of shape [depth_out, filter_height, filter_width, depth_in], + * specifying the filter. + * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_FLOAT32} type, the bias should + * also be of {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the bias + * should be of {@link ANEURALNETWORKS_TENSOR_INT32}, with zeroPoint of 0 and + * bias_scale == input_scale * filter_scale. + * * 3: An INT32 value, specifying the implicit padding scheme, has to be one of the + * {@link PaddingCode} values. + * * 4: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 5: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 6: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth_out]. + * For output tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the following + * condition must be satisfied: output_scale > input_scale * filter_scale. + */ + ANEURALNETWORKS_CONV_2D = 3, + + /** Performs a depthwise 2-D convolution operation. + * + * Given an input tensor of shape [batches, height, width, depth_in] and a filter + * tensor of shape [1, filter_height, filter_width, depth_out] containing + * depth_out convolutional filters of depth 1, DEPTHWISE_CONV applies a different + * filter to each input channel (expanding from 1 channel to channel_multiplier channels + * for each), then concatenates the results together. + * + * The output has depth_out = depth_in * depth_multiplier channels. + * The output dimensions are functions of the filter dimensions, stride, and padding. + * + * The values in the output tensor are computed as: + * + * output[b, i, j, k * channel_multiplier + q] = + * sum_{di, dj} ( + * input[b, strides[1] * i + di, strides[2] * j + dj, k] * + * filter[1, di, dj, k * channel_multiplier + q] + * ) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Both explicit padding and implicit padding are supported. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out], + * specifying the filter. + * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_FLOAT32} type, the bias should + * also be of {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the bias + * should be of {@link ANEURALNETWORKS_TENSOR_INT32}, with zeroPoint of 0 and + * bias_scale == input_scale * filter_scale. + * * 3: An INT32 value, specifying the padding on the left, in the ‘width’ dimension. + * * 4: An INT32 value, specifying the padding on the right,in the ‘width’ dimension. + * * 5: An INT32 value, specifying the padding on the top, in the ‘height’ dimension. + * * 6: An INT32 value, specifying the padding on the bottom, in the ‘height’ dimension. + * * 7: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 8: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 9: An INT32 value, specifying the depthwise multiplier. + * * 10: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out], + * specifying the filter. + * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_FLOAT32} type, the bias should + * also be of {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the bias + * should be of {@link ANEURALNETWORKS_TENSOR_INT32}, with zeroPoint of 0 and + * bias_scale == input_scale * filter_scale. + * * 3: An INT32 value, specifying the implicit padding scheme, has to be one of the + * {@link PaddingCode} values. + * * 4: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 5: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 6: An INT32 value, specifying the depthwise multiplier. + * * 7: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth_out]. + * For output tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the following + * condition must be satisfied: output_scale > input_scale * filter_scale. + */ + ANEURALNETWORKS_DEPTHWISE_CONV_2D = 4, + + /** Rearranges data from depth into blocks of spatial data. + * + * More specifically, this op outputs a copy of the input tensor where values from + * the depth dimension are moved in spatial blocks to the height and width dimensions. + * The value block_size indicates the input block size and how the data is moved. + * + * Chunks of data of size block_size * block_size from depth are rearranged into + * non-overlapping blocks of size block_size x block_size. + * + * The width of the output tensor is input_depth * block_size, whereas the height is + * input_height * block_size. + * The depth of the input tensor must be divisible by block_size * block_size + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Inputs: + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: An INT32 value, specifying the block_size. block_size must be >=1 and + * block_size * block_size must be a divisor of the input depth. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batch, height*block_size, width*block_size, + * depth/(block_size*block_size)]. + */ + ANEURALNETWORKS_DEPTH_TO_SPACE = 5, + + /** Dequantizes the input tensor. + * + * The formula is: + * + * output = (input - zeroPoint) * scale. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: A tensor of type {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM}. + * + * Outputs: + * * 0: The output tensor of same shape as input0, but with type + * {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + */ + ANEURALNETWORKS_DEQUANTIZE = 6, + + /** Looks up sub-tensors in the input tensor. + * + * This operator takes for input a tensor of values (Values) and + * a one-dimensional tensor of selection indices (Lookups). + * The output tensor is the concatenation of sub-tensors of Values as + * selected by Lookups. + * + * Think of Values as being sliced along its first dimension: + * The entries in Lookups select which slices are concatenated together + * to create the output tensor. + * + * For example, if Values has shape of [40, 200, 300] and + * Lookups has shape of [3], we would expect all three values + * found in Lookups to be between 0 and 39. The resulting tensor will + * have shape of [3, 200, 300]. + * + * If a value in Lookups is out of bounds, the operation will fail + * and an error will be reported. + * + * Inputs: + * * 0: Lookups. A 1-D tensor of {@link ANEURALNETWORKS_TENSOR_INT32} type. + * The values are indices into the first dimension of Values. + * * 1: Values. An n-D tensor, where n >= 2, from which sub-tensors are + * extracted. + * + * Output: + * * 0: A n-D tensor with the same rank and shape as the Values + * tensor, except for the first dimension which has the same size + * as Lookups' only dimension. + */ + ANEURALNETWORKS_EMBEDDING_LOOKUP = 7, + + /** Computes element-wise floor() on the input tensor. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: A tensor. + * + * Outputs: + * * 0: The output tensor, of the same type and dimensions as the input tensor. + */ + ANEURALNETWORKS_FLOOR = 8, + + /** Denotes a fully (densely) connected layer, which connects all elements in the input + * tensor with each element in the output tensor. + * + * This layer implements the operation: + * + * outputs = activation(inputs * weights’ + bias) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. If rank is greater than 2, then it gets flattened to + * a 2-D Tensor. The 2-D Tensor is handled as if dimensions corresponded to shape + * [batch_size, input_size], where “batch_size” corresponds to the batching dimension, + * and “input_size” is the size of the input. + * * 1: A 2-D tensor, specifying the weights, of shape [num_units, input_size], where + * "num_units" corresponds to the number of output nodes. + * * 2: A 1-D tensor, of shape [num_units], specifying the bias. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_FLOAT32} type, the bias should + * also be of {@link ANEURALNETWORKS_TENSOR_FLOAT32}. + * For input tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the bias + * should be of {@link ANEURALNETWORKS_TENSOR_INT32}, with zeroPoint of 0 and + * bias_scale == input_scale * filter_scale. + * * 3: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output tensor, of shape [batch_size, num_units]. + * For output tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the following + * condition must be satisfied: output_scale > input_scale * filter_scale. + */ + ANEURALNETWORKS_FULLY_CONNECTED = 9, + + /** Looks up sub-tensors in the input tensor using a key-value map. + * + * This operator takes for input a tensor of values (Values), + * a one-dimensional tensor of selection values (Lookups) and + * a one-dimensional tensor that maps these values to Values + * indexes. The output tensor is the concatenation of sub-tensors of + * Values as selected by Lookups via Keys. + * + * Think of Values as being sliced along its outer-most dimension. + * The output is a concatenation of selected slices, with one slice + * for each entry of Lookups. The slice selected is the one at the + * same index as the Maps entry that matches the value in Lookups. + * + * For a hit, the corresponding sub-tensor of Values is included + * in the Output tensor. For a miss, the corresponding sub-tensor in + * Output will have zero values. + * + * For example, if Values has shape of [40, 200, 300], + * Keys should have a shape of [40]. If Lookups tensor has shape + * of [3], we're concatenating three slices, so the resulting tensor + * will have the shape of [3, 200, 300]. If the first entry in + * Lookups has the value 123456, we'll look for that value in Keys tensor. + * If the sixth entry of Keys contains 123456, we'll select the sixth + * slice of Values. If no entry in Keys has 123456, a slice of zeroes + * will be concatenated. + * + * Inputs: + * * 0: Lookups. A 1-D {@link ANEURALNETWORKS_TENSOR_INT32} tensor with shape [ k ]. + * * 1: Keys. A 1-D {@link ANEURALNETWORKS_TENSOR_INT32} tensor with shape [ n ]; + * Keys and Values pair represent a map, i.e., the ith element + * in Keys (Keys[i]) is the key to select the ith sub-tensor + * in Values (Values[i]), where 0 <= i <= n-1. + * Keys tensor *MUST* be sorted in ascending order. + * * 2: Values. A tensor with shape of [ n, … ]; i.e., the first dimension must be n. + * + * Outputs: + * * 0: Output. A tensor with shape [ k …]. + * * 1: Hits. A boolean tensor with shape [ k ] indicates whether the lookup + * hits (True) or not (False). + * Stored as {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} with offset 0 and scale 1.0f. + * A non-zero byte represents True, a hit. A zero indicates otherwise. + */ + ANEURALNETWORKS_HASHTABLE_LOOKUP = 10, + + /** Applies L2 normalization along the depth dimension. + * + * The values in the output tensor are computed as: + * + * output[batch, row, col, channel] = + * input[batch, row, col, channel] / + * sqrt(sum_{c} pow(input[batch, row, col, c], 2)) + * + * For input tensor with more dimensions, independently normalizes each 1-D slice along dimension dim. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: 4, with "NHWC" data layout (i.e., Num_samples, Height, Width, and Channels). + * + * Inputs: + * * 0: A 4-D tensor, of shape [batches, height, width, depth]. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth]. + */ + ANEURALNETWORKS_L2_NORMALIZATION = 11, + + /** Performs an 2-D L2 pooling operation. + * + * The output dimensions are functions of the filter dimensions, stride, and padding. + * + * The values in the output tensor are computed as: + * + * output[batch, row, col, channel] = + * sqrt(sum_{i, j} pow(input[batch, row + i, col + j, channel], 2) / sum(1)) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Both explicit padding and implicit padding are supported. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the padding on the left, in the ‘width’ dimension. + * * 2: An INT32 value, specifying the padding on the right,in the ‘width’ dimension. + * * 3: An INT32 value, specifying the padding on the top, in the ‘height’ dimension. + * * 4: An INT32 value, specifying the padding on the bottom, in the ‘height’ dimension. + * * 5: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 6: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 7: An INT32 value, specifying the filter width. + * * 8: An INT32 value, specifying the filter height. + * * 9: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Inputs (implicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the implicit padding scheme, has to be one of the + * {@link PaddingCode} values. + * * 2: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 3: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 4: An INT32 value, specifying the filter width. + * * 5: An INT32 value, specifying the filter height. + * * 6: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth]. + */ + ANEURALNETWORKS_L2_POOL_2D = 12, + + /** Applies Local Response Normalization along the depth dimension. + * + * The 4-D input tensor is treated as a 3-D array of 1-D vectors (along the last + * dimension), and each vector is normalized independently. Within a given vector, + * each component is divided by the weighted, squared sum of inputs within depth_radius. + * + * The output is calculated using this formula: + * + * sqr_sum[a, b, c, d] = + * sum(pow(input[a, b, c, d - depth_radius : d + depth_radius + 1], 2) + * output = input / pow((bias + alpha * sqr_sum), beta) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Inputs: + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the radius of the normalization window. + * * 2: A FLOAT32 value, specifying the bias, must not be zero. + * * 3: A FLOAT32 value, specifying the scale factor, alpha. + * * 4: A FLOAT32 value, specifying the exponent, beta. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + */ + ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION = 13, + + /** Computes sigmoid activation on the input tensor element-wise. + * + * The output is calculated using this formula: + * + * output = 1 / (1 + exp(-input)) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + * For {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, + * the scale must be 1.f / 256 and the zeroPoint must be 0. + */ + ANEURALNETWORKS_LOGISTIC = 14, + + /** + * Projects an input to a bit vector via locality senstive hashing. + * + * Inputs: + * * 0: Hash functions. Dim.size == 2, DataType: Float. + * Tensor[0].Dim[0]: Number of hash functions. + * Tensor[0].Dim[1]: Number of seeds per hash functions. + * Tensor[0].Dim[1] <= 32 in sparse case. + * + * * 1: Input. Dim.size >= 1, no restriction on DataType. + * * 2: Weight. Optional. Dim.size == 1, DataType: Float. + * If not set, each input element is considered to have the same weight of + * 1.0. + * Tensor[1].Dim[0] == Tensor[2].Dim[0] + * * 3: Type: + * Sparse: Value LSHProjectionType_SPARSE(=1). + * Computed bit vector is considered to be sparse. + * Each output element is an int32 made up of multiple bits computed from + * hash functions. + * + * Dense: Value LSHProjectionType_DENSE(=2). + * Computed bit vector is considered to be dense. Each output element + * represents a bit and can take the value of either 0 or 1. + * + * Outputs: + * * 0: If the projection type is sparse: + * Output.Dim == { Tensor[0].Dim[0] } + * A tensor of int32 that represents hash signatures. + * If the projection type is Dense: + * Output.Dim == { Tensor[0].Dim[0] * Tensor[0].Dim[1] } + * A flattened tensor that represents projected bit vectors. + */ + ANEURALNETWORKS_LSH_PROJECTION = 15, + + /** + * Long short-term memory unit (LSTM) recurrent network layer. + * + * The default non-peephole implementation is based on: + * http://deeplearning.cs.cmu.edu/pdfs/Hochreiter97_lstm.pdf + * S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural + * Computation, 9(8):1735-1780, 1997. + * + * The peephole implementation is based on: + * https://research.google.com/pubs/archive/43905.pdf + * Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory + * recurrent neural network architectures for large scale acoustic modeling." + * INTERSPEECH, 2014. + * + * The coupling of input and forget gate (CIFG) is based on: + * http://arxiv.org/pdf/1503.04069.pdf + * Greff et al. "LSTM: A Search Space Odyssey" + * + * The class has the following independently optional inputs: + * * If input gate (if CIFG): “input_to_forget_weights”, + * “recurrent_to_input_weights”, “cell_to_input_weights”, “input_gate_bias”. + * * If no peephole connections: “cell_to_input_weights”, + * “cell_to_forget_weights”, “cell_to_output_weights”. + * * If no projection layer: “projection_weights” and “projection_bias”. + * * If no projection bias: “projection_bias”. + * + * Supported tensor types (type T): + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Inputs: + * * 0: Input. + * A 2-D tensor of type T, of shape [batch_size, input_size], where + * “batch_size” corresponds to the batching dimension, and “input_size” + * is the size of the input. + * * 1: input_to_input_weights. + * A 2-D tensor of type T, of shape [num_units, input_size], where + * “num_units” corresponds to the number of cell units. + * * 2: input_to_forget_weights. + * A 2-D tensor of type T, of shape [num_units, input_size]. + * * 3: input_to_cell_weights. + * A 2-D tensor of type T, of shape [num_units, input_size]. + * * 4: input_to_output_weights. + * A 2-D tensor of type T, of shape [num_units, input_size]. + * * 5: recurrent_to_input_weights. + * A 2-D tensor of type T, of shape [num_units, output_size], where + * “output_size” corresponds to either the number of cell units (i.e., + * “num_units”), or the second dimension of the “projection_weights”, if + * defined. + * * 6: recurrent_to_forget_weights. + * A 2-D tensor of type T, of shape [num_units, output_size]. + * * 7: recurrent_to_cell_weights. + * A 2-D tensor of type T, of shape [num_units, output_size]. + * * 8: recurrent_to_output_weights. + * A 2-D tensor of type T, of shape [num_units, output_size]. + * * 9: cell_to_input_weights. + * A 1-D tensor of type T, of shape [num_units]. + * * 10:cell_to_forget_weights. + * A 1-D tensor of type T, of shape [num_units]. + * * 11:cell_to_output_weights. + * A 1-D tensor of type T, of shape [num_units]. + * * 12:input_gate_bias. + * A 1-D tensor of type T, of shape [num_units]. + * * 13:forget_gate_bias. + * A 1-D tensor of type T, of shape [num_units]. + * * 14:cell_bias. + * A 1-D tensor of type T, of shape [num_units]. + * * 15:output_gate_bias. + * A 1-D tensor of type T, of shape [num_units]. + * * 16:projection_weights. + * A 2-D tensor of type T, of shape [output_size, num_units]. + * * 17:projection_bias. + * A 1-D tensor of type T, of shape [output_size]. + * * 18: output_state (in). + * A 2-D tensor of type T, of shape [batch_size, output_size]. + * * 19: cell_state (in). + * A 2-D tensor of type T, of shape [batch_size, num_units]. + * * 20:fused_activation_function. + * An optional {@link FuseCode} value indicating the activation + * function. + * If “NONE” is specified then it results in a linear activation. + * * 21:cell_clip. + * A clipping threshold for the cell state, such that values are bound + * within [-cell_clip, cell_clip]. If set to 0.0 then clipping is + * disabled. + * * 22:proj_clip. + * A clipping threshold for the output from the projection layer, such + * that values are bound within [-proj_clip, proj_clip]. If set to 0.0 + * then clipping is disabled. + * + * Outputs: + * * 0: scratch_buffer. + * A 3-D tensor of type T, of shape [batch_size, num_cell, 4]. + * * 1: output_state (out). + * A 2-D tensor of type T, of shape [batch_size, output_size]. + * * 2: cell_state (out). + * A 2-D tensor of type T, of shape [batch_size, num_units]. + * * 3: output. + * A 2-D tensor of type T, of shape [batch_size, output_size]. This is + * effectively the same as the current “output_state” value. + */ + ANEURALNETWORKS_LSTM = 16, + + /** Performs an 2-D max pooling operation. + * + * The output dimensions are functions of the filter dimensions, stride, and padding. + * + * The values in the output tensor are computed as: + * + * output[batch, row, col, channel] = + * max_{i, j} (input[batch, row + i, col + j, channel]) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Both explicit padding and implicit padding are supported. + * + * Inputs (explicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the padding on the left, in the ‘width’ dimension. + * * 2: An INT32 value, specifying the padding on the right,in the ‘width’ dimension. + * * 3: An INT32 value, specifying the padding on the top, in the ‘height’ dimension. + * * 4: An INT32 value, specifying the padding on the bottom, in the ‘height’ dimension. + * * 5: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 6: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 7: An INT32 value, specifying the filter width. + * * 8: An INT32 value, specifying the filter height. + * * 9: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Inputs (implicit padding): + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the implicit padding scheme, has to be one of the + * {@link PaddingCode} values. + * * 2: An INT32 value, specifying the stride when walking through input + * in the ‘width’ dimension. + * * 3: An INT32 value, specifying the stride when walking through input + * in the ‘height’ dimension. + * * 4: An INT32 value, specifying the filter width. + * * 5: An INT32 value, specifying the filter height. + * * 6: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, out_height, out_width, depth]. + */ + ANEURALNETWORKS_MAX_POOL_2D = 17, + + /** Multiplies two tensors, element-wise. + * + * Takes two input tensors of identical type and compatible dimensions. The output + * is the product of both input tensors, optionally modified by an activation function. + * + * Two dimensions are compatible when: + * 1. they are equal, or + * 2. one of them is 1 + * + * The size of the resulting output is the maximum size along each dimension of the + * input operands. It starts with the trailing dimensions, and works its way forward. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: A tensor. + * * 1: A tensor of the same type, and compatible dimensions as input0. + * * 2: An INT32 value, and has to be one of the {@link FuseCode} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * * 0: The product, a tensor of the same type as input0. + * For output tensor of {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, the following + * condition must be satisfied: output_scale > input1_scale * input2_scale. + */ + ANEURALNETWORKS_MUL = 18, + + /** Computes rectified linear activation on the input tensor element-wise. + * + * The output is calculated using this formula: + * + * output = max(0, input) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + */ + ANEURALNETWORKS_RELU = 19, + + /** Computes rectified linear 1 activation on the input tensor element-wise. + * + * The output is calculated using this formula: + * + * output = min(1.f, max(-1.f, input)) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + */ + ANEURALNETWORKS_RELU1 = 20, + + /** Computes rectified linear 6 activation on the input tensor element-wise. + * + * The output is calculated using this formula: + * + * output = min(6, max(0, input)) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + */ + ANEURALNETWORKS_RELU6 = 21, + + /** Reshapes a tensor. + * + * Given tensor, this operation returns a tensor that has the same values as tensor, + * but with a newly specified shape. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the tensor to be reshaped. + * * 1: A 1-D tensor of type {@link ANEURALNETWORKS_TENSOR_INT32}, defining the shape + * of the output tensor. The number of elements implied by shape must be the same + * as the number of elements in the input tensor. + * + * Outputs: + * * 0: The output tensor, of shape specified by the input shape. + */ + ANEURALNETWORKS_RESHAPE = 22, + + /** Resizes images to given size using the bilinear interpretation. + * + * Resized images will be distorted if their output aspect ratio is not the same as + * input aspect ratio. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Inputs: + * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying the input. + * * 1: An INT32 value, specifying the output height of the output tensor. + * * 2: An INT32 value, specifying the output width of the output tensor. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batches, new_height, new_width, depth]. + */ + ANEURALNETWORKS_RESIZE_BILINEAR = 23, + + /** + * A basic recurrent neural network layer. + * + * This layer implements the operation: + * outputs = state = activation(inputs * input_weights + state * recurrent_weights + bias) + * + * Where: + * * “input_weights” is a weight matrix that multiplies the inputs; + * * “recurrent_weights” is a weight matrix that multiplies the current + * “state” which itself is the output from the previous time step + * computation; + * * “bias” is a bias vector (added to each output vector in the batch); + * * “activation” is the function passed as the “fused_activation_function” + * argument (if not “NONE”). + * + * Supported tensor types (Type T): + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Inputs: + * * 0: input. + * A 2-D tensor of type T, of shape [batch_size, input_size], where + * “batch_size” corresponds to the batching dimension, and “input_size” is + * the size of the input. + * * 1: weights. + * A 2-D tensor of type T, of shape [num_units, input_size], where + * “num_units” corresponds to the number of units. + * * 2: recurrent_weights. + * A 2-D tensor of type T, of shape [num_units, num_units], with columns + * corresponding to the weights from each unit. + * * 3: bias. + * A 1-D tensor of type T, of shape [num_units]. + * * 4: hidden state (in). + * A 2-D tensor of type T, of shape [batch_size, num_units]. + * * 5: fused_activation_function. + * An optional {@link FuseCode} value indicating the activation + * function. If “NONE” is specified then it results in a linear + * activation. + * + * Outputs: + * * 0: hidden state (out). + * A 2-D tensor of type T, of shape [batch_size, num_units]. + * + * * 1: output. + * A 2-D tensor of type T, of shape [batch_size, num_units]. This is + * effectively the same as the current state value. + */ + ANEURALNETWORKS_RNN = 24, + + /** Computes the softmax activation on the input tensor element-wise, per batch, by + * normalizing the input vector so the maximum coefficient is zero. + * + * The output is calculated using this formula: + * + * output[batch, i] = + * exp((input[batch, i] - max(input[batch, :])) * beta) / + * sum_{k}{exp((input[batch, k] - max(input[batch, :])) * beta)} + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 2 or 4. + * + * Inputs: + * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped. + * * 1: A FLOAT32 value, specifying the positive scaling factor for the exponent, beta. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + * For {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} type, + * the scale must be 1.f / 256 and the zeroPoint must be 0. + */ + ANEURALNETWORKS_SOFTMAX = 25, + + /** Rearranges blocks of spatial data, into depth. + * + * More specifically, this op outputs a copy of the input tensor where values from + * the height and width dimensions are moved to the depth dimension. + * The value block_size indicates the input block size and how the data is moved. + * + * Chunks of data of size block_size * block_size from depth are rearranged into + * non-overlapping blocks of size block_size x block_size. + * + * The depth of the output tensor is input_depth * block_size * block_size. + * The input tensor's height and width must be divisible by block_size. + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: 4, with "NHWC" data layout. + * + * Inputs: + * * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input. + * * 1: An INT32 value, specifying the block_size. block_size must be >=1 and + * block_size must be a divisor of both the input height and width. + * + * Outputs: + * * 0: The output 4-D tensor, of shape [batch, height/block_size, width/block_size, + * depth*block_size*block_size]. + */ + ANEURALNETWORKS_SPACE_TO_DEPTH = 26, + + /** + * SVDF op is a kind of stateful layer derived from the notion that a + * densely connected layer that's processing a sequence of input frames can + * be approximated by using a singular value decomposition of each of its + * nodes. The implementation is based on: + * + * https://research.google.com/pubs/archive/43813.pdf + * + * P. Nakkiran, R. Alvarez, R. Prabhavalkar, C. Parada. + * “Compressing Deep Neural Networks using a Rank-Constrained Topology”. + * INTERSPEECH, 2015. + * + * It processes the incoming input using a 2-stage filtering mechanism: + * * stage 1 performs filtering on the "features" dimension, whose outputs get + * pushed into a memory of fixed-size memory_size. + * * stage 2 performs filtering on the "time" dimension of the memory_size + * memoized outputs of stage 1. + * + * Specifically, for rank 1, this layer implements the operation: + * + * memory = push(conv1d(inputs, weights_feature, feature_dim, + * "ANEURALNETWORKS_PADDING_VALID")); + * outputs = activation(memory * weights_time + bias); + * + * Where: + * * “weights_feature” is a weights matrix that processes the inputs (by + * convolving the input with every “feature filter”), and whose outputs get + * pushed, stacked in order, into the fixed-size “memory” (the oldest entry + * gets dropped); + * * “weights_time” is a weights matrix that processes the “memory” (by a + * batched matrix multiplication on the num_units); + * * “bias” is an optional bias vector (added to each output vector in the + * batch); and + * * “activation” is the function passed as the “fused_activation_function” + * argument (if not “NONE”). + * + * Each rank adds a dimension to the weights matrices by means of stacking + * the filters. + * + * Supported tensor types (type T): + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Inputs: + * * 0: input. + * A 2-D tensor of type T, of shape [batch_size, input_size], where + * “batch_size” corresponds to the batching dimension, and “input_size” is + * the size of the input. + * * 1: weights_feature. + * A 2-D tensor of type T, of shape [num_units, input_size], where + * “num_units” corresponds to the number of units. + * * 2: weights_time. + * A 2-D tensor of type T, of shape [num_units, memory_size], where + * “memory_size” corresponds to the fixed-size of the memory. + * * 3: bias. + * An optional 1-D tensor of type T, of shape [num_units]. + * * 4: state (in). + * A 2-D tensor of type T, of shape [batch_size, (memory_size - 1) * num_units * rank]. + * * 5: rank. + * The rank of the SVD approximation. + * * 6: fused_activation_function. + * An optional {@link FuseCode} value indicating the activation function. + * If “NONE” is specified then it results in a linear activation. + * + * Outputs: + * * 0: state (out). + * A 2-D tensor of type T, of shape [batch_size, (memory_size - 1) * num_units * rank]. + * * 1: output. + * A 2-D tensor of type T, of shape [batch_size, num_units]. + */ + ANEURALNETWORKS_SVDF = 27, + + /** Computes hyperbolic tangent of input tensor element-wise. + * + * The output is calculated using this formula: + * + * output = tanh(input) + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: up to 4. + * + * Inputs: + * * 0: A tensor, specifying the input. + * + * Outputs: + * * 0: The output tensor of same shape as input0. + */ + ANEURALNETWORKS_TANH = 28, + + /** + * Element-wise division of two tensors. + * + * Takes two input tensors of identical type and compatible dimensions. The output + * is the result of dividing the first input tensor by the second, optionally + * modified by an activation function. + * + * Two dimensions are compatible when: + * 1. they are equal, or + * 2. one of them is 1 + * + * The size of the output is the maximum size along each dimension of the input operands. + * It starts with the trailing dimensions, and works its way forward. + * + * Example: + * input1.dimension = {4, 1, 2} + * input2.dimension = {5, 4, 3, 1} + * output.dimension = {5, 4, 3, 2} + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * 0: An n-D tensor, specifying the first input. + * 1: A tensor of the same type, and compatible dimensions as input0. + * 2: An INT32 value, and has to be one of the {@link FusedActivationFunc} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * 0: A tensor of the same type as input0. + */ + ANEURALNETWORKS_DIV = 30, + + /** + * Pads a tensor. + * + * This operation pads a tensor according to the specified paddings. + * + * Supported tensor {@link OperandCode}: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: An n-D tensor, specifying the tensor to be padded. + * * 1: A 2-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the paddings + * for each spatial dimension of the input tensor. The shape of the + * tensor must be {rank(input0), 2}. + * padding[i, 0] specifies the number of elements to be padded in the + * front of dimension i. + * padding[i, 1] specifies the number of elements to be padded after the + * end of dimension i. + * + * Outputs: + * * 0: A tensor of the same {@link OperandCode} as input0. The + * output tensor has the same rank as input0, and each + * dimension of the output tensor has the same size as the + * corresponding dimension of the input tensor plus the size + * of the padding: + * output0.dimension[i] = + * padding[i, 0] + input0.dimension[i] + padding[i, 1] + * + * Available since API level 28. + */ + ANEURALNETWORKS_PAD = 32, + + /** + * Extracts a strided slice of a tensor. + * + * Roughly speaking, this op extracts a slice of size (end - begin) / stride + * from the given input tensor. Starting at the location specified by begin + * the slice continues by adding stride to the index until all dimensions + * are not less than end. Note that a stride can be negative, which causes a + * reverse slice. + * + * Supported tensor {@link OperandCode}: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * * 0: An n-D tensor, specifying the tensor to be sliced. + * * 1: A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the starts of + * the dimensions of the input tensor to be sliced. The length must be + * of rank(input0). + * * 2: A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the ends of + * the dimensions of the input tensor to be sliced. The length must be + * of rank(input0). + * * 3: A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the strides of + * the dimensions of the input tensor to be sliced. The length must be + * of rank(input0). + * * 4: An {@link ANEURALNETWORKS_INT32} scalar, begin_mask. If the ith bit + * of begin_mask is set, begin[i] is ignored and the fullest possible + * range in that dimension is used instead. + * * 5: An {@link ANEURALNETWORKS_INT32} scalar, end_mask. If the ith bit of + * end_mask is set, end[i] is ignored and the fullest possible range in + * that dimension is used instead. + * * 6: An {@link ANEURALNETWORKS_INT32} scalar, shrink_axis_mask. An int32 + * mask. If the ith bit of shrink_axis_mask is set, it implies that the + * ith specification shrinks the dimensionality by 1. A slice of size 1 + * starting from begin[i] in the dimension must be preserved. + * + * Outputs: + * * 0: A tensor of the same {@link OperandCode} as input0. + */ + ANEURALNETWORKS_STRIDED_SLICE = 35, + + /** + * Element-wise subtraction of two tensors. + * + * Takes two input tensors of identical type and compatible dimensions. The output + * is the result of subtracting the second input tensor from the first one, optionally + * modified by an activation function. + * + * Two dimensions are compatible when: + * 1. they are equal, or + * 2. one of them is 1 + * + * The size of the output is the maximum size along each dimension of the input operands. + * It starts with the trailing dimensions, and works its way forward. + * + * Example: + * input1.dimension = {4, 1, 2} + * input2.dimension = {5, 4, 3, 1} + * output.dimension = {5, 4, 3, 2} + * + * Supported tensor types: + * * {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * + * Supported tensor rank: up to 4 + * + * Inputs: + * 0: An n-D tensor, specifying the first input. + * 1: A tensor of the same type, and compatible dimensions as input0. + * 2: An INT32 value, and has to be one of the {@link FusedActivationFunc} values. + * Specifies the activation to invoke on the result of each addition. + * + * Outputs: + * 0: A tensor of the same type as input0. + */ + ANEURALNETWORKS_SUB = 36, +} OperationCode; + +/** + * Fused activation function types. + * + */ +typedef enum { + /** NO fused activation function. */ + ANEURALNETWORKS_FUSED_NONE = 0, + /** Fused ReLU activation function. */ + ANEURALNETWORKS_FUSED_RELU = 1, + /** Fused ReLU1 activation function. */ + ANEURALNETWORKS_FUSED_RELU1 = 2, + /** Fused ReLU6 activation function. */ + ANEURALNETWORKS_FUSED_RELU6 = 3, +} FuseCode; + +/** + * Implicit padding algorithms. + * + */ +typedef enum { + /** + * SAME padding. + * Padding on both ends are the "same": + * padding_to_beginning = total_padding / 2 + * padding_to_end = (total_padding + 1)/2. + * i.e., for even number of padding, padding to both ends are exactly + * the same; for odd number of padding, padding to the ending is bigger + * than the padding to the beginning by 1. + * + * total_padding is a function of input, stride and filter size. + * It could be computed as follows: + * out_size = (input + stride - 1) / stride; + * needed_input = (out_size - 1) * stride + filter_size + * total_padding = max(0, needed_input - output_size) + * The computation is the same for the horizontal and vertical directions. + */ + ANEURALNETWORKS_PADDING_SAME = 1, + + /** + * VALID padding. + * No padding. When the input size is not evenly divisible by + * the filter size, the input at the end that could not fill + * the whole filter tile will simply be ignored. + */ + ANEURALNETWORKS_PADDING_VALID = 2, +} PaddingCode; + +/** + * Execution preferences. + */ +typedef enum { + /** + * Prefer executing in a way that minimizes battery drain. + * This is desirable for compilations that will be executed often. + */ + ANEURALNETWORKS_PREFER_LOW_POWER = 0, + /** + * Prefer returning a single answer as fast as possible, even if this causes + * more power consumption. + */ + ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER = 1, + /** + * Prefer maximizing the throughput of successive frames, for example when + * processing successive frames coming from the camera. + */ + ANEURALNETWORKS_PREFER_SUSTAINED_SPEED = 2, +} PreferenceCode; + +/** + * Result codes. + */ +typedef enum { + ANEURALNETWORKS_NO_ERROR = 0, + ANEURALNETWORKS_OUT_OF_MEMORY = 1, + ANEURALNETWORKS_INCOMPLETE = 2, + ANEURALNETWORKS_UNEXPECTED_NULL = 3, + ANEURALNETWORKS_BAD_DATA = 4, + ANEURALNETWORKS_OP_FAILED = 5, + ANEURALNETWORKS_UNMAPPABLE = 5, + ANEURALNETWORKS_BAD_STATE = 6, +} ResultCode; + +/** + * For {@link ANeuralNetworksModel_setOperandValue}, values with a + * length smaller or equal to this will be immediately copied into + * the model. The size is in bytes. + */ +enum { + ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES = 128 +}; + +/** + * ANeuralNetworksMemory is an opaque type that represents memory. + * + * This type is used to represent shared memory, memory mapped files, + * and similar memories. + * + * By using shared memory, a program can efficiently communicate to the + * runtime and drivers the tensors that define a model. See + * {@link ANeuralNetworksModel_setOperandValueFromMemory}. An application + * should typically create one shared memory object that contains every tensor + * needed to define a model. {@link ANeuralNetworksMemory_createFromFd} can be + * used to create shared memory from a file handle. {@link ANeuralNetworksMemory_createShared} + * can be used to directly created shared memory. + * + * Memory objects can also be used to specify the input and output arguments of + * an execution. See {@link ANeuralNetworksExecution_setInputFromMemory} + * and {@link ANeuralNetworksExecution_setOutputFromMemory}. + */ +typedef struct ANeuralNetworksMemory ANeuralNetworksMemory; + +/** + * ANeuralNetworksModel is an opaque type that contains a description of the + * mathematical operations that constitute the model. + * + *

The model will be built by calling

    + *
  • {@link ANeuralNetworksModel_create},
  • + *
  • {@link ANeuralNetworksModel_addOperation},
  • + *
  • {@link ANeuralNetworksModel_addOperand},
  • + *
+ * + * A model is completed by calling {@link ANeuralNetworksModel_finish}. + * A model is destroyed by calling {@link ANeuralNetworksModel_free}. + * + *

A model cannot be modified once {@link ANeuralNetworksModel_finish} + * has been called on it.

+ * + *

It is the application's responsibility to make sure that only one thread + * modifies a model at a given time. It is however safe for more than one + * thread to use the model once {@link ANeuralNetworksModel_finish} has returned.

+ * + *

It is also the application's responsibility to ensure that there are no other + * uses of the model after calling {@link ANeuralNetworksModel_free}. + * This includes any compilation or execution object created using the model.

+ */ +typedef struct ANeuralNetworksModel ANeuralNetworksModel; + +/** + * ANeuralNetworksCompilation is an opaque type that can be used to compile + * a machine learning model. + * + *

To use:

    + *
  • Create a new compilation instance by calling the + * {@link ANeuralNetworksCompilation_create} function.
  • + *
  • Set any desired properties on the compilation (for example, + * {@link ANeuralNetworksCompilation_setPreference}).
  • + *
  • Complete the compilation with {@link ANeuralNetworksCompilation_finish}.
  • + *
  • Use the compilation as many times as needed + * with {@link ANeuralNetworksExecution_create}.
  • + *
  • Destroy the compilation with {@link ANeuralNetworksCompilation_free} + * once all executions using the compilation have completed.

+ * + * A compilation is completed by calling {@link ANeuralNetworksCompilation_finish}. + * A compilation is destroyed by calling {@link ANeuralNetworksCompilation_free}. + * + *

A compilation cannot be modified once {@link ANeuralNetworksCompilation_finish} + * has been called on it.

+ * + *

It is the application's responsibility to make sure that only + * one thread modifies a compilation at a given time. It is however + * safe for more than one thread to use the compilation once + * {@link ANeuralNetworksCompilation_finish} has returned.

+ * + *

It is also the application's responsibility to ensure that there are no other + * uses of the compilation after calling {@link ANeuralNetworksCompilation_free}. + * This includes any execution object created using the compilation.

+ */ +typedef struct ANeuralNetworksCompilation ANeuralNetworksCompilation; + +/** + * ANeuralNetworksExecution is an opaque type that can be used to apply a machine + * learning model to a set of inputs. + * + *

To use:

    + *
  • Create a new execution instance by calling the + * {@link ANeuralNetworksExecution_create} function.
  • + *
  • Associate data to the model inputs with + * {@link ANeuralNetworksExecution_setInput} or + * {@link ANeuralNetworksExecution_setInputFromMemory}.
  • + *
  • Associate output buffers to the model outputs with + * {@link ANeuralNetworksExecution_setOutput} or + * {@link ANeuralNetworksExecution_setOutputFromMemory}.
  • + *
  • Apply the model with {@link ANeuralNetworksExecution_startCompute}.
  • + *
  • Wait for the execution to complete with {@link + * ANeuralNetworksEvent_wait}.
  • + *
  • Destroy the execution with + * {@link ANeuralNetworksExecution_free}.

+ * + *

An execution cannot be modified once {@link ANeuralNetworksExecution_startCompute} + * has been called on it.

+ * + *

An execution can be applied to a model with + * {@link ANeuralNetworksExecution_startCompute} only once. Create new executions + * to do new evaluations of the model.

+ * + *

It is the application's responsibility to make sure that only one thread + * modifies an execution at a given time. It is however safe for more than one + * thread to use {@link ANeuralNetworksEvent_wait} at the same time.

+ * + *

It is also the application's responsibility to ensure that there are no other + * uses of the request after calling {@link ANeuralNetworksExecution_free}.

+ */ +typedef struct ANeuralNetworksExecution ANeuralNetworksExecution; + +/** + * ANeuralNetworksOperandType describes the type of an operand. + * This structure is used to describe both scalars and tensors. + */ +typedef struct ANeuralNetworksOperandType { + /** The data type, e.g ANEURALNETWORKS_INT8. */ + int32_t type; + /** The number of dimensions. It should be 0 for scalars. */ + uint32_t dimensionCount; + /** The dimensions of the tensor. It should be nullptr for scalars. */ + const uint32_t* dimensions; + /** These two fields are only used for quantized tensors. + * They should be zero for scalars and non-fixed point tensors. + * The dequantized value of each entry is (value - zeroPoint) * scale. + */ + float scale; + int32_t zeroPoint; +} ANeuralNetworksOperandType; + +typedef int32_t ANeuralNetworksOperationType; + +/** + * ANeuralNetworksEvent is an opaque type that represents an event + * that will be signaled once an execution completes. + */ +typedef struct ANeuralNetworksEvent ANeuralNetworksEvent; + + +/** + * Creates a shared memory object from a file descriptor. + * + * The shared memory is backed by a file descriptor via mmap. + * See {@link ANeuralNetworksMemory} for a description on how to use + * this shared memory. + * + * @param size The requested size in bytes. + * Must not be larger than the file size. + * @param prot The desired memory protection for the mapping. + * It is either PROT_NONE or the bitwise OR of one or + * more of the following flags: PROT_READ, PROT_WRITE. + * @param fd The requested file descriptor. + * The file descriptor has to be mmap-able. The file + * descriptor will be duplicated. + * @param offset The offset to the beginning of the file of the area to map. + * The offset has to be aligned to a page size. + * @param memory The memory object to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if the request completed normally. + */ +int ANeuralNetworksMemory_createFromFd(size_t size, int protect, int fd, size_t offset, + ANeuralNetworksMemory** memory); + +/** + * Delete a memory object. + * + * Destroys the object used by the run time to keep track of the memory. + * This will free the underlying actual memory if no other code has open + * handles to this memory. + * + * @param memory The memory object to be freed. + */ +void ANeuralNetworksMemory_free(ANeuralNetworksMemory* memory); + +/** + * Create an empty {@link ANeuralNetworksModel}. + * + *

This only creates the object. Computation is performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + * The model should be constructed with calls to + * {@link ANeuralNetworksModel_addOperation} and + * {@link ANeuralNetworksModel_addOperand} + * + *

{@link ANeuralNetworksModel_finish} should be called once the model + * has been fully constructed.

+ * + *

{@link ANeuralNetworksModel_free} should be called once the model + * is no longer needed.

+ * + * @param model The {@link ANeuralNetworksModel} to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_create(ANeuralNetworksModel** model); + +/** + * Destroy a model. + * + * The model need not have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be destroyed. Passing NULL is acceptable and + * results in no operation. + */ +void ANeuralNetworksModel_free(ANeuralNetworksModel* model); + +/** + * Indicate that we have finished modifying a model. Required before + * calling {@link ANeuralNetworksCompilation_create}. + * + * An application is responsible to make sure that no other thread uses + * the model at the same time. + * + * This function must only be called once for a given model. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be finished. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_finish(ANeuralNetworksModel* model); + +/** + * Add an operand to a model. + * + * The order in which the operands are added is important. The first one added + * to a model will have the index value 0, the second 1, etc. These indexes are + * used as operand identifiers in {@link ANeuralNetworksModel_addOperation}, + * {@link ANeuralNetworksExecution_setInput}, + * {@link ANeuralNetworksExecution_setInputFromMemory}, + * {@link ANeuralNetworksExecution_setOutput}, + * {@link ANeuralNetworksExecution_setOutputFromMemory} and + * {@link ANeuralNetworksExecution_setOperandValue}. + * + * To build a model that can accomodate inputs of various sizes, as you may want + * to do for a CNN, set the size of the dimensions that will vary at run time to 0. + * If you do so, provide the full dimensions when calling + * {@link ANeuralNetworksExecution_setInput} or {@link ANeuralNetworksExecution_setInputFromMemory}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has been + * called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param type The {@link ANeuralNetworksOperandType} that describes the shape + * of the operand. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_addOperand(ANeuralNetworksModel* model, + const ANeuralNetworksOperandType* type); + +/** + * Sets an operand to a constant value. + * + * Values of length smaller or equal to + * {@link ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES} + * are immediately copied into the model. + * + * For values of length greater than {@link ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES}, + * a pointer to the buffer is stored within the model. The application is responsible + * for not changing the content of this region until all executions using this model + * have completed. As the data may be copied during processing, modifying the data + * after this call yields undefined results. + * + * For large tensors, using {@link ANeuralNetworksModel_setOperandValueFromMemory} + * is likely to be more efficient. + * + * To indicate that an optional operand should be considered missing, + * pass nullptr for buffer and 0 for length. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has been + * called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel* model, int32_t index, + const void* buffer, size_t length); + +/** + * Sets an operand to a value stored in a memory object. + * + * The content of the memory is not copied. A reference to that memory is stored + * inside the model. The application is responsible for not changing the content + * of the memory region until all executions using this model have completed. + * As the data may be copied during processing, modifying the data after this call + * yields undefined results. + * + * To indicate that an optional operand should be considered missing, + * use {@link ANeuralNetworksModel_setOperandValue} instead, passing nullptr for buffer. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has been + * called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_setOperandValueFromMemory(ANeuralNetworksModel* model, int32_t index, + const ANeuralNetworksMemory* memory, + size_t offset, size_t length); + +/** + * Add an operation to a model. + * + * @param model The model to be modified. + * @param type The type of the operation. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying each operand. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying each operand. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has been + * called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model, + ANeuralNetworksOperationType type, uint32_t inputCount, + const uint32_t* inputs, uint32_t outputCount, + const uint32_t* outputs); + +/** + * Specfifies which operands will be the model's inputs and outputs. + * + * An operand cannot be used for both input and output. Doing so will + * return an error. + * + * @param model The model to be modified. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying the input operands. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying the output operands. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has been + * called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + */ +int ANeuralNetworksModel_identifyInputsAndOutputs(ANeuralNetworksModel* model, uint32_t inputCount, + const uint32_t* inputs, uint32_t outputCount, + const uint32_t* outputs); + +/** + * Create a {@link ANeuralNetworksCompilation} to compile the given model. + * + *

This only creates the object. Compilation is only performed once + * {@link ANeuralNetworksCompilation_finish} is invoked.

+ * + *

{@link ANeuralNetworksCompilation_finish} should be called once + * all desired properties have been set on the compilation.

+ * + *

{@link ANeuralNetworksModel_free} should be called once the compilation + * is no longer needed.

+ * + *

The provided model must outlive the compilation.

+ * + * The model must already have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded usage. + * + * @param model The {@link ANeuralNetworksModel} to be compiled. + * @param compilation The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the model is invalid. + */ +int ANeuralNetworksCompilation_create(ANeuralNetworksModel* model, + ANeuralNetworksCompilation** compilation); + +/** + * Destroy a compilation. + * + * The compilation need not have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded usage. + * + * @param compilation The compilation to be destroyed. Passing NULL is acceptable and + * results in no operation. + */ +void ANeuralNetworksCompilation_free(ANeuralNetworksCompilation* compilation); + +/** + * Sets the execution preference. + * + *

Provides guidance to the runtime when trade-offs are possible.

+ * + * See {@link ANeuralNetworksCompilation} for information on multithreaded usage. + * + * @param compilation The compilation to be modified. + * @param preference Either {@link PREFER_LOW_POWER}, + * {@link PREFER_SINGLE_FAST_ANSWER}, or + * {@link PREFER_SUSTAINED_SPEED}. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksCompilation_setPreference(ANeuralNetworksCompilation* compilation, + int32_t preference); + +/** + * Indicate that we have finished modifying a compilation. Required before + * calling {@link ANeuralNetworksExecution_create}. + * + * An application is responsible to make sure that no other thread uses + * the compilation at the same time. + * + * This function must only be called once for a given compilation. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded usage. + * + * @param compilation The compilation to be finished. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksCompilation_finish(ANeuralNetworksCompilation* compilation); + +/** + * Create a {@link ANeuralNetworksExecution} to apply the given compilation. + * This only creates the object. Computation is only performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + *

The provided compilation must outlive the execution.

+ * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param compilation The {@link ANeuralNetworksCompilation} to be evaluated. + * @param execution The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the compilation is invalid. + */ +int ANeuralNetworksExecution_create(ANeuralNetworksCompilation* compilation, + ANeuralNetworksExecution** execution); + +/** + * Destroy an execution. + * + *

If called on an execution for which + * {@link ANeuralNetworksExecution_startCompute} has been called, the + * function will return immediately but will mark the execution to be deleted + * once the computation completes. The related {@link ANeuralNetworksEvent} + * will be signaled and the {@link ANeuralNetworksEvent_wait} will return + * ANEURALNETWORKS_ERROR_DELETED. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be destroyed. Passing NULL is acceptable and + * results in no operation. + */ +void ANeuralNetworksExecution_free(ANeuralNetworksExecution* execution); + +/** + * Associate a user buffer with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

The provided buffer must outlive the execution.

+ * + * If the input is optional, you can indicate that it is omitted by + * passing nullptr for buffer and 0 for length. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This should be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other properties of the type must be the same as + * specified in the model. If the type is the same as specified + * when the model was built, NULL can be passed. + * @param buffer The buffer containing the data. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if the + * name is not recognized or the buffer is too small for the input. + */ +int ANeuralNetworksExecution_setInput(ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, const void* buffer, + size_t length); + +/** + * Associate part of a memory object with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

The provided memory must outlive the execution.

+ * + * If the input is optional, you can indicate that it is omitted by + * using @{Link ANeuralNetworks_setInput} instead, passing nullptr for buffer + * and 0 for length. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data whithin the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if the + * name is not recognized or the buffer is too small for the input. + */ +int ANeuralNetworksExecution_setInputFromMemory(ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, + const ANeuralNetworksMemory* memory, size_t offset, + size_t length); + +/** + * Associate a user buffer with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + * If the output is optional, you can indicate that it is omitted by + * passing nullptr for buffer and 0 for length. + * + *

The provided buffer must outlive the execution.

+ * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param buffer The buffer where the data is to be written. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if the + * name is not recognized or the buffer is too small for the output. + */ +int ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, void* buffer, + size_t length); + +/** + * Associate part of a memory object with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + * If the output is optional, you can indicate that it is omitted by + * using @{Link ANeuralNetworks_setOutput} instead, passing nullptr for buffer + * and 0 for length. + * + *

The provided memory must outlive the execution.

+ * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory where the data is to be stored. + * @param offset This specifies the location of the data whithin the memory. + * The offset is in bytes from the start of memory. + * @param length The length in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if the + * name is not recognized or the buffer is too small for the output. + */ +int ANeuralNetworksExecution_setOutputFromMemory(ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, + const ANeuralNetworksMemory* memory, size_t offset, + size_t length); + +/** + * Schedule evaluation of the execution. + * + *

Schedules evaluation of the execution. Once the model has been + * applied and the outputs are ready to be consumed, the returned event will be + * signaled. Use {@link ANeuralNetworksEvent_wait} to wait for that event. + *

+ * + * Multiple executions can be scheduled and evaluated concurrently. The + * runtime makes no guarantee on the ordering of completion of + * executions. If it's important to the application, the application + * should enforce the ordering by using + * {@link ANeuralNetworksEvent_wait}. + * + * ANeuralNetworksEvent_wait must be called to recuperate the resources used + * by the execution. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be scheduled and executed. + * @param event The event that will be signaled on completion. event is set to + * NULL if there's an error. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +int ANeuralNetworksExecution_startCompute(ANeuralNetworksExecution* execution, + ANeuralNetworksEvent** event); + +/** + * Waits until the execution completes. + * + * More than one thread can wait on an event. When the execution completes, + * all threads will be released. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @return ANEURALNETWORKS_NO_ERROR if the execution completed normally. + */ +int ANeuralNetworksEvent_wait(ANeuralNetworksEvent* event); + +/** + * Destroys the event. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + */ +void ANeuralNetworksEvent_free(ANeuralNetworksEvent* event); + +__END_DECLS + +#endif // __ANDROID_API__ >= 27 + +#endif // ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_H + +/** @} */ diff --git a/compiler/ann-ref/.FORMATDENY b/compiler/ann-ref/.FORMATDENY new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/ann-ref/CMakeLists.txt b/compiler/ann-ref/CMakeLists.txt new file mode 100644 index 000000000..0f3822514 --- /dev/null +++ b/compiler/ann-ref/CMakeLists.txt @@ -0,0 +1,32 @@ +nnas_find_package(Eigen QUIET) + +if(NOT Eigen_FOUND) + return() +endif(NOT Eigen_FOUND) + +nnas_find_package(GEMMLowp QUIET) + +if(NOT GEMMLowp_FOUND) + return() +endif(NOT GEMMLowp_FOUND) + +nnas_include(TargetRequire) + +TargetRequire_Assert(ann_api eigen gemmlowp) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +function(ann_ref_configure TARGET) + target_include_directories(${TARGET} PRIVATE src) + target_link_libraries(${TARGET} PRIVATE ann_api) + target_link_libraries(${TARGET} PRIVATE eigen) + target_link_libraries(${TARGET} PRIVATE gemmlowp) +endfunction(ann_ref_configure) + +add_library(ann_ref_static STATIC ${SOURCES}) +set_target_properties(ann_ref_static PROPERTIES POSITION_INDEPENDENT_CODE ON) +ann_ref_configure(ann_ref_static) + +add_library(ann_ref_shared SHARED ${SOURCES}) +set_target_properties(ann_ref_shared PROPERTIES OUTPUT_NAME neuralnetworks) +ann_ref_configure(ann_ref_shared) diff --git a/compiler/ann-ref/README.md b/compiler/ann-ref/README.md new file mode 100644 index 000000000..6b13b5fdd --- /dev/null +++ b/compiler/ann-ref/README.md @@ -0,0 +1,7 @@ +# ann-ref + +_ann-ref_ is a reference Android NN API implementation for Linux. + +**DISCLAIMER** + +_ann-ref_ is incomplete in terms of its functionalities. diff --git a/compiler/ann-ref/requires.cmake b/compiler/ann-ref/requires.cmake new file mode 100644 index 000000000..b6b647600 --- /dev/null +++ b/compiler/ann-ref/requires.cmake @@ -0,0 +1 @@ +require("ann-api") diff --git a/compiler/ann-ref/src/Assert.h b/compiler/ann-ref/src/Assert.h new file mode 100644 index 000000000..744305607 --- /dev/null +++ b/compiler/ann-ref/src/Assert.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __ASSERT_H__ +#define __ASSERT_H__ + +#include "Logging.h" + +// Assert macro, as Android does not generally support assert. +#define ASSERT(v) \ + do \ + { \ + if (!(v)) \ + { \ + LOG(ERROR) << "'" << #v << "' failed at " << __FILE__ << ":" << __LINE__ << "'\n"; \ + abort(); \ + } \ + } while (0) + +#endif // __ASSERT_H__ diff --git a/compiler/ann-ref/src/CompilationBuilder.cpp b/compiler/ann-ref/src/CompilationBuilder.cpp new file mode 100644 index 000000000..a14dbc1b6 --- /dev/null +++ b/compiler/ann-ref/src/CompilationBuilder.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ExecutionBuilder.h" +#include "CompilationBuilder.h" + +#include "Logging.h" + +CompilationBuilder::CompilationBuilder(const ModelBuilder *model) : mModel(model) +{ + VLOG(COMPILATION) << "CompilationBuilder::CompilationBuilder"; +} + +int CompilationBuilder::finish() +{ + if (mFinished) + { + LOG(ERROR) << "ANeuralNetworksCompilation_finish called more than once"; + return ANEURALNETWORKS_BAD_STATE; + } + // TODO validate the rest + + mFinished = true; + + return ANEURALNETWORKS_NO_ERROR; +} + +int CompilationBuilder::createExecution(ExecutionBuilder **execution) +{ + if (!mFinished) + { + LOG(ERROR) << "ANeuralNetworksExecution_create passed an unfinished compilation"; + *execution = nullptr; + return ANEURALNETWORKS_BAD_STATE; + } + *execution = new ExecutionBuilder(mModel); + return (*execution ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY); +} diff --git a/compiler/ann-ref/src/CompilationBuilder.h b/compiler/ann-ref/src/CompilationBuilder.h new file mode 100644 index 000000000..92c1ab4bf --- /dev/null +++ b/compiler/ann-ref/src/CompilationBuilder.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __COMPILATION_BUILDER_H__ +#define __COMPILATION_BUILDER_H__ + +#include "NeuralNetworks.h" + +class ModelBuilder; +class ExecutionBuilder; + +class CompilationBuilder +{ +public: + CompilationBuilder(const ModelBuilder *model); + +public: + int finish(); + + int createExecution(ExecutionBuilder **execution); + +private: + const ModelBuilder *mModel; + + // Once the compilation has been finished, we should not allow further + // modifications to the compilation. + bool mFinished = false; +}; + +#endif // __COMPILATION_BUILDER_H__ diff --git a/compiler/ann-ref/src/ExecutionBuilder.cpp b/compiler/ann-ref/src/ExecutionBuilder.cpp new file mode 100644 index 000000000..9df78bfc3 --- /dev/null +++ b/compiler/ann-ref/src/ExecutionBuilder.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ExecutionBuilder.h" +#include "CompilationBuilder.h" +#include "ModelBuilder.h" + +#include "Executor.h" + +#include "Logging.h" +#include "Validation.h" + +static void setRequestArgumentArray(const std::vector &argumentInfos, + std::vector *ioInfos) +{ + size_t count = argumentInfos.size(); + ioInfos->resize(count); + for (size_t i = 0; i < count; i++) + { + const auto &info = argumentInfos[i]; + (*ioInfos)[i] = { + .hasNoValue = info.state == ModelArgumentInfo::HAS_NO_VALUE, + .location = info.locationAndLength, + .dimensions = info.dimensions, + }; + } +} + +bool setRunTimePoolInfosFromMemories(std::vector *poolInfos, + const std::vector &pools) +{ + poolInfos->resize(pools.size()); + for (size_t i = 0; i < pools.size(); i++) + { + auto &poolInfo = (*poolInfos)[i]; + if (!poolInfo.set(pools[i])) + { + LOG(ERROR) << "Could not map pool"; + return false; + } + } + return true; +} + +ExecutionBuilder::ExecutionBuilder(const ModelBuilder *model) + : mModel(model), mInputs(mModel->inputCount()), mOutputs(mModel->outputCount()) +{ + VLOG(EXECUTION) << "ExecutionBuilder::ExecutionBuilder"; +} + +int ExecutionBuilder::setInput(uint32_t index, const ANeuralNetworksOperandType *type, + const void *buffer, size_t length) +{ + uint32_t count = static_cast(mInputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) + { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setInput", false); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast(length); + return mInputs[index].setFromPointer(mModel->getInputOperand(index), type, + const_cast(buffer), l); +} + +int ExecutionBuilder::setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length) +{ + uint32_t count = static_cast(mInputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t poolIndex = mMemories.add(memory); + return mInputs[index].setFromMemory(mModel->getInputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::setOutput(uint32_t index, const ANeuralNetworksOperandType *type, + void *buffer, size_t length) +{ + uint32_t count = static_cast(mOutputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) + { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setOutput", false); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast(length); + return mOutputs[index].setFromPointer(mModel->getOutputOperand(index), type, buffer, l); +} + +int ExecutionBuilder::setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length) +{ + // Should be similar to StepExecutor::setInputOrOutputFromTemporaryMemory() + + uint32_t count = static_cast(mOutputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory bad index " << index << " " + << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate the rest + uint32_t poolIndex = mMemories.add(memory); + return mOutputs[index].setFromMemory(mModel->getOutputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::startCompute(void) +{ + Model model; + mModel->publish(&model); + + // modelPoolInfo holds the infomation of pre-allocated memory pools during model construction + std::vector modelPoolInfos; + if (!setRunTimePoolInfosFromMemories(&modelPoolInfos, model.pools)) + { + return ANEURALNETWORKS_UNMAPPABLE; + } + + std::vector requestPoolInfos; + uint32_t count = mMemories.size(); + requestPoolInfos.resize(count); + // Create as many pools as there are input / output + auto fixPointerArguments = [&requestPoolInfos](std::vector &argumentInfos) { + for (ModelArgumentInfo &argumentInfo : argumentInfos) + { + if (argumentInfo.state == ModelArgumentInfo::POINTER) + { + RunTimePoolInfo runTimeInfo; + runTimeInfo.buffer = static_cast(argumentInfo.buffer); + argumentInfo.locationAndLength.poolIndex = static_cast(requestPoolInfos.size()); + argumentInfo.locationAndLength.offset = 0; + requestPoolInfos.push_back(runTimeInfo); + } + } + }; + fixPointerArguments(mInputs); + fixPointerArguments(mOutputs); + + Request request; + setRequestArgumentArray(mInputs, &request.inputs); + setRequestArgumentArray(mOutputs, &request.outputs); + + Executor executor; + return executor.run(model, request, modelPoolInfos, requestPoolInfos); +} diff --git a/compiler/ann-ref/src/ExecutionBuilder.h b/compiler/ann-ref/src/ExecutionBuilder.h new file mode 100644 index 000000000..0bf5ef755 --- /dev/null +++ b/compiler/ann-ref/src/ExecutionBuilder.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __EXECUTION_BUILDER_H__ +#define __EXECUTION_BUILDER_H__ + +#include "NeuralNetworks.h" + +#include "ModelBuilder.h" +#include "ModelArgumentInfo.h" + +#include "Memory.h" + +#include + +class ModelBuilder; + +class ExecutionBuilder +{ +public: + ExecutionBuilder(const ModelBuilder *); + +public: + int setInput(uint32_t index, const ANeuralNetworksOperandType *type, const void *buffer, + size_t length); + int setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length); + +public: + int setOutput(uint32_t index, const ANeuralNetworksOperandType *type, void *buffer, + size_t length); + int setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length); + +public: + int startCompute(void); + +private: + const ModelBuilder *mModel; + +private: + // The information we'll send to the driver about the inputs and outputs. + // Note that we build this in two steps: + // 1. As the arguments are specified, set the corresponding mInputs or mOutputs element. + // If set from a pointer, don't set the location in the RequestArgument but store it + // instead in mInputBuffers or mOutputBuffers. + // 2. Once we have all the inputs and outputs, if needed, allocate shared memory for + // the m*Buffers entries. Copy the input values into the shared memory. + // We do this to avoid creating a lot of shared memory objects if we have a lot of + // parameters specified via pointers. We also avoid copying in the case where + // some of the nodes will interpreted on the CPU anyway. + std::vector mInputs; + std::vector mOutputs; + +private: + MemoryTracker mMemories; +}; + +#endif // __EXECUTION_BUILDER_H__ diff --git a/compiler/ann-ref/src/Executor.cpp b/compiler/ann-ref/src/Executor.cpp new file mode 100644 index 000000000..888fc9c81 --- /dev/null +++ b/compiler/ann-ref/src/Executor.cpp @@ -0,0 +1,814 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Executor.h" + +#include "NeuralNetworks.h" +#include "Shape.h" + +#include "ops/Add.h" +#include "ops/Add.float.h" +#include "ops/Conv2D.h" +#include "ops/Conv2D.float.h" +#include "ops/DepthwiseConv2D.h" +#include "ops/DepthwiseConv2D.float.h" +#include "ops/AvgPool2D.h" +#include "ops/AvgPool2D.float.h" +#include "ops/MaxPool2D.h" +#include "ops/MaxPool2D.float.h" +#include "ops/Mul.h" +#include "ops/Mul.float.h" +#include "ops/ReLU.h" +#include "ops/ReLU.float.h" +#include "ops/ReLU6.h" +#include "ops/ReLU6.float.h" +#include "ops/Concatenation.h" +#include "ops/Concatenation.float.h" +#include "ops/Reshape.h" +#include "ops/Softmax.h" +#include "ops/Softmax.float.h" +#include "ops/FullyConnected.h" +#include "ops/FullyConnected.float.h" +#include "ops/Pad.h" +#include "ops/Sub.h" +#include "ops/Sub.float.h" +#include "ops/Div.h" +#include "ops/Div.float.h" + +#include "Logging.h" +#include "Assert.h" + +enum PaddingScheme +{ + kPaddingUnknown = 0, + kPaddingSame = 1, + kPaddingValid = 2, +}; + +inline void calculateExplicitPadding(int32_t in_size, int32_t stride, int32_t filter_size, + int32_t padding_implicit, int32_t *padding_head, + int32_t *padding_tail) +{ + *padding_head = 0; + *padding_tail = 0; + + if (padding_implicit == kPaddingSame) + { + int32_t out_size = (in_size + stride - 1) / stride; + int32_t tmp = (out_size - 1) * stride + filter_size; + if (tmp > in_size) + { + *padding_head = (tmp - in_size) / 2; + *padding_tail = (tmp - in_size) - *padding_head; + } + } +} + +template static inline T getScalarData(const RunTimeOperandInfo &info) +{ + // TODO: Check buffer is at least as long as size of data. + T *data = reinterpret_cast(info.buffer); + return data[0]; +} + +// Updates the RunTimeOperandInfo with the newly calculated shape. +// Allocate the buffer if we need to. +static bool setInfoAndAllocateIfNeeded(RunTimeOperandInfo *info, const Shape &shape) +{ + // For user-provided model output operands, the parameters must match the Shape + // calculated from the preparation step. + if (info->lifetime == OperandLifeTime::MODEL_OUTPUT) + { + if (info->type != shape.type || info->dimensions != shape.dimensions) + { + LOG(ERROR) << "Invalid type or dimensions for model output"; + return false; + } + if (info->type == OperandType::TENSOR_QUANT8_ASYMM && + (info->scale != shape.scale || info->zeroPoint != shape.offset)) + { + LOG(ERROR) << "Invalid scale or zeroPoint for model output"; + return false; + } + } + info->type = shape.type; + info->dimensions = shape.dimensions; + info->scale = shape.scale; + info->zeroPoint = shape.offset; + if (info->lifetime == OperandLifeTime::TEMPORARY_VARIABLE && info->buffer == nullptr) + { + uint32_t length = sizeOfData(info->type, info->dimensions); + info->buffer = new uint8_t[length]; + if (info->buffer == nullptr) + { + return false; + } + } + return true; +} + +// Ignore the .pools entry in model and request. This will have been taken care of +// by the caller. +int Executor::run(const Model &model, const Request &request, + const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos) +{ + VLOG(CPUEXE) << "Executor::run()"; + + mModel = &model; + mRequest = &request; // TODO check if mRequest is needed + initializeRunTimeInfo(modelPoolInfos, requestPoolInfos); + // The model has serialized the operation in execution order. + for (const auto &operation : model.operations) + { + int n = executeOperation(operation); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + mModel = nullptr; + mRequest = nullptr; + VLOG(CPUEXE) << "Completed run normally"; + return ANEURALNETWORKS_NO_ERROR; +} + +bool Executor::initializeRunTimeInfo(const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos) +{ + VLOG(CPUEXE) << "Executor::initializeRunTimeInfo"; + const size_t count = mModel->operands.size(); + mOperands.resize(count); + + // Start by setting the runtime info to what's in the model. + for (size_t i = 0; i < count; i++) + { + const Operand &from = mModel->operands[i]; + RunTimeOperandInfo &to = mOperands[i]; + to.type = from.type; + to.dimensions = from.dimensions; + to.scale = from.scale; + to.zeroPoint = from.zeroPoint; + to.length = from.location.length; + to.lifetime = from.lifetime; + switch (from.lifetime) + { + case OperandLifeTime::TEMPORARY_VARIABLE: + to.buffer = nullptr; + to.numberOfUsesLeft = from.numberOfConsumers; + break; + case OperandLifeTime::CONSTANT_COPY: + to.buffer = const_cast(&mModel->operandValues[from.location.offset]); + to.numberOfUsesLeft = 0; + break; + case OperandLifeTime::CONSTANT_REFERENCE: + { + auto poolIndex = from.location.poolIndex; + ASSERT(poolIndex < modelPoolInfos.size()); + auto &r = modelPoolInfos[poolIndex]; + to.buffer = r.buffer + from.location.offset; + to.numberOfUsesLeft = 0; + break; + } + case OperandLifeTime::MODEL_INPUT: + case OperandLifeTime::MODEL_OUTPUT: + case OperandLifeTime::NO_VALUE: + to.buffer = nullptr; + to.numberOfUsesLeft = 0; + break; + default: + ASSERT(false); + break; + } + } + + // Adjust the runtime info for the arguments passed to the model, + // modifying the buffer location, and possibly the dimensions. + auto updateForArguments = [this, &requestPoolInfos](const std::vector &indexes, + const std::vector &arguments) { + ASSERT(indexes.size() == arguments.size()); + for (size_t i = 0; i < indexes.size(); i++) + { + const uint32_t operandIndex = indexes[i]; + const RequestArgument &from = arguments[i]; + RunTimeOperandInfo &to = mOperands[operandIndex]; + if (from.dimensions.size() > 0) + { + // It's the responsibility of the caller to validate that + // from.dimensions only modifies the dimensions that were + // unspecified in the model. That's the case in SampleDriver.cpp + // with the call to validateRequest(). + // TODO make sure that's the case for the default CPU path. + to.dimensions = from.dimensions; + } + if (from.hasNoValue) + { + to.lifetime = OperandLifeTime::NO_VALUE; + ASSERT(to.buffer == nullptr); + } + else + { + auto poolIndex = from.location.poolIndex; + ASSERT(poolIndex < requestPoolInfos.size()); + auto &r = requestPoolInfos[poolIndex]; + to.buffer = r.buffer + from.location.offset; + } + } + }; + updateForArguments(mModel->inputIndexes, mRequest->inputs); + updateForArguments(mModel->outputIndexes, mRequest->outputs); + + return true; +} + +void Executor::freeNoLongerUsedOperands(const std::vector &inputs) +{ + for (uint32_t i : inputs) + { + auto &info = mOperands[i]; + // Check if it's a static or model input/output. + if (info.numberOfUsesLeft == 0) + { + continue; + } + info.numberOfUsesLeft--; + if (info.numberOfUsesLeft == 0) + { + ASSERT(info.buffer != nullptr); + delete[] info.buffer; + info.buffer = nullptr; + } + } +} + +int Executor::executeOperation(const Operation &operation) +{ + const std::vector &ins = operation.inputs; + const std::vector &outs = operation.outputs; + bool success = false; + + // Function to verify that the number of input and output parameters + // matches what is expected. Also checks that all the parameters have + // values. This function is to be used only for operations that do not + // accept optional arguments. + // TODO Have a version that works for optional arguments. + auto allParametersPresent = [&operation, &ins, &outs, this](size_t requiredIns, + size_t requiredOuts) -> bool { + auto verify = [&operation, this](size_t requiredCount, const std::vector &indexes, + const char *type) -> bool { + size_t actualCount = indexes.size(); + if (actualCount != requiredCount) + { + LOG(ERROR) << getOperationName(operation.type) << ": Invalid number of " << type + << " operands. Got " << actualCount << " of " << requiredCount; + return false; + } + for (size_t i = 0; i < actualCount; i++) + { + if (mOperands[indexes[i]].lifetime == OperandLifeTime::NO_VALUE) + { + LOG(ERROR) << getOperationName(operation.type) << " " << type << " operand " << i + << " is required but missing."; + return false; + } + } + return true; + }; + return verify(requiredIns, ins, "in") && verify(requiredOuts, outs, "out"); + }; + + switch (operation.type) + { + case OperationType::ADD: + { + if (!allParametersPresent(3, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &in1 = mOperands[ins[0]]; + const RunTimeOperandInfo &in2 = mOperands[ins[1]]; + int32_t activation = getScalarData(mOperands[ins[2]]); + + RunTimeOperandInfo &out = mOperands[outs[0]]; + Shape outShape = out.shape(); + + ASSERT(in1.type == OperandType::TENSOR_FLOAT32); + { + success = addPrepare(in1.shape(), in2.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&out, outShape) && + addFloat32(reinterpret_cast(in1.buffer), in1.shape(), + reinterpret_cast(in2.buffer), in2.shape(), activation, + reinterpret_cast(out.buffer), outShape); + } + } + break; + case OperationType::DEPTHWISE_CONV_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 11 && inCount != 8) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &filter = mOperands[ins[1]]; + const RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t depth_multiplier; + int32_t activation; + + if (inCount == 11) + { + padding_left = getScalarData(mOperands[ins[3]]); + padding_right = getScalarData(mOperands[ins[4]]); + padding_top = getScalarData(mOperands[ins[5]]); + padding_bottom = getScalarData(mOperands[ins[6]]); + stride_width = getScalarData(mOperands[ins[7]]); + stride_height = getScalarData(mOperands[ins[8]]); + depth_multiplier = getScalarData(mOperands[ins[9]]); + activation = getScalarData(mOperands[ins[10]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[3]]); + stride_width = getScalarData(mOperands[ins[4]]); + stride_height = getScalarData(mOperands[ins[5]]); + depth_multiplier = getScalarData(mOperands[ins[6]]); + activation = getScalarData(mOperands[ins[7]]); + + Shape inputShape = input.shape(); + Shape filterShape = filter.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + int32_t filter_width = getSizeOfDimension(filterShape, 2); + int32_t filter_height = getSizeOfDimension(filterShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = + depthwiseConvPrepare(input.shape(), filter.shape(), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, + stride_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + depthwiseConvFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(filter.buffer), filter.shape(), + reinterpret_cast(bias.buffer), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + depth_multiplier, activation, reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::CONV_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &filter = mOperands[ins[1]]; + const RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[3]]); + padding_right = getScalarData(mOperands[ins[4]]); + padding_top = getScalarData(mOperands[ins[5]]); + padding_bottom = getScalarData(mOperands[ins[6]]); + stride_width = getScalarData(mOperands[ins[7]]); + stride_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[3]]); + stride_width = getScalarData(mOperands[ins[4]]); + stride_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + Shape filterShape = filter.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + int32_t filter_width = getSizeOfDimension(filterShape, 2); + int32_t filter_height = getSizeOfDimension(filterShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = + convPrepare(input.shape(), filter.shape(), bias.shape(), padding_left, padding_right, + padding_top, padding_bottom, stride_width, stride_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + convFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(filter.buffer), filter.shape(), + reinterpret_cast(bias.buffer), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + activation, reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::AVERAGE_POOL_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t filter_width, filter_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[1]]); + padding_right = getScalarData(mOperands[ins[2]]); + padding_top = getScalarData(mOperands[ins[3]]); + padding_bottom = getScalarData(mOperands[ins[4]]); + stride_width = getScalarData(mOperands[ins[5]]); + stride_height = getScalarData(mOperands[ins[6]]); + filter_width = getScalarData(mOperands[ins[7]]); + filter_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[1]]); + stride_width = getScalarData(mOperands[ins[2]]); + stride_height = getScalarData(mOperands[ins[3]]); + filter_width = getScalarData(mOperands[ins[4]]); + filter_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = averagePoolPrepare(input.shape(), padding_left, padding_right, padding_top, + padding_bottom, stride_width, stride_height, filter_width, + filter_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + averagePoolFloat32(reinterpret_cast(input.buffer), input.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + filter_width, filter_height, activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::MAX_POOL_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t filter_width, filter_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[1]]); + padding_right = getScalarData(mOperands[ins[2]]); + padding_top = getScalarData(mOperands[ins[3]]); + padding_bottom = getScalarData(mOperands[ins[4]]); + stride_width = getScalarData(mOperands[ins[5]]); + stride_height = getScalarData(mOperands[ins[6]]); + filter_width = getScalarData(mOperands[ins[7]]); + filter_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[1]]); + stride_width = getScalarData(mOperands[ins[2]]); + stride_height = getScalarData(mOperands[ins[3]]); + filter_width = getScalarData(mOperands[ins[4]]); + filter_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = maxPoolPrepare(input.shape(), padding_left, padding_right, padding_top, + padding_bottom, stride_width, stride_height, filter_width, + filter_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + maxPoolFloat32(reinterpret_cast(input.buffer), input.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + filter_width, filter_height, activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::MUL: + { + if (!allParametersPresent(3, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &in1 = mOperands[ins[0]]; + const RunTimeOperandInfo &in2 = mOperands[ins[1]]; + int32_t activation = getScalarData(mOperands[ins[2]]); + + RunTimeOperandInfo &out = mOperands[outs[0]]; + Shape outShape = out.shape(); + + ASSERT(in1.type == OperandType::TENSOR_FLOAT32); + { + success = mulPrepare(in1.shape(), in2.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&out, outShape) && + mulFloat32(reinterpret_cast(in1.buffer), in1.shape(), + reinterpret_cast(in2.buffer), in2.shape(), activation, + reinterpret_cast(out.buffer), outShape); + } + } + break; + case OperationType::RELU: + { + if (!allParametersPresent(1, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = reluPrepare(input.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + reluFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::RELU6: + { + if (!allParametersPresent(1, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = relu6Prepare(input.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + relu6Float32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::SOFTMAX: + { + if (!allParametersPresent(2, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + RunTimeOperandInfo &input = mOperands[ins[0]]; + float beta = getScalarData(mOperands[ins[1]]); + if (beta <= 0.0f) + { + LOG(ERROR) << "beta must be positive for softmax"; + return ANEURALNETWORKS_BAD_DATA; + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = softmaxPrepare(input.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + softmaxFloat32(reinterpret_cast(input.buffer), input.shape(), beta, + reinterpret_cast(output.buffer), output.shape()); + } + } + break; + case OperationType::FULLY_CONNECTED: + { + if (!allParametersPresent(4, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + RunTimeOperandInfo &input = mOperands[ins[0]]; + RunTimeOperandInfo &weights = mOperands[ins[1]]; + RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t activation = getScalarData(mOperands[ins[3]]); + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = fullyConnectedPrepare(input.shape(), weights.shape(), bias.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + fullyConnectedFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(weights.buffer), weights.shape(), + reinterpret_cast(bias.buffer), bias.shape(), activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::CONCATENATION: + { + if (outs.size() != 1 || ins.size() < 2) + { + return ANEURALNETWORKS_BAD_DATA; + } + int numInputTensors = ins.size() - 1; + int32_t axis = getScalarData(mOperands[ins[numInputTensors]]); + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + const RunTimeOperandInfo &firstInput = mOperands[ins[0]]; + ASSERT(firstInput.type == OperandType::TENSOR_FLOAT32); + { + std::vector inputShapes(numInputTensors); + std::vector inputDataPtrs(numInputTensors); + + for (int i = 0; i < numInputTensors; i++) + { + RunTimeOperandInfo &input = mOperands[ins[i]]; + inputShapes[i] = input.shape(); + inputDataPtrs[i] = reinterpret_cast(input.buffer); + } + success = concatenationPrepare(inputShapes, axis, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + concatenationFloat32(inputDataPtrs, inputShapes, axis, reinterpret_cast(output.buffer), + outShape); + } + } + break; + case OperationType::RESHAPE: + { + if (!allParametersPresent(2, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &targetShape = mOperands[ins[1]]; + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + success = reshapePrepare(input.shape(), reinterpret_cast(targetShape.buffer), + getNumberOfElements(targetShape.shape()), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + reshapeGeneric(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(output.buffer), outShape); + } + break; + case OperationType::PAD: + { + if (!allParametersPresent(2, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo& input = mOperands[ins[0]]; + const RunTimeOperandInfo& paddings = mOperands[ins[1]]; + + RunTimeOperandInfo& output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + success = padPrepare(input.shape(), + reinterpret_cast(paddings.buffer), + paddings.shape(), + &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + padGeneric(input.buffer, + input.shape(), + reinterpret_cast(paddings.buffer), + output.buffer, + outShape); + } + break; + case OperationType::SUB: + { + if (!allParametersPresent(3, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &in1 = mOperands[ins[0]]; + const RunTimeOperandInfo &in2 = mOperands[ins[1]]; + int32_t activation = getScalarData(mOperands[ins[2]]); + + RunTimeOperandInfo &out = mOperands[outs[0]]; + Shape outShape = out.shape(); + + ASSERT(in1.type == OperandType::TENSOR_FLOAT32); + { + success = subPrepare(in1.shape(), in2.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&out, outShape) && + subFloat32(reinterpret_cast(in1.buffer), in1.shape(), + reinterpret_cast(in2.buffer), in2.shape(), activation, + reinterpret_cast(out.buffer), outShape); + } + } + break; + case OperationType::DIV: + { + if (!allParametersPresent(3, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &in1 = mOperands[ins[0]]; + const RunTimeOperandInfo &in2 = mOperands[ins[1]]; + int32_t activation = getScalarData(mOperands[ins[2]]); + + RunTimeOperandInfo &out = mOperands[outs[0]]; + Shape outShape = out.shape(); + + ASSERT(in1.type == OperandType::TENSOR_FLOAT32); + { + success = divPrepare(in1.shape(), in2.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&out, outShape) && + divFloat32(reinterpret_cast(in1.buffer), in1.shape(), + reinterpret_cast(in2.buffer), in2.shape(), activation, + reinterpret_cast(out.buffer), outShape); + } + } + break; + default: + NYI(getOperationName(operation.type)); + break; + } + if (!success) + { + LOG(ERROR) << getOperationName(operation.type) << " failed."; + return ANEURALNETWORKS_OP_FAILED; + } + + freeNoLongerUsedOperands(ins); + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/compiler/ann-ref/src/Executor.h b/compiler/ann-ref/src/Executor.h new file mode 100644 index 000000000..66dcca116 --- /dev/null +++ b/compiler/ann-ref/src/Executor.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __EXECUTOR_H__ +#define __EXECUTOR_H__ + +#include "Model.h" + +#include "Shape.h" +#include "Request.h" + +#include + +// Information we maintain about each operand during execution that +// may change during execution. +struct RunTimeOperandInfo +{ + // TODO Storing the type here is redundant, as it won't change during execution. + OperandType type; + + // The type and dimensions of the operand. The dimensions can + // change at runtime. We include the type because it's useful + // to pass together with the dimension to the functions implementing + // the operators. + // + // Q: Is it possible?? + std::vector dimensions; + float scale; + int32_t zeroPoint; + + // Where the operand's data is stored. Check the corresponding + // location information in the model to figure out if this points + // to memory we have allocated for an temporary operand. + uint8_t *buffer; + // The length of the buffer. + uint32_t length; + + // Whether this is a temporary variable, a model input, a constant, etc. + OperandLifeTime lifetime; + + // Keeps track of how many operations have yet to make use + // of this temporary variable. When the count is decremented to 0, + // we free the buffer. For non-temporary variables, this count is + // always 0. + uint32_t numberOfUsesLeft; + + Shape shape() const + { + return Shape{.type = type, .dimensions = dimensions, .scale = scale, .offset = zeroPoint}; + } +}; + +// Used to keep a pointer to each of the memory pools +struct RunTimePoolInfo +{ + uint8_t *buffer; + + bool set(uint8_t *m) + { + buffer = m; + return true; + } +}; + +// This class is used to execute a model on the CPU. +class Executor +{ +public: + // Executes the model. The results will be stored at the locations + // specified in the constructor. + // The model must outlive the executor. We prevent it from being modified + // while this is executing. + int run(const Model &model, const Request &request, + const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos); + +private: + bool initializeRunTimeInfo(const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos); + // Runs one operation of the graph. + int executeOperation(const Operation &entry); + // Decrement the usage count for the operands listed. Frees the memory + // allocated for any temporary variable with a count of zero. + void freeNoLongerUsedOperands(const std::vector &inputs); + + // The model and the request that we'll execute. Only valid while run() + // is being executed. + const Model *mModel = nullptr; + const Request *mRequest = nullptr; + + // We're copying the list of all the dimensions from the model, as + // these may be modified when we run the operatins. Since we're + // making a full copy, the indexes used in the operand description + // stay valid. + // std::vector mDimensions; + // Runtime information about all the operands. + std::vector mOperands; +}; + +#endif // __CPU_EXECUTOR_H__ diff --git a/compiler/ann-ref/src/Logging.cpp b/compiler/ann-ref/src/Logging.cpp new file mode 100644 index 000000000..4f849efaa --- /dev/null +++ b/compiler/ann-ref/src/Logging.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Logging.h" + +VLogging::VLogging() +{ + _enabled = false; +} + +VLogging &VLogging::access() +{ + static VLogging instance; + return instance; +} + +std::ostream &VLogging::stream() { return std::cout; } diff --git a/compiler/ann-ref/src/Logging.h b/compiler/ann-ref/src/Logging.h new file mode 100644 index 000000000..1f81ad6e3 --- /dev/null +++ b/compiler/ann-ref/src/Logging.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include + +class VLogging +{ +public: + static VLogging &access(void); + bool enabled() const { return _enabled; } + std::ostream &stream(void); + +private: + VLogging(); + +private: + bool _enabled; +}; + +#define LOG(...) std::cout << std::endl +#define VLOG(...) \ + if (VLogging::access().enabled()) \ + (VLogging::access().stream() << std::endl) +#define NYI(module) std::cout << "NYI : '" << module << "' is not supported now." << std::endl; + +#endif // __LOGGING_H__ diff --git a/compiler/ann-ref/src/Macro.h b/compiler/ann-ref/src/Macro.h new file mode 100644 index 000000000..829c15425 --- /dev/null +++ b/compiler/ann-ref/src/Macro.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MACRO_H__ +#define __MACRO_H__ + +#define COUNT(X) (sizeof(X) / sizeof(X[0])) + +#endif // __MACRO_H__ diff --git a/compiler/ann-ref/src/Memory.cpp b/compiler/ann-ref/src/Memory.cpp new file mode 100644 index 000000000..fd70f8db7 --- /dev/null +++ b/compiler/ann-ref/src/Memory.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Memory" + +#include "Memory.h" +#include "NeuralNetworks.h" // ANEURALNETWORKS_XXX + +#include + +MappedMemory::~MappedMemory() +{ + if (_base) + { + munmap(_base, _size); + } +} + +int MappedMemory::set(size_t size, int prot, int fd, size_t offset) +{ +#if 0 + if (fd < 0) + { + LOG(ERROR) << "ANeuralNetworksMemory_createFromFd invalid fd " << fd; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (size == 0 || fd < 0) + { + LOG(ERROR) << "Invalid size or fd"; + return ANEURALNETWORKS_BAD_DATA; + } + int dupfd = dup(fd); + if (dupfd == -1) + { + LOG(ERROR) << "Failed to dup the fd"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } +#endif + void * const base = mmap(nullptr, size, prot, MAP_PRIVATE, fd, offset); + + if (base == MAP_FAILED) + { + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + _base = static_cast(base); + _size = size; + + return ANEURALNETWORKS_NO_ERROR; +} + +int MappedMemory::getPointer(uint8_t **buffer) const +{ + *buffer = _base; + return ANEURALNETWORKS_NO_ERROR; +} + +bool MappedMemory::validateSize(uint32_t offset, uint32_t length) const +{ + return true; +} + +PrivateMemory::~PrivateMemory() +{ + if (_base) + { + delete[] _base; + } +} + +int PrivateMemory::create(uint32_t size) +{ + auto base = new uint8_t[size]; + + // TODO Check allocation failure + _base = base; + _size = size; + + return ANEURALNETWORKS_NO_ERROR; +} + +int PrivateMemory::getPointer(uint8_t **buffer) const +{ + *buffer = _base; + return ANEURALNETWORKS_NO_ERROR; +} + +bool PrivateMemory::validateSize(uint32_t offset, uint32_t length) const +{ + return true; +} diff --git a/compiler/ann-ref/src/Memory.h b/compiler/ann-ref/src/Memory.h new file mode 100644 index 000000000..648b5c7d1 --- /dev/null +++ b/compiler/ann-ref/src/Memory.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MEMORY_H__ +#define __MEMORY_H__ + +#include +#include + +// Represents a memory region. +struct Memory +{ + Memory() = default; + virtual ~Memory() = default; + + // Disallow copy semantics to ensure the runtime object can only be freed + // once. Copy semantics could be enabled if some sort of reference counting + // or deep-copy system for runtime objects is added later. + Memory(const Memory &) = delete; + Memory &operator=(const Memory &) = delete; + + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const = 0; + virtual bool validateSize(uint32_t offset, uint32_t length) const = 0; +}; + +class MappedMemory final : public Memory +{ +public: + MappedMemory() = default; + +public: + ~MappedMemory(); + +public: + // Create the native_handle based on input size, prot, and fd. + // Existing native_handle will be deleted, and mHidlMemory will wrap + // the newly created native_handle. + int set(size_t size, int prot, int fd, size_t offset); + +public: + int getPointer(uint8_t **buffer) const override; + bool validateSize(uint32_t offset, uint32_t length) const override; + +private: + uint8_t *_base = nullptr; + size_t _size = 0; +}; + +// Represents a memory region. +class AllocatedMemory : public Memory +{ +public: + AllocatedMemory() = default; + virtual ~AllocatedMemory() = default; + +public: + virtual int create(uint32_t size) = 0; + +public: + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const = 0; + virtual bool validateSize(uint32_t offset, uint32_t length) const = 0; +}; + +class PrivateMemory final : public AllocatedMemory +{ +public: + PrivateMemory() = default; + ~PrivateMemory(); + +public: + // Disallow copy semantics to ensure the runtime object can only be freed + // once. Copy semantics could be enabled if some sort of reference counting + // or deep-copy system for runtime objects is added later. + PrivateMemory(const PrivateMemory &) = delete; + PrivateMemory &operator=(const PrivateMemory &) = delete; + +public: + virtual int create(uint32_t size); + +public: + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const; + virtual bool validateSize(uint32_t offset, uint32_t length) const; + +private: + uint8_t *_base = nullptr; + size_t _size = 0; +}; + +#endif // __MEMORY_H__ diff --git a/compiler/ann-ref/src/MemoryTracker.cpp b/compiler/ann-ref/src/MemoryTracker.cpp new file mode 100644 index 000000000..3c65149c6 --- /dev/null +++ b/compiler/ann-ref/src/MemoryTracker.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Memory" + +#include "NeuralNetworks.h" // For ANEURALNETWORKS_... +#include "MemoryTracker.h" + +#include "Logging.h" + +#include // It's for 'close' and 'dup' + // TODO-NNRT : Remove this if included another header including this. + +uint32_t MemoryTracker::add(const Memory *memory) +{ + VLOG(MODEL) << __func__ << " for " << memory; + // See if we already have this memory. If so, + // return its index. + auto i = mKnown.find(memory); + if (i != mKnown.end()) + { + return i->second; + } + VLOG(MODEL) << "It's new"; + // It's a new one. Save it an assign an index to it. + size_t next = mKnown.size(); + if (next > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworks more than 2^32 memories."; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t idx = static_cast(next); + mKnown[memory] = idx; + mMemories.push_back(memory); + return idx; +} diff --git a/compiler/ann-ref/src/MemoryTracker.h b/compiler/ann-ref/src/MemoryTracker.h new file mode 100644 index 000000000..af687d183 --- /dev/null +++ b/compiler/ann-ref/src/MemoryTracker.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MEMORY_TRACKER_H__ +#define __MEMORY_TRACKER_H__ + +#include "Memory.h" + +#include +#include + +// A utility class to accumulate mulitple Memory objects and assign each +// a distinct index number, starting with 0. +// +// The user of this class is responsible for avoiding concurrent calls +// to this class from multiple threads. +class MemoryTracker +{ +public: + // Adds the memory, if it does not already exists. Returns its index. + // The memories should survive the tracker. + uint32_t add(const Memory *memory); + // Returns the number of memories contained. + uint32_t size() const { return static_cast(mKnown.size()); } + // Returns the ith memory. + const Memory *operator[](size_t i) const { return mMemories[i]; } + +private: + // The vector of Memory pointers we are building. + std::vector mMemories; + // A faster way to see if we already have a memory than doing find(). + std::unordered_map mKnown; +}; + +#endif // __MEMORY_TRACKER_H__ diff --git a/compiler/ann-ref/src/Model.h b/compiler/ann-ref/src/Model.h new file mode 100644 index 000000000..dc6a0d3c9 --- /dev/null +++ b/compiler/ann-ref/src/Model.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MODEL_H__ +#define __MODEL_H__ + +#include "Operand.h" +#include "Operation.h" + +#include +#include + +struct Model final { + std::vector operands; + std::vector operations; + + std::vector inputIndexes; + std::vector outputIndexes; + + std::vector operandValues; + + std::vector pools; +}; + +#endif // __MODEL_H__ diff --git a/compiler/ann-ref/src/ModelArgumentInfo.cpp b/compiler/ann-ref/src/ModelArgumentInfo.cpp new file mode 100644 index 000000000..3c10cd0ea --- /dev/null +++ b/compiler/ann-ref/src/ModelArgumentInfo.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ModelArgumentInfo.h" +#include "NeuralNetworks.h" // For ANEURALNETWORKS_XXX +#include "Logging.h" +#include "Assert.h" + +// TODO-NNRT: Consider removing ModelArgumentInfo completely if it's not necessary +int ModelArgumentInfo::setFromPointer(const Operand &operand, + const ANeuralNetworksOperandType *type, void *data, + uint32_t length) +{ + if ((data == nullptr) != (length == 0)) + { + LOG(ERROR) << "Data pointer must be nullptr if and only if length is zero (data = " << data + << ", length = " << length << ")"; + return ANEURALNETWORKS_BAD_DATA; + } + if (data == nullptr) + { + state = ModelArgumentInfo::HAS_NO_VALUE; + } + else + { + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t neededLength = sizeOfData(operand.type, dimensions); + if (neededLength != length) + { + LOG(ERROR) << "Setting argument with invalid length: " << length + << ", expected length: " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + state = ModelArgumentInfo::POINTER; + } + buffer = data; + locationAndLength = {.poolIndex = 0, .offset = 0, .length = length}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::setFromMemory(const Operand &operand, const ANeuralNetworksOperandType *type, + uint32_t poolIndex, uint32_t offset, uint32_t length) +{ + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t neededLength = sizeOfData(operand.type, dimensions); + if (neededLength != length) + { + LOG(ERROR) << "Setting argument with invalid length: " << length + << ", expected length: " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + + state = ModelArgumentInfo::MEMORY; + locationAndLength = {.poolIndex = poolIndex, .offset = offset, .length = length}; + buffer = nullptr; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::updateDimensionInfo(const Operand &operand, + const ANeuralNetworksOperandType *newType) +{ + ASSERT(dimensions.empty()); + if (newType == nullptr) + { + for (auto i : operand.dimensions) + { + if (i == 0) + { + LOG(ERROR) << "Setting input/output with unspecified dimensions"; + return ANEURALNETWORKS_BAD_DATA; + } + } + dimensions = operand.dimensions; + } + else + { + uint32_t count = newType->dimensionCount; + if (static_cast(newType->type) != operand.type || + count != operand.dimensions.size()) + { + LOG(ERROR) << "Setting input/output with incompatible types"; + return ANEURALNETWORKS_BAD_DATA; + } + dimensions = std::vector(count); + for (uint32_t i = 0; i < count; i++) + { + if (operand.dimensions[i] != 0 && operand.dimensions[i] != newType->dimensions[i]) + { + LOG(ERROR) << "Overriding a fully specified dimension is disallowed"; + return ANEURALNETWORKS_BAD_DATA; + } + else + { + dimensions[i] = newType->dimensions[i]; + } + } + } + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/compiler/ann-ref/src/ModelArgumentInfo.h b/compiler/ann-ref/src/ModelArgumentInfo.h new file mode 100644 index 000000000..5773da743 --- /dev/null +++ b/compiler/ann-ref/src/ModelArgumentInfo.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MODEL_ARGUMENT_INFO_H__ +#define __MODEL_ARGUMENT_INFO_H__ + +#include "NeuralNetworks.h" + +#include "Operand.h" + +#include + +struct ModelArgumentInfo +{ + // Whether the argument was specified as being in a Memory, as a pointer, + // has no value, or has not been specified. + // If POINTER then: + // locationAndLength.length is valid. + // dimensions is valid. + // buffer is valid + // If MEMORY then: + // locationAndLength.location.{poolIndex, offset, length} is valid. + // dimensions is valid. + enum + { + POINTER, + MEMORY, + HAS_NO_VALUE, + UNSPECIFIED + } state = UNSPECIFIED; + + DataLocation locationAndLength; + + std::vector dimensions; + void *buffer; + + int setFromPointer(const Operand &operand, const ANeuralNetworksOperandType *type, void *buffer, + uint32_t length); + int setFromMemory(const Operand &operand, const ANeuralNetworksOperandType *type, + uint32_t poolIndex, uint32_t offset, uint32_t length); + int updateDimensionInfo(const Operand &operand, const ANeuralNetworksOperandType *newType); +}; + +#endif // __MODEL_ARGUMENT_INFO_H__ diff --git a/compiler/ann-ref/src/ModelBuilder.cpp b/compiler/ann-ref/src/ModelBuilder.cpp new file mode 100644 index 000000000..1f966bd2e --- /dev/null +++ b/compiler/ann-ref/src/ModelBuilder.cpp @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ModelBuilder.h" + +#include "CompilationBuilder.h" +#include "Validation.h" +#include "Logging.h" +#include "Assert.h" + +#include +#include + +static inline void setFromIntList(std::vector *vec, uint32_t count, const uint32_t *data) +{ + vec->resize(count); + for (uint32_t i = 0; i < count; i++) + { + (*vec)[i] = data[i]; + } +} + +// Returns the number of padding bytes needed to align data of the +// specified length. It aligns object of length: +// 2, 3 on a 2 byte boundary, +// 4+ on a 4 byte boundary. +// We may want to have different alignments for tensors. +// TODO: This is arbitrary, more a proof of concept. We need +// to determine what this should be. +uint32_t alignBytesNeeded(uint32_t index, size_t length) +{ + uint32_t pattern; + if (length < 2) + { + pattern = 0; // No alignment necessary + } + else if (length < 4) + { + pattern = 1; // Align on 2-byte boundary + } + else + { + pattern = 3; // Align on 4-byte boundary + } + uint32_t extra = (~(index - 1)) & pattern; + return extra; +} + + +// The maximum number of operands and operations that a model may have. +const uint32_t MAX_NUMBER_OF_OPERANDS = 0xFFFFFFFE; +const uint32_t MAX_NUMBER_OF_OPERATIONS = 0xFFFFFFFE; + +bool ModelBuilder::badState(const char *name) +{ + if (mCompletedModel) + { + LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify after model finished"; + return true; + } + if (mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify an invalid model"; + return true; + } + return false; +} + +int ModelBuilder::addOperand(const ANeuralNetworksOperandType &type) +{ + if (badState("addOperand")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + int n = validateOperandType(type, "ANeuralNetworksModel_addOperand", true); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + size_t idx = mOperands.size(); + if (idx >= MAX_NUMBER_OF_OPERANDS) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperand exceed max operands"; + return ANEURALNETWORKS_BAD_DATA; + } + mOperands.resize(idx + 1); + auto &operand = mOperands[idx]; + operand.type = static_cast(type.type); + setFromIntList(&operand.dimensions, type.dimensionCount, type.dimensions); + operand.numberOfConsumers = 0; + operand.scale = type.scale; + operand.zeroPoint = type.zeroPoint; + operand.lifetime = OperandLifeTime::TEMPORARY_VARIABLE; + operand.location = {.poolIndex = 0, .offset = 0, .length = 0}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::setOperandValue(uint32_t index, const void *buffer, size_t length) +{ + if (badState("setOperandValue")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + VLOG(MODEL) << __func__ << " for operand " << index << " size " << length; + if (index >= operandCount()) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting operand " << index << " of " + << operandCount(); + return ANEURALNETWORKS_BAD_DATA; + } + Operand &operand = mOperands[index]; + if (buffer == nullptr) + { + if (length) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue buffer is nullptr but length is " + "not 0"; + return ANEURALNETWORKS_BAD_DATA; + } + operand.lifetime = OperandLifeTime::NO_VALUE; + // The location is unused and is set to zeros. + operand.location = {.poolIndex = 0, .offset = 0, .length = 0}; + } + else + { + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue value length of " << length + << " exceeds max size"; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t valueLength = static_cast(length); + uint32_t neededLength = sizeOfData(operand.type, operand.dimensions); + if (neededLength != valueLength) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting " << valueLength + << " bytes when needing " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + if (valueLength <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) + { + uint32_t existingSize = static_cast(mSmallOperandValues.size()); + uint32_t extraBytes = alignBytesNeeded(existingSize, valueLength); + mSmallOperandValues.resize(existingSize + extraBytes + valueLength); + operand.lifetime = OperandLifeTime::CONSTANT_COPY; + operand.location = { + .poolIndex = 0, .offset = existingSize + extraBytes, .length = neededLength}; + memcpy(&mSmallOperandValues[operand.location.offset], buffer, valueLength); + VLOG(MODEL) << "Copied small value to offset " << operand.location.offset; + } + else + { + VLOG(MODEL) << "Saving large value"; + operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE; + // The values for poolIndex and offset will be set when the model is finished. + operand.location = {.poolIndex = 0, .offset = 0, .length = valueLength}; + // We keep track of the buffers. We'll allocate the shared memory only + // once we know the total size, to avoid needless copies. + mLargeOperandValues.push_back(LargeValue{.operandIndex = index, .buffer = buffer}); + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::setOperandValueFromMemory(uint32_t index, const Memory *memory, uint32_t offset, + size_t length) +{ + VLOG(MODEL) << __func__ << " for operand " << index << " offset " << offset << " size " << length; + if (badState("setOperandValueFromMemory")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + if (index >= operandCount()) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting operand " << index + << " of " << operandCount(); + return ANEURALNETWORKS_BAD_DATA; + } + Operand &operand = mOperands[index]; + uint32_t neededLength = sizeOfData(operand.type, operand.dimensions); + if (neededLength != length) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting " << length + << " bytes when needing " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate does not exceed length of memory + operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE; + operand.location = {.poolIndex = mMemories.add(memory), .offset = offset, .length = neededLength}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::addOperation(OperationType type, uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs) +{ + + if (badState("addOperation")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + if (!validateOperationType(type)) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation invalid operations type " + << static_cast(type); + return ANEURALNETWORKS_BAD_DATA; + } + int n = validateOperandList(inputCount, inputs, operandCount(), + "ANeuralNetworksModel_addOperation inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + n = validateOperandList(outputCount, outputs, operandCount(), + "ANeuralNetworksModel_addOperation outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + uint32_t operationIndex = operationCount(); + if (operationIndex >= MAX_NUMBER_OF_OPERATIONS) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation exceed max operations"; + return ANEURALNETWORKS_BAD_DATA; + } + mOperations.resize(operationIndex + 1); + auto &entry = mOperations[operationIndex]; + entry.type = type; + + setFromIntList(&entry.inputs, inputCount, inputs); + setFromIntList(&entry.outputs, outputCount, outputs); + for (uint32_t i : entry.inputs) + { + mOperands[i].numberOfConsumers++; + // TODO mOperands[i].consumers.push_back(operationIndex); + } + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::identifyInputsAndOutputs(uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs) +{ + if (badState("identifyInputsAndOutputs")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + int n = validateOperandList(inputCount, inputs, operandCount(), + "ANeuralNetworksModel_identifyInputsAndOutputs inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + n = validateOperandList(outputCount, outputs, operandCount(), + "ANeuralNetworksModel_identifyInputsAndOutputs outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + // Makes a copy of the index list, validates the arguments, and changes + // the lifetime info of the corresponding operand. + auto setArguments = [&](std::vector *indexVector, uint32_t indexCount, + const uint32_t *indexList, OperandLifeTime lifetime) -> bool { + indexVector->resize(indexCount); + for (uint32_t i = 0; i < indexCount; i++) + { + const uint32_t operandIndex = indexList[i]; + if (operandIndex >= mOperands.size()) + { + LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set input or output " + "to be " + << operandIndex << " as this exceeds the numbe of operands " << mOperands.size(); + return false; + } + (*indexVector)[i] = operandIndex; + Operand &operand = mOperands[operandIndex]; + if (operand.lifetime != OperandLifeTime::TEMPORARY_VARIABLE) + { + LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set operand " + << operandIndex + << " to be an input or output. Check that it's not a constant or " + "already an input or output"; + return false; + } + operand.lifetime = lifetime; + } + return true; + }; + + if (!setArguments(&mInputIndexes, inputCount, inputs, OperandLifeTime::MODEL_INPUT) || + !setArguments(&mOutputIndexes, outputCount, outputs, OperandLifeTime::MODEL_OUTPUT)) + { + return ANEURALNETWORKS_BAD_DATA; + } + + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::createCompilation(CompilationBuilder **compilation) +{ + if (!mCompletedModel || mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksCompilation_create passed an unfinished model"; + *compilation = nullptr; + return ANEURALNETWORKS_BAD_STATE; + } + *compilation = new CompilationBuilder(this); + return (*compilation ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY); +} + +int ModelBuilder::finish() +{ + if (mCompletedModel) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called more than once"; + return ANEURALNETWORKS_BAD_STATE; + } + if (mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called on an invalid model"; + return ANEURALNETWORKS_BAD_STATE; + } + + int n = copyLargeValuesToMemory(); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + Model modelForValidation; + publish(&modelForValidation); + if (!validateModel(modelForValidation)) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called on invalid model"; + mInvalidModel = true; + return ANEURALNETWORKS_BAD_DATA; + } + + // We sort the operations so that they will be in the appropriate + // order for a single-threaded, op at a time execution. + // TODO: we don't need this if we always run the partitioner. + sortIntoRunOrder(); + mCompletedModel = true; + return ANEURALNETWORKS_NO_ERROR; +} + +void ModelBuilder::sortIntoRunOrder() +{ + // Tracks the operations that can be executed. + std::vector opsReadyToRun; + std::vector runOrder; + + // Tracks how many inputs are needed for each operation to be ready to run. + std::multimap operandToOperations; + std::vector unknownInputCount(operationCount()); + for (uint32_t operationIndex = 0; operationIndex < operationCount(); operationIndex++) + { + uint32_t &count = unknownInputCount[operationIndex]; + count = 0; + for (uint32_t operandIndex : mOperations[operationIndex].inputs) + { + auto lifetime = mOperands[operandIndex].lifetime; + if (lifetime == OperandLifeTime::TEMPORARY_VARIABLE || + lifetime == OperandLifeTime::MODEL_OUTPUT) + { + count++; + operandToOperations.insert(std::pair(operandIndex, operationIndex)); + } + } + if (count == 0) + { + opsReadyToRun.push_back(operationIndex); + } + } + + while (opsReadyToRun.size() > 0) + { + // Execute the next op + int opIndex = opsReadyToRun.back(); + opsReadyToRun.pop_back(); + const Operation &operation = mOperations[opIndex]; + + runOrder.push_back(mOperations[opIndex]); + + // Mark all its outputs as known. + for (uint32_t operandIndex : operation.outputs) + { + auto range = operandToOperations.equal_range(operandIndex); + for (auto i = range.first; i != range.second; i++) + { + uint32_t &count = unknownInputCount[i->second]; + if (--count == 0) + { + opsReadyToRun.push_back(i->second); + } + } + } + } + mOperations = runOrder; +} + +void ModelBuilder::publish(Model *model) const +{ + model->operands = mOperands; + model->operations = mOperations; + model->inputIndexes = mInputIndexes; + model->outputIndexes = mOutputIndexes; + model->operandValues = mSmallOperandValues; + + uint32_t count = mMemories.size(); + model->pools.resize(count); + for (uint32_t i = 0; i < count; i++) + { + uint8_t *buffer; + mMemories[i]->getPointer(&buffer); + model->pools[i] = buffer; + } +} + +int ModelBuilder::copyLargeValuesToMemory() +{ + if (!mLargeOperandValues.empty()) + { + // Calculate the size of the shared memory needed for all the large values. + // Also sets the offset for each value within the memory. + size_t poolSize = 0; + for (LargeValue &l : mLargeOperandValues) + { + Operand &operand = mOperands[l.operandIndex]; + ASSERT(operand.lifetime == OperandLifeTime::CONSTANT_REFERENCE); + poolSize += alignBytesNeeded(poolSize, operand.location.length); + operand.location.offset = poolSize; + poolSize += operand.location.length; + } + + // Allocated the shared memory. + int n = mLargeValueMemory.create(poolSize); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint8_t *memoryPointer = nullptr; + n = mLargeValueMemory.getPointer(&memoryPointer); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t poolIndex = mMemories.add(&mLargeValueMemory); + VLOG(MODEL) << "Allocated large value pool of size " << poolSize << " at index " << poolIndex; + + // Copy the values to this memory. + for (LargeValue &l : mLargeOperandValues) + { + Operand &operand = mOperands[l.operandIndex]; + operand.location.poolIndex = poolIndex; + memcpy(memoryPointer + operand.location.offset, l.buffer, operand.location.length); + } + } + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/compiler/ann-ref/src/ModelBuilder.h b/compiler/ann-ref/src/ModelBuilder.h new file mode 100644 index 000000000..ad50fad1d --- /dev/null +++ b/compiler/ann-ref/src/ModelBuilder.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MODEL_BUILDER_H__ +#define __MODEL_BUILDER_H__ + +#include "NeuralNetworks.h" + +#include "Model.h" + +#include "Memory.h" +#include "MemoryTracker.h" + +#include +#include + +class CompilationBuilder; + +class ModelBuilder +{ +public: + virtual ~ModelBuilder() = default; + +public: + // Adds an operand to the model. + int addOperand(const ANeuralNetworksOperandType &type); + +public: + int setOperandValue(uint32_t index, const void *buffer, size_t length); + int setOperandValueFromMemory(uint32_t index, const Memory *memory, uint32_t offset, + size_t length); + +public: + int addOperation(OperationType type, uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs); + +public: + int identifyInputsAndOutputs(uint32_t inputCount, const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs); + +public: + int finish(); + bool isFinished() const { return mCompletedModel; } + +public: + int createCompilation(CompilationBuilder **compilation); + +public: + void publish(Model *model) const; + +public: + uint32_t operandCount() const + { + // We don't allow more than uint32_t worth of operands + return static_cast(mOperands.size()); + } + uint32_t operationCount() const + { + // We don't allow more than uint32_t worth of operations + return static_cast(mOperations.size()); + } + +public: + uint32_t inputCount() const { return static_cast(mInputIndexes.size()); } + uint32_t getInputOperandIndex(uint32_t i) const { return mInputIndexes[i]; } + const Operand &getInputOperand(uint32_t i) const { return mOperands[getInputOperandIndex(i)]; } + +public: + uint32_t outputCount() const { return static_cast(mOutputIndexes.size()); } + uint32_t getOutputOperandIndex(uint32_t i) const { return mOutputIndexes[i]; } + const Operand &getOutputOperand(uint32_t i) const { return mOperands[getOutputOperandIndex(i)]; } + +public: + const Operand &getOperand(uint32_t index) const { return mOperands[index]; } + const Operation &getOperation(uint32_t index) const { return mOperations[index]; } + +public: + const MemoryTracker &getMemories() const { return mMemories; } + const std::vector &getOperations() const { return mOperations; } + +private: + // Return true if either mCompleteModel or mInvalidModel is true. + bool badState(const char *name); + + // Sorts the operations to be in the correct order for single threaded + // node-at-a-time execution. + void sortIntoRunOrder(); + + // Copies the large values to a shared memory, if we have any. + int copyLargeValuesToMemory(); + +private: + // The operations of the graph. + std::vector mOperations; + // The description of the operands of the graph. + std::vector mOperands; + + // Specifies where to find the list of indexes identifying + // the inputs and outputs of the model. The offset is into + // the mOperandIndexes table. + std::vector mInputIndexes; + std::vector mOutputIndexes; + + MemoryTracker mMemories; + + // The value of the small operands that are defined at model + // creation time. + std::vector mSmallOperandValues; + + struct LargeValue + { + uint32_t operandIndex; + const void *buffer; + }; + // Operand index and buffer pointer for all the large operand values of this model. + std::vector mLargeOperandValues; + PrivateMemory mLargeValueMemory; + + // Once the model has been finished, we should not allow further + // modifications to the model. + mutable bool mCompletedModel = false; + + // Any invalid manipulation of the model will mark the model invalid. + // No further modifications are allowed to the model. + mutable bool mInvalidModel = false; +}; + +#endif // __MODEL_BUILDER_H__ diff --git a/compiler/ann-ref/src/NeuralNetworks.cpp b/compiler/ann-ref/src/NeuralNetworks.cpp new file mode 100644 index 000000000..e43a82667 --- /dev/null +++ b/compiler/ann-ref/src/NeuralNetworks.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "NeuralNetworks.h" + +#include "CompilationBuilder.h" +#include "ExecutionBuilder.h" +#include "ModelBuilder.h" +#include "Memory.h" + +#include "Logging.h" + +#include + +int ANeuralNetworksMemory_createFromFd(size_t size, int prot, int fd, size_t offset, + ANeuralNetworksMemory **memory) +{ + *memory = nullptr; + auto m = std::make_unique(); + if (m == nullptr) + { + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + int n = m->set(size, prot, fd, offset); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + *memory = reinterpret_cast(m.release()); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksMemory_free(ANeuralNetworksMemory *memory) +{ + // No validation. Free of nullptr is valid. + Memory *m = reinterpret_cast(memory); + delete m; +} + +int ANeuralNetworksModel_create(ANeuralNetworksModel **model) +{ + if (!model) + { + LOG(ERROR) << "ANeuralNetworksModel_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = new ModelBuilder(); + if (m == nullptr) + { + *model = nullptr; + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + *model = reinterpret_cast(m); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksModel_free(ANeuralNetworksModel *model) +{ + // No validation. Free of nullptr is valid. + ModelBuilder *m = reinterpret_cast(model); + delete m; +} + +int ANeuralNetworksModel_finish(ANeuralNetworksModel *model) +{ + if (!model) + { + LOG(ERROR) << "ANeuralNetworksModel_finish passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->finish(); +} + +int ANeuralNetworksModel_addOperand(ANeuralNetworksModel *model, + const ANeuralNetworksOperandType *type) +{ + if (!model || !type) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperand passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->addOperand(*type); +} + +int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel *model, int32_t index, + const void *buffer, size_t length) +{ + if (!model || !buffer) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->setOperandValue(index, buffer, length); +} + +int ANeuralNetworksModel_setOperandValueFromMemory(ANeuralNetworksModel *model, int32_t index, + const ANeuralNetworksMemory *memory, + size_t offset, size_t length) +{ + if (!model || !memory) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + const Memory *mem = reinterpret_cast(memory); + ModelBuilder *m = reinterpret_cast(model); + return m->setOperandValueFromMemory(index, mem, offset, length); +} + +int ANeuralNetworksModel_addOperation(ANeuralNetworksModel *model, + ANeuralNetworksOperationType type, uint32_t inputCount, + const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs) +{ + if (!model || !inputs || !outputs) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->addOperation(static_cast(type), inputCount, inputs, outputCount, + outputs); +} + +int ANeuralNetworksModel_identifyInputsAndOutputs(ANeuralNetworksModel *model, uint32_t inputCount, + const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs) +{ + if (!model || !inputs || !outputs) + { + LOG(ERROR) << ("ANeuralNetworksModel_identifyInputsAndOutputs passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->identifyInputsAndOutputs(inputCount, inputs, outputCount, outputs); +} + +int ANeuralNetworksCompilation_create(ANeuralNetworksModel *model, + ANeuralNetworksCompilation **compilation) +{ + if (!model || !compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ModelBuilder *m = reinterpret_cast(model); + CompilationBuilder *c = nullptr; + int result = m->createCompilation(&c); + *compilation = reinterpret_cast(c); + return result; +} + +void ANeuralNetworksCompilation_free(ANeuralNetworksCompilation *compilation) +{ + // No validation. Free of nullptr is valid. + // TODO specification says that a compilation-in-flight can be deleted + CompilationBuilder *c = reinterpret_cast(compilation); + delete c; +} + +int ANeuralNetworksCompilation_setPreference(ANeuralNetworksCompilation *compilation, + int32_t preference) +{ + if (!compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_setPreference passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // NOTE Ignore preference + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksCompilation_finish(ANeuralNetworksCompilation *compilation) +{ + if (!compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_finish passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + CompilationBuilder *c = reinterpret_cast(compilation); + return c->finish(); +} + +int ANeuralNetworksExecution_create(ANeuralNetworksCompilation *compilation, + ANeuralNetworksExecution **execution) +{ + if (!compilation || !execution) + { + LOG(ERROR) << "ANeuralNetworksExecution_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + CompilationBuilder *c = reinterpret_cast(compilation); + ExecutionBuilder *r = nullptr; + int result = c->createExecution(&r); + *execution = reinterpret_cast(r); + return result; +} + +void ANeuralNetworksExecution_free(ANeuralNetworksExecution *execution) +{ + // TODO specification says that an execution-in-flight can be deleted + // No validation. Free of nullptr is valid. + ExecutionBuilder *r = reinterpret_cast(execution); + delete r; +} + +int ANeuralNetworksExecution_setInput(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, const void *buffer, + size_t length) +{ + if (!execution) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput passed execution with a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (!buffer && length != 0) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput passed buffer with a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setInput(index, type, buffer, length); +} + +int ANeuralNetworksExecution_setInputFromMemory(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, + const ANeuralNetworksMemory *memory, size_t offset, + size_t length) +{ + if (!execution || !memory) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + const Memory *m = reinterpret_cast(memory); + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setInputFromMemory(index, type, m, offset, length); +} + +int ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, void *buffer, + size_t length) +{ + if (!execution || !buffer) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setOutput(index, type, buffer, length); +} + +int ANeuralNetworksExecution_setOutputFromMemory(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, + const ANeuralNetworksMemory *memory, size_t offset, + size_t length) +{ + if (!execution || !memory) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ExecutionBuilder *r = reinterpret_cast(execution); + const Memory *m = reinterpret_cast(memory); + return r->setOutputFromMemory(index, type, m, offset, length); +} + +int ANeuralNetworksExecution_startCompute(ANeuralNetworksExecution *execution, + ANeuralNetworksEvent **event) +{ + if (!execution || !event) + { + LOG(ERROR) << "ANeuralNetworksExecution_startCompute passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // TODO validate the rest + + ExecutionBuilder *r = reinterpret_cast(execution); + + // Dynamically allocate an sp to wrap an ExecutionCallback, seen in the NN + // API as an abstract event object. The sp object is + // returned when the execution has been successfully launched, otherwise a + // nullptr is returned. The sp is used for ref-counting purposes. Without + // it, the HIDL service could attempt to communicate with a dead callback + // object. + *event = nullptr; + + int n = r->startCompute(); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + *event = reinterpret_cast(new int); + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksEvent_wait(ANeuralNetworksEvent *event) +{ + if (event == nullptr) + { + LOG(ERROR) << "ANeuralNetworksEvent_wait passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksEvent_free(ANeuralNetworksEvent *event) +{ + // No validation. Free of nullptr is valid. + if (event) + { + int *e = reinterpret_cast(event); + delete e; + } +} diff --git a/compiler/ann-ref/src/Operand.h b/compiler/ann-ref/src/Operand.h new file mode 100644 index 000000000..870a05644 --- /dev/null +++ b/compiler/ann-ref/src/Operand.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __OPERAND_H__ +#define __OPERAND_H__ + +#include "OperandType.h" + +#include +#include + +enum class OperandLifeTime : int32_t { + TEMPORARY_VARIABLE = 0, + MODEL_INPUT = 1, + MODEL_OUTPUT = 2, + CONSTANT_COPY = 3, + CONSTANT_REFERENCE = 4, + NO_VALUE = 5, +}; + +struct DataLocation final { + uint32_t poolIndex; + uint32_t offset; + uint32_t length; +}; + +struct Operand final { + OperandType type; + float scale; + int32_t zeroPoint; + + std::vector dimensions; + + DataLocation location; + + uint32_t numberOfConsumers; + OperandLifeTime lifetime; +}; + +// Returns the amount of space needed to store a value of the dimensions and +// type of this operand. +inline uint32_t sizeOfData(const Operand &operand) +{ + return sizeOfData(operand.type, operand.dimensions); +} + +#endif // __OPERAND_H__ diff --git a/compiler/ann-ref/src/OperandType.cpp b/compiler/ann-ref/src/OperandType.cpp new file mode 100644 index 000000000..9f75fcc54 --- /dev/null +++ b/compiler/ann-ref/src/OperandType.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "OperandType.h" +#include "Macro.h" + +const char *kTypeNames[] = { + "FLOAT32", "INT32", "UINT32", "TENSOR_FLOAT32", "TENSOR_INT32", "TENSOR_QUANT8_ASYMM", +}; + +static_assert(COUNT(kTypeNames) == kNumberOfDataTypes, "kTypeNames is incorrect"); + +const uint32_t kSizeOfDataType[]{ + 4, // ANEURALNETWORKS_FLOAT32 + 4, // ANEURALNETWORKS_INT32 + 4, // ANEURALNETWORKS_UINT32 + 4, // ANEURALNETWORKS_TENSOR_FLOAT32 + 4, // ANEURALNETWORKS_TENSOR_INT32 + 1 // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8 +}; + +static_assert(COUNT(kSizeOfDataType) == kNumberOfDataTypes, "kSizeOfDataType is incorrect"); + +const char *getOperandTypeName(OperandType type) +{ + uint32_t n = static_cast(type); + return kTypeNames[n]; +} + +uint32_t sizeOfData(OperandType type, const std::vector &dimensions) +{ + int n = static_cast(type); + + uint32_t size = kSizeOfDataType[n]; + + for (auto d : dimensions) + { + size *= d; + } + return size; +} diff --git a/compiler/ann-ref/src/OperandType.h b/compiler/ann-ref/src/OperandType.h new file mode 100644 index 000000000..3dfd2329b --- /dev/null +++ b/compiler/ann-ref/src/OperandType.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __OPERAND_TYPES_H__ +#define __OPERAND_TYPES_H__ + +#include +#include + +enum class OperandType : int32_t { + FLOAT32 = 0, + INT32 = 1, + UINT32 = 2, + TENSOR_FLOAT32 = 3, + TENSOR_INT32 = 4, + TENSOR_QUANT8_ASYMM = 5, +}; + +// The number of data types (OperandCode) defined in NeuralNetworks.h. +const int kNumberOfDataTypes = 6; + +// Returns the name of the operand type in ASCII. +const char *getOperandTypeName(OperandType type); + +// Returns the amount of space needed to store a value of the specified +// dimensions and type. +uint32_t sizeOfData(OperandType type, const std::vector &dimensions); + +#endif // __OPERAND_TYPES_H__ diff --git a/compiler/ann-ref/src/OperandType.probe.cpp b/compiler/ann-ref/src/OperandType.probe.cpp new file mode 100644 index 000000000..2caffdeeb --- /dev/null +++ b/compiler/ann-ref/src/OperandType.probe.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "OperandType.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(OperandType::FLOAT32) == ANEURALNETWORKS_FLOAT32, + "FLOAT32 != ANEURALNETWORKS_FLOAT32"); +static_assert(static_cast(OperandType::INT32) == ANEURALNETWORKS_INT32, + "INT32 != ANEURALNETWORKS_INT32"); +static_assert(static_cast(OperandType::UINT32) == ANEURALNETWORKS_UINT32, + "UINT32 != ANEURALNETWORKS_UINT32"); + +static_assert(static_cast(OperandType::TENSOR_FLOAT32) == ANEURALNETWORKS_TENSOR_FLOAT32, + "TENSOR_FLOAT32 != ANEURALNETWORKS_TENSOR_FLOAT32"); +static_assert(static_cast(OperandType::TENSOR_QUANT8_ASYMM) == + ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, + "TENSOR_QUANT8_ASYMM != ANEURALNETWORKS_TENSOR_QUANT8_ASYMM"); diff --git a/compiler/ann-ref/src/Operation.h b/compiler/ann-ref/src/Operation.h new file mode 100644 index 000000000..37f6a8727 --- /dev/null +++ b/compiler/ann-ref/src/Operation.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __OPERATION_H__ +#define __OPERATION_H__ + +#include "OperationType.h" + +#include +#include + +struct Operation final { + OperationType type; + std::vector inputs; + std::vector outputs; +}; + +#endif // __OPERATION_H__ diff --git a/compiler/ann-ref/src/OperationType.cpp b/compiler/ann-ref/src/OperationType.cpp new file mode 100644 index 000000000..f938b4d1c --- /dev/null +++ b/compiler/ann-ref/src/OperationType.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "OperationType.h" +#include "Macro.h" + +const char *kOperationNames[kNumberOfOperationTypes] = { + "ADD", + "AVERAGE_POOL", + "CONCATENATION", + "CONV", + "DEPTHWISE_CONV", + "DEPTH_TO_SPACE", + "DEQUANTIZE", + "EMBEDDING_LOOKUP", + "FLOOR", + "FULLY_CONNECTED", + "HASHTABLE_LOOKUP", + "L2_NORMALIZATION", + "L2_POOL", + "LOCAL_RESPONSE_NORMALIZATION", + "LOGISTIC", + "LSH_PROJECTION", + "LSTM", + "MAX_POOL", + "MUL", + "RELU", + "RELU1", + "RELU6", + "RESHAPE", + "RESIZE_BILINEAR", + "RNN", + "SOFTMAX", + "SPACE_TO_DEPTH", + "SVDF", + "TANH", + "BATCH_TO_SPACE_ND", // V1_1, will not be merged till V1_1 is finalized + "DIV", + "MEAN", // V1_1, will not be merged till V1_1 is finalized + "PAD", // V1_1, will not be merged till V1_1 is finalized + "SPACE_TO_BATCH_ND", // V1_1, will not be merged till V1_1 is finalized + "SQUEEZE", // V1_1, will not be merged till V1_1 is finalized + "STRIDED_SLICE", + "SUB", +}; + +static_assert(COUNT(kOperationNames) == kNumberOfOperationTypes, "kOperationNames is incorrect"); + +const char *getOperationName(OperationType type) +{ + uint32_t n = static_cast(type); + return kOperationNames[n]; +} diff --git a/compiler/ann-ref/src/OperationType.h b/compiler/ann-ref/src/OperationType.h new file mode 100644 index 000000000..fc66eeeab --- /dev/null +++ b/compiler/ann-ref/src/OperationType.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __OPERATION_TYPE_H__ +#define __OPERATION_TYPE_H__ + +#include + +enum class OperationType : int32_t { + ADD = 0, + AVERAGE_POOL_2D = 1, + CONCATENATION = 2, + CONV_2D = 3, + DEPTHWISE_CONV_2D = 4, + DEPTH_TO_SPACE = 5, + DEQUANTIZE = 6, + EMBEDDING_LOOKUP = 7, + FLOOR = 8, + FULLY_CONNECTED = 9, + HASHTABLE_LOOKUP = 10, + L2_NORMALIZATION = 11, + L2_POOL_2D = 12, + LOCAL_RESPONSE_NORMALIZATION = 13, + LOGISTIC = 14, + LSH_PROJECTION = 15, + LSTM = 16, + MAX_POOL_2D = 17, + MUL = 18, + RELU = 19, + RELU1 = 20, + RELU6 = 21, + RESHAPE = 22, + RESIZE_BILINEAR = 23, + RNN = 24, + SOFTMAX = 25, + SPACE_TO_DEPTH = 26, + SVDF = 27, + TANH = 28, + DIV = 30, + PAD = 32, + STRIDED_SLICE = 35, + SUB = 36, + OEM_OPERATION = 10000, +}; + +// The number of operation types (OperationCode) defined in NeuralNetworks.h. +const int kNumberOfOperationTypes = 37; + +// Returns the name of the operation in ASCII. +const char *getOperationName(OperationType opCode); + +#endif // __OPERATION_TYPE_H__ diff --git a/compiler/ann-ref/src/OperationType.probe.cpp b/compiler/ann-ref/src/OperationType.probe.cpp new file mode 100644 index 000000000..c9886f35b --- /dev/null +++ b/compiler/ann-ref/src/OperationType.probe.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "OperationType.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(OperationType::ADD) == ANEURALNETWORKS_ADD, + "OperationType::ADD != ANEURALNETWORKS_ADD"); +static_assert(static_cast(OperationType::AVERAGE_POOL_2D) == + ANEURALNETWORKS_AVERAGE_POOL_2D, + "OperationType::AVERAGE_POOL_2D != ANEURALNETWORKS_AVERAGE_POOL_2D"); +static_assert(static_cast(OperationType::CONV_2D) == ANEURALNETWORKS_CONV_2D, + "OperationType::CONV_2D != ANEURALNETWORKS_CONV_2D"); +static_assert(static_cast(OperationType::DEPTHWISE_CONV_2D) == + ANEURALNETWORKS_DEPTHWISE_CONV_2D, + "OperationType::DEPTHWISE_CONV_2D != ANEURALNETWORKS_DEPTHWISE_CONV_2D"); +static_assert(static_cast(OperationType::DEPTH_TO_SPACE) == ANEURALNETWORKS_DEPTH_TO_SPACE, + "OperationType::DEPTH_TO_SPACE != ANEURALNETWORKS_DEPTH_TO_SPACE"); +static_assert(static_cast(OperationType::DEQUANTIZE) == ANEURALNETWORKS_DEQUANTIZE, + "OperationType::DEQUANTIZE != ANEURALNETWORKS_DEQUANTIZE"); +static_assert(static_cast(OperationType::EMBEDDING_LOOKUP) == + ANEURALNETWORKS_EMBEDDING_LOOKUP, + "OperationType::EMBEDDING_LOOKUP != ANEURALNETWORKS_EMBEDDING_LOOKUP"); +static_assert(static_cast(OperationType::FLOOR) == ANEURALNETWORKS_FLOOR, + "OperationType::FLOOR != ANEURALNETWORKS_FLOOR"); +static_assert(static_cast(OperationType::FULLY_CONNECTED) == + ANEURALNETWORKS_FULLY_CONNECTED, + "OperationType::FULLY_CONNECTED != ANEURALNETWORKS_FULLY_CONNECTED"); +static_assert(static_cast(OperationType::HASHTABLE_LOOKUP) == + ANEURALNETWORKS_HASHTABLE_LOOKUP, + "OperationType::HASHTABLE_LOOKUP != ANEURALNETWORKS_HASHTABLE_LOOKUP"); +static_assert(static_cast(OperationType::L2_NORMALIZATION) == + ANEURALNETWORKS_L2_NORMALIZATION, + "OperationType::L2_NORMALIZATION != ANEURALNETWORKS_L2_NORMALIZATION"); +static_assert(static_cast(OperationType::L2_POOL_2D) == ANEURALNETWORKS_L2_POOL_2D, + "OperationType::L2_POOL_2D != ANEURALNETWORKS_L2_POOL_2D"); +static_assert(static_cast(OperationType::LOCAL_RESPONSE_NORMALIZATION) == + ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION, + "OperationType::LOCAL_RESPONSE_NORMALIZATION != " + "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION"); +static_assert(static_cast(OperationType::LOGISTIC) == ANEURALNETWORKS_LOGISTIC, + "OperationType::LOGISTIC != ANEURALNETWORKS_LOGISTIC"); +static_assert(static_cast(OperationType::LSH_PROJECTION) == ANEURALNETWORKS_LSH_PROJECTION, + "OperationType::LSH_PROJECTION != ANEURALNETWORKS_LSH_PROJECTION"); +static_assert(static_cast(OperationType::LSTM) == ANEURALNETWORKS_LSTM, + "OperationType::LSTM != ANEURALNETWORKS_LSTM"); +static_assert(static_cast(OperationType::MAX_POOL_2D) == ANEURALNETWORKS_MAX_POOL_2D, + "OperationType::MAX_POOL_2D != ANEURALNETWORKS_MAX_POOL_2D"); +static_assert(static_cast(OperationType::MUL) == ANEURALNETWORKS_MUL, + "OperationType::MUL != ANEURALNETWORKS_MUL"); +static_assert(static_cast(OperationType::RELU) == ANEURALNETWORKS_RELU, + "OperationType::RELU != ANEURALNETWORKS_RELU"); +static_assert(static_cast(OperationType::RELU1) == ANEURALNETWORKS_RELU1, + "OperationType::RELU1 != ANEURALNETWORKS_RELU1"); +static_assert(static_cast(OperationType::RELU6) == ANEURALNETWORKS_RELU6, + "OperationType::RELU6 != ANEURALNETWORKS_RELU6"); +static_assert(static_cast(OperationType::RESHAPE) == ANEURALNETWORKS_RESHAPE, + "OperationType::RESHAPE != ANEURALNETWORKS_RESHAPE"); +static_assert(static_cast(OperationType::RESIZE_BILINEAR) == + ANEURALNETWORKS_RESIZE_BILINEAR, + "OperationType::RESIZE_BILINEAR != ANEURALNETWORKS_RESIZE_BILINEAR"); +static_assert(static_cast(OperationType::RNN) == ANEURALNETWORKS_RNN, + "OperationType::RNN != ANEURALNETWORKS_RNN"); +static_assert(static_cast(OperationType::SOFTMAX) == ANEURALNETWORKS_SOFTMAX, + "OperationType::SOFTMAX != ANEURALNETWORKS_SOFTMAX"); +static_assert(static_cast(OperationType::SPACE_TO_DEPTH) == ANEURALNETWORKS_SPACE_TO_DEPTH, + "OperationType::SPACE_TO_DEPTH != ANEURALNETWORKS_SPACE_TO_DEPTH"); +static_assert(static_cast(OperationType::SVDF) == ANEURALNETWORKS_SVDF, + "OperationType::SVDF != ANEURALNETWORKS_SVDF"); +static_assert(static_cast(OperationType::TANH) == ANEURALNETWORKS_TANH, + "OperationType::TANH != ANEURALNETWORKS_TANH"); diff --git a/compiler/ann-ref/src/Probe.cpp b/compiler/ann-ref/src/Probe.cpp new file mode 100644 index 000000000..3a085a19d --- /dev/null +++ b/compiler/ann-ref/src/Probe.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "NeuralNetworks.h" + +// Make sure the constants defined in the header files have not changed values. +// IMPORTANT: When adding new values, update kNumberOfDataTypes or kNumberOfDataTypesOEM +// in Utils.h. +static_assert(ANEURALNETWORKS_FLOAT32 == 0, "ANEURALNETWORKS_FLOAT32 has changed"); +static_assert(ANEURALNETWORKS_INT32 == 1, "ANEURALNETWORKS_INT32 has changed"); +static_assert(ANEURALNETWORKS_UINT32 == 2, "ANEURALNETWORKS_UINT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_FLOAT32 == 3, "ANEURALNETWORKS_TENSOR_FLOAT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_INT32 == 4, "ANEURALNETWORKS_TENSOR_INT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM == 5, + "ANEURALNETWORKS_TENSOR_QUANT8_ASYMM has changed"); + +// IMPORTANT: When adding new values, update kNumberOfOperationTypes or +// kNumberOfOperationTypesOEM kNumberOfOperationTypesEx in Utils.h. +static_assert(ANEURALNETWORKS_ADD == 0, "ANEURALNETWORKS_ADD has changed"); +static_assert(ANEURALNETWORKS_AVERAGE_POOL_2D == 1, "ANEURALNETWORKS_AVERAGE_POOL_2D has changed"); +static_assert(ANEURALNETWORKS_CONCATENATION == 2, "ANEURALNETWORKS_CONCATENATION has changed"); +static_assert(ANEURALNETWORKS_CONV_2D == 3, "ANEURALNETWORKS_CONV_2D has changed"); +static_assert(ANEURALNETWORKS_DEPTHWISE_CONV_2D == 4, + "ANEURALNETWORKS_DEPTHWISE_CONV_2D has changed"); +static_assert(ANEURALNETWORKS_DEPTH_TO_SPACE == 5, "ANEURALNETWORKS_DEPTH_TO_SPACE has changed"); +static_assert(ANEURALNETWORKS_DEQUANTIZE == 6, "ANEURALNETWORKS_DEQUANTIZE has changed"); +static_assert(ANEURALNETWORKS_EMBEDDING_LOOKUP == 7, + "ANEURALNETWORKS_EMBEDDING_LOOKUP has changed"); +static_assert(ANEURALNETWORKS_FLOOR == 8, "ANEURALNETWORKS_FLOOR has changed"); +static_assert(ANEURALNETWORKS_FULLY_CONNECTED == 9, "ANEURALNETWORKS_FULLY_CONNECTED has changed"); +static_assert(ANEURALNETWORKS_HASHTABLE_LOOKUP == 10, + "ANEURALNETWORKS_HASHTABLE_LOOKUP has changed"); +static_assert(ANEURALNETWORKS_L2_NORMALIZATION == 11, + "ANEURALNETWORKS_L2_NORMALIZATION has changed"); +static_assert(ANEURALNETWORKS_L2_POOL_2D == 12, "ANEURALNETWORKS_L2_POOL has changed"); +static_assert(ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION == 13, + "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION has changed"); +static_assert(ANEURALNETWORKS_LOGISTIC == 14, "ANEURALNETWORKS_LOGISTIC has changed"); +static_assert(ANEURALNETWORKS_LSH_PROJECTION == 15, "ANEURALNETWORKS_LSH_PROJECTION has changed"); +static_assert(ANEURALNETWORKS_LSTM == 16, "ANEURALNETWORKS_LSTM has changed"); +static_assert(ANEURALNETWORKS_MAX_POOL_2D == 17, "ANEURALNETWORKS_MAX_POOL has changed"); +static_assert(ANEURALNETWORKS_MUL == 18, "ANEURALNETWORKS_MUL has changed"); +static_assert(ANEURALNETWORKS_RELU == 19, "ANEURALNETWORKS_RELU has changed"); +static_assert(ANEURALNETWORKS_RELU1 == 20, "ANEURALNETWORKS_RELU1 has changed"); +static_assert(ANEURALNETWORKS_RELU6 == 21, "ANEURALNETWORKS_RELU6 has changed"); +static_assert(ANEURALNETWORKS_RESHAPE == 22, "ANEURALNETWORKS_RESHAPE has changed"); +static_assert(ANEURALNETWORKS_RESIZE_BILINEAR == 23, "ANEURALNETWORKS_RESIZE_BILINEAR has changed"); +static_assert(ANEURALNETWORKS_RNN == 24, "ANEURALNETWORKS_RNN has changed"); +static_assert(ANEURALNETWORKS_SOFTMAX == 25, "ANEURALNETWORKS_SOFTMAX has changed"); +static_assert(ANEURALNETWORKS_SPACE_TO_DEPTH == 26, "ANEURALNETWORKS_SPACE_TO_DEPTH has changed"); +static_assert(ANEURALNETWORKS_SVDF == 27, "ANEURALNETWORKS_SVDF has changed"); +static_assert(ANEURALNETWORKS_TANH == 28, "ANEURALNETWORKS_TANH has changed"); + +static_assert(ANEURALNETWORKS_FUSED_NONE == 0, "ANEURALNETWORKS_FUSED_NONE has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU == 1, "ANEURALNETWORKS_FUSED_RELU has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU1 == 2, "ANEURALNETWORKS_FUSED_RELU1 has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU6 == 3, "ANEURALNETWORKS_FUSED_RELU6 has changed"); + +static_assert(ANEURALNETWORKS_PREFER_LOW_POWER == 0, + "ANEURALNETWORKS_PREFER_LOW_POWER has changed"); +static_assert(ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER == 1, + "ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER has changed"); +static_assert(ANEURALNETWORKS_PREFER_SUSTAINED_SPEED == 2, + "ANEURALNETWORKS_PREFER_SUSTAINED_SPEED has changed"); + +static_assert(ANEURALNETWORKS_NO_ERROR == 0, "ANEURALNETWORKS_NO_ERROR has changed"); +static_assert(ANEURALNETWORKS_OUT_OF_MEMORY == 1, "ANEURALNETWORKS_OUT_OF_MEMORY has changed"); +static_assert(ANEURALNETWORKS_INCOMPLETE == 2, "ANEURALNETWORKS_INCOMPLETE has changed"); +static_assert(ANEURALNETWORKS_UNEXPECTED_NULL == 3, "ANEURALNETWORKS_UNEXPECTED_NULL has changed"); +static_assert(ANEURALNETWORKS_BAD_DATA == 4, "ANEURALNETWORKS_BAD_DATA has changed"); +static_assert(ANEURALNETWORKS_OP_FAILED == 5, "ANEURALNETWORKS_OP_FAILED has changed"); +static_assert(ANEURALNETWORKS_BAD_STATE == 6, "ANEURALNETWORKS_BAD_STATE has changed"); + +static_assert(ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES == 128, + "ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES has changed"); diff --git a/compiler/ann-ref/src/Request.h b/compiler/ann-ref/src/Request.h new file mode 100644 index 000000000..49f74fdf5 --- /dev/null +++ b/compiler/ann-ref/src/Request.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __REQUEST_H__ +#define __REQUEST_H__ + +#include +#include + +struct RequestArgument final { + bool hasNoValue; + DataLocation location; + std::vector dimensions; +}; + +struct Request final { + std::vector inputs; + std::vector outputs; +}; + +#endif // __REQUEST_H__ diff --git a/compiler/ann-ref/src/Shape.cpp b/compiler/ann-ref/src/Shape.cpp new file mode 100644 index 000000000..37a54c213 --- /dev/null +++ b/compiler/ann-ref/src/Shape.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Shape.h" + +#include // For 'size_t' + +bool SameShape(const Shape &in1, const Shape &in2) +{ + if (in1.type != in2.type || in1.dimensions.size() != in2.dimensions.size()) + { + return false; + } + for (size_t i = 0; i < in1.dimensions.size(); i++) + { + if (in1.dimensions[i] != in2.dimensions[i]) + { + return false; + } + } + return true; +} + +bool SetShape(const Shape &in, Shape *out) +{ + if (in.type != out->type || in.dimensions.size() != out->dimensions.size()) + { + return false; + } + out->dimensions = in.dimensions; + return true; +} + +uint32_t getNumberOfElements(const Shape &shape) +{ + uint32_t count = 1; + for (size_t i = 0; i < shape.dimensions.size(); i++) + { + count *= shape.dimensions[i]; + } + return count; +} + +uint32_t getNumberOfDimensions(const Shape &shape) { return shape.dimensions.size(); } + +uint32_t getSizeOfDimension(const Shape &shape, uint32_t dimensionIdx) +{ + if (dimensionIdx >= shape.dimensions.size()) + { + // TODO, log the error + return 0; + } + return shape.dimensions[dimensionIdx]; +} diff --git a/compiler/ann-ref/src/Shape.h b/compiler/ann-ref/src/Shape.h new file mode 100644 index 000000000..2e3d92e50 --- /dev/null +++ b/compiler/ann-ref/src/Shape.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __SHAPE_H__ +#define __SHAPE_H__ + +#include "OperandType.h" + +#include +#include + +// The type and dimensions of an operand. +struct Shape +{ + OperandType type; + std::vector dimensions; + float scale; + int32_t offset; +}; + +// Verifies that the two shapes are the same. +bool SameShape(const Shape &in1, const Shape &in2); + +// Sets out to the same shape as in. +bool SetShape(const Shape &in, Shape *out); + +// Return the total number of elements, i.e. all the dimensions multiplied +// together. For a scalar, returns one. +uint32_t getNumberOfElements(const Shape &shape); +uint32_t getNumberOfDimensions(const Shape &shape); +uint32_t getSizeOfDimension(const Shape &shape, uint32_t dimensionIdx); + +#endif // __SHAPE_H__ diff --git a/compiler/ann-ref/src/Validation.cpp b/compiler/ann-ref/src/Validation.cpp new file mode 100644 index 000000000..679b14a9a --- /dev/null +++ b/compiler/ann-ref/src/Validation.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Validation.h" +#include "Macro.h" +#include "Assert.h" + +static inline bool validCode(uint32_t codeCount, uint32_t code) +{ + return (code < codeCount); +} + +int validateOperationType(const OperationType &type) +{ + return validCode(kNumberOfOperationTypes, static_cast(type)); +} + +// Validates the type. The used dimensions can be underspecified. +int validateOperandType(const ANeuralNetworksOperandType &type, const char *tag, bool allowPartial) +{ + if (!allowPartial) + { + for (uint32_t i = 0; i < type.dimensionCount; i++) + { + if (type.dimensions[i] == 0) + { + LOG(ERROR) << tag << " OperandType invalid dimensions[" << i + << "] = " << type.dimensions[i]; + return ANEURALNETWORKS_BAD_DATA; + } + } + } + if (!validCode(kNumberOfDataTypes, type.type)) + { + LOG(ERROR) << tag << " OperandType invalid type " << type.type; + return ANEURALNETWORKS_BAD_DATA; + } + if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) + { + if (type.zeroPoint < 0 || type.zeroPoint > 255) + { + LOG(ERROR) << tag << " OperandType invalid zeroPoint " << type.zeroPoint; + return ANEURALNETWORKS_BAD_DATA; + } + if (type.scale < 0.f) + { + LOG(ERROR) << tag << " OperandType invalid scale " << type.scale; + return ANEURALNETWORKS_BAD_DATA; + } + } + + // TODO-NNRT : add 'type.type == ANEURALNETWORKS_OEM_SCALAR' later. + // OEM operaters are not supported now. + if (type.type == ANEURALNETWORKS_FLOAT32 || type.type == ANEURALNETWORKS_INT32 || + type.type == ANEURALNETWORKS_UINT32) + { + if (type.dimensionCount != 0 || type.dimensions != nullptr) + { + LOG(ERROR) << tag << " Invalid dimensions for scalar type"; + return ANEURALNETWORKS_BAD_DATA; + } + } + + return ANEURALNETWORKS_NO_ERROR; +} + +int validateOperandList(uint32_t count, const uint32_t *list, uint32_t operandCount, + const char *tag) +{ + for (uint32_t i = 0; i < count; i++) + { + if (list[i] >= operandCount) + { + LOG(ERROR) << tag << " invalid operand index at " << i << " = " << list[i] + << ", operandCount " << operandCount; + return ANEURALNETWORKS_BAD_DATA; + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +static bool validOperandIndexes(const std::vector indexes, size_t operandCount) +{ + for (uint32_t i : indexes) + { + if (i >= operandCount) + { + LOG(ERROR) << "Index out of range " << i << "/" << operandCount; + return false; + } + } + return true; +} + +static bool validOperands(const std::vector &operands, const std::vector &operandValues) +{ + for (auto &operand : operands) + { + if (!validCode(kNumberOfDataTypes, static_cast(operand.type))) + { + LOG(ERROR) << "Invalid operand type "; + return false; + } + /* TODO validate dim with type + if (!validOperandIndexes(operand.dimensions, mDimensions)) { + return false; + } + */ + switch (operand.lifetime) + { + case OperandLifeTime::CONSTANT_COPY: + if (operand.location.offset + operand.location.length > operandValues.size()) + { + LOG(ERROR) << "OperandValue location out of range. Starts at " << operand.location.offset + << ", length " << operand.location.length << ", max " << operandValues.size(); + return false; + } + break; + case OperandLifeTime::TEMPORARY_VARIABLE: + case OperandLifeTime::MODEL_INPUT: + case OperandLifeTime::MODEL_OUTPUT: + case OperandLifeTime::NO_VALUE: + if (operand.location.offset != 0 || operand.location.length != 0) + { + LOG(ERROR) << "Unexpected offset " << operand.location.offset << " or length " + << operand.location.length << " for runtime location."; + return false; + } + break; + case OperandLifeTime::CONSTANT_REFERENCE: +#if 0 + if (operand.location.poolIndex >= poolCount) + { + LOG(ERROR) << "Invalid poolIndex " << operand.location.poolIndex << "/" << poolCount; + return false; + } +#endif + break; + // TODO: Validate that we are within the pool. + default: + LOG(ERROR) << "Invalid lifetime"; + return false; + } + } + return true; +} + +static bool validOperations(const std::vector &operations, size_t operandCount) +{ + for (auto &op : operations) + { + if (!validCode(kNumberOfOperationTypes, static_cast(op.type))) + { + LOG(ERROR) << "Invalid operation type "; + return false; + } + if (!validOperandIndexes(op.inputs, operandCount) || + !validOperandIndexes(op.outputs, operandCount)) + { + return false; + } + } + return true; +} + +// TODO doublecheck +bool validateModel(const Model &model) +{ + const size_t operandCount = model.operands.size(); + return (validOperands(model.operands, model.operandValues) && + validOperations(model.operations, operandCount) && + validOperandIndexes(model.inputIndexes, operandCount) && + validOperandIndexes(model.outputIndexes, operandCount)); +} + +bool validRequestArguments(const std::vector &arguments, + const std::vector &operandIndexes, + const std::vector &operands, size_t poolCount, const char *type) +{ + const size_t argumentCount = arguments.size(); + if (argumentCount != operandIndexes.size()) + { + LOG(ERROR) << "Request specifies " << argumentCount << " " << type << "s but the model has " + << operandIndexes.size(); + return false; + } + for (size_t argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + const RequestArgument &argument = arguments[argumentIndex]; + const uint32_t operandIndex = operandIndexes[argumentIndex]; + const Operand &operand = operands[operandIndex]; + if (argument.hasNoValue) + { + if (argument.location.poolIndex != 0 || argument.location.offset != 0 || + argument.location.length != 0 || argument.dimensions.size() != 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex + << " has no value yet has details."; + return false; + } + } + if (argument.location.poolIndex >= poolCount) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has an invalid poolIndex " + << argument.location.poolIndex << "/" << poolCount; + return false; + } + // TODO: Validate that we are within the pool. + uint32_t rank = argument.dimensions.size(); + if (rank > 0) + { + if (rank != operand.dimensions.size()) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has number of dimensions (" + << rank << ") different than the model's (" << operand.dimensions.size() << ")"; + return false; + } + for (size_t i = 0; i < rank; i++) + { + if (argument.dimensions[i] != operand.dimensions[i] && operand.dimensions[i] != 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has dimension " << i + << " of " << operand.dimensions[i] << " different than the model's " + << operand.dimensions[i]; + return false; + } + if (argument.dimensions[i] == 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has dimension " << i + << " of zero"; + return false; + } + } + } + } + return true; +} + +// TODO doublecheck +bool validateRequest(const Request &request, const Model &model) +{ + //const size_t poolCount = request.pools.size(); + const size_t poolCount = 0; + return (validRequestArguments(request.inputs, model.inputIndexes, model.operands, poolCount, + "input") && + validRequestArguments(request.outputs, model.outputIndexes, model.operands, poolCount, + "output")); +} + diff --git a/compiler/ann-ref/src/Validation.h b/compiler/ann-ref/src/Validation.h new file mode 100644 index 000000000..dab426af4 --- /dev/null +++ b/compiler/ann-ref/src/Validation.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __VALIDATION_H__ +#define __VALIDATION_H__ + +#include "OperationType.h" +#include "Model.h" +#include "Request.h" +#include "NeuralNetworks.h" + +int validateOperationType(const OperationType &); +int validateOperandType(const ANeuralNetworksOperandType &type, const char *tag, bool allowPartial); +int validateOperandList(uint32_t count, const uint32_t *list, uint32_t operandCount, + const char *tag); + +bool validateModel(const Model &model); +bool validateRequest(const Request &request, const Model &model); + +#endif // __VALIDATION_H__ diff --git a/compiler/ann-ref/src/ops/Add.cpp b/compiler/ann-ref/src/ops/Add.cpp new file mode 100644 index 000000000..0b826f05d --- /dev/null +++ b/compiler/ann-ref/src/ops/Add.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Add.h" +#include "Assert.h" + +bool addPrepare(const Shape &in1, const Shape &in2, Shape *out) +{ + ASSERT(getNumberOfDimensions(in1) <= 4 && getNumberOfDimensions(in2) <= 4); + ASSERT(in1.type == in2.type); + if (SameShape(in1, in2)) + { + return SetShape(in1, out); + } + else + { + // BroadcastAdd needed + uint32_t numberOfDims1 = getNumberOfDimensions(in1); + uint32_t numberOfDims2 = getNumberOfDimensions(in2); + uint32_t maxDims = std::max(numberOfDims1, numberOfDims2); + out->dimensions = std::vector(maxDims); + for (uint32_t i = 1; i <= maxDims; i++) + { + uint32_t dim1 = 1; + if (i <= numberOfDims1) + { + dim1 = getSizeOfDimension(in1, numberOfDims1 - i); + } + uint32_t dim2 = 1; + if (i <= numberOfDims2) + { + dim2 = getSizeOfDimension(in2, numberOfDims2 - i); + } + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) + { + LOG(ERROR) << "Dimensions mismatch for BroadcastAdd"; + return false; + } + out->dimensions[maxDims - i] = std::max(dim1, dim2); + } + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Add.float.cpp b/compiler/ann-ref/src/ops/Add.float.cpp new file mode 100644 index 000000000..ce825d43d --- /dev/null +++ b/compiler/ann-ref/src/ops/Add.float.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Add.float.h" + +#include "internal/Array.h" +#include "internal/NDArray.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void Add(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + MatchingArraySize(input1_dims, 3, input2_dims, 3, output_dims, 3); + MatchingArraySize(input1_dims, 2, input2_dims, 2, output_dims, 2); + MatchingArraySize(input1_dims, 1, input2_dims, 1, output_dims, 1); + MatchingArraySize(input1_dims, 0, input2_dims, 0, output_dims, 0); + DCHECK(IsPackedWithoutStrides(input1_dims)); + DCHECK(IsPackedWithoutStrides(input2_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + + int i = 0; + const int size = input1_dims.sizes[3] * input1_dims.strides[3]; + + for (; i < size; i++) + { + auto x = input1_data[i] + input2_data[i]; + output_data[i] = ActivationFunction(x); + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// TODO: We can implement BroadcastAdd on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +// TODO: BroadcastAdd is intentionally duplicated from +// reference_ops.h. Once an optimized version is implemented and NdArrayDesc +// is no longer referenced in this file, move NdArrayDesc from types.h to +// reference_ops.h. +template +void BroadcastAdd(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_dims, input2_dims, &desc1, &desc2); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < ArraySize(output_dims, 3); ++b) + { + for (int y = 0; y < ArraySize(output_dims, 2); ++y) + { + for (int x = 0; x < ArraySize(output_dims, 1); ++x) + { + for (int c = 0; c < ArraySize(output_dims, 0); ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(input1_data[SubscriptToIndex(desc1, c, x, y, b)] + + input2_data[SubscriptToIndex(desc2, c, x, y, b)]); + } + } + } + } +} + +bool addFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut) +{ + bool needBroadcast = !SameShape(shape1, shape2); + +#define ANDROID_NN_NORMAL_ADD(activation) \ + Add(in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + +#define ANDROID_NN_BROADCAST_ADD(activation) \ + BroadcastAdd( \ + in1, convertShapeToDims(shape1), in2, convertShapeToDims(shape2), out, \ + convertShapeToDims(shapeOut)) + + if (needBroadcast) + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_BROADCAST_ADD) + } + else + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_NORMAL_ADD) + } + +#undef ANDROID_NN_NORMAL_ADD +#undef ANDROID_NN_BROADCAST_ADD + return true; +} diff --git a/compiler/ann-ref/src/ops/Add.float.h b/compiler/ann-ref/src/ops/Add.float.h new file mode 100644 index 000000000..3657a045d --- /dev/null +++ b/compiler/ann-ref/src/ops/Add.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_ADD_FLOAT_H__ +#define __OP_ADD_FLOAT_H__ + +#include "Shape.h" + +#include + +bool addFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut); + +#endif // __OP_ADD_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Add.h b/compiler/ann-ref/src/ops/Add.h new file mode 100644 index 000000000..c6751fc00 --- /dev/null +++ b/compiler/ann-ref/src/ops/Add.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_ADD_H__ +#define __OP_ADD_H__ + +#include "Shape.h" + +bool addPrepare(const Shape &in1, const Shape &in2, Shape *out1); + +#endif // __OP_ADD_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/AvgPool2D.cpp b/compiler/ann-ref/src/ops/AvgPool2D.cpp new file mode 100644 index 000000000..cd9fcff66 --- /dev/null +++ b/compiler/ann-ref/src/ops/AvgPool2D.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AvgPool2D.h" + +#include "internal/Pooling.h" + +bool averagePoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + return genericPoolingPrepare(input, padding_left, padding_right, padding_top, padding_bottom, + stride_width, stride_height, filter_width, filter_height, + output); +} diff --git a/compiler/ann-ref/src/ops/AvgPool2D.float.cpp b/compiler/ann-ref/src/ops/AvgPool2D.float.cpp new file mode 100644 index 000000000..21d3e977c --- /dev/null +++ b/compiler/ann-ref/src/ops/AvgPool2D.float.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AvgPool2D.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/FeatureMap.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +// From optimized_ops.h in TensorFlow Lite +template +void AveragePool(const float *input_data, const Dims<4> &input_dims, int stride_width, + int stride_height, int pad_width, int pad_height, int kwidth, int kheight, + float *output_data, const Dims<4> &output_dims) +{ + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); + const int depth = MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // TODO: get rid of the dynamic memory allocation here! + Eigen::VectorXf out_count(out_mat.cols()); + out_count.setZero(); + // Prefill the output to 0. + out_mat.setZero(); + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < input_height; ++h) + { + for (int w = 0; w < input_width; ++w) + { + // (h_start, h_end) * (w_start, w_end) is the range that the input + // vector projects to. + int hpad = h + pad_height; + int wpad = w + pad_width; + int h_start = (hpad < kheight) ? 0 : (hpad - kheight) / stride_height + 1; + int h_end = std::min(hpad / stride_height + 1, output_height); + int w_start = (wpad < kwidth) ? 0 : (wpad - kwidth) / stride_width + 1; + int w_end = std::min(wpad / stride_width + 1, output_width); + // compute elementwise sum + for (int ph = h_start; ph < h_end; ++ph) + { + for (int pw = w_start; pw < w_end; ++pw) + { + int out_offset = NodeOffset(b, ph, pw, output_height, output_width); + out_mat.col(out_offset) += in_mat.col(NodeOffset(b, h, w, input_height, input_width)); + out_count(out_offset)++; + } + } + } + } + } + // Divide the output by the actual number of elements being averaged over + DCHECK_GT(out_count.minCoeff(), 0); + out_mat.array().rowwise() /= out_count.transpose().array(); + + for (int b = 0; b < batches; ++b) + { + for (int y = 0; y < output_height; ++y) + { + for (int x = 0; x < output_width; ++x) + { + for (int c = 0; c < depth; ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(output_data[Offset(output_dims, c, x, y, b)]); + } + } + } + } +} + +#define ANDROID_NN_POOLING_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool averagePoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_POOLING_PARAMETERS + +#define ANDROID_NN_AVERAGE_POOL(activation) \ + AveragePool( \ + inputData, convertShapeToDims(inputShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, filter_width, filter_height, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_AVERAGE_POOL) +#undef ANDROID_NN_AVERAGE_POOL + + return true; +} + +#undef ANDROID_NN_POOLING_PARAMETERS diff --git a/compiler/ann-ref/src/ops/AvgPool2D.float.h b/compiler/ann-ref/src/ops/AvgPool2D.float.h new file mode 100644 index 000000000..b980e004b --- /dev/null +++ b/compiler/ann-ref/src/ops/AvgPool2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_AVG_POOL_2D_FLOAT_H__ +#define __OP_AVG_POOL_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool averagePoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_AVG_POOL_2D_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/AvgPool2D.h b/compiler/ann-ref/src/ops/AvgPool2D.h new file mode 100644 index 000000000..c86385531 --- /dev/null +++ b/compiler/ann-ref/src/ops/AvgPool2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_AVG_POOL_2D_H__ +#define __OP_AVG_POOL_2D_H__ + +#include "Shape.h" + +#include + +bool averagePoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + +#endif // __OP_AVG_POOL_2D_H__ diff --git a/compiler/ann-ref/src/ops/Concatenation.cpp b/compiler/ann-ref/src/ops/Concatenation.cpp new file mode 100644 index 000000000..6bfe640b5 --- /dev/null +++ b/compiler/ann-ref/src/ops/Concatenation.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Concatenation.h" +#include "Assert.h" + +bool concatenationPrepare(const std::vector &inputShapes, int32_t axis, Shape *output) +{ + + int num_inputs = inputShapes.size(); + OperandType input_type = inputShapes[0].type; + uint32_t num_dimensions = getNumberOfDimensions(inputShapes[0]); + + ASSERT(axis >= 0); + ASSERT(axis < (int32_t)num_dimensions); + + int sum_axis = getSizeOfDimension(inputShapes[0], axis); + for (int i = 1; i < num_inputs; ++i) + { + ASSERT(getNumberOfDimensions(inputShapes[i]) == num_dimensions); + ASSERT(inputShapes[i].type == inputShapes[0].type); + if (input_type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(inputShapes[0].offset == inputShapes[i].offset); + ASSERT(inputShapes[0].scale == inputShapes[i].scale); + } + for (int d = 0; d < (int32_t)num_dimensions; ++d) + { + if (d == axis) + { + sum_axis += getSizeOfDimension(inputShapes[i], axis); + } + else + { + ASSERT(getSizeOfDimension(inputShapes[0], d) == + getSizeOfDimension(inputShapes[i], d)); + } + } + } + + output->type = input_type; + output->dimensions = inputShapes[0].dimensions; + output->dimensions[axis] = sum_axis; + + if (input_type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(inputShapes[0].offset == output->offset); + ASSERT(inputShapes[0].scale == output->scale); + } + + return true; +} diff --git a/compiler/ann-ref/src/ops/Concatenation.float.cpp b/compiler/ann-ref/src/ops/Concatenation.float.cpp new file mode 100644 index 000000000..ac32aa0ff --- /dev/null +++ b/compiler/ann-ref/src/ops/Concatenation.float.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Concatenation.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" + +// From optimized_ops.h in TensorFlow Lite +template +void Concatenation(int concat_dim, const Scalar *const *input_data, + const Dims<4> *const *input_dims, int inputs_count, Scalar *output_data, + const Dims<4> &output_dims) +{ + DCHECK_GT(inputs_count, 1); + int concat_size = 0; + for (int i = 0; i < inputs_count; i++) + { + for (int j = 0; j < 4; j++) + { + if (j != concat_dim) + { + MatchingArraySize(*input_dims[i], j, output_dims, j); + } + } + concat_size += ArraySize(*input_dims[i], concat_dim); + } + DCHECK_EQ(concat_size, ArraySize(output_dims, concat_dim)); + DCHECK(IsPackedWithoutStrides(output_dims)); + // for now we dont have a model with a Concatenation + // with fused activation function. + DCHECK(Ac == FusedActivationFunctionType::kNone); + int outer_size = 1; + for (int i = concat_dim + 1; i < 4; i++) + { + outer_size *= output_dims.sizes[i]; + } + Scalar *output_ptr = output_data; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < inputs_count; ++i) + { + const int copy_size = input_dims[i]->sizes[concat_dim] * input_dims[i]->strides[concat_dim]; + memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); + output_ptr += copy_size; + } + } +} + +bool concatenationFloat32(const std::vector &inputDataPtrs, + const std::vector &inputShapes, int32_t axis, float *outputData, + const Shape &outputShape) +{ + int num_inputs = inputShapes.size(); + std::vector *> inputDimsPtr(num_inputs); + std::vector> inputDims(num_inputs); + for (int i = 0; i < num_inputs; i++) + { + inputDims[i] = convertShapeToDims(inputShapes[i]); + inputDimsPtr[i] = &inputDims[i]; + } + + Concatenation( + getNumberOfDimensions(outputShape) - axis - 1, inputDataPtrs.data(), inputDimsPtr.data(), + num_inputs, outputData, convertShapeToDims(outputShape)); + + return true; +} diff --git a/compiler/ann-ref/src/ops/Concatenation.float.h b/compiler/ann-ref/src/ops/Concatenation.float.h new file mode 100644 index 000000000..65bca1880 --- /dev/null +++ b/compiler/ann-ref/src/ops/Concatenation.float.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONCATENATION_FLOAT_H__ +#define __OP_CONCATENATION_FLOAT_H__ + +#include "Shape.h" + +#include +#include + +bool concatenationFloat32(const std::vector &inputDataPtrs, + const std::vector &inputShapes, int32_t axis, float *outputData, + const Shape &outputShape); + +#endif // __OP_CONCATENATION_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Concatenation.h b/compiler/ann-ref/src/ops/Concatenation.h new file mode 100644 index 000000000..b92071e45 --- /dev/null +++ b/compiler/ann-ref/src/ops/Concatenation.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONCATENATION_H__ +#define __OP_CONCATENATION_H__ + +#include "Shape.h" + +#include +#include + +bool concatenationPrepare(const std::vector &inputShapes, int32_t axis, Shape *output); + +#endif // __OP_CONCATENATION_H__ diff --git a/compiler/ann-ref/src/ops/Conv2D.cpp b/compiler/ann-ref/src/ops/Conv2D.cpp new file mode 100644 index 000000000..ef4407e00 --- /dev/null +++ b/compiler/ann-ref/src/ops/Conv2D.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Conv2D.h" +#include "Assert.h" + +#include "internal/Spatial.h" + +bool convPrepare(const Shape &input, const Shape &filter, const Shape &bias, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, Shape *output) +{ + ASSERT(input.type == filter.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) == 4); + ASSERT(getNumberOfDimensions(filter) == 4); + ASSERT(getNumberOfDimensions(bias) == 1); + + ASSERT(getSizeOfDimension(filter, 0) == getSizeOfDimension(bias, 0)); + ASSERT(getSizeOfDimension(filter, 3) == getSizeOfDimension(input, 3)); + + uint32_t channels_out = getSizeOfDimension(filter, 0); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t filterWidth = getSizeOfDimension(filter, 2); + uint32_t filterHeight = getSizeOfDimension(filter, 1); + uint32_t batches = getSizeOfDimension(input, 0); + + uint32_t outWidth = computeOutSize(width, filterWidth, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filterHeight, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/compiler/ann-ref/src/ops/Conv2D.float.cpp b/compiler/ann-ref/src/ops/Conv2D.float.cpp new file mode 100644 index 000000000..b47fcce27 --- /dev/null +++ b/compiler/ann-ref/src/ops/Conv2D.float.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Conv2D.float.h" + +#include "internal/Spatial.h" +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" + +// From optimized_ops.h in TensorFlow Lite +template +inline void ExtractPatchIntoBufferColumn(const Dims<4> &input_dims, int w, int h, int b, + int kheight, int kwidth, int stride_width, + int stride_height, int pad_width, int pad_height, + int in_width, int in_height, int in_depth, + int single_buffer_length, int buffer_id, const T *in_data, + T *conv_buffer_data, uint8 byte_zero) +{ + // This chunk of code reshapes all the inputs corresponding to + // output (b, h, w) to a column vector in conv_buffer(:, buffer_id). + const int kwidth_times_indepth = kwidth * in_depth; + const int inwidth_times_indepth = in_width * in_depth; + const int ih_ungated_start = h * stride_height - pad_height; + const int ih_ungated_end = (ih_ungated_start + kheight); + const int ih_end = std::min(ih_ungated_end, in_height); + const int iw_ungated_start = w * stride_width - pad_width; + const int iw_ungated_end = (iw_ungated_start + kwidth); + const int iw_end = std::min(iw_ungated_end, in_width); + // If the patch is off the edge of the input image, skip writing those rows + // and columns from the patch into the output array. + const int h_offset = std::max(0, -ih_ungated_start); + const int w_offset = std::max(0, -iw_ungated_start); + const int ih_start = std::max(0, ih_ungated_start); + const int iw_start = std::max(0, iw_ungated_start); + const int single_row_num = std::min(kwidth - w_offset, in_width - iw_start) * in_depth; + const int output_row_offset = (buffer_id * single_buffer_length); + int out_offset = output_row_offset + (h_offset * kwidth + w_offset) * in_depth; + int in_offset = Offset(input_dims, 0, iw_start, ih_start, b); + + // Express all of the calculations as padding around the input patch. + const int top_padding = h_offset; + const int bottom_padding = (ih_ungated_end - ih_end); + const int left_padding = w_offset; + const int right_padding = (iw_ungated_end - iw_end); + assert(single_row_num == ((kwidth - (left_padding + right_padding)) * in_depth)); + + // Write out zeroes to the elements representing the top rows of the input + // patch that are off the edge of the input image. + if (top_padding > 0) + { + const int top_row_elements = (top_padding * kwidth * in_depth); + memset(conv_buffer_data + output_row_offset, byte_zero, (top_row_elements * sizeof(T))); + } + + // If the patch is on the interior of the input image horizontally, just copy + // over the rows sequentially, otherwise add zero padding at the start or end. + if ((left_padding == 0) && (right_padding == 0)) + { + for (int ih = ih_start; ih < ih_end; ++ih) + { + memcpy(conv_buffer_data + out_offset, in_data + in_offset, single_row_num * sizeof(T)); + out_offset += kwidth_times_indepth; + in_offset += inwidth_times_indepth; + } + } + else + { + for (int ih = ih_start; ih < ih_end; ++ih) + { + if (left_padding > 0) + { + const int left_start = (out_offset - (left_padding * in_depth)); + memset(conv_buffer_data + left_start, byte_zero, (left_padding * in_depth * sizeof(T))); + } + memcpy(conv_buffer_data + out_offset, in_data + in_offset, single_row_num * sizeof(T)); + if (right_padding > 0) + { + const int right_start = (out_offset + single_row_num); + memset(conv_buffer_data + right_start, byte_zero, (right_padding * in_depth * sizeof(T))); + } + out_offset += kwidth_times_indepth; + in_offset += inwidth_times_indepth; + } + } + + // If the bottom of the patch falls off the input image, pad the values + // representing those input rows with zeroes. + if (bottom_padding > 0) + { + const int bottom_row_elements = (bottom_padding * kwidth * in_depth); + const int bottom_start = + output_row_offset + ((top_padding + (ih_end - ih_start)) * kwidth * in_depth); + memset(conv_buffer_data + bottom_start, byte_zero, (bottom_row_elements * sizeof(T))); + } +} + +template +void Im2col(const T *input_data, const Dims<4> &input_dims, int stride_width, int stride_height, + int pad_width, int pad_height, int kheight, int kwidth, uint8 byte_zero, T *output_data, + const Dims<4> &output_dims) +{ + DCHECK(IsPackedWithoutStrides(input_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_depth = ArraySize(input_dims, 0); + const int input_width = ArraySize(input_dims, 1); + const int input_height = ArraySize(input_dims, 2); + const int output_depth = ArraySize(output_dims, 0); + const int output_width = ArraySize(output_dims, 1); + const int output_height = ArraySize(output_dims, 2); + + int buffer_id = 0; + // Loop over the output nodes. + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < output_height; ++h) + { + for (int w = 0; w < output_width; ++w) + { + ExtractPatchIntoBufferColumn(input_dims, w, h, b, kheight, kwidth, stride_width, + stride_height, pad_width, pad_height, input_width, + input_height, input_depth, output_depth, buffer_id, input_data, + output_data, byte_zero); + ++buffer_id; + } + } + } +} + +// From optimized_ops.h in TensorFlow Lite +template +void Conv(const float *input_data, const Dims<4> &input_dims, const float *filter_data, + const Dims<4> &filter_dims, const float *bias_data, const Dims<4> &bias_dims, + int stride_width, int stride_height, int pad_width, int pad_height, float *output_data, + const Dims<4> &output_dims, float *im2col_data, const Dims<4> &im2col_dims) +{ + (void)im2col_data; + (void)im2col_dims; + + const float *gemm_input_data = nullptr; + const Dims<4> *gemm_input_dims = nullptr; + const int filter_width = ArraySize(filter_dims, 1); + const int filter_height = ArraySize(filter_dims, 2); + const bool need_im2col = + stride_width != 1 || stride_height != 1 || filter_width != 1 || filter_height != 1; + if (need_im2col) + { + DCHECK(im2col_data); + Im2col(input_data, input_dims, stride_width, stride_height, pad_width, pad_height, + filter_height, filter_width, 0, im2col_data, im2col_dims); + gemm_input_data = im2col_data; + gemm_input_dims = &im2col_dims; + } + else + { +#if 0 // TODO-NNRT : Check if it needs, 'im2col_data' seems to be always not null. + DCHECK(!im2col_data); +#endif + gemm_input_data = input_data; + gemm_input_dims = &input_dims; + } + + const auto im2col_matrix_map = MapAsMatrixWithFirstDimAsRows(gemm_input_data, *gemm_input_dims); + const auto filter_matrix_map = MapAsMatrixWithLastDimAsCols(filter_data, filter_dims); + auto output_matrix_map = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + + Gemm(filter_matrix_map.transpose(), im2col_matrix_map, &output_matrix_map); + + AddBiasAndEvalActivationFunction(bias_data, bias_dims, output_data, output_dims); +} + +// If possible we will use this static buffer for the tensor. +static constexpr int kStaticBufferSize = 1605632; +static char static_scratch_buffer[kStaticBufferSize]; + +#define ANDROID_NN_CONV_PARAMETERS(Type) \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t filterHeight = getSizeOfDimension(filterShape, 1); \ + uint32_t filterWidth = getSizeOfDimension(filterShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + uint32_t inDepth = getSizeOfDimension(inputShape, 3); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; \ + \ + Dims<4> im2colDim; \ + im2colDim.sizes[3] = (int)getSizeOfDimension(outputShape, 0); \ + im2colDim.sizes[2] = (int)getSizeOfDimension(outputShape, 1); \ + im2colDim.sizes[1] = (int)getSizeOfDimension(outputShape, 2); \ + im2colDim.sizes[0] = (int)inDepth * filterHeight * filterWidth; \ + \ + im2colDim.strides[0] = 1; \ + for (int i = 1; i < 4; i++) \ + { \ + im2colDim.strides[i] = im2colDim.strides[i - 1] * im2colDim.sizes[i - 1]; \ + } \ + \ + Type *im2colData = nullptr; \ + int im2colByteSize = sizeof(Type); \ + for (int i = 0; i < 4; i++) \ + { \ + im2colByteSize *= im2colDim.sizes[i]; \ + } \ + if (im2colByteSize <= kStaticBufferSize) \ + { \ + im2colData = reinterpret_cast(static_scratch_buffer); \ + } \ + else \ + { \ + im2colData = new (std::nothrow) Type[im2colByteSize / sizeof(Type)]; \ + } + +bool convFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t activation, float *outputData, const Shape &outputShape) +{ + + ANDROID_NN_CONV_PARAMETERS(float) + +#define ANDROID_NN_CONV(activation) \ + Conv( \ + inputData, convertShapeToDims(inputShape), filterData, convertShapeToDims(filterShape), \ + biasData, convertShapeToDims(biasShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, outputData, convertShapeToDims(outputShape), im2colData, im2colDim) + + ANDROID_NN_MACRO_DISPATCH_WITH_DELETE(ANDROID_NN_CONV) +#undef ANDROID_NN_CONV + + if (im2colByteSize > kStaticBufferSize) + { + delete[] im2colData; + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Conv2D.float.h b/compiler/ann-ref/src/ops/Conv2D.float.h new file mode 100644 index 000000000..620263fc3 --- /dev/null +++ b/compiler/ann-ref/src/ops/Conv2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONV_2D_FLOAT_H__ +#define __OP_CONV_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool convFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t activation, float *outputData, const Shape &outputShape); + +#endif // __OP_CONV_2D_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Conv2D.h b/compiler/ann-ref/src/ops/Conv2D.h new file mode 100644 index 000000000..7dc1e3424 --- /dev/null +++ b/compiler/ann-ref/src/ops/Conv2D.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONV_2D_H__ +#define __OP_CONV_2D_H__ + +#include "Shape.h" + +#include + +bool convPrepare(const Shape &input, const Shape &filter, const Shape &bias, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, Shape *output); + +#endif // __OP_CONV_2D_H__ diff --git a/compiler/ann-ref/src/ops/DepthwiseConv2D.cpp b/compiler/ann-ref/src/ops/DepthwiseConv2D.cpp new file mode 100644 index 000000000..4692564e7 --- /dev/null +++ b/compiler/ann-ref/src/ops/DepthwiseConv2D.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DepthwiseConv2D.h" +#include "Assert.h" + +#include "internal/Spatial.h" + +bool depthwiseConvPrepare(const Shape &input, const Shape &filter, const Shape &bias, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + Shape *output) +{ + ASSERT(input.type == filter.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) == 4); + ASSERT(getNumberOfDimensions(filter) == 4); + ASSERT(getNumberOfDimensions(bias) == 1); + + ASSERT(getSizeOfDimension(filter, 3) == getSizeOfDimension(bias, 0)); + + uint32_t channels_out = getSizeOfDimension(filter, 3); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t filterWidth = getSizeOfDimension(filter, 2); + uint32_t filterHeight = getSizeOfDimension(filter, 1); + uint32_t batches = getSizeOfDimension(input, 0); + + uint32_t outWidth = computeOutSize(width, filterWidth, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filterHeight, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/compiler/ann-ref/src/ops/DepthwiseConv2D.float.cpp b/compiler/ann-ref/src/ops/DepthwiseConv2D.float.cpp new file mode 100644 index 000000000..936b24ec7 --- /dev/null +++ b/compiler/ann-ref/src/ops/DepthwiseConv2D.float.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DepthwiseConv2D.float.h" +#include "Assert.h" + +#include "internal/Spatial.h" +#include "internal/Array.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +#include // 'memcpy' + +namespace optimized_ops +{ + +// Implementation of float DepthwiseConv + +template +struct FloatDepthwiseConvKernel +{ +}; + +// From optimized_ops.h in TensorFlow Lite +// +// Accumulates the effect of one row of the filter, on a segment of one row +// of the output, accessing the corresponding one row of the input. +template +void FloatDepthwiseConvAccumRow(int stride, int input_depth, int input_width, + const float *input_data, int pad_width, int depth_multiplier, + int filter_width, const float *filter_data, int out_x_buffer_start, + int out_x_buffer_end, int output_depth, float *acc_buffer) +{ + // Sanity check parameters. This is important in particular to ensure + // that we keep the number of template instantiations minimal, so we don't + // increase binary size unnecessarily. + static_assert(kFixedDepthMultiplier || !kFixedInputDepth, ""); + static_assert(kFixedInputDepth || kAllowStrided, ""); + DCHECK(stride == 1 || kAllowStrided); + if (kFixedInputDepth) + { + DCHECK_EQ(input_depth, kFixedInputDepth); + } + if (kFixedDepthMultiplier) + { + DCHECK_EQ(depth_multiplier, kFixedDepthMultiplier); + } + DCHECK_EQ(output_depth, input_depth * depth_multiplier); + const int input_ptr_increment = stride * input_depth; + const float *filter_base_ptr = filter_data; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + // For the current (filter_x, filter_y) point in the filter, + // compute the boundaries of the corresponding output row segment. + int out_x_loop_start_unclampled = 0; + int out_x_loop_end_unclampled = 0; + if (kAllowStrided) + { + if (stride == 2) + { + out_x_loop_start_unclampled = (pad_width - filter_x + 1) / 2; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + 1) / 2; + } + else if (stride == 4) + { + out_x_loop_start_unclampled = (pad_width - filter_x + 3) / 4; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + 3) / 4; + } + else + { + out_x_loop_start_unclampled = (pad_width - filter_x + stride - 1) / stride; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + stride - 1) / stride; + } + } + else + { + out_x_loop_start_unclampled = pad_width - filter_x; + out_x_loop_end_unclampled = pad_width + input_width - filter_x; + } + // The kernel will have to iterate on the segment of the + // output row that starts at out_x_loop_start and out_x_loop_end. + const int out_x_loop_start = std::max(out_x_buffer_start, out_x_loop_start_unclampled); + const int out_x_loop_end = std::min(out_x_buffer_end, out_x_loop_end_unclampled); + + float *acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; + const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const float *input_ptr = input_data + in_x_origin * input_depth; + const int num_output_pixels = out_x_loop_end - out_x_loop_start; + FloatDepthwiseConvKernel::Run( + num_output_pixels, input_depth, depth_multiplier, input_ptr, input_ptr_increment, + filter_base_ptr, acc_buffer_ptr); + filter_base_ptr += output_depth; + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// generic fallback of FloatDepthwiseConvAccumRow, portable, non-templatized. +inline void FloatDepthwiseConvAccumRowGeneric(int stride, int input_depth, int input_width, + const float *input_data, int pad_width, + int depth_multiplier, int filter_width, + const float *filter_data, int out_x_buffer_start, + int out_x_buffer_end, int output_depth, + float *acc_buffer) +{ + const float *filter_base_ptr = filter_data; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int out_x_loop_start = + std::max(out_x_buffer_start, (pad_width - filter_x + stride - 1) / stride); + const int out_x_loop_end = + std::min(out_x_buffer_end, (pad_width + input_width - filter_x + stride - 1) / stride); + + float *acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; + const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const float *input_ptr = input_data + in_x_origin * input_depth; + const int input_ptr_increment = (stride - 1) * input_depth; + for (int out_x = out_x_loop_start; out_x < out_x_loop_end; out_x++) + { + const float *filter_ptr = filter_base_ptr; + for (int ic = 0; ic < input_depth; ++ic) + { + const float input_val = *input_ptr++; + for (int m = 0; m < depth_multiplier; m++) + { + const float filter_val = *filter_ptr++; + *acc_buffer_ptr++ += filter_val * input_val; + } + } + input_ptr += input_ptr_increment; + } + filter_base_ptr += output_depth; + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// Initializes the accumulator buffer with bias values. +inline void DepthwiseConvInitAccBuffer(int num_output_pixels, int output_depth, + const float *bias_data, float *acc_buffer) +{ + for (int i = 0; i < num_output_pixels; i++) + { + memcpy(acc_buffer + i * output_depth, bias_data, sizeof(acc_buffer[0]) * output_depth); + } +} + +// From optimized_ops.h in TensorFlow Lite +template +void DepthwiseConv(const float *input_data, const Dims<4> &input_dims, const float *filter_data, + const Dims<4> &filter_dims, const float *bias_data, const Dims<4> &bias_dims, + int stride_width, int stride_height, int pad_width, int pad_height, + int depth_multiplier, float *output_data, const Dims<4> &output_dims) +{ + static_assert( + Ac == FusedActivationFunctionType::kNone || Ac == FusedActivationFunctionType::kRelu || + Ac == FusedActivationFunctionType::kRelu6 || Ac == FusedActivationFunctionType::kRelu1, + ""); + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int output_depth = MatchingArraySize(filter_dims, 0, output_dims, 0); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int input_depth = ArraySize(input_dims, 0); + const int filter_height = ArraySize(filter_dims, 2); + const int filter_width = ArraySize(filter_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); +#if 0 // TODO-NNRT : Check if assertion is needed, output depth some times not equal to input * + // depthmultiplier + DCHECK(output_depth == input_depth * depth_multiplier); +#endif + + static const int kAccBufferMaxSize = 1024; + float acc_buffer[kAccBufferMaxSize]; + DCHECK_GE(kAccBufferMaxSize, output_depth); + const int kOutputPixelsInAccBuffer = kAccBufferMaxSize / output_depth; + const int kAccBufferActualSize = kOutputPixelsInAccBuffer * output_depth; + DCHECK_LE(kOutputPixelsInAccBuffer * output_depth, kAccBufferActualSize); + DCHECK_LE(kAccBufferActualSize, kAccBufferMaxSize); + DCHECK_GE(kOutputPixelsInAccBuffer, 1); + + // row_accum_func will point to the core accumulation function to be used + // for this DepthwiseConv op. + auto *row_accum_func = FloatDepthwiseConvAccumRowGeneric; + + const int kMaxFixedDepthMultiplier = 16; + int fixed_depth_multiplier = 0; + if (depth_multiplier <= kMaxFixedDepthMultiplier) + { + fixed_depth_multiplier = depth_multiplier; + } + // kMaxUnrolling is the max number of output values that we aim to handle + // in one unrolled iteration of the inner loop. For practical performance + // reasons, it is limited by the number of available registers. We could + // fine-tune it depending on the architecture, but that's not worth doing + // since this whole code is not very optimized to begin with. The + // present value reflects what's realistic on ARM 32bit NEON with 16 128-bit + // vector registers. + const int kMaxUnrolling = 8; + int fixed_input_depth = 0; + if (fixed_depth_multiplier && input_depth * fixed_depth_multiplier <= kMaxUnrolling) + { + fixed_input_depth = input_depth; + } + + // Now that we have determined row_accum_func, we can start work. + float *output_ptr = output_data; + for (int b = 0; b < batches; ++b) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + const int in_y_origin = (out_y * stride_height) - pad_height; + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(filter_height, input_height - in_y_origin); + for (int out_x_buffer_start = 0; out_x_buffer_start < output_width; + out_x_buffer_start += kOutputPixelsInAccBuffer) + { + const int out_x_buffer_end = + std::min(output_width, out_x_buffer_start + kOutputPixelsInAccBuffer); + // We call a 'pixel' a group of activation that share all but the + // 'depth'/'channel' coordinate. num_output_pixels is the number of + // output pixels that we will accumulate in this loop iteration. + const int num_output_pixels = out_x_buffer_end - out_x_buffer_start; + // Initialize our local accumulator with the bias values, so we don't + // have to add them later. + DepthwiseConvInitAccBuffer(num_output_pixels, output_depth, bias_data, acc_buffer); + // Accumulation loop. Most of the time should be spent in here. + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + const int in_y = in_y_origin + filter_y; + row_accum_func(stride_width, input_depth, input_width, + input_data + in_y * input_dims.strides[2] + b * input_dims.strides[3], + pad_width, depth_multiplier, filter_width, + filter_data + filter_y * filter_dims.strides[2], out_x_buffer_start, + out_x_buffer_end, output_depth, acc_buffer); + } + // Finished accumulating. Now store to destination. + const int num_output_values = output_depth * num_output_pixels; + int i = 0; + // Handle leftover values, one by one. This is very slow. + for (; i < num_output_values; i++) + { + float acc = acc_buffer[i]; + if (Ac == FusedActivationFunctionType::kRelu) + { + acc = std::max(0.f, acc); + } + else if (Ac == FusedActivationFunctionType::kRelu6) + { + acc = std::max(0.f, std::min(6.f, acc)); + } + else if (Ac == FusedActivationFunctionType::kRelu1) + { + acc = std::max(-1.f, std::min(1.f, acc)); + } + *output_ptr++ = acc; + } + } + } + } +} + +} // namespace optimized_ops + +#define ANDROID_NN_DEPTHWISE_CONV_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t filterHeight = getSizeOfDimension(filterShape, 1); \ + uint32_t filterWidth = getSizeOfDimension(filterShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool depthwiseConvFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t depth_multiplier, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_DEPTHWISE_CONV_PARAMETERS + +#define ANDROID_NN_DEPTHWISE_CONV(activation) \ + optimized_ops::DepthwiseConv( \ + inputData, convertShapeToDims(inputShape), filterData, convertShapeToDims(filterShape), \ + biasData, convertShapeToDims(biasShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, depth_multiplier, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_DEPTHWISE_CONV) +#undef ANDROID_NN_DEPTHWISE_CONV + + return true; +} diff --git a/compiler/ann-ref/src/ops/DepthwiseConv2D.float.h b/compiler/ann-ref/src/ops/DepthwiseConv2D.float.h new file mode 100644 index 000000000..3fbfeae67 --- /dev/null +++ b/compiler/ann-ref/src/ops/DepthwiseConv2D.float.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DEPTHWISE_CONV_2D_FLOAT_H__ +#define __OP_DEPTHWISE_CONV_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool depthwiseConvFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t depth_multiplier, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_DEPTHWISE_CONV_2D_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/DepthwiseConv2D.h b/compiler/ann-ref/src/ops/DepthwiseConv2D.h new file mode 100644 index 000000000..13f520219 --- /dev/null +++ b/compiler/ann-ref/src/ops/DepthwiseConv2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DEPTHWISE_CONV_2D_H__ +#define __OP_DEPTHWISE_CONV_2D_H__ + +#include "Shape.h" + +#include + +bool depthwiseConvPrepare(const Shape &input, const Shape &filter, const Shape &bias, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + Shape *output); + +#endif // __OP_DEPTHWISE_CONV_2D_H__ diff --git a/compiler/ann-ref/src/ops/Div.cpp b/compiler/ann-ref/src/ops/Div.cpp new file mode 100644 index 000000000..250e72b1d --- /dev/null +++ b/compiler/ann-ref/src/ops/Div.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Div.h" +#include "Assert.h" + +bool divPrepare(const Shape &in1, const Shape &in2, Shape *out) +{ + ASSERT(getNumberOfDimensions(in1) <= 4 && getNumberOfDimensions(in2) <= 4); + ASSERT(in1.type == in2.type); + if (SameShape(in1, in2)) + { + return SetShape(in1, out); + } + else + { + // Broadcast needed + uint32_t numberOfDims1 = getNumberOfDimensions(in1); + uint32_t numberOfDims2 = getNumberOfDimensions(in2); + uint32_t maxDims = std::max(numberOfDims1, numberOfDims2); + out->dimensions = std::vector(maxDims); + for (uint32_t i = 1; i <= maxDims; i++) + { + uint32_t dim1 = 1; + if (i <= numberOfDims1) + { + dim1 = getSizeOfDimension(in1, numberOfDims1 - i); + } + uint32_t dim2 = 1; + if (i <= numberOfDims2) + { + dim2 = getSizeOfDimension(in2, numberOfDims2 - i); + } + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) + { + LOG(ERROR) << "Dimensions mismatch for BroadcastDiv"; + return false; + } + out->dimensions[maxDims - i] = std::max(dim1, dim2); + } + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Div.float.cpp b/compiler/ann-ref/src/ops/Div.float.cpp new file mode 100644 index 000000000..a1a39e546 --- /dev/null +++ b/compiler/ann-ref/src/ops/Div.float.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Div.float.h" + +#include "internal/Array.h" +#include "internal/NDArray.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void Div(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + MatchingArraySize(input1_dims, 3, input2_dims, 3, output_dims, 3); + MatchingArraySize(input1_dims, 2, input2_dims, 2, output_dims, 2); + MatchingArraySize(input1_dims, 1, input2_dims, 1, output_dims, 1); + MatchingArraySize(input1_dims, 0, input2_dims, 0, output_dims, 0); + DCHECK(IsPackedWithoutStrides(input1_dims)); + DCHECK(IsPackedWithoutStrides(input2_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + + const int size = input1_dims.sizes[3] * input1_dims.strides[3]; + + for (int i = 0; i < size; i++) + { + auto x = input1_data[i] / input2_data[i]; + output_data[i] = ActivationFunction(x); + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// TODO: We can implement BroadcastDiv on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +// TODO: BroadcastDiv is intentionally duplicated from +// reference_ops.h. Once an optimized version is implemented and NdArrayDesc +// is no longer referenced in this file, move NdArrayDesc from types.h to +// reference_ops.h. +template +void BroadcastDiv(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_dims, input2_dims, &desc1, &desc2); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < ArraySize(output_dims, 3); ++b) + { + for (int y = 0; y < ArraySize(output_dims, 2); ++y) + { + for (int x = 0; x < ArraySize(output_dims, 1); ++x) + { + for (int c = 0; c < ArraySize(output_dims, 0); ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(input1_data[SubscriptToIndex(desc1, c, x, y, b)] / + input2_data[SubscriptToIndex(desc2, c, x, y, b)]); + } + } + } + } +} + +bool divFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut) +{ + bool needBroadcast = !SameShape(shape1, shape2); + +#define ANDROID_NN_NORMAL_DIV(activation) \ + Div(in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + +#define ANDROID_NN_BROADCAST_DIV(activation) \ + BroadcastDiv( \ + in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + + if (needBroadcast) + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_BROADCAST_DIV) + } + else + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_NORMAL_DIV) + } + +#undef ANDROID_NN_NORMAL_ADD +#undef ANDROID_NN_BROADCAST_ADD + return true; +} diff --git a/compiler/ann-ref/src/ops/Div.float.h b/compiler/ann-ref/src/ops/Div.float.h new file mode 100644 index 000000000..a2aa7e1a9 --- /dev/null +++ b/compiler/ann-ref/src/ops/Div.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DIV_FLOAT_H__ +#define __OP_DIV_FLOAT_H__ + +#include "Shape.h" + +#include + +bool divFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut); + +#endif // __OP_DIV_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Div.h b/compiler/ann-ref/src/ops/Div.h new file mode 100644 index 000000000..5eb98a3f2 --- /dev/null +++ b/compiler/ann-ref/src/ops/Div.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DIV_H__ +#define __OP_DIV_H__ + +#include "Shape.h" + +bool divPrepare(const Shape &in1, const Shape &in2, Shape *out); + +#endif // __OP_DIV_H__ diff --git a/compiler/ann-ref/src/ops/FullyConnected.cpp b/compiler/ann-ref/src/ops/FullyConnected.cpp new file mode 100644 index 000000000..d21389e7e --- /dev/null +++ b/compiler/ann-ref/src/ops/FullyConnected.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FullyConnected.h" +#include "Assert.h" + +#if 0 +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" +#endif + +bool fullyConnectedPrepare(const Shape &input, const Shape &weights, const Shape &bias, + Shape *output) +{ + // Check all the parameters of tensor match within themselves and match the + // input configuration. + ASSERT(input.type == weights.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) >= 2); + uint32_t input_size = getNumberOfElements(input); + uint32_t num_units = getSizeOfDimension(weights, 0); + + // modified to resolve Coverity 118949 (Apr 25, 2018) by hyunsik.yoon + // Original Code: + // uint32_t batch_size = input_size / getSizeOfDimension(weights, 1); + // + // Coverity Detection: Division by zero + // + // Code below is modified code + + uint32_t shape_size = getSizeOfDimension(weights, 1); + if (shape_size == 0) + { + return false; + } + + uint32_t batch_size = input_size / shape_size; + + ASSERT(getSizeOfDimension(bias, 0) == num_units); + ASSERT(getSizeOfDimension(weights, 1) * batch_size == input_size); + ASSERT(getNumberOfDimensions(weights) == 2); + + output->type = input.type; + output->dimensions = {batch_size, num_units}; + + return true; +} diff --git a/compiler/ann-ref/src/ops/FullyConnected.float.cpp b/compiler/ann-ref/src/ops/FullyConnected.float.cpp new file mode 100644 index 000000000..4d12382ca --- /dev/null +++ b/compiler/ann-ref/src/ops/FullyConnected.float.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FullyConnected.float.h" +#include "Assert.h" + +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" + +// From optimized_ops.h in TensorFlow Lite +template +void FullyConnected(const float *input_data, const Dims<4> &input_dims, const float *weights_data, + const Dims<4> &weights_dims, const float *bias_data, const Dims<4> &bias_dims, + float *output_data, const Dims<4> &output_dims) +{ + // TODO(b/62193649): this convoluted shape computation (determining + // input_rows from the weights_dims, then MapAsMatrixWithGivenNumberOfRows) + // is because the current --variable_batch hack consists in overwriting the + // 3rd dimension with the runtime batch size, as we don't keep track for each + // array of which dimension is the batch dimension in it. + // When that is fixed, this should become: + // const auto input_matrix_map = + // MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + const int input_rows = ArraySize(weights_dims, 0); + const auto input_matrix_map = + MapAsMatrixWithGivenNumberOfRows(input_data, input_dims, input_rows); + const auto filter_matrix_map = MapAsMatrixWithFirstDimAsRows(weights_data, weights_dims); + auto output_matrix_map = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + + Gemm(filter_matrix_map.transpose(), input_matrix_map, &output_matrix_map); + AddBiasAndEvalActivationFunction(bias_data, bias_dims, output_data, output_dims); +} + +bool fullyConnectedFloat32(const float *inputData, const Shape &inputShape, + const float *weightsData, const Shape &weightsShape, + const float *biasData, const Shape &biasShape, int32_t activation, + float *outputData, const Shape &outputShape) +{ + +#define ANDROID_NN_FULLY_CONNECTED(activation) \ + FullyConnected( \ + inputData, convertShapeToDims(inputShape), weightsData, convertShapeToDims(weightsShape), \ + biasData, convertShapeToDims(biasShape), outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_FULLY_CONNECTED) +#undef ANDROID_NN_FULLY_CONNECTED + return true; +} diff --git a/compiler/ann-ref/src/ops/FullyConnected.float.h b/compiler/ann-ref/src/ops/FullyConnected.float.h new file mode 100644 index 000000000..3412fdb06 --- /dev/null +++ b/compiler/ann-ref/src/ops/FullyConnected.float.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_FULLY_CONNECTED_FLOAT_H__ +#define __OP_FULLY_CONNECTED_FLOAT_H__ + +#include "Shape.h" + +#include + +bool fullyConnectedFloat32(const float *inputData, const Shape &inputShape, const float *weights, + const Shape &weightsShape, const float *biasData, const Shape &biasShape, + int32_t activation, float *outputData, const Shape &outputShape); + +#endif // __OP_FULLY_CONNECTED_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/FullyConnected.h b/compiler/ann-ref/src/ops/FullyConnected.h new file mode 100644 index 000000000..985fd7ec2 --- /dev/null +++ b/compiler/ann-ref/src/ops/FullyConnected.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_FULLY_CONNECTED_H__ +#define __OP_FULLY_CONNECTED_H__ + +#include "Shape.h" + +#include + +bool fullyConnectedPrepare(const Shape &input, const Shape &weights, const Shape &bias, + Shape *output); + +#endif // __OP_FULLY_CONNECTED_H__ diff --git a/compiler/ann-ref/src/ops/MaxPool2D.cpp b/compiler/ann-ref/src/ops/MaxPool2D.cpp new file mode 100644 index 000000000..405afbbdc --- /dev/null +++ b/compiler/ann-ref/src/ops/MaxPool2D.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "MaxPool2D.h" + +#include "internal/Pooling.h" + +bool maxPoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + return genericPoolingPrepare(input, padding_left, padding_right, padding_top, padding_bottom, + stride_width, stride_height, filter_width, filter_height, + output); +} diff --git a/compiler/ann-ref/src/ops/MaxPool2D.float.cpp b/compiler/ann-ref/src/ops/MaxPool2D.float.cpp new file mode 100644 index 000000000..d49b6aad8 --- /dev/null +++ b/compiler/ann-ref/src/ops/MaxPool2D.float.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MaxPool2D.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/FeatureMap.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +// From optimized_ops.h in TensorFlow Lite +template +void MaxPool(const float *input_data, const Dims<4> &input_dims, int stride_width, + int stride_height, int pad_width, int pad_height, int kwidth, int kheight, + float *output_data, const Dims<4> &output_dims) +{ + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); + const int depth = MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // Prefill the output to minimum representable float value + out_mat.setConstant(std::numeric_limits::lowest()); + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < input_height; ++h) + { + for (int w = 0; w < input_width; ++w) + { + // (h_start, h_end) * (w_start, w_end) is the range that the input + // vector projects to. + int hpad = h + pad_height; + int wpad = w + pad_width; + int h_start = (hpad < kheight) ? 0 : (hpad - kheight) / stride_height + 1; + int h_end = std::min(hpad / stride_height + 1, output_height); + int w_start = (wpad < kwidth) ? 0 : (wpad - kwidth) / stride_width + 1; + int w_end = std::min(wpad / stride_width + 1, output_width); + // compute elementwise sum + for (int ph = h_start; ph < h_end; ++ph) + { + for (int pw = w_start; pw < w_end; ++pw) + { + int out_offset = NodeOffset(b, ph, pw, output_height, output_width); + out_mat.col(out_offset) = + out_mat.col(out_offset) + .cwiseMax(in_mat.col(NodeOffset(b, h, w, input_height, input_width))); + } + } + } + } + } + + for (int b = 0; b < batches; ++b) + { + for (int y = 0; y < output_height; ++y) + { + for (int x = 0; x < output_width; ++x) + { + for (int c = 0; c < depth; ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(output_data[Offset(output_dims, c, x, y, b)]); + } + } + } + } +} + +#define ANDROID_NN_POOLING_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool maxPoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_POOLING_PARAMETERS + +#define ANDROID_NN_MAX_POOL(activation) \ + MaxPool( \ + inputData, convertShapeToDims(inputShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, filter_width, filter_height, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_MAX_POOL) +#undef ANDROID_NN_MAX_POOL + + return true; +} + +#undef ANDROID_NN_POOLING_PARAMETERS diff --git a/compiler/ann-ref/src/ops/MaxPool2D.float.h b/compiler/ann-ref/src/ops/MaxPool2D.float.h new file mode 100644 index 000000000..fd320f3b4 --- /dev/null +++ b/compiler/ann-ref/src/ops/MaxPool2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_MAX_POOL_2D_FLOAT_H__ +#define __OP_MAX_POOL_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool maxPoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_MAX_POOL_2D_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/MaxPool2D.h b/compiler/ann-ref/src/ops/MaxPool2D.h new file mode 100644 index 000000000..e15a030bb --- /dev/null +++ b/compiler/ann-ref/src/ops/MaxPool2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_MAX_POOL_2D_H__ +#define __OP_MAX_POOL_2D_H__ + +#include "Shape.h" + +#include + +bool maxPoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + +#endif // __OP_MAX_POOL_2D_H__ diff --git a/compiler/ann-ref/src/ops/Mul.cpp b/compiler/ann-ref/src/ops/Mul.cpp new file mode 100644 index 000000000..03ea9383a --- /dev/null +++ b/compiler/ann-ref/src/ops/Mul.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Mul.h" +#include "Assert.h" + +bool mulPrepare(const Shape &in1, const Shape &in2, Shape *out) +{ + ASSERT(getNumberOfDimensions(in1) <= 4 && getNumberOfDimensions(in2) <= 4); + ASSERT(in1.type == in2.type); + if (SameShape(in1, in2)) + { + return SetShape(in1, out); + } + else + { + // Broadcast needed + uint32_t numberOfDims1 = getNumberOfDimensions(in1); + uint32_t numberOfDims2 = getNumberOfDimensions(in2); + uint32_t maxDims = std::max(numberOfDims1, numberOfDims2); + out->dimensions = std::vector(maxDims); + for (uint32_t i = 1; i <= maxDims; i++) + { + uint32_t dim1 = 1; + if (i <= numberOfDims1) + { + dim1 = getSizeOfDimension(in1, numberOfDims1 - i); + } + uint32_t dim2 = 1; + if (i <= numberOfDims2) + { + dim2 = getSizeOfDimension(in2, numberOfDims2 - i); + } + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) + { + LOG(ERROR) << "Dimensions mismatch for BroadcastAdd"; + return false; + } + out->dimensions[maxDims - i] = std::max(dim1, dim2); + } + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Mul.float.cpp b/compiler/ann-ref/src/ops/Mul.float.cpp new file mode 100644 index 000000000..8a6f039d4 --- /dev/null +++ b/compiler/ann-ref/src/ops/Mul.float.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Mul.float.h" + +#include "internal/Array.h" +#include "internal/NDArray.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void Mul(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + MatchingArraySize(input1_dims, 3, input2_dims, 3, output_dims, 3); + MatchingArraySize(input1_dims, 2, input2_dims, 2, output_dims, 2); + MatchingArraySize(input1_dims, 1, input2_dims, 1, output_dims, 1); + MatchingArraySize(input1_dims, 0, input2_dims, 0, output_dims, 0); + DCHECK(IsPackedWithoutStrides(input1_dims)); + DCHECK(IsPackedWithoutStrides(input2_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + + int i = 0; + const int size = input1_dims.sizes[3] * input1_dims.strides[3]; + + for (; i < size; i++) + { + auto x = input1_data[i] * input2_data[i]; + output_data[i] = ActivationFunction(x); + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// TODO: We can implement BroadcastMul on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +// TODO: BroadcastMul is intentionally duplicated from +// reference_ops.h. Once an optimized version is implemented and NdArrayDesc +// is no longer referenced in this file, move NdArrayDesc from types.h to +// reference_ops.h. +template +void BroadcastMul(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_dims, input2_dims, &desc1, &desc2); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < ArraySize(output_dims, 3); ++b) + { + for (int y = 0; y < ArraySize(output_dims, 2); ++y) + { + for (int x = 0; x < ArraySize(output_dims, 1); ++x) + { + for (int c = 0; c < ArraySize(output_dims, 0); ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(input1_data[SubscriptToIndex(desc1, c, x, y, b)] * + input2_data[SubscriptToIndex(desc2, c, x, y, b)]); + } + } + } + } +} + +bool mulFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut) +{ + bool needBroadcast = !SameShape(shape1, shape2); + +#define ANDROID_NN_NORMAL_MUL(activation) \ + Mul(in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + +#define ANDROID_NN_BROADCAST_MUL(activation) \ + BroadcastMul( \ + in1, convertShapeToDims(shape1), in2, convertShapeToDims(shape2), out, \ + convertShapeToDims(shapeOut)) + + if (needBroadcast) + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_BROADCAST_MUL) + } + else + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_NORMAL_MUL) + } + +#undef ANDROID_NN_NORMAL_ADD +#undef ANDROID_NN_BROADCAST_ADD + return true; +} diff --git a/compiler/ann-ref/src/ops/Mul.float.h b/compiler/ann-ref/src/ops/Mul.float.h new file mode 100644 index 000000000..bb6b9410b --- /dev/null +++ b/compiler/ann-ref/src/ops/Mul.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_MUL_FLOAT_H__ +#define __OP_MUL_FLOAT_H__ + +#include "Shape.h" + +#include + +bool mulFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut); + +#endif // __OP_MUL_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Mul.h b/compiler/ann-ref/src/ops/Mul.h new file mode 100644 index 000000000..ed808062b --- /dev/null +++ b/compiler/ann-ref/src/ops/Mul.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_MUL_H__ +#define __OP_MUL_H__ + +#include "Shape.h" + +bool mulPrepare(const Shape &in1, const Shape &in2, Shape *out1); + +#endif // __OP_MUL_H__ diff --git a/compiler/ann-ref/src/ops/Pad.cpp b/compiler/ann-ref/src/ops/Pad.cpp new file mode 100644 index 000000000..91741762d --- /dev/null +++ b/compiler/ann-ref/src/ops/Pad.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Pad.h" +#include "Assert.h" +#include "Logging.h" + +#include "internal/Dims.h" + +#include +#include // For 'memset' + +bool padPrepare(const Shape& input, const int32_t* paddingsData, const Shape& paddingsShape, + Shape* output) +{ + // Currently only 4D tensors are supported. + uint32_t numInputDims = getNumberOfDimensions(input); + ASSERT(numInputDims == 4); + + // paddings need to be provided as a 2-D int32 tensor. + ASSERT(paddingsShape.type == OperandType::TENSOR_INT32); + ASSERT(getNumberOfDimensions(paddingsShape) == 2); + ASSERT(getSizeOfDimension(paddingsShape, 0) == numInputDims); + ASSERT(getSizeOfDimension(paddingsShape, 1) == 2); + + std::vector outDims(numInputDims); + for (uint32_t i = 0; i < numInputDims; ++i) + { + int32_t beforePadding = *paddingsData++; + int32_t afterPadding = *paddingsData++; + // Pad value has to be greater than equal to 0. + ASSERT(beforePadding >= 0 && afterPadding >= 0); + outDims[i] = beforePadding + getSizeOfDimension(input, i) + afterPadding; + } + output->type = input.type; + output->dimensions = outDims; + output->offset = input.offset; + output->scale = input.scale; + + return true; +} + +namespace +{ + +// From optimized_ops.h in TensorFlow Lite +template +inline void Pad(const T* input_data, const Dims<4>& input_dims, + const std::vector& left_paddings, + const std::vector& right_paddings, T* output_data, + const Dims<4>& output_dims) { + const int output_batch = ArraySize(output_dims, 3); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); + const int output_depth = ArraySize(output_dims, 0); + + const int left_b_padding = left_paddings[3]; + const int left_h_padding = left_paddings[2]; + const int left_w_padding = left_paddings[1]; + const int left_d_padding = left_paddings[0]; + + const int right_b_padding = right_paddings[3]; + const int right_h_padding = right_paddings[2]; + const int right_w_padding = right_paddings[1]; + const int right_d_padding = right_paddings[0]; + + const int input_depth = ArraySize(input_dims, 0); + + if (left_b_padding != 0) + { + memset(output_data, 0, left_b_padding * output_height * output_width * output_depth * + sizeof(T)); + } + for (int out_b = left_b_padding; out_b < output_batch - right_b_padding; ++out_b) + { + if (left_h_padding != 0) + { + memset(output_data + Offset(output_dims, 0, 0, 0, out_b), 0, + left_h_padding * output_width * output_depth * sizeof(T)); + } + for (int out_h = left_h_padding; out_h < output_height - right_h_padding; ++out_h) + { + if (left_w_padding != 0) + { + memset(output_data + Offset(output_dims, 0, 0, out_h, out_b), 0, + left_w_padding * output_depth * sizeof(T)); + } + for (int out_w = left_w_padding; out_w < output_width - right_w_padding; ++out_w) + { + if (left_d_padding != 0) + { + memset(output_data + Offset(output_dims, 0, out_w, out_h, out_b), 0, + left_d_padding * sizeof(T)); + } + + T* out = output_data + + Offset(output_dims, left_d_padding, out_w, out_h, out_b); + const T* in = + input_data + Offset(input_dims, 0, out_w - left_w_padding, + out_h - left_h_padding, out_b - left_b_padding); + memcpy(out, in, input_depth * sizeof(T)); + + if (right_d_padding != 0) + { + memset( + output_data + Offset(output_dims, output_depth - right_d_padding, + out_w, out_h, out_b), + 0, right_d_padding * sizeof(T)); + } + } + if (right_w_padding != 0) + { + memset( + output_data + Offset(output_dims, 0, output_width - right_w_padding, + out_h, out_b), + 0, right_w_padding * output_depth * sizeof(T)); + } + } + if (right_h_padding != 0) + { + memset(output_data + Offset(output_dims, 0, 0, + output_height - right_h_padding, out_b), + 0, right_h_padding * output_width * output_depth * sizeof(T)); + } + } + if (right_b_padding != 0) + { + memset(output_data + + Offset(output_dims, 0, 0, 0, output_batch - right_b_padding), + 0, + right_b_padding * output_height * output_width * output_depth * + sizeof(T)); + } +} + +} // namespace + +bool padGeneric(const uint8_t* inputData, const Shape& inputShape, const int32_t* paddings, + uint8_t* outputData, const Shape& outputShape) +{ + int32_t numInputDims = static_cast(getNumberOfDimensions(inputShape)); + + std::vector beforePadding; + std::vector afterPadding; + // The lower level implementation expects the paddings in the reverse order. + for (int32_t i = numInputDims - 1; i >= 0; --i) + { + beforePadding.push_back(paddings[i * 2]); + afterPadding.push_back(paddings[i * 2 + 1]); + } + + if (inputShape.type == OperandType::TENSOR_FLOAT32) + { + ::Pad(reinterpret_cast(inputData), + convertShapeToDims(inputShape), + beforePadding, afterPadding, + reinterpret_cast(outputData), + convertShapeToDims(outputShape)); + } + else if (inputShape.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ::Pad(reinterpret_cast(inputData), + convertShapeToDims(inputShape), + beforePadding, afterPadding, + reinterpret_cast(outputData), + convertShapeToDims(outputShape)); + } + else + { + LOG(ERROR) << "Unsupported data type"; + return false; + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Pad.h b/compiler/ann-ref/src/ops/Pad.h new file mode 100644 index 000000000..542ab8962 --- /dev/null +++ b/compiler/ann-ref/src/ops/Pad.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_PAD_H__ +#define __OP_PAD_H__ + +#include "Shape.h" + +#include + +bool padPrepare(const Shape& input, const int32_t* paddingsData, const Shape& paddingsShape, + Shape* output); + +bool padGeneric(const uint8_t* inputData, const Shape& inputShape, const int32_t* paddings, + uint8_t* outputData, const Shape& outputShape); + +#endif // __OP_PAD_H__ diff --git a/compiler/ann-ref/src/ops/ReLU.cpp b/compiler/ann-ref/src/ops/ReLU.cpp new file mode 100644 index 000000000..334291ae5 --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ReLU.h" + +#include "internal/Elementwise.h" + +bool reluPrepare(const Shape &input, Shape *output) +{ + return genericActivationPrepare(input, output); +} diff --git a/compiler/ann-ref/src/ops/ReLU.float.cpp b/compiler/ann-ref/src/ops/ReLU.float.cpp new file mode 100644 index 000000000..df170e48e --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU.float.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ReLU.float.h" + +#include + +bool reluFloat32(const float *inputData, const Shape &inputShape, float *outputData, + const Shape &outputShape) +{ + int numElements = getNumberOfElements(inputShape); + for (int i = 0; i < numElements; i++, inputData++, outputData++) + { + *outputData = std::max(0.f, *inputData); + } + return true; +} diff --git a/compiler/ann-ref/src/ops/ReLU.float.h b/compiler/ann-ref/src/ops/ReLU.float.h new file mode 100644 index 000000000..4c6cf3833 --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU.float.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU_FLOAT_H__ +#define __OP_RELU_FLOAT_H__ + +#include "Shape.h" + +bool reluFloat32(const float *inputData, const Shape &inputShape, float *outputData, + const Shape &outputShape); + +#endif // __OP_RELU_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/ReLU.h b/compiler/ann-ref/src/ops/ReLU.h new file mode 100644 index 000000000..4b329fb8d --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU_H__ +#define __OP_RELU_H__ + +#include "Shape.h" + +bool reluPrepare(const Shape &input, Shape *output); + +#endif // __OP_RELU_H__ diff --git a/compiler/ann-ref/src/ops/ReLU6.cpp b/compiler/ann-ref/src/ops/ReLU6.cpp new file mode 100644 index 000000000..acaa58bda --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU6.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ReLU6.h" + +#include "internal/Elementwise.h" + +bool relu6Prepare(const Shape &input, Shape *output) +{ + return genericActivationPrepare(input, output); +} diff --git a/compiler/ann-ref/src/ops/ReLU6.float.cpp b/compiler/ann-ref/src/ops/ReLU6.float.cpp new file mode 100644 index 000000000..b8aa790b5 --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU6.float.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ReLU6.float.h" + +#include + +bool relu6Float32(const float *inputData, const Shape &inputShape, float *outputData, + const Shape &outputShape) +{ + int numElements = getNumberOfElements(inputShape); + for (int i = 0; i < numElements; i++, inputData++, outputData++) + { + *outputData = std::min(std::max(0.f, *inputData),6.f); + } + return true; +} diff --git a/compiler/ann-ref/src/ops/ReLU6.float.h b/compiler/ann-ref/src/ops/ReLU6.float.h new file mode 100644 index 000000000..06c421a0b --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU6.float.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU6_FLOAT_H__ +#define __OP_RELU6_FLOAT_H__ + +#include "Shape.h" + +bool relu6Float32(const float *inputData, const Shape &inputShape, float *outputData, + const Shape &outputShape); + +#endif // __OP_RELU6_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/ReLU6.h b/compiler/ann-ref/src/ops/ReLU6.h new file mode 100644 index 000000000..625db4b6e --- /dev/null +++ b/compiler/ann-ref/src/ops/ReLU6.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU6_H__ +#define __OP_RELU6_H__ + +#include "Shape.h" + +bool relu6Prepare(const Shape &input, Shape *output); + +#endif // __OP_RELU6_H__ diff --git a/compiler/ann-ref/src/ops/Reshape.cpp b/compiler/ann-ref/src/ops/Reshape.cpp new file mode 100644 index 000000000..a88e81ae4 --- /dev/null +++ b/compiler/ann-ref/src/ops/Reshape.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Reshape.h" +#include "Operand.h" +#include "Assert.h" + +#include + +bool reshapePrepare(const Shape &input, const int32_t *targetDims, const int32_t targetDimsSize, + Shape *output) +{ + // Reshape allows one of the targetDims components to have the + // special -1 value, meaning it will be calculated automatically based on the + // input. Here we calculate what that dimension should be so that the number + // of output elements in the same as the number of input elements. + int32_t numInputElements = (int32_t)getNumberOfElements(input); + + std::vector outDims(targetDimsSize); + int32_t numOutputElements = 1; + int32_t strechDim = -1; + for (int32_t i = 0; i < targetDimsSize; ++i) + { + int32_t value = targetDims[i]; + if (value == -1) + { + ASSERT(strechDim == -1); + strechDim = i; + } + else + { + numOutputElements *= value; + outDims[i] = (uint32_t)value; + } + } + if (strechDim != -1) + { + int32_t strechValue = numInputElements / numOutputElements; + outDims[strechDim] = (uint32_t)strechValue; + numOutputElements *= strechValue; + } + + ASSERT(numInputElements == numOutputElements); + + output->type = input.type; + output->dimensions = outDims; + output->offset = input.offset; + output->scale = input.scale; + + return true; +} + +bool reshapeGeneric(const void *inputData, const Shape &inputShape, void *outputData, + const Shape &outputShape) +{ + size_t count = sizeOfData(inputShape.type, inputShape.dimensions); + memcpy(outputData, inputData, count); + return true; +} diff --git a/compiler/ann-ref/src/ops/Reshape.h b/compiler/ann-ref/src/ops/Reshape.h new file mode 100644 index 000000000..47609ff3c --- /dev/null +++ b/compiler/ann-ref/src/ops/Reshape.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RESHAPE_H__ +#define __OP_RESHAPE_H__ + +#include "Shape.h" + +#include + +bool reshapePrepare(const Shape &input, const int32_t *targetDims, const int32_t targetDimsSize, + Shape *output); + +bool reshapeGeneric(const void *inputData, const Shape &inputShape, void *outputData, + const Shape &outputShape); + +#endif // __OP_RESHAPE_H__ diff --git a/compiler/ann-ref/src/ops/Softmax.cpp b/compiler/ann-ref/src/ops/Softmax.cpp new file mode 100644 index 000000000..9e9044636 --- /dev/null +++ b/compiler/ann-ref/src/ops/Softmax.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Softmax.h" + +#include "internal/Elementwise.h" + +bool softmaxPrepare(const Shape &input, Shape *output) +{ + return genericActivationPrepare(input, output); +} diff --git a/compiler/ann-ref/src/ops/Softmax.float.cpp b/compiler/ann-ref/src/ops/Softmax.float.cpp new file mode 100644 index 000000000..31c29c0c6 --- /dev/null +++ b/compiler/ann-ref/src/ops/Softmax.float.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Softmax.float.h" +#include "Logging.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" + +// From optimized_ops.h in TensorFlow Lite +inline void Softmax(const float *input_data, const Dims<4> &input_dims, float beta, + float *output_data, const Dims<4> &output_dims) +{ + MatchingArraySize(input_dims, 3, output_dims, 3); + MatchingArraySize(input_dims, 2, output_dims, 2); + MatchingArraySize(input_dims, 1, output_dims, 1); + MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // Compute the exponential first, removing the max coefficient for numerical + // stability. + out_mat = (in_mat.rowwise() - in_mat.colwise().maxCoeff()).array() * beta; + // We are separating out the exp function so that exp can be vectorized. + out_mat = out_mat.array().exp(); + // Normalize to get the activations. + Eigen::Array scale = out_mat.array().colwise().sum().inverse(); + out_mat.array().rowwise() *= scale; +} + +bool softmaxFloat32(const float *inputData, const Shape &inputShape, const float beta, + float *outputData, const Shape &outputShape) +{ + Dims<4> dim; + if (getNumberOfDimensions(inputShape) == 2) + { + uint32_t batch_size = getSizeOfDimension(inputShape, 0); + uint32_t input_size = getNumberOfElements(inputShape) / batch_size; + + Shape shapeIn4D; + shapeIn4D.dimensions = {batch_size, 1, 1, input_size}; + dim = convertShapeToDims(shapeIn4D); + } + else if (getNumberOfDimensions(inputShape) == 4) + { + dim = convertShapeToDims(inputShape); + } + else + { + LOG(ERROR) << "only 2D and 4D tensors supported"; + return false; + } + + Softmax(inputData, dim, beta, outputData, dim); + return true; +} diff --git a/compiler/ann-ref/src/ops/Softmax.float.h b/compiler/ann-ref/src/ops/Softmax.float.h new file mode 100644 index 000000000..227b65807 --- /dev/null +++ b/compiler/ann-ref/src/ops/Softmax.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_SOFTMAX_FLOAT_H__ +#define __OP_SOFTMAX_FLOAT_H__ + +#include "Shape.h" + +#include + +bool softmaxFloat32(const float *inputData, const Shape &inputShape, const float beta, + float *outputData, const Shape &outputShape); + +#endif // __OP_SOFTMAX_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Softmax.h b/compiler/ann-ref/src/ops/Softmax.h new file mode 100644 index 000000000..a1e2e9c1b --- /dev/null +++ b/compiler/ann-ref/src/ops/Softmax.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_SOFTMAX_H__ +#define __OP_SOFTMAX_H__ + +#include "Shape.h" + +#include + +bool softmaxPrepare(const Shape &input, Shape *output); + +#endif // __OP_SOFTMAX_H__ diff --git a/compiler/ann-ref/src/ops/Sub.cpp b/compiler/ann-ref/src/ops/Sub.cpp new file mode 100644 index 000000000..accda9127 --- /dev/null +++ b/compiler/ann-ref/src/ops/Sub.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Sub.h" +#include "Assert.h" + +bool subPrepare(const Shape &in1, const Shape &in2, Shape *out) +{ + ASSERT(getNumberOfDimensions(in1) <= 4 && getNumberOfDimensions(in2) <= 4); + ASSERT(in1.type == in2.type); + if (SameShape(in1, in2)) + { + return SetShape(in1, out); + } + else + { + // BroadcastSub needed + uint32_t numberOfDims1 = getNumberOfDimensions(in1); + uint32_t numberOfDims2 = getNumberOfDimensions(in2); + uint32_t maxDims = std::max(numberOfDims1, numberOfDims2); + out->dimensions = std::vector(maxDims); + for (uint32_t i = 1; i <= maxDims; i++) + { + uint32_t dim1 = 1; + if (i <= numberOfDims1) + { + dim1 = getSizeOfDimension(in1, numberOfDims1 - i); + } + uint32_t dim2 = 1; + if (i <= numberOfDims2) + { + dim2 = getSizeOfDimension(in2, numberOfDims2 - i); + } + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) + { + LOG(ERROR) << "Dimensions mismatch for BroadcastSub"; + return false; + } + out->dimensions[maxDims - i] = std::max(dim1, dim2); + } + } + return true; +} diff --git a/compiler/ann-ref/src/ops/Sub.float.cpp b/compiler/ann-ref/src/ops/Sub.float.cpp new file mode 100644 index 000000000..deb5d9855 --- /dev/null +++ b/compiler/ann-ref/src/ops/Sub.float.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Sub.float.h" + +#include "internal/Array.h" +#include "internal/NDArray.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void Sub(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + MatchingArraySize(input1_dims, 3, input2_dims, 3, output_dims, 3); + MatchingArraySize(input1_dims, 2, input2_dims, 2, output_dims, 2); + MatchingArraySize(input1_dims, 1, input2_dims, 1, output_dims, 1); + MatchingArraySize(input1_dims, 0, input2_dims, 0, output_dims, 0); + DCHECK(IsPackedWithoutStrides(input1_dims)); + DCHECK(IsPackedWithoutStrides(input2_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + + int i = 0; + const int size = input1_dims.sizes[3] * input1_dims.strides[3]; + + for (; i < size; i++) + { + auto x = input1_data[i] - input2_data[i]; + output_data[i] = ActivationFunction(x); + } +} + +// From optimized_ops.h in TensorFlow Lite +// +// TODO: We can implement BroadcastSub on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +// TODO: BroadcastSub is intentionally duplicated from +// reference_ops.h. Once an optimized version is implemented and NdArrayDesc +// is no longer referenced in this file, move NdArrayDesc from types.h to +// reference_ops.h. +template +void BroadcastSub(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_dims, input2_dims, &desc1, &desc2); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < ArraySize(output_dims, 3); ++b) + { + for (int y = 0; y < ArraySize(output_dims, 2); ++y) + { + for (int x = 0; x < ArraySize(output_dims, 1); ++x) + { + for (int c = 0; c < ArraySize(output_dims, 0); ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(input1_data[SubscriptToIndex(desc1, c, x, y, b)] - + input2_data[SubscriptToIndex(desc2, c, x, y, b)]); + } + } + } + } +} + +bool subFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut) +{ + bool needBroadcast = !SameShape(shape1, shape2); + +#define ANDROID_NN_NORMAL_SUB(activation) \ + Sub(in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + +#define ANDROID_NN_BROADCAST_SUB(activation) \ + BroadcastSub( \ + in1, convertShapeToDims(shape1), in2, convertShapeToDims(shape2), out, \ + convertShapeToDims(shapeOut)) + + if (needBroadcast) + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_BROADCAST_SUB) + } + else + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_NORMAL_SUB) + } + +#undef ANDROID_NN_NORMAL_SUB +#undef ANDROID_NN_BROADCAST_SUB + return true; +} diff --git a/compiler/ann-ref/src/ops/Sub.float.h b/compiler/ann-ref/src/ops/Sub.float.h new file mode 100644 index 000000000..d494f7576 --- /dev/null +++ b/compiler/ann-ref/src/ops/Sub.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_SUB_FLOAT_H__ +#define __OP_SUB_FLOAT_H__ + +#include "Shape.h" + +#include + +bool subFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut); + +#endif // __OP_SUB_FLOAT_H__ diff --git a/compiler/ann-ref/src/ops/Sub.h b/compiler/ann-ref/src/ops/Sub.h new file mode 100644 index 000000000..d3626205b --- /dev/null +++ b/compiler/ann-ref/src/ops/Sub.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_SUB_H__ +#define __OP_SUB_H__ + +#include "Shape.h" + +bool subPrepare(const Shape &in1, const Shape &in2, Shape *out1); + +#endif // __OP_SUB_H__ diff --git a/compiler/ann-ref/src/ops/internal/ActivationUtils.h b/compiler/ann-ref/src/ops/internal/ActivationUtils.h new file mode 100644 index 000000000..9d413c6a4 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/ActivationUtils.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __ACTIVATION_UTILS_H__ +#define __ACTIVATION_UTILS_H__ + +#include "Logging.h" + +#define ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + case (int32_t)FusedActivationFunc::NONE: \ + macro(kNone); \ + break; \ + case (int32_t)FusedActivationFunc::RELU: \ + macro(kRelu); \ + break; \ + case (int32_t)FusedActivationFunc::RELU1: \ + macro(kRelu1); \ + break; \ + case (int32_t)FusedActivationFunc::RELU6: \ + macro(kRelu6); \ + break; + +#define ANDROID_NN_MACRO_DISPATCH(macro) \ + switch (activation) \ + { \ + ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + default: \ + LOG(ERROR) << "Unsupported fused activation function type"; \ + return false; \ + } + +#define ANDROID_NN_MACRO_DISPATCH_WITH_DELETE(macro) \ + switch (activation) \ + { \ + ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + default: \ + LOG(ERROR) << "Unsupported fused activation function type"; \ + if (im2colByteSize > kStaticBufferSize) \ + { \ + delete[] im2colData; \ + } \ + return false; \ + } + +#endif // __ACTIVATION_UTILS_H__ diff --git a/compiler/ann-ref/src/ops/internal/Array.h b/compiler/ann-ref/src/ops/internal/Array.h new file mode 100644 index 000000000..49a3e771b --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Array.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ARRAY_H__ +#define __ARRAY_H__ + +#include "Shape.h" +#include "Dims.h" + +#include "Macro.h" + +// From types.h in TensorFlow Lite +// +// Get common array size, DCHECKing that they all agree. +template +int MatchingArraySize(const ArrayType1 &array1, int index1, const ArrayType2 &array2, int index2) +{ + DCHECK_EQ(ArraySize(array1, index1), ArraySize(array2, index2)); + return ArraySize(array1, index1); +} + +// From types.h in TensorFlow Lite +template +int MatchingArraySize(const ArrayType1 &array1, int index1, const ArrayType2 &array2, int index2, + Args... args) +{ + DCHECK_EQ(ArraySize(array1, index1), ArraySize(array2, index2)); + return MatchingArraySize(array1, index1, args...); +} + +#endif // __ARRAY_H__ diff --git a/compiler/ann-ref/src/ops/internal/Dims.h b/compiler/ann-ref/src/ops/internal/Dims.h new file mode 100644 index 000000000..2b3aaa65a --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Dims.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DIMS_H__ +#define __DIMS_H__ + +#include "Shape.h" +#include "Macro.h" + +template struct Dims +{ + int sizes[N]; + int strides[N]; +}; + +inline Dims<4> convertShapeToDims(const Shape &shape) +{ + Dims<4> dims; + for (int i = 0; i < 4; i++) + { + dims.sizes[i] = 1; + } + + if (shape.dimensions.size() == 1) + { + dims.sizes[0] = (int)getSizeOfDimension(shape, 0); + } + else + { + for (int i = 0; i < 4; i++) + { + int src = (int)shape.dimensions.size() - i - 1; + if (src >= 0) + { + dims.sizes[i] = (int)getSizeOfDimension(shape, src); + } + } + } + + dims.strides[0] = 1; + for (int i = 1; i < 4; i++) + { + dims.strides[i] = dims.strides[i - 1] * dims.sizes[i - 1]; + } + return dims; +} + +// From types.h in TensorFlow Lite +inline int Offset(const Dims<4> &dims, int i0, int i1, int i2, int i3) +{ + DCHECK(i0 >= 0 && i0 < dims.sizes[0]); + DCHECK(i1 >= 0 && i1 < dims.sizes[1]); + DCHECK(i2 >= 0 && i2 < dims.sizes[2]); + DCHECK(i3 >= 0 && i3 < dims.sizes[3]); + return i0 * dims.strides[0] + i1 * dims.strides[1] + i2 * dims.strides[2] + i3 * dims.strides[3]; +} + +// From types.h in TensorFlow Lite +// +// Get array size, DCHECKing that the dim index is in range. +template int ArraySize(const Dims &array, int index) +{ + DCHECK(index >= 0 && index < N); + return array.sizes[index]; +} + +// From types.h in TensorFlow Lite +template inline int FlatSize(const Dims &dims) +{ + int flat_size = 1; + for (int i = 0; i < N; ++i) + { + flat_size *= dims.sizes[i]; + } + return flat_size; +} + +// From types.h in TensorFlow Lite +inline int RequiredBufferSizeForDims(const Dims<4> &dims) +{ + int max_offset = 0; + for (int i = 0; i < 4; i++) + { + max_offset += (dims.sizes[i] - 1) * dims.strides[i]; + } + return max_offset + 1; +} + +// From types.h in TensorFlow Lite +// +// Flat size calculation, checking that dimensions match with one or more other +// arrays. +template inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims); +} + +// From types.h in TensorFlow Lite +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return MatchingFlatSize(dims, check_dims_1); +} + +// From types.h in TensorFlow Lite +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1, const Dims &check_dims_2) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims, check_dims_1, check_dims_2); +} + +// From types.h in TensorFlow Lite +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1, const Dims &check_dims_2, + const Dims &check_dims_3) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims, check_dims_1, check_dims_2, check_dims_3); +} + +// From types.h in TensorFlow Lite +template bool IsPackedWithoutStrides(const Dims &dims) +{ + int expected_stride = 1; + for (int d = 0; d < N; d++) + { + if (dims.strides[d] != expected_stride) + return false; + expected_stride *= dims.sizes[d]; + } + return true; +} + +#endif // __DIMS_H__ diff --git a/compiler/ann-ref/src/ops/internal/Elementwise.cpp b/compiler/ann-ref/src/ops/internal/Elementwise.cpp new file mode 100644 index 000000000..5615e309d --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Elementwise.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Elementwise.h" +#include "Assert.h" + +bool genericActivationPrepare(const Shape &input, Shape *output) +{ + ASSERT(getNumberOfDimensions(input) <= 4); + return SetShape(input, output); +} diff --git a/compiler/ann-ref/src/ops/internal/Elementwise.h b/compiler/ann-ref/src/ops/internal/Elementwise.h new file mode 100644 index 000000000..732f9b8a2 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Elementwise.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __ELEMENTWISE_H__ +#define __ELEMENTWISE_H__ + +#include "Shape.h" + +bool genericActivationPrepare(const Shape &input, Shape *output); + +#endif // __ELEMENTWISE_H__ diff --git a/compiler/ann-ref/src/ops/internal/FeatureMap.h b/compiler/ann-ref/src/ops/internal/FeatureMap.h new file mode 100644 index 000000000..e4d323f62 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/FeatureMap.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __FEATURE_MAP_H__ +#define __FEATURE_MAP_H__ + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +#endif // __FEATURE_MAP_H__ diff --git a/compiler/ann-ref/src/ops/internal/Fused.cpp b/compiler/ann-ref/src/ops/internal/Fused.cpp new file mode 100644 index 000000000..c50b9dea0 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Fused.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Fused.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(FusedActivationFunc::NONE) == ANEURALNETWORKS_FUSED_NONE, + "FusedActivationFunc::NONE != ANEURALNETWORKS_FUSED_NONE"); +static_assert(static_cast(FusedActivationFunc::RELU) == ANEURALNETWORKS_FUSED_RELU, + "FusedActivationFunc::RELU != ANEURALNETWORKS_FUSED_RELU"); +static_assert(static_cast(FusedActivationFunc::RELU1) == ANEURALNETWORKS_FUSED_RELU1, + "FusedActivationFunc::RELU1 != ANEURALNETWORKS_FUSED_RELU1"); +static_assert(static_cast(FusedActivationFunc::RELU6) == ANEURALNETWORKS_FUSED_RELU6, + "FusedActivationFunc::RELU6 != ANEURALNETWORKS_FUSED_RELU6"); diff --git a/compiler/ann-ref/src/ops/internal/Fused.h b/compiler/ann-ref/src/ops/internal/Fused.h new file mode 100644 index 000000000..fccd72cc3 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Fused.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __FUSED_H__ +#define __FUSED_H__ + +#include "Dims.h" + +#include + +enum class FusedActivationFunc : int32_t { + NONE = 0, + RELU = 1, + RELU1 = 2, + RELU6 = 3, +}; + +enum class FusedActivationFunctionType +{ + kNone, + kRelu6, + kRelu1, + kRelu +}; + +template struct ActivationFunctionImpl; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x < 0.f ? 0.f : x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x > 1.f ? 1.f : x < -1.f ? -1.f : x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x > 6.f ? 6.f : x < 0.f ? 0.f : x; } +}; + +template float ActivationFunction(float x) +{ + return ActivationFunctionImpl::Eval(x); +} + +template +void AddBiasAndEvalActivationFunction(const float *bias_data, const Dims<4> &bias_dims, + float *array_data, const Dims<4> &array_dims) +{ + const int bias_size = bias_dims.sizes[3] * bias_dims.strides[3]; + const int array_size = array_dims.sizes[3] * array_dims.strides[3]; + DCHECK_EQ((array_size % bias_size), 0); + for (int array_offset = 0; array_offset < array_size; array_offset += bias_size) + { + for (int i = 0; i < bias_size; i++) + { + array_data[array_offset + i] = + ActivationFunction(array_data[array_offset + i] + bias_data[i]); + } + } +} + +#endif // __FUSED_H__ diff --git a/compiler/ann-ref/src/ops/internal/GEMM.h b/compiler/ann-ref/src/ops/internal/GEMM.h new file mode 100644 index 000000000..e94b35855 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/GEMM.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __GEMM_H__ +#define __GEMM_H__ + +#include "Eigen/Core" + +template +void Gemm(const Eigen::MatrixBase &lhs, const Eigen::MatrixBase &rhs, + Eigen::MatrixBase *result) +{ + if (rhs.cols() == 1) + { + result->col(0).noalias() = lhs * rhs.col(0); + } + else + { + result->noalias() = lhs * rhs; + } +} + + +#endif // __GEMM_H__ diff --git a/compiler/ann-ref/src/ops/internal/Macro.h b/compiler/ann-ref/src/ops/internal/Macro.h new file mode 100644 index 000000000..b80a748bb --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Macro.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __COMPATIBILITY_H__ +#define __COMPATIBILITY_H__ + +#include +#include + +#ifndef DCHECK +#define DCHECK(condition) assert((condition)) +#endif + +#ifndef DCHECK_EQ +#define DCHECK_EQ(x, y) assert((x) == (y)) +#endif + +#ifndef DCHECK_GE +#define DCHECK_GE(x, y) assert((x) >= (y)) +#endif + +#ifndef DCHECK_GT +#define DCHECK_GT(x, y) assert((x) > (y)) +#endif + +#ifndef DCHECK_LE +#define DCHECK_LE(x, y) assert((x) <= (y)) +#endif + +#ifndef DCHECK_LT +#define DCHECK_LT(x, y) assert((x) < (y)) +#endif + +#ifndef CHECK_EQ +#define CHECK_EQ(x, y) assert((x) == (y)) +#endif + +using uint8 = std::uint8_t; +using int16 = std::int16_t; +using uint16 = std::uint16_t; +using int32 = std::int32_t; +using uint32 = std::uint32_t; + +#endif // __COMPATIBILITY_H__ diff --git a/compiler/ann-ref/src/ops/internal/Matrix.h b/compiler/ann-ref/src/ops/internal/Matrix.h new file mode 100644 index 000000000..71b1fc5d7 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Matrix.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MATRIX_H__ +#define __MATRIX_H__ + +#include "Dims.h" +#include "Eigen/Core" + +// From optimized_ops.h in TensorFlow Lite +// +// Make a local VectorMap typedef allowing to map a float array +// as a Eigen vector expression. The std::conditional here is to +// construct the suitable Eigen type for the constness of the +// data. Indeed, for const data, we need to produce +// Eigen::Map> +// and not the more straightforward +// Eigen::Map> +template +using VectorMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, 1>>, + Eigen::Map>>::type; + +template VectorMap MapAsVector(Scalar *data, const Dims &dims) +{ + const int size = RequiredBufferSizeForDims(dims); + return VectorMap(data, size, 1); +} + +// From optimized_ops.h in TensorFlow Lite +// +// Make a local VectorMap typedef allowing to map a float array +// as a Eigen matrix expression. The same explanation as for VectorMap +// above also applies here. +template +using MatrixMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, + Eigen::Dynamic>>, + Eigen::Map>>::type; + +// From optimized_ops.h in TensorFlow Lite +template +MatrixMap MapAsMatrixWithFirstDimAsRows(Scalar *data, const Dims &dims) +{ + const int rows = dims.sizes[0]; + int cols = 1; + for (int d = 1; d < N; d++) + { + cols *= dims.sizes[d]; + } + return MatrixMap(data, rows, cols); +} + +// From optimized_ops.h in TensorFlow Lite +template +MatrixMap MapAsMatrixWithLastDimAsCols(Scalar *data, const Dims &dims) +{ + const int cols = dims.sizes[N - 1]; + int rows = 1; + for (int d = 0; d < N - 1; d++) + { + rows *= dims.sizes[d]; + } + return MatrixMap(data, rows, cols); +} + +// From optimized_ops.h in TensorFlow Lite +template +using ArrayMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, + Eigen::Dynamic>>, + Eigen::Map>>::type; + +// From optimized_ops.h in TensorFlow Lite +template +ArrayMap MapAsArrayWithFirstDimAsRows(Scalar *data, const Dims &dims) +{ + const int rows = dims.sizes[0]; + int cols = 1; + for (int d = 1; d < N; d++) + { + cols *= dims.sizes[d]; + } + return ArrayMap(data, rows, cols); +} + +// From optimized_ops.h in TensorFlow Lite +// +// TODO(b/62193649): this function is only needed as long +// as we have the --variable_batch hack. +template +MatrixMap MapAsMatrixWithGivenNumberOfRows(Scalar *data, const Dims &dims, int rows) +{ + int cols = 1; + bool matched_rows = false; + for (int d = 0; d < N; d++) + { + cols *= dims.sizes[d]; + if (cols == rows) + { + matched_rows = true; + cols = 1; + } + } + DCHECK(matched_rows); + return MatrixMap(data, rows, cols); +} + +#endif // __MATRIX_H__ diff --git a/compiler/ann-ref/src/ops/internal/NDArray.h b/compiler/ann-ref/src/ops/internal/NDArray.h new file mode 100644 index 000000000..14b160469 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/NDArray.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ND_ARRAY_H__ +#define __ND_ARRAY_H__ + +#include "Dims.h" +#include "Macro.h" + +// From optimized_ops.h in TensorFlow Lite +// +// DO NOT USE THIS STRUCT FOR NEW FUNCTIONALITY BEYOND IMPLEMENTING ELEMENT-WISE +// BROADCASTING. +// +// NdArrayDesc describes the shape and memory layout of an N-dimensional +// rectangular array of numbers. +// +// NdArrayDesc is basically identical to Dims defined in types.h. +// However, as Dims is to be deprecated, this class exists as an adaptor +// to enable simple unoptimized implementations of element-wise broadcasting +// operations. +template struct NdArrayDesc +{ + // The "extent" of each dimension. Indices along dimension d must be in the + // half-open interval [0, extents[d]). + int extents[N]; + + // The number of *elements* (not bytes) between consecutive indices of each + // dimension. + int strides[N]; +}; + +// From optimized_ops.h in TensorFlow Lite +// +// DO NOT USE THIS FUNCTION FOR NEW FUNCTIONALITY BEYOND IMPLEMENTING +// ELEMENT-WISE BROADCASTING. +// +// Same as Offset(), except takes as NdArrayDesc instead of Dims. +inline int SubscriptToIndex(const NdArrayDesc<4> &desc, int i0, int i1, int i2, int i3) +{ + DCHECK(i0 >= 0 && i0 < desc.extents[0]); + DCHECK(i1 >= 0 && i1 < desc.extents[1]); + DCHECK(i2 >= 0 && i2 < desc.extents[2]); + DCHECK(i3 >= 0 && i3 < desc.extents[3]); + return i0 * desc.strides[0] + i1 * desc.strides[1] + i2 * desc.strides[2] + i3 * desc.strides[3]; +} + +// From optimized_ops.h in TensorFlow Lite +// +// Given the dimensions of the operands for an element-wise binary broadcast, +// adjusts them so that they can be directly iterated over with simple loops. +// Returns the adjusted dims as instances of NdArrayDesc in 'desc0_out' and +// 'desc1_out'. 'desc0_out' and 'desc1_out' cannot be nullptr. +// +// This function assumes that the two input shapes are compatible up to +// broadcasting and the shorter one has already been prepended with 1s to be the +// same length. E.g., if shape0 is (1, 16, 16, 64) and shape1 is (1, 64), +// shape1 must already have been prepended to be (1, 1, 1, 64). Recall that +// Dims refer to shapes in reverse order. In this case, input0_dims will be +// (64, 16, 16, 1) and input1_dims will be (64, 1, 1, 1). +// +// When two shapes are compatible up to broadcasting, for each dimension d, +// the input extents are either equal, or one of them is 1. +// +// This function performs the following for each dimension d: +// - If the extents are equal, then do nothing since the loop that walks over +// both of the input arrays is correct. +// - Otherwise, one (and only one) of the extents must be 1. Say extent0 is 1 +// and extent1 is e1. Then set extent0 to e1 and stride0 *to 0*. This allows +// array0 to be referenced *at any index* in dimension d and still access the +// same slice. +template +inline void +NdArrayDescsForElementwiseBroadcast(const Dims &input0_dims, const Dims &input1_dims, + NdArrayDesc *desc0_out, NdArrayDesc *desc1_out) +{ + DCHECK(desc0_out != nullptr); + DCHECK(desc1_out != nullptr); + + // Copy dims to desc. + for (int i = 0; i < N; ++i) + { + desc0_out->extents[i] = input0_dims.sizes[i]; + desc0_out->strides[i] = input0_dims.strides[i]; + desc1_out->extents[i] = input1_dims.sizes[i]; + desc1_out->strides[i] = input1_dims.strides[i]; + } + + // Walk over each dimension. If the extents are equal do nothing. + // Otherwise, set the desc with extent 1 to have extent equal to the other and + // stride 0. + for (int i = 0; i < N; ++i) + { + const int extent0 = ArraySize(input0_dims, i); + const int extent1 = ArraySize(input1_dims, i); + if (extent0 != extent1) + { + if (extent0 == 1) + { + desc0_out->strides[i] = 0; + desc0_out->extents[i] = extent1; + } + else + { + DCHECK_EQ(extent1, 1); + desc1_out->strides[i] = 0; + desc1_out->extents[i] = extent0; + } + } + } +} + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +#endif // __ND_ARRAY_H__ diff --git a/compiler/ann-ref/src/ops/internal/Pooling.cpp b/compiler/ann-ref/src/ops/internal/Pooling.cpp new file mode 100644 index 000000000..a3b8cf326 --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Pooling.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Pooling.h" +#include "Spatial.h" + +#include "Assert.h" + +bool genericPoolingPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + ASSERT(getNumberOfDimensions(input) == 4); + + uint32_t batches = getSizeOfDimension(input, 0); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t channels_out = getSizeOfDimension(input, 3); + + uint32_t outWidth = + computeOutSize(width, filter_width, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filter_height, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/compiler/ann-ref/src/ops/internal/Pooling.h b/compiler/ann-ref/src/ops/internal/Pooling.h new file mode 100644 index 000000000..c55bc16cd --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Pooling.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __POOLING_H__ +#define __POOLING_H__ + +#include "Shape.h" + +#include + +bool genericPoolingPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + + +#endif // __POOLING_H__ diff --git a/compiler/ann-ref/src/ops/internal/Spatial.h b/compiler/ann-ref/src/ops/internal/Spatial.h new file mode 100644 index 000000000..6b8f0c11f --- /dev/null +++ b/compiler/ann-ref/src/ops/internal/Spatial.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __SPATIAL_H__ +#define __SPATIAL_H__ + +#include + +inline uint32_t computeOutSize(uint32_t imageSize, uint32_t filterSize, uint32_t stride, + uint32_t paddingHead, uint32_t paddingTail) +{ + return (imageSize - filterSize + stride + paddingHead + paddingTail) / stride; +} + +#endif // __SPATIAL_H__ diff --git a/compiler/bino/CMakeLists.txt b/compiler/bino/CMakeLists.txt new file mode 100644 index 000000000..519eecdc8 --- /dev/null +++ b/compiler/bino/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(bino INTERFACE) +target_include_directories(bino INTERFACE include) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +file(GLOB_RECURSE TESTS "tests/*.cpp") + +GTest_AddTest(bino_test ${TESTS}) +target_link_libraries(bino_test bino) diff --git a/compiler/bino/README.md b/compiler/bino/README.md new file mode 100644 index 000000000..9d58c725d --- /dev/null +++ b/compiler/bino/README.md @@ -0,0 +1,5 @@ +# bino + +Let's manipulate std::pair values with UNIX pipe-like syntax. + +**NOTE** The _bino_ originates from a binocular telescope. diff --git a/compiler/bino/include/bino.h b/compiler/bino/include/bino.h new file mode 100644 index 000000000..fc22d1285 --- /dev/null +++ b/compiler/bino/include/bino.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BINO_H__ +#define __BINO_H__ + +#include + +namespace bino +{ + +template class UniformTransform +{ +public: + UniformTransform(Callable &&cb) : f{std::forward(cb)} + { + // DO NOTHING + } + +public: + template + auto operator()(const std::pair &p) const + -> decltype(std::make_pair(std::declval()(p.first), + std::declval()(p.second))) + { + return std::make_pair(f(p.first), f(p.second)); + } + +private: + Callable f; +}; + +template UniformTransform transform_both(Callable &&f) +{ + return UniformTransform{std::forward(f)}; +} + +// TODO Implement transform_both(f, g) +// TODO Implement transform_first(f) +// TODO Implement transform_second(f) + +} // namespace bino + +#endif // __BINO_H__ diff --git a/compiler/bino/tests/Functional.tests.cpp b/compiler/bino/tests/Functional.tests.cpp new file mode 100644 index 000000000..14dde6a45 --- /dev/null +++ b/compiler/bino/tests/Functional.tests.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Let's test functionals in "bino". + * + * NOTE The tests in this file assume that operator overloading works well. + */ + +#include "bino.h" + +#include + +TEST(FunctionalTests, transform_both_uniform) +{ + auto inc = [](int n) { return n + 1; }; + auto f = bino::transform_both(inc); + auto res = f(std::make_pair(1, 3)); + + ASSERT_EQ(res.first, 2); + ASSERT_EQ(res.second, 4); +} diff --git a/compiler/caffe2circle/CMakeLists.txt b/compiler/caffe2circle/CMakeLists.txt new file mode 100644 index 000000000..eaf541705 --- /dev/null +++ b/compiler/caffe2circle/CMakeLists.txt @@ -0,0 +1,16 @@ +if(NOT TARGET mir_caffe_importer) + return() +endif() + +if(NOT TARGET mir2loco) + return() +endif() + +if(NOT TARGET exo) + return() +endif() + +message(STATUS "Build caffe2circle: TRUE") + +add_executable(caffe2circle src/caffe2circle.cpp) +target_link_libraries(caffe2circle PRIVATE mir_caffe_importer mir2loco exo) diff --git a/compiler/caffe2circle/README.md b/compiler/caffe2circle/README.md new file mode 100644 index 000000000..fe9ea26dd --- /dev/null +++ b/compiler/caffe2circle/README.md @@ -0,0 +1,3 @@ +# caffe2circle + +_caffe2circle_ is a Caffe-to-Circle model converter. diff --git a/compiler/caffe2circle/requires.cmake b/compiler/caffe2circle/requires.cmake new file mode 100644 index 000000000..cc05edd84 --- /dev/null +++ b/compiler/caffe2circle/requires.cmake @@ -0,0 +1,3 @@ +require("mir-onnx-importer") +require("mir2loco") +require("exo") diff --git a/compiler/caffe2circle/src/caffe2circle.cpp b/compiler/caffe2circle/src/caffe2circle.cpp new file mode 100644 index 000000000..fb09c0a1c --- /dev/null +++ b/compiler/caffe2circle/src/caffe2circle.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + std::cerr << "Usage: caffe2circle \n"; + return EXIT_FAILURE; + } + + const char *caffe_path = argv[1]; + const char *circle_path = argv[2]; + + std::unique_ptr mir_graph = mir_caffe::importModelFromBinaryFile(caffe_path); + std::unique_ptr loco_graph = mir2loco::Transformer().transform(mir_graph.get()); + exo::CircleExporter(loco_graph.get()).dumpToFile(circle_path); + return EXIT_SUCCESS; +} diff --git a/compiler/caffegen/CMakeLists.txt b/compiler/caffegen/CMakeLists.txt new file mode 100644 index 000000000..334174dcd --- /dev/null +++ b/compiler/caffegen/CMakeLists.txt @@ -0,0 +1,14 @@ +nnas_find_package(Caffe QUIET) + +if(NOT Caffe_FOUND) + return() +endif(NOT Caffe_FOUND) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(caffegen ${SOURCES}) +target_link_libraries(caffegen stdex) +target_link_libraries(caffegen cli) +# NOTE "Caffe" package provides both caffe and caffeproto target +# NOTE "caffeproto" is linked to "caffe" +target_link_libraries(caffegen caffe) diff --git a/compiler/caffegen/README.md b/compiler/caffegen/README.md new file mode 100644 index 000000000..c322721b3 --- /dev/null +++ b/compiler/caffegen/README.md @@ -0,0 +1,45 @@ +# caffegen + +`caffegen` is a tool for generating caffe model and decoding binary file of caffe model + +## How caffegen works + +Some of commands in `caffegen` use standard input for reading data and standard output for exporting result. +In this case, we strongly recommand you to use pipe, not copy & paste the content of file itself. + +Otherwise, `caffegen` use arguments to pass some directories. + +## Supported command + +Basically, caffgen command is used as `caffegen [COMMAND]` and there are four `COMMAND` types. + - init : initialize parameters using prototxt. + - encode : make a binary file(caffemodel) using initialized data + - decode : decode a binary file(caffemodel) and reproduce the initialized data + - merge : copy the trained weights from a caffemodel into a prototxt file + +## How to use each command + +1. Init (Using stdin and stdout) + - `./build/compiler/caffegen/caffegen init` + - Type the prototxt by yourself + - Then you can get the result on the shell. + - `cat ./res/BVLCCaffeTests/Convolution_000/test.prototxt | ./build/compiler/caffegen/caffegen init` + - Prototxt will be automatically passed + - Then you can get the result on the shell. + +2. Encode (Using stdin and stdout) + - `./build/compiler/caffegen/caffegen encode` + - Type the initialized data by yourself + - Then you can get the result on the shell. + - `cat ./res/BVLCCaffeTests/Convolution_000/test.prototxt | ./build/compiler/caffegen/caffegen init | ./build/compiler/caffegen/caffegen encode > Convolution_000.caffemodel` + - The initialized data will be automatically passed + - The encoded result will be automatically saved in caffemodel file + +3. Decode (Using stdin and stdout) + - `cat Convolution_000.caffemodel | ./build/compiler/caffegen/caffegen decode` + - Caffemodel file will be automatically passed + - Then you can get the result on the shell + +4. Merge (Using arguments) + - `./build/compiler/caffegen/caffegen merge ./res/BVLCCaffeTests/Convolution_000/test.prototxt Convolution_000.caffemodel` + - `./build/compiler/caffegen/caffegen merge ./res/BVLCCaffeTests/Convolution_000/test.prototxt Convolution_000.caffemodel > Convolution_000.merged` diff --git a/compiler/caffegen/src/DecodeCommand.cpp b/compiler/caffegen/src/DecodeCommand.cpp new file mode 100644 index 000000000..02d044ed3 --- /dev/null +++ b/compiler/caffegen/src/DecodeCommand.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DecodeCommand.h" + +#include + +#include +#include +#include + +#include + +int DecodeCommand::run(int, const char *const *) const +{ + caffe::NetParameter param; + + // Load binary from standard input + google::protobuf::io::IstreamInputStream is{&std::cin}; + google::protobuf::io::CodedInputStream coded_is{&is}; + + if (!param.ParseFromCodedStream(&coded_is)) + { + std::cerr << "ERROR: Failed to parse caffemodel" << std::endl; + return 255; + } + + // Write text into standard output + google::protobuf::io::OstreamOutputStream os{&std::cout}; + google::protobuf::TextFormat::Print(param, &os); + + return 0; +} diff --git a/compiler/caffegen/src/DecodeCommand.h b/compiler/caffegen/src/DecodeCommand.h new file mode 100644 index 000000000..4b43b465b --- /dev/null +++ b/compiler/caffegen/src/DecodeCommand.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DECODE_COMMAND_H__ +#define __DECODE_COMMAND_H__ + +#include + +struct DecodeCommand final : public cli::Command +{ + int run(int argc, const char *const *argv) const override; +}; + +#endif // __DECODE_COMMAND_H__ diff --git a/compiler/caffegen/src/Driver.cpp b/compiler/caffegen/src/Driver.cpp new file mode 100644 index 000000000..81b01e6f1 --- /dev/null +++ b/compiler/caffegen/src/Driver.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InitCommand.h" +#include "EncodeCommand.h" +#include "DecodeCommand.h" +#include "MergeCommand.h" + +#include +#include + +#include +#include + +using stdex::make_unique; + +int main(int argc, char **argv) +{ + cli::App app{argv[0]}; + + // all receive data from stdin + app.insert("init", make_unique()); + app.insert("encode", make_unique()); + app.insert("decode", make_unique()); + // takes 2 args: prototxt model and caffemodel weights in that order + app.insert("merge", make_unique()); + + return app.run(argc - 1, argv + 1); +} diff --git a/compiler/caffegen/src/EncodeCommand.cpp b/compiler/caffegen/src/EncodeCommand.cpp new file mode 100644 index 000000000..4b35030bd --- /dev/null +++ b/compiler/caffegen/src/EncodeCommand.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EncodeCommand.h" + +#include + +#include +#include +#include + +#include + +int EncodeCommand::run(int, const char *const *) const +{ + caffe::NetParameter param; + + // Load text from standard input + google::protobuf::io::IstreamInputStream is{&std::cin}; + + if (!google::protobuf::TextFormat::Parse(&is, ¶m)) + { + std::cerr << "ERROR: Failed to parse prototxt" << std::endl; + return 255; + } + + // Write binary into standard output + google::protobuf::io::OstreamOutputStream os{&std::cout}; + google::protobuf::io::CodedOutputStream coded_os{&os}; + + if (!param.SerializeToCodedStream(&coded_os)) + { + std::cerr << "ERROR: Failed to serialize" << std::endl; + return 255; + } + + return 0; +} diff --git a/compiler/caffegen/src/EncodeCommand.h b/compiler/caffegen/src/EncodeCommand.h new file mode 100644 index 000000000..1115c2363 --- /dev/null +++ b/compiler/caffegen/src/EncodeCommand.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCODE_COMMAND_H__ +#define __ENCODE_COMMAND_H__ + +#include + +struct EncodeCommand final : public cli::Command +{ + int run(int argc, const char *const *argv) const override; +}; + +#endif // __ENCODE_COMMAND_H__ diff --git a/compiler/caffegen/src/InitCommand.cpp b/compiler/caffegen/src/InitCommand.cpp new file mode 100644 index 000000000..fd5b8a467 --- /dev/null +++ b/compiler/caffegen/src/InitCommand.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InitCommand.h" + +#include +#include +#include + +#include +#include +#include + +#include + +int InitCommand::run(int, const char *const *) const +{ + // Read prototxt from standard input + ::caffe::NetParameter in; + { + google::protobuf::io::IstreamInputStream is{&std::cin}; + if (!google::protobuf::TextFormat::Parse(&is, &in)) + { + std::cerr << "ERROR: Failed to parse prototxt" << std::endl; + return 255; + } + } + + // Upgrade prototxt if necessary + if (::caffe::NetNeedsUpgrade(in)) + { + if (!::caffe::UpgradeNetAsNeeded("", &in)) + { + std::cerr << "ERROR: Failed to upgrade prototxt" << std::endl; + return 255; + } + } + + ::caffe::Net net(in); + + // Extract initialized parameters + ::caffe::NetParameter out; + { + net.ToProto(&out); + } + + // Write initialized parameters to standard output + google::protobuf::io::OstreamOutputStream os{&std::cout}; + google::protobuf::TextFormat::Print(out, &os); + + return 0; +} diff --git a/compiler/caffegen/src/InitCommand.h b/compiler/caffegen/src/InitCommand.h new file mode 100644 index 000000000..8d86a195f --- /dev/null +++ b/compiler/caffegen/src/InitCommand.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __INIT_COMMAND_H__ +#define __INIT_COMMAND_H__ + +#include + +struct InitCommand final : public cli::Command +{ + int run(int argc, const char *const *argv) const override; +}; + +#endif // __INIT_COMMAND_H__ diff --git a/compiler/caffegen/src/MergeCommand.cpp b/compiler/caffegen/src/MergeCommand.cpp new file mode 100644 index 000000000..4e1d863bc --- /dev/null +++ b/compiler/caffegen/src/MergeCommand.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MergeCommand.h" + +#include +#include + +#include +#include +#include + +#include +#include + +int MergeCommand::run(int argc, const char *const *argv) const +{ + if (argc != 2) + { + std::cerr << "ERROR: this command requires exactly 2 arguments" << std::endl; + return 254; + } + + std::string model_file = argv[0]; + std::string trained_file = argv[1]; + + // Load the network + caffe::Net caffe_test_net(model_file, caffe::TEST); + // Load the weights + caffe_test_net.CopyTrainedLayersFrom(trained_file); + + caffe::NetParameter net_param; + caffe_test_net.ToProto(&net_param); + + // Write binary with initialized params into standard output + google::protobuf::io::OstreamOutputStream os(&std::cout); + google::protobuf::io::CodedOutputStream coded_os{&os}; + + if (!net_param.SerializeToCodedStream(&coded_os)) + { + std::cerr << "ERROR: Failed to serialize" << std::endl; + return 255; + } + return 0; +} diff --git a/compiler/caffegen/src/MergeCommand.h b/compiler/caffegen/src/MergeCommand.h new file mode 100644 index 000000000..e0134626c --- /dev/null +++ b/compiler/caffegen/src/MergeCommand.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MERGE_COMMAND_H__ +#define __MERGE_COMMAND_H__ + +#include + +/** + * @brief Takes .prototxt and .caffemodel filenames from ARGV + * and fills the model with trained weights. + * The resulting binary model with weights to be consumed by nnc is printed to StdOut + * @return error code + */ +struct MergeCommand final : public cli::Command +{ + int run(int argc, const char *const *argv) const override; +}; + +#endif //__MERGE_COMMAND_H__ diff --git a/compiler/circle-inspect/CMakeLists.txt b/compiler/circle-inspect/CMakeLists.txt new file mode 100644 index 000000000..222f8cb1a --- /dev/null +++ b/compiler/circle-inspect/CMakeLists.txt @@ -0,0 +1,13 @@ +if(NOT TARGET mio_circle) + return() +endif(NOT TARGET mio_circle) + +set(DRIVER "driver/Driver.cpp") + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(circle-inspect ${DRIVER} ${SOURCES}) +target_include_directories(circle-inspect PRIVATE src) +target_link_libraries(circle-inspect mio_circle) +target_link_libraries(circle-inspect safemain) +target_link_libraries(circle-inspect stdex) diff --git a/compiler/circle-inspect/README.md b/compiler/circle-inspect/README.md new file mode 100644 index 000000000..1f76c8ede --- /dev/null +++ b/compiler/circle-inspect/README.md @@ -0,0 +1,22 @@ +# circle-inspect + +_circle-inspect_ allows users to retrieve various information from a Circle model file + +## Information to inspect + +Operators with `--operators` +- show operator codes one line at a time in execution order + +Example +``` +$ circle-inspect --operators model.circle +``` + +Result +``` +RESHAPE +DEPTHWISE_CONV_2D +ADD +``` + +To get the count of specific operator, use other tools like sort, uniq, etc. diff --git a/compiler/circle-inspect/driver/Driver.cpp b/compiler/circle-inspect/driver/Driver.cpp new file mode 100644 index 000000000..d23cd0f8b --- /dev/null +++ b/compiler/circle-inspect/driver/Driver.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Model.h" +#include "Dump.h" + +#include + +#include +#include +#include +#include +#include + +using OptionHook = std::function(void)>; + +int entry(int argc, char **argv) +{ + if (argc < 3) + { + std::cerr << "ERROR: Failed to parse arguments" << std::endl; + std::cerr << std::endl; + std::cerr << "USAGE: " << argv[0] << " [options] [circle]" << std::endl; + std::cerr << " --operators : dump operators in circle file" << std::endl; + std::cerr << " --conv2d_weight : dump Conv2D series weight operators in circle file" + << std::endl; + return 255; + } + + // Simple argument parser (based on map) + std::map argparse; + + argparse["--operators"] = [&](void) { + // dump all operators + return std::move(stdex::make_unique()); + }; + + argparse["--conv2d_weight"] = [&](void) { + // dump Conv2D, DepthwiseConv2D weight operators + return std::move(stdex::make_unique()); + }; + + std::vector> dumps; + + for (int n = 1; n < argc - 1; ++n) + { + const std::string tag{argv[n]}; + + auto it = argparse.find(tag); + if (it == argparse.end()) + { + std::cerr << "Option '" << tag << "' is not supported" << std::endl; + return 255; + } + auto dump = it->second(); + assert(dump != nullptr); + dumps.push_back(std::move(dump)); + } + + std::string model_file = argv[argc - 1]; + + // Load Circle model from a circle file + auto model = circleinspect::load_circle(model_file); + if (model == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << model_file << "'" << std::endl; + return 255; + } + + const circle::Model *circlemodel = model->model(); + if (circlemodel == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << model_file << "'" << std::endl; + return 255; + } + + for (auto &dump : dumps) + { + dump->run(std::cout, circlemodel); + } + + return 0; +} diff --git a/compiler/circle-inspect/requires.cmake b/compiler/circle-inspect/requires.cmake new file mode 100644 index 000000000..b090dbd4d --- /dev/null +++ b/compiler/circle-inspect/requires.cmake @@ -0,0 +1,3 @@ +require("mio-circle") +require("safemain") +require("stdex") diff --git a/compiler/circle-inspect/src/Dump.cpp b/compiler/circle-inspect/src/Dump.cpp new file mode 100644 index 000000000..fbc092b89 --- /dev/null +++ b/compiler/circle-inspect/src/Dump.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dump.h" +#include "Reader.h" + +#include + +namespace circleinspect +{ + +void DumpOperators::run(std::ostream &os, const circle::Model *model) +{ + circleinspect::Reader reader(model); + + assert(reader.num_subgraph() == 1); + reader.select_subgraph(0); + + auto ops = reader.operators(); + + // dump operators + for (uint32_t i = 0; i < ops->Length(); ++i) + { + const auto op = ops->Get(i); + + auto op_name = reader.opcode_name(op); + + os << op_name << std::endl; + } +} + +} // namespace circleinspect + +namespace +{ + +const circle::Operator *operator_match_output(circleinspect::Reader &reader, const int32_t tensor) +{ + auto ops = reader.operators(); + + for (uint32_t i = 0; i < ops->Length(); ++i) + { + const auto op = ops->Get(i); + + const std::vector &outputs = circleinspect::as_index_vector(op->outputs()); + + for (auto output : outputs) + { + if (output == tensor) + return op; + } + } + return nullptr; +} + +size_t tensor_buffer_size(circleinspect::Reader &reader, const int32_t tensor_id) +{ + auto tensors = reader.tensors(); + + if (tensor_id < 0 || tensor_id >= tensors->Length()) + { + throw std::runtime_error("Invalid Tensor ID"); + } + + auto tensor = tensors->Get(tensor_id); + auto buffer_id = tensor->buffer(); + + size_t size = reader.buffer_info(buffer_id, nullptr); + + return size; +} + +} // namespace + +namespace circleinspect +{ + +void DumpConv2DWeight::run(std::ostream &os, const circle::Model *model) +{ + circleinspect::Reader reader(model); + + assert(reader.num_subgraph() == 1); + reader.select_subgraph(0); + + auto ops = reader.operators(); + + // dump Conv2D, DepthwiseConv2D and its weight input operator + for (uint32_t i = 0; i < ops->Length(); ++i) + { + const auto op = ops->Get(i); + auto bc = reader.builtin_code(op); + + if (bc == circle::BuiltinOperator_CONV_2D || bc == circle::BuiltinOperator_DEPTHWISE_CONV_2D) + { + const std::vector &inputs = circleinspect::as_index_vector(op->inputs()); + if (inputs.size() < 2) + { + throw std::runtime_error("Operator has invalid input"); + } + auto weight_input = inputs[1]; // Tensor ID of weight input + + const auto op_weight = operator_match_output(reader, weight_input); + const auto buffer_size = tensor_buffer_size(reader, weight_input); + + std::string weight_op_name = "?"; + + if (op_weight == nullptr && buffer_size > 0) + { + weight_op_name = "CONST"; + } + else if (op_weight != nullptr) + { + weight_op_name = reader.opcode_name(op_weight); + } + + auto op_name = reader.opcode_name(op); + os << op_name << "," << weight_op_name << std::endl; + } + } +} + +} // namespace circleinspect diff --git a/compiler/circle-inspect/src/Dump.h b/compiler/circle-inspect/src/Dump.h new file mode 100644 index 000000000..6afba83b3 --- /dev/null +++ b/compiler/circle-inspect/src/Dump.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DUMP_H__ +#define __DUMP_H__ + +#include + +#include + +namespace circleinspect +{ + +class DumpInterface +{ +public: + virtual ~DumpInterface() = default; + +public: + virtual void run(std::ostream &os, const circle::Model *model) = 0; +}; + +class DumpOperators final : public DumpInterface +{ +public: + DumpOperators() = default; + +public: + void run(std::ostream &os, const circle::Model *model); +}; + +class DumpConv2DWeight final : public DumpInterface +{ +public: + DumpConv2DWeight() = default; + +public: + void run(std::ostream &os, const circle::Model *model); +}; + +} // namespace circleinspect + +#endif // __DUMP_H__ diff --git a/compiler/circle-inspect/src/Model.cpp b/compiler/circle-inspect/src/Model.cpp new file mode 100644 index 000000000..1924bfafc --- /dev/null +++ b/compiler/circle-inspect/src/Model.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Model.h" + +#include +#include +#include +#include + +namespace +{ + +class MemoryMappedModel final : public circleinspect::Model +{ +public: + /** + * @require fd and data SHOULD be valid + */ + explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} + { + // DO NOTHING + } + +public: + ~MemoryMappedModel() + { + munmap(_data, _size); + close(_fd); + } + +public: + MemoryMappedModel(const MemoryMappedModel &) = delete; + MemoryMappedModel(MemoryMappedModel &&) = delete; + +public: + const ::circle::Model *model(void) const override { return ::circle::GetModel(_data); } + +private: + int _fd = -1; + void *_data = nullptr; + size_t _size = 0; +}; + +class FileDescriptor final +{ +public: + FileDescriptor(int value) : _value{value} + { + // DO NOTHING + } + +public: + // NOTE Copy is not allowed + FileDescriptor(const FileDescriptor &) = delete; + +public: + // NOTE Move is allowed + FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); } + +public: + ~FileDescriptor() + { + if (_value != -1) + { + // Close on descturction + close(_value); + } + } + +public: + int value(void) const { return _value; } + +public: + int release(void) + { + auto res = _value; + _value = -1; + return res; + } + +private: + int _value = -1; +}; + +} // namespace + +namespace circleinspect +{ + +std::unique_ptr load_circle(const std::string &path) +{ + FileDescriptor fd = open(path.c_str(), O_RDONLY); + + if (fd.value() == -1) + { + // Return nullptr on open failure + return nullptr; + } + + struct stat st; + if (fstat(fd.value(), &st) == -1) + { + // Return nullptr on fstat failure + return nullptr; + } + + auto size = st.st_size; + auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0); + + if (data == MAP_FAILED) + { + // Return nullptr on mmap failure + return nullptr; + } + + // Check if file is a valid Flatbuffer file + const uint8_t *u8data = reinterpret_cast(data); + flatbuffers::Verifier verifier{u8data, static_cast(size)}; + if (!circle::VerifyModelBuffer(verifier)) + { + munmap(data, size); + close(fd.release()); + return nullptr; + } + + return std::unique_ptr{new MemoryMappedModel(fd.release(), data, size)}; +} + +} // namespace circleinspect diff --git a/compiler/circle-inspect/src/Model.h b/compiler/circle-inspect/src/Model.h new file mode 100644 index 000000000..8206ed364 --- /dev/null +++ b/compiler/circle-inspect/src/Model.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MODEL_H__ +#define __MODEL_H__ + +#include + +#include + +namespace circleinspect +{ + +struct Model +{ + virtual ~Model() = default; + + virtual const ::circle::Model *model(void) const = 0; +}; + +/** + * @brief Load Circle model (as a raw Model) from a given path + * + * @note May return a nullptr + */ +std::unique_ptr load_circle(const std::string &path); + +} // namespace circleinspect + +#endif // __MODEL_H__ diff --git a/compiler/circle-inspect/src/Reader.cpp b/compiler/circle-inspect/src/Reader.cpp new file mode 100644 index 000000000..dbbc7c75e --- /dev/null +++ b/compiler/circle-inspect/src/Reader.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Reader.h" + +#include +#include + +namespace circleinspect +{ + +bool is_valid(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX); +} + +bool is_custom(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (code == circle::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const circle::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + return opcode->custom_code()->c_str(); + } + + circle::BuiltinOperator code = opcode->builtin_code(); + return circle::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const circle::Tensor *tensor) +{ + return circle::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const circle::Tensor *tensor) +{ + static const char *kEmptyTensorName = "(noname)"; + + auto name = tensor->name(); + if (name) + return name->c_str(); + + return kEmptyTensorName; +} + +Reader::Reader(const circle::Model *model) +{ + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +{ + if (buff_data != nullptr) + { + *buff_data = nullptr; + } + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + if (buff_data != nullptr) + { + *buff_data = reinterpret_cast(array->data()); + } + return size; + } + } + } + + return 0; +} + +circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + return opcode->builtin_code(); +} + +std::string Reader::opcode_name(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return circleinspect::opcode_name(opcode); +} + +bool Reader::select_subgraph(uint32_t sgindex) +{ + _tensors = nullptr; + _operators = nullptr; + + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +} // namespace circleinspect diff --git a/compiler/circle-inspect/src/Reader.h b/compiler/circle-inspect/src/Reader.h new file mode 100644 index 000000000..b5a99df3f --- /dev/null +++ b/compiler/circle-inspect/src/Reader.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __READER_H__ +#define __READER_H__ + +#include + +#include +#include +#include + +namespace circleinspect +{ + +template std::vector as_index_vector(const flatbuffers::Vector *flat_array) +{ + std::vector ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + +bool is_valid(const circle::OperatorCode *opcode); +bool is_custom(const circle::OperatorCode *opcode); +std::string opcode_name(const circle::OperatorCode *opcode); +const char *tensor_type(const circle::Tensor *tensor); +const char *tensor_name(const circle::Tensor *tensor); + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class Reader +{ +private: + using CircleSubGraphs_t = flatbuffers::Vector>; + using CircleBuffers_t = flatbuffers::Vector>; + using CircleTensors_t = flatbuffers::Vector>; + using CircleOperators_t = flatbuffers::Vector>; + +public: + Reader(const circle::Model *model); + + Reader() = delete; + +public: + const std::vector &opcodes() { return _op_codes; } + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector &inputs() const { return _inputs; } + const std::vector &outputs() const { return _outputs; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + circle::BuiltinOperator builtin_code(const circle::Operator *op) const; + std::string opcode_name(const circle::Operator *op) const; + +public: + bool select_subgraph(uint32_t subgraph); + +private: + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + + std::vector _op_codes; + std::vector _inputs; + std::vector _outputs; +}; + +} // namespace circleinspect + +#endif // __READER_H__ diff --git a/compiler/circle-verify/CMakeLists.txt b/compiler/circle-verify/CMakeLists.txt new file mode 100644 index 000000000..2e19951e1 --- /dev/null +++ b/compiler/circle-verify/CMakeLists.txt @@ -0,0 +1,12 @@ +if(NOT TARGET mio_circle) + return() +endif(NOT TARGET mio_circle) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(circle-verify ${SOURCES}) +target_include_directories(circle-verify PRIVATE src) +target_link_libraries(circle-verify mio_circle) +target_link_libraries(circle-verify safemain) +target_link_libraries(circle-verify cwrap) +target_link_libraries(circle-verify stdex) diff --git a/compiler/circle-verify/README.md b/compiler/circle-verify/README.md new file mode 100644 index 000000000..1eda8a99e --- /dev/null +++ b/compiler/circle-verify/README.md @@ -0,0 +1,23 @@ +# circle-verify + +_circle-verify_ allows users to verify Circle models. + +## Usage + +Provide _circle_ file as a parameter to verify validity. + +``` +$ circle-verify circlefile.circle +``` + +Result for valid file +``` +[ RUN ] Check circlefile.circle +[ PASS ] Check circlefile.circle +``` + +Result for invalid file +``` +[ RUN ] Check circlefile.circle +[ FAIL ] Check circlefile.circle +``` diff --git a/compiler/circle-verify/requires.cmake b/compiler/circle-verify/requires.cmake new file mode 100644 index 000000000..2509b6931 --- /dev/null +++ b/compiler/circle-verify/requires.cmake @@ -0,0 +1,4 @@ +require("mio-circle") +require("safemain") +require("cwrap") +require("stdex") diff --git a/compiler/circle-verify/src/Driver.cpp b/compiler/circle-verify/src/Driver.cpp new file mode 100644 index 000000000..ad13e504f --- /dev/null +++ b/compiler/circle-verify/src/Driver.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VerifyFlatBuffers.h" + +#include + +#include +#include + +int entry(int argc, char **argv) +{ + if (argc != 2) + { + std::cerr << "ERROR: Failed to parse arguments" << std::endl; + std::cerr << std::endl; + std::cerr << "USAGE: " << argv[0] << " [circle]" << std::endl; + return 255; + } + auto verifier = stdex::make_unique(); + + std::string model_file = argv[argc - 1]; + + std::cout << "[ RUN ] Check " << model_file << std::endl; + + auto result = verifier->run(model_file); + + if (result == 0) + { + std::cout << "[ PASS ] Check " << model_file << std::endl; + } + else + { + std::cout << "[ FAIL ] Check " << model_file << std::endl; + } + + return result; +} diff --git a/compiler/circle-verify/src/Model.cpp b/compiler/circle-verify/src/Model.cpp new file mode 100644 index 000000000..efac1210d --- /dev/null +++ b/compiler/circle-verify/src/Model.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Model.h" + +#include + +#include +#include +#include +#include + +namespace +{ + +class MemoryMappedModel final : public ModelData +{ +public: + /** + * @require fd and data SHOULD be valid + */ + explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} + { + // DO NOTHING + } + +public: + ~MemoryMappedModel() + { + munmap(_data, _size); + close(_fd); + } + +public: + MemoryMappedModel(const MemoryMappedModel &) = delete; + MemoryMappedModel(MemoryMappedModel &&) = delete; + +public: + const void *data(void) const override { return _data; }; + const size_t size(void) const override { return _size; }; + +private: + int _fd = -1; + void *_data = nullptr; + size_t _size = 0; +}; + +} // namespace + +std::unique_ptr load_modeldata(const std::string &path) +{ + cwrap::Fildes fd(open(path.c_str(), O_RDONLY)); + + if (fd.get() == -1) + { + // Return nullptr on open failure + return nullptr; + } + + struct stat st; + if (fstat(fd.get(), &st) == -1) + { + // Return nullptr on fstat failure + return nullptr; + } + + auto size = st.st_size; + auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.get(), 0); + + if (data == MAP_FAILED) + { + // Return nullptr on mmap failure + return nullptr; + } + + return std::unique_ptr{new MemoryMappedModel(fd.release(), data, size)}; +} diff --git a/compiler/circle-verify/src/Model.h b/compiler/circle-verify/src/Model.h new file mode 100644 index 000000000..e1bd83971 --- /dev/null +++ b/compiler/circle-verify/src/Model.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MODEL_H__ +#define __MODEL_H__ + +#include +#include + +struct ModelData +{ + virtual ~ModelData() = default; + + virtual const void *data(void) const = 0; + virtual const size_t size(void) const = 0; +}; + +/** + * @brief Load Circle model (as a raw data) from a given path + * + * @note May return a nullptr + */ +std::unique_ptr load_modeldata(const std::string &path); + +#endif // __MODEL_H__ diff --git a/compiler/circle-verify/src/VerifyFlatBuffers.cpp b/compiler/circle-verify/src/VerifyFlatBuffers.cpp new file mode 100644 index 000000000..36b16685f --- /dev/null +++ b/compiler/circle-verify/src/VerifyFlatBuffers.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VerifyFlatBuffers.h" + +#include "Model.h" + +#include + +int VerifyFlatbuffers::run(const std::string &model_file) +{ + auto modeldata = load_modeldata(model_file); + + const uint8_t *data = reinterpret_cast(modeldata->data()); + flatbuffers::Verifier verifier{data, modeldata->size()}; + + if (!circle::VerifyModelBuffer(verifier)) + { + return -1; + } + + return 0; +} diff --git a/compiler/circle-verify/src/VerifyFlatBuffers.h b/compiler/circle-verify/src/VerifyFlatBuffers.h new file mode 100644 index 000000000..c301b5b10 --- /dev/null +++ b/compiler/circle-verify/src/VerifyFlatBuffers.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VERIFY_FLATBUFFERS_H__ +#define __VERIFY_FLATBUFFERS_H__ + +#include +#include + +class VerifyFlatbuffers +{ +public: + VerifyFlatbuffers() = default; + +public: + int run(const std::string &model_file); +}; + +#endif // __VERIFY_FLATBUFFERS_H__ diff --git a/compiler/circle2circle/CMakeLists.txt b/compiler/circle2circle/CMakeLists.txt new file mode 100644 index 000000000..644179941 --- /dev/null +++ b/compiler/circle2circle/CMakeLists.txt @@ -0,0 +1,42 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_executable(circle2circle "${SOURCES}") +target_include_directories(circle2circle PRIVATE include) +target_include_directories(circle2circle PRIVATE src) +target_link_libraries(circle2circle nncc_common) +target_link_libraries(circle2circle safemain) +target_link_libraries(circle2circle stdex) +target_link_libraries(circle2circle oops) +target_link_libraries(circle2circle hermes) +target_link_libraries(circle2circle hermes_std) +target_link_libraries(circle2circle loco) +target_link_libraries(circle2circle mio_circle) +target_link_libraries(circle2circle luci_import) +target_link_libraries(circle2circle luci_service) +target_link_libraries(circle2circle luci_pass) +target_link_libraries(circle2circle luci_export) + +install(TARGETS circle2circle DESTINATION bin) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(circle2circle_test ${TESTS} ${SOURCES}) +target_include_directories(circle2circle_test PRIVATE include) +target_include_directories(circle2circle_test PRIVATE src) +target_link_libraries(circle2circle_test nncc_common) +target_link_libraries(circle2circle_test stdex) +target_link_libraries(circle2circle_test oops) +target_link_libraries(circle2circle_test hermes) +target_link_libraries(circle2circle_test hermes_std) +target_link_libraries(circle2circle_test loco) +target_link_libraries(circle2circle_test mio_circle) +target_link_libraries(circle2circle_test luci_import) +target_link_libraries(circle2circle_test luci_service) +target_link_libraries(circle2circle_test luci_pass) +target_link_libraries(circle2circle_test luci_export) diff --git a/compiler/circle2circle/README.md b/compiler/circle2circle/README.md new file mode 100644 index 000000000..7bc1b7f59 --- /dev/null +++ b/compiler/circle2circle/README.md @@ -0,0 +1,3 @@ +# circle2circle + +_circle2circle_ provides Circle optimizations and quantizations as executable tool diff --git a/compiler/circle2circle/include/CircleExpContract.h b/compiler/circle2circle/include/CircleExpContract.h new file mode 100644 index 000000000..313b16d22 --- /dev/null +++ b/compiler/circle2circle/include/CircleExpContract.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 __CIRCLE2CIRCLE_CIRCLEXPCONTRACT_H__ +#define __CIRCLE2CIRCLE_CIRCLEXPCONTRACT_H__ + +#include +#include +#include +#include + +#include +#include + +struct CircleExpContract : public luci::CircleExporter::Contract +{ +public: + 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 nullptr; } + luci::Module *module(void) const final { return _module; }; + +public: + bool store(const char *ptr, const size_t size) const final; + +private: + luci::Module *_module; + const std::string _filepath; +}; + +#endif // __CIRCLE2CIRCLE_CIRCLEXPCONTRACT_H__ diff --git a/compiler/circle2circle/include/Model.h b/compiler/circle2circle/include/Model.h new file mode 100644 index 000000000..35b7e3239 --- /dev/null +++ b/compiler/circle2circle/include/Model.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 __CIRCLE2CIRCLE_MODEL_H__ +#define __CIRCLE2CIRCLE_MODEL_H__ + +#include + +#include + +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 load_model(const std::string &path); + +} // namespace luci + +#endif // __CIRCLE2CIRCLE_MODEL_H__ diff --git a/compiler/circle2circle/requires.cmake b/compiler/circle2circle/requires.cmake new file mode 100644 index 000000000..5b1e657ca --- /dev/null +++ b/compiler/circle2circle/requires.cmake @@ -0,0 +1,10 @@ +require("loco") +require("locop") +require("logo-core") +require("stdex") +require("safemain") +require("mio-circle") +require("oops") +require("hermes") +require("hermes-std") +require("luci") diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp new file mode 100644 index 000000000..781825fdd --- /dev/null +++ b/compiler/circle2circle/src/Circle2Circle.cpp @@ -0,0 +1,120 @@ +/* + * 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 "Model.h" +#include "CircleExpContract.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using OptionHook = std::function; + +using Algorithms = luci::CircleOptimizer::Options::Algorithm; + +void print_help(const char *progname) +{ + std::cerr << "USAGE: " << progname << " [options] input output" << std::endl; + std::cerr << " --fuse_instnorm : Enable FuseInstanceNormalization Pass" << std::endl; + std::cerr << std::endl; +} + +int entry(int argc, char **argv) +{ + if (argc < 3) + { + std::cerr << "ERROR: Failed to parse arguments" << std::endl; + std::cerr << std::endl; + print_help(argv[0]); + return 255; + } + + // Simple argument parser (based on map) + std::map argparse; + luci::CircleOptimizer optimizer; + + auto options = optimizer.options(); + + // TODO merge this with help message + argparse["--fuse_instnorm"] = [&options](const char **) { + options->enable(Algorithms::FuseInstanceNorm); + return 0; + }; + + for (int n = 1; n < argc - 2; ++n) + { + const std::string tag{argv[n]}; + auto it = argparse.find(tag); + if (it == argparse.end()) + { + std::cerr << "Option '" << tag << "' is not supported" << std::endl; + std::cerr << std::endl; + print_help(argv[0]); + return 255; + } + + n += it->second((const char **)&argv[n + 1]); + } + + std::string input_path = argv[argc - 2]; + std::string output_path = argv[argc - 1]; + + // Load model from the file + std::unique_ptr 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); + + for (size_t idx = 0; idx < module->size(); ++idx) + { + auto graph = module->graph(idx); + + // call luci optimizations + optimizer.optimize(graph); + + 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/circle2circle/src/Circle2Circle.test.cpp b/compiler/circle2circle/src/Circle2Circle.test.cpp new file mode 100644 index 000000000..015358ae7 --- /dev/null +++ b/compiler/circle2circle/src/Circle2Circle.test.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 "TestHelper.h" + +#include + +TEST(Circle2CircleTest, NoArg_NEG) +{ + Argv<1> argv; + argv.add("circle2circle"); + + ::testing::internal::CaptureStdout(); + int result = entry(1, argv.argv()); + ASSERT_EQ(result, 255); +} diff --git a/compiler/circle2circle/src/CircleExpContract.cpp b/compiler/circle2circle/src/CircleExpContract.cpp new file mode 100644 index 000000000..b56b7eedc --- /dev/null +++ b/compiler/circle2circle/src/CircleExpContract.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 "CircleExpContract.h" + +#include + +#include +#include + +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(); +} diff --git a/compiler/circle2circle/src/Model.cpp b/compiler/circle2circle/src/Model.cpp new file mode 100644 index 000000000..20d55a131 --- /dev/null +++ b/compiler/circle2circle/src/Model.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 "Model.h" + +#include +#include + +#include +#include +#include + +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 _data; +}; + +} // namespace + +namespace luci +{ + +std::unique_ptr load_model(const std::string &path) +{ + return std::unique_ptr{new FileModel(path)}; +} + +} // namespace luci diff --git a/compiler/circle2circle/src/TestHelper.h b/compiler/circle2circle/src/TestHelper.h new file mode 100644 index 000000000..f4dbe23a9 --- /dev/null +++ b/compiler/circle2circle/src/TestHelper.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 __CIRCLE2CIRCLE_TEST_HELPER_H__ +#define __CIRCLE2CIRCLE_TEST_HELPER_H__ + +#include +#include +#include + +int entry(int argc, char **argv); + +template class Argv +{ +public: + typedef char *pchar_t; + +public: + ~Argv() + { + for (size_t n = 0; n < _ptr; ++n) + delete _argv[n]; + } + + void add(const char *in) + { + assert(_ptr < N); + _argv[_ptr] = new char[strlen(in) + 1]; + strcpy(_argv[_ptr], in); + _ptr++; + } + + pchar_t *argv(void) { return _argv; } + +private: + pchar_t _argv[N] = { + nullptr, + }; + size_t _ptr = 0; +}; + +#endif // __CIRCLE2CIRCLE_TEST_HELPER_H__ diff --git a/compiler/circledump/CMakeLists.txt b/compiler/circledump/CMakeLists.txt new file mode 100644 index 000000000..a117e7285 --- /dev/null +++ b/compiler/circledump/CMakeLists.txt @@ -0,0 +1,14 @@ +if(NOT TARGET mio_circle) + return() +endif(NOT TARGET mio_circle) + +set(DRIVER "driver/Driver.cpp") + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(circledump ${DRIVER} ${SOURCES}) +target_include_directories(circledump PRIVATE include) +target_link_libraries(circledump mio_circle) +target_link_libraries(circledump safemain) +target_link_libraries(circledump stdex) +target_link_libraries(circledump flatbuffers) diff --git a/compiler/circledump/README.md b/compiler/circledump/README.md new file mode 100644 index 000000000..686e918ac --- /dev/null +++ b/compiler/circledump/README.md @@ -0,0 +1,71 @@ +# circledump + +### What is this? + +circledump is a tool that dumps binary circle file into human readable text to console. + +circledump is implemented with C++ not python. We can do the same thing much easier +with python but this tool doesn't need to install TensorFlow python package. + +Schema for FlatBuffer used is from TensorFlow v1.13.1 release. + +### Design philosophy + +Make the code simple. + +### To do + +- Print weight values other than uint8_t +- Add more operators + +### How to use + +Command argument format: +``` +circledump circle_file +``` + +Example output of dump `readme.circle` file +``` +Dump: readme.circle + +Data Format: +CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data) + +Operator Codes: [order] OpCodeName (OpCode Enum) +[0] CONV_2D (code: 3) + +Buffers: B(index) (length) values, if any +B(0) (0) +B(1) (8) 0x94 0x5b 0x95 0xbf 0x42 0xa4 0x52 0xbf ... +B(2) (4) 0xcd 0xcc 0x8c 0x3f + +Operands: T(tensor index) TYPE (shape) B(buffer index) OperandName +T(0) FLOAT32 (1, 3, 3, 2) B(0) ifm +T(1) FLOAT32 (1, 1, 1, 2) B(1) ker +T(2) FLOAT32 (1) B(2) bias +T(3) FLOAT32 (1, 3, 3, 1) B(0) ofm + +Operators: O(operator index) OpCodeName + Option(values) ... <-- depending on OpCode + I T(tensor index) OperandName <-- as input + O T(tensor index) OperandName <-- as output +O(0) CONV_2D + Padding(1) Stride.W(1) Stride.H(1) Activation(0) + I T(0) ifm + I T(1) ker + I T(2) bias + O T(3) ofm + +Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName +I T(0) ifm +I T(1) ker +O T(3) ofm +``` + +### Dependency + +- mio-circle +- safemain +- stdex +- FlatBuffers diff --git a/compiler/circledump/driver/Driver.cpp b/compiler/circledump/driver/Driver.cpp new file mode 100644 index 000000000..8ed88e1d8 --- /dev/null +++ b/compiler/circledump/driver/Driver.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 +#include + +#include + +int entry(int argc, char **argv) +{ + if (argc != 2) + { + std::cerr << "ERROR: Failed to parse arguments" << std::endl; + std::cerr << std::endl; + std::cerr << "USAGE: " << argv[0] << " [circle]" << std::endl; + return 255; + } + + // Load Circle model from a circle file + std::unique_ptr model = circleread::load_circle(argv[1]); + if (model == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << argv[1] << "'" << std::endl; + return 255; + } + + const circle::Model *circlemodel = model->model(); + if (circlemodel == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << argv[1] << "'" << std::endl; + return 255; + } + + std::cout << "Dump: " << argv[1] << std::endl << std::endl; + + std::cout << circlemodel << std::endl; + + return 0; +} diff --git a/compiler/circledump/include/circledump/Dump.h b/compiler/circledump/include/circledump/Dump.h new file mode 100644 index 000000000..a129458f4 --- /dev/null +++ b/compiler/circledump/include/circledump/Dump.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 __CIRCLEDUMP_DUMP_H__ +#define __CIRCLEDUMP_DUMP_H__ + +#include + +#include + +namespace circledump +{ + +void dump_model(std::ostream &os, const circle::Model *model); +} + +std::ostream &operator<<(std::ostream &os, const circle::Model *model); + +#endif // __CIRCLEDUMP_DUMP_H__ diff --git a/compiler/circledump/include/circleread/Model.h b/compiler/circledump/include/circleread/Model.h new file mode 100644 index 000000000..234db8b4c --- /dev/null +++ b/compiler/circledump/include/circleread/Model.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 __CIRCLEREAD_MODEL_H__ +#define __CIRCLEREAD_MODEL_H__ + +#include + +#include + +namespace circleread +{ + +struct Model +{ + virtual ~Model() = default; + + virtual const ::circle::Model *model(void) const = 0; +}; + +/** + * @brief Load Circle model (as a raw Model) from a given path + * + * @note May return a nullptr + */ +std::unique_ptr load_circle(const std::string &path); + +} // namespace circleread + +#endif // __CIRCLEREAD_MODEL_H__ diff --git a/compiler/circledump/requires.cmake b/compiler/circledump/requires.cmake new file mode 100644 index 000000000..b090dbd4d --- /dev/null +++ b/compiler/circledump/requires.cmake @@ -0,0 +1,3 @@ +require("mio-circle") +require("safemain") +require("stdex") diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp new file mode 100644 index 000000000..3d99189f9 --- /dev/null +++ b/compiler/circledump/src/Dump.cpp @@ -0,0 +1,310 @@ +/* + * 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 + +#include "Read.h" +#include "OpPrinter.h" + +#include + +#include // min +#include // setfill + +namespace circledump +{ + +void dump_buffer(std::ostream &os, const uint8_t *buffer, size_t size, size_t amount) +{ + std::ios_base::fmtflags saveflags(os.flags()); + + bool second = false; + bool ellipsis = amount > 0 && size > 4; + size_t count = ellipsis ? std::min(size, amount) : size; + + for (size_t i = 0; i < count; i++) + { + if (second) + { + os << " "; + } + + os << std::showbase << std::setfill('0') << std::setw(2); + os << std::hex << (uint32_t)buffer[i]; + + second = true; + } + if (ellipsis) + { + os << " ..."; + } + + os.flags(saveflags); +} + +void dump_vector(std::ostream &os, const std::vector &vs) +{ + uint32_t seq = 0; + for (auto &v : vs) + { + if (seq) + os << ", "; + os << v; + seq++; + } +} + +std::ostream &operator<<(std::ostream &os, const std::vector &vect) +{ + circledump::dump_vector(os, vect); + return os; +} + +template void dump_fbvect(std::ostream &os, const flatbuffers::Vector *fbvect) +{ + if (fbvect == nullptr) + return; + + bool ellipsis = (fbvect->size() > 4); + auto limit_size = ellipsis ? 4 : fbvect->size(); + + if (ellipsis) + { + os << "(" << fbvect->size() << ") "; + } + for (uint32_t q = 0; q < limit_size; q++) + { + if (q) + os << ", "; + os << fbvect->Get(q); + } + if (ellipsis) + { + os << " ... "; + } +} + +template +std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector *fbvect) +{ + dump_fbvect(os, fbvect); + return os; +} + +void dump_sub_graph(std::ostream &os, circleread::Reader &reader) +{ + auto tensors = reader.tensors(); + auto operators = reader.operators(); + auto data_format = reader.data_format(); + + // dump data_format + os << "Data Format:" << std::endl; + if (data_format == circle::DataFormat::DataFormat_CHANNELS_LAST) + { + os << "CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data)" << std::endl; + } + else if (data_format == circle::DataFormat::DataFormat_CHANNELS_FIRST) + { + os << "CHANNEL_FIRST (NCHW for 2d, NCDHW for 3d data)" << std::endl; + } + os << std::endl; + + // dump operands(tensors) + os << "Operands: T(subgraph index : tensor index) TYPE (shape) B(buffer index) OperandName" + << std::endl; + for (uint32_t i = 0; i < tensors->Length(); ++i) + { + // TODO refactor to some better structure + auto tensor = tensors->Get(i); + std::vector dims = {-1}; + + if (tensor->shape()) + dims = circleread::as_index_vector(tensor->shape()); + + os << "T(" << reader.subgraph_index() << ":" << i << ") " << circleread::tensor_type(tensor) + << " "; + os << "(" << dims << ") "; + os << "B(" << tensor->buffer() << ") "; + os << circleread::tensor_name(tensor) << std::endl; + + if (auto q_params = tensor->quantization()) + { + if ((q_params->min() && q_params->max()) || (q_params->scale() && q_params->zero_point())) + { + std::string strquantiz = " Quantization: "; + std::string strqindent(strquantiz.size(), ' '); + os << strquantiz; + + if (q_params->min()) + { + os << "min(" << q_params->min() << ") "; + if (q_params->min()->size() > 1) + os << std::endl << strqindent; + } + if (q_params->max()) + { + os << "max(" << q_params->max() << ") "; + if (q_params->max()->size() > 1) + os << std::endl << strqindent; + } + if (q_params->scale()) + { + os << "scale(" << q_params->scale() << ") "; + if (q_params->scale()->size() > 1) + os << std::endl << strqindent; + } + if (q_params->zero_point()) + os << "zeropt(" << q_params->zero_point() << ") "; + + os << std::endl; + } + } + } + os << std::endl; + + // dump operators + os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl; + os << " Option(values) ... <-- depending on OpCode" << std::endl; + os << " I T(tensor index) OperandName <-- as input" << std::endl; + os << " O T(tensor index) OperandName <-- as output" << std::endl; + for (uint32_t i = 0; i < operators->Length(); ++i) + { + const auto op = operators->Get(i); + circle::BuiltinOperator builtincode = reader.builtin_code(op); + + const std::vector &inputs = circleread::as_index_vector(op->inputs()); + const std::vector &outputs = circleread::as_index_vector(op->outputs()); + auto op_name = reader.opcode_name(op); + + os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " "; + os << std::endl; + + if (auto op_prn = OpPrinterRegistry::get().lookup(builtincode)) + { + op_prn->options(op, os); + } + + for (auto input : inputs) + { + os << " I T(" << input << ") "; + if (input >= 0) + { + auto tensor = tensors->Get(input); + os << circleread::tensor_name(tensor); + } + os << std::endl; + } + for (auto output : outputs) + { + os << " O T(" << output << ") "; + if (output >= 0) + { + auto tensor = tensors->Get(output); + os << circleread::tensor_name(tensor); + } + os << std::endl; + } + } + os << std::endl; + + // dump network inputs/outputs + os << "Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName" << std::endl; + + for (const auto input : reader.inputs()) + { + auto tensor = tensors->Get(input); + std::string name = circleread::tensor_name(tensor); + os << "I T(" << input << ") " << name << std::endl; + } + + for (const auto output : reader.outputs()) + { + auto tensor = tensors->Get(output); + std::string name = circleread::tensor_name(tensor); + os << "O T(" << output << ") " << name << std::endl; + } + + os << std::endl; +} + +void dump_model(std::ostream &os, const circle::Model *model) +{ + circleread::Reader reader(model); + + uint32_t num_subgraph = reader.num_subgraph(); + + // dump model version + os << "===================================================================" << std::endl; + os << "Model version: " << reader.version() << std::endl; + os << " # sub graphs: " << num_subgraph << std::endl; + os << std::endl; + + auto opcodes = reader.opcodes(); + auto buffers = reader.buffers(); + + // dump operator_codes + os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl; + int32_t opcode_index = 0; + for (auto opcode : opcodes) + { + circle::BuiltinOperator op_code = opcode->builtin_code(); + auto op_name = circleread::opcode_name(opcode); + auto op_version = opcode->version(); + + os << "[" << opcode_index << "] " << op_name << " (code: " << op_code + << ", version: " << op_version << ")" << std::endl; + + opcode_index++; + } + os << std::endl; + + // dump buffer + os << "Buffers: B(index) (length) values, if any" << std::endl; + for (uint32_t i = 0; i < buffers->Length(); ++i) + { + const uint8_t *buff_data; + size_t size = reader.buffer_info(i, &buff_data); + + os << "B(" << i << ") (" << size << ") "; + if (buff_data != nullptr) + { + dump_buffer(os, buff_data, size, 16); + } + os << std::endl; + } + os << std::endl; + + for (uint32_t sg = 0; sg < num_subgraph; ++sg) + { + reader.select_subgraph(sg); + + os << "-------------------------------------------------------------------" << std::endl; + os << "Sub-Graph: #" << sg << " " << reader.subgraph_name() << std::endl; + os << std::endl; + + dump_sub_graph(os, reader); + } + + os << "===================================================================" << std::endl; +} + +} // namespace circledump + +std::ostream &operator<<(std::ostream &os, const circle::Model *model) +{ + circledump::dump_model(os, model); + return os; +} diff --git a/compiler/circledump/src/Load.cpp b/compiler/circledump/src/Load.cpp new file mode 100644 index 000000000..ec91ed189 --- /dev/null +++ b/compiler/circledump/src/Load.cpp @@ -0,0 +1,133 @@ +/* + * 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 + +#include +#include +#include +#include + +namespace +{ + +class MemoryMappedModel final : public circleread::Model +{ +public: + /** + * @require fd and data SHOULD be valid + */ + explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} + { + // DO NOTHING + } + +public: + ~MemoryMappedModel() + { + munmap(_data, _size); + close(_fd); + } + +public: + MemoryMappedModel(const MemoryMappedModel &) = delete; + MemoryMappedModel(MemoryMappedModel &&) = delete; + +public: + const ::circle::Model *model(void) const override { return ::circle::GetModel(_data); } + +private: + int _fd = -1; + void *_data = nullptr; + size_t _size = 0; +}; + +class FileDescriptor final +{ +public: + FileDescriptor(int value) : _value{value} + { + // DO NOTHING + } + +public: + // NOTE Copy is not allowed + FileDescriptor(const FileDescriptor &) = delete; + +public: + // NOTE Move is allowed + FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); } + +public: + ~FileDescriptor() + { + if (_value != -1) + { + // Close on descturction + close(_value); + } + } + +public: + int value(void) const { return _value; } + +public: + int release(void) + { + auto res = _value; + _value = -1; + return res; + } + +private: + int _value = -1; +}; + +} // namespace + +namespace circleread +{ + +std::unique_ptr load_circle(const std::string &path) +{ + FileDescriptor fd = open(path.c_str(), O_RDONLY); + + if (fd.value() == -1) + { + // Return nullptr on open failure + return nullptr; + } + + struct stat st; + if (fstat(fd.value(), &st) == -1) + { + // Return nullptr on fstat failure + return nullptr; + } + + auto size = st.st_size; + auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0); + + if (data == MAP_FAILED) + { + // Return nullptr on mmap failure + return nullptr; + } + + return std::unique_ptr{new MemoryMappedModel(fd.release(), data, size)}; +} + +} // namespace circleread diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp new file mode 100644 index 000000000..f9daab494 --- /dev/null +++ b/compiler/circledump/src/OpPrinter.cpp @@ -0,0 +1,307 @@ +/* + * 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 "OpPrinter.h" +#include "Read.h" + +#include + +#include + +using stdex::make_unique; + +namespace circledump +{ + +// TODO move to some header +std::ostream &operator<<(std::ostream &os, const std::vector &vect); + +// TODO Re-arrange in alphabetical order + +class AddPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_AddOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << std::endl; + } + } +}; + +class ArgMaxPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_ArgMaxOptions()) + { + os << " "; + os << "OutputType(" << EnumNameTensorType(params->output_type()) << ") "; + os << std::endl; + } + } +}; + +class Conv2DPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto conv_params = op->builtin_options_as_Conv2DOptions()) + { + os << " "; + os << "Padding(" << conv_params->padding() << ") "; + os << "Stride.W(" << conv_params->stride_w() << ") "; + os << "Stride.H(" << conv_params->stride_h() << ") "; + os << "Activation(" + << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ")"; + os << std::endl; + } + } +}; + +class DivPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_DivOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << std::endl; + } + } +}; + +class Pool2DPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto pool_params = op->builtin_options_as_Pool2DOptions()) + { + os << " "; + os << "Padding(" << pool_params->padding() << ") "; + os << "Stride.W(" << pool_params->stride_w() << ") "; + os << "Stride.H(" << pool_params->stride_h() << ") "; + os << "Filter.W(" << pool_params->filter_width() << ") "; + os << "Filter.H(" << pool_params->filter_height() << ") "; + os << "Activation(" + << EnumNameActivationFunctionType(pool_params->fused_activation_function()) << ")"; + os << std::endl; + } + } +}; + +class ConcatenationPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions()) + { + os << " "; + os << "Activation(" + << EnumNameActivationFunctionType(concatenation_params->fused_activation_function()) + << ") "; + os << "Axis(" << concatenation_params->axis() << ")"; + os << std::endl; + } + } +}; + +class ReshapePrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *reshape_params = op->builtin_options_as_ReshapeOptions()) + { + auto new_shape = circleread::as_index_vector(reshape_params->new_shape()); + os << " "; + os << "NewShape(" << new_shape << ")"; + os << std::endl; + } + } +}; + +class DepthwiseConv2DPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto conv_params = op->builtin_options_as_DepthwiseConv2DOptions()) + { + os << " "; + os << "Padding(" << conv_params->padding() << ") "; + os << "Stride.W(" << conv_params->stride_w() << ") "; + os << "Stride.H(" << conv_params->stride_h() << ") "; + os << "DepthMultiplier(" << conv_params->depth_multiplier() << ") "; + os << "Dilation.W(" << conv_params->dilation_w_factor() << ") "; + os << "Dilation.H(" << conv_params->dilation_h_factor() << ")"; + os << "Activation(" + << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ") "; + os << std::endl; + } + } +}; + +class FullyConnectedPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_FullyConnectedOptions()) + { + os << " "; + os << "WeightFormat(" << EnumNameFullyConnectedOptionsWeightsFormat(params->weights_format()) + << ") "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + + os << std::endl; + } + } +}; + +class MulPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_MulOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << std::endl; + } + } +}; + +class PackPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_PackOptions()) + { + os << " "; + os << "ValuesCount(" << params->values_count() << ") "; + os << "Axis(" << params->axis() << ") "; + os << std::endl; + } + } +}; + +class SoftmaxPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *softmax_params = op->builtin_options_as_SoftmaxOptions()) + { + os << " "; + os << "Beta(" << softmax_params->beta() << ")"; + os << std::endl; + } + } +}; + +class SubPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_SubOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << std::endl; + } + } +}; + +class CustomOpPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (op->custom_options_format() != circle::CustomOptionsFormat::CustomOptionsFormat_FLEXBUFFERS) + { + os << " "; + os << "Unknown custom option format"; + return; + } + + const flatbuffers::Vector *option_buf = op->custom_options(); + + if (option_buf == nullptr || option_buf->size() == 0) + { + os << "No attrs found." << std::endl; + return; + } + + // printing attrs + // attrs of custom ops are encoded in flexbuffer format + auto attr_map = flexbuffers::GetRoot(option_buf->data(), option_buf->size()).AsMap(); + + os << " "; + auto keys = attr_map.Keys(); + for (int i = 0; i < keys.size(); i++) + { + auto key = keys[i].ToString(); + os << key << "(" << attr_map[key].ToString() << ") "; + } + + // Note: attr in "Shape" type does not seem to be converted by circle_convert. + // When the converted circle file (with custom op) is opened with hexa editory, + // attrs names can be found but attr name in "Shape" type is not found. + + os << std::endl; + } +}; + +OpPrinterRegistry::OpPrinterRegistry() +{ + _op_map[circle::BuiltinOperator_ADD] = make_unique(); + _op_map[circle::BuiltinOperator_ARG_MAX] = make_unique(); + _op_map[circle::BuiltinOperator_AVERAGE_POOL_2D] = make_unique(); + _op_map[circle::BuiltinOperator_CONCATENATION] = make_unique(); + _op_map[circle::BuiltinOperator_CONV_2D] = make_unique(); + _op_map[circle::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique(); + _op_map[circle::BuiltinOperator_DIV] = make_unique(); + _op_map[circle::BuiltinOperator_FULLY_CONNECTED] = make_unique(); + _op_map[circle::BuiltinOperator_MAX_POOL_2D] = make_unique(); + _op_map[circle::BuiltinOperator_MUL] = make_unique(); + _op_map[circle::BuiltinOperator_PACK] = make_unique(); + // There is no Option for Pad + // There is no Option for ReLU and ReLU6 + _op_map[circle::BuiltinOperator_RESHAPE] = make_unique(); + _op_map[circle::BuiltinOperator_SOFTMAX] = make_unique(); + _op_map[circle::BuiltinOperator_SUB] = make_unique(); + _op_map[circle::BuiltinOperator_CUSTOM] = make_unique(); +} + +} // namespace circledump diff --git a/compiler/circledump/src/OpPrinter.h b/compiler/circledump/src/OpPrinter.h new file mode 100644 index 000000000..6b978a4c7 --- /dev/null +++ b/compiler/circledump/src/OpPrinter.h @@ -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. + */ + +#ifndef __CIRCLEDUMP_OPPRINTER_H__ +#define __CIRCLEDUMP_OPPRINTER_H__ + +#include + +#include +#include + +namespace circledump +{ + +class OpPrinter +{ +public: + virtual void options(const circle::Operator *, std::ostream &) const {}; +}; + +class OpPrinterRegistry +{ +public: + OpPrinterRegistry(); + +public: + const OpPrinter *lookup(circle::BuiltinOperator op) const + { + if (_op_map.find(op) == _op_map.end()) + return nullptr; + + return _op_map.at(op).get(); + } + +public: + static OpPrinterRegistry &get() + { + static OpPrinterRegistry me; + return me; + } + +private: + std::map> _op_map; +}; + +} // namespace circledump + +#endif // __CIRCLEDUMP_OPPRINTER_H__ diff --git a/compiler/circledump/src/Read.cpp b/compiler/circledump/src/Read.cpp new file mode 100644 index 000000000..053225536 --- /dev/null +++ b/compiler/circledump/src/Read.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 "Read.h" + +#include +#include + +namespace circleread +{ + +bool is_valid(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX); +} + +bool is_custom(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (code == circle::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const circle::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + std::string custom_op = "CUSTOM("; + custom_op += opcode->custom_code()->c_str(); + custom_op += ")"; + return custom_op; + } + + circle::BuiltinOperator code = opcode->builtin_code(); + return circle::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const circle::Tensor *tensor) +{ + return circle::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const circle::Tensor *tensor) +{ + static const char *kEmptyTensorName = "(noname)"; + + auto name = tensor->name(); + if (name) + return name->c_str(); + + return kEmptyTensorName; +} + +Reader::Reader(const circle::Model *model) +{ + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +{ + *buff_data = nullptr; + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + *buff_data = reinterpret_cast(array->data()); + return size; + } + } + } + + return 0; +} + +circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + return opcode->builtin_code(); +} + +std::string Reader::opcode_name(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return circleread::opcode_name(opcode); +} + +bool Reader::select_subgraph(uint32_t sgindex) +{ + _subgraph_index = sgindex; + _tensors = nullptr; + _operators = nullptr; + + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + auto name = subgraph->name(); + _subgraph_name = name ? name->c_str() : "(noname)"; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + _data_format = subgraph->data_format(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +} // namespace circleread diff --git a/compiler/circledump/src/Read.h b/compiler/circledump/src/Read.h new file mode 100644 index 000000000..dd1ef20b6 --- /dev/null +++ b/compiler/circledump/src/Read.h @@ -0,0 +1,101 @@ +/* + * 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 __CIRCLEREAD_READ_H__ +#define __CIRCLEREAD_READ_H__ + +#include + +#include +#include +#include + +namespace circleread +{ + +template std::vector as_index_vector(const flatbuffers::Vector *flat_array) +{ + std::vector ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + +bool is_valid(const circle::OperatorCode *opcode); +bool is_custom(const circle::OperatorCode *opcode); +std::string opcode_name(const circle::OperatorCode *opcode); +const char *tensor_type(const circle::Tensor *tensor); +const char *tensor_name(const circle::Tensor *tensor); + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class Reader +{ +private: + using CircleSubGraphs_t = flatbuffers::Vector>; + using CircleBuffers_t = flatbuffers::Vector>; + using CircleTensors_t = flatbuffers::Vector>; + using CircleOperators_t = flatbuffers::Vector>; + +public: + Reader(const circle::Model *model); + + Reader() = delete; + +public: + uint32_t version() const { return _version; } + + const std::vector &opcodes() { return _op_codes; } + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector &inputs() const { return _inputs; } + const std::vector &outputs() const { return _outputs; } + const circle::DataFormat &data_format() const { return _data_format; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + circle::BuiltinOperator builtin_code(const circle::Operator *op) const; + std::string opcode_name(const circle::Operator *op) const; + +public: + bool select_subgraph(uint32_t subgraph); + const std::string &subgraph_name(void) const { return _subgraph_name; } + uint32_t subgraph_index(void) const { return _subgraph_index; } + +private: + uint32_t _version; + + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + + uint32_t _subgraph_index; + std::string _subgraph_name; + std::vector _op_codes; + std::vector _inputs; + std::vector _outputs; + circle::DataFormat _data_format; +}; + +} // namespace circleread + +#endif // __CIRCLEREAD_READ_H__ diff --git a/compiler/cli/CMakeLists.txt b/compiler/cli/CMakeLists.txt new file mode 100644 index 000000000..22948fff9 --- /dev/null +++ b/compiler/cli/CMakeLists.txt @@ -0,0 +1,15 @@ +list(APPEND SOURCES "src/App.cpp") +list(APPEND TESTS "src/App.test.cpp") + +add_library(cli ${SOURCES}) +target_include_directories(cli PUBLIC include) + +nnas_find_package(GTest QUIET) + +if(NOT GTest_FOUND) + return() +endif(NOT GTest_FOUND) + +GTest_AddTEst(cli_test ${TESTS}) +target_link_libraries(cli_test cli) +target_link_libraries(cli_test stdex) diff --git a/compiler/cli/README.md b/compiler/cli/README.md new file mode 100644 index 000000000..6095c73bf --- /dev/null +++ b/compiler/cli/README.md @@ -0,0 +1,13 @@ +# cli + +`cli` is a CLI (Command Line Interface) application framework. + +# Background + +Many tools in `nncc` are command-line interface (CLI) applications. They generally need to handle command line parameters. +`cli` was written to reduce code duplication across such applications. + + +# How to use + +Please refer to `cli/src/App.test.cpp` for an example. diff --git a/compiler/cli/include/cli/App.h b/compiler/cli/include/cli/App.h new file mode 100644 index 000000000..61554e933 --- /dev/null +++ b/compiler/cli/include/cli/App.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CLI_APP_H__ +#define __CLI_APP_H__ + +#include "Command.h" + +#include +#include +#include + +namespace cli +{ + +class App +{ +public: + explicit App(const std::string &name); + +public: + App &insert(const std::string &tag, std::unique_ptr &&command); + +public: + int run(int argc, const char *const *argv) const; + +private: + void usage(std::ostream &os) const; + +private: + const std::string _name; + std::map> _commands; +}; + +} // namespace cli + +#endif // __APP_H__ diff --git a/compiler/cli/include/cli/Command.h b/compiler/cli/include/cli/Command.h new file mode 100644 index 000000000..2e264f9ef --- /dev/null +++ b/compiler/cli/include/cli/Command.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CLI_COMMAND_H__ +#define __CLI_COMMAND_H__ + +namespace cli +{ + +struct Command +{ + virtual ~Command() = default; + + virtual int run(int argc, const char *const *argv) const = 0; +}; + +} // namespace cli + +#endif // __CLI_COMMAND_H__ diff --git a/compiler/cli/include/cli/FunctionCommand.h b/compiler/cli/include/cli/FunctionCommand.h new file mode 100644 index 000000000..585653099 --- /dev/null +++ b/compiler/cli/include/cli/FunctionCommand.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CLI_FUNCTION_COMMAND_H__ +#define __CLI_FUNCTION_COMMAND_H__ + +#include + +namespace cli +{ + +class FunctionCommand final : public Command +{ +public: + // NOTE The use of pure funtion pointer here is intended to disallow variable capture + using Entry = int (*)(int argc, const char *const *argv); + +public: + FunctionCommand(const Entry &entry) : _entry{entry} + { + // DO NOTHING + } + +public: + int run(int argc, const char *const *argv) const override { return _entry(argc, argv); }; + +private: + Entry const _entry; +}; + +} // namespace cli + +#endif // __CLI_FUNCTION_COMMAND_H__ diff --git a/compiler/cli/src/App.cpp b/compiler/cli/src/App.cpp new file mode 100644 index 000000000..5052f682a --- /dev/null +++ b/compiler/cli/src/App.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cli/App.h" + +#include +#include + +namespace cli +{ + +App::App(const std::string &name) : _name{name} +{ + // DO NOTHING +} + +App &App::insert(const std::string &tag, std::unique_ptr &&command) +{ + assert(_commands.find(tag) == _commands.end()); + + _commands[tag] = std::move(command); + + return (*this); +} + +int App::run(int argc, const char *const *argv) const +{ + if (argc < 1) + { + std::cerr << "ERROR: COMMAND is not provided" << std::endl; + usage(std::cerr); + return 255; + } + + const std::string command{argv[0]}; + + auto it = _commands.find(command); + + if (it == _commands.end()) + { + std::cerr << "ERROR: '" << command << "' is not a valid command" << std::endl; + usage(std::cerr); + return 255; + } + + return it->second->run(argc - 1, argv + 1); +} + +void App::usage(std::ostream &os) const +{ + os << std::endl; + os << "USAGE: " << _name << " [COMMAND] ..." << std::endl; + os << std::endl; + os << "SUPPORTED COMMANDS:" << std::endl; + for (auto it = _commands.begin(); it != _commands.end(); ++it) + { + os << " " << it->first << std::endl; + } +} + +} // namespace cli diff --git a/compiler/cli/src/App.test.cpp b/compiler/cli/src/App.test.cpp new file mode 100644 index 000000000..fe2d44179 --- /dev/null +++ b/compiler/cli/src/App.test.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cli/App.h" + +#include + +#include + +class RecordCommand final : public cli::Command +{ +public: + RecordCommand(int ret, std::string &out) : _ret{ret}, _out(out) + { + // DO NOTHING + } + +public: + int run(int argc, const char *const *argv) const override + { + _out += std::to_string(argc); + + for (int n = 0; n < argc; ++n) + { + _out += ";"; + _out += argv[n]; + } + + return _ret; + } + +private: + int const _ret; + std::string &_out; +}; + +TEST(APP, run) +{ + cli::App app("test"); + + std::string args; + app.insert("record", stdex::make_unique(3, args)); + + const char *argv[] = {"record", "hello", "world"}; + + int ret = app.run(3, argv); + + ASSERT_EQ(ret, 3); + ASSERT_EQ(args, "2;hello;world"); +} diff --git a/compiler/coco/CMakeLists.txt b/compiler/coco/CMakeLists.txt new file mode 100644 index 000000000..4be53e8a8 --- /dev/null +++ b/compiler/coco/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(core) +add_subdirectory(generic) diff --git a/compiler/coco/README.md b/compiler/coco/README.md new file mode 100644 index 000000000..cfef8bafe --- /dev/null +++ b/compiler/coco/README.md @@ -0,0 +1,3 @@ +# coco + +_coco_ is an experimental coarse-grained intermediate representation (IR) for NN compilers. diff --git a/compiler/coco/core/CMakeLists.txt b/compiler/coco/core/CMakeLists.txt new file mode 100644 index 000000000..8c6844733 --- /dev/null +++ b/compiler/coco/core/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(coco_core SHARED ${SOURCES}) +target_include_directories(coco_core PUBLIC include) +# NOTE Some coco_core PUBLIC headers include angkor headers +target_link_libraries(coco_core PUBLIC angkor) +target_link_libraries(coco_core PRIVATE pepper_assert) +target_link_libraries(coco_core PRIVATE stdex) +# Let's apply nncc common compile options +# NOTE This will enable strict compilation (warnings as error). +# Please refer to top-level CMakeLists.txt for details +target_link_libraries(coco_core PRIVATE nncc_common) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is required for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(coco_core_test ${TESTS}) +target_link_libraries(coco_core_test coco_core) +target_link_libraries(coco_core_test stdex) diff --git a/compiler/coco/core/include/coco/ADT/DLinkedList.h b/compiler/coco/core/include/coco/ADT/DLinkedList.h new file mode 100644 index 000000000..e3c275041 --- /dev/null +++ b/compiler/coco/core/include/coco/ADT/DLinkedList.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_ADT_DLINKED_LIST_H__ +#define __COCO_ADT_DLINKED_LIST_H__ + +#include +#include + +namespace coco +{ + +// **CAUTION** Child SHOULD inherit DLinkedList::Node +template struct DLinkedList +{ + /// @brief A hook for Child-Join event + static void joined(Parent *, Child *); + /// @brief A hook for Child-Leave event + static void leaving(Parent *, Child *); + + class Head + { + public: + Head(Parent *parent) : _parent{parent} + { + _head = nullptr; + _tail = nullptr; + } + + public: + Head(const Head &) = delete; + Head(Head &&) = delete; + + public: + Child *head(void) const { return _head; } + Child *tail(void) const { return _tail; } + + public: + bool empty(void) const + { + if (_head == nullptr) + { + assert(_head == _tail); + return true; + } + + assert(_head != nullptr); + assert(_tail != nullptr); + return false; + } + + public: + void enlist(Child *child) + { + assert((child->prev() == nullptr) || (child->prev()->parent() == _parent)); + assert((child->next() == nullptr) || (child->next()->parent() == _parent)); + + if (empty()) + { + _head = child; + _tail = child; + } + else + { + if (child->next() == _head) + { + // _child is a new head + assert(child->prev() == nullptr); + _head = child; + } + + if (child->prev() == _tail) + { + // _child is a new tail + assert(child->next() == nullptr); + _tail = child; + } + } + + // Update parent-child relation + child->parent(_parent); + + // Notify Child-Joining event + joined(_parent, child); + } + + public: + void delist(Child *child) + { + assert(child->parent() == _parent); + assert(!empty()); + + // Notify Child-Leaving event + leaving(_parent, child); + + if (child == _head) + { + _head = child->next(); + } + + if (child == _tail) + { + _tail = child->prev(); + } + + // Update parent-child relation + child->parent(nullptr); + } + + public: + void prepend(Child *child) + { + if (empty()) + { + enlist(child); + } + else + { + child->insertBefore(_head); + } + } + + public: + void append(Child *child) + { + if (empty()) + { + enlist(child); + } + else + { + child->insertAfter(_tail); + } + } + + private: + Parent *const _parent; + + private: + Child *_head; + Child *_tail; + }; + + // NOTE Client SHOULD implement this static method + static Head *head(Parent *); + + class Node + { + public: + friend class Head; + + public: + Node() + { + static_assert(std::is_base_of::value, + "Type `Child` must be subclass of `Node`."); + + _prev = nullptr; + _next = nullptr; + } + + public: + virtual ~Node() + { + // Each Child should unlink itself on destruction + // + // NOTE detach invokes "leaving" hook which may access the internal of each Child, + // so it is not safe to invoke detach here + assert(parent() == nullptr); + } + + public: + Parent *parent(void) const { return _parent; } + + private: + Child *curr(void) { return reinterpret_cast(this); } + const Child *curr(void) const { return reinterpret_cast(this); } + + public: + Child *prev(void) const { return _prev; } + Child *next(void) const { return _next; } + + public: + void insertBefore(Node *next) + { + assert(next != nullptr); + assert(next->parent() != nullptr); + assert(head(next->parent()) != nullptr); + + assert(_prev == nullptr); + assert(_next == nullptr); + + // Update the link of the current node + _prev = next->prev(); + _next = next->curr(); + + if (auto prev = next->prev()) + { + prev->_next = curr(); + } + next->_prev = curr(); + + // Update parent-child relation + assert(parent() == nullptr); + head(next->parent())->enlist(curr()); + assert(parent() == next->parent()); + } + + public: + void insertAfter(Node *prev) + { + assert(prev != nullptr); + assert(prev->parent() != nullptr); + assert(head(prev->parent()) != nullptr); + + assert(_prev == nullptr); + assert(_next == nullptr); + + // Update the link of the current node + _prev = prev->curr(); + _next = prev->next(); + + // Update the link of the sibling nodes + if (auto next = prev->next()) + { + next->_prev = curr(); + } + prev->_next = curr(); + + // Update parent-child relation + assert(parent() == nullptr); + head(prev->parent())->enlist(curr()); + assert(parent() == prev->parent()); + }; + + public: + void detach(void) + { + // Update parent-child relation + assert(parent() != nullptr); + assert(head(parent()) != nullptr); + head(parent())->delist(curr()); + assert(parent() == nullptr); + + // Update the link of sibling nodes + if (prev()) + { + prev()->_next = next(); + } + + if (next()) + { + next()->_prev = prev(); + } + + // Update the link of the current node + _prev = nullptr; + _next = nullptr; + } + + private: + // WARN Do NOT invoke this method outside Head::enlist + void parent(Parent *p) { _parent = p; } + + private: + // WARN Do NOT modify this field inside Node. + Parent *_parent = nullptr; + Child *_prev; + Child *_next; + }; +}; + +} // namespace coco + +#endif // __COCO_ADT_DLINKED_LIST_H__ diff --git a/compiler/coco/core/include/coco/ADT/PtrList.h b/compiler/coco/core/include/coco/ADT/PtrList.h new file mode 100644 index 000000000..37fead728 --- /dev/null +++ b/compiler/coco/core/include/coco/ADT/PtrList.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_ADT_PTR_LIST_H__ +#define __COCO_ADT_PTR_LIST_H__ + +#include + +#include + +namespace coco +{ + +template class PtrList +{ +public: + PtrList() = default; + +public: + PtrList(const PtrList &) = delete; + PtrList(PtrList &&) = delete; + +public: + virtual ~PtrList() = default; + +public: + uint32_t size(void) const { return _ptrs.size(); } + +public: + T *at(uint32_t n) const { return _ptrs.at(n); } + +public: + void insert(T *ptr) { _ptrs.emplace_back(ptr); } + +private: + std::vector _ptrs; +}; + +} // namespace coco + +#endif // __COCO_ADT_PTR_LIST_H__ diff --git a/compiler/coco/core/include/coco/ADT/PtrManager.h b/compiler/coco/core/include/coco/ADT/PtrManager.h new file mode 100644 index 000000000..2b254c70a --- /dev/null +++ b/compiler/coco/core/include/coco/ADT/PtrManager.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_ADT_PTR_MANAGER_H__ +#define __COCO_ADT_PTR_MANAGER_H__ + +#include + +#include +#include + +namespace coco +{ + +template class PtrManager +{ +public: + /// @brief Return the number of managed objects + uint32_t size(void) const { return _ptrs.size(); } + +public: + T *at(uint32_t n) const { return _ptrs.at(n).get(); } + +protected: + template U *take(std::unique_ptr &&o) + { + auto res = o.get(); + _ptrs.emplace_back(std::move(o)); + return res; + } + +protected: + std::unique_ptr release(T *ptr) + { + for (auto it = _ptrs.begin(); it != _ptrs.end(); ++it) + { + if (it->get() == ptr) + { + std::unique_ptr res = std::move(*it); + _ptrs.erase(it); + return res; + } + } + + throw std::invalid_argument{"ptr"}; + } + +private: + std::vector> _ptrs; +}; + +} // namespace coco + +#endif // __COCO_ADT_PTR_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR.h b/compiler/coco/core/include/coco/IR.h new file mode 100644 index 000000000..aa7ad5727 --- /dev/null +++ b/compiler/coco/core/include/coco/IR.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_H__ +#define __COCO_IR_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/Object.h" +#include "coco/IR/FeatureLayouts.h" +#include "coco/IR/KernelLayouts.h" + +#include "coco/IR/Op.h" +#include "coco/IR/Instr.h" +#include "coco/IR/Block.h" + +#include "coco/IR/Input.h" +#include "coco/IR/Output.h" + +#include "coco/IR/Module.h" + +#endif // __COCO_IR_H__ diff --git a/compiler/coco/core/include/coco/IR/Arg.h b/compiler/coco/core/include/coco/IR/Arg.h new file mode 100644 index 000000000..fc451a231 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Arg.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_ARG_H__ +#define __COCO_IR_ARG_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/ElemID.h" + +#include +#include +#include + +#include +#include + +namespace coco +{ + +/** + * @brief Base class for NN model arguments (Input/Output) + */ +class Arg +{ +public: + explicit Arg(const nncc::core::ADT::tensor::Shape &shape); + +public: + virtual ~Arg() = default; + +public: + const nncc::core::ADT::tensor::Shape &shape(void) const { return _shape; } + +public: + const std::string &name(void) const { return _name; } + void name(const std::string &s) { _name = s; } + +protected: + virtual void onTake(Bag *) { return; } + virtual void onRelease(Bag *) { return; } + +public: + Bag *bag(void) const { return _bag; } + void bag(Bag *); + +public: + ElemID &at(const nncc::core::ADT::tensor::Index &); + const ElemID &at(const nncc::core::ADT::tensor::Index &) const; + +public: + void reorder(const nncc::core::ADT::tensor::Layout &l); + template void reorder(void) { reorder(LayoutImpl{}); } + +private: + nncc::core::ADT::tensor::Shape const _shape; + +private: + std::string _name; + +private: + Bag *_bag; + std::vector _map; +}; + +} // namespace coco + +#endif // __COCO_IR_ARG_H__ diff --git a/compiler/coco/core/include/coco/IR/Bag.h b/compiler/coco/core/include/coco/IR/Bag.h new file mode 100644 index 000000000..1c86899d7 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Bag.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BAG_H__ +#define __COCO_IR_BAG_H__ + +#include "coco/IR/Entity.h" +#include "coco/IR/ObjectSet.h" +#include "coco/IR/DepSet.h" +#include "coco/IR/ReadSet.h" +#include "coco/IR/UpdateSet.h" +#include "coco/IR/Input.forward.h" +#include "coco/IR/Output.forward.h" +#include "coco/IR/Locatable.h" + +#include + +#include + +namespace coco +{ + +/** + * @brief A collection of (abstracted) elements of the same type + * + * When there are N elements in a bag, we refer to N as the size of this bag, and every + * element in a bag has a unique numeric ID whose range is [0, N). + * + * NOTE 'Bag' is not a container (such as std::vector). 'Bag' just assures that there are + * N elements. It does not state about its value. + * + * NOTE coco IR treats Bag as virtual memory allocation + */ +class Bag final : public Entity +{ +public: + struct Updater : public Locatable + { + virtual ~Updater() = default; + }; + + using UpdaterSet = std::set; + + struct Reader : public Locatable + { + virtual ~Reader() = default; + }; + + using ReaderSet = std::set; + +public: + friend class Dep; + friend class Read; + friend class Update; + friend class Input; + friend class Output; + +public: + explicit Bag(uint32_t size); + +public: + ~Bag(); + +public: + uint32_t size(void) const; + +public: + bool isInput(void) const; + bool isOutput(void) const; + +public: + /// @brief Return the set of Dep links that point to this bag + const DepSet *deps(void) const; + /// @brief Return the set of Read links that point to this bag + const ReadSet *reads(void) const; + /// @brief Return the set of Update links that point to this bag + const UpdateSet *updates(void) const; + +public: + /// @brief Return a valid pointer if this bag is marked as an input of the model + Input *input(void) const { return _input; } + /// @brief Return a valid pointer if this bag is marked as an output of the model + Output *output(void) const { return _output; } + +public: + /** + * @brief Replace all the occurence of a bag (except those in Input/Output) with another bag + * + * NOTE reaplceWith(b) works correctly only when b is neither Input nor Output + */ + void replaceWith(Bag *b); + + /** + * @brief Replace all the occurence of a bag in Object with another bag + * + * NOTE Unlike replaceWith(b), replaceAllDepsWith(b) has no restriction + */ + void replaceAllDepsWith(Bag *); + +private: + // "mutable_" prefix is deliberately introduced below to avoid resolution issue. + // + // Let's assume that two "deps" are overloaded in Bag as follows: + // class Bag + // { + // private: + // DepSet *deps(void); <-- 1 + // public: + // const DepSet *deps(void) const; <-- 2 + // }; + // + // C++ compiler tries to invoke method 1 unless a bag itself is const. Thus, any "deps" calls + // over non-const bags except those calls from friend classes will introduce build error. + + // WARN Only Dep is allowed to access this method + DepSet *mutable_deps(void) { return &_deps; } + // WARN Only Read is allowed to access this method + ReadSet *mutable_reads(void) { return &_reads; } + // WARN Only Update is allowed to access this method + UpdateSet *mutable_updates(void) { return &_updates; } + +private: + // WARN Only Input is allowed to access this method + void input(Input *i) { _input = i; } + // WARN Only Output is allowed to access this method + void output(Output *o) { _output = o; } + +private: + uint32_t _size; + + /** @brief Links to dependent Object(s) */ + DepSet _deps; + /** @brief Direct reads (not through Object) */ + ReadSet _reads; + /** @brief Direct updates (not through Object) */ + UpdateSet _updates; + + Input *_input = nullptr; + Output *_output = nullptr; +}; + +/// @brief Return a set of objects that depends on a given bag +ObjectSet dependent_objects(const Bag *); +/// @brief Return a set of readers that reads a given bag +Bag::ReaderSet readers(const Bag *); +/// @brief Return a set of updaters that updates a given bag +Bag::UpdaterSet updaters(const Bag *); + +} // namespace coco + +#endif // __COCO_IR_BAG_H__ diff --git a/compiler/coco/core/include/coco/IR/BagManager.h b/compiler/coco/core/include/coco/IR/BagManager.h new file mode 100644 index 000000000..6ba644101 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/BagManager.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BAG_MANAGER_H__ +#define __COCO_IR_BAG_MANAGER_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class BagManager final : public PtrManager, public EntityBuilder +{ +public: + BagManager(Module *m = nullptr) { module(m); } + +public: + Bag *create(uint32_t size); + +public: + /** + * @brief Destroy (= deallocate) a Bag entity + * + * NOTE A Bag SHOULD BE detached from IR before destruction + */ + void destroy(Bag *b); +}; + +} // namespace coco + +#endif // __COCO_IR_BAG_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Block.forward.h b/compiler/coco/core/include/coco/IR/Block.forward.h new file mode 100644 index 000000000..6d1793141 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Block.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BLOCK_FORWARD_H__ +#define __COCO_IR_BLOCK_FORWARD_H__ + +namespace coco +{ + +class Block; + +} // namespace coco + +#endif // __COCO_IR_BLOCK_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Block.h b/compiler/coco/core/include/coco/IR/Block.h new file mode 100644 index 000000000..1bb3f47c7 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Block.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BLOCK_H__ +#define __COCO_IR_BLOCK_H__ + +#include "coco/IR/Module.forward.h" +#include "coco/IR/Block.forward.h" +#include "coco/IR/BlockIndex.h" +#include "coco/IR/Instr.h" +#include "coco/IR/Entity.h" + +#include "coco/ADT/DLinkedList.h" + +namespace coco +{ + +using BlockList = DLinkedList::Head; + +/** + * @brief A unit of (grouped) instructions + * + * Block allows backend to manage a set of instructions as one unit, which is useful for H/W that + * has a restriction on code size + */ +class Block final : public DLinkedList::Node, public Entity +{ +public: + friend void DLinkedList::joined(Module *, Block *); + friend void DLinkedList::leaving(Module *, Block *); + +public: + Block() : _instr{this} + { + // DO NOTHING + } + +public: + Block(const Block &) = delete; + Block(Block &&) = delete; + +public: + ~Block() + { + if (parent()) + { + detach(); + } + } + +public: + InstrList *instr(void) { return &_instr; } + const InstrList *instr(void) const { return &_instr; } + +public: + const BlockIndex &index(void) const { return _index; } + +private: + BlockIndex _index; + DLinkedList::Head _instr; +}; + +} // namespace coco + +#endif // __COCO_IR_BLOCK_H__ diff --git a/compiler/coco/core/include/coco/IR/BlockIndex.h b/compiler/coco/core/include/coco/IR/BlockIndex.h new file mode 100644 index 000000000..7deabf488 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/BlockIndex.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BLOCK_INDEX_H__ +#define __COCO_IR_BLOCK_INDEX_H__ + +#include + +namespace coco +{ + +/** + * @brief A BlockIndex denotes the index of a block in a block list + */ +class BlockIndex final +{ +private: + static const uint32_t undefined = 0xffffffff; + +public: + BlockIndex() : _value{undefined} + { + // DO NOTHING + } + +public: + BlockIndex(uint32_t value) { set(value); } + +public: + bool valid(void) const { return _value != undefined; } + +public: + uint32_t value(void) const { return _value; } + +public: + void set(uint32_t value); + void reset(void) { _value = undefined; } + +private: + uint32_t _value; +}; + +static inline bool operator<(const BlockIndex &lhs, const BlockIndex &rhs) +{ + return lhs.value() < rhs.value(); +} + +} // namespace coco + +#endif // __COCO_IR_BLOCK_INDEX_H__ diff --git a/compiler/coco/core/include/coco/IR/BlockManager.h b/compiler/coco/core/include/coco/IR/BlockManager.h new file mode 100644 index 000000000..f81f1f22b --- /dev/null +++ b/compiler/coco/core/include/coco/IR/BlockManager.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_BLOCK_MANAGER_H__ +#define __COCO_IR_BLOCK_MANAGER_H__ + +#include "coco/IR/Block.h" +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class BlockManager final : public PtrManager, public EntityBuilder +{ +public: + BlockManager(Module *m = nullptr) { module(m); } + +public: + Block *create(void); + +public: + /** + * @brief Free 'Block' object + * + * NOTE Block SHOULD be detached from any list before it is destructed + */ + void destroy(Block *); +}; + +} // namespace coco + +#endif // __COCO_IR_BLOCK_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Def.forward.h b/compiler/coco/core/include/coco/IR/Def.forward.h new file mode 100644 index 000000000..93878c658 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Def.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DEF_FORWARD_H__ +#define __COCO_IR_DEF_FORWARD_H__ + +namespace coco +{ + +class Def; + +} // namespace coco + +#endif // __COCO_IR_DEF_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Def.h b/compiler/coco/core/include/coco/IR/Def.h new file mode 100644 index 000000000..d9b1567e5 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Def.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DEF_H__ +#define __COCO_IR_DEF_H__ + +#include "coco/IR/Object.h" + +namespace coco +{ + +class Def final +{ +public: + Def(Object::Producer *producer) : _producer{producer} + { + // DO NOTHING + } + +public: + ~Def() { value(nullptr); } + +public: + Object *value(void) const { return _value; } + +public: + void value(Object *value); + +public: + Object::Producer *producer(void) const { return _producer; } + +private: + Object *_value = nullptr; + Object::Producer *_producer = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_DEF_H__ diff --git a/compiler/coco/core/include/coco/IR/Dep.forward.h b/compiler/coco/core/include/coco/IR/Dep.forward.h new file mode 100644 index 000000000..596ee3126 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Dep.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DEP_FORWARD_H__ +#define __COCO_IR_DEP_FORWARD_H__ + +namespace coco +{ + +class Dep; + +} // namespace coco + +#endif // __COCO_IR_DEP_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Dep.h b/compiler/coco/core/include/coco/IR/Dep.h new file mode 100644 index 000000000..645c3befe --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Dep.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DEP_H__ +#define __COCO_IR_DEP_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/Object.forward.h" + +namespace coco +{ + +/** + * @brief A Dep represents the edge between a Bag and its dependent Object + * + * WARNING A Dep will update dependent Object set (stored BagInfo) only when + * users properly initialize object and link values. + */ +class Dep final +{ +public: + Dep() = default; + +public: + Dep(const Dep &) = delete; + Dep(Dep &&) = delete; + +public: + ~Dep(); + +public: + Bag *bag(void) const { return _bag; } + void bag(Bag *); + +public: + Object *object(void) const { return _object; } + void object(Object *object) { _object = object; } + +private: + Bag *_bag = nullptr; + Object *_object = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_DEP_H__ diff --git a/compiler/coco/core/include/coco/IR/DepSet.h b/compiler/coco/core/include/coco/IR/DepSet.h new file mode 100644 index 000000000..c4e2df979 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/DepSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DEP_SET_H__ +#define __COCO_IR_DEP_SET_H__ + +#include "coco/IR/Dep.forward.h" + +#include + +namespace coco +{ + +using DepSet = std::set; + +} // namespace coco + +#endif // __COCO_IR_DEP_SET_H__ diff --git a/compiler/coco/core/include/coco/IR/ElemID.h b/compiler/coco/core/include/coco/IR/ElemID.h new file mode 100644 index 000000000..7065d13eb --- /dev/null +++ b/compiler/coco/core/include/coco/IR/ElemID.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_ELEM_ID_H__ +#define __COCO_IR_ELEM_ID_H__ + +#include + +namespace coco +{ + +class ElemID final +{ +public: + ElemID() : _value{0xffffffff} + { + // DO NOTHING + } + +public: + explicit ElemID(uint32_t value) : _value{value} + { + // DO NOTHING + } + +public: + uint32_t value(void) const { return _value; } + +private: + uint32_t _value; +}; + +bool operator==(const ElemID &lhs, const ElemID &rhs); +bool operator<(const ElemID &lhs, const ElemID &rhs); + +} // namespace coco + +#endif // __COCO_IR_ELEM_ID_H__ diff --git a/compiler/coco/core/include/coco/IR/Entity.h b/compiler/coco/core/include/coco/IR/Entity.h new file mode 100644 index 000000000..4bf9df651 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Entity.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_ENTITY_H__ +#define __COCO_IR_ENTITY_H__ + +#include "coco/IR/Module.forward.h" + +namespace coco +{ + +/** + * @brief A base class for IR entities + * + * NOTE Each IR entity has a link to a module that it belongs to + */ +class Entity +{ +public: + friend class EntityBuilder; + +public: + virtual ~Entity() = default; + +public: + Module *module(void) const { return _module; } + +private: + // WARN Only EntityBuilder is allowed to access this method + void module(Module *m) { _module = m; } + +private: + Module *_module = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_ENTITY_H__ diff --git a/compiler/coco/core/include/coco/IR/EntityBuilder.h b/compiler/coco/core/include/coco/IR/EntityBuilder.h new file mode 100644 index 000000000..161f3f294 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/EntityBuilder.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_ENTITY_BUILDER_H__ +#define __COCO_IR_ENTITY_BUILDER_H__ + +#include "coco/IR/Entity.h" +#include "coco/IR/Module.forward.h" + +namespace coco +{ + +/** + * @brief A base class for IR entity builders + * + * NOTE Only EntityBuilder is allowed to update module field of each Entity + */ +class EntityBuilder +{ +public: + virtual ~EntityBuilder() = default; + +protected: + Module *module(void) const { return _module; } + + void module(Module *m) { _module = m; } + void modulize(Entity *entity) const { entity->module(_module); } + +private: + Module *_module = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_ENTITY_BUILDER_H__ diff --git a/compiler/coco/core/include/coco/IR/EntityManager.h b/compiler/coco/core/include/coco/IR/EntityManager.h new file mode 100644 index 000000000..e76dec7aa --- /dev/null +++ b/compiler/coco/core/include/coco/IR/EntityManager.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_ENTITY_MANAGER_H__ +#define __COCO_IR_ENTITY_MANAGER_H__ + +#include "coco/IR/BagManager.h" +#include "coco/IR/ObjectManager.h" + +#include "coco/IR/OpManager.h" +#include "coco/IR/InstrManager.h" +#include "coco/IR/BlockManager.h" + +#include "coco/IR/InputManager.h" +#include "coco/IR/OutputManager.h" + +namespace coco +{ + +/** + * @brief Meta (lifetime) manager interface + * + * EntityManager is referred as meta manager as it is a gateway to other + * managers. + */ +struct EntityManager +{ + virtual ~EntityManager() = default; + + virtual BagManager *bag(void) = 0; + virtual const BagManager *bag(void) const = 0; + + virtual ObjectManager *object(void) = 0; + virtual const ObjectManager *object(void) const = 0; + + virtual OpManager *op(void) = 0; + virtual const OpManager *op(void) const = 0; + + virtual InstrManager *instr(void) = 0; + virtual const InstrManager *instr(void) const = 0; + + virtual BlockManager *block(void) = 0; + virtual const BlockManager *block(void) const = 0; + + virtual InputManager *input(void) = 0; + virtual const InputManager *input(void) const = 0; + + virtual OutputManager *output(void) = 0; + virtual const OutputManager *output(void) const = 0; +}; + +} // namespace coco + +#endif // __COCO_IR_ENTITY_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/FeatureLayout.h b/compiler/coco/core/include/coco/IR/FeatureLayout.h new file mode 100644 index 000000000..63f02c8ba --- /dev/null +++ b/compiler/coco/core/include/coco/IR/FeatureLayout.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_FEATURE_LAYOUT_H__ +#define __COCO_IR_FEATURE_LAYOUT_H__ + +#include "coco/IR/ElemID.h" +#include "coco/IR/FeatureShape.h" + +namespace coco +{ + +/** + * @brief A FeatureLayout connects each feature index to a Bag element + * + * NOTE FeatureLayout is an immutable interface + */ +struct FeatureLayout +{ + struct ID + { + virtual ~ID() = default; + }; + + virtual ~FeatureLayout() = default; + + virtual const ID *id(void) const = 0; + + virtual const FeatureShape &shape(void) const = 0; + + uint32_t batch(void) const { return shape().batch(); } + uint32_t depth(void) const { return shape().depth(); } + uint32_t height(void) const { return shape().height(); } + uint32_t width(void) const { return shape().width(); } + + virtual ElemID at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const = 0; +}; + +} // namespace coco + +#endif // __COCO_IR_FEATURE_LAYOUT_H__ diff --git a/compiler/coco/core/include/coco/IR/FeatureLayouts.h b/compiler/coco/core/include/coco/IR/FeatureLayouts.h new file mode 100644 index 000000000..23b9c4919 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/FeatureLayouts.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_FEATURE_LAYOUTS_H__ +#define __COCO_IR_FEATURE_LAYOUTS_H__ + +#include "coco/IR/FeatureLayout.h" + +#include + +#include +#include + +namespace coco +{ +namespace FeatureLayouts +{ + +/** + * @brief BCHW Feature Layout + */ +class BCHW final : public FeatureLayout +{ +private: + BCHW(const FeatureShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + static const FeatureLayout::ID *uid(void); + const FeatureLayout::ID *id(void) const override { return uid(); } + + const FeatureShape &shape(void) const override { return _shape; } + + ElemID at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const override; + +private: + FeatureShape _shape; + +public: + static std::unique_ptr create(const nncc::core::ADT::feature::Shape &shape); +}; + +/** + * @brief BHWC Feature Layout + */ +class BHWC : public coco::FeatureLayout +{ +private: + BHWC(const FeatureShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + static const FeatureLayout::ID *uid(void); + const FeatureLayout::ID *id(void) const override { return uid(); } + + const FeatureShape &shape(void) const override { return _shape; } + + coco::ElemID at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const override; + +private: + FeatureShape _shape; + +public: + static std::unique_ptr create(const nncc::core::ADT::feature::Shape &shape); + static std::unique_ptr create(const FeatureShape &shape); +}; + +/** + * @brief BC (Channel-wise Channel-major) Feature Layout + * + * 1. A layout is said to be channel-wise if the following holds: + * + * For each pair of valid feature index I and J, + * at(I) == at(J) if batch(I) == batch(J) and channel(I) == channel(J) + * + * 2. A layout is said to be channel-major if the followings hold: + * + * For each pair of valid feature index I and J, + * at(I) + 1 == at(J) if batch(I) == batch(J) and channel(I) + 1 == channel(J) + * + * For each pair of valid feature index I and J, + * at(I) + 1 == at(J) if batch(I) + 1 == batch(J), channel(I) == depth - 1, and channel(J) == 0 + */ +class BC : public coco::FeatureLayout +{ +private: + BC(const FeatureShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + static const FeatureLayout::ID *uid(void); + const FeatureLayout::ID *id(void) const override { return uid(); } + + const FeatureShape &shape(void) const override { return _shape; } + + coco::ElemID at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const override; + +private: + FeatureShape _shape; + +public: + static std::unique_ptr create(const nncc::core::ADT::feature::Shape &shape); +}; + +/** + * @brief Generic Feature Layout + */ +class Generic final : public FeatureLayout +{ +private: + Generic(const FeatureShape &shape); + +public: + static const FeatureLayout::ID *uid(void); + const FeatureLayout::ID *id(void) const override { return uid(); } + + const FeatureShape &shape(void) const override { return _shape; } + + ElemID &at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col); + ElemID at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const override; + + void reorder(const nncc::core::ADT::feature::Layout &l); + +private: + uint32_t offset(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const; + +private: + FeatureShape _shape; + +private: + std::vector _content; + +public: + static std::unique_ptr create(const nncc::core::ADT::feature::Shape &shape); +}; + +} // namespace FeatureLayouts +} // namespace coco + +#endif // __COCO_IR_FEATURE_LAYOUTS_H__ diff --git a/compiler/coco/core/include/coco/IR/FeatureObject.forward.h b/compiler/coco/core/include/coco/IR/FeatureObject.forward.h new file mode 100644 index 000000000..41477e853 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/FeatureObject.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_FEATURE_OBJECT_FORWARD_H__ +#define __COCO_IR_FEATURE_OBJECT_FORWARD_H__ + +namespace coco +{ + +class FeatureObject; + +} // namespace coco + +#endif // __COCO_IR_FEATURE_OBJECT_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/FeatureObject.h b/compiler/coco/core/include/coco/IR/FeatureObject.h new file mode 100644 index 000000000..f4244d9be --- /dev/null +++ b/compiler/coco/core/include/coco/IR/FeatureObject.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_FEATURE_OBJECT_H__ +#define __COCO_IR_FEATURE_OBJECT_H__ + +#include "coco/IR/Object.h" +#include "coco/IR/FeatureShape.h" +#include "coco/IR/FeatureLayout.h" +#include "coco/IR/ElemID.h" + +#include + +#include + +namespace coco +{ + +/** + * @brief FeatureMap values (used in CNN) + */ +class FeatureObject final : public Object +{ +public: + FeatureObject() = default; + +public: + ~FeatureObject(); + +public: + Object::Kind kind(void) const override { return Object::Kind::Feature; } + +public: + FeatureObject *asFeature(void) override { return this; } + const FeatureObject *asFeature(void) const override { return this; } + +public: + const FeatureShape &shape(void) const; + +public: + const FeatureLayout *layout(void) const { return _layout.get(); } + void layout(std::unique_ptr &&l) { _layout = std::move(l); } + +private: + std::unique_ptr _layout; +}; + +} // namespace coco + +#endif // __COCO_IR_FEATURE_OBJECT_H__ diff --git a/compiler/coco/core/include/coco/IR/FeatureShape.h b/compiler/coco/core/include/coco/IR/FeatureShape.h new file mode 100644 index 000000000..015fc709d --- /dev/null +++ b/compiler/coco/core/include/coco/IR/FeatureShape.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_FEATURE_SHAPE_H__ +#define __COCO_IR_FEATURE_SHAPE_H__ + +#include + +namespace coco +{ + +/** + * @brief The shape of a feature map + * + * TODO Implement coco's own FeatureShape without "nncc::core::ADT::feature::Shape" + */ +class FeatureShape : public nncc::core::ADT::feature::Shape +{ +public: + FeatureShape(uint32_t depth, uint32_t height, uint32_t width) + : Shape{depth, height, width}, _batch{1} + { + // DO NOTHING + } + + FeatureShape(uint32_t batch, uint32_t depth, uint32_t height, uint32_t width) + : Shape{depth, height, width}, _batch{batch} + { + // DO NOTHING + } + + FeatureShape(const nncc::core::ADT::feature::Shape &shape) : Shape{shape}, _batch{1} + { + // DO NOTHING + } + +public: + uint32_t batch(void) const { return _batch; } + +private: + uint32_t _batch; +}; + +static inline bool operator==(const FeatureShape &lhs, const FeatureShape &rhs) +{ + return (lhs.batch() == rhs.batch()) && (lhs.depth() == rhs.depth()) && + (lhs.height() == rhs.height()) && (lhs.width() == rhs.width()); +} + +static inline bool operator!=(const FeatureShape &lhs, const FeatureShape &rhs) +{ + return !(lhs == rhs); +} + +} // namespace coco + +#endif // __COCO_IR_FEATURE_SHAPE_H__ diff --git a/compiler/coco/core/include/coco/IR/Input.forward.h b/compiler/coco/core/include/coco/IR/Input.forward.h new file mode 100644 index 000000000..4b529cddf --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Input.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INPUT_FORWARD_H__ +#define __COCO_IR_INPUT_FORWARD_H__ + +namespace coco +{ + +class Input; + +} // namespace coco + +#endif // __COCO_IR_INPUT_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Input.h b/compiler/coco/core/include/coco/IR/Input.h new file mode 100644 index 000000000..ef8e88c9d --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Input.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INPUT_H__ +#define __COCO_IR_INPUT_H__ + +#include "coco/IR/Arg.h" +#include "coco/IR/Entity.h" + +#include +#include + +#include +#include + +namespace coco +{ + +class Input final : public Arg, public Entity +{ +public: + Input(const nncc::core::ADT::tensor::Shape &shape); + +private: + void onTake(Bag *) override; + void onRelease(Bag *) override; +}; + +} // namespace coco + +#endif // __COCO_IR_INPUT_H__ diff --git a/compiler/coco/core/include/coco/IR/InputList.h b/compiler/coco/core/include/coco/IR/InputList.h new file mode 100644 index 000000000..cd6337a5a --- /dev/null +++ b/compiler/coco/core/include/coco/IR/InputList.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INPUT_LIST_H__ +#define __COCO_IR_INPUT_LIST_H__ + +#include "coco/IR/Input.h" + +#include "coco/ADT/PtrList.h" + +namespace coco +{ + +using InputList = PtrList; + +} // namespace coco + +#endif // __COCO_IR_INPUT_LIST_H__ diff --git a/compiler/coco/core/include/coco/IR/InputManager.h b/compiler/coco/core/include/coco/IR/InputManager.h new file mode 100644 index 000000000..bfbd712b5 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/InputManager.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INPUT_MANAGER_H__ +#define __COCO_IR_INPUT_MANAGER_H__ + +#include "coco/IR/Input.h" +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class InputManager final : public PtrManager, public EntityBuilder +{ +public: + InputManager(Module *m = nullptr) { module(m); } + +public: + Input *create(const nncc::core::ADT::tensor::Shape &); +}; + +} // namespace coco + +#endif // __COCO_IR_INPUT_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Instr.forward.h b/compiler/coco/core/include/coco/IR/Instr.forward.h new file mode 100644 index 000000000..4043970db --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Instr.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INSTR_FORWARD_H__ +#define __COCO_IR_INSTR_FORWARD_H__ + +namespace coco +{ + +// WARNING This header should be aligned with Instr.h +class Instr; + +} // namespace coco + +#endif // __COCO_IR_INSTR_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Instr.h b/compiler/coco/core/include/coco/IR/Instr.h new file mode 100644 index 000000000..fc1cc332d --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Instr.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INSTR_H__ +#define __COCO_IR_INSTR_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/Block.forward.h" +#include "coco/IR/Instr.forward.h" +#include "coco/IR/InstrIndex.h" +#include "coco/IR/Entity.h" + +#include "coco/ADT/DLinkedList.h" + +#include +#include + +namespace coco +{ + +#define INSTR(Name) class Name; +#include "coco/IR/Instr.lst" +#undef INSTR + +using InstrList = coco::DLinkedList::Head; + +/** + * @brief Base interface on explicit computation steps in coco IR + * + * NOTE Input/output is explicit in Instr, but implicit in Op + * NOTE Instr is may (not always) be a combination of multiple NN operations + * + * One may find a set of supported instructions from "Instrs.h" + * + * >> How to add a new base instruction in coco IR << + * + * To introduce a new instruction (whose name is INS), + * 1. Append "INSTR(INS)" to "Instr.lst" + * 2. Declare class INS which inherits Instr class in "Instrs.h" + * NOTE This class SHOULD be default constructible + * + */ +class Instr : public coco::DLinkedList::Node, public Entity +{ +public: + friend void DLinkedList::joined(Block *, Instr *); + friend void DLinkedList::leaving(Block *, Instr *); + +public: + Instr() = default; + +public: + Instr(const Instr &) = delete; + Instr(Instr &&) = delete; + +public: + virtual ~Instr() + { + if (parent()) + { + // NOTE It is safe to invoke detach here (although "Instr" is not a final class) + // as "leaving" hook accesses only the internal of "Instr" class + detach(); + } + } + +public: +#define INSTR(Name) \ + virtual Name *as##Name(void) { return nullptr; } \ + virtual const Name *as##Name(void) const { return nullptr; } +#include "coco/IR/Instr.lst" +#undef INSTR + +public: + /** + * @brief Instr visitor interface + * + * WARN Use this interface only for coco-internal classes + * (to minimize changes upon Instr extension) + */ + template struct IVisitor + { + virtual ~IVisitor() = default; + +#define INSTR(Name) virtual T visit(const Name *) = 0; +#include "coco/IR/Instr.lst" +#undef INSTR + }; + + template struct Visitor : public IVisitor + { + virtual ~Visitor() = default; + +#define INSTR(Name) \ + T visit(const Name *) override { throw std::runtime_error{"NYI"}; } +#include "coco/IR/Instr.lst" +#undef INSTR + }; + +public: + template T accept(IVisitor *v) const + { +#define INSTR(Name) \ + if (auto ins = as##Name()) \ + { \ + return v->visit(ins); \ + } +#include "coco/IR/Instr.lst" +#undef INSTR + throw std::runtime_error{"unreachable"}; + } + + template T accept(IVisitor &v) const { return accept(&v); } + template T accept(IVisitor &&v) const { return accept(&v); } + +public: + const InstrIndex &index(void) const { return _index; } + +private: + InstrIndex _index; +}; + +/** + * @brief Return true if a given instruction is of T type + * + * @note "ins" cannot be a null pointer + */ +template bool isa(const Instr *ins) +{ + assert(ins != nullptr); + return dynamic_cast(ins) != nullptr; +} + +/** + * @brief Cast as a derived instruction + * + * @note "safe_cast(ins)" returns a null pointer if "ins" is not of T type + * @note "safe_cast(ins)" returns a null pointer if "ins" is a null pointer + */ +template T *safe_cast(Instr *ins) +{ + // NOTE dynamic_cast(nullptr) returns nullptr + return dynamic_cast(ins); +} + +} // namespace coco + +#endif // __COCO_IR_INSTR_H__ diff --git a/compiler/coco/core/include/coco/IR/Instr.lst b/compiler/coco/core/include/coco/IR/Instr.lst new file mode 100644 index 000000000..f13a65bf2 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Instr.lst @@ -0,0 +1,9 @@ +#ifndef INSTR +#error Define INSTR first +#endif // INSTR + +// INSTR(Name) + +INSTR(Eval) +INSTR(Shuffle) +INSTR(Copy) diff --git a/compiler/coco/core/include/coco/IR/InstrIndex.h b/compiler/coco/core/include/coco/IR/InstrIndex.h new file mode 100644 index 000000000..a61d97cad --- /dev/null +++ b/compiler/coco/core/include/coco/IR/InstrIndex.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INSTR_INDEX_H__ +#define __COCO_IR_INSTR_INDEX_H__ + +#include + +namespace coco +{ + +/** + * @brief A InstrIndex denotes the index of an instruction in an instruction list + */ +class InstrIndex final +{ +private: + static const uint32_t undefined = 0xffffffff; + +public: + InstrIndex() : _value{undefined} + { + // DO NOTHING + } + +public: + InstrIndex(uint32_t value) { set(value); } + +public: + bool valid(void) const { return _value != undefined; } + +public: + uint32_t value(void) const { return _value; } + +public: + void set(uint32_t value); + void reset(void) { _value = undefined; } + +private: + uint32_t _value; +}; + +static inline bool operator<(const InstrIndex &lhs, const InstrIndex &rhs) +{ + return lhs.value() < rhs.value(); +} + +} // namespace coco + +#endif // __COCO_IR_INSTR_INDEX_H__ diff --git a/compiler/coco/core/include/coco/IR/InstrManager.h b/compiler/coco/core/include/coco/IR/InstrManager.h new file mode 100644 index 000000000..537467ae2 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/InstrManager.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INSTR_MANAGER_H__ +#define __COCO_IR_INSTR_MANAGER_H__ + +#include "coco/IR/Instr.h" +#include "coco/IR/Instrs.h" + +#include "coco/IR/Op.forward.h" + +#include "coco/IR/Bag.h" + +#include "coco/IR/Object.forward.h" + +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class InstrManager final : public PtrManager, public EntityBuilder +{ +public: + InstrManager(Module *m = nullptr) { module(m); } + +public: + template Ins *create(void); + +public: + /** + * @brief Destroy (= deallocate) an Instr instance + * + * NOTE destroy(ins) WILL NOT update ins->parent(). An Instruction SHOULD BE detacted from a + * module before destroy call + */ + void destroy(Instr *); +}; + +// +// Every instruction class SHOULD be default constructible +// +template Ins *InstrManager::create(void) +{ + auto ins = new Ins; + modulize(ins); + return take(std::unique_ptr(ins)); +} + +} // namespace coco + +#endif // __COCO_IR_INSTR_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Instrs.h b/compiler/coco/core/include/coco/IR/Instrs.h new file mode 100644 index 000000000..9245443e9 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Instrs.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_INSTRS_H__ +#define __COCO_IR_INSTRS_H__ + +#include "coco/IR/Instr.h" + +#include "coco/IR/ElemID.h" + +#include "coco/IR/Bag.h" +#include "coco/IR/Object.h" + +#include "coco/IR/Def.h" +#include "coco/IR/Use.h" +#include "coco/IR/Read.h" +#include "coco/IR/Update.h" + +#include "coco/IR/Step.h" + +#include + +namespace coco +{ + +/** + * @brief Evaluate an Object from a given Op + */ +class Eval final : public Instr, public Object::Producer +{ +public: + explicit Eval(); + +public: + Eval *asEval(void) override { return this; } + const Eval *asEval(void) const override { return this; } + +public: + Instr *loc(void) override { return this; } + +public: + Object *out(void) const { return _out.value(); } + void out(Object *obj) { _out.value(obj); } + +public: + Op *op(void) const { return _step.op(); } + void op(Op *op) { _step.op(op); } + +private: + Def _out; + Step _step; +}; + +/** + * @brief Index-wise element transfer between two objects + * + * Given two objects "src" and "dst" of the same kind/shape, "copy(src, dst)" + * denotes index-wise element transfer. + * + * For example, the following pseudo-code describes "copy(src, dat)" + * when both src and dst are a feature map of the shape B x C x H x W: + * + * for each valid index b, ch, row, col: + * load the "src->at(b, ch, row, col)"-th element from bag(src) + * store it as the "dst->at(b, ch, row, col)"-th element of bag(dst) + * + * In principle, "copy" is unnecessary as it is always possible to rewrite "copy" + * as a "shuffle" below. However, "shuffle"-based optimization is too heavy as it + * requires much of iterations. + */ +class Copy final : public Instr, public Object::Producer, public Object::Consumer +{ +public: + Copy() : _from{this}, _into{this} + { + // DO NOTHING + } + +public: + Copy *asCopy(void) override { return this; } + const Copy *asCopy(void) const override { return this; } + +public: + Instr *loc(void) override { return this; } + +public: + Object *from(void) const { return _from.value(); } + void from(Object *o) { _from.value(o); } + +public: + Object *into(void) const { return _into.value(); } + void into(Object *o) { _into.value(o); } + +private: + Use _from; + Def _into; +}; + +/** + * @brief Generic element transfer + */ +class Shuffle final : public Instr, public Bag::Reader, public Bag::Updater +{ +public: + Shuffle() : _from{this}, _into{this} + { + // DO NOTHING + } + +public: + Shuffle *asShuffle(void) override { return this; } + const Shuffle *asShuffle(void) const override { return this; } + +public: + Instr *loc(void) override { return this; } + +public: + Bag *from(void) const { return _from.bag(); } + void from(Bag *bag); + +public: + Bag *into(void) const { return _into.bag(); } + void into(Bag *); + +public: + /** + * @brief Return the number of Element-wise transfers + * + * NOTE size() SHOULD BE identical to range().size() + */ + uint32_t size(void) const; + + /// @brief Return a set of elements in the destination bag that Shuffle will update + std::set range(void) const; + +public: + /// @brief Return true if a given elem is updated after execution + bool defined(const ElemID &dst) const { return _content.find(dst) != _content.end(); } + +public: + /** + * Let M be the return of at(N). This means that N-th element in the destination + * bag will be filled with the value of M-th element in the source bag. + * + * NOTE at(n) may be undefined on partial shuffle + */ + const ElemID &at(const ElemID &dst) const { return _content.at(dst); } + +public: + void insert(const ElemID &from, const ElemID &into); + +private: + Read _from; + Update _into; + +private: + std::map _content; +}; + +} // namespace coco + +#endif // __COCO_IR_INSTRS_H__ diff --git a/compiler/coco/core/include/coco/IR/KernelLayout.h b/compiler/coco/core/include/coco/IR/KernelLayout.h new file mode 100644 index 000000000..49aaf1a81 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/KernelLayout.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_KERNEL_LAYOUT_H__ +#define __COCO_IR_KERNEL_LAYOUT_H__ + +#include "coco/IR/ElemID.h" + +#include + +namespace coco +{ + +/** + * @brief A KernelLayout connectes each kernel index to an element (in a bag) + * + * NOTE KernelLayout is an immutable interface + */ +struct KernelLayout +{ + struct ID + { + virtual ~ID() = default; + }; + + virtual ~KernelLayout() = default; + + /** + * @brief Return the identifier of each layout + * + * REQUIRED + * + * Given l1 and l2 of KernelLayout * type, + * typeid(*l1) == typeif(*l2) SHOULD hold if l1->id() == l2->id() holds. + */ + virtual const ID *id(void) const = 0; + + virtual const nncc::core::ADT::kernel::Shape &shape(void) const = 0; + + virtual ElemID at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const = 0; +}; + +} // namespace coco + +#endif // __COCO_IR_KERNEL_LAYOUT_H__ diff --git a/compiler/coco/core/include/coco/IR/KernelLayouts.h b/compiler/coco/core/include/coco/IR/KernelLayouts.h new file mode 100644 index 000000000..0a04cf163 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/KernelLayouts.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_KERNEL_LAYOUTS_H__ +#define __COCO_IR_KERNEL_LAYOUTS_H__ + +#include "coco/IR/KernelLayout.h" + +#include + +#include +#include + +namespace coco +{ +namespace KernelLayouts +{ + +/** + * @brief NCHW Kernel Layout + */ +class NCHW final : public KernelLayout +{ +private: + NCHW(const nncc::core::ADT::kernel::Shape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + static const KernelLayout::ID *uid(void); + const KernelLayout::ID *id(void) const override { return uid(); } + + const nncc::core::ADT::kernel::Shape &shape(void) const override { return _shape; } + + ElemID at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const override; + +private: + nncc::core::ADT::kernel::Shape _shape; + +public: + static std::unique_ptr create(const nncc::core::ADT::kernel::Shape &shape); +}; + +/** + * @brief NHWC Kernel Layout + */ +class NHWC final : public KernelLayout +{ +private: + NHWC(const nncc::core::ADT::kernel::Shape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + static const KernelLayout::ID *uid(void); + const KernelLayout::ID *id(void) const override { return uid(); } + + const nncc::core::ADT::kernel::Shape &shape(void) const override { return _shape; } + + ElemID at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const override; + +private: + nncc::core::ADT::kernel::Shape _shape; + +public: + static std::unique_ptr create(const nncc::core::ADT::kernel::Shape &shape); +}; + +/** + * @brief Generic Kernel Layout + */ +class Generic final : public KernelLayout +{ +private: + Generic(const nncc::core::ADT::kernel::Shape &shape); + +public: + static const KernelLayout::ID *uid(void); + const KernelLayout::ID *id(void) const override { return uid(); } + + const nncc::core::ADT::kernel::Shape &shape(void) const override { return _shape; } + + ElemID &at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col); + ElemID at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const override; + + void reorder(const nncc::core::ADT::kernel::Layout &l); + template void reorder(void) { reorder(LayoutImpl{}); } + +private: + nncc::core::ADT::kernel::Shape _shape; + +private: + std::vector _content; + +public: + static std::unique_ptr create(const nncc::core::ADT::kernel::Shape &shape); +}; + +} // namespace KernelLayouts +} // namespace coco + +#endif // __COCO_IR_KERNEL_LAYOUTS_H__ diff --git a/compiler/coco/core/include/coco/IR/KernelObject.forward.h b/compiler/coco/core/include/coco/IR/KernelObject.forward.h new file mode 100644 index 000000000..10fbac4ca --- /dev/null +++ b/compiler/coco/core/include/coco/IR/KernelObject.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_KERNEL_OBJECT_FORWARD_H__ +#define __COCO_IR_KERNEL_OBJECT_FORWARD_H__ + +namespace coco +{ + +class KernelObject; + +} // namespace coco + +#endif // __COCO_IR_KERNEL_OBJECT_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/KernelObject.h b/compiler/coco/core/include/coco/IR/KernelObject.h new file mode 100644 index 000000000..2ec0cee0b --- /dev/null +++ b/compiler/coco/core/include/coco/IR/KernelObject.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_KERNEL_OBJECT_H__ +#define __COCO_IR_KERNEL_OBJECT_H__ + +#include "coco/IR/Object.h" +#include "coco/IR/KernelLayout.h" +#include "coco/IR/ElemID.h" + +#include +#include + +namespace coco +{ + +/** + * @brief Convolution Kernel (in CNN) values + */ +class KernelObject final : public Object +{ +public: + KernelObject() = default; + explicit KernelObject(const nncc::core::ADT::kernel::Shape &shape); + +public: + virtual ~KernelObject(); + +public: + Object::Kind kind(void) const override { return Object::Kind::Kernel; } + +public: + KernelObject *asKernel(void) override { return this; } + const KernelObject *asKernel(void) const override { return this; } + +public: + const nncc::core::ADT::kernel::Shape &shape(void) const; + +public: + ElemID at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const; + +public: + const KernelLayout *layout(void) const { return _layout.get(); } + void layout(std::unique_ptr &&l) { _layout = std::move(l); } + +private: + std::unique_ptr _layout; +}; + +} // namespace coco + +#endif // __COCO_IR_KERNEL_OBJECT_H__ diff --git a/compiler/coco/core/include/coco/IR/Locatable.h b/compiler/coco/core/include/coco/IR/Locatable.h new file mode 100644 index 000000000..b80a4a360 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Locatable.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_LOCATABLE_H__ +#define __COCO_IR_LOCATABLE_H__ + +#include "coco/IR/Instr.forward.h" + +namespace coco +{ + +/** + * @brief Return the associated instruction if exists. + */ +struct Locatable +{ + virtual ~Locatable() = default; + + virtual Instr *loc(void) = 0; +}; + +} // namespace coco + +#endif // __COCO_IR_LOCATABLE_H__ diff --git a/compiler/coco/core/include/coco/IR/Module.forward.h b/compiler/coco/core/include/coco/IR/Module.forward.h new file mode 100644 index 000000000..94f8cc7d2 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Module.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_MODULE_FORWARD_H__ +#define __COCO_IR_MODULE_FORWARD_H__ + +namespace coco +{ + +class Module; + +} // namespace coco + +#endif // __COCO_IR_MODULE_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Module.h b/compiler/coco/core/include/coco/IR/Module.h new file mode 100644 index 000000000..9eb0b248b --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Module.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_MODULE_H__ +#define __COCO_IR_MODULE_H__ + +#include "coco/IR/EntityManager.h" +#include "coco/IR/Block.h" +#include "coco/IR/InputList.h" +#include "coco/IR/OutputList.h" + +#include + +namespace coco +{ + +/** + * @brief Top-level element of coco IR which represents a neural network + */ +class Module +{ +public: + Module() = default; + +public: + Module(const Module &) = delete; + Module(Module &&) = delete; + +public: + virtual ~Module() = default; + +public: + virtual EntityManager *entity(void) = 0; + virtual const EntityManager *entity(void) const = 0; + +public: + virtual BlockList *block(void) = 0; + virtual const BlockList *block(void) const = 0; + +public: + virtual InputList *input(void) = 0; + virtual const InputList *input(void) const = 0; + +public: + virtual OutputList *output(void) = 0; + virtual const OutputList *output(void) const = 0; + +public: + static std::unique_ptr create(void); +}; + +} // namespace coco + +#endif // __COCO_IR_MODULE_H__ diff --git a/compiler/coco/core/include/coco/IR/Object.forward.h b/compiler/coco/core/include/coco/IR/Object.forward.h new file mode 100644 index 000000000..d9a6c0422 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Object.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OBJECT_FORWARD_H__ +#define __COCO_IR_OBJECT_FORWARD_H__ + +namespace coco +{ + +class Object; + +} // namespace coco + +#endif // __COCO_IR_OBJECT_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Object.h b/compiler/coco/core/include/coco/IR/Object.h new file mode 100644 index 000000000..617e8a198 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Object.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OBJECT_H__ +#define __COCO_IR_OBJECT_H__ + +#include "coco/IR/Entity.h" +#include "coco/IR/Bag.h" +#include "coco/IR/Dep.h" +#include "coco/IR/Def.forward.h" +#include "coco/IR/UseSet.h" + +#include "coco/IR/FeatureObject.forward.h" +#include "coco/IR/KernelObject.forward.h" + +#include + +namespace coco +{ + +/** + * @brief Base interface on all typed NN values + */ +class Object : public Entity +{ +public: + friend class Def; + friend class Use; + +public: + enum class Kind + { + Unknown, + Feature, + Kernel, + }; + +public: + struct Producer : public Bag::Updater + { + virtual ~Producer() = default; + }; + + struct Consumer : public Bag::Reader + { + virtual ~Consumer() = default; + }; + + using ConsumerSet = std::set; + +public: + Object(); + +public: + virtual ~Object() = default; + +public: + virtual Kind kind(void) const { return Kind::Unknown; } + +public: + coco::Bag *bag(void) const { return _dep.bag(); } + void bag(coco::Bag *bag) { _dep.bag(bag); } + +public: + virtual FeatureObject *asFeature(void) { return nullptr; } + virtual const FeatureObject *asFeature(void) const { return nullptr; } + + virtual KernelObject *asKernel(void) { return nullptr; } + virtual const KernelObject *asKernel(void) const { return nullptr; } + +public: + Def *def(void) const; + const UseSet *uses(void) const; + +private: + /** + * @brief Update the link to a producer + * + * WARN Only Def class is allowed to access this method + */ + void def(Def *d); + + // NOTE "mutable_" prefix is introduced to avoid resolution issue similarly as in Bag + // WARN Only Use class is allowed to access this method + UseSet *mutable_uses(void); + +private: + Dep _dep; + Def *_def = nullptr; + UseSet _uses; +}; + +/** + * @brief Check whether a given object is of type T + * + * The example below shows how to use this "isa" helper: + * auto obj = new FeatureObject{}; + * + * if (isa()) + * { + * std::cout << "FeatureObject" << std::endl; + * } + */ +template bool isa(const Object *); + +/** + * @brief Cast a generic object as a specific one + * + * "cast(o)" accepts only a valid object pointer "o" that "isa(o)" holds + * - Then, "cast(o)" always returns a valid object pointer. + */ +template T *cast(Object *); + +/** + * @brief Cast a generic object as a specific one + * + * Unlike "cast", "safe_cast" accepts any object pointer + * - "safe_cast(nullptr)" returns "nullptr" + * - "safe_cast(o)" returns "nullptr" if "isa(o)" does not hold + */ +template T *safe_cast(Object *); + +/// @brief Return the producer of a given object if it exists +Object::Producer *producer(const Object *); + +/// @brief Return a set of consumers of a given object. +Object::ConsumerSet consumers(const Object *); + +} // namespace coco + +#endif // __COCO_IR_OBJECT_H__ diff --git a/compiler/coco/core/include/coco/IR/ObjectManager.h b/compiler/coco/core/include/coco/IR/ObjectManager.h new file mode 100644 index 000000000..a05b724ce --- /dev/null +++ b/compiler/coco/core/include/coco/IR/ObjectManager.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OBJECT_MANAGER_H__ +#define __COCO_IR_OBJECT_MANAGER_H__ + +#include "coco/IR/Object.h" +#include "coco/IR/FeatureShape.h" +#include "coco/IR/FeatureObject.h" +#include "coco/IR/KernelObject.forward.h" +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +#include + +namespace coco +{ + +class ObjectManager final : public PtrManager, public EntityBuilder +{ +public: + ObjectManager(Module *m = nullptr) { module(m); } + +public: + template T *create(void); + +public: + /** + * @brief Destroy (= deallocate) an Object entity + * + * NOTE An Object SHOULD HAVE NO DEF & USES to be destructed + * NOTE An Object WILL BE unlinked from its dependent bag (if has) on destruction + */ + void destroy(Object *o); +}; + +} // namespace coco + +#endif // __COCO_IR_OBJECT_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/ObjectSet.h b/compiler/coco/core/include/coco/IR/ObjectSet.h new file mode 100644 index 000000000..d97781996 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/ObjectSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OBJECT_SET_H__ +#define __COCO_IR_OBJECT_SET_H__ + +#include "coco/IR/Object.forward.h" + +#include + +namespace coco +{ + +using ObjectSet = std::set; + +} // namespace coco + +#endif // __COCO_IR_OBJECT_SET_H__ diff --git a/compiler/coco/core/include/coco/IR/Op.forward.h b/compiler/coco/core/include/coco/IR/Op.forward.h new file mode 100644 index 000000000..9ba3c94e3 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Op.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OP_FORWARD_H__ +#define __COCO_IR_OP_FORWARD_H__ + +namespace coco +{ + +struct Op; + +} // namespace coco + +#endif // __COCO_IR_OP_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Op.h b/compiler/coco/core/include/coco/IR/Op.h new file mode 100644 index 000000000..090527e2f --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Op.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Op.h + * @brief This header file declares "Op" class and several traits related with "Op" + */ +#ifndef __COCO_IR_OP_H__ +#define __COCO_IR_OP_H__ + +#include "coco/IR/Object.forward.h" +#include "coco/IR/Instr.forward.h" +#include "coco/IR/Step.forward.h" +#include "coco/IR/Part.h" +#include "coco/IR/Entity.h" + +#include + +#include + +namespace coco +{ + +#define OP(Name) class Name; +#include "coco/IR/Op.lst" +#undef OP + +/** + * @brief Base interface on all supported NN operations + */ +struct Op : public Entity +{ + friend class Step; + friend class Part; + + virtual ~Op(); + + /** + * @brief Return the number of arguments (# of child Ops) + */ + virtual uint32_t arity(void) const = 0; + + /** + * @brief Return N-th argument + * + * @note The behavior of arg(n) is defined only when n < artiy() + */ + virtual Op *arg(uint32_t n) const = 0; + + /** + * @brief Return a set of object(s) used during execution + * + * NOTE There is no 'def' method as Op is not allowed to define a new object + */ + virtual std::set uses(void) const = 0; + +#define OP(Name) \ + virtual Name *as##Name(void) { return nullptr; } \ + virtual const Name *as##Name(void) const { return nullptr; } +#include "coco/IR/Op.lst" +#undef OP + + /** + * @brief Op visitor interface + * + * WARN Use this interface only for coco-internal classes + * (to minimize changes upon Op extension) + */ + template struct IVisitor + { + virtual ~IVisitor() = default; + +#define OP(Name) virtual T visit(const Name *) = 0; +#include "coco/IR/Op.lst" +#undef OP + }; + + template struct Visitor : public IVisitor + { + virtual ~Visitor() = default; + +#define OP(Name) \ + T visit(const Name *) override { throw std::runtime_error{"NYI"}; } +#include "coco/IR/Op.lst" +#undef OP + }; + + template T accept(IVisitor *v) const + { +#define OP(Name) \ + if (auto op = as##Name()) \ + { \ + return v->visit(op); \ + } +#include "coco/IR/Op.lst" +#undef OP + throw std::runtime_error{"unreachable"}; + } + + template T accept(IVisitor &v) const { return accept(&v); } + template T accept(IVisitor &&v) const { return accept(&v); } + +public: + /** + * @brief Op mutator interface + * + * WARN Use this interface only for coco-internal classes + * (to minimize changes upon Instr extension) + */ + struct IMutator + { + virtual ~IMutator() = default; + +#define OP(Name) virtual void mutate(Name *) = 0; +#include "coco/IR/Op.lst" +#undef OP + }; + + struct Mutator : public IMutator + { + virtual ~Mutator() = default; + +#define OP(Name) \ + void mutate(Name *) override { throw std::runtime_error{"NYI"}; } +#include "coco/IR/Op.lst" +#undef OP + }; + + void accept(IMutator *m) + { +#define OP(Name) \ + if (auto op = as##Name()) \ + { \ + return m->mutate(op); \ + } +#include "coco/IR/Op.lst" +#undef OP + throw std::runtime_error{"unreachable"}; + } + + void accept(IMutator &m) { return accept(&m); } + void accept(IMutator &&m) { return accept(&m); } + +public: + Instr *parent(void) const; + + /// @brief Return a pointer to the parent Op + Op *up(void) const; + +private: + /** + * @brief A link to Instr from Op + * + * WARN Update this field only through Step + */ + Step *_step = nullptr; + + /** + * @brief A link to a parent Op + * + * WARN Update this field only through Part + * NOTE An "Op" CANNOT have a link to a parent Op if it is linked to an "Instr" + */ + Part *_part = nullptr; +}; + +/** + * @brief Op with a single argument + */ +class UnaryOp : public Op +{ +public: + explicit UnaryOp(); + +public: + UnaryOp(const UnaryOp &) = delete; + UnaryOp(UnaryOp &&) = delete; + +public: + virtual ~UnaryOp() = default; + +public: + uint32_t arity(void) const final; + Op *arg(uint32_t n) const final; + + std::set uses(void) const final; + +public: + Op *arg(void) const { return _arg.child(); } + void arg(Op *arg) { _arg.child(arg); } + +private: + /// @brief Link to Op's argument + Part _arg; +}; + +/** + * @brief Op with two arguments + */ +class BinaryOp : public Op +{ +public: + explicit BinaryOp(); + +public: + BinaryOp(const BinaryOp &) = delete; + BinaryOp(BinaryOp &&) = delete; + +public: + virtual ~BinaryOp() = default; + +public: + uint32_t arity(void) const final; + Op *arg(uint32_t n) const final; + + std::set uses(void) const final; + +public: + Op *left(void) const { return _left.child(); } + void left(Op *op) { _left.child(op); } + +public: + Op *right(void) const { return _right.child(); } + void right(Op *op) { _right.child(op); } + +private: + /// @brief Left-hand side (LHS) argument + Part _left; + /// @brief Right-hand side (RHS) argument + Part _right; +}; + +/** + * @brief Return the root Op from a given Op node + * + * @note root(op) == op holds for a root op + */ +Op *root(Op *); + +} // namespace coco + +#endif // __COCO_IR_OP_H__ diff --git a/compiler/coco/core/include/coco/IR/Op.lst b/compiler/coco/core/include/coco/IR/Op.lst new file mode 100644 index 000000000..a3028bde2 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Op.lst @@ -0,0 +1,19 @@ +#ifndef OP +#error OP should be defined before including this file +#endif // OP + +// OP(Name) + +OP(Load) +OP(Conv2D) +OP(MaxPool2D) +OP(AvgPool2D) +OP(PadF) +OP(ReLU) +OP(ReLU6) +OP(Add) +OP(Sqrt) +OP(Sub) +OP(Mul) +OP(Div) +OP(ConcatF) diff --git a/compiler/coco/core/include/coco/IR/OpManager.h b/compiler/coco/core/include/coco/IR/OpManager.h new file mode 100644 index 000000000..2c88867de --- /dev/null +++ b/compiler/coco/core/include/coco/IR/OpManager.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OP_MANAGER_H__ +#define __COCO_IR_OP_MANAGER_H__ + +#include "coco/IR/Op.h" +#include "coco/IR/Ops.h" + +#include "coco/IR/Instr.forward.h" + +#include "coco/IR/Object.forward.h" + +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class OpManager final : public PtrManager, public EntityBuilder +{ +public: + OpManager(Module *m = nullptr) { module(m); } + +public: + ~OpManager(); + +public: + template T *create(void); + +public: + /** + * @brief Destroy (= deallocate) a Op instance + * + * NOTE destroy(op) WILL NOT update op->parent(). Client SHOULD detach op before destroy(op) call + */ + void destroy(Op *); + + /** + * @brief Destroy a Op tree + * + * @require op->parent() == nullptr && op->up() == nullptr + */ + void destroy_all(Op *); +}; + +} // namespace coco + +#endif // __COCO_IR_OP_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Ops.h b/compiler/coco/core/include/coco/IR/Ops.h new file mode 100644 index 000000000..01ac92b7f --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Ops.h @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OPS_H__ +#define __COCO_IR_OPS_H__ + +#include "coco/IR/Op.h" +#include "coco/IR/Object.h" +#include "coco/IR/KernelObject.h" + +#include "coco/IR/Use.h" +#include "coco/IR/Part.h" + +#include "coco/IR/Padding2D.h" +#include "coco/IR/Stride2D.h" +#include "coco/IR/Window2D.h" + +namespace coco +{ + +/** + * @brief Load an Object + */ +class Load final : public Op, public Object::Consumer +{ +public: + explicit Load(); + +public: + Load(const Load &) = delete; + Load(Load &&) = delete; + +public: + uint32_t arity(void) const final; + Op *arg(uint32_t n) const final; + + std::set uses(void) const override; + +public: + Load *asLoad(void) override { return this; } + const Load *asLoad(void) const override { return this; } + +public: + Instr *loc(void) override { return parent(); } + +public: + void object(Object *o) { _obj.value(o); } + Object *object(void) const { return _obj.value(); } + +private: + Use _obj; +}; + +/** + * @brief 2D Convolution over 3D Feature Map with 4D kernel + * + * NOTE IFM and OFM are implicit. Only 4D kernel is explicit in this class + * TODO Decide source code layout policy and extract this class if necessary + */ +class Conv2D : public Op, public Object::Consumer +{ +public: + explicit Conv2D(); + +public: + uint32_t arity(void) const final; + Op *arg(uint32_t n) const final; + + std::set uses(void) const override; + +public: + Conv2D *asConv2D(void) override { return this; } + const Conv2D *asConv2D(void) const override { return this; } + +public: + Instr *loc(void) override { return parent(); } + +private: + Use _ker; + +public: + Op *arg(void) const { return _arg.child(); } + void arg(Op *arg) { _arg.child(arg); } + +public: + KernelObject *ker(void) const; + void ker(KernelObject *ker); + +public: + /** + * @brief Divide an input and kernel (= convolution filter) into G independent groups + * + * Given an input of shape(Ic, Ih, Iw), a kernel of shape(Kn, Kc, Kh, Kw), and group G, + * Conv2D is identical to G independent convolutions over G inputs of shape(Ic / G, Ih, Iw) + * and a kernel of shape(Kn / G, Kc, Kh, Kw) followed by concatenation. + * + * REQUIRED + * - "Ic" SHOULD BE a multiple of "G" + * - "Kc" SHOULD BE identical to "Ic /G" + * + * NOTE Depthwise convolution is a special case of group convolution where Ic == G. + */ + uint32_t group(void) const { return _group; } + void group(uint32_t g) { _group = g; } + +public: + Padding2D *pad(void) { return &_pad; } + const Padding2D *pad(void) const { return &_pad; } + +public: + Stride2D *stride(void) { return &_stride; } + const Stride2D *stride(void) const { return &_stride; } + +private: + uint32_t _group = 1; + + Padding2D _pad; + Stride2D _stride; + +private: + /// @brief Link to an argument of Conv2D operation (= IFM) + Part _arg; +}; + +/** + * @brief 2D Max Pooling + */ +class MaxPool2D final : public UnaryOp +{ +public: + explicit MaxPool2D() = default; + +public: + MaxPool2D(const MaxPool2D &) = delete; + MaxPool2D(MaxPool2D &&) = delete; + +public: + MaxPool2D *asMaxPool2D(void) override { return this; } + const MaxPool2D *asMaxPool2D(void) const override { return this; } + +public: + Window2D *window(void) { return &_window; } + const Window2D *window(void) const { return &_window; } + +public: + Stride2D *stride(void) { return &_stride; } + const Stride2D *stride(void) const { return &_stride; } + +public: + Padding2D *pad(void) { return &_pad; } + const Padding2D *pad(void) const { return &_pad; } + +private: + Window2D _window; + Stride2D _stride; + Padding2D _pad; +}; + +/** + * @brief 2D Average Pooling + */ +class AvgPool2D final : public UnaryOp +{ +public: + enum class Divisor + { + Unknown, + // Use the number of elements in each receptive field as a divisor + Static, + // Use the number of valid (non-padding) elements in each receptive field as a divisor + PaddingExcluded + }; + +public: + explicit AvgPool2D() = default; + +public: + AvgPool2D(const AvgPool2D &) = delete; + AvgPool2D(AvgPool2D &&) = delete; + +public: + AvgPool2D *asAvgPool2D(void) override { return this; } + const AvgPool2D *asAvgPool2D(void) const override { return this; } + +public: + Divisor divisor(void) const { return _divisor; } + void divisor(const Divisor &divisor) { _divisor = divisor; } + +public: + Window2D *window(void) { return &_window; } + const Window2D *window(void) const { return &_window; } + +public: + Padding2D *pad(void) { return &_pad; } + const Padding2D *pad(void) const { return &_pad; } + +public: + Stride2D *stride(void) { return &_stride; } + const Stride2D *stride(void) const { return &_stride; } + +private: + Divisor _divisor = Divisor::Unknown; + + Window2D _window; + Stride2D _stride; + Padding2D _pad; +}; + +/** + * @brief Introduce padding area + */ +class PadF final : public UnaryOp +{ +public: + explicit PadF() = default; + +public: + PadF(const PadF &) = delete; + PadF(PadF &&) = delete; + +public: + PadF *asPadF(void) override { return this; } + const PadF *asPadF(void) const override { return this; } + +public: + Padding2D *pad(void) { return &_pad; } + const Padding2D *pad(void) const { return &_pad; } + +private: + Padding2D _pad; +}; + +/** + * @brief Apply ReLU over elements + */ +class ReLU final : public UnaryOp +{ +public: + explicit ReLU() = default; + +public: + ReLU(const ReLU &) = delete; + ReLU(ReLU &&) = delete; + +public: + ReLU *asReLU(void) override { return this; } + const ReLU *asReLU(void) const override { return this; } +}; + +/** + * @brief Apply ReLU6 over elements + * @note ReLU6 is subject to change + */ +class ReLU6 final : public UnaryOp +{ +public: + explicit ReLU6() = default; + +public: + ReLU6(const ReLU6 &) = delete; + ReLU6(ReLU6 &&) = delete; + +public: + ReLU6 *asReLU6(void) override { return this; } + const ReLU6 *asReLU6(void) const override { return this; } +}; + +/** + * @brief Element-wise addition + * + * Add(L, R) is valid only when L and R have identical kind/shape/dtype + */ +class Add final : public BinaryOp +{ +public: + explicit Add() = default; + +public: + Add(const Add &) = delete; + Add(Add &&) = delete; + +public: + Add *asAdd(void) override { return this; } + const Add *asAdd(void) const override { return this; } +}; + +/** + * @brief Element-wise subtraction + * + * Sub(L, R) is valid only when L and R have identical kind/shape/dtype + */ +class Sub final : public BinaryOp +{ +public: + explicit Sub() = default; + +public: + Sub(const Sub &) = delete; + Sub(Sub &&) = delete; + +public: + Sub *asSub(void) override { return this; } + const Sub *asSub(void) const override { return this; } +}; + +/** + * @brief Element-wise multiplication + * + * Mul(L, R) is valid only when L and R have identical kind/shape/dtype + */ +class Mul final : public BinaryOp +{ +public: + explicit Mul() = default; + +public: + Mul(const Mul &) = delete; + Mul(Mul &&) = delete; + +public: + Mul *asMul(void) override { return this; } + const Mul *asMul(void) const override { return this; } +}; + +/** + * @brief Element-wise division + * + * Div(L, R) is valid only when L and R have identical kind/shape/dtype + */ +class Div final : public BinaryOp +{ +public: + explicit Div() = default; + +public: + Div(const Div &) = delete; + Div(Div &&) = delete; + +public: + Div *asDiv(void) override { return this; } + const Div *asDiv(void) const override { return this; } +}; + +/** + * @brief Concatenate two feature maps + * + * ConcatF(L, R) requires + */ +class ConcatF final : public BinaryOp +{ +public: + enum class Axis + { + Unknown = 0, + Batch = 1, + Depth = 2, + Height = 3, + Width = 4, + }; + +public: + explicit ConcatF() = default; + +public: + ConcatF(const ConcatF &) = delete; + ConcatF(ConcatF &&) = delete; + +public: + ConcatF *asConcatF(void) override { return this; } + const ConcatF *asConcatF(void) const override { return this; } + +public: + const Axis &axis(void) const { return _axis; } + void axis(const Axis &axis) { _axis = axis; } + +private: + Axis _axis = Axis::Unknown; +}; + +/** + * @brief Apply Sqrt over elements + */ +class Sqrt final : public UnaryOp +{ +public: + explicit Sqrt() = default; + +public: + Sqrt(const Sqrt &) = delete; + Sqrt(Sqrt &&) = delete; + +public: + Sqrt *asSqrt(void) override { return this; } + const Sqrt *asSqrt(void) const override { return this; } +}; + +} // namesapce coco + +#endif // __COCO_IR_OPS_H__ diff --git a/compiler/coco/core/include/coco/IR/Output.forward.h b/compiler/coco/core/include/coco/IR/Output.forward.h new file mode 100644 index 000000000..f011400c0 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Output.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OUTPUT_FORWARD_H__ +#define __COCO_IR_OUTPUT_FORWARD_H__ + +namespace coco +{ + +class Output; + +} // namespace coco + +#endif // __COCO_IR_OUTPUT_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Output.h b/compiler/coco/core/include/coco/IR/Output.h new file mode 100644 index 000000000..3f77c131d --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Output.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OUTPUT_H__ +#define __COCO_IR_OUTPUT_H__ + +#include "coco/IR/Arg.h" +#include "coco/IR/Entity.h" + +#include +#include + +#include +#include + +namespace coco +{ + +class Output final : public Arg, public Entity +{ +public: + Output(const nncc::core::ADT::tensor::Shape &shape); + +private: + void onTake(Bag *) override; + void onRelease(Bag *) override; +}; + +} // namespace coco + +#endif // __COCO_IR_OUTPUT_H__ diff --git a/compiler/coco/core/include/coco/IR/OutputList.h b/compiler/coco/core/include/coco/IR/OutputList.h new file mode 100644 index 000000000..0e2abad75 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/OutputList.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OUTPUT_LIST_H__ +#define __COCO_IR_OUTPUT_LIST_H__ + +#include "coco/IR/Output.h" + +#include "coco/ADT/PtrList.h" + +namespace coco +{ + +using OutputList = PtrList; + +} // namespace coco + +#endif // __COCO_IR_OUTPUT_LIST_H__ diff --git a/compiler/coco/core/include/coco/IR/OutputManager.h b/compiler/coco/core/include/coco/IR/OutputManager.h new file mode 100644 index 000000000..b40380388 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/OutputManager.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_OUTPUT_MANAGER_H__ +#define __COCO_IR_OUTPUT_MANAGER_H__ + +#include "coco/IR/Output.h" +#include "coco/IR/EntityBuilder.h" + +#include "coco/ADT/PtrManager.h" + +namespace coco +{ + +class OutputManager final : public PtrManager, public EntityBuilder +{ +public: + OutputManager(Module *m = nullptr) { module(m); } + +public: + Output *create(const nncc::core::ADT::tensor::Shape &); +}; + +} // namespace coco + +#endif // __COCO_IR_OUTPUT_MANAGER_H__ diff --git a/compiler/coco/core/include/coco/IR/Padding2D.h b/compiler/coco/core/include/coco/IR/Padding2D.h new file mode 100644 index 000000000..b764656cc --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Padding2D.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_PADDING_2D_H__ +#define __COCO_IR_PADDING_2D_H__ + +#include + +namespace coco +{ + +class Padding2D +{ +public: + Padding2D() : _top{0}, _bottom{0}, _left{0}, _right{0} + { + // DO NOTHING + } + +public: + Padding2D(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) + : _top{top}, _bottom{bottom}, _left{left}, _right{right} + { + // DO NOTHING + } + +public: + uint32_t top(void) const { return _top; } + Padding2D &top(uint32_t value); + +public: + uint32_t bottom(void) const { return _bottom; } + Padding2D &bottom(uint32_t value); + +public: + uint32_t left(void) const { return _left; } + Padding2D &left(uint32_t value); + +public: + uint32_t right(void) const { return _right; } + Padding2D &right(uint32_t value); + +private: + uint32_t _top; + uint32_t _bottom; + uint32_t _left; + uint32_t _right; +}; + +} // namespace coco + +#endif // __COCO_IR_PADDING_2D_H__ diff --git a/compiler/coco/core/include/coco/IR/Part.forward.h b/compiler/coco/core/include/coco/IR/Part.forward.h new file mode 100644 index 000000000..642ea56b5 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Part.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_PART_FORWARD_H__ +#define __COCO_IR_PART_FORWARD_H__ + +namespace coco +{ + +class Part; + +} // namespace coco + +#endif // __COCO_IR_PART_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Part.h b/compiler/coco/core/include/coco/IR/Part.h new file mode 100644 index 000000000..72af217cc --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Part.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_PART_H__ +#define __COCO_IR_PART_H__ + +#include "coco/IR/Op.forward.h" + +namespace coco +{ + +/** + * @brief A Part represents the edge between a child Op and its parent Op + */ +class Part final +{ +public: + Part(Op *parent) : _parent{parent} + { + // DO NOTHING + } + +public: + ~Part() { child(nullptr); } + +public: + Op *child(void) const { return _child; } + void child(Op *c); + +public: + Op *parent(void) const { return _parent; } + +private: + Op *_parent = nullptr; + Op *_child = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_PART_H__ diff --git a/compiler/coco/core/include/coco/IR/Read.forward.h b/compiler/coco/core/include/coco/IR/Read.forward.h new file mode 100644 index 000000000..7fd99e212 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Read.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_READ_FORWARD_H__ +#define __COCO_IR_READ_FORWARD_H__ + +namespace coco +{ + +class Read; + +} // namespace coco + +#endif // __COCO_IR_READ_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Read.h b/compiler/coco/core/include/coco/IR/Read.h new file mode 100644 index 000000000..9f62d8bf8 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Read.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_READ_H__ +#define __COCO_IR_READ_H__ + +#include "coco/IR/Bag.h" + +namespace coco +{ + +/** + * @brief A Read represents an edge between a Bag and its Reader + */ +class Read final +{ +public: + Read(Bag::Reader *r) + { + // Initialize link and reader + reader(r); + } + +public: + ~Read(); + +public: + Bag *bag(void) const { return _bag; } + void bag(Bag *bag); + +public: + Bag::Reader *reader(void) const { return _reader; } + void reader(Bag::Reader *r) { _reader = r; } + +private: + Bag *_bag = nullptr; + Bag::Reader *_reader = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_READ_H__ diff --git a/compiler/coco/core/include/coco/IR/ReadSet.h b/compiler/coco/core/include/coco/IR/ReadSet.h new file mode 100644 index 000000000..c470c4bfd --- /dev/null +++ b/compiler/coco/core/include/coco/IR/ReadSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_READ_SET_H__ +#define __COCO_IR_READ_SET_H__ + +#include "coco/IR/Read.forward.h" + +#include + +namespace coco +{ + +using ReadSet = std::set; + +} // namespace coco + +#endif // __COCO_IR_READ_SET_H__ diff --git a/compiler/coco/core/include/coco/IR/Step.forward.h b/compiler/coco/core/include/coco/IR/Step.forward.h new file mode 100644 index 000000000..635069122 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Step.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_STEP_FORWARD_H__ +#define __COCO_IR_STEP_FORWARD_H__ + +namespace coco +{ + +class Step; + +} // namespace coco + +#endif // __COCO_IR_STEP_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Step.h b/compiler/coco/core/include/coco/IR/Step.h new file mode 100644 index 000000000..31dad4389 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Step.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_STEP_H__ +#define __COCO_IR_STEP_H__ + +#include "coco/IR/Op.forward.h" +#include "coco/IR/Instr.forward.h" + +namespace coco +{ + +/** + * @brief A Step denotes the edge between Op and Instr + */ +class Step final +{ +public: + explicit Step(Instr *instr) : _instr{instr} + { + // DO NOTHING + } + +public: + ~Step() { op(nullptr); } + +public: + Op *op(void) const { return _op; } + void op(Op *o); + +public: + Instr *instr(void) const { return _instr; } + +private: + Op *_op = nullptr; + Instr *_instr = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_STEP_H__ diff --git a/compiler/coco/core/include/coco/IR/Stride2D.h b/compiler/coco/core/include/coco/IR/Stride2D.h new file mode 100644 index 000000000..9e69ffa40 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Stride2D.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_STRIDE_2D_H__ +#define __COCO_IR_STRIDE_2D_H__ + +#include + +namespace coco +{ + +class Stride2D +{ +public: + Stride2D() : _vertical{1}, _horizontal{1} + { + // DO NOTHING + } + +public: + Stride2D(uint32_t vertical, uint32_t horizontal) : _vertical{vertical}, _horizontal{horizontal} + { + // DO NOTHING + } + +public: + uint32_t vertical(void) const { return _vertical; } + Stride2D &vertical(uint32_t value); + +public: + uint32_t horizontal(void) const { return _horizontal; } + Stride2D &horizontal(uint32_t value); + +private: + uint32_t _vertical; + uint32_t _horizontal; +}; + +} // namespace coco + +#endif // __COCO_IR_STRIDE_2D_H__ diff --git a/compiler/coco/core/include/coco/IR/Update.forward.h b/compiler/coco/core/include/coco/IR/Update.forward.h new file mode 100644 index 000000000..059f318c9 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Update.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_UPDATE_FORWARD_H__ +#define __COCO_IR_UPDATE_FORWARD_H__ + +namespace coco +{ + +class Update; + +} // namespace coco + +#endif // __COCO_IR_UPDATE_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Update.h b/compiler/coco/core/include/coco/IR/Update.h new file mode 100644 index 000000000..7cf876d74 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Update.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_UPDATE_H__ +#define __COCO_IR_UPDATE_H__ + +#include "coco/IR/Bag.h" + +namespace coco +{ + +/** + * @brief A Update represents an edge between a Bag and its Updater + */ +class Update final +{ +public: + Update(Bag::Updater *u) { updater(u); } + +public: + ~Update(); + +public: + Bag *bag(void) const { return _bag; } + void bag(Bag *bag); + +public: + Bag::Updater *updater(void) const { return _updater; } + void updater(Bag::Updater *u) { _updater = u; } + +private: + Bag *_bag = nullptr; + Bag::Updater *_updater = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_UPDATE_H__ diff --git a/compiler/coco/core/include/coco/IR/UpdateSet.h b/compiler/coco/core/include/coco/IR/UpdateSet.h new file mode 100644 index 000000000..1e772adf3 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/UpdateSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_UPDATE_SET_H__ +#define __COCO_IR_UPDATE_SET_H__ + +#include "coco/IR/Update.forward.h" + +#include + +namespace coco +{ + +using UpdateSet = std::set; + +} // namespace coco + +#endif // __COCO_IR_UPDATE_SET_H__ diff --git a/compiler/coco/core/include/coco/IR/Use.forward.h b/compiler/coco/core/include/coco/IR/Use.forward.h new file mode 100644 index 000000000..329430bb3 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Use.forward.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_USE_FORWARD_H__ +#define __COCO_IR_USE_FORWARD_H__ + +namespace coco +{ + +class Use; + +} // namespace coco + +#endif // __COCO_IR_USE_FORWARD_H__ diff --git a/compiler/coco/core/include/coco/IR/Use.h b/compiler/coco/core/include/coco/IR/Use.h new file mode 100644 index 000000000..c4c9b98b4 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Use.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_USE_H__ +#define __COCO_IR_USE_H__ + +#include "coco/IR/Object.h" + +namespace coco +{ + +class Use final +{ +public: + Use(Object::Consumer *use) : _value{nullptr}, _consumer{use} + { + // DO NOTHING + } + +public: + ~Use() { value(nullptr); } + +public: + Object *value(void) const { return _value; } + +public: + void value(Object *value); + +public: + Object::Consumer *consumer(void) const { return _consumer; } + +private: + Object *_value; + Object::Consumer *_consumer = nullptr; +}; + +} // namespace coco + +#endif // __COCO_IR_USE_H__ diff --git a/compiler/coco/core/include/coco/IR/UseSet.h b/compiler/coco/core/include/coco/IR/UseSet.h new file mode 100644 index 000000000..a698a733f --- /dev/null +++ b/compiler/coco/core/include/coco/IR/UseSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_USE_SET_H__ +#define __COCO_IR_USE_SET_H__ + +#include "coco/IR/Use.forward.h" + +#include + +namespace coco +{ + +using UseSet = std::set; + +} // namespace coco + +#endif // __COCO_IR_USE_SET_H__ diff --git a/compiler/coco/core/include/coco/IR/Window2D.h b/compiler/coco/core/include/coco/IR/Window2D.h new file mode 100644 index 000000000..a434538f3 --- /dev/null +++ b/compiler/coco/core/include/coco/IR/Window2D.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_WINDOW_2D_H__ +#define __COCO_IR_WINDOW_2D_H__ + +#include + +namespace coco +{ + +class Window2D +{ +public: + Window2D() : _vertical{1}, _horizontal{1} + { + // DO NOTHING + } + +public: + Window2D(uint32_t vertical, uint32_t horizontal) : _vertical{vertical}, _horizontal{horizontal} + { + // DO NOTHING + } + +public: + uint32_t height(void) const { return _vertical; } + void height(uint32_t value) { _vertical = value; } + +public: + uint32_t width(void) const { return _horizontal; } + void width(uint32_t value) { _horizontal = value; } + +private: + // TODO Rename these fields as _height and _width, respectively + uint32_t _vertical; + uint32_t _horizontal; +}; + +} // namespace coco + +#endif // __COCO_IR_WINDOW_2D_H__ diff --git a/compiler/coco/core/src/ADT/DLinkedList.test.cpp b/compiler/coco/core/src/ADT/DLinkedList.test.cpp new file mode 100644 index 000000000..563a39653 --- /dev/null +++ b/compiler/coco/core/src/ADT/DLinkedList.test.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/ADT/DLinkedList.h" + +#include + +#include + +namespace +{ + +class Parent; +class Child; + +using ChildList = coco::DLinkedList::Head; + +class Parent +{ +public: + friend void coco::DLinkedList::joined(Parent *, Child *); + friend void coco::DLinkedList::leaving(Parent *, Child *); + +public: + Parent() : _list{this}, _count{0} + { + // DO NOTHING + } + +public: + ChildList *children(void) { return &_list; } + uint32_t count(void) const { return _count; } + +private: + ChildList _list; + uint32_t _count; +}; + +class Child final : public coco::DLinkedList::Node +{ +public: + ~Child() + { + if (parent()) + { + detach(); + } + } +}; + +} // namespace + +namespace coco +{ + +template <> void DLinkedList::joined(Parent *p, Child *) { p->_count += 1; } +template <> void DLinkedList::leaving(Parent *p, Child *) { p->_count -= 1; } + +template <> ChildList *DLinkedList::head(Parent *p) { return p->children(); } + +} // namespace coco + +namespace +{ +class DLinkedListTest : public ::testing::Test +{ +public: + virtual ~DLinkedListTest() + { + // NOTE Child SHOULD BE freed before parent + for (auto child : _children) + { + delete child; + } + + for (auto parent : _parents) + { + delete parent; + } + } + +protected: + template T *create(void); + + void destroy(Child *); + +private: + std::set<::Parent *> _parents; + std::set<::Child *> _children; +}; + +template <>::Parent *DLinkedListTest::create(void) +{ + auto parent = new ::Parent; + _parents.insert(parent); + return parent; +} + +template <>::Child *DLinkedListTest::create(void) +{ + auto child = new ::Child; + _children.insert(child); + return child; +} + +void DLinkedListTest::destroy(Child *child) +{ + _children.erase(child); + delete child; +} + +} // namespace + +TEST_F(DLinkedListTest, append) +{ + auto parent = create<::Parent>(); + auto child = create<::Child>(); + + parent->children()->append(child); + + ASSERT_EQ(child->parent(), parent); + ASSERT_EQ(child->prev(), nullptr); + ASSERT_EQ(child->next(), nullptr); + + ASSERT_EQ(parent->children()->head(), child); + ASSERT_EQ(parent->children()->tail(), child); + ASSERT_EQ(parent->count(), 1); +} + +TEST_F(DLinkedListTest, insert_two_elements) +{ + auto parent = create<::Parent>(); + + ASSERT_EQ(parent->children()->head(), nullptr); + ASSERT_EQ(parent->children()->tail(), nullptr); + + auto child_1 = create<::Child>(); + + ASSERT_EQ(child_1->parent(), nullptr); + ASSERT_EQ(child_1->prev(), nullptr); + ASSERT_EQ(child_1->next(), nullptr); + + parent->children()->append(child_1); + + ASSERT_EQ(child_1->parent(), parent); + ASSERT_EQ(child_1->prev(), nullptr); + ASSERT_EQ(child_1->next(), nullptr); + + ASSERT_EQ(parent->children()->head(), child_1); + ASSERT_EQ(parent->children()->tail(), child_1); + + auto child_2 = create<::Child>(); + + ASSERT_EQ(child_2->parent(), nullptr); + ASSERT_EQ(child_2->prev(), nullptr); + ASSERT_EQ(child_2->next(), nullptr); + + child_2->insertAfter(child_1); + + ASSERT_EQ(child_2->parent(), parent); + ASSERT_EQ(child_2->prev(), child_1); + ASSERT_EQ(child_2->next(), nullptr); + + ASSERT_EQ(child_1->parent(), parent); + ASSERT_EQ(child_1->prev(), nullptr); + ASSERT_EQ(child_1->next(), child_2); + + ASSERT_EQ(parent->children()->head(), child_1); + ASSERT_EQ(parent->children()->tail(), child_2); +} + +TEST_F(DLinkedListTest, insertBefore) +{ + auto parent = create<::Parent>(); + + auto child_1 = create<::Child>(); + auto child_2 = create<::Child>(); + + parent->children()->append(child_1); + child_2->insertBefore(child_1); + + ASSERT_EQ(child_2->parent(), parent); + ASSERT_EQ(child_2->prev(), nullptr); + ASSERT_EQ(child_2->next(), child_1); + + ASSERT_EQ(child_1->parent(), parent); + ASSERT_EQ(child_1->prev(), child_2); + ASSERT_EQ(child_1->next(), nullptr); + + ASSERT_EQ(parent->children()->head(), child_2); + ASSERT_EQ(parent->children()->tail(), child_1); +} + +TEST_F(DLinkedListTest, prepend_after_append) +{ + auto parent = create<::Parent>(); + + auto child_1 = create<::Child>(); + auto child_2 = create<::Child>(); + + parent->children()->append(child_1); + parent->children()->prepend(child_2); + + ASSERT_EQ(child_2->next(), child_1); + + ASSERT_EQ(child_1->parent(), parent); + ASSERT_EQ(child_1->prev(), child_2); + ASSERT_EQ(child_1->next(), nullptr); + + ASSERT_EQ(parent->children()->head(), child_2); + ASSERT_EQ(parent->children()->tail(), child_1); +} + +TEST_F(DLinkedListTest, detach) +{ + auto parent = create<::Parent>(); + + auto child_1 = create<::Child>(); + auto child_2 = create<::Child>(); + + parent->children()->append(child_1); + parent->children()->append(child_2); + + child_1->detach(); + + ASSERT_EQ(child_1->parent(), nullptr); + ASSERT_EQ(child_1->prev(), nullptr); + ASSERT_EQ(child_1->next(), nullptr); + + ASSERT_EQ(child_2->parent(), parent); + ASSERT_EQ(child_2->prev(), nullptr); + + ASSERT_EQ(parent->children()->head(), child_2); + ASSERT_EQ(parent->children()->tail(), child_2); + + child_2->detach(); + + ASSERT_EQ(child_2->parent(), nullptr); + ASSERT_EQ(child_2->prev(), nullptr); + ASSERT_EQ(child_2->next(), nullptr); + + ASSERT_TRUE(parent->children()->empty()); + ASSERT_EQ(parent->children()->head(), nullptr); + ASSERT_EQ(parent->children()->tail(), nullptr); +} + +TEST_F(DLinkedListTest, node_destructor) +{ + auto parent = create<::Parent>(); + + auto child_1 = create<::Child>(); + auto child_2 = create<::Child>(); + + parent->children()->append(child_1); + parent->children()->append(child_2); + + destroy(child_2); + + ASSERT_EQ(parent->children()->head(), child_1); + ASSERT_EQ(parent->children()->tail(), child_1); + ASSERT_EQ(child_1->next(), nullptr); + ASSERT_EQ(child_1->prev(), nullptr); + + destroy(child_1); + + ASSERT_EQ(parent->children()->head(), nullptr); + ASSERT_EQ(parent->children()->tail(), nullptr); +} diff --git a/compiler/coco/core/src/ADT/PtrList.cpp b/compiler/coco/core/src/ADT/PtrList.cpp new file mode 100644 index 000000000..ea2beb06b --- /dev/null +++ b/compiler/coco/core/src/ADT/PtrList.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/ADT/PtrList.h" + +// NOTE Do NOT delete this file; this file checks the completeness of 'PtrList.h' diff --git a/compiler/coco/core/src/ADT/PtrList.test.cpp b/compiler/coco/core/src/ADT/PtrList.test.cpp new file mode 100644 index 000000000..dcbad8b90 --- /dev/null +++ b/compiler/coco/core/src/ADT/PtrList.test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/ADT/PtrList.h" + +#include + +#include + +namespace +{ +struct Object +{ +}; +} + +TEST(ADT_PTR_LIST, ctor) +{ + coco::PtrList l; + + ASSERT_EQ(l.size(), 0); +} + +TEST(ADT_PTR_LIST, insert) +{ + coco::PtrList l; + + std::unique_ptr ptr{new Object}; + + l.insert(ptr.get()); + + ASSERT_EQ(l.size(), 1); + ASSERT_EQ(l.at(0), ptr.get()); +} diff --git a/compiler/coco/core/src/ADT/PtrManager.test.cpp b/compiler/coco/core/src/ADT/PtrManager.test.cpp new file mode 100644 index 000000000..bb9056f29 --- /dev/null +++ b/compiler/coco/core/src/ADT/PtrManager.test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/ADT/PtrManager.h" + +#include + +#include + +namespace +{ +struct Count +{ + uint32_t allocated; + uint32_t freed; + + Count() : allocated{0}, freed{0} + { + // DO NOTHING + } +}; + +class Object +{ +public: + Object(Count *count, uint32_t value) : _count{count}, _value{value} { _count->allocated += 1; } + +public: + ~Object() { _count->freed += 1; } + +public: + uint32_t value(void) const { return _value; } + +private: + Count *const _count; + +private: + uint32_t _value; +}; + +struct ObjectManager final : public coco::PtrManager +{ + Object *alloc(Count *count, uint32_t value) + { + std::unique_ptr o{new Object{count, value}}; + return take(std::move(o)); + } + + void free(Object *o) { release(o); } +}; +} + +TEST(ADT_PTR_MANAGER, usecase) +{ + Count c; + + ASSERT_EQ(c.allocated, 0); + ASSERT_EQ(c.freed, 0); + + { + ::ObjectManager mgr; + + auto obj_1 = mgr.alloc(&c, 3); + auto obj_2 = mgr.alloc(&c, 4); + + EXPECT_EQ(c.allocated, 2); + ASSERT_EQ(c.freed, 0); + + EXPECT_EQ(mgr.size(), 2); + EXPECT_EQ(mgr.at(0), obj_1); + EXPECT_EQ(mgr.at(1), obj_2); + + // Let's delete obj_1 + mgr.free(obj_1); + + EXPECT_EQ(c.allocated, 2); + ASSERT_EQ(c.freed, 1); + + EXPECT_EQ(mgr.size(), 1); + EXPECT_EQ(mgr.at(0), obj_2); + } + + // PtrManger SHOULD destruct all of the allocated object when it is destructed. + ASSERT_EQ(c.allocated, 2); + ASSERT_EQ(c.freed, 2); +} diff --git a/compiler/coco/core/src/IR.test.cpp b/compiler/coco/core/src/IR.test.cpp new file mode 100644 index 000000000..3f8c0ad34 --- /dev/null +++ b/compiler/coco/core/src/IR.test.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR.h" + +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +using nncc::core::ADT::feature::num_elements; + +using nncc::core::ADT::kernel::num_elements; + +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::num_elements; + +// +// 'caffe_conv' test demonstrates how to translate the following Caffe network into coco IR: +// +// layer { +// name: "data" +// type: "Input" +// top: "data" +// input_param: { shape: { dim: 1 dim: 1 dim: 3 dim: 3 } } +// } +// +// layer { +// name: "conv" +// type: "Convolution" +// bottom: "data" +// top: "conv" +// blobs { +// ... +// shape { dim: 1 dim: 1 dim: 3 dim: 3 } +// } +// convolution_param { +// bias_term: false +// num_output: 1 +// kernel_size: 3 +// } +// } +// +TEST(IR, caffe_conv) +{ + // For inter-layer communication + std::map bags; + std::map shapes; + + std::set top_blobs; + + // Create a module and block + auto m = coco::Module::create(); + auto blk = m->entity()->block()->create(); + + // Next, append the block to the module + m->block()->append(blk); + + // Now, the block belongs to the module (and has no sibling) + ASSERT_EQ(blk->parent(), m.get()); + ASSERT_EQ(blk->next(), nullptr); + ASSERT_EQ(blk->prev(), nullptr); + + // The head and tail points to the appended block + ASSERT_EQ(m->block()->head(), blk); + ASSERT_EQ(m->block()->tail(), blk); + + // Let's translate the first 'Input' layer + { + using nncc::core::ADT::tensor::Shape; + + const Shape shape{1, 1, 3, 3}; + + auto bag = m->entity()->bag()->create(num_elements(shape)); + auto input = m->entity()->input()->create(shape); + + input->bag(bag); + input->name("data"); + + // Caffe uses lexical layout for tensors + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const static LexicalLayout l{}; + const auto offset = static_cast(l.offset(shape, e.current())); + + input->at(e.current()) = coco::ElemID{offset}; + } + + m->input()->insert(input); + + bags["data"] = bag; + shapes["data"] = shape; + + top_blobs = {"data"}; + } + + // Next, translate 'Convolution' layer + { + using nncc::core::ADT::feature::CHWLayout; + using nncc::core::ADT::kernel::NCHWLayout; + + const nncc::core::ADT::feature::Shape ifm_shape{1, 3, 3}; + auto ifm_bag = bags["data"]; + auto ifm_obj = m->entity()->object()->create(); + auto ifm_layout = coco::FeatureLayouts::BCHW::create(ifm_shape); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(std::move(ifm_layout)); + + const nncc::core::ADT::kernel::Shape ker_shape{1, 1, 3, 3}; + auto ker_bag = m->entity()->bag()->create(num_elements(ker_shape)); + auto ker_layout = coco::KernelLayouts::Generic::create(ker_shape); + + ker_layout->reorder(); + + auto ker_obj = m->entity()->object()->create(); + + ker_obj->bag(ker_bag); + ker_obj->layout(std::move(ker_layout)); + + const nncc::core::ADT::feature::Shape ofm_shape{1, 1, 1}; + auto ofm_bag = m->entity()->bag()->create(1 * 1 * 1); + auto ofm_obj = m->entity()->object()->create(); + auto ofm_layout = coco::FeatureLayouts::BCHW::create(ifm_shape); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(std::move(ofm_layout)); + + // Create Load operation + auto load = m->entity()->op()->create(); + + load->object(ifm_obj); + + // Create Conv2D operation + // + // NOTE Conv2D op in coco IR does not perform BiasAdd + auto op = m->entity()->op()->create(); + + op->ker(ker_obj); + + // Create UnitF instruction with Conv2D operation + auto ins = m->entity()->instr()->create(); + + ins->out(ofm_obj); + ins->op(op); + + // Append the instruction (to the block) + blk->instr()->append(ins); + + bags["conv"] = ofm_bag; + shapes["conv"] = nncc::core::ADT::tensor::Shape{1, 1, 1, 1}; + + top_blobs = {"conv"}; + } + + // Finalize + for (const auto &top_blob : top_blobs) + { + const auto &shape = shapes[top_blob]; + + auto output = m->entity()->output()->create(shape); + + output->bag(bags[top_blob]); + output->name(top_blob); + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const static LexicalLayout l{}; + const auto offset = static_cast(l.offset(shape, e.current())); + + output->at(e.current()) = coco::ElemID{offset}; + } + + m->output()->insert(output); + } + + // Let's validate the constructed IR + { + // There is one input whose name is 'data' + ASSERT_EQ(m->input()->size(), 1); + ASSERT_EQ(m->input()->at(0)->name(), "data"); + + // There is one output whose name is 'conv' + ASSERT_EQ(m->output()->size(), 1); + ASSERT_EQ(m->output()->at(0)->name(), "conv"); + + ASSERT_FALSE(m->block()->empty()); + + // There is one block in the module + auto blk = m->block()->head(); + + ASSERT_EQ(blk->next(), nullptr); + ASSERT_FALSE(blk->instr()->empty()); + + // There is one instruction in the block + auto ins = blk->instr()->head(); + + ASSERT_EQ(ins->next(), nullptr); + + // That instruction is 'Eval' + // TODO Rename 'unit' + auto unit = ins->asEval(); + + ASSERT_NE(unit, nullptr); + +// TODO Rewrite below test +#if 0 + // Input #0 points to IFM + ASSERT_NE(unit->ifm(), nullptr); + ASSERT_EQ(unit->ifm()->bag(), m->input()->at(0)->bag()); +#endif + + // Output #0 points to OFM + ASSERT_NE(unit->out(), nullptr); + ASSERT_EQ(unit->out()->bag(), m->output()->at(0)->bag()); + + // The actual operation is Conv2D + auto conv = unit->op()->asConv2D(); + + ASSERT_NE(conv, nullptr); + + // Let's check Kernel Object + ASSERT_NE(conv->ker(), nullptr); +// TODO Rewrite below test +#if 0 + ASSERT_NE(conv->ker()->bag(), unit->ifm()->bag()); + ASSERT_NE(conv->ker()->bag(), unit->ofm()->bag()); +#endif + +// One may find the correspondence among Input, Output, and Objects through ElemID +// TODO Rewrite below test +#if 0 + { + auto input_0 = m->input()->at(0); + auto ifm = unit->ifm(); + + nncc::core::ADT::tensor::Index input_index{0, 0, 2, 2}; + + // Here we can check that Input(0, 0, 2, 2) corresponds to IFM(0, 2, 2) + ASSERT_EQ(input_0->at(input_index).value(), ifm->at(0, 2, 2).value()); + } +#endif + } +} + +// +// This test demonstrates how to use 'replaceWith' method +// +TEST(IR, bag_replaceWith) +{ + auto m = coco::Module::create(); + + auto bag_1 = m->entity()->bag()->create(1); + auto bag_2 = m->entity()->bag()->create(1); + + auto obj = m->entity()->object()->create(); + obj->bag(bag_1); + + auto shuffle_1 = m->entity()->instr()->create(); + shuffle_1->into(bag_1); + + auto shuffle_2 = m->entity()->instr()->create(); + shuffle_2->from(bag_1); + + ASSERT_EQ(obj->bag(), bag_1); + ASSERT_EQ(shuffle_1->into(), bag_1); + ASSERT_EQ(shuffle_2->from(), bag_1); + + bag_1->replaceAllDepsWith(bag_2); + + ASSERT_EQ(obj->bag(), bag_2); + ASSERT_EQ(shuffle_1->into(), bag_1); + ASSERT_EQ(shuffle_2->from(), bag_1); + + bag_1->replaceWith(bag_2); + + ASSERT_EQ(obj->bag(), bag_2); + ASSERT_EQ(shuffle_1->into(), bag_2); + ASSERT_EQ(shuffle_2->from(), bag_2); +} diff --git a/compiler/coco/core/src/IR/Arg.cpp b/compiler/coco/core/src/IR/Arg.cpp new file mode 100644 index 000000000..b6f9c4777 --- /dev/null +++ b/compiler/coco/core/src/IR/Arg.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Arg.h" + +#include +#include + +#include + +namespace +{ + +const nncc::core::ADT::tensor::LexicalLayout l; + +} // namespace + +namespace coco +{ + +Arg::Arg(const nncc::core::ADT::tensor::Shape &shape) : _shape{shape}, _bag{nullptr} +{ + _map.resize(nncc::core::ADT::tensor::num_elements(shape)); +} + +void Arg::bag(Bag *bag) +{ + if (_bag != nullptr) + { + onRelease(_bag); + _bag = nullptr; + } + + assert(_bag == nullptr); + + if (bag != nullptr) + { + _bag = bag; + onTake(_bag); + } +} + +ElemID &Arg::at(const nncc::core::ADT::tensor::Index &index) +{ + return _map.at(l.offset(_shape, index)); +} + +const ElemID &Arg::at(const nncc::core::ADT::tensor::Index &index) const +{ + return _map.at(l.offset(_shape, index)); +} + +void Arg::reorder(const nncc::core::ADT::tensor::Layout &l) +{ + using nncc::core::ADT::tensor::IndexEnumerator; + + for (IndexEnumerator e{shape()}; e.valid(); e.advance()) + { + const auto offset = static_cast(l.offset(shape(), e.current())); + + at(e.current()) = coco::ElemID{offset}; + } +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Arg.test.cpp b/compiler/coco/core/src/IR/Arg.test.cpp new file mode 100644 index 000000000..391e05901 --- /dev/null +++ b/compiler/coco/core/src/IR/Arg.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Arg.h" + +#include +#include + +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; + +namespace +{ +class ArgTest : public ::testing::Test +{ +protected: + coco::Arg *allocate(const Shape &shape) + { + auto arg = new coco::Arg{shape}; + _allocated.emplace_back(arg); + return arg; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(ArgTest, constructor) +{ + const Shape shape{1, 3, 3, 1}; + + auto arg = allocate(shape); + + ASSERT_EQ(arg->shape(), shape); + ASSERT_TRUE(arg->name().empty()); + ASSERT_EQ(arg->bag(), nullptr); +} + +TEST_F(ArgTest, name_update) +{ + const Shape shape{1, 3, 3, 1}; + + auto arg = allocate(shape); + + arg->name("data"); + ASSERT_EQ(arg->name(), "data"); +} + +TEST_F(ArgTest, at) +{ + const Shape shape{1, 3, 3, 1}; + + auto arg = allocate(shape); + + coco::Arg *mutable_ptr = arg; + const coco::Arg *immutable_ptr = arg; + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + mutable_ptr->at(e.current()) = coco::ElemID{16}; + } + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + ASSERT_EQ(immutable_ptr->at(e.current()).value(), 16); + } +} + +TEST_F(ArgTest, reorder) +{ + const Shape shape{2, 2, 2, 2}; + + auto arg = allocate(shape); + + arg->reorder(); + + ASSERT_EQ(arg->at(Index{0, 0, 0, 0}).value(), 0); + ASSERT_EQ(arg->at(Index{0, 0, 0, 1}).value(), 1); +} diff --git a/compiler/coco/core/src/IR/AvgPool2D.test.cpp b/compiler/coco/core/src/IR/AvgPool2D.test.cpp new file mode 100644 index 000000000..d62bc9b7d --- /dev/null +++ b/compiler/coco/core/src/IR/AvgPool2D.test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsAvgPool2D : public coco::Op::Visitor +{ + bool visit(const coco::AvgPool2D *) override { return true; } +}; + +class AvgPool2DTest : public ::testing::Test +{ +public: + AvgPool2DTest() + { + // DO NOTHING + } + +protected: + coco::AvgPool2D *allocate(void) + { + auto op = new coco::AvgPool2D; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(AvgPool2DTest, initialization) +{ + auto op = allocate(); + + coco::AvgPool2D *mutable_ptr = op; + const coco::AvgPool2D *immutable_ptr = op; + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + // arg() should be nullptr on construction + ASSERT_EQ(immutable_ptr->arg(), nullptr); + + // divisor() SHOULD be unknow on construction + ASSERT_EQ(immutable_ptr->divisor(), coco::AvgPool2D::Divisor::Unknown); + + // window() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->window(), nullptr); + ASSERT_EQ(mutable_ptr->window(), immutable_ptr->window()); + + // pad() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->pad(), nullptr); + ASSERT_EQ(mutable_ptr->pad(), immutable_ptr->pad()); + + // stride() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->stride(), nullptr); + ASSERT_EQ(mutable_ptr->stride(), immutable_ptr->stride()); +} + +TEST_F(AvgPool2DTest, asAvgPool2D) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asAvgPool2D(), op); + ASSERT_EQ(mutable_base->asAvgPool2D(), immutable_base->asAvgPool2D()); +} + +TEST_F(AvgPool2DTest, accept) +{ + // Test 'AvgPool2D' class + auto op = allocate(); + + coco::AvgPool2D *mutable_ptr = op; + const coco::AvgPool2D *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsAvgPool2D{})); + ASSERT_TRUE(immutable_ptr->accept(IsAvgPool2D{})); +} + +TEST_F(AvgPool2DTest, disivor) +{ + auto op = allocate(); + + op->divisor(coco::AvgPool2D::Divisor::Static); + + ASSERT_EQ(op->divisor(), coco::AvgPool2D::Divisor::Static); +} diff --git a/compiler/coco/core/src/IR/Bag.cpp b/compiler/coco/core/src/IR/Bag.cpp new file mode 100644 index 000000000..7dce48587 --- /dev/null +++ b/compiler/coco/core/src/IR/Bag.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Bag.h" + +#include "coco/IR/Object.h" +#include "coco/IR/Read.h" +#include "coco/IR/Update.h" + +#include + +namespace coco +{ + +Bag::Bag(uint32_t size) : _size{size} +{ + // DO NOTHING +} + +Bag::~Bag() +{ + // All the references over a bag SHOULD be dropped before its destruction + assert(deps()->size() == 0); + assert(reads()->size() == 0); + assert(updates()->size() == 0); +} + +uint32_t Bag::size(void) const { return _size; } + +bool Bag::isInput(void) const { return _input != nullptr; } +bool Bag::isOutput(void) const { return _output != nullptr; } + +const DepSet *Bag::deps(void) const { return &_deps; } +const ReadSet *Bag::reads(void) const { return &_reads; } +const UpdateSet *Bag::updates(void) const { return &_updates; } + +void Bag::replaceWith(Bag *b) +{ + assert(!isInput() && !isOutput()); + + replaceAllDepsWith(b); + // Replace all the occurence inside Read + while (!(reads()->empty())) + { + auto read = *(reads()->begin()); + assert(read->bag() == this); + read->bag(b); + } + + // Replace all the occurence insider Update + while (!(updates()->empty())) + { + auto update = *(updates()->begin()); + assert(update->bag() == this); + update->bag(b); + } + + assert(deps()->empty()); + assert(reads()->empty()); + assert(updates()->empty()); +} + +void Bag::replaceAllDepsWith(Bag *b) +{ + // Replace all the occurence inside Dep + while (!(deps()->empty())) + { + auto dep = *(deps()->begin()); + assert(dep->bag() == this); + dep->bag(b); + } +} + +ObjectSet dependent_objects(const Bag *b) +{ + ObjectSet res; + + for (const auto &dep : *(b->deps())) + { + if (auto obj = dep->object()) + { + res.insert(obj); + } + } + + return res; +} + +Bag::ReaderSet readers(const Bag *b) +{ + Bag::ReaderSet res; + + for (auto obj : dependent_objects(b)) + { + for (auto consumer : consumers(obj)) + { + // NOTE Object::Consumer inherits Bag::Reader + res.insert(consumer); + } + } + + for (auto read : *b->reads()) + { + auto reader = read->reader(); + assert(reader != nullptr); + res.insert(reader); + } + + return res; +} + +Bag::UpdaterSet updaters(const Bag *b) +{ + Bag::UpdaterSet res; + + for (auto obj : dependent_objects(b)) + { + if (auto p = producer(obj)) + { + res.insert(p); + } + } + + for (auto update : *b->updates()) + { + auto updater = update->updater(); + assert(updater != nullptr); + res.insert(updater); + } + + return res; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Bag.test.cpp b/compiler/coco/core/src/IR/Bag.test.cpp new file mode 100644 index 000000000..9995e81ef --- /dev/null +++ b/compiler/coco/core/src/IR/Bag.test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Bag.h" + +#include + +TEST(IR_BAG, ctor_should_set_size) +{ + coco::Bag b{3}; + + ASSERT_EQ(b.size(), 3); + + // Bag has no read/updates at the beginning + EXPECT_EQ(b.reads()->size(), 0); + EXPECT_EQ(b.updates()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/BagManager.cpp b/compiler/coco/core/src/IR/BagManager.cpp new file mode 100644 index 000000000..10fe69d57 --- /dev/null +++ b/compiler/coco/core/src/IR/BagManager.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BagManager.h" + +#include + +namespace coco +{ + +Bag *BagManager::create(uint32_t size) +{ + auto bag = stdex::make_unique(size); + modulize(bag.get()); + return take(std::move(bag)); +} + +void BagManager::destroy(Bag *b) { release(b); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/BagManager.test.cpp b/compiler/coco/core/src/IR/BagManager.test.cpp new file mode 100644 index 000000000..bf135a951 --- /dev/null +++ b/compiler/coco/core/src/IR/BagManager.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BagManager.h" + +#include + +TEST(IR_BAG_MANAGER, create) +{ + coco::BagManager mgr; + + auto bag = mgr.create(3); + + ASSERT_EQ(bag->size(), 3); +} + +TEST(IR_BAG_MANAGER, destruct) +{ + coco::BagManager mgr; + + auto b = mgr.create(3); + mgr.destroy(b); + + ASSERT_EQ(mgr.size(), 0); +} diff --git a/compiler/coco/core/src/IR/Block.cpp b/compiler/coco/core/src/IR/Block.cpp new file mode 100644 index 000000000..14c026039 --- /dev/null +++ b/compiler/coco/core/src/IR/Block.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Block.h" +#include "coco/IR/Module.h" + +#include + +namespace coco +{ + +template <> void DLinkedList::joined(Module *, Block *curr_blk) +{ + assert(!curr_blk->index().valid()); + uint32_t value = 0; + + if (auto prev_blk = curr_blk->prev()) + { + value = prev_blk->index().value() + 1; + } + + for (auto blk = curr_blk; blk; blk = blk->next()) + { + blk->_index.set(value++); + } +} + +template <> void DLinkedList::leaving(Module *, Block *curr_blk) +{ + assert(curr_blk->index().valid()); + uint32_t value = curr_blk->index().value(); + + for (auto blk = curr_blk->next(); blk; blk = blk->next()) + { + blk->_index.set(value++); + } + + curr_blk->_index.reset(); +} + +template <> BlockList *DLinkedList::head(Module *m) { return m->block(); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Block.test.cpp b/compiler/coco/core/src/IR/Block.test.cpp new file mode 100644 index 000000000..c2acc89f7 --- /dev/null +++ b/compiler/coco/core/src/IR/Block.test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Block.h" + +#include + +TEST(IR_BLOCK, default_block_has_empty_instr_list) +{ + coco::Block blk; + + ASSERT_TRUE(blk.instr()->empty()); + ASSERT_EQ(blk.instr()->head(), nullptr); + ASSERT_EQ(blk.instr()->tail(), nullptr); +} diff --git a/compiler/coco/core/src/IR/BlockIndex.cpp b/compiler/coco/core/src/IR/BlockIndex.cpp new file mode 100644 index 000000000..8cb56724c --- /dev/null +++ b/compiler/coco/core/src/IR/BlockIndex.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BlockIndex.h" + +#include + +namespace coco +{ + +void BlockIndex::set(uint32_t value) +{ + assert(value != undefined); + _value = value; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/BlockIndex.test.cpp b/compiler/coco/core/src/IR/BlockIndex.test.cpp new file mode 100644 index 000000000..68afb889e --- /dev/null +++ b/compiler/coco/core/src/IR/BlockIndex.test.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BlockIndex.h" + +#include + +namespace +{ + +class BlockIndexTest : public ::testing::Test +{ +}; + +} // namespace + +TEST_F(BlockIndexTest, default_constructor) +{ + coco::BlockIndex blk_ind; + + ASSERT_FALSE(blk_ind.valid()); +} + +TEST_F(BlockIndexTest, explicit_constructor) +{ + coco::BlockIndex blk_ind{3}; + + ASSERT_TRUE(blk_ind.valid()); + ASSERT_EQ(blk_ind.value(), 3); +} + +TEST_F(BlockIndexTest, operator_lt) +{ + // Valid index is always less than undefined one. + ASSERT_TRUE(coco::BlockIndex(3) < coco::BlockIndex()); + ASSERT_TRUE(coco::BlockIndex(3) < coco::BlockIndex(4)); +} diff --git a/compiler/coco/core/src/IR/BlockManager.cpp b/compiler/coco/core/src/IR/BlockManager.cpp new file mode 100644 index 000000000..5e3b88173 --- /dev/null +++ b/compiler/coco/core/src/IR/BlockManager.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BlockManager.h" + +#include + +#include + +namespace coco +{ + +Block *BlockManager::create(void) +{ + auto blk = stdex::make_unique(); + modulize(blk.get()); + return take(std::move(blk)); +} + +void BlockManager::destroy(Block *blk) +{ + assert(blk->parent() == nullptr); + assert(blk->prev() == nullptr); + assert(blk->next() == nullptr); + release(blk); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/BlockManager.test.cpp b/compiler/coco/core/src/IR/BlockManager.test.cpp new file mode 100644 index 000000000..94f69b773 --- /dev/null +++ b/compiler/coco/core/src/IR/BlockManager.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/BlockManager.h" + +#include +#include + +#include + +namespace +{ +class BlockManagerTest : public ::testing::Test +{ +public: + // Create a coco::BlockManager for testing + coco::BlockManager *allocate(void) + { + auto p = new coco::BlockManager; + _allocated.emplace_back(p); + return p; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(BlockManagerTest, create) +{ + auto mgr = allocate(); + auto blk = mgr->create(); + + ASSERT_NE(blk, nullptr); +} + +TEST_F(BlockManagerTest, destroy) +{ + auto mgr = allocate(); + auto blk_1 = mgr->create(); + auto blk_2 = mgr->create(); + + mgr->destroy(blk_1); + + ASSERT_EQ(mgr->size(), 1); + ASSERT_EQ(mgr->at(0), blk_2); +} diff --git a/compiler/coco/core/src/IR/Consumer.mock.h b/compiler/coco/core/src/IR/Consumer.mock.h new file mode 100644 index 000000000..7d7cc492a --- /dev/null +++ b/compiler/coco/core/src/IR/Consumer.mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_CONSUMER_MOCK_H__ +#define __COCO_IR_CONSUMER_MOCK_H__ + +#include "coco/IR/Object.h" + +namespace +{ +namespace mock +{ +struct Consumer final : public coco::Object::Consumer +{ + coco::Instr *loc(void) override { return nullptr; } +}; +} // namespace mock +} // namespace + +#endif // __COCO_IR_CONSUMER_MOCK_H__ diff --git a/compiler/coco/core/src/IR/Conv2D.cpp b/compiler/coco/core/src/IR/Conv2D.cpp new file mode 100644 index 000000000..19395a158 --- /dev/null +++ b/compiler/coco/core/src/IR/Conv2D.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include + +namespace coco +{ + +Conv2D::Conv2D() : _ker{this}, _arg{this} +{ + // DO NOTHING +} + +uint32_t Conv2D::arity(void) const +{ + // Conv2D has one argument (IFM) + // NOTE This design is subject to change + return 1; +} + +Op *Conv2D::arg(DBGARG(uint32_t, n)) const +{ + assert(n < arity()); + return arg(); +} + +std::set Conv2D::uses(void) const +{ + std::set res; + + if (ker()) + { + res.insert(ker()); + } + + if (auto ifm = arg()) + { + for (auto obj : ifm->uses()) + { + res.insert(obj); + } + } + + return res; +} + +void Conv2D::ker(KernelObject *ker) { _ker.value(ker); } + +KernelObject *Conv2D::ker(void) const +{ + if (auto obj = _ker.value()) + { + assert(obj->asKernel() != nullptr); + return obj->asKernel(); + } + + return nullptr; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Conv2D.test.cpp b/compiler/coco/core/src/IR/Conv2D.test.cpp new file mode 100644 index 000000000..df0a2470b --- /dev/null +++ b/compiler/coco/core/src/IR/Conv2D.test.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" +#include "coco/IR/ObjectManager.h" + +#include +#include + +#include + +#include + +using stdex::make_unique; + +namespace +{ +class Conv2DTest : public ::testing::Test +{ +public: + Conv2DTest() + { + // DO NOTHING + } + +protected: + coco::Conv2D *allocate(void) + { + auto op = new coco::Conv2D; + _allocated.emplace_back(op); + return op; + } + +protected: + coco::ObjectManager obj_mgr; + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(Conv2DTest, ctor) +{ + auto op = allocate(); + + // arg() should be initialized as nullptr on construction + ASSERT_EQ(op->arg(), nullptr); + // ker() should be initialized as nullptr on construction + ASSERT_EQ(op->ker(), nullptr); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + ASSERT_EQ(op->group(), 1); + + ASSERT_NE(op->pad(), nullptr); + ASSERT_EQ(op->pad()->top(), 0); + ASSERT_EQ(op->pad()->bottom(), 0); + ASSERT_EQ(op->pad()->left(), 0); + ASSERT_EQ(op->pad()->right(), 0); + + ASSERT_NE(op->stride(), nullptr); + ASSERT_EQ(op->stride()->vertical(), 1); + ASSERT_EQ(op->stride()->horizontal(), 1); +} + +TEST_F(Conv2DTest, asConv2D) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asConv2D(), op); + ASSERT_EQ(mutable_base->asConv2D(), immutable_base->asConv2D()); +} + +namespace +{ +struct IsConv2D : public coco::Op::Visitor +{ + bool visit(const coco::Conv2D *) override { return true; } +}; +} // namespace + +TEST_F(Conv2DTest, ker_update) +{ + // Prepare a kernel object for testing + auto obj = obj_mgr.create(); + + // Test 'Conv2D' class + auto op = allocate(); + + op->ker(obj); + ASSERT_EQ(op->ker(), obj); + + // Op now uses 'obj' + { + auto uses = op->uses(); + + ASSERT_NE(uses.find(obj), uses.end()); + } + + // ker method should enlist op itself as a consumer of a given kernel object + { + auto consumers = coco::consumers(obj); + + ASSERT_EQ(consumers.size(), 1); + ASSERT_NE(consumers.find(op), consumers.end()); + } +} + +TEST_F(Conv2DTest, accept) +{ + // Test 'Conv2D' class + auto op = allocate(); + + coco::Conv2D *mutable_ptr = op; + const coco::Conv2D *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsConv2D{})); + ASSERT_TRUE(immutable_ptr->accept(IsConv2D{})); +} + +TEST_F(Conv2DTest, destructor) +{ + // Prepare a kernel object for testing + auto obj = obj_mgr.create(); + + // Create 'Conv2D' op + auto op = make_unique(); + + op->ker(obj); + + // Destroy 'Conv2D' op + op.reset(); + + ASSERT_EQ(obj->uses()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/Def.cpp b/compiler/coco/core/src/IR/Def.cpp new file mode 100644 index 000000000..1546b6693 --- /dev/null +++ b/compiler/coco/core/src/IR/Def.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Def.h" + +#include + +namespace coco +{ + +void Def::value(Object *value) +{ + if (_value) + { + _value->def(nullptr); + _value = nullptr; + } + + assert(_value == nullptr); + + if (value) + { + _value = value; + _value->def(this); + } + + assert(_value == value); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Def.test.cpp b/compiler/coco/core/src/IR/Def.test.cpp new file mode 100644 index 000000000..98455c09e --- /dev/null +++ b/compiler/coco/core/src/IR/Def.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Def.h" +#include "coco/IR/ObjectManager.h" + +#include "coco/IR/FeatureObject.h" + +#include + +#include "Producer.mock.h" + +#include + +using stdex::make_unique; + +namespace +{ +class DefTest : public ::testing::Test +{ +protected: + coco::ObjectManager obj_mgr; +}; +} // namespace + +TEST_F(DefTest, constructor) +{ + auto o = obj_mgr.create(); + + ::mock::Producer producer; + coco::Def def{&producer}; + + ASSERT_EQ(def.value(), nullptr); +} + +TEST_F(DefTest, value) +{ + auto o = obj_mgr.create(); + + ::mock::Producer producer; + coco::Def def{&producer}; + + def.value(o); + + ASSERT_EQ(def.value(), o); + + ASSERT_EQ(o->def(), &def); + + def.value(nullptr); + + ASSERT_EQ(o->def(), nullptr); +} + +TEST_F(DefTest, unlink_on_destruction) +{ + auto o = obj_mgr.create(); + + ::mock::Producer producer; + auto def = make_unique(&producer); + + def->value(o); + ASSERT_EQ(o->def(), def.get()); + + // Let's destruct the allocated slot + def.reset(nullptr); + + // The def of Object SHOULD BE updated + ASSERT_EQ(o->def(), nullptr); +} diff --git a/compiler/coco/core/src/IR/Dep.cpp b/compiler/coco/core/src/IR/Dep.cpp new file mode 100644 index 000000000..6a5d3cafb --- /dev/null +++ b/compiler/coco/core/src/IR/Dep.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Dep.h" +#include "coco/IR/Object.h" + +#include + +namespace coco +{ + +Dep::~Dep() { bag(nullptr); } + +void Dep::bag(Bag *bag) +{ + if (_bag != nullptr) + { + // Remove bag <-> dep link + assert(_bag->deps()->find(this) != _bag->deps()->end()); + _bag->mutable_deps()->erase(this); + + // Reset _bag + _bag = nullptr; + } + + assert(_bag == nullptr); + + if (bag != nullptr) + { + // Set _bag + _bag = bag; + + // Create bag <-> dep link + _bag->mutable_deps()->insert(this); + } + + assert(_bag == bag); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Dep.test.cpp b/compiler/coco/core/src/IR/Dep.test.cpp new file mode 100644 index 000000000..e2104a8af --- /dev/null +++ b/compiler/coco/core/src/IR/Dep.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Dep.h" + +#include "coco/IR/BagManager.h" + +#include "coco/IR/ObjectManager.h" +#include "coco/IR/FeatureObject.h" + +#include + +using namespace nncc::core::ADT; + +namespace +{ +class DepTest : public ::testing::Test +{ +protected: + coco::BagManager bag_mgr; + coco::ObjectManager obj_mgr; +}; +} // namespace + +TEST_F(DepTest, default_constructor) +{ + coco::Dep dep; + + ASSERT_EQ(dep.bag(), nullptr); + ASSERT_EQ(dep.object(), nullptr); +} + +TEST_F(DepTest, bag_update) +{ + auto bag = bag_mgr.create(3); + + coco::Dep dep; + + // NOTE b->object() is not updated here + dep.bag(bag); + + ASSERT_EQ(dep.bag(), bag); +} + +TEST_F(DepTest, bag_update_with_link_and_object) +{ + auto bag = bag_mgr.create(3); + auto obj = obj_mgr.create(); + + coco::Dep dep; + + dep.object(obj); + + dep.bag(bag); + + auto deps = coco::dependent_objects(bag); + + ASSERT_EQ(deps.size(), 1); + ASSERT_NE(deps.count(obj), 0); +} diff --git a/compiler/coco/core/src/IR/ElemID.cpp b/compiler/coco/core/src/IR/ElemID.cpp new file mode 100644 index 000000000..145bb986a --- /dev/null +++ b/compiler/coco/core/src/IR/ElemID.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/ElemID.h" + +namespace coco +{ + +bool operator==(const ElemID &lhs, const ElemID &rhs) { return lhs.value() == rhs.value(); } +bool operator<(const ElemID &lhs, const ElemID &rhs) { return lhs.value() < rhs.value(); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/ElemID.test.cpp b/compiler/coco/core/src/IR/ElemID.test.cpp new file mode 100644 index 000000000..dff2fa27c --- /dev/null +++ b/compiler/coco/core/src/IR/ElemID.test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/ElemID.h" + +#include + +#include + +TEST(IR_ELEM_ID, constructor) +{ + coco::ElemID id{128}; + + ASSERT_EQ(id.value(), 128); +} + +TEST(IR_ELEM_ID, copy) +{ + coco::ElemID src{16}; + coco::ElemID dst{32}; + + dst = src; + + ASSERT_EQ(dst.value(), 16); +} + +TEST(IR_ELEM_ID, std_vector_compatible) +{ + // ElemID SHOULD be compatible with standard container (including std::vector) + std::vector vec; + + vec.resize(16); + vec.clear(); + vec.emplace_back(coco::ElemID{128}); + + ASSERT_EQ(vec.at(0).value(), 128); +} + +TEST(IR_ELEM_ID, operator_eq) +{ + ASSERT_TRUE(coco::ElemID{16} == coco::ElemID{16}); + ASSERT_FALSE(coco::ElemID{16} == coco::ElemID{17}); +} + +TEST(IR_ELEM_ID, operator_lt) +{ + ASSERT_FALSE(coco::ElemID{16} < coco::ElemID{16}); + ASSERT_TRUE(coco::ElemID{16} < coco::ElemID{17}); +} diff --git a/compiler/coco/core/src/IR/EntityManager.cpp b/compiler/coco/core/src/IR/EntityManager.cpp new file mode 100644 index 000000000..f6f2cb382 --- /dev/null +++ b/compiler/coco/core/src/IR/EntityManager.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/EntityManager.h" + +// NOTE Do NOT delete this file; this file enforces compiler to check whether 'EntityManager.h' is +// complete. diff --git a/compiler/coco/core/src/IR/Eval.cpp b/compiler/coco/core/src/IR/Eval.cpp new file mode 100644 index 000000000..dcf579049 --- /dev/null +++ b/compiler/coco/core/src/IR/Eval.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Instrs.h" +#include "coco/IR/Op.h" + +namespace coco +{ + +Eval::Eval() : _out{this}, _step{this} +{ + // DO NOTHING +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Eval.test.cpp b/compiler/coco/core/src/IR/Eval.test.cpp new file mode 100644 index 000000000..6469f6763 --- /dev/null +++ b/compiler/coco/core/src/IR/Eval.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Instrs.h" +#include "coco/IR/ObjectManager.h" +#include "coco/IR/OpManager.h" + +#include + +namespace +{ +class EvalTest : public ::testing::Test +{ +public: + virtual ~EvalTest() = default; + +protected: + coco::Eval *allocate(void) + { + auto ins = new coco::Eval{}; + _allocated.emplace_back(ins); + return ins; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(EvalTest, constructor) +{ + auto ins = allocate(); + + ASSERT_EQ(ins->out(), nullptr); + ASSERT_EQ(ins->op(), nullptr); +} + +TEST_F(EvalTest, asEval) +{ + auto ins = allocate(); + + coco::Instr *mutable_ptr = ins; + const coco::Instr *immutable_ptr = ins; + + ASSERT_NE(mutable_ptr->asEval(), nullptr); + ASSERT_EQ(mutable_ptr->asEval(), immutable_ptr->asEval()); +} diff --git a/compiler/coco/core/src/IR/FeatureLayouts.cpp b/compiler/coco/core/src/IR/FeatureLayouts.cpp new file mode 100644 index 000000000..98423e01f --- /dev/null +++ b/compiler/coco/core/src/IR/FeatureLayouts.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/FeatureLayouts.h" + +#include +#include + +#include + +using namespace nncc::core::ADT::feature; + +// +// BCHW Layout +// +namespace coco +{ +namespace FeatureLayouts +{ + +const FeatureLayout::ID *BCHW::uid(void) +{ + struct LayoutID final : public FeatureLayout::ID + { + }; + static LayoutID id; + return &id; +} + +ElemID BCHW::at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const +{ + static CHWLayout l; + + uint32_t offset = 0; + offset += b * num_elements(_shape); + offset += l.offset(_shape, ch, row, col); + return ElemID{offset}; +} + +std::unique_ptr BCHW::create(const nncc::core::ADT::feature::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new BCHW{FeatureShape{shape}}}; +} + +} // namespace FeatureLayouts +} // namespace coco + +// +// BHWC Layout +// +namespace coco +{ +namespace FeatureLayouts +{ + +const FeatureLayout::ID *BHWC::uid(void) +{ + struct LayoutID final : public FeatureLayout::ID + { + }; + static LayoutID id; + return &id; +} + +ElemID BHWC::at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const +{ + static HWCLayout l; + + uint32_t offset = 0; + offset += b * num_elements(_shape); + offset += l.offset(_shape, ch, row, col); + + return ElemID{offset}; +} + +std::unique_ptr BHWC::create(const nncc::core::ADT::feature::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new BHWC{FeatureShape{shape}}}; +} + +std::unique_ptr BHWC::create(const FeatureShape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new BHWC{shape}}; +} + +} // namespace FeatureLayouts +} // namespace coco + +// +// BC: Channel-major Channel-wise Layout +// +namespace coco +{ +namespace FeatureLayouts +{ + +const FeatureLayout::ID *BC::uid(void) +{ + struct LayoutID final : public FeatureLayout::ID + { + }; + static LayoutID id; + return &id; +} + +// NOTE BC layout ignores row/col as its name suggests +ElemID BC::at(uint32_t b, uint32_t ch, uint32_t /*row*/, uint32_t /*col*/) const +{ + assert(b < shape().batch()); + + uint32_t offset = 0; + + offset += b * _shape.depth(); + offset += ch; + + return ElemID{offset}; +} + +std::unique_ptr BC::create(const nncc::core::ADT::feature::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new BC{FeatureShape{shape}}}; +} + +} // namespace FeatureLayouts +} // namespace coco + +// +// Generic Layout +// +namespace coco +{ +namespace FeatureLayouts +{ + +Generic::Generic(const FeatureShape &shape) : _shape{shape} +{ + _content.resize(_shape.batch() * num_elements(_shape)); +} + +const FeatureLayout::ID *Generic::uid(void) +{ + struct LayoutID final : public FeatureLayout::ID + { + }; + static LayoutID id; + return &id; +} + +uint32_t Generic::offset(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const +{ + static nncc::core::ADT::feature::CHWLayout l{}; + + uint32_t res = 0; + + res += b * num_elements(_shape); + res += l.offset(shape(), ch, row, col); + + return res; +} + +ElemID &Generic::at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) +{ + return _content.at(offset(b, ch, row, col)); +} + +ElemID Generic::at(uint32_t b, uint32_t ch, uint32_t row, uint32_t col) const +{ + return _content.at(offset(b, ch, row, col)); +} + +void Generic::reorder(const nncc::core::ADT::feature::Layout &l) +{ + assert(shape().batch() == 1); + + for (uint32_t ch = 0; ch < shape().depth(); ++ch) + { + for (uint32_t row = 0; row < shape().height(); ++row) + { + for (uint32_t col = 0; col < shape().width(); ++col) + { + at(0, ch, row, col) = ElemID{l.offset(shape(), ch, row, col)}; + } + } + } +} + +std::unique_ptr Generic::create(const nncc::core::ADT::feature::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new Generic{shape}}; +} + +} // namespace FeatureLayouts +} // namespace coco diff --git a/compiler/coco/core/src/IR/FeatureLayouts.test.cpp b/compiler/coco/core/src/IR/FeatureLayouts.test.cpp new file mode 100644 index 000000000..9f9772dd8 --- /dev/null +++ b/compiler/coco/core/src/IR/FeatureLayouts.test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/FeatureLayouts.h" + +#include + +using namespace nncc::core::ADT; + +TEST(FeatureLayoutsTest, BC) +{ + // NOTE The current implementation uses a hard-coded "batch" value + const uint32_t B = 1; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 5; + + auto l = coco::FeatureLayouts::BC::create(feature::Shape{C, H, W}); + + ASSERT_EQ(l->batch(), B); + ASSERT_EQ(l->depth(), C); + ASSERT_EQ(l->height(), H); + ASSERT_EQ(l->width(), W); + + // Check whether BC layout is actually channel-wise + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + ASSERT_EQ(l->at(b, ch, 0, 0), l->at(b, ch, row, col)); + } + } + } + } + + // Check whether BC layout is actually channel-major + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 1; ch < C; ++ch) + { + ASSERT_EQ(l->at(b, ch - 1, 0, 0).value() + 1, l->at(b, ch, 0, 0).value()); + } + } + + for (uint32_t b = 1; b < B; ++b) + { + ASSERT_EQ(l->at(b - 1, C - 1, 0, 0).value() + 1, l->at(b, 0, 0, 0).value()); + } +} diff --git a/compiler/coco/core/src/IR/FeatureObject.cpp b/compiler/coco/core/src/IR/FeatureObject.cpp new file mode 100644 index 000000000..46de98874 --- /dev/null +++ b/compiler/coco/core/src/IR/FeatureObject.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/FeatureObject.h" + +#include + +namespace coco +{ + +FeatureObject::~FeatureObject() +{ + // DO NOTHING +} + +const FeatureShape &FeatureObject::shape(void) const { return _layout->shape(); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/FeatureObject.test.cpp b/compiler/coco/core/src/IR/FeatureObject.test.cpp new file mode 100644 index 000000000..23188f866 --- /dev/null +++ b/compiler/coco/core/src/IR/FeatureObject.test.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/FeatureObject.h" +#include "coco/IR/FeatureLayouts.h" + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace +{ +class FeatureObjectTest : public ::testing::Test +{ +protected: + coco::FeatureObject *allocate() + { + auto o = new coco::FeatureObject{}; + _allocated.emplace_back(o); + return o; + } + + // TODO Deprecate this method + coco::FeatureObject *allocate(const coco::FeatureShape &shape) + { + auto o = new coco::FeatureObject{}; + o->layout(coco::FeatureLayouts::Generic::create(shape)); + _allocated.emplace_back(o); + return o; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(FeatureObjectTest, ctor) +{ + const coco::FeatureShape shape{1, 3, 3}; + + auto o = allocate(shape); + + ASSERT_EQ(o->shape(), shape); + ASSERT_EQ(o->kind(), coco::Object::Kind::Feature); +} + +// TODO Reimplement this test as a test for GenericFeatureLayout +#if 0 +TEST_F(FeatureObjectTest, at) +{ + const uint32_t C = 1; + const uint32_t H = 3; + const uint32_t W = 3; + + const coco::FeatureShape shape{C, H, W}; + + auto o = allocate(shape); + + coco::FeatureObject *mutable_ptr = o; + const coco::FeatureObject *immutable_ptr = o; + + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + mutable_ptr->at(ch, row, col) = coco::ElemID{16}; + } + } + } + + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + ASSERT_EQ(immutable_ptr->at(ch, row, col).value(), 16); + } + } + } +} +#endif + +TEST_F(FeatureObjectTest, asFeature) +{ + const coco::FeatureShape shape{1, 3, 3}; + + auto o = allocate(shape); + + coco::Object *mutable_object = o; + const coco::Object *immutable_object = o; + + ASSERT_NE(mutable_object->asFeature(), nullptr); + ASSERT_EQ(mutable_object->asFeature(), immutable_object->asFeature()); +} + +TEST_F(FeatureObjectTest, casting_helpers) +{ + auto obj = allocate(); + + ASSERT_TRUE(coco::isa(obj)); + ASSERT_EQ(coco::cast(obj), obj); + ASSERT_EQ(coco::safe_cast(obj), obj); +} diff --git a/compiler/coco/core/src/IR/FeatureShape.test.cpp b/compiler/coco/core/src/IR/FeatureShape.test.cpp new file mode 100644 index 000000000..ceeab02b7 --- /dev/null +++ b/compiler/coco/core/src/IR/FeatureShape.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/FeatureShape.h" + +#include + +TEST(FeatureShapeTest, constructor_with_4_arguments) +{ + const coco::FeatureShape shape{1, 2, 3, 4}; + + ASSERT_EQ(shape.batch(), 1); + ASSERT_EQ(shape.depth(), 2); + ASSERT_EQ(shape.height(), 3); + ASSERT_EQ(shape.width(), 4); +} diff --git a/compiler/coco/core/src/IR/Input.cpp b/compiler/coco/core/src/IR/Input.cpp new file mode 100644 index 000000000..4385ac26c --- /dev/null +++ b/compiler/coco/core/src/IR/Input.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Input.h" + +#include + +namespace coco +{ + +Input::Input(const nncc::core::ADT::tensor::Shape &shape) : Arg{shape} +{ + // DO NOT?HING +} + +void Input::onTake(Bag *bag) +{ + assert(bag->input() == nullptr); + bag->input(this); +} + +void Input::onRelease(Bag *bag) +{ + assert(bag->input() == this); + bag->input(nullptr); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Input.test.cpp b/compiler/coco/core/src/IR/Input.test.cpp new file mode 100644 index 000000000..7cc1731cc --- /dev/null +++ b/compiler/coco/core/src/IR/Input.test.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Input.h" +#include "coco/IR/BagManager.h" + +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +TEST(IR_INPUT, ctor_should_set_shape) +{ + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Input input{shape}; + + ASSERT_EQ(input.shape(), shape); + ASSERT_TRUE(input.name().empty()); +} + +TEST(IR_INPUT, bag_update) +{ + // Create a bag + coco::BagManager bag_mgr; + + auto bag = bag_mgr.create(9); + + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Input input{shape}; + + input.bag(bag); + ASSERT_EQ(input.bag(), bag); + + // bag(...) method SHOULD update 'bag' type + ASSERT_TRUE(bag->isInput()); +} + +TEST(IR_INPUT, name_update) +{ + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Input input{shape}; + + input.name("data"); + ASSERT_EQ(input.name(), "data"); +} + +TEST(IR_INPUT, at) +{ + const Shape shape{1, 3, 3, 1}; + coco::Input input{shape}; + + coco::Input *mutable_ptr = &input; + const coco::Input *immutable_ptr = &input; + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + mutable_ptr->at(e.current()) = coco::ElemID{16}; + } + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + ASSERT_EQ(immutable_ptr->at(e.current()).value(), 16); + } +} diff --git a/compiler/coco/core/src/IR/InputManager.cpp b/compiler/coco/core/src/IR/InputManager.cpp new file mode 100644 index 000000000..6d5b9470b --- /dev/null +++ b/compiler/coco/core/src/IR/InputManager.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InputManager.h" + +#include + +namespace coco +{ + +Input *InputManager::create(const nncc::core::ADT::tensor::Shape &shape) +{ + auto input = stdex::make_unique(shape); + modulize(input.get()); + return take(std::move(input)); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/InputManager.test.cpp b/compiler/coco/core/src/IR/InputManager.test.cpp new file mode 100644 index 000000000..be43113b4 --- /dev/null +++ b/compiler/coco/core/src/IR/InputManager.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InputManager.h" + +#include + +TEST(IR_INPUT_MANAGER, make) +{ + coco::InputManager mgr; + + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + auto input = mgr.create(shape); + + ASSERT_EQ(input->shape(), shape); +} diff --git a/compiler/coco/core/src/IR/Instr.cpp b/compiler/coco/core/src/IR/Instr.cpp new file mode 100644 index 000000000..9f000ba1c --- /dev/null +++ b/compiler/coco/core/src/IR/Instr.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Instr.h" +#include "coco/IR/Block.h" + +#include + +namespace coco +{ + +template <> void DLinkedList::joined(Block *, Instr *curr_ins) +{ + assert(!curr_ins->index().valid()); + uint32_t value = 0; + + if (auto prev_ins = curr_ins->prev()) + { + value = prev_ins->index().value() + 1; + } + + for (auto ins = curr_ins; ins; ins = ins->next()) + { + ins->_index.set(value++); + } +} + +template <> void DLinkedList::leaving(Block *, Instr *curr_ins) +{ + assert(curr_ins->index().valid()); + uint32_t value = curr_ins->index().value(); + + for (auto ins = curr_ins->next(); ins; ins = ins->next()) + { + ins->_index.set(value++); + } + + curr_ins->_index.reset(); +} + +template <> InstrList *DLinkedList::head(Block *b) { return b->instr(); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/InstrIndex.cpp b/compiler/coco/core/src/IR/InstrIndex.cpp new file mode 100644 index 000000000..c447cfc42 --- /dev/null +++ b/compiler/coco/core/src/IR/InstrIndex.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InstrIndex.h" + +#include + +namespace coco +{ + +void InstrIndex::set(uint32_t value) +{ + assert(value != undefined); + _value = value; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/InstrIndex.test.cpp b/compiler/coco/core/src/IR/InstrIndex.test.cpp new file mode 100644 index 000000000..40f5d49de --- /dev/null +++ b/compiler/coco/core/src/IR/InstrIndex.test.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InstrIndex.h" + +#include + +namespace +{ + +class InstrIndexTest : public ::testing::Test +{ +}; + +} // namespace + +TEST_F(InstrIndexTest, default_constructor) +{ + coco::InstrIndex ins_ind; + + ASSERT_FALSE(ins_ind.valid()); +} + +TEST_F(InstrIndexTest, explicit_constructor) +{ + coco::InstrIndex ins_ind{3}; + + ASSERT_TRUE(ins_ind.valid()); + ASSERT_EQ(ins_ind.value(), 3); +} + +TEST_F(InstrIndexTest, operator_lt) +{ + // Valid index is always less than undefined one. + ASSERT_TRUE(coco::InstrIndex(3) < coco::InstrIndex()); + ASSERT_TRUE(coco::InstrIndex(3) < coco::InstrIndex(4)); +} diff --git a/compiler/coco/core/src/IR/InstrManager.cpp b/compiler/coco/core/src/IR/InstrManager.cpp new file mode 100644 index 000000000..32f1cbf28 --- /dev/null +++ b/compiler/coco/core/src/IR/InstrManager.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InstrManager.h" + +#include "coco/IR/Op.h" + +#include + +namespace coco +{ + +void InstrManager::destroy(Instr *ins) +{ + // ins SHOULD BE detached from any block before destroy call + assert(ins->parent() == nullptr); + release(ins); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/InstrManager.test.cpp b/compiler/coco/core/src/IR/InstrManager.test.cpp new file mode 100644 index 000000000..23d9f8e86 --- /dev/null +++ b/compiler/coco/core/src/IR/InstrManager.test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/InstrManager.h" +#include "coco/IR/Op.h" + +#include + +namespace +{ + +// Dummy custom instruction for testing +struct CustomInstr final : public coco::Instr +{ +}; + +class InstrManagerTest : public ::testing::Test +{ +public: + virtual ~InstrManagerTest() = default; + +protected: + coco::InstrManager mgr; +}; +} // namespace + +TEST_F(InstrManagerTest, create_Shuffle) +{ + auto ins = mgr.create(); + ASSERT_NE(ins, nullptr); + mgr.destroy(ins); +} + +TEST_F(InstrManagerTest, create_Custom) +{ + auto ins = mgr.create(); + ASSERT_NE(ins, nullptr); + mgr.destroy(ins); +} diff --git a/compiler/coco/core/src/IR/KernelLayouts.cpp b/compiler/coco/core/src/IR/KernelLayouts.cpp new file mode 100644 index 000000000..6e9a1575a --- /dev/null +++ b/compiler/coco/core/src/IR/KernelLayouts.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/KernelLayouts.h" + +#include +#include + +#include + +using namespace nncc::core::ADT::kernel; + +using nncc::core::ADT::kernel::num_elements; +using nncc::core::ADT::kernel::Shape; + +// +// NCHW Layout +// +namespace coco +{ +namespace KernelLayouts +{ + +const KernelLayout::ID *NCHW::uid(void) +{ + struct LayoutID final : public KernelLayout::ID + { + }; + static LayoutID id; + return &id; +} + +ElemID NCHW::at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const +{ + static NCHWLayout l; + return ElemID{l.offset(_shape, n, ch, row, col)}; +} + +std::unique_ptr NCHW::create(const nncc::core::ADT::kernel::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new NCHW{shape}}; +} + +} // namespace KernelLayouts +} // namespace coco + +// +// NHWC Layout +// +namespace coco +{ +namespace KernelLayouts +{ + +const KernelLayout::ID *NHWC::uid(void) +{ + struct LayoutID final : public KernelLayout::ID + { + }; + static LayoutID id; + return &id; +} + +ElemID NHWC::at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const +{ + static NHWCLayout l; + return ElemID{l.offset(_shape, n, ch, row, col)}; +} + +std::unique_ptr NHWC::create(const nncc::core::ADT::kernel::Shape &shape) +{ + // NOTE It is impossible to use make_unique here as the constructor is private + return std::unique_ptr{new NHWC{shape}}; +} + +} // namespace KernelLayouts +} // namespace coco + +// +// Generic Layout +// +namespace +{ + +nncc::core::ADT::kernel::NCHWLayout l; + +} // namespace + +namespace coco +{ +namespace KernelLayouts +{ + +Generic::Generic(const nncc::core::ADT::kernel::Shape &shape) : _shape{shape} +{ + _content.resize(num_elements(_shape)); +} + +const KernelLayout::ID *Generic::uid(void) +{ + struct LayoutID final : public KernelLayout::ID + { + }; + static LayoutID id; + return &id; +} + +ElemID &Generic::at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) +{ + return _content.at(l.offset(_shape, n, ch, row, col)); +} + +ElemID Generic::at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const +{ + return _content.at(l.offset(_shape, n, ch, row, col)); +} + +void Generic::reorder(const nncc::core::ADT::kernel::Layout &l) +{ + for (uint32_t n = 0; n < shape().count(); ++n) + { + for (uint32_t ch = 0; ch < shape().depth(); ++ch) + { + for (uint32_t row = 0; row < shape().height(); ++row) + { + for (uint32_t col = 0; col < shape().width(); ++col) + { + at(n, ch, row, col) = ElemID{l.offset(shape(), n, ch, row, col)}; + } + } + } + } +} + +std::unique_ptr Generic::create(const Shape &shape) +{ + return std::unique_ptr{new Generic{shape}}; +} + +} // namespace KernelLayouts +} // namespace coco diff --git a/compiler/coco/core/src/IR/KernelLayouts.test.cpp b/compiler/coco/core/src/IR/KernelLayouts.test.cpp new file mode 100644 index 000000000..df13cb051 --- /dev/null +++ b/compiler/coco/core/src/IR/KernelLayouts.test.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/KernelLayouts.h" + +#include +#include + +#include + +using namespace nncc::core::ADT; + +TEST(KernelLayoutsTest, NCHW_increment) +{ + const uint32_t N = 2; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 4; + + auto l = coco::KernelLayouts::NCHW::create(kernel::Shape{N, C, H, W}); + + // check NCHW order + ASSERT_EQ(l->at(0, 0, 0, 1).value(), l->at(0, 0, 0, 0).value() + 1); + ASSERT_EQ(l->at(0, 0, 1, 0).value(), l->at(0, 0, 0, 0).value() + W); + ASSERT_EQ(l->at(0, 1, 0, 0).value(), l->at(0, 0, 0, 0).value() + H * W); + ASSERT_EQ(l->at(1, 0, 0, 0).value(), l->at(0, 0, 0, 0).value() + C * H * W); +} + +TEST(KernelLayoutsTest, NHWC_increment) +{ + const uint32_t N = 2; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 4; + + auto l = coco::KernelLayouts::NHWC::create(kernel::Shape{N, C, H, W}); + + // check NHWC order + ASSERT_EQ(l->at(0, 1, 0, 0).value(), l->at(0, 0, 0, 0).value() + 1); + ASSERT_EQ(l->at(0, 0, 0, 1).value(), l->at(0, 0, 0, 0).value() + C); + ASSERT_EQ(l->at(0, 0, 1, 0).value(), l->at(0, 0, 0, 0).value() + W * C); + ASSERT_EQ(l->at(1, 0, 0, 0).value(), l->at(0, 0, 0, 0).value() + H * W * C); +} + +TEST(KernelLayoutsTest, Generic_increment) +{ + const uint32_t N = 2; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 4; + + auto nchw = coco::KernelLayouts::Generic::create(kernel::Shape{N, C, H, W}); + auto nhwc = coco::KernelLayouts::Generic::create(kernel::Shape{N, C, H, W}); + + // reorder + nchw->reorder(kernel::NCHWLayout()); + nhwc->reorder(kernel::NHWCLayout()); + + // check NCHW order + ASSERT_EQ(nchw->at(0, 0, 0, 1).value(), nchw->at(0, 0, 0, 0).value() + 1); + ASSERT_EQ(nchw->at(0, 0, 1, 0).value(), nchw->at(0, 0, 0, 0).value() + W); + ASSERT_EQ(nchw->at(0, 1, 0, 0).value(), nchw->at(0, 0, 0, 0).value() + H * W); + ASSERT_EQ(nchw->at(1, 0, 0, 0).value(), nchw->at(0, 0, 0, 0).value() + C * H * W); + + // check NHWC order + ASSERT_EQ(nhwc->at(0, 1, 0, 0).value(), nhwc->at(0, 0, 0, 0).value() + 1); + ASSERT_EQ(nhwc->at(0, 0, 0, 1).value(), nhwc->at(0, 0, 0, 0).value() + C); + ASSERT_EQ(nhwc->at(0, 0, 1, 0).value(), nhwc->at(0, 0, 0, 0).value() + W * C); + ASSERT_EQ(nhwc->at(1, 0, 0, 0).value(), nhwc->at(0, 0, 0, 0).value() + H * W * C); +} + +TEST(KernelLayoutsTest, Generic_at) +{ + const uint32_t N = 2; + const uint32_t C = 3; + const uint32_t H = 4; + const uint32_t W = 4; + + auto l = coco::KernelLayouts::Generic::create(kernel::Shape{N, C, H, W}); + + ASSERT_NE(l.get(), nullptr); + + coco::KernelLayouts::Generic *mutable_ptr = l.get(); + const coco::KernelLayouts::Generic *immutable_ptr = l.get(); + + for (uint32_t n = 0; n < N; ++n) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + mutable_ptr->at(n, ch, row, col) = coco::ElemID{16}; + } + } + } + } + + for (uint32_t n = 0; n < N; ++n) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + ASSERT_EQ(immutable_ptr->at(n, ch, row, col).value(), 16); + } + } + } + } +} diff --git a/compiler/coco/core/src/IR/KernelObject.cpp b/compiler/coco/core/src/IR/KernelObject.cpp new file mode 100644 index 000000000..79c298b43 --- /dev/null +++ b/compiler/coco/core/src/IR/KernelObject.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/KernelObject.h" +#include "coco/IR/KernelLayouts.h" + +#include + +namespace coco +{ + +KernelObject::KernelObject(const nncc::core::ADT::kernel::Shape &shape) +{ + _layout = KernelLayouts::Generic::create(shape); +} + +KernelObject::~KernelObject() +{ + // DO NOTHING +} + +const nncc::core::ADT::kernel::Shape &KernelObject::shape(void) const { return _layout->shape(); } + +ElemID KernelObject::at(uint32_t n, uint32_t ch, uint32_t row, uint32_t col) const +{ + return _layout->at(n, ch, row, col); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/KernelObject.test.cpp b/compiler/coco/core/src/IR/KernelObject.test.cpp new file mode 100644 index 000000000..f227764ca --- /dev/null +++ b/compiler/coco/core/src/IR/KernelObject.test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/KernelObject.h" + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace +{ +class KernelObjectTest : public ::testing::Test +{ +protected: + coco::KernelObject *allocate() + { + auto o = new coco::KernelObject{}; + _allocated.emplace_back(o); + return o; + } + + coco::KernelObject *allocate(const kernel::Shape &shape) + { + auto o = new coco::KernelObject{shape}; + _allocated.emplace_back(o); + return o; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(KernelObjectTest, constructor) +{ + const nncc::core::ADT::kernel::Shape shape{1, 1, 3, 3}; + auto o = allocate(shape); + + ASSERT_EQ(o->shape(), shape); + ASSERT_EQ(o->kind(), coco::Object::Kind::Kernel); +} + +TEST_F(KernelObjectTest, asKernel) +{ + const nncc::core::ADT::kernel::Shape shape{1, 1, 3, 3}; + auto o = allocate(shape); + + coco::Object *mutable_object = o; + const coco::Object *immutable_object = o; + + ASSERT_NE(mutable_object->asKernel(), nullptr); + ASSERT_EQ(mutable_object->asKernel(), immutable_object->asKernel()); +} + +TEST_F(KernelObjectTest, casting_helpers) +{ + auto obj = allocate(); + + ASSERT_TRUE(coco::isa(obj)); + ASSERT_EQ(coco::cast(obj), obj); + ASSERT_EQ(coco::safe_cast(obj), obj); +} diff --git a/compiler/coco/core/src/IR/Load.cpp b/compiler/coco/core/src/IR/Load.cpp new file mode 100644 index 000000000..4985e9254 --- /dev/null +++ b/compiler/coco/core/src/IR/Load.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include + +namespace coco +{ + +Load::Load() : _obj{this} +{ + // DO NOTHING +} + +uint32_t Load::arity(void) const +{ + // Load has no child Op + return 0; +} + +Op *Load::arg(uint32_t) const +{ + assert(!"Load has no argument"); + return nullptr; +} + +std::set Load::uses(void) const +{ + std::set res; + + if (auto obj = object()) + { + res.insert(obj); + } + + return res; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/MaxPool2D.test.cpp b/compiler/coco/core/src/IR/MaxPool2D.test.cpp new file mode 100644 index 000000000..864edddb3 --- /dev/null +++ b/compiler/coco/core/src/IR/MaxPool2D.test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsMaxPool2D : public coco::Op::Visitor +{ + bool visit(const coco::MaxPool2D *) override { return true; } +}; + +class MaxPool2DTest : public ::testing::Test +{ +public: + MaxPool2DTest() + { + // DO NOTHING + } + +protected: + coco::MaxPool2D *allocate(void) + { + auto op = new coco::MaxPool2D; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(MaxPool2DTest, initialization) +{ + auto op = allocate(); + + coco::MaxPool2D *mutable_ptr = op; + const coco::MaxPool2D *immutable_ptr = op; + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + // arg() should be nullptr on construction + ASSERT_EQ(immutable_ptr->arg(), nullptr); + + // window() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->window(), nullptr); + ASSERT_EQ(mutable_ptr->window(), immutable_ptr->window()); + + // stride() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->stride(), nullptr); + ASSERT_EQ(mutable_ptr->stride(), immutable_ptr->stride()); + + // pad() SHOULD return a valid pointer + ASSERT_NE(mutable_ptr->pad(), nullptr); + ASSERT_EQ(mutable_ptr->pad(), immutable_ptr->pad()); +} + +TEST_F(MaxPool2DTest, asMaxPool2D) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asMaxPool2D(), op); + ASSERT_EQ(mutable_base->asMaxPool2D(), immutable_base->asMaxPool2D()); +} + +TEST_F(MaxPool2DTest, accept) +{ + // Test 'MaxPool2D' class + auto op = allocate(); + + coco::MaxPool2D *mutable_ptr = op; + const coco::MaxPool2D *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsMaxPool2D{})); + ASSERT_TRUE(immutable_ptr->accept(IsMaxPool2D{})); +} diff --git a/compiler/coco/core/src/IR/Module.cpp b/compiler/coco/core/src/IR/Module.cpp new file mode 100644 index 000000000..0b65ceedc --- /dev/null +++ b/compiler/coco/core/src/IR/Module.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Module.h" + +#include + +using stdex::make_unique; + +namespace +{ + +struct EntityManagerImpl final : public coco::EntityManager +{ +public: + std::unique_ptr _bag; + +public: + coco::BagManager *bag(void) override { return _bag.get(); } + const coco::BagManager *bag(void) const override { return _bag.get(); } + +public: + std::unique_ptr _object; + +public: + coco::ObjectManager *object(void) override { return _object.get(); } + const coco::ObjectManager *object(void) const override { return _object.get(); } + +public: + std::unique_ptr _op; + +public: + coco::OpManager *op(void) override { return _op.get(); } + const coco::OpManager *op(void) const override { return _op.get(); } + +public: + coco::InstrManager *instr(void) override { return _instr.get(); } + const coco::InstrManager *instr(void) const override { return _instr.get(); } + +public: + coco::BlockManager *block(void) override { return _block.get(); } + const coco::BlockManager *block(void) const override { return _block.get(); } + +public: + std::unique_ptr _input; + +public: + coco::InputManager *input(void) override { return _input.get(); } + const coco::InputManager *input(void) const override { return _input.get(); } + +public: + std::unique_ptr _output; + +public: + coco::OutputManager *output(void) override { return _output.get(); } + const coco::OutputManager *output(void) const override { return _output.get(); } + +public: + // WARN Do NOT change the order of these fields: _block -> _instr + // + // Note that each instruction may have a reference to a block, and + // the destructor of Instr accesses this 'block' reference. + // + // Thus, Instr entities SHOULD BE destructed before Block entities are destructed. + std::unique_ptr _block; + std::unique_ptr _instr; +}; + +} // namespace + +namespace +{ + +class ModuleImpl final : public coco::Module +{ +public: + coco::EntityManager *entity(void) override { return _entity.get(); } + const coco::EntityManager *entity(void) const override { return _entity.get(); } + +public: + std::unique_ptr _block; + +public: + coco::BlockList *block(void) override { return _block.get(); } + const coco::BlockList *block(void) const override { return _block.get(); } + +public: + std::unique_ptr _input; + +public: + coco::InputList *input(void) override { return _input.get(); } + const coco::InputList *input(void) const override { return _input.get(); } + +public: + std::unique_ptr _output; + +public: + coco::OutputList *output(void) override { return _output.get(); } + const coco::OutputList *output(void) const override { return _output.get(); } + +public: + // WARN _entity SHOULD BE declared after _block in order to allow each Block(s) to detach itself. + // + // If not, Block is destructed after its corresponding BlockList is destructed, which results + // in invalid memory access during the update on BlockList (inside Block's destructor). + std::unique_ptr _entity; +}; + +} // namespace + +namespace coco +{ + +std::unique_ptr Module::create(void) +{ + auto m = make_unique<::ModuleImpl>(); + + auto mgr = make_unique<::EntityManagerImpl>(); + { + mgr->_bag = make_unique(m.get()); + mgr->_object = make_unique(m.get()); + mgr->_op = make_unique(m.get()); + mgr->_instr = make_unique(m.get()); + mgr->_block = make_unique(m.get()); + mgr->_input = make_unique(m.get()); + mgr->_output = make_unique(m.get()); + } + m->_entity = std::move(mgr); + + m->_block = make_unique(m.get()); + m->_input = make_unique(); + m->_output = make_unique(); + + return std::move(m); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Module.test.cpp b/compiler/coco/core/src/IR/Module.test.cpp new file mode 100644 index 000000000..b55ceacb8 --- /dev/null +++ b/compiler/coco/core/src/IR/Module.test.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Module.h" + +#include + +TEST(IR_MODULE, create) +{ + auto m = coco::Module::create(); + + ASSERT_NE(m.get(), nullptr); + + coco::Module *mutable_m = m.get(); + const coco::Module *immutable_m = m.get(); + + ASSERT_NE(mutable_m->entity(), nullptr); + ASSERT_NE(immutable_m->entity(), nullptr); + + ASSERT_NE(mutable_m->entity()->bag(), nullptr); + ASSERT_EQ(immutable_m->entity()->bag(), mutable_m->entity()->bag()); + + ASSERT_NE(mutable_m->entity()->object(), nullptr); + ASSERT_EQ(immutable_m->entity()->object(), mutable_m->entity()->object()); + + ASSERT_NE(mutable_m->entity()->op(), nullptr); + ASSERT_EQ(immutable_m->entity()->op(), mutable_m->entity()->op()); + + ASSERT_NE(mutable_m->entity()->instr(), nullptr); + ASSERT_EQ(immutable_m->entity()->instr(), mutable_m->entity()->instr()); + + ASSERT_NE(mutable_m->entity()->block(), nullptr); + ASSERT_EQ(immutable_m->entity()->block(), mutable_m->entity()->block()); + + ASSERT_NE(mutable_m->entity()->input(), nullptr); + ASSERT_EQ(immutable_m->entity()->input(), mutable_m->entity()->input()); + + ASSERT_NE(mutable_m->entity()->output(), nullptr); + ASSERT_EQ(immutable_m->entity()->output(), mutable_m->entity()->output()); + + ASSERT_NE(mutable_m->block(), nullptr); + ASSERT_EQ(immutable_m->block(), mutable_m->block()); + + ASSERT_NE(mutable_m->input(), nullptr); + ASSERT_EQ(immutable_m->input(), mutable_m->input()); + + ASSERT_NE(mutable_m->output(), nullptr); + ASSERT_EQ(immutable_m->output(), mutable_m->output()); +} + +TEST(IR_MODULE, append_two_blocks) +{ + auto m = coco::Module::create(); + + auto blk_1 = m->entity()->block()->create(); + m->block()->append(blk_1); + + auto blk_2 = m->entity()->block()->create(); + m->block()->append(blk_2); + + ASSERT_EQ(m->block()->head(), blk_1); + ASSERT_EQ(m->block()->tail(), blk_2); + + ASSERT_EQ(blk_1->prev(), nullptr); + ASSERT_EQ(blk_1->next(), blk_2); + + ASSERT_EQ(blk_2->prev(), blk_1); + ASSERT_EQ(blk_2->next(), nullptr); + + ASSERT_EQ(blk_1->index().value(), 0); + ASSERT_EQ(blk_2->index().value(), 1); +} + +TEST(IR_MODULE, append_two_instrs) +{ + auto m = coco::Module::create(); + + auto blk = m->entity()->block()->create(); + auto ins_1 = m->entity()->instr()->create(); + auto ins_2 = m->entity()->instr()->create(); + + blk->instr()->append(ins_1); + blk->instr()->append(ins_2); + + ASSERT_EQ(blk->instr()->head(), ins_1); + ASSERT_EQ(blk->instr()->tail(), ins_2); + + ASSERT_EQ(ins_1->parent(), blk); + ASSERT_EQ(ins_1->prev(), nullptr); + ASSERT_EQ(ins_1->next(), ins_2); + + ASSERT_EQ(ins_2->parent(), blk); + ASSERT_EQ(ins_2->prev(), ins_1); + ASSERT_EQ(ins_2->next(), nullptr); + + ASSERT_EQ(ins_1->index().value(), 0); + ASSERT_EQ(ins_2->index().value(), 1); +} + +TEST(IR_MODULE, iterate_constant_block) +{ + auto m = coco::Module::create(); + auto blk = m->entity()->block()->create(); + auto ins_1 = m->entity()->instr()->create(); + auto ins_2 = m->entity()->instr()->create(); + + blk->instr()->append(ins_1); + blk->instr()->append(ins_2); + + const coco::Block *immutable_blk = blk; + + ASSERT_EQ(immutable_blk->instr()->head(), ins_1); + ASSERT_EQ(immutable_blk->instr()->head()->next(), ins_2); +} + +TEST(IR_MODULE, input_as_output) +{ + // Some NN frameworks allows users to use a network input as its output. + // + // For example, let us consider the following Caffe network + // + // name: "example" + // layer { + // name: "l" + // type: "Input" + // top: "data" + // input_param { shape: { dim: 1 dim: 1 dim: 3 dim: 3 } } + // } + // + // "data" blob is the input of this network, and it is also the output of this network. + const nncc::core::ADT::tensor::Shape shape{1, 1, 3, 3}; + + auto m = coco::Module::create(); + auto bag = m->entity()->bag()->create(9); + + auto input = m->entity()->input()->create(shape); + auto output = m->entity()->output()->create(shape); + + input->name("data"); + input->bag(bag); + + output->name("data"); + output->bag(bag); + + ASSERT_TRUE(bag->isInput()); + ASSERT_TRUE(bag->isOutput()); + + output->bag(nullptr); + + ASSERT_TRUE(bag->isInput()); + ASSERT_FALSE(bag->isOutput()); +} + +/** + * This test ensures that IR entities allocated via EntityManager have a correct module link + */ +TEST(IR_Module, create_entites) +{ + using namespace coco; + using namespace nncc::core::ADT; + + auto m = Module::create(); + auto entity = m->entity(); + + ASSERT_EQ(entity->bag()->create(1)->module(), m.get()); + ASSERT_EQ(entity->object()->create()->module(), m.get()); + ASSERT_EQ(entity->object()->create()->module(), m.get()); +#define OP(Name) ASSERT_EQ(entity->op()->create()->module(), m.get()); +#include "coco/IR/Op.lst" +#undef OP +#define INSTR(Name) \ + { \ + auto ins = entity->instr()->create(); \ + ASSERT_EQ(ins->module(), m.get()); \ + ASSERT_TRUE(coco::isa(ins)); \ + ASSERT_NE(coco::safe_cast(ins), nullptr); \ + } +#include "coco/IR/Instr.lst" +#undef INSTR + ASSERT_EQ(entity->block()->create()->module(), m.get()); + ASSERT_EQ(entity->input()->create(tensor::Shape{1})->module(), m.get()); + ASSERT_EQ(entity->output()->create(tensor::Shape{1})->module(), m.get()); +} diff --git a/compiler/coco/core/src/IR/Object.cpp b/compiler/coco/core/src/IR/Object.cpp new file mode 100644 index 000000000..6a51a61a3 --- /dev/null +++ b/compiler/coco/core/src/IR/Object.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Object.h" +#include "coco/IR/Def.h" +#include "coco/IR/Use.h" + +#include +#include + +namespace coco +{ + +Object::Object() +{ + // Register self to Dep + _dep.object(this); +} + +Def *Object::def(void) const { return _def; } + +void Object::def(Def *d) +{ + // This assert enforces users to explicitly reset def before update. + // + // Let's consider an object o with def d0. + // + // The following code is allowed: + // o->def(nullptr); + // o->def(d1); + // + // However, the following code is not allowed: + // o->def(d1); + // + assert((_def == nullptr) || (d == nullptr)); + _def = d; +} + +const UseSet *Object::uses(void) const { return &_uses; } +UseSet *Object::mutable_uses(void) { return &_uses; } + +Object::Producer *producer(const Object *obj) +{ + if (auto d = obj->def()) + { + return d->producer(); + } + + return nullptr; +} + +Object::ConsumerSet consumers(const Object *obj) +{ + Object::ConsumerSet res; + + for (const auto &use : *(obj->uses())) + { + if (auto consumer = use->consumer()) + { + res.insert(consumer); + } + } + + return res; +} + +/** + * Casting Helpers + * + * TODO Use Macro to reduce code duplication + */ +template <> bool isa(const Object *o) { return o->asFeature() != nullptr; } +template <> bool isa(const Object *o) { return o->asKernel() != nullptr; } + +template <> FeatureObject *cast(Object *o) +{ + assert(o != nullptr); + auto res = o->asFeature(); + assert(res != nullptr); + return res; +} + +template <> KernelObject *cast(Object *o) +{ + assert(o != nullptr); + auto res = o->asKernel(); + assert(res != nullptr); + return res; +} + +template <> FeatureObject *safe_cast(Object *o) +{ + // NOTE o may be nullptr + return (o == nullptr) ? nullptr : o->asFeature(); +} + +template <> KernelObject *safe_cast(Object *o) +{ + // NOTE o may be nullptr + return (o == nullptr) ? nullptr : o->asKernel(); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Object.test.cpp b/compiler/coco/core/src/IR/Object.test.cpp new file mode 100644 index 000000000..2a2e4db23 --- /dev/null +++ b/compiler/coco/core/src/IR/Object.test.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Object.h" +#include "coco/IR/BagManager.h" + +#include + +#include + +namespace +{ +class ObjectTest : public ::testing::Test +{ +protected: + coco::BagManager bag_mgr; +}; +} // namespace + +namespace +{ +namespace mock +{ +struct Object : public coco::Object +{ +public: + virtual ~Object() = default; +}; +} // namespace mock +} // namespace + +TEST_F(ObjectTest, ctor) +{ + ::mock::Object obj; + + // Newly created object should not have a backing bag + ASSERT_EQ(obj.bag(), nullptr); + + // Newly created object should not have def and uses + ASSERT_EQ(obj.def(), nullptr); + ASSERT_TRUE(obj.uses()->empty()); +} + +TEST_F(ObjectTest, bag_update) +{ + // Prepare bag + auto bag = bag_mgr.create(1); + + // Test 'Object' class through a mock-up object + ::mock::Object obj; + + obj.bag(bag); + + // 'bag(Bag *)' should affect the return of 'bag(void)' + ASSERT_EQ(obj.bag(), bag); + + // User SHOULD be able to access dependent objects through 'bag' + { + auto deps = coco::dependent_objects(bag); + ASSERT_EQ(deps.size(), 1); + ASSERT_EQ(deps.count(&obj), 1); + } + + // Unlink Object-Bag relation + obj.bag(nullptr); + + ASSERT_EQ(obj.bag(), nullptr); + + { + auto deps = coco::dependent_objects(bag); + ASSERT_EQ(deps.size(), 0); + } +} + +TEST_F(ObjectTest, destructor) +{ + auto bag = bag_mgr.create(1); + + // Destruct Object after proper initialization + { + ::mock::Object obj; + + obj.bag(bag); + } + + // Object SHOULD be unlinked from Bag on destruction + { + auto deps = coco::dependent_objects(bag); + ASSERT_EQ(deps.size(), 0); + } +} + +TEST_F(ObjectTest, safe_cast) +{ + ASSERT_EQ(coco::safe_cast(nullptr), nullptr); + ASSERT_EQ(coco::safe_cast(nullptr), nullptr); +} diff --git a/compiler/coco/core/src/IR/ObjectManager.cpp b/compiler/coco/core/src/IR/ObjectManager.cpp new file mode 100644 index 000000000..1b7215a04 --- /dev/null +++ b/compiler/coco/core/src/IR/ObjectManager.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/ObjectManager.h" + +#include "coco/IR/FeatureObject.h" +#include "coco/IR/KernelObject.h" + +#include + +#include + +using stdex::make_unique; + +namespace coco +{ + +template <> FeatureObject *ObjectManager::create(void) +{ + auto feature = make_unique(); + modulize(feature.get()); + return take(std::move(feature)); +} + +template <> KernelObject *ObjectManager::create(void) +{ + auto kernel = make_unique(); + modulize(kernel.get()); + return take(std::move(kernel)); +} + +void ObjectManager::destroy(Object *o) +{ + assert(o->def() == nullptr); + assert(o->uses()->size() == 0); + release(o); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/ObjectManager.test.cpp b/compiler/coco/core/src/IR/ObjectManager.test.cpp new file mode 100644 index 000000000..781775f25 --- /dev/null +++ b/compiler/coco/core/src/IR/ObjectManager.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/ObjectManager.h" +#include "coco/IR/BagManager.h" + +#include "coco/IR/FeatureObject.h" +#include "coco/IR/KernelObject.h" + +#include + +TEST(IR_OBJECT_MANAGER, create_feature_with_template) +{ + coco::ObjectManager mgr; + + auto feature = mgr.create(); + + ASSERT_EQ(feature->layout(), nullptr); +} + +TEST(IR_OBJECT_MANAGER, create_kernel_with_template) +{ + coco::ObjectManager mgr; + + auto kernel = mgr.create(); + + ASSERT_EQ(kernel->layout(), nullptr); +} + +TEST(IR_OBJECT_MANAGER, destroy) +{ + coco::BagManager bag_mgr; + coco::ObjectManager obj_mgr; + + auto bag = bag_mgr.create(3); + auto feature = obj_mgr.create(); + + feature->bag(bag); + + obj_mgr.destroy(feature); + + // Object SHOULD BE unlinked from its dependent bag on destruction + ASSERT_EQ(bag->deps()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/Op.cpp b/compiler/coco/core/src/IR/Op.cpp new file mode 100644 index 000000000..d3808a9d6 --- /dev/null +++ b/compiler/coco/core/src/IR/Op.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Op.h" +#include "coco/IR/Step.h" +#include "coco/IR/Part.h" + +#include + +namespace coco +{ +Op::~Op() +{ + // NOTE Op SHOULD NOT be referred by an instruction to be destructed + assert(_step == nullptr); +} + +Instr *Op::parent(void) const +{ + // Get the parent instruction specified by _step for root nodes + if (_step) + { + // Op SHOULD BE a root node + assert(_part == nullptr); + assert(_step->instr() != nullptr); + return _step->instr(); + } + + // Get the parent instruction of its parent Op for non-root nodes + if (_part) + { + assert(_part->parent() != nullptr); + return _part->parent()->parent(); + } + + return nullptr; +} + +Op *Op::up(void) const +{ + if (_part) + { + assert(_part->parent() != nullptr); + return _part->parent(); + } + return nullptr; +} + +// +// UnaryOP trait +// +UnaryOp::UnaryOp() : _arg{this} +{ + // DO NOTHING +} + +uint32_t UnaryOp::arity(void) const +{ + // There is only one argument + return 1; +} + +Op *UnaryOp::arg(DBGARG(uint32_t, n)) const +{ + assert(n < 1); + return arg(); +} + +std::set UnaryOp::uses(void) const +{ + std::set res; + + if (auto ifm = arg()) + { + for (auto obj : ifm->uses()) + { + res.insert(obj); + } + } + + return res; +} + +// +// BinaryOp trait +// +BinaryOp::BinaryOp() : _left{this}, _right{this} +{ + // DO NOTHING +} + +uint32_t BinaryOp::arity(void) const +{ + // There are two arguments + return 2; +} + +Op *BinaryOp::arg(uint32_t n) const +{ + assert(n < arity()); + + return (n == 0) ? left() : right(); +} + +std::set BinaryOp::uses(void) const +{ + std::set res; + + if (auto l = left()) + { + for (auto obj : l->uses()) + { + res.insert(obj); + } + } + + if (auto r = right()) + { + for (auto obj : r->uses()) + { + res.insert(obj); + } + } + + return res; +} + +// +// Additional Helpers +// +Op *root(Op *cur) +{ + while (cur->up()) + { + cur = cur->up(); + } + return cur; +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/OpManager.cpp b/compiler/coco/core/src/IR/OpManager.cpp new file mode 100644 index 000000000..c87b704fe --- /dev/null +++ b/compiler/coco/core/src/IR/OpManager.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/OpManager.h" + +#include + +#include +#include +#include + +using stdex::make_unique; + +namespace coco +{ + +OpManager::~OpManager() +{ + std::set roots; + + for (uint32_t n = 0; n < size(); ++n) + { + auto op = at(n); + + if (op->up() != nullptr) + { + continue; + } + + roots.insert(op); + } + + for (const auto &op : roots) + { + destroy_all(op); + } +} + +// +// Each Op class SHOULD be default constructible +// +#define OP(Name) \ + template <> Name *OpManager::create(void) \ + { \ + auto op = make_unique(); \ + modulize(op.get()); \ + return take(std::move(op)); \ + } +#include "coco/IR/Op.lst" +#undef OP + +void OpManager::destroy(Op *op) +{ + assert(op->parent() == nullptr); + release(op); +} + +void OpManager::destroy_all(Op *op) +{ + assert(op->parent() == nullptr); + assert(op->up() == nullptr); + + std::queue q; + + q.emplace(op); + + while (q.size() > 0) + { + auto cur = q.front(); + q.pop(); + + // Insert child op nodes + for (uint32_t n = 0; n < cur->arity(); ++n) + { + if (auto child = cur->arg(n)) + { + q.emplace(child); + } + } + + // Destroy the current op node + destroy(cur); + } +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/OpManager.test.cpp b/compiler/coco/core/src/IR/OpManager.test.cpp new file mode 100644 index 000000000..9d463b3e4 --- /dev/null +++ b/compiler/coco/core/src/IR/OpManager.test.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/OpManager.h" + +#include + +namespace +{ + +class OpManagerTest : public ::testing::Test +{ +protected: + coco::OpManager mgr; +}; + +} // namespace + +TEST(IR_OP_MANAGER, create_Conv2D) +{ + coco::OpManager mgr; + + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST(IR_OP_MANAGER, create_AvgPool2D) +{ + coco::OpManager mgr; + + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, ReLU) +{ + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, ReLU6) +{ + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, Sqrt) +{ + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, Sub) +{ + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, Div) +{ + auto obj = mgr.create(); + + ASSERT_NE(obj, nullptr); +} + +TEST_F(OpManagerTest, PadF) +{ + auto op = mgr.create(); + ASSERT_NE(op, nullptr); + mgr.destroy(op); +} + +TEST_F(OpManagerTest, destroy) +{ + auto op = mgr.create(); + mgr.destroy(op); + ASSERT_EQ(mgr.size(), 0); +} + +TEST_F(OpManagerTest, destroy_all) +{ + // Create a Op tree + auto load_op = mgr.create(); + auto conv_op = mgr.create(); + + conv_op->arg(load_op); + + mgr.destroy_all(conv_op); + + ASSERT_EQ(mgr.size(), 0); +} + +TEST_F(OpManagerTest, destroy_all_partial_tree) +{ + // Create a (partial) Op tree + auto conv_op = mgr.create(); + + mgr.destroy_all(conv_op); + + ASSERT_EQ(mgr.size(), 0); +} diff --git a/compiler/coco/core/src/IR/Ops.cpp b/compiler/coco/core/src/IR/Ops.cpp new file mode 100644 index 000000000..1c1ef5d28 --- /dev/null +++ b/compiler/coco/core/src/IR/Ops.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +namespace coco +{ + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Ops.test.cpp b/compiler/coco/core/src/IR/Ops.test.cpp new file mode 100644 index 000000000..ae979b2bf --- /dev/null +++ b/compiler/coco/core/src/IR/Ops.test.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" +#include "coco/IR/ObjectManager.h" +#include "coco/IR/OpManager.h" + +#include +#include + +#include + +#include + +using stdex::make_unique; + +/** + * Section: Add Op + */ +namespace +{ + +class AddTest : public ::testing::Test +{ +public: + AddTest() + { + // DO NOTHING + } + +protected: + coco::Add *allocate(void) + { + auto op = new coco::Add; + _allocated.emplace_back(op); + return op; + } + +protected: + coco::ObjectManager obj_mgr; + +private: + std::vector> _allocated; +}; + +} // namespace + +TEST_F(AddTest, constructor) +{ + auto op = allocate(); + + ASSERT_EQ(op->left(), nullptr); + ASSERT_EQ(op->right(), nullptr); +} + +/** + * Section: Mul Op + */ +TEST(MulTest, constructor) +{ + auto op = make_unique(); + + ASSERT_EQ(op->left(), nullptr); + ASSERT_EQ(op->right(), nullptr); +} + +/** + * Section: Div Op + */ +TEST(DivTest, constructor) +{ + auto op = make_unique(); + + ASSERT_EQ(op->left(), nullptr); + ASSERT_EQ(op->right(), nullptr); +} + +/** + * Section: Op Helpers + */ +namespace +{ + +class OpHelperTest : public ::testing::Test +{ +public: + OpHelperTest() + { + // DO NOTHING + } + +protected: + template Op *allocate(void) { return op_mgr.create(); } + +protected: + coco::ObjectManager obj_mgr; + +private: + coco::OpManager op_mgr; +}; + +} // namespace + +TEST_F(OpHelperTest, root) +{ + auto load = allocate(); + + ASSERT_EQ(root(load), load); + + auto avgpool = allocate(); + + avgpool->arg(load); + + ASSERT_EQ(root(load), avgpool); + ASSERT_EQ(root(avgpool), avgpool); +} diff --git a/compiler/coco/core/src/IR/Output.cpp b/compiler/coco/core/src/IR/Output.cpp new file mode 100644 index 000000000..7b6d1870b --- /dev/null +++ b/compiler/coco/core/src/IR/Output.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Output.h" + +#include + +namespace coco +{ + +Output::Output(const nncc::core::ADT::tensor::Shape &shape) : Arg{shape} +{ + // DO NOTHING +} + +void Output::onTake(Bag *bag) +{ + assert(bag->output() == nullptr); + bag->output(this); +} + +void Output::onRelease(Bag *bag) +{ + assert(bag->output() == this); + bag->output(nullptr); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Output.test.cpp b/compiler/coco/core/src/IR/Output.test.cpp new file mode 100644 index 000000000..715a83875 --- /dev/null +++ b/compiler/coco/core/src/IR/Output.test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Output.h" +#include "coco/IR/BagManager.h" + +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +TEST(IR_OUTPUT, ctor_should_set_shape) +{ + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Output output{shape}; + + ASSERT_EQ(output.shape(), shape); +} + +TEST(IR_OUTPUT, bag_update) +{ + // Create a bag for test + coco::BagManager bag_mgr; + + auto bag = bag_mgr.create(9); + + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Output output{shape}; + + output.bag(bag); + ASSERT_EQ(output.bag(), bag); + + // bag(...) method SHOULD update 'bag' type + ASSERT_TRUE(bag->isOutput()); + + output.bag(nullptr); + + // bag(nullptr) SHOULD revert 'bag' type + ASSERT_FALSE(bag->isOutput()); +} + +TEST(IR_OUTPUT, name_update) +{ + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + coco::Output output{shape}; + + output.name("softmax"); + ASSERT_EQ(output.name(), "softmax"); +} + +TEST(IR_OUTPUT, at) +{ + const Shape shape{1, 3, 3, 1}; + coco::Output input{shape}; + + coco::Output *mutable_ptr = &input; + const coco::Output *immutable_ptr = &input; + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + mutable_ptr->at(e.current()) = coco::ElemID{16}; + } + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + ASSERT_EQ(immutable_ptr->at(e.current()).value(), 16); + } +} diff --git a/compiler/coco/core/src/IR/OutputManager.cpp b/compiler/coco/core/src/IR/OutputManager.cpp new file mode 100644 index 000000000..86b9580ac --- /dev/null +++ b/compiler/coco/core/src/IR/OutputManager.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/OutputManager.h" + +#include + +namespace coco +{ + +Output *OutputManager::create(const nncc::core::ADT::tensor::Shape &shape) +{ + auto output = stdex::make_unique(shape); + modulize(output.get()); + return take(std::move(output)); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/OutputManager.test.cpp b/compiler/coco/core/src/IR/OutputManager.test.cpp new file mode 100644 index 000000000..80b38b42c --- /dev/null +++ b/compiler/coco/core/src/IR/OutputManager.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/OutputManager.h" + +#include + +TEST(IR_OUTPUT_MANAGER, make) +{ + coco::OutputManager mgr; + + const nncc::core::ADT::tensor::Shape shape{1, 3, 3, 1}; + auto output = mgr.create(shape); + + ASSERT_EQ(output->shape(), shape); +} diff --git a/compiler/coco/core/src/IR/PadF.test.cpp b/compiler/coco/core/src/IR/PadF.test.cpp new file mode 100644 index 000000000..b443d86fb --- /dev/null +++ b/compiler/coco/core/src/IR/PadF.test.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsPadF : public coco::Op::Visitor +{ + bool visit(const coco::PadF *) override { return true; } +}; + +class PadFTest : public ::testing::Test +{ +public: + PadFTest() + { + // DO NOTHING + } + +protected: + coco::PadF *allocate(void) + { + auto op = new coco::PadF; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(PadFTest, initialization) +{ + auto op = allocate(); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + // arg() should be nullptr on construction + ASSERT_EQ(op->arg(), nullptr); + + // pad() should be a valid + ASSERT_NE(op->pad(), nullptr); +} + +TEST_F(PadFTest, asPadF) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asPadF(), op); + ASSERT_EQ(mutable_base->asPadF(), immutable_base->asPadF()); +} + +TEST_F(PadFTest, accept) +{ + // Test 'PadF' class + auto op = allocate(); + + coco::PadF *mutable_ptr = op; + const coco::PadF *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsPadF{})); + ASSERT_TRUE(immutable_ptr->accept(IsPadF{})); +} diff --git a/compiler/coco/core/src/IR/Padding2D.cpp b/compiler/coco/core/src/IR/Padding2D.cpp new file mode 100644 index 000000000..8cdc42638 --- /dev/null +++ b/compiler/coco/core/src/IR/Padding2D.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Padding2D.h" + +namespace coco +{ + +Padding2D &Padding2D::top(uint32_t value) +{ + _top = value; + return (*this); +} + +Padding2D &Padding2D::bottom(uint32_t value) +{ + _bottom = value; + return (*this); +} + +Padding2D &Padding2D::left(uint32_t value) +{ + _left = value; + return (*this); +} + +Padding2D &Padding2D::right(uint32_t value) +{ + _right = value; + return (*this); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Padding2D.test.cpp b/compiler/coco/core/src/IR/Padding2D.test.cpp new file mode 100644 index 000000000..292ce7d17 --- /dev/null +++ b/compiler/coco/core/src/IR/Padding2D.test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Padding2D.h" + +#include + +TEST(IR_PADDING, default_constructor) +{ + coco::Padding2D pad; + + ASSERT_EQ(pad.top(), 0); + ASSERT_EQ(pad.bottom(), 0); + ASSERT_EQ(pad.left(), 0); + ASSERT_EQ(pad.right(), 0); +} + +TEST(IR_PADDING, explicit_constructor_4) +{ + coco::Padding2D pad{1, 2, 3, 4}; + + ASSERT_EQ(pad.top(), 1); + ASSERT_EQ(pad.bottom(), 2); + ASSERT_EQ(pad.left(), 3); + ASSERT_EQ(pad.right(), 4); +} + +TEST(IR_PADDING, update) +{ + coco::Padding2D pad; + + pad.top(1).bottom(2).left(3).right(4); + + ASSERT_EQ(pad.top(), 1); + ASSERT_EQ(pad.bottom(), 2); + ASSERT_EQ(pad.left(), 3); + ASSERT_EQ(pad.right(), 4); +} diff --git a/compiler/coco/core/src/IR/Part.cpp b/compiler/coco/core/src/IR/Part.cpp new file mode 100644 index 000000000..bf68c1feb --- /dev/null +++ b/compiler/coco/core/src/IR/Part.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Part.h" +#include "coco/IR/Op.h" + +#include + +namespace coco +{ + +void Part::child(Op *c) +{ + if (_child != nullptr) + { + assert(_child->_part == this); + _child->_part = nullptr; + _child = nullptr; + } + + assert(_child == nullptr); + + if (c != nullptr) + { + assert(c->_part == nullptr); + assert(c->_step == nullptr); + _child = c; + _child->_part = this; + } +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Part.test.cpp b/compiler/coco/core/src/IR/Part.test.cpp new file mode 100644 index 000000000..87e0e1516 --- /dev/null +++ b/compiler/coco/core/src/IR/Part.test.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Part.h" +#include "coco/IR/Op.h" + +#include + +#include + +using stdex::make_unique; + +namespace +{ +namespace mock +{ + +// TODO Inherit UnaryOp instead of Op +struct Op final : public coco::Op +{ +public: + Op() : _arg{this} + { + // DO NOTHING + } + +public: + uint32_t arity(void) const final { return 1; } + coco::Op *arg(uint32_t n) const final { return arg(); } + + std::set uses() const override { throw std::runtime_error{"Not supported"}; } + +public: + ::coco::Op *arg(void) const { return _arg.child(); } + void arg(::coco::Op *child) { _arg.child(child); } + +private: + coco::Part _arg; +}; + +} // namespace mock +} // namespace + +TEST(PartTest, destructor) +{ + auto parent = make_unique<::mock::Op>(); + auto child = make_unique<::mock::Op>(); + + parent->arg(child.get()); + ASSERT_EQ(parent->arg(), child.get()); + ASSERT_EQ(child->up(), parent.get()); + + parent.reset(); + + // NOTE parent SHOULD unlink itself from child on destruction + ASSERT_EQ(child->up(), nullptr); +} diff --git a/compiler/coco/core/src/IR/Producer.mock.h b/compiler/coco/core/src/IR/Producer.mock.h new file mode 100644 index 000000000..ffc343ee8 --- /dev/null +++ b/compiler/coco/core/src/IR/Producer.mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_PRODUCER_MOCK_H__ +#define __COCO_IR_PRODUCER_MOCK_H__ + +#include "coco/IR/Object.h" + +namespace +{ +namespace mock +{ +struct Producer final : public coco::Object::Producer +{ + coco::Instr *loc(void) override { return nullptr; } +}; +} // namespace mock +} // namespace + +#endif // __COCO_IR_PRODUCER_MOCK_H__ diff --git a/compiler/coco/core/src/IR/ReLU.test.cpp b/compiler/coco/core/src/IR/ReLU.test.cpp new file mode 100644 index 000000000..22ef1730e --- /dev/null +++ b/compiler/coco/core/src/IR/ReLU.test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsReLU : public coco::Op::Visitor +{ + bool visit(const coco::ReLU *) override { return true; } +}; + +class ReLUTest : public ::testing::Test +{ +public: + ReLUTest() + { + // DO NOTHING + } + +protected: + coco::ReLU *allocate(void) + { + auto op = new coco::ReLU; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(ReLUTest, initialization) +{ + auto op = allocate(); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + ASSERT_EQ(op->arg(), nullptr); +} + +TEST_F(ReLUTest, asReLU) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asReLU(), op); + ASSERT_EQ(mutable_base->asReLU(), immutable_base->asReLU()); +} + +TEST_F(ReLUTest, accept) +{ + // Test 'ReLU' class + auto op = allocate(); + + coco::ReLU *mutable_ptr = op; + const coco::ReLU *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsReLU{})); + ASSERT_TRUE(immutable_ptr->accept(IsReLU{})); +} diff --git a/compiler/coco/core/src/IR/ReLU6.test.cpp b/compiler/coco/core/src/IR/ReLU6.test.cpp new file mode 100644 index 000000000..dd148254f --- /dev/null +++ b/compiler/coco/core/src/IR/ReLU6.test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsReLU6 : public coco::Op::Visitor +{ + bool visit(const coco::ReLU6 *) override { return true; } +}; + +class ReLU6Test : public ::testing::Test +{ +public: + ReLU6Test() + { + // DO NOTHING + } + +protected: + coco::ReLU6 *allocate(void) + { + auto op = new coco::ReLU6; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(ReLU6Test, initialization) +{ + auto op = allocate(); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + ASSERT_EQ(op->arg(), nullptr); +} + +TEST_F(ReLU6Test, asReLU6) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asReLU6(), op); + ASSERT_EQ(mutable_base->asReLU6(), immutable_base->asReLU6()); +} + +TEST_F(ReLU6Test, accept) +{ + // Test 'ReLU6' class + auto op = allocate(); + + coco::ReLU6 *mutable_ptr = op; + const coco::ReLU6 *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsReLU6{})); + ASSERT_TRUE(immutable_ptr->accept(IsReLU6{})); +} diff --git a/compiler/coco/core/src/IR/Read.cpp b/compiler/coco/core/src/IR/Read.cpp new file mode 100644 index 000000000..ea01cce1d --- /dev/null +++ b/compiler/coco/core/src/IR/Read.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Read.h" + +#include + +namespace coco +{ + +Read::~Read() +{ + // Unlink self from Bag if there is a linked bag + bag(nullptr); +} + +void Read::bag(Bag *bag) +{ + if (_bag) + { + _bag->mutable_reads()->erase(this); + _bag = nullptr; + } + + assert(_bag == nullptr); + + if (bag) + { + _bag = bag; + _bag->mutable_reads()->insert(this); + } + + assert(_bag == bag); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Read.test.cpp b/compiler/coco/core/src/IR/Read.test.cpp new file mode 100644 index 000000000..7c36820a6 --- /dev/null +++ b/compiler/coco/core/src/IR/Read.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Read.h" +#include "coco/IR/BagManager.h" + +#include "Reader.mock.h" + +#include + +namespace +{ +class ReadTest : public ::testing::Test +{ +protected: + coco::BagManager bag_mgr; +}; +} // namespace + +TEST_F(ReadTest, constructor) +{ + // TODO Rename 'read' as 'reader' + ::mock::Reader read; + + // TODO Rename 'slot' + coco::Read slot{&read}; + + ASSERT_EQ(slot.bag(), nullptr); +} + +TEST_F(ReadTest, value) +{ + // TODO Rename 'read' as 'reader' + ::mock::Reader read; + + // TODO Rename 'slot' + coco::Read slot{&read}; + + auto bag = bag_mgr.create(16); + + slot.bag(bag); + + ASSERT_EQ(slot.bag(), bag); + + ASSERT_EQ(bag->reads()->size(), 1); + ASSERT_NE(bag->reads()->find(&slot), bag->reads()->end()); + + slot.bag(nullptr); + + ASSERT_EQ(slot.bag(), nullptr); + + ASSERT_EQ(bag->reads()->size(), 0); +} + +TEST_F(ReadTest, unlink_on_destruction) +{ + // TODO Rename 'read' as 'reader' + ::mock::Reader reader; + + auto bag = bag_mgr.create(1); + + { + coco::Read read{&reader}; + read.bag(bag); + } + + ASSERT_EQ(bag->reads()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/Reader.mock.h b/compiler/coco/core/src/IR/Reader.mock.h new file mode 100644 index 000000000..0965abfeb --- /dev/null +++ b/compiler/coco/core/src/IR/Reader.mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_READER_MOCK_H__ +#define __COCO_IR_READER_MOCK_H__ + +#include "coco/IR/Bag.h" + +namespace +{ +namespace mock +{ +struct Reader final : public coco::Bag::Reader +{ + coco::Instr *loc(void) override { return nullptr; } +}; +} // namespace mock +} // namespace + +#endif // __COCO_IR_READER_MOCK_H__ diff --git a/compiler/coco/core/src/IR/Shuffle.cpp b/compiler/coco/core/src/IR/Shuffle.cpp new file mode 100644 index 000000000..f8007dd1b --- /dev/null +++ b/compiler/coco/core/src/IR/Shuffle.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Instrs.h" + +namespace coco +{ + +uint32_t Shuffle::size(void) const { return _content.size(); } + +std::set Shuffle::range(void) const +{ + std::set res; + + for (auto it = _content.begin(); it != _content.end(); ++it) + { + res.insert(it->first); + } + + return res; +} + +void Shuffle::insert(const ElemID &from, const ElemID &into) { _content[into] = from; } + +void Shuffle::from(Bag *b) { _from.bag(b); } +void Shuffle::into(Bag *b) { _into.bag(b); } + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Shuffle.test.cpp b/compiler/coco/core/src/IR/Shuffle.test.cpp new file mode 100644 index 000000000..f564c08c3 --- /dev/null +++ b/compiler/coco/core/src/IR/Shuffle.test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Instrs.h" +#include "coco/IR/ObjectManager.h" +#include "coco/IR/OpManager.h" + +#include + +namespace +{ +class ShuffleTest : public ::testing::Test +{ +public: + virtual ~ShuffleTest() = default; + +protected: + coco::Shuffle *allocate(void) + { + auto ins = new coco::Shuffle; + _allocated.emplace_back(ins); + return ins; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(ShuffleTest, constructor) +{ + auto ins = allocate(); + + ASSERT_EQ(ins->from(), nullptr); + ASSERT_EQ(ins->into(), nullptr); +} + +TEST_F(ShuffleTest, asShuffle) +{ + auto ins = allocate(); + + coco::Instr *mutable_ptr = ins; + const coco::Instr *immutable_ptr = ins; + + ASSERT_NE(mutable_ptr->asShuffle(), nullptr); + ASSERT_EQ(mutable_ptr->asShuffle(), immutable_ptr->asShuffle()); +} + +TEST_F(ShuffleTest, size) +{ + auto shuffle = allocate(); + + shuffle->insert(coco::ElemID{3}, coco::ElemID{2}); + shuffle->insert(coco::ElemID{3}, coco::ElemID{5}); + + ASSERT_EQ(shuffle->size(), 2); + ASSERT_EQ(shuffle->range().size(), shuffle->size()); +} + +TEST_F(ShuffleTest, range) +{ + auto shuffle = allocate(); + + shuffle->insert(coco::ElemID{3}, coco::ElemID{2}); + shuffle->insert(coco::ElemID{3}, coco::ElemID{5}); + + auto range = shuffle->range(); + + EXPECT_EQ(range.size(), 2); + EXPECT_NE(range.count(coco::ElemID{2}), 0); + EXPECT_NE(range.count(coco::ElemID{5}), 0); +} + +TEST_F(ShuffleTest, defined) +{ + auto shuffle = allocate(); + + shuffle->insert(coco::ElemID{3}, coco::ElemID{2}); + + EXPECT_TRUE(shuffle->defined(coco::ElemID{2})); + EXPECT_FALSE(shuffle->defined(coco::ElemID{3})); +} diff --git a/compiler/coco/core/src/IR/Sqrt.test.cpp b/compiler/coco/core/src/IR/Sqrt.test.cpp new file mode 100644 index 000000000..cf9b232ea --- /dev/null +++ b/compiler/coco/core/src/IR/Sqrt.test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsSqrt : public coco::Op::Visitor +{ + bool visit(const coco::Sqrt *) override { return true; } +}; + +class SqrtTest : public ::testing::Test +{ +public: + SqrtTest() + { + // DO NOTHING + } + +protected: + coco::Sqrt *allocate(void) + { + auto op = new coco::Sqrt; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(SqrtTest, initialization) +{ + auto op = allocate(); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); + + ASSERT_EQ(op->arg(), nullptr); +} + +TEST_F(SqrtTest, asSqrt) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asSqrt(), op); + ASSERT_EQ(mutable_base->asSqrt(), immutable_base->asSqrt()); +} + +TEST_F(SqrtTest, accept) +{ + // Test 'Sqrt' class + auto op = allocate(); + + coco::Sqrt *mutable_ptr = op; + const coco::Sqrt *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsSqrt{})); + ASSERT_TRUE(immutable_ptr->accept(IsSqrt{})); +} diff --git a/compiler/coco/core/src/IR/Step.cpp b/compiler/coco/core/src/IR/Step.cpp new file mode 100644 index 000000000..04400d46b --- /dev/null +++ b/compiler/coco/core/src/IR/Step.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Step.h" +#include "coco/IR/Op.h" + +#include + +namespace coco +{ + +void Step::op(Op *o) +{ + if (_op != nullptr) + { + // Unlink step from _op + assert(_op->_step == this); + _op->_step = nullptr; + + // Reset _op + _op = nullptr; + } + + assert(_op == nullptr); + + if (o) + { + // Update _op + _op = o; + + // Link step to _op + assert(_op->_step == nullptr); + _op->_step = this; + } + + assert(_op == o); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Stride2D.cpp b/compiler/coco/core/src/IR/Stride2D.cpp new file mode 100644 index 000000000..a034876ef --- /dev/null +++ b/compiler/coco/core/src/IR/Stride2D.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Stride2D.h" + +namespace coco +{ + +Stride2D &Stride2D::vertical(uint32_t value) +{ + _vertical = value; + return (*this); +} + +Stride2D &Stride2D::horizontal(uint32_t value) +{ + _horizontal = value; + return (*this); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Stride2D.test.cpp b/compiler/coco/core/src/IR/Stride2D.test.cpp new file mode 100644 index 000000000..43d159ee0 --- /dev/null +++ b/compiler/coco/core/src/IR/Stride2D.test.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Stride2D.h" + +#include + +TEST(IR_STRIDE_2D, default_constructor) +{ + coco::Stride2D stride; + + ASSERT_EQ(stride.vertical(), 1); + ASSERT_EQ(stride.horizontal(), 1); +} + +TEST(IR_STRIDE_2D, explicit_constructor_4) +{ + coco::Stride2D stride{2, 3}; + + ASSERT_EQ(stride.vertical(), 2); + ASSERT_EQ(stride.horizontal(), 3); +} + +TEST(IR_STRIDE_2D, update) +{ + coco::Stride2D stride; + + stride.vertical(2).horizontal(3); + + ASSERT_EQ(stride.vertical(), 2); + ASSERT_EQ(stride.horizontal(), 3); +} diff --git a/compiler/coco/core/src/IR/Sub.test.cpp b/compiler/coco/core/src/IR/Sub.test.cpp new file mode 100644 index 000000000..6c8b9ba54 --- /dev/null +++ b/compiler/coco/core/src/IR/Sub.test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Ops.h" + +#include +#include + +#include + +namespace +{ +struct IsSub : public coco::Op::Visitor +{ + bool visit(const coco::Sub *) override { return true; } +}; + +class SubTest : public ::testing::Test +{ +public: + SubTest() + { + // DO NOTHING + } + +protected: + coco::Sub *allocate(void) + { + auto op = new coco::Sub; + _allocated.emplace_back(op); + return op; + } + +private: + std::vector> _allocated; +}; +} // namespace + +TEST_F(SubTest, initialization) +{ + auto op = allocate(); + + // arguments should be empty on construction + ASSERT_EQ(op->left(), nullptr); + ASSERT_EQ(op->right(), nullptr); + + // uses() should be empty on construction + ASSERT_EQ(op->uses().size(), 0); + // parent() should be nullptr on construction + ASSERT_EQ(op->parent(), nullptr); +} + +TEST_F(SubTest, asSub) +{ + auto op = allocate(); + + coco::Op *mutable_base = op; + const coco::Op *immutable_base = op; + + ASSERT_EQ(mutable_base->asSub(), op); + ASSERT_EQ(mutable_base->asSub(), immutable_base->asSub()); +} + +TEST_F(SubTest, accept) +{ + // Test 'Sub' class + auto op = allocate(); + + coco::Sub *mutable_ptr = op; + const coco::Sub *immutable_ptr = op; + + ASSERT_TRUE(mutable_ptr->accept(IsSub{})); + ASSERT_TRUE(immutable_ptr->accept(IsSub{})); +} diff --git a/compiler/coco/core/src/IR/Update.cpp b/compiler/coco/core/src/IR/Update.cpp new file mode 100644 index 000000000..8e81c85cf --- /dev/null +++ b/compiler/coco/core/src/IR/Update.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Update.h" + +#include + +namespace coco +{ + +Update::~Update() +{ + // Unlink self from a linked bag if it exists + bag(nullptr); +} + +void Update::bag(Bag *bag) +{ + if (_bag) + { + _bag->mutable_updates()->erase(this); + _bag = nullptr; + } + + assert(_bag == nullptr); + + if (bag) + { + _bag = bag; + _bag->mutable_updates()->insert(this); + } + + assert(_bag == bag); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Update.test.cpp b/compiler/coco/core/src/IR/Update.test.cpp new file mode 100644 index 000000000..0bd355998 --- /dev/null +++ b/compiler/coco/core/src/IR/Update.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Update.h" +#include "coco/IR/BagManager.h" + +#include "Updater.mock.h" + +#include + +namespace +{ +class UpdateTest : public ::testing::Test +{ +protected: + coco::BagManager bag_mgr; +}; +} // namespace + +TEST_F(UpdateTest, constructor) +{ + // TODO Rename 'update' + ::mock::Updater update; + + // TODO Rename 'slot' + coco::Update slot{&update}; + + ASSERT_EQ(slot.bag(), nullptr); +} + +TEST_F(UpdateTest, value) +{ + // TODO Rename 'update' + ::mock::Updater update; + + // TODO Rename 'slot' + coco::Update slot{&update}; + + auto bag = bag_mgr.create(16); + + slot.bag(bag); + + ASSERT_EQ(slot.bag(), bag); + + ASSERT_EQ(bag->updates()->size(), 1); + ASSERT_NE(bag->updates()->find(&slot), bag->updates()->end()); + + slot.bag(nullptr); + + ASSERT_EQ(slot.bag(), nullptr); + + ASSERT_EQ(bag->updates()->size(), 0); +} + +TEST_F(UpdateTest, unlink_on_destruction) +{ + ::mock::Updater updater; + + auto bag = bag_mgr.create(1); + + { + coco::Update update{&updater}; + update.bag(bag); + ASSERT_EQ(bag->updates()->size(), 1); + } + + ASSERT_EQ(bag->updates()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/Updater.mock.h b/compiler/coco/core/src/IR/Updater.mock.h new file mode 100644 index 000000000..6441cdd02 --- /dev/null +++ b/compiler/coco/core/src/IR/Updater.mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_UPDATER_MOCK_H__ +#define __COCO_IR_UPDATER_MOCK_H__ + +#include "coco/IR/Bag.h" + +namespace +{ +namespace mock +{ +struct Updater final : public coco::Bag::Updater +{ + coco::Instr *loc(void) override { return nullptr; } +}; +} // namespace mock +} // namespace + +#endif // __COCO_IR_UPDATER_MOCK_H__ diff --git a/compiler/coco/core/src/IR/Use.cpp b/compiler/coco/core/src/IR/Use.cpp new file mode 100644 index 000000000..cd9b68105 --- /dev/null +++ b/compiler/coco/core/src/IR/Use.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Use.h" + +#include + +namespace coco +{ + +void Use::value(Object *value) +{ + if (_value) + { + _value->mutable_uses()->erase(this); + _value = nullptr; + } + + assert(_value == nullptr); + + if (value) + { + _value = value; + _value->mutable_uses()->insert(this); + } + + assert(_value == value); +} + +} // namespace coco diff --git a/compiler/coco/core/src/IR/Use.test.cpp b/compiler/coco/core/src/IR/Use.test.cpp new file mode 100644 index 000000000..3191e9852 --- /dev/null +++ b/compiler/coco/core/src/IR/Use.test.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Use.h" +#include "coco/IR/ObjectManager.h" + +#include "coco/IR/FeatureObject.h" + +#include "Consumer.mock.h" + +#include + +#include + +using stdex::make_unique; + +namespace +{ +class UseTest : public ::testing::Test +{ +protected: + coco::ObjectManager obj_mgr; +}; +} // namespace + +TEST_F(UseTest, constructor) +{ + auto o = obj_mgr.create(); + + // TODO Rename 'use' + ::mock::Consumer use; + + coco::Use slot{&use}; + + ASSERT_EQ(slot.value(), nullptr); +} + +TEST_F(UseTest, value) +{ + auto o = obj_mgr.create(); + + // TODO Rename 'use' + ::mock::Consumer use; + + coco::Use slot{&use}; + + slot.value(o); + + ASSERT_EQ(slot.value(), o); + + ASSERT_EQ(o->uses()->size(), 1); + ASSERT_NE(o->uses()->find(&slot), o->uses()->end()); + + slot.value(nullptr); + + ASSERT_EQ(slot.value(), nullptr); + + ASSERT_EQ(o->uses()->size(), 0); +} + +TEST_F(UseTest, destructor) +{ + ::mock::Consumer consumer; + + auto o = obj_mgr.create(); + auto use = make_unique(&consumer); + + use->value(o); + use.reset(); + + // ~Use SHOULD unlink itself from linked Object (if exists) + ASSERT_EQ(o->uses()->size(), 0); +} diff --git a/compiler/coco/core/src/IR/Window2D.test.cpp b/compiler/coco/core/src/IR/Window2D.test.cpp new file mode 100644 index 000000000..c0e919237 --- /dev/null +++ b/compiler/coco/core/src/IR/Window2D.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Window2D.h" + +#include + +TEST(IR_WINDOW_2D, default_constructor) +{ + coco::Window2D window; + + ASSERT_EQ(window.height(), 1); + ASSERT_EQ(window.width(), 1); +} + +TEST(IR_WINDOW_2D, explicit_constructor_4) +{ + coco::Window2D window{2, 3}; + + ASSERT_EQ(window.height(), 2); + ASSERT_EQ(window.width(), 3); +} + +TEST(IR_WINDOW_2D, update) +{ + coco::Window2D window; + + window.height(2); + window.width(3); + + ASSERT_EQ(window.height(), 2); + ASSERT_EQ(window.width(), 3); +} diff --git a/compiler/coco/generic/CMakeLists.txt b/compiler/coco/generic/CMakeLists.txt new file mode 100644 index 000000000..02fbf67f5 --- /dev/null +++ b/compiler/coco/generic/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(coco_generic SHARED ${SOURCES}) +target_include_directories(coco_generic PUBLIC include) +target_link_libraries(coco_generic PUBLIC coco_core) +target_link_libraries(coco_generic PRIVATE stdex) +target_link_libraries(coco_generic PRIVATE nncc_common) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is required for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(coco_generic_test ${TESTS}) +target_link_libraries(coco_generic_test coco_generic) +# stdex is a PRIVATE dependency of coco_generic, and thus is not linked to coco_generic_test +# even though coco_generic_test is linked to coco_generic +target_link_libraries(coco_generic_test stdex) diff --git a/compiler/coco/generic/include/coco/ADT/Span.h b/compiler/coco/generic/include/coco/ADT/Span.h new file mode 100644 index 000000000..240e6afec --- /dev/null +++ b/compiler/coco/generic/include/coco/ADT/Span.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_ADT_SPAN_H__ +#define __COCO_ADT_SPAN_H__ + +#include +#include + +namespace coco +{ + +/** + * @brief A Span is a non-owing reference to a memory chunk + * + * @note A Span DOES NOT OWN a memory chunk. + */ +template class Span +{ +public: + Span(T *data, uint32_t size) : _data{data}, _size{size} + { + // DO NOTHING + } + +public: + T *data(void) { return _data; } + const T *data(void) const { return _data; } + +public: + uint32_t size(void) const { return _size; } + +public: + T &operator[](uint32_t n) + { + assert(n < _size); + return *(_data + n); + } + +public: + const T &operator[](uint32_t n) const + { + assert(n < _size); + return *(_data + n); + } + +private: + T *_data; + uint32_t _size; +}; + +} // namespace coco + +#endif // __COCO_ADT_SPAN_H__ diff --git a/compiler/coco/generic/include/coco/IR/Data.h b/compiler/coco/generic/include/coco/IR/Data.h new file mode 100644 index 000000000..0cbee85e9 --- /dev/null +++ b/compiler/coco/generic/include/coco/IR/Data.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_DATA_H__ +#define __COCO_IR_DATA_H__ + +#include "coco/IR/PlainWeightContext.h" + +#include + +namespace coco +{ + +/** + * @brief Core coco entity for constant weights + */ +struct Data +{ + virtual ~Data() = default; + + /** + * @brief Return true if a given bag has an allocated weight data + */ + virtual bool allocated(const coco::Bag *) const = 0; + + /** + * @brief Release a memory chunk allocated for weight data of a given bag + * + * WARN Do NOT invoke release for a bag "b" for which allocated(b) does NOT hold + */ + virtual void release(const coco::Bag *) = 0; + + virtual PlainWeightContext *f32(void) = 0; + virtual const PlainWeightContext *f32(void) const = 0; + + static std::unique_ptr create(void); +}; + +} // namespace coco + +#endif // __COCO_IR_DATA_H__ diff --git a/compiler/coco/generic/include/coco/IR/PlainWeightContext.h b/compiler/coco/generic/include/coco/IR/PlainWeightContext.h new file mode 100644 index 000000000..5100e9d90 --- /dev/null +++ b/compiler/coco/generic/include/coco/IR/PlainWeightContext.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COCO_IR_PLAIN_WEIGHT_CONTEXT_H__ +#define __COCO_IR_PLAIN_WEIGHT_CONTEXT_H__ + +#include "coco/IR/Bag.h" +#include "coco/IR/KernelObject.h" + +#include "coco/ADT/Span.h" + +#include +#include + +#include + +namespace coco +{ + +/** + * @brief Non-quantized (plain) Weight Data Accessor + */ +template struct PlainWeightContext +{ + virtual ~PlainWeightContext() = default; + + /** + * @brief Allocate a weight space for a given blob + * + * @require the following code SHOULD work for any bag "b": + * PlainWeightContext ctx; + * + * auto span = ctx.allocate(b); + * assert(span.data() != nullptr); + * assert(span.size() == bag->size()); + */ + virtual Span allocate(const Bag *) = 0; + + /** + * @brief Return a pointer to the underlying storage + * + * @note weight returns a null-span S for an invalid bag + * i.e S.data() == nullptr and S.size() == 0 + */ + virtual Span weight(const Bag *) = 0; + + virtual std::unique_ptr> access(const KernelObject *) = 0; + virtual std::unique_ptr> read(const KernelObject *) const = 0; +}; + +} // namespace coco + +#endif // __COCO_IR_PLAIN_WEIGHT_CONTEXT_H__ diff --git a/compiler/coco/generic/src/ADT/Span.test.cpp b/compiler/coco/generic/src/ADT/Span.test.cpp new file mode 100644 index 000000000..c313233a2 --- /dev/null +++ b/compiler/coco/generic/src/ADT/Span.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/ADT/Span.h" + +#include + +TEST(SpanTest, constructor) +{ + const uint32_t arr_size = 16; + int arr_data[arr_size]; + + coco::Span span{arr_data, arr_size}; + + coco::Span &ref = span; + const coco::Span &cref = span; + + ASSERT_EQ(ref.data(), arr_data); + ASSERT_EQ(cref.data(), arr_data); + ASSERT_EQ(ref.size(), arr_size); +} + +TEST(SpanTest, array_subscript_operator) +{ + // Create a stack-allocated chunk + const uint32_t arr_size = 16; + int arr_data[arr_size]; + + for (uint32_t n = 0; n < arr_size; ++n) + { + arr_data[n] = n; + } + + // Create a Span + coco::Span span{arr_data, arr_size}; + + coco::Span &ref = span; + const coco::Span &cref = span; + + ASSERT_EQ(ref[3], 3); + ASSERT_EQ(cref[3], 3); + + arr_data[3] = 16; + + ASSERT_EQ(ref[3], 16); + ASSERT_EQ(cref[3], 16); +} diff --git a/compiler/coco/generic/src/IR/Data.cpp b/compiler/coco/generic/src/IR/Data.cpp new file mode 100644 index 000000000..b71947253 --- /dev/null +++ b/compiler/coco/generic/src/IR/Data.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Data.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +using stdex::make_unique; + +namespace +{ +class BlobContext +{ +public: + void allocate(const coco::Bag *b, uint32_t elemsize) + { + auto buffer = make_unique>(); + buffer->resize(b->size() * elemsize); + + _data[b] = std::move(buffer); + } + + void release(const coco::Bag *b) { _data.erase(b); } + +public: + uint8_t *at(const coco::Bag *b) + { + auto it = _data.find(b); + + if (it != _data.end()) + { + return it->second->data(); + } + + return nullptr; + } + +public: + uint32_t size(const coco::Bag *b) const + { + auto it = _data.find(b); + + if (it != _data.end()) + { + return it->second->size(); + } + + return 0; + } + +private: + std::map>> _data; +}; +} + +namespace +{ + +template class KernelOverlay : public kernel::Reader, public kernel::Accessor +{ +public: + KernelOverlay(T *base, const coco::KernelObject *object) : _base{base}, _object{object} + { + // DO NOTHING + } + +public: + T at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override + { + assert(_object->layout() != nullptr); + auto offset = _object->layout()->at(nth, ch, row, col); + return *(_base + offset.value()); + } + +public: + T &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) override + { + assert(_object->layout() != nullptr); + auto offset = _object->layout()->at(nth, ch, row, col); + return *(_base + offset.value()); + } + +private: + T *_base; + const coco::KernelObject *_object; +}; + +} // namespace + +namespace +{ +template class PlainWeightContextImpl final : public coco::PlainWeightContext +{ +public: + PlainWeightContextImpl(BlobContext *blob) : _blob{blob} + { + // DO NOTHING + } + +public: + PlainWeightContextImpl(const PlainWeightContextImpl &) = delete; + PlainWeightContextImpl(PlainWeightContextImpl &&) = delete; + +public: + coco::Span allocate(const coco::Bag *bag) override + { + assert(bag != nullptr); + _blob->allocate(bag, sizeof(T)); + return weight(bag); + } + + coco::Span weight(const coco::Bag *b) override + { + // TODO Check type later + if (auto data = _blob->at(b)) + { + uint32_t byte_size = _blob->size(b); + assert(byte_size % sizeof(T) == 0); + uint32_t elem_size = static_cast(byte_size / sizeof(T)); + + return coco::Span{reinterpret_cast(data), elem_size}; + } + + return coco::Span{nullptr, 0}; + } + +public: + std::unique_ptr> access(const coco::KernelObject *o) override + { + auto b = o->bag(); + assert(b != nullptr); + + if (auto base = reinterpret_cast(_blob->at(b))) + { + return make_unique>(base, o); + } + + return nullptr; + } + +public: + std::unique_ptr> read(const coco::KernelObject *o) const override + { + auto b = o->bag(); + assert(b != nullptr); + + if (auto base = reinterpret_cast(_blob->at(b))) + { + return make_unique>(base, o); + } + + return nullptr; + } + +private: + BlobContext *const _blob; +}; +} // namespace + +namespace +{ +struct DataImpl final : public coco::Data +{ + std::unique_ptr _blob; + std::unique_ptr> _fp32; + + bool allocated(const coco::Bag *b) const override { return _blob->at(b) != nullptr; } + + void release(const coco::Bag *b) override + { + assert(allocated(b)); + _blob->release(b); + } + + coco::PlainWeightContext *f32(void) override { return _fp32.get(); } + const coco::PlainWeightContext *f32(void) const override { return _fp32.get(); } +}; +} // namespace + +namespace coco +{ + +std::unique_ptr Data::create(void) +{ + auto blob = make_unique(); + auto fp32 = make_unique>(blob.get()); + + auto data = make_unique(); + + data->_blob = std::move(blob); + data->_fp32 = std::move(fp32); + + // GCC 4.9 tries to copy data (while GCC 6.X doesn't) + return std::move(data); +} + +} // namespace coco diff --git a/compiler/coco/generic/src/IR/Data.test.cpp b/compiler/coco/generic/src/IR/Data.test.cpp new file mode 100644 index 000000000..1029dfe9f --- /dev/null +++ b/compiler/coco/generic/src/IR/Data.test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coco/IR/Data.h" +#include "coco/IR/Module.h" +#include "coco/IR/KernelLayouts.h" + +#include + +#include + +TEST(IR_DATA, construct) +{ + auto data = coco::Data::create(); + + coco::Data *mutable_ptr = data.get(); + const coco::Data *immutable_ptr = data.get(); + + ASSERT_NE(mutable_ptr->f32(), nullptr); + ASSERT_EQ(mutable_ptr->f32(), immutable_ptr->f32()); +} + +TEST(IR_DATA, allocate_and_link_bag) +{ + auto m = coco::Module::create(); + auto d = coco::Data::create(); + + // Create a bag + auto bag = m->entity()->bag()->create(9); + + // weight(...) SHOULD return a null-span for an invalid bag + { + auto span = d->f32()->weight(bag); + + ASSERT_EQ(span.data(), nullptr); + ASSERT_EQ(span.size(), 0); + } + + // Allocate a weight space + { + auto allocated_span = d->f32()->allocate(bag); + + ASSERT_NE(allocated_span.data(), nullptr); + ASSERT_EQ(allocated_span.size(), bag->size()); + + auto retrieved_span = d->f32()->weight(bag); + + ASSERT_EQ(allocated_span.data(), retrieved_span.data()); + ASSERT_EQ(allocated_span.size(), retrieved_span.size()); + } +} diff --git a/compiler/coco/requires.cmake b/compiler/coco/requires.cmake new file mode 100644 index 000000000..654db88c3 --- /dev/null +++ b/compiler/coco/requires.cmake @@ -0,0 +1 @@ +require("angkor") diff --git a/compiler/cwrap/CMakeLists.txt b/compiler/cwrap/CMakeLists.txt new file mode 100644 index 000000000..e1ae4d0b5 --- /dev/null +++ b/compiler/cwrap/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(cwrap STATIC ${SOURCES}) +set_target_properties(cwrap PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(cwrap PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(cwrap PUBLIC include) +# Let's apply nncc common compile options +# NOTE This will enable strict compilation (warnings as error). +# Please refer to top-level CMakeLists.txt for details +target_link_libraries(cwrap PRIVATE nncc_common) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(cwrap_test ${TESTS}) +target_link_libraries(cwrap_test cwrap) diff --git a/compiler/cwrap/README.md b/compiler/cwrap/README.md new file mode 100644 index 000000000..5440ca3f9 --- /dev/null +++ b/compiler/cwrap/README.md @@ -0,0 +1,23 @@ +# cwrap + +_cwrap_ is a collection of C++ wrappers for POSIX C API. + +## How to use + +Currently it supports only file descriptor. + +## Example +- File Descriptor + +```cpp +cwrap::Fildes fildes{open(path.c_str(), O_RDONLY)}; + +if (fildes.get() < 0) +{ + std::ostringstream ostr; + ostr << "Error: " << path << " not found" << std::endl; + throw std::runtime_error{ostr.str()}; +} + +google::protobuf::io::FileInputStream fis(fildes.get()); +``` diff --git a/compiler/cwrap/include/cwrap/Fildes.h b/compiler/cwrap/include/cwrap/Fildes.h new file mode 100644 index 000000000..f1061cc57 --- /dev/null +++ b/compiler/cwrap/include/cwrap/Fildes.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CWRAP_FILDES_H__ +#define __CWRAP_FILDES_H__ + +namespace cwrap +{ + +/** + * @brief POSIX File Descriptor + * + * @note Fildes owns underlying file descriptor + */ +class Fildes final +{ +public: + Fildes(); + explicit Fildes(int value); + + // NOTE Copy is not allowed + Fildes(const Fildes &) = delete; + Fildes(Fildes &&); + + ~Fildes(); + +public: + Fildes &operator=(Fildes &&); + +public: + int get(void) const; + void set(int value); + + int release(void); + +private: + int _value; +}; + +bool valid(const Fildes &); + +} // namespace cwrap + +#endif // __CWRAP_FILDES_H__ diff --git a/compiler/cwrap/src/Fildes.cpp b/compiler/cwrap/src/Fildes.cpp new file mode 100644 index 000000000..5ccb83f05 --- /dev/null +++ b/compiler/cwrap/src/Fildes.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cwrap/Fildes.h" + +#include +#include + +namespace +{ + +/** + * @note making inline to this function will prevent unused function error + * as error_value() is used only inside assert() + */ +inline bool error_value(int fd) { return fd == -1; } + +inline bool valid_value(int fd) { return fd >= 0; } + +} // namespace + +namespace cwrap +{ + +Fildes::Fildes() : _value{-1} +{ + // DO NOTHING +} + +Fildes::Fildes(int value) : _value{value} +{ + // DO NOTHING + assert(error_value(value) || valid_value(value)); +} + +Fildes::Fildes(Fildes &&fildes) +{ + set(fildes.release()); + assert(error_value(fildes.get())); +} + +Fildes::~Fildes() +{ + assert(error_value(_value) || valid_value(_value)); + + if (valid_value(_value)) + { + close(_value); + _value = -1; + } + + assert(error_value(_value)); +} + +Fildes &Fildes::operator=(Fildes &&fildes) +{ + set(fildes.release()); + return (*this); +} + +int Fildes::get(void) const { return _value; } + +void Fildes::set(int value) +{ + assert(error_value(_value) || valid_value(_value)); + + if (valid_value(_value)) + { + close(_value); + _value = -1; + } + assert(error_value(_value)); + + _value = value; + assert(_value == value); +} + +int Fildes::release(void) +{ + int res = get(); + _value = -1; + return res; +} + +bool valid(const Fildes &fildes) { return valid_value(fildes.get()); } + +} // namespace cwrap diff --git a/compiler/cwrap/src/Fildes.test.cpp b/compiler/cwrap/src/Fildes.test.cpp new file mode 100644 index 000000000..08e1e2a5e --- /dev/null +++ b/compiler/cwrap/src/Fildes.test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cwrap/Fildes.h" + +#include +#include +#include + +#include +#include + +#include + +#define DECLARE_TEMPLATE(NAME) char NAME[] = "FILDES-TEST-XXXXXX" + +namespace +{ + +int make_temp(char *name_template) +{ + int fd = mkstemp(name_template); + + if (fd == -1) + { + throw std::runtime_error{"mkstemp failed"}; + } + + return fd; +} + +} // namespace make_temp + +TEST(FildesTest, default_constructor) +{ + cwrap::Fildes fildes; + + ASSERT_FALSE(cwrap::valid(fildes)); +} + +TEST(FildesTest, value_constructor) +{ + DECLARE_TEMPLATE(name_template); + + cwrap::Fildes fildes{make_temp(name_template)}; + + ASSERT_TRUE(cwrap::valid(fildes)); +} + +TEST(FildesTest, move_constructor) +{ + DECLARE_TEMPLATE(src_template); + DECLARE_TEMPLATE(dst_template); + + int src_fd = make_temp(src_template); + int dst_fd = make_temp(dst_template); + + cwrap::Fildes src{src_fd}; + cwrap::Fildes dst{dst_fd}; + + dst = std::move(src); + + ASSERT_FALSE(cwrap::valid(src)); + ASSERT_TRUE(cwrap::valid(dst)); + + ASSERT_EQ(dst.get(), src_fd); + + // "src_fd" SHOULD be valid, and "dst_fd" SHOULD be closed + ASSERT_NE(fcntl(src_fd, F_GETFD), -1); + ASSERT_EQ(fcntl(dst_fd, F_GETFD), -1); +} + +TEST(FildesTest, destructor) +{ + DECLARE_TEMPLATE(name_template); + + int fd = make_temp(name_template); + + ASSERT_NE(fcntl(fd, F_GETFD), -1); + { + cwrap::Fildes fildes{fd}; + } + ASSERT_EQ(fcntl(fd, F_GETFD), -1); +} diff --git a/compiler/dredd-rule-lib/CMakeLists.txt b/compiler/dredd-rule-lib/CMakeLists.txt new file mode 100644 index 000000000..b39d86272 --- /dev/null +++ b/compiler/dredd-rule-lib/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# copy rule-lib.sh (a library of shell script functions) +# +set(SOURCE_RULE_LIB "${CMAKE_CURRENT_SOURCE_DIR}/rule-lib.sh") +set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh") + +add_custom_command( + OUTPUT ${TARGET_RULE_LIB} + COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}" + DEPENDS ${SOURCE_RULE_LIB} + COMMENT "Generate rule lib" +) + +# Generate dependencies +add_custom_target(dredd_rule_lib ALL DEPENDS ${TARGET_RULE_LIB}) + +# How to get the path of rule-lib.sh in other CMakeLists.txt +# +# get_target_property(DREDD_RULE_LIB_DIR +# dredd_rule_lib BINARY_DIR) +# set(RULE_LIB_PATH "${DREDD_RULE_LIB_DIR}/rule-lib.sh") diff --git a/compiler/dredd-rule-lib/README.md b/compiler/dredd-rule-lib/README.md new file mode 100644 index 000000000..348b0aefb --- /dev/null +++ b/compiler/dredd-rule-lib/README.md @@ -0,0 +1,112 @@ +# dredd-rule-lib + +*dredd-rule-lib* is a library that defines functions to run *dredd* tests, which checks non-functional aspect of compiled files. + +## Terms + +Assume that we want to check the size of generated tflite file to be less than 1024 Bytes. +In such case, we'd like to use the following terms: + +- "metric" : *file size* +- "rule" : *file size < 1024* +- "metric function": `file_size` that returns size of a compiled tflite file + +Models (input of test) exist in *model repo*, where + +- "model repo" : directory where models exist. For *tf2tflite-dredd-pbtxt-test*, model repo is + `res/TensorFlowTests`. + +## Metrics supported + +The following metric functions are provided: +- `all_op_count` : the count of operations inside a compiled tflite file +- `file_size` : the size of compiled tflite file +- In addition, `op_count`, `conv2d_weight_not_constant`, etc. +- Please , refer to [`rule-lib.sh`](rule-lib.sh) for metric functions + +## Related projects - *dredd* tests + +Four *dredd* test projects use *dredd-rule-lib*: + +- *tf2tflite-dredd-pbtxt-test* + - Models in `pbtxt`, text file, are compiled into `tflite` file. + - Then `rule` file that each model has is checked against the `tflite` file. +- *tf2tflite-dredd-pb-test* + - Models in `pb`, binary file, are compiled into `tflite` file. + - Then `rule` file that each model has is checked against the `tflite` file. +- *tf2circle-dredd-pbtxt-test* + - Models in `pbtxt`, text file, are compiled into `circle` file. + - Then `rule` file that each model has is checked against the `circle` file. +- *tf2circle-dredd-pb-test* + - Models in `pb`, binary file, are compiled into `circle` file. + - Then `rule` file that each model has is checked against the `circle` file. + +## Rule file + +To be a target of *dredd*-tests, a `.rule` file **must** exist in a model directory. +Please refer to `res/TensorFlowTests/NET_0025/tflite_1.0_rel_requirement.rule` for an example. + +### Naming convention of rule file + +Note that the file name `tflite_1.0_rel_requirement.rule` is our convention containing the +information below: +- Generated file type (`tflite`) +- SDK version (`1.0_rel`) +- Purpose (`requirement`) + +## How do all these work? + +For *tf2tflite-dredd-pbtxt-test*, (*tf2circle-dredd-pbtxt-test* works similarly) + +``` +model repo tf2tflite-dredd-pbtxt-test +----------------------------------------------------------------------------------------------- + NET_0025 + ├── test.pbtxt ----------------------> converted to NET_0025.pb, and then NET_0025.tflite + | /|\ + ├── test.info ---------------------------+ + | (input/output info of model) + | + └── tflite_1.0_rel_requirement.rule --> running rule file against tflite --> pass or fail + /|\ + dredd-rule-lib | (using) + ---------------------- | + rule-lib.sh | + - defining rule function --+ +``` + +For *tf2tflite-dredd-pb-test*, (*tf2circle-dredd-pb-test* works similarly) + +``` +model repo tf2tflite-dredd-pb-test +----------------------------------------------------------------------------------------------- + Inception_v3 + ├── model.pb ------------------------> converted to Inception_v3.tflite + | /|\ + ├── model.info --------------------------+ + | (input/output info of model) + | + └── tflite_1.0_rel_requirement.rule --> running rule file against tflite --> pass or fail + /|\ + dredd-rule-lib | (using) + ---------------------- | + rule-lib.sh | + - defining rule function --+ +``` + +## Model repo and How to add a model as a target of a *dredd*-test. + +For *tf2tflite-dredd-pbtxt-test* and *tf2circle-dredd-pbtxt-test*, +model repo is `res/TensorFlowTests`. + +To add a model into these tests, the model directory name should be added into one of the following files: +- `test.lst` : This file resides in git +- `test.local.lst` : This file is ignored by git. Use this for personal purpose. + +For *tf2tflite-dredd-pb-test* and *tf2circle-dredd-pb-test*, +model repo is `tf2tflite-dredd-pb-test/contrib` and .`tf2circle-dredd-pb-test/contrib` respectively. + +Use these tests for binary models in large size. + +To add a model into these tests, the model directory name should be added into the following file: +- `contrib.lst` : This file is ignored by git. diff --git a/compiler/dredd-rule-lib/rule-lib.sh b/compiler/dredd-rule-lib/rule-lib.sh new file mode 100755 index 000000000..8ebe3d7af --- /dev/null +++ b/compiler/dredd-rule-lib/rule-lib.sh @@ -0,0 +1,203 @@ +#!/bin/bash + +# the following env vars should be defined to call dredd function (except RULE): +# COMPILED_FILE +# INSPECT_PROG_PATH +# VERIFY_PROG_PATH +# ERROR_LOG + +# exit if unknown var is used +set -u + +# --------------- +# HELPER FUNCTION + +init_error_log() +{ + # create ${ERROR_LOG} that redirect stderr for pipe + exec 2>"${ERROR_LOG}" +} + +argc_check() +{ + ACTUAL_ARGC=$1 + EXPECTED_ARGC=$2 + + if [ "$#" -ne 2 ];then + echo "argc_check : param count must be 2" > ${ERROR_LOG} + echo "error" # return value of sub-shell + exit 1 + fi + + if [ ${ACTUAL_ARGC} -ne ${EXPECTED_ARGC} ];then + echo "arg count mismatch: actual = ${ACTUAL_ARGC} vs expected = ${EXPECTED_ARGC}" > ${ERROR_LOG} + echo "error" # return value of sub-shell + exit 1 + fi +} + +file_path_check() +{ + argc_check $# 1 + + if [ ! -f $1 ]; then + echo "$1 does not exist" > ${ERROR_LOG} + echo "error" # return value of sub-shell + exit 1 + fi +} + +check_success_exit_code() +{ + ACTUAL_EXIT_CODE=$1 + EXPECTED_SUCCESS_CODE=$2 + + if [ ${ACTUAL_EXIT_CODE} -ne ${EXPECTED_SUCCESS_CODE} ];then + echo "error" + exit 1 + fi +} + +check_error_exit_code() +{ + ACTUAL_EXIT_CODE=$1 + EXPECTED_ERROR_CODE=$2 + + if [ ${ACTUAL_EXIT_CODE} -eq ${EXPECTED_ERROR_CODE} ];then + echo "error" + exit 1 + fi +} + +# END of HELPER FUNCTION +# ---------------------- + +# +# Define rule +# +# - Params: rule name (metric), actual value, condition, expected value +# - condition is '=', '!=', '<', '>', '<=', '>='. Refer to "man expr" +# - Return +# - 0 : success +# - 1 : fail (condition check fail) +# + +RULE() +{ + argc_check $# 4 + + RULE_NAME=$1 + ACTUAL=$2 + COND=$3 + EXPECTED=$4 + + # not to exit when expr result with 0 + set +e + + expr ${ACTUAL} ${COND} ${EXPECTED} > /dev/null + RESULT=$? + + # roll-back + set -e + + # Note: return value of 'expr' + # - 0 : result is true + # - 1 : result is false + # - 2 : error + + if [ ${RESULT} -eq 0 ];then + echo -e "** [${RULE_NAME}] \t success \t ([actual: ${ACTUAL}] ${COND} [expected: ${EXPECTED}])" + elif [ ${RESULT} -eq 1 ];then + echo -e "** [${RULE_NAME}] \t ** fail \t ([actual: ${ACTUAL}] ${COND} [expected: ${EXPECTED}])" + else + echo -e "\t** Error in [expr ${ACTUAL} ${COND} ${EXPECTED}]" + fi + + return ${RESULT} +} + +# +# Define each function to get quality value +# + +# Note: These function is called by a sub-shell. +# So return value should be passed through "echo return_value" +# tip: for debugging, surround the code with "set -x" and "set +x" + +file_size() +{ + file_path_check ${COMPILED_FILE} + + set -o pipefail + + ACTUAL=`init_error_log ; cat ${COMPILED_FILE} | wc -c` + + check_success_exit_code $? 0 + + echo ${ACTUAL} +} + +all_op_count() +{ + file_path_check ${COMPILED_FILE} + file_path_check ${INSPECT_PROG_PATH} + + set -o pipefail + + ACTUAL=`init_error_log ; ${INSPECT_PROG_PATH} --operators ${COMPILED_FILE} | wc -l` + + check_success_exit_code $? 0 + + echo ${ACTUAL} +} + +op_count() +{ + argc_check $# 1 + file_path_check ${COMPILED_FILE} + file_path_check ${INSPECT_PROG_PATH} + + set -o pipefail + + RESULT=`init_error_log ; ${INSPECT_PROG_PATH} --operators ${COMPILED_FILE}` + check_success_exit_code $? 0 + + # note : grep's exit code is 2 in case of error. + ACTUAL=`init_error_log ; echo "${RESULT}" | grep -wc "$1"` + check_error_exit_code $? 2 + + echo ${ACTUAL} +} + +conv2d_weight_not_constant() +{ + file_path_check ${COMPILED_FILE} + file_path_check ${INSPECT_PROG_PATH} + + set -o pipefail + + ACTUAL=`init_error_log ; \ + ${INSPECT_PROG_PATH} --conv2d_weight ${COMPILED_FILE} | \ + awk -F, '{ if ($2 != "CONST") print $0}' | wc -l` + + check_success_exit_code $? 0 + + echo ${ACTUAL} +} + +verify_file_format() +{ + file_path_check ${COMPILED_FILE} + file_path_check ${VERIFY_PROG_PATH} + + set -o pipefail + + ACTUAL=`init_error_log ; ${VERIFY_PROG_PATH} ${COMPILED_FILE} | grep -c "PASS"` + + # note grep can exit with 1 ("PASS" not found) and this is treated as an error + check_success_exit_code $? 0 + + echo ${ACTUAL} +} + +# TODO define more qullity test function diff --git a/compiler/enco-intf/CMakeLists.txt b/compiler/enco-intf/CMakeLists.txt new file mode 100644 index 000000000..6014512c8 --- /dev/null +++ b/compiler/enco-intf/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(frontend) +add_subdirectory(cmdline) diff --git a/compiler/enco-intf/cmdline/CMakeLists.txt b/compiler/enco-intf/cmdline/CMakeLists.txt new file mode 100644 index 000000000..91221ca1a --- /dev/null +++ b/compiler/enco-intf/cmdline/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(enco_intf_cmdline INTERFACE) +target_include_directories(enco_intf_cmdline INTERFACE include) diff --git a/compiler/enco-intf/cmdline/include/cmdline/View.h b/compiler/enco-intf/cmdline/include/cmdline/View.h new file mode 100644 index 000000000..dd8d1d7eb --- /dev/null +++ b/compiler/enco-intf/cmdline/include/cmdline/View.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMDLINE_VIEW_H__ +#define __CMDLINE_VIEW_H__ + +#include + +namespace cmdline +{ + +struct View +{ + virtual ~View() = default; + + virtual uint32_t size(void) const = 0; + virtual const char *at(uint32_t n) const = 0; +}; + +} // namespace cmdline + +#endif // __CMDLINE_VIEW_H__ diff --git a/compiler/enco-intf/frontend/CMakeLists.txt b/compiler/enco-intf/frontend/CMakeLists.txt new file mode 100644 index 000000000..164dbd2b5 --- /dev/null +++ b/compiler/enco-intf/frontend/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(enco_intf_frontend INTERFACE) +target_include_directories(enco_intf_frontend INTERFACE include) +target_link_libraries(enco_intf_frontend INTERFACE coco_core) +target_link_libraries(enco_intf_frontend INTERFACE coco_generic) diff --git a/compiler/enco-intf/frontend/include/enco/Bundle.h b/compiler/enco-intf/frontend/include/enco/Bundle.h new file mode 100644 index 000000000..7c3dca88f --- /dev/null +++ b/compiler/enco-intf/frontend/include/enco/Bundle.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_BUNDLE_H__ +#define __ENCO_BUNDLE_H__ + +#include "coco/IR/Module.h" +#include "coco/IR/Data.h" + +#include + +namespace enco +{ + +class Bundle +{ +public: + Bundle() = default; + +public: + coco::Module *module(void) const { return _m.get(); } + void module(std::unique_ptr &&m) { _m = std::move(m); } + +public: + coco::Data *data(void) const { return _d.get(); } + void data(std::unique_ptr &&d) { _d = std::move(d); } + +private: + std::unique_ptr _m; + std::unique_ptr _d; +}; + +} // namespace enco + +#endif // __ENCO_BUNDLE_H__ diff --git a/compiler/enco-intf/frontend/include/enco/Frontend.h b/compiler/enco-intf/frontend/include/enco/Frontend.h new file mode 100644 index 000000000..d3a48183a --- /dev/null +++ b/compiler/enco-intf/frontend/include/enco/Frontend.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_FRONTEND_H__ +#define __ENCO_FRONTEND_H__ + +#include "Bundle.h" + +namespace enco +{ + +struct Frontend +{ + virtual ~Frontend() = default; + + virtual Bundle load(void) const = 0; +}; + +} // namespace enco + +#endif // __FRONTEND_H__ diff --git a/compiler/enco/CMakeLists.txt b/compiler/enco/CMakeLists.txt new file mode 100644 index 000000000..17300e25e --- /dev/null +++ b/compiler/enco/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(core) +add_subdirectory(frontend) +add_subdirectory(cli) +add_subdirectory(test) diff --git a/compiler/enco/README.md b/compiler/enco/README.md new file mode 100644 index 000000000..d995a1e55 --- /dev/null +++ b/compiler/enco/README.md @@ -0,0 +1,25 @@ +# enco + +_enco_ is a tool which translates a NN model into a C++ source code that implements the following functions: +``` +struct Network; + +Network *Network_construct(); +void Network_destruct(Network *net); + +unsigned Network_input_count(const Network *); +const char *Network_input_name(const Network *, unsigned n); +unsigned Network_input_rank(const Network *, unsigned n); +unsigned Network_input_dim(const Network *, unsigned n, unsigned axis); +void Network_input_bind(Network *net, unsigned n, const void *ptr, unsigned len); + +unsigned Network_output_count(const Network *net); +const char *Network_output_name(const Network *, unsigned n); +unsigned Network_output_rank(const Network *, unsigned n); +unsigned Network_output_dim(const Network *, unsigned n, unsigned axis); +void Network_output_bind(Network *net, unsigned n, void *ptr, unsigned len); + +void Network_invoke(Network *net); +``` + +Generated C++ code internally uses Android NN API for acceleration. diff --git a/compiler/enco/cli/CMakeLists.txt b/compiler/enco/cli/CMakeLists.txt new file mode 100644 index 000000000..5a43ab655 --- /dev/null +++ b/compiler/enco/cli/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(enco-cli ${SOURCES}) +target_include_directories(enco-cli PRIVATE src) +target_link_libraries(enco-cli enco_intf_cmdline) +target_link_libraries(enco-cli enco_intf_frontend) +target_link_libraries(enco-cli enco_core) +target_link_libraries(enco-cli stdex) +target_link_libraries(enco-cli dl) +# Let's use project-wide compile options +target_link_libraries(enco-cli nncc_common) diff --git a/compiler/enco/cli/src/Driver.cpp b/compiler/enco/cli/src/Driver.cpp new file mode 100644 index 000000000..185bb13b9 --- /dev/null +++ b/compiler/enco/cli/src/Driver.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include + +namespace cmdline +{ + +// TODO Extract this helper class +class Vector : public cmdline::View +{ +public: + uint32_t size(void) const { return _args.size(); } + +public: + const char *at(uint32_t nth) const { return _args.at(nth).c_str(); } + +public: + Vector &append(const std::string &arg) + { + _args.emplace_back(arg); + return (*this); + } + +private: + std::vector _args; +}; + +} // namespace cmdline + +namespace +{ + +class Zone +{ +public: + Zone() = default; + +public: + const cmdline::View *args(void) const { return &_args; } + +public: + void append(const std::string &arg) { _args.append(arg); } + +private: + cmdline::Vector _args; +}; + +} // namespace + +#include + +namespace +{ + +class FrontendFactory +{ +public: + FrontendFactory(const std::string &path) + { + _handle = dlopen(path.c_str(), RTLD_LAZY); + assert(_handle != nullptr); + } + +public: + // Copy is not allowed to avoid double close + FrontendFactory(const FrontendFactory &) = delete; + FrontendFactory(FrontendFactory &&) = delete; + +public: + ~FrontendFactory() { dlclose(_handle); } + +private: + using Entry = std::unique_ptr (*)(const cmdline::View &); + +private: + Entry entry(void) const + { + auto entry = reinterpret_cast(dlsym(_handle, "make_frontend")); + assert(entry != nullptr); + return entry; + } + +public: + std::unique_ptr make(const cmdline::View *args) const + { + auto fn = entry(); + return fn(*args); + } + +private: + void *_handle; +}; + +} // namespace + +namespace +{ + +class FrontendZone : public Zone +{ +public: + FrontendZone(const std::string &path) : _factory{path} + { + // DO NOTHING + } + +public: + const FrontendFactory *factory(void) const { return &_factory; } + +private: + FrontendFactory _factory; +}; + +} // namespace + +#include + +#include + +#include +#include + +static int entry(int argc, char **argv) +{ + // Usage: + // [Command] --frontend [Frontend .so path] --frontend-arg ... + std::unique_ptr frontend_zone; + cmdline::Vector backend_args; + + // Simple argument parser (based on map) + std::map> argparse; + + argparse["--frontend"] = [&](const std::string &path) { + frontend_zone = stdex::make_unique(path); + }; + + argparse["--frontend-arg"] = [&](const std::string &arg) { frontend_zone->append(arg); }; + argparse["--backend-arg"] = [&](const std::string &arg) { backend_args.append(arg); }; + + if (argc < 2) + { + std::cerr << "Usage:" << std::endl; + std::cerr << "[Command] --frontend [.so path]" << std::endl; + std::cerr << " --frontend-arg [argument] ..." << std::endl; + std::cerr << " --backend-arg [argument] ..." << std::endl; + return 255; + } + + for (int n = 1; n < argc; n += 2) + { + const std::string tag{argv[n]}; + const std::string arg{argv[n + 1]}; + + auto it = argparse.find(tag); + + if (it == argparse.end()) + { + std::cerr << "Option '" << tag << "' is not supported" << std::endl; + return 255; + } + + it->second(arg); + } + + assert(frontend_zone != nullptr); + + auto frontend = frontend_zone->factory()->make(frontend_zone->args()); + + auto bundle = frontend->load(); + + auto backend = make_backend(backend_args); + + backend->compile(bundle.module(), bundle.data()); + + return 0; +} + +#ifdef NDEBUG +int main(int argc, char **argv) +{ + try + { + return entry(argc, argv); + } + catch (const std::exception &e) + { + std::cerr << "ERROR: " << e.what() << std::endl; + } + + return 255; +} +#else // NDEBUG +int main(int argc, char **argv) +{ + // NOTE main does not catch internal exceptions for debug build to make it easy to + // check the stacktrace with a debugger + return entry(argc, argv); +} +#endif // !NDEBUG diff --git a/compiler/enco/core/CMakeLists.txt b/compiler/enco/core/CMakeLists.txt new file mode 100644 index 000000000..f437e687a --- /dev/null +++ b/compiler/enco/core/CMakeLists.txt @@ -0,0 +1,35 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +### +### enco_core is built as a shared library to support "interactive debugging". +### +### interactive debugging helpers are stripped during linking when enco_core is +### built as a static library +### +add_library(enco_core SHARED ${SOURCES}) +target_include_directories(enco_core PRIVATE src) +target_include_directories(enco_core PUBLIC include) +target_link_libraries(enco_core PUBLIC enco_intf_cmdline) +target_link_libraries(enco_core PUBLIC coco_core) +target_link_libraries(enco_core PUBLIC coco_generic) +# These libraries are linked for internal use, and thus does not appear in public headers. +target_link_libraries(enco_core PRIVATE pp) +target_link_libraries(enco_core PRIVATE morph) +target_link_libraries(enco_core PRIVATE stdex) +# Let's use nncc project-wide build options +target_link_libraries(enco_core PRIVATE nncc_common) + +nnas_find_package(GTest QUIET) + +if(NOT GTest_FOUND) + return() +endif(NOT GTest_FOUND) + +add_executable(enco_core_test ${TESTS}) +target_include_directories(enco_core_test PRIVATE src) +target_link_libraries(enco_core_test gtest_main) +target_link_libraries(enco_core_test enco_core) +target_link_libraries(enco_core_test morph) +add_test(enco_core_test enco_core_test) diff --git a/compiler/enco/core/include/enco/Backend.h b/compiler/enco/core/include/enco/Backend.h new file mode 100644 index 000000000..5da903ed2 --- /dev/null +++ b/compiler/enco/core/include/enco/Backend.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_BACKEND_H__ +#define __ENCO_BACKEND_H__ + +#include "cmdline/View.h" + +#include "coco/IR/Module.h" +#include "coco/IR/Data.h" + +#include + +namespace enco +{ + +struct Backend +{ + virtual ~Backend() = default; + + virtual void compile(coco::Module *m, coco::Data *d) = 0; +}; + +} // namespace enco + +std::unique_ptr make_backend(const cmdline::View &); + +#endif // __ENCO_BACKEND_H__ diff --git a/compiler/enco/core/src/ANN/Binder.h b/compiler/enco/core/src/ANN/Binder.h new file mode 100644 index 000000000..71b95676b --- /dev/null +++ b/compiler/enco/core/src/ANN/Binder.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_BINDER_H__ +#define __ANN_BINDER_H__ + +#include "ANN/IR/Module.h" + +#include + +#include + +#include + +/** + * @brief A bridge between ann::Module and coco::Block + */ +class ANNBinder +{ +public: + ANNBinder(coco::Block *block, std::unique_ptr &&module) + : _block{block}, _module{std::move(module)} + { + // DO NOTHING + } + +public: + const coco::Block *block(void) const { return _block; } + coco::Block *block(void) { return _block; } + +public: + const ann::Module *module(void) const { return _module.get(); } + +public: + /** + * @brief Return the set of bags that the current ANN subnet accesses + */ + std::set bags(void) const + { + std::set res; + + for (auto it = _operands.begin(); it != _operands.end(); ++it) + { + res.insert(it->first); + } + + return res; + } + +public: + template ann::OperandID addOperand(void) + { + return _module->operand()->create(ann::dtype()); + }; + + template ann::OperandID addOperand(const nncc::core::ADT::tensor::Shape &shape) + { + return _module->operand()->create(ann::dtype(), shape); + } + +public: + template ann::OperandID addOperand(const coco::FeatureObject *obj) + { + auto bag = obj->bag(); + assert(bag != nullptr); + + auto it = _operands.find(bag); + + if (it != _operands.end()) + { + return it->second; + } + + auto operand = addOperand(morph::nnapi::as_tensor_shape(obj->shape())); + _operands[obj->bag()] = operand; + return operand; + }; + + template ann::OperandID addOperand(const coco::KernelObject *obj) + { + auto bag = obj->bag(); + assert(bag != nullptr); + + auto it = _operands.find(bag); + + if (it != _operands.end()) + { + return it->second; + } + + auto operand = addOperand(morph::nnapi::as_tensor_shape(obj->shape())); + _operands[obj->bag()] = operand; + return operand; + }; + +public: + /// @brief Set scalar weight + template void setOperand(const ann::OperandID &id, const T &value) + { + static_assert(std::is_arithmetic::value, "T should be arithmetic"); + auto weight = _module->weight()->create(); + weight->fill(value); + _module->operand()->at(id)->weight(weight); + } + + /// @brief Set non-scalar weight + template void setOperand(const ann::OperandID &id, It beg, It end) + { + auto weight = _module->weight()->create(); + weight->fill(beg, end); + _module->operand()->at(id)->weight(weight); + } + +public: + void addOperation(ann::Operation::Code code, std::initializer_list inputs, + std::initializer_list outputs) + { + _module->operation()->create(code, inputs, outputs); + } + +public: + /** + * @brief Identify a sequence of coco::Bag * as subnet's inputs + * + * NOTE 1. This method takes input iterator over coco::Bag * values + * NOTE 2. All the identifyInputs class except the last one will be ignored if there are + * multiple identifyInputs calls + */ + template void identifyInputs(It beg, It end) + { + _inputs.clear(); + _module->input()->clear(); + + for (auto it = beg; it != end; ++it) + { + auto const bag = *it; + _inputs.emplace_back(*it); + _module->input()->emplace_back(_operands.at(bag)); + } + } + + template void identifyInputs(T &&values) + { + identifyInputs(std::begin(values), std::end(values)); + } + +public: + /** + * @brief Identify a sequence of coco::Bag * as subnet's outputs + * + * NOTE 1. This method takes input iterator over coco::Bag * values + * NOTE 2. All the identifyOutputs class except the last one will be ignored if there are + * multiple identifyOutputs calls + */ + template void identifyOutputs(It beg, It end) + { + _outputs.clear(); + _module->output()->clear(); + + for (auto it = beg; it != end; ++it) + { + auto const bag = *it; + _outputs.emplace_back(bag); + _module->output()->emplace_back(_operands.at(bag)); + } + } + + template void identifyOutputs(T &&values) + { + identifyOutputs(std::begin(values), std::end(values)); + } + +public: + coco::Bag *input(uint32_t n) const { return _inputs.at(n); } + coco::Bag *output(uint32_t n) const { return _outputs.at(n); } + +public: + /** + * @brief Return true if a given bag has an associated operand in ANN IR + */ + bool associated(coco::Bag *b) const { return _operands.find(b) != _operands.end(); } + + /** + * @brief Return operand ID associated with a given bag + * @note The behavior of operand(b) is defined only when associated(b) holds. + */ + ann::OperandID operand(coco::Bag *b) const + { + assert(associated(b)); + return _operands.at(b); + } + +private: + coco::Block *const _block; + std::unique_ptr _module; + +private: + std::vector _inputs; + std::vector _outputs; + +private: + /// @brief Operand ID assigned for each coco::Bag + std::map _operands; +}; + +#endif // __ANN_BINDER_H__ diff --git a/compiler/enco/core/src/ANN/Context.cpp b/compiler/enco/core/src/ANN/Context.cpp new file mode 100644 index 000000000..d4d1882fa --- /dev/null +++ b/compiler/enco/core/src/ANN/Context.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ANN/Context.h" + +#include + +ANNBinder *ANNContext::create(coco::Block *blk) +{ + auto mod = stdex::make_unique(); + auto obj = stdex::make_unique(blk, std::move(mod)); + auto ptr = obj.get(); + + _binders.emplace_back(std::move(obj)); + _map[blk] = ptr; + + return ptr; +} diff --git a/compiler/enco/core/src/ANN/Context.h b/compiler/enco/core/src/ANN/Context.h new file mode 100644 index 000000000..915651eb5 --- /dev/null +++ b/compiler/enco/core/src/ANN/Context.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_CONTEXT_H__ +#define __ANN_CONTEXT_H__ + +#include "ANN/Binder.h" + +#include +#include + +#include + +struct ANNContext +{ +public: + ANNBinder *create(coco::Block *blk); + +public: + uint32_t count(void) const { return _binders.size(); } + +public: + ANNBinder *nth(uint32_t n) { return _binders.at(n).get(); } + const ANNBinder *nth(uint32_t n) const { return _binders.at(n).get(); } + +public: + ANNBinder *find(const coco::Block *blk) const + { + auto it = _map.find(blk); + + if (it == _map.end()) + { + return nullptr; + } + + return it->second; + } + +private: + std::vector> _binders; + std::map _map; +}; + +#endif // __ANN_CONTEXT_H__ diff --git a/compiler/enco/core/src/ANN/Context.test.cpp b/compiler/enco/core/src/ANN/Context.test.cpp new file mode 100644 index 000000000..7fd26f30c --- /dev/null +++ b/compiler/enco/core/src/ANN/Context.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Context.h" + +#include + +#include + +namespace +{ +class ANNContextTest : public ::testing::Test +{ +public: + ANNContextTest() { m = coco::Module::create(); } + +public: + virtual ~ANNContextTest() = default; + +protected: + std::unique_ptr m; +}; +} + +TEST_F(ANNContextTest, constructor) +{ + ANNContext ann_ctx; + + ASSERT_EQ(ann_ctx.count(), 0); +} + +TEST_F(ANNContextTest, create) +{ + ANNContext ann_ctx; + + auto blk = m->entity()->block()->create(); + auto binder = ann_ctx.create(blk); + + ASSERT_NE(binder, nullptr); +} + +TEST_F(ANNContextTest, find) +{ + ANNContext ann_ctx; + + // CASE: Corresponding binder does not exist + { + auto blk = m->entity()->block()->create(); + ASSERT_EQ(ann_ctx.find(blk), nullptr); + } + + // CASE: Corresponding binder does exist + { + auto blk = m->entity()->block()->create(); + auto binder_created = ann_ctx.create(blk); + auto binder_found = ann_ctx.find(blk); + + ASSERT_EQ(binder_created, binder_found); + } +} diff --git a/compiler/enco/core/src/ANN/IR/DType.cpp b/compiler/enco/core/src/ANN/IR/DType.cpp new file mode 100644 index 000000000..7d4585a49 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/DType.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DType.h" + +namespace ann +{ + +template <> DType dtype(void) { return DType::S32; } +template <> DType dtype(void) { return DType::F32; } + +} // namespace ann diff --git a/compiler/enco/core/src/ANN/IR/DType.h b/compiler/enco/core/src/ANN/IR/DType.h new file mode 100644 index 000000000..b7583b09a --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/DType.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_DTYPE_H__ +#define __ANN_IR_DTYPE_H__ + +#include + +namespace ann +{ + +enum class DType +{ + UNK, + S32, + F32 +}; + +template DType dtype(void); + +} // namespace ann + +#endif // __ANN_IR_DTYPE_H__ diff --git a/compiler/enco/core/src/ANN/IR/DType.test.cpp b/compiler/enco/core/src/ANN/IR/DType.test.cpp new file mode 100644 index 000000000..8184ece9b --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/DType.test.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DType.h" + +#include + +TEST(ANN_IR_DTYPE, dtype) +{ + ASSERT_EQ(ann::dtype(), ann::DType::S32); + ASSERT_EQ(ann::dtype(), ann::DType::F32); +} diff --git a/compiler/enco/core/src/ANN/IR/InputList.h b/compiler/enco/core/src/ANN/IR/InputList.h new file mode 100644 index 000000000..51f0fd95a --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/InputList.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_INPUT_LIST_H__ +#define __ANN_IR_INPUT_LIST_H__ + +#include "ANN/IR/OperandID.h" + +#include + +namespace ann +{ + +using InputList = std::vector; + +} // namespace ann + +#endif // __ANN_IR_INPUT_LIST_H__ diff --git a/compiler/enco/core/src/ANN/IR/Module.h b/compiler/enco/core/src/ANN/IR/Module.h new file mode 100644 index 000000000..b443b4235 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Module.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_MODULE_H__ +#define __ANN_IR_MODULE_H__ + +#include "ANN/IR/WeightInventory.h" +#include "ANN/IR/OperandInventory.h" +#include "ANN/IR/OperationInventory.h" +#include "ANN/IR/InputList.h" +#include "ANN/IR/OutputList.h" + +namespace ann +{ + +class Module +{ +public: + Module() = default; + +public: + WeightInventory *weight(void) { return &_weight; } + const WeightInventory *weight(void) const { return &_weight; } + + OperandInventory *operand(void) { return &_operand; } + const OperandInventory *operand(void) const { return &_operand; } + + OperationInventory *operation(void) { return &_operation; } + const OperationInventory *operation(void) const { return &_operation; } + + InputList *input(void) { return &_input; } + const InputList *input(void) const { return &_input; } + + OutputList *output(void) { return &_output; } + const OutputList *output(void) const { return &_output; } + +private: + WeightInventory _weight; + OperandInventory _operand; + OperationInventory _operation; + InputList _input; + OutputList _output; +}; + +} // namespace ann + +#endif // __ANN_IR_MODULE_H__ diff --git a/compiler/enco/core/src/ANN/IR/Module.test.cpp b/compiler/enco/core/src/ANN/IR/Module.test.cpp new file mode 100644 index 000000000..4b946c875 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Module.test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Module.h" + +#include + +TEST(ANN_IR_MODULE, constructor) +{ + ann::Module m; + + ann::Module *mutable_ptr = &m; + const ann::Module *immutable_ptr = &m; + + ASSERT_NE(mutable_ptr->weight(), nullptr); + ASSERT_EQ(mutable_ptr->weight(), immutable_ptr->weight()); + + ASSERT_NE(mutable_ptr->operand(), nullptr); + ASSERT_EQ(mutable_ptr->operand(), immutable_ptr->operand()); + + ASSERT_NE(mutable_ptr->operation(), nullptr); + ASSERT_EQ(mutable_ptr->operation(), immutable_ptr->operation()); +} diff --git a/compiler/enco/core/src/ANN/IR/Operand.h b/compiler/enco/core/src/ANN/IR/Operand.h new file mode 100644 index 000000000..3b15ed739 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Operand.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OPERAND_H__ +#define __ANN_IR_OPERAND_H__ + +#include "ANN/IR/DType.h" +#include "ANN/IR/Weight.h" + +#include + +namespace ann +{ + +class Operand +{ +public: + virtual ~Operand() = default; + +public: + DType dtype(void) const { return _dtype; } + void dtype(const DType &dtype) { _dtype = dtype; } + + const Weight *weight(void) const { return _weight; } + void weight(const Weight *weight) { _weight = weight; } + +private: + DType _dtype = DType::UNK; + const Weight *_weight = nullptr; +}; + +} // namespace ann + +namespace ann +{ + +/** + * @brief Plain (non-qunatized) Scalar Operand + */ +struct ScalarOperand final : public Operand +{ +}; + +} // namespace ann + +namespace ann +{ + +/** + * @brief Plain (non-qunatized) Tensor Operand + */ +struct TensorOperand final : public Operand +{ +public: + TensorOperand(const nncc::core::ADT::tensor::Shape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + const nncc::core::ADT::tensor::Shape &shape(void) const { return _shape; } + +private: + nncc::core::ADT::tensor::Shape _shape; +}; + +} // namespace ann + +#endif // __ANN_IR_OPERAND_H__ diff --git a/compiler/enco/core/src/ANN/IR/Operand.test.cpp b/compiler/enco/core/src/ANN/IR/Operand.test.cpp new file mode 100644 index 000000000..98ac4ebd0 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Operand.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Operand.h" + +#include + +TEST(ANN_IR_SCALAR_OPERAND, constructor) +{ + const ann::ScalarOperand operand; + + ASSERT_EQ(operand.dtype(), ann::DType::UNK); + ASSERT_EQ(operand.weight(), nullptr); +} + +TEST(ANN_IR_TENSOR_OPERAND, constructor) +{ + const nncc::core::ADT::tensor::Shape shape{1, 2}; + const ann::TensorOperand operand{shape}; + + ASSERT_EQ(operand.dtype(), ann::DType::UNK); + ASSERT_EQ(operand.weight(), nullptr); + ASSERT_EQ(operand.shape(), shape); +} diff --git a/compiler/enco/core/src/ANN/IR/OperandID.h b/compiler/enco/core/src/ANN/IR/OperandID.h new file mode 100644 index 000000000..f1617aacb --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperandID.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OPERAND_ID_H__ +#define __ANN_IR_OPERAND_ID_H__ + +#include + +namespace ann +{ + +class OperandID +{ +public: + OperandID() : _value{0} + { + // DO NOTHING + } + +public: + explicit OperandID(uint32_t value) : _value{value} + { + // DO NOTHING + } + +public: + uint32_t value(void) const { return _value; } + +private: + uint32_t _value; +}; + +} // namespace ann + +#endif // __ANN_IR_OPERAND_ID_H__ diff --git a/compiler/enco/core/src/ANN/IR/OperandID.test.cpp b/compiler/enco/core/src/ANN/IR/OperandID.test.cpp new file mode 100644 index 000000000..04c23b9c8 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperandID.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OperandID.h" + +#include + +TEST(ANN_IR_OPERAND_ID, default_constructor) +{ + ann::OperandID id; + + ASSERT_EQ(id.value(), 0); +} + +TEST(ANN_IR_OPERAND_ID, explicit_constructor) +{ + ann::OperandID id{4}; + + ASSERT_EQ(id.value(), 4); +} diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.cpp b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp new file mode 100644 index 000000000..c7ad38811 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ANN/IR/OperandInventory.h" + +#include + +using stdex::make_unique; + +namespace ann +{ + +OperandID OperandInventory::create(const DType &dtype) +{ + uint32_t id = _operands.size(); + + auto operand = make_unique(); + operand->dtype(dtype); + + _operands.emplace_back(std::move(operand)); + + return OperandID{id}; +} + +OperandID OperandInventory::create(const DType &dtype, const nncc::core::ADT::tensor::Shape &shape) +{ + uint32_t id = _operands.size(); + + auto operand = make_unique(shape); + operand->dtype(dtype); + + _operands.emplace_back(std::move(operand)); + + return OperandID{id}; +} + +Operand *OperandInventory::at(const OperandID &id) { return _operands.at(id.value()).get(); } + +const Operand *OperandInventory::at(const OperandID &id) const +{ + return _operands.at(id.value()).get(); +} + +} // namespace ann diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.h b/compiler/enco/core/src/ANN/IR/OperandInventory.h new file mode 100644 index 000000000..23eb08119 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperandInventory.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OPERAND_INVENTORY_H__ +#define __ANN_IR_OPERAND_INVENTORY_H__ + +#include "ANN/IR/OperandID.h" +#include "ANN/IR/Operand.h" + +#include + +#include +#include + +namespace ann +{ + +class OperandInventory +{ +public: + OperandID create(const DType &); + OperandID create(const DType &, const nncc::core::ADT::tensor::Shape &); + +public: + template void each(Callable &&cb) const + { + for (uint32_t n = 0; n < _operands.size(); ++n) + { + cb(OperandID{n}, _operands.at(n).get()); + } + } + +public: + Operand *at(const OperandID &id); + const Operand *at(const OperandID &id) const; + +private: + std::vector> _operands; +}; + +} // namespace ann + +#endif // __ANN_IR_OPERAND_INVENTORY_H__ diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp b/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp new file mode 100644 index 000000000..e576752bc --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OperandInventory.h" + +#include + +TEST(ANN_IR_OPERAND_INVENTORY, constructor) +{ + ann::OperandInventory inven; + + uint32_t count = 0; + + inven.each([&](const ann::OperandID &, const ann::Operand *) { ++count; }); + + ASSERT_EQ(count, 0); +} diff --git a/compiler/enco/core/src/ANN/IR/Operation.def b/compiler/enco/core/src/ANN/IR/Operation.def new file mode 100644 index 000000000..68fd394cf --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Operation.def @@ -0,0 +1,17 @@ +#ifndef ANN_OPERATION +#error Define ANN_OPERATION first +#endif // ANN_OPERATION + +// ANN_OPERATION(TAG, ENUM_VALUE) +ANN_OPERATION(ADD, ANEURALNETWORKS_ADD) +ANN_OPERATION(MUL, ANEURALNETWORKS_MUL) +ANN_OPERATION(CONV_2D, ANEURALNETWORKS_CONV_2D) +ANN_OPERATION(DEPTHWISE_CONV_2D, ANEURALNETWORKS_DEPTHWISE_CONV_2D) +ANN_OPERATION(MAX_POOL_2D, ANEURALNETWORKS_MAX_POOL_2D) +ANN_OPERATION(AVG_POOL_2D, ANEURALNETWORKS_AVERAGE_POOL_2D) +ANN_OPERATION(RELU, ANEURALNETWORKS_RELU) +ANN_OPERATION(RELU6, ANEURALNETWORKS_RELU6) +ANN_OPERATION(PAD, ANEURALNETWORKS_PAD) +ANN_OPERATION(CONCAT, ANEURALNETWORKS_CONCATENATION) +ANN_OPERATION(SUB, ANEURALNETWORKS_SUB) +ANN_OPERATION(DIV, ANEURALNETWORKS_DIV) diff --git a/compiler/enco/core/src/ANN/IR/Operation.h b/compiler/enco/core/src/ANN/IR/Operation.h new file mode 100644 index 000000000..cacc2b794 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Operation.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OPERATION_H__ +#define __ANN_IR_OPERATION_H__ + +#include "ANN/IR/OperandID.h" + +#include +#include + +namespace ann +{ + +class Operation +{ +public: + enum class Code + { +#define ANN_OPERATION(TAG, VALUE) TAG, +#include "Operation.def" +#undef ANN_OPERATION + }; + +public: + Operation(const Code &code, std::initializer_list inputs, + std::initializer_list outputs) + : _code{code}, _inputs{inputs}, _outputs{outputs} + { + // DO NOTHING + } + +public: + const Code &code(void) const { return _code; } + const std::vector &inputs(void) const { return _inputs; } + const std::vector &outputs(void) const { return _outputs; } + +private: + Code _code; + std::vector _inputs; + std::vector _outputs; +}; + +} // namespace ann + +#endif // __ANN_IR_OPERATION_H__ diff --git a/compiler/enco/core/src/ANN/IR/Operation.test.cpp b/compiler/enco/core/src/ANN/IR/Operation.test.cpp new file mode 100644 index 000000000..d1b716733 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Operation.test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Operation.h" + +#include + +TEST(ANN_IR_OPERATION, constructor) +{ + ann::Operation op{ann::Operation::Code::CONV_2D, {}, {}}; + + ASSERT_EQ(op.code(), ann::Operation::Code::CONV_2D); + ASSERT_EQ(op.inputs().size(), 0); + ASSERT_EQ(op.outputs().size(), 0); +} diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.cpp b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp new file mode 100644 index 000000000..37d48c170 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OperationInventory.h" + +#include + +using stdex::make_unique; + +namespace ann +{ + +void OperationInventory::create(Operation::Code code, std::initializer_list inputs, + std::initializer_list outputs) +{ + _operations.emplace_back(make_unique(code, inputs, outputs)); +} + +} // namespace ann diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.h b/compiler/enco/core/src/ANN/IR/OperationInventory.h new file mode 100644 index 000000000..11c6be98a --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperationInventory.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OPERATION_INVENTORY_H__ +#define __ANN_IR_OPERATION_INVENTORY_H__ + +#include "ANN/IR/Operation.h" +#include "ANN/IR/OperandID.h" + +#include + +#include + +namespace ann +{ + +class OperationInventory +{ +public: + void create(Operation::Code code, std::initializer_list inputs, + std::initializer_list outputs); + +public: + uint32_t count(void) const { return _operations.size(); } + +public: + const Operation *at(uint32_t n) const { return _operations.at(n).get(); } + +private: + std::vector> _operations; +}; + +} // namespace ann + +#endif // __ANN_IR_OPERATION_INVENTORY_H__ diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp b/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp new file mode 100644 index 000000000..0e91a4f53 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OperationInventory.h" + +#include + +TEST(ANN_IR_OPERATION_INVENTORY, constructor) +{ + ann::OperationInventory inven; + + ASSERT_EQ(inven.count(), 0); +} + +TEST(ANN_IR_OPERATION_INVENTORY, create) +{ + ann::OperationInventory inven; + + inven.create(ann::Operation::Code::CONV_2D, {ann::OperandID{0}}, {ann::OperandID{3}}); + + ASSERT_EQ(inven.count(), 1); + ASSERT_NE(inven.at(0), nullptr); + + ASSERT_EQ(inven.at(0)->code(), ann::Operation::Code::CONV_2D); + ASSERT_EQ(inven.at(0)->inputs().size(), 1); + ASSERT_EQ(inven.at(0)->outputs().size(), 1); +} diff --git a/compiler/enco/core/src/ANN/IR/OutputList.h b/compiler/enco/core/src/ANN/IR/OutputList.h new file mode 100644 index 000000000..2dd891138 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/OutputList.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_OUTPUT_LIST_H__ +#define __ANN_IR_OUTPUT_LIST_H__ + +#include "ANN/IR/OperandID.h" + +#include + +namespace ann +{ + +using OutputList = std::vector; + +} // namespace ann + +#endif // __ANN_IR_OUTPUT_LIST_H__ diff --git a/compiler/enco/core/src/ANN/IR/Weight.h b/compiler/enco/core/src/ANN/IR/Weight.h new file mode 100644 index 000000000..062aa6d19 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Weight.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANN_IR_WEIGHT_H__ +#define __ANN_IR_WEIGHT_H__ + +#include + +#include +#include + +namespace ann +{ + +class Weight +{ +public: + const uint8_t *base(void) const { return _buffer.data(); } + uint32_t size(void) const { return _buffer.size(); } + +public: + template void fill(const T &value) + { + static_assert(std::is_arithmetic::value, "T should be arithmetic"); + _buffer.clear(); + + auto arr = reinterpret_cast(&value); + + for (uint32_t b = 0; b < sizeof(T); ++b) + { + _buffer.emplace_back(arr[b]); + } + } + + template void fill(It beg, It end) + { + _buffer.clear(); + + for (auto it = beg; it != end; ++it) + { + const auto value = *it; + auto arr = reinterpret_cast(&value); + + for (uint32_t b = 0; b < sizeof(value); ++b) + { + _buffer.emplace_back(arr[b]); + } + } + } + +private: + std::vector _buffer; +}; + +} // namespace ann + +#endif // __ANN_IR_WEIGHT_H__ diff --git a/compiler/enco/core/src/ANN/IR/Weight.test.cpp b/compiler/enco/core/src/ANN/IR/Weight.test.cpp new file mode 100644 index 000000000..53532114c --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/Weight.test.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Weight.h" + +#include + +TEST(ANN_IR_WEIGHT, constructor) +{ + ann::Weight weight; + + ASSERT_EQ(weight.base(), nullptr); + ASSERT_EQ(weight.size(), 0); +} + +TEST(ANN_IR_WEIGHT, fill_scalar_int) +{ + ann::Weight weight; + + weight.fill(3); + + ASSERT_NE(weight.base(), nullptr); + ASSERT_EQ(*reinterpret_cast(weight.base()), 3); +} + +TEST(ANN_IR_WEIGHT, fill_vector_float) +{ + std::vector values{1.0f, 2.0f}; + + ann::Weight weight; + + weight.fill(values.begin(), values.end()); + + ASSERT_NE(weight.base(), nullptr); + + auto arr = reinterpret_cast(weight.base()); + + ASSERT_FLOAT_EQ(arr[0], 1.0f); + ASSERT_FLOAT_EQ(arr[1], 2.0f); +} diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.cpp b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp new file mode 100644 index 000000000..d8809ac08 --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WeightInventory.h" + +#include + +using stdex::make_unique; + +namespace ann +{ + +Weight *WeightInventory::create(void) +{ + auto hnd = make_unique(); + auto ptr = hnd.get(); + _weights.push_back(std::move(hnd)); + return ptr; +} + +} // namespace ann diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.h b/compiler/enco/core/src/ANN/IR/WeightInventory.h new file mode 100644 index 000000000..fd166837f --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/WeightInventory.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __WEIGHT_INVENTORY_H__ +#define __WEIGHT_INVENTORY_H__ + +#include "ANN/IR/Weight.h" + +#include + +namespace ann +{ + +class WeightInventory +{ +public: + Weight *create(void); + +private: + std::vector> _weights; +}; + +} // namespace ann + +#endif // __WEIGHT_INVENTORY_H__ diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp b/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp new file mode 100644 index 000000000..143bdfddf --- /dev/null +++ b/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WeightInventory.h" + +#include + +TEST(ANN_IR_WEIGHT_INVENTORY, create) +{ + ann::WeightInventory inven; + + auto weight = inven.create(); + + ASSERT_EQ(weight->base(), nullptr); + ASSERT_EQ(weight->size(), 0); +} diff --git a/compiler/enco/core/src/AsmCode.cpp b/compiler/enco/core/src/AsmCode.cpp new file mode 100644 index 000000000..70d6f30b3 --- /dev/null +++ b/compiler/enco/core/src/AsmCode.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AsmCode.h" + +namespace enco +{ + +void AsmCode::dump(std::ostream &os) const +{ + os << ".section .rodata" << std::endl; + os << ".global " << _varname << std::endl; + // Please refer to https://www.sourceware.org/binutils/docs/as/Type.html#Type for details + os << ".type " << _varname << ", STT_OBJECT" << std::endl; + os << ".align " << 4 << std::endl; + os << _varname << ":" << std::endl; + os << ".incbin " << '"' << _filename << '"' << std::endl; +} + +} // namespace enco diff --git a/compiler/enco/core/src/AsmCode.h b/compiler/enco/core/src/AsmCode.h new file mode 100644 index 000000000..c43892888 --- /dev/null +++ b/compiler/enco/core/src/AsmCode.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_ASM_CODE_H__ +#define __ENCO_ASM_CODE_H__ + +#include +#include + +namespace enco +{ + +class AsmCode +{ +public: + AsmCode(const std::string &filename, const std::string &varname) + : _filename{filename}, _varname{varname} + { + // DO NOTHING + } + +public: + void dump(std::ostream &) const; + +private: + std::string _filename; + std::string _varname; +}; + +} // namespace enco + +static inline std::ostream &operator<<(std::ostream &os, const enco::AsmCode &code) +{ + code.dump(os); + return os; +} + +#endif // __ENCO_ASM_CODE_H__ diff --git a/compiler/enco/core/src/Backend.cpp b/compiler/enco/core/src/Backend.cpp new file mode 100644 index 000000000..d4bec7447 --- /dev/null +++ b/compiler/enco/core/src/Backend.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "enco/Backend.h" + +#include "IRValidator.h" + +#include "Session.h" +#include "Pipeline.h" + +#include "Code.h" +#include "AsmCode.h" +#include "CppCode.h" + +#include "Transforms/Duplicate.h" +#include "Transforms/FeatureUnification.h" +#include "Transforms/AvgPoolLowering.h" +#include "Transforms/IntrinsicSelection.h" +#include "Transforms/DataLayoutConversion.h" +#include "Transforms/IndirectCopyElimination.h" +#include "Transforms/IdenticalObjectReduction.h" +#include "Transforms/DuplicatedObjectReduction.h" +#include "Transforms/DeadObjectElimination.h" +#include "Transforms/ConstantFolding.h" +#include "Transforms/CopyLowering.h" +#include "Transforms/ConcatLowering.h" +#include "Transforms/FreeInstrElimination.h" +#include "Transforms/FreeOpElimination.h" +#include "Transforms/DeadBagElimination.h" +#include "Transforms/Optimizations.h" +#include "Transforms/Split.h" +#include "Transforms/GlobalDataGeneration.h" + +#include + +#include +#include +#include + +using stdex::make_unique; +using namespace enco; + +namespace +{ + +// has_inout_bag(m) returns true if there is a pair of coco::Input and coco::Output that share +// the same bag as their backing storage +inline bool has_inout_bag(const coco::Module *m) +{ + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + if (bag->isInput() && bag->isOutput()) + { + return true; + } + } + return false; +} + +class BackendImpl final : public enco::Backend +{ +public: + BackendImpl(const std::string &prefix) : _prefix{prefix} + { + // DO NOTHING + } + +public: + void compile(coco::Module *m, coco::Data *d) override; + +private: + std::string _prefix; +}; + +void BackendImpl::compile(coco::Module *m, coco::Data *d) +{ + auto sess = make_session(m, d); + + // validate if IR from frontend is correct + assert(validate(code(sess))); + + enco::Pipeline pipeline; + + // Configure pipeline + + // As explained below, the current implementation does not work if there is a pair of input/output + // that share the same bag as their underlying bag. + // + // BagDuplicationPass creates a copy of such bags in order to eliminate such a pair. + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + // Insert data ordering if necessary + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + // Eliminate dead object + // + // NOTE Dead Object Elimination (DOE) is performed before Copy lowering + // in order to reduce compilation overhead. + pipeline.append(make_unique()); + // Lower Copy as Shuffle + pipeline.append(make_unique()); + // Lower ConcatF as Shuffle if it is not delegated to NNAPI yet + pipeline.append(make_unique()); + pipeline.append(make_unique()); + pipeline.append(make_unique()); + // NOTE Free Op Elimination should be applied after Free Instr Elimination + // - Free Instr Elimination may generate additional free Op(s) + pipeline.append(make_unique()); + pipeline.append(make_unique()); + // Split instructions into a set of phases (each block serves as a phase) + pipeline.append(make_unique()); + + // Apply transforms in the pipeline + for (uint32_t n = 0; n < pipeline.size(); ++n) + { + const auto &pass = pipeline.at(n); + + pass.run(sess); + } + + // The current implementation will assign memory region for each bag as follows: + // Bind input bag to the region provided by Network_input_bind + // Bind output bag to the region provided by Network_output_bind + // Bind intermediate bag to the region allocated during execution + // + // Note that this scheme does not work if there is a pair of input/output + // that share the same bag as their underlying bag + assert(!has_inout_bag(code(sess)->module())); + + const std::string data_var = "data"; + const std::string data_filename = _prefix + ".bin"; + + // Generate 'bin' file + { + std::ofstream ofs{data_filename, std::ios::binary}; + generate_global_data(ofs, code(sess)); + } + + // Generate 'embed.S' file + { + std::ofstream ofs{_prefix + ".embed.S"}; + ofs << AsmCode{data_filename, data_var}; + } + + // TODO Run various transforms over enco::Code + + std::ofstream ofs{_prefix + ".cpp"}; + ofs << CppCode{data_var, code(sess)} << std::endl; +} + +} // namespace enco + +#include + +std::unique_ptr make_backend(const cmdline::View &cmdline) +{ + return make_unique<::BackendImpl>(cmdline.at(0)); +} diff --git a/compiler/enco/core/src/Code.h b/compiler/enco/core/src/Code.h new file mode 100644 index 000000000..91756d5f8 --- /dev/null +++ b/compiler/enco/core/src/Code.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CODE_H__ +#define __ENCO_CODE_H__ + +#include "ANN/Context.h" + +#include +#include + +namespace enco +{ + +struct Code +{ +public: + Code(coco::Module *module, coco::Data *data) : _module{module}, _data{data} + { + // DO NOTHING + } + +public: + coco::Module *module(void) const { return _module; } + coco::Data *data(void) const { return _data; } + +private: + coco::Module *const _module; + coco::Data *const _data; +}; + +} // namespace enco + +#endif // __ENCO_CODE_H__ diff --git a/compiler/enco/core/src/Code.test.cpp b/compiler/enco/core/src/Code.test.cpp new file mode 100644 index 000000000..8e96e4751 --- /dev/null +++ b/compiler/enco/core/src/Code.test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Code.h" + +#include + +TEST(CODE, constructor) +{ + auto m = coco::Module::create(); + auto d = coco::Data::create(); + + enco::Code code{m.get(), d.get()}; + + ASSERT_EQ(code.module(), m.get()); + ASSERT_EQ(code.data(), d.get()); +} diff --git a/compiler/enco/core/src/CodeIndex.h b/compiler/enco/core/src/CodeIndex.h new file mode 100644 index 000000000..7f2da6463 --- /dev/null +++ b/compiler/enco/core/src/CodeIndex.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CODE_INDEX_H__ +#define __CODE_INDEX_H__ + +#include +#include + +/** + * @brief A CodeIndex denotes the index of instruction inside the whole module + */ +class CodeIndex +{ +public: + CodeIndex() = default; + +public: + CodeIndex(const coco::BlockIndex &blk_ind, const coco::InstrIndex &ins_ind) + : _blk_ind{blk_ind}, _ins_ind{ins_ind} + { + } + +public: + const coco::BlockIndex &block(void) const { return _blk_ind; } + const coco::InstrIndex &instr(void) const { return _ins_ind; } + +private: + coco::BlockIndex _blk_ind; + coco::InstrIndex _ins_ind; +}; + +static inline coco::BlockIndex block_index(const coco::Block *blk) +{ + if (blk == nullptr) + { + return coco::BlockIndex{}; + } + + return blk->index(); +} + +static inline CodeIndex code_index(const coco::Instr *ins) +{ + return CodeIndex{block_index(ins->parent()), ins->index()}; +} + +static inline bool operator<(const CodeIndex &lhs, const CodeIndex &rhs) +{ + if (lhs.block() < rhs.block()) + { + return true; + } + + if (lhs.block().value() > rhs.block().value()) + { + return false; + } + + return lhs.instr() < rhs.instr(); +} + +#endif // __CODE_INDEX_H__ diff --git a/compiler/enco/core/src/CppCode.cpp b/compiler/enco/core/src/CppCode.cpp new file mode 100644 index 000000000..aa5ef3156 --- /dev/null +++ b/compiler/enco/core/src/CppCode.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CppCode.h" + +#include "Transforms/GlobalDataGeneration.h" +#include "Transforms/Split.h" + +#include "CppGen/MemoryContext.h" + +#include "CppGen/Host.h" +#include "CppGen/Subnet.h" + +#include "Dims.h" + +#include +#include + +#include +#include +#include +#include + +namespace +{ + +struct SubnetInfo +{ + std::string struct_name; + /// @brief The field name (in this subnet struct) of ANeuralNetworksCompilation value + std::string compilation_field; + + /// @brief The field name (in Network struct) for this subnet + std::string field_name; +}; + +struct NetworkStruct +{ + pp::LinearDocument def; +}; + +struct InvokeFunction +{ + pp::LinearDocument head; + pp::LinearDocument body; + pp::LinearDocument tail{pp::LinearDocument::Direction::Reverse}; + +public: + /** @brief Create a (fresh) local variable */ + std::string local(void) { return pp::fmt("v_", ++_var_count); } + +private: + uint32_t _var_count = 0; +}; + +/** + * @brief Enumerate a set of Bag accessed by a given instruction + * + * Supported instruction: + * "Shuffle" + */ +class AccessedBagAccumulator : public coco::Instr::Visitor +{ +public: + AccessedBagAccumulator(std::set *out) : _out{out} + { + // Validate "out" + assert(_out != nullptr); + } + +public: + void visit(const coco::Shuffle *shuffle) override + { + assert(shuffle->from() != nullptr); + assert(shuffle->into() != nullptr); + + _out->insert(shuffle->from()); + _out->insert(shuffle->into()); + } + +private: + std::set *_out; +}; + +/** + * @brief Return a set of bags that SHOULD have a host allocation + */ +std::set hosted(const enco::Code *code) +{ + std::set res; + + auto m = code->module(); + auto ann_ctx = enco::SubnetManager::context(m); + + for (auto blk = m->block()->head(); blk; blk = blk->next()) + { + if (auto ann_binder = ann_ctx->find(blk)) + { + // Case: The current block is ANN-compatible + + // Each ANN input SHOULD have a corresponding host allocation + for (uint32_t n = 0; n < ann_binder->module()->input()->size(); ++n) + { + res.insert(ann_binder->input(n)); + } + + // Each ANN output SHOULD have a corresponding host allocation + for (uint32_t n = 0; n < ann_binder->module()->output()->size(); ++n) + { + res.insert(ann_binder->output(n)); + } + } + else + { + // Every bag that ANN-incompatible block accesses SHOULD have a corresponding host allocation + AccessedBagAccumulator acc{&res}; + + for (auto ins = blk->instr()->head(); ins; ins = ins->next()) + { + ins->accept(acc); + } + } + } + + return res; +} +} // namespace + +namespace enco +{ + +void CppCode::dump(std::ostream &os) const +{ + auto m = _code->module(); + auto d = _code->data(); + auto ann_ctx = enco::SubnetManager::context(m); + + NetworkStruct network; + InvokeFunction invoke; + pp::LinearDocument internal; + + auto data_exp = [this](const GlobalOffset &off) { return pp::fmt(_varname, " + ", off); }; + + // Record the subnet information + std::map subnet_ctx; + + /** + * Create a struct for each android NN network of the following form: + * + * struct [Name] + * { + * ... + * + * [Name]() // constructor + * { + * ... + * } + * + * ~[Name]() // destructor + * { + * ... + * } + * }; + * + */ + for (uint32_t n = 0; n < ann_ctx->count(); ++n) + { + SubnetStructBuilder builder; + + auto subnet_binder = ann_ctx->nth(n); + auto subnet_struct_name = pp::fmt("Subnet_", subnet_ctx.size()); + auto subnet_field_name = pp::fmt("_subnet_", subnet_ctx.size()); + + // Create global data variable + auto emit_weight = [&](const ann::OperandID &, const ann::Operand *info) { + if (info->weight()) + { + auto size = info->weight()->size(); + auto off = enco::GlobalData::data_offset(info); + auto base_exp = pp::fmt("reinterpret_cast(", data_exp(off), ")"); + auto size_exp = pp::fmt(size); + + builder.expr(info, base_exp, size_exp); + } + }; + subnet_binder->module()->operand()->each(emit_weight); + + auto subnet_struct_content = builder.build(subnet_binder); + + // Emit C++ declaration + internal.append("struct ", subnet_struct_name); + internal.append("{"); + internal.indent(); + + internal.append(subnet_struct_content->def()); + + internal.append(subnet_struct_name, "()"); + internal.append("{"); + internal.indent(); + internal.append(subnet_struct_content->ctor()); + internal.unindent(); + internal.append("}"); + + internal.append("~", subnet_struct_name, "()"); + internal.append("{"); + internal.indent(); + internal.append(subnet_struct_content->dtor()); + internal.unindent(); + internal.append("}"); + + internal.unindent(); + internal.append("};"); + + // Declare subnet field + network.def.append(subnet_struct_name, " ", subnet_field_name, ";"); + + // Update subnet context + SubnetInfo subnet_info; + + subnet_info.struct_name = subnet_struct_name; + subnet_info.compilation_field = subnet_struct_content->compilation(); + subnet_info.field_name = subnet_field_name; + + assert(subnet_ctx.find(subnet_binder) == subnet_ctx.end()); + subnet_ctx[subnet_binder] = subnet_info; + } + + MemoryContext mem; + + // Set dedicated memory region for network inputs + for (uint32_t n = 0; n < m->input()->size(); ++n) + { + mem.base(m->input()->at(n)->bag(), pp::fmt("net->inputs[", n, "].ptr")); + mem.size(m->input()->at(n)->bag(), pp::fmt("net->inputs[", n, "].len")); + } + + // Set dedicated memory region for network outputs + for (uint32_t n = 0; n < m->output()->size(); ++n) + { + mem.base(m->output()->at(n)->bag(), pp::fmt("net->outputs[", n, "].ptr")); + mem.size(m->output()->at(n)->bag(), pp::fmt("net->outputs[", n, "].len")); + } + + // Set dedicated memory region for constant weight values + // TODO Support non-constant bags with initial values + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + if (!d->allocated(bag)) + { + // Skip if no weight exists + continue; + } + + // TODO Support non-float(fp32) weight + auto offset = enco::GlobalData::data_offset(bag); + + auto base_expr = data_exp(offset); + auto size_expr = pp::fmt(bag->size() * sizeof(float)); + + mem.base(bag, base_expr); + mem.size(bag, size_expr); + } + + // Set dedicated memory reigion for intermediate buffer(s) + for (const auto &bag : hosted(_code)) + { + // Skip if a bag is already allocated + if (mem.member(bag)) + { + continue; + } + + auto name = invoke.local(); + + invoke.head.append("auto ", name, " = new uint8_t[", bag->size() * sizeof(float), "];"); + invoke.tail.append("delete[] ", name, ";"); + + mem.base(bag, name); + mem.size(bag, pp::fmt(bag->size() * sizeof(float))); + } + + // Create Code Block Builder + SubnetBlockCompiler subnet_compiler{mem}; + + for (auto it = subnet_ctx.begin(); it != subnet_ctx.end(); ++it) + { + // Specify how to access ANeuralNetworksCompilation + const auto &info = it->second; + subnet_compiler.bind(it->first, pp::fmt("net->", info.field_name, ".", info.compilation_field)); + } + + HostBlockCompiler host_compiler{mem}; + + for (auto blk = m->block()->head(); blk; blk = blk->next()) + { + invoke.body.append("{"); + invoke.body.indent(); + + if (auto binder = ann_ctx->find(blk)) + { + // Generate code that invokes Android NN sub-network + auto lines = subnet_compiler.compile(binder); + invoke.body.append(*lines); + } + else + { + // Generate code on-the-fly for Android NN-incompatible blocks + auto lines = host_compiler.compile(blk); + invoke.body.append(*lines); + } + + invoke.body.unindent(); + invoke.body.append("}"); + } + + // + // Generate full C++ source code with code snippet + // + const std::string name{"Network"}; + + pp::LinearDocument includes; + { + // Include Android NN API header + includes.append("#include "); + includes.append(); + + includes.append("#include "); + includes.append("#include "); + includes.append("#include "); + } + + pp::LinearDocument net_def; + { + net_def.append("struct ", name, " {"); + net_def.indent(); + net_def.append("struct Shape { uint32_t rank; const uint32_t *dims; };"); + net_def.append("struct Input {"); + net_def.indent(); + net_def.append("const char *name;"); + net_def.append("const uint8_t *ptr;"); + net_def.append("unsigned len;"); + net_def.append("Shape shape;"); + net_def.unindent(); + net_def.append("};"); + net_def.append("struct Output {"); + net_def.indent(); + net_def.append("const char *name;"); + net_def.append("uint8_t *ptr;"); + net_def.append("unsigned len;"); + net_def.append("Shape shape;"); + net_def.unindent(); + net_def.append("};"); + net_def.append(); + net_def.append(name, "();"); + net_def.append("~", name, "();"); + + net_def.append(); + net_def.append(network.def); + net_def.append(); + + net_def.append("std::arrayinput()->size(), "> inputs;"); + net_def.append("std::arrayoutput()->size(), "> outputs;"); + + net_def.unindent(); + net_def.append("};"); + } + + pp::LinearDocument net_ctor; + { + net_ctor.append("Network::Network() {"); + net_ctor.indent(); + + // Initialize input metadata + for (uint32_t n = 0; n < m->input()->size(); ++n) + { + auto input = m->input()->at(n); + auto dims = as_dims(input->shape()); + + auto name_off = enco::GlobalData::name_offset(input); + auto name_exp = pp::fmt("reinterpret_cast(", data_exp(name_off), ")"); + auto dims_off = enco::GlobalData::dims_offset(input); + auto dims_exp = pp::fmt("reinterpret_cast(", data_exp(dims_off), ")"); + + net_ctor.append("inputs.at(", n, ").name = ", name_exp, ";"); + net_ctor.append("inputs.at(", n, ").shape.rank = ", dims.size(), ";"); + net_ctor.append("inputs.at(", n, ").shape.dims = ", dims_exp, ";"); + } + + // Initialize output metadata + for (uint32_t n = 0; n < m->output()->size(); ++n) + { + auto output = m->output()->at(n); + auto dims = as_dims(output->shape()); + + auto name_off = enco::GlobalData::name_offset(output); + auto name_exp = pp::fmt("reinterpret_cast(", data_exp(name_off), ")"); + auto dims_off = enco::GlobalData::dims_offset(output); + auto dims_exp = pp::fmt("reinterpret_cast(", data_exp(dims_off), ")"); + + net_ctor.append("outputs.at(", n, ").name = ", name_exp, ";"); + net_ctor.append("outputs.at(", n, ").shape.rank = ", dims.size(), ";"); + net_ctor.append("outputs.at(", n, ").shape.dims = ", dims_exp, ";"); + } + + // TODO Implement this + net_ctor.unindent(); + net_ctor.append("}"); + } + + pp::LinearDocument net_dtor; + { + net_dtor.append("Network::~Network() {"); + net_dtor.indent(); + // TODO Implement this + net_dtor.unindent(); + net_dtor.append("}"); + } + + pp::LinearDocument source; + + source.append(includes); + source.append(); + source.append("extern uint8_t ", _varname, "[];"); + source.append(); + + source.append("namespace"); + source.append("{"); + source.append(internal); + source.append("} // namespace"); + source.append(); + source.append(net_def); + source.append(); + source.append(net_ctor); + source.append(); + source.append(net_dtor); + + source.append(); + source.append(name, " *", name, "_construct() { return new ", name, "{}; }"); + source.append("void ", name, "_destruct(", name, " *net) { delete net; }"); + + source.append(); + + // Emit Network_input_count function + source.append("unsigned ", name, "_input_count(const ", name, " *net) {"); + source.indent(); + source.append("return net->inputs.size();"); + source.unindent(); + source.append("}"); + + source.append(); + + // Emit Network_input_name function + source.append("const char *", name, "_input_name(const ", name, " *net, unsigned n) {"); + source.indent(); + source.append("return net->inputs.at(n).name;"); + source.unindent(); + source.append("}"); + + // Emit Network_input_rank function + source.append("unsigned ", name, "_input_rank(const ", name, " *net, unsigned n) {"); + source.indent(); + source.append("return net->inputs.at(n).shape.rank;"); + source.unindent(); + source.append("}"); + + // Emit Network_input_dim function + source.append("unsigned ", name, "_input_dim(const ", name, " *net, unsigned n, unsigned axe)"); + source.append("{"); + source.indent(); + source.append("return net->inputs.at(n).shape.dims[axe];"); + source.unindent(); + source.append("}"); + + // Emit Network_input_bind function + source.append("void ", name, "_input_bind(", name, + " *net, unsigned n, const void *ptr, unsigned len) {"); + source.indent(); + source.append("net->inputs.at(n).ptr = reinterpret_cast(ptr);"); + source.append("net->inputs.at(n).len = len;"); + source.unindent(); + source.append("}"); + + source.append(); + + // Emit Network_output_count function + source.append("unsigned ", name, "_output_count(const ", name, " *net) {"); + source.indent(); + source.append("return net->outputs.size();"); + source.unindent(); + source.append("}"); + + source.append(); + + // Emit Network_output_name function + source.append("const char *", name, "_output_name(const ", name, " *net, unsigned n) {"); + source.indent(); + source.append("return net->outputs.at(n).name;"); + source.unindent(); + source.append("}"); + + // Emit Network_output_rank function + source.append("unsigned ", name, "_output_rank(const ", name, " *net, unsigned n) {"); + source.indent(); + source.append("return net->outputs.at(n).shape.rank;"); + source.unindent(); + source.append("}"); + + // Emit Network_output_dim function + source.append("unsigned ", name, "_output_dim(const ", name, " *net, unsigned n, unsigned axe)"); + source.append("{"); + source.indent(); + source.append("return net->outputs.at(n).shape.dims[axe];"); + source.unindent(); + source.append("}"); + + // Emit Network_output_bind function + source.append("void ", name, "_output_bind(", name, + " *net, unsigned n, void *ptr, unsigned len) {"); + source.indent(); + source.append("net->outputs.at(n).ptr = reinterpret_cast(ptr);"); + source.append("net->outputs.at(n).len = len;"); + source.unindent(); + source.append("}"); + + source.append(); + + source.append("void ", name, "_invoke(", name, " *net) {"); + source.indent(); + source.append(invoke.head); + source.append(invoke.body); + source.append(invoke.tail); + source.unindent(); + source.append("}"); + + os << source; +} + +} // namespace enco diff --git a/compiler/enco/core/src/CppCode.h b/compiler/enco/core/src/CppCode.h new file mode 100644 index 000000000..c52ea1d5d --- /dev/null +++ b/compiler/enco/core/src/CppCode.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CPP_CODE_H__ +#define __ENCO_CPP_CODE_H__ + +#include "Code.h" + +#include + +namespace enco +{ + +class CppCode +{ +public: + CppCode(const std::string &varname, const Code *code) : _varname{varname}, _code{code} + { + // DO NOTHING + } + +public: + void dump(std::ostream &) const; + +private: + const std::string _varname; + const Code *_code; +}; + +} // namespace enco + +static inline std::ostream &operator<<(std::ostream &os, const enco::CppCode &code) +{ + code.dump(os); + return os; +} + +#endif // __ENCO_CPP_CODE_H__ diff --git a/compiler/enco/core/src/CppGen/Host.cpp b/compiler/enco/core/src/CppGen/Host.cpp new file mode 100644 index 000000000..37e0583d7 --- /dev/null +++ b/compiler/enco/core/src/CppGen/Host.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Host.h" + +#include + +#include + +#include +#include + +namespace +{ + +/** + * @brief Data transfer between flat arrays + * + * Transfer(from, into) denotes the following C code: + * dst[into] = src[from]; + */ +class Transfer +{ +public: + Transfer(uint32_t from, uint32_t into) : _from{from}, _into{into} + { + // DO NOTHING + } + +public: + uint32_t from(void) const { return _from; } + uint32_t into(void) const { return _into; } + +private: + uint32_t _from; + uint32_t _into; +}; + +using TransferSequence = std::vector; + +/** + * @brief Convert Shuffle instruction as a sequence of data transfer + */ +TransferSequence as_transfer_sequence(const coco::Shuffle *shuffle) +{ + TransferSequence seq; + + for (const auto &dst : shuffle->range()) + { + const auto src = shuffle->at(dst); + seq.emplace_back(src.value(), dst.value()); + } + + return seq; +} + +/** + * Given a sequence of N data transfers, + * find_loop tries to compute count, src_step, dst_step that satisfies + * the following properties: + * + * First, N should be a multiple of count. + * Below we refer to that multiplier as 'window' (= N / count) + * + * Second, + * for all n in [0, count), + * for all k in [0, window), + * from[n * window + k] == from[k] + src_step, and + * into[n * window + k] == into[k] + dst_step + */ +bool find_loop(TransferSequence::const_iterator beg, TransferSequence::const_iterator end, + uint32_t *p_count, uint32_t *p_src_step, uint32_t *p_dst_step) +{ + assert(p_count != nullptr); + assert(p_src_step != nullptr); + assert(p_dst_step != nullptr); + + const uint32_t size = end - beg; + + for (uint32_t window = 1; window <= size; ++window) + { + if (size % window != 0) + { + continue; + } + + auto src_step_at = [&beg, window](uint32_t n) { + return (beg + n)->from() - (beg + n - window)->from(); + }; + + auto dst_step_at = [&beg, window](uint32_t n) { + return (beg + n)->into() - (beg + n - window)->into(); + }; + + const uint32_t count = size / window; + const uint32_t src_step = src_step_at(window); + const uint32_t dst_step = dst_step_at(window); + + bool consistent = true; + + for (uint32_t n = window + 1; n < size; ++n) + { + if ((src_step_at(n) != src_step) || (dst_step_at(n) != dst_step)) + { + consistent = false; + break; + } + } + + if (consistent) + { + *p_count = count; + *p_src_step = src_step; + *p_dst_step = dst_step; + return true; + } + } + + return false; +} + +/** + * @brief Single transfer loop (a triple of count, source step, detination step) + */ +class TransferLoop +{ +public: + class Step + { + public: + Step(uint32_t src, uint32_t dst) : _src{src}, _dst{dst} + { + // DO NOTHING + } + + public: + uint32_t src(void) const { return _src; } + uint32_t dst(void) const { return _dst; } + + private: + uint32_t _src; + uint32_t _dst; + }; + +public: + TransferLoop(uint32_t count, uint32_t src_step, uint32_t dst_step) + : _count{count}, _step{src_step, dst_step} + { + // DO NOTHING + } + +public: + uint32_t count(void) const { return _count; } + const Step &step(void) const { return _step; } + +private: + uint32_t _count; + Step _step; +}; + +/** + * @brief Nested transfer loops + */ +using TransferNest = std::vector; + +/** + * @brief Construct nested transfer loop-nest that correponds to a given Shuffle instruction + */ +TransferNest as_nest(const TransferSequence &seq) +{ + TransferNest nest; + + auto beg = seq.begin(); + auto end = seq.end(); + + uint32_t window = end - beg; + uint32_t count = 0; + uint32_t src_step = 0; + uint32_t dst_step = 0; + + while ((window > 1) && find_loop(beg, end, &count, &src_step, &dst_step)) + { + assert(window % count == 0); + + window /= count; + end = beg + window; + + nest.emplace_back(count, src_step, dst_step); + } + + return nest; +}; + +uint32_t loop_count(const TransferNest &nest) +{ + uint32_t count = 1; + + for (const auto &loop : nest) + { + count *= loop.count(); + } + + return count; +}; + +class InstrPrinter : public coco::Instr::Visitor +{ +public: + InstrPrinter(const enco::MemoryContext &mem) : _mem(mem) + { + // DO NOTHING + } + +private: + pp::LinearDocument visit(const coco::Shuffle *shuffle) override + { + auto from = shuffle->from(); + auto into = shuffle->into(); + + // + // Analyze 'Shuffle' pattern, and convert it as nested loops + // + auto tseq = as_transfer_sequence(shuffle); + auto nest = as_nest(tseq); + assert(tseq.size() % loop_count(nest) == 0); + uint32_t window = tseq.size() / loop_count(nest); + + // + // Generate loop body + // + pp::EnclosedDocument loop_body; + + auto var_at = [](uint32_t lv) { return pp::fmt("_", lv); }; + + for (uint32_t lv = 0; lv < nest.size(); ++lv) + { + auto var = var_at(lv); + + loop_body.front().append("for (uint32_t ", var, " = 0; ", var, " < ", nest.at(lv).count(), + "; ++", var, ") {"); + loop_body.front().indent(); + + loop_body.back().append("}"); + loop_body.back().indent(); + } + + std::string src_index = "0"; + std::string dst_index = "0"; + + for (uint32_t lv = 0; lv < nest.size(); ++lv) + { + src_index += pp::fmt(" + ", nest.at(lv).step().src(), " * ", var_at(lv)); + dst_index += pp::fmt(" + ", nest.at(lv).step().dst(), " * ", var_at(lv)); + } + + for (uint32_t n = 0; n < window; ++n) + { + const auto src_base = pp::fmt("reinterpret_cast(", _mem.base(from), ")"); + const auto dst_base = pp::fmt("reinterpret_cast(", _mem.base(into), ")"); + + loop_body.front().append(dst_base, "[", dst_index, " + ", tseq.at(n).into(), "] = ", src_base, + "[", src_index, " + ", tseq.at(n).from(), "];"); + } + + pp::LinearDocument res; + res.append(loop_body); + return res; + } + +private: + const enco::MemoryContext &_mem; +}; + +} // namespace + +namespace enco +{ + +std::unique_ptr HostBlockCompiler::compile(const coco::Block *blk) const +{ + InstrPrinter prn{_mem}; + + auto res = stdex::make_unique(); + + for (auto ins = blk->instr()->head(); ins; ins = ins->next()) + { + res->append(ins->accept(prn)); + } + + return std::move(res); +} + +} // namespace enco diff --git a/compiler/enco/core/src/CppGen/Host.h b/compiler/enco/core/src/CppGen/Host.h new file mode 100644 index 000000000..0adb7fe1f --- /dev/null +++ b/compiler/enco/core/src/CppGen/Host.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CPP_GEN_HOST_H__ +#define __ENCO_CPP_GEN_HOST_H__ + +#include "CppGen/MemoryContext.h" + +#include +#include + +namespace enco +{ + +/*** + * @brief Generate C++ code that does not depend on Anroid NN API + */ +class HostBlockCompiler +{ +public: + HostBlockCompiler(const enco::MemoryContext &mem) : _mem(mem) + { + // DO NOTHING + } + +public: + std::unique_ptr compile(const coco::Block *blk) const; + +private: + const enco::MemoryContext &_mem; +}; + +} // namespace enco + +#endif // __ENCO_CPP_GEN_HOST_H__ diff --git a/compiler/enco/core/src/CppGen/MemoryContext.cpp b/compiler/enco/core/src/CppGen/MemoryContext.cpp new file mode 100644 index 000000000..e522968a8 --- /dev/null +++ b/compiler/enco/core/src/CppGen/MemoryContext.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MemoryContext.h" + +#include + +namespace enco +{ + +bool MemoryContext::member(const coco::Bag *bag) const +{ + // NOTE _base and _size SHOULD BE consistent + if (_base.find(bag) != _base.end()) + { + assert(_size.find(bag) != _size.end()); + return true; + } + + assert(_size.find(bag) == _size.end()); + return false; +} + +void MemoryContext::base(const coco::Bag *bag, const std::string &exp) { _base[bag] = exp; } +void MemoryContext::size(const coco::Bag *bag, const std::string &exp) { _size[bag] = exp; } + +} // namespace enco diff --git a/compiler/enco/core/src/CppGen/MemoryContext.h b/compiler/enco/core/src/CppGen/MemoryContext.h new file mode 100644 index 000000000..99c20f3e8 --- /dev/null +++ b/compiler/enco/core/src/CppGen/MemoryContext.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CPP_GEN_MEMORY_CONTEXT_H__ +#define __ENCO_CPP_GEN_MEMORY_CONTEXT_H__ + +#include + +#include +#include + +namespace enco +{ + +/** + * @brief Record C/C++ expression that denotes the base and size of memory region + * dedicated to each bag + */ +class MemoryContext +{ +public: + /** + * @brief Check whether a base/size expression for a given bag + */ + bool member(const coco::Bag *bag) const; + +public: + void base(const coco::Bag *bag, const std::string &exp); + void size(const coco::Bag *bag, const std::string &exp); + +public: + const std::string &base(const coco::Bag *bag) const { return _base.at(bag); } + const std::string &size(const coco::Bag *bag) const { return _size.at(bag); } + +private: + std::map _base; + std::map _size; +}; + +} // namespace enco + +#endif // __ENCO_CPP_GEN_MEMORY_CONTEXT_H__ diff --git a/compiler/enco/core/src/CppGen/Subnet.cpp b/compiler/enco/core/src/CppGen/Subnet.cpp new file mode 100644 index 000000000..9a636c6ae --- /dev/null +++ b/compiler/enco/core/src/CppGen/Subnet.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CppGen/Subnet.h" + +#include "Dims.h" +#include "String.h" + +#include + +#include + +#include + +using stdex::make_unique; +using enco::concat; + +#define S(content) #content + +namespace ann +{ +static std::ostream &operator<<(std::ostream &os, const ann::OperandID &id) +{ + os << id.value(); + return os; +} +} // namespace ann + +namespace +{ + +class SubnetStructImpl final : public enco::SubnetStruct +{ +public: + SubnetStructImpl() : _dtor{pp::LinearDocument::Direction::Reverse} + { + // DO NOTHING + } + +public: + std::string model(void) const override { return "_model"; } + std::string compilation(void) const override { return "_compilation"; } + +public: + const pp::MultiLineText &def(void) const override { return _def; } + pp::LinearDocument *def(void) { return &_def; } + +public: + const pp::MultiLineText &ctor(void) const override { return _ctor; } + pp::LinearDocument *ctor(void) { return &_ctor; } + +public: + const pp::MultiLineText &dtor(void) const override { return _dtor; } + pp::LinearDocument *dtor(void) { return &_dtor; } + +private: + pp::LinearDocument _def; + pp::LinearDocument _ctor; + pp::LinearDocument _dtor; +}; + +struct CodeFragment +{ + virtual ~CodeFragment() = default; + + virtual void dump(pp::LinearDocument *) const = 0; +}; + +pp::LinearDocument *operator<<(pp::LinearDocument *doc, const CodeFragment &fragment) +{ + fragment.dump(doc); + return doc; +} + +const char *scalar_operand_code(const ann::DType &dtype) +{ + switch (dtype) + { + case ann::DType::S32: + return "ANEURALNETWORKS_INT32"; + default: + break; + }; + + throw std::invalid_argument("dtype"); +} + +const char *tensor_operand_code(const ann::DType &dtype) +{ + switch (dtype) + { + case ann::DType::S32: + return "ANEURALNETWORKS_TENSOR_INT32"; + case ann::DType::F32: + return "ANEURALNETWORKS_TENSOR_FLOAT32"; + default: + break; + }; + + throw std::invalid_argument("dtype"); +} + +class ScalarOperandDecl final : public CodeFragment +{ +public: + ScalarOperandDecl(const std::string &model, const ann::DType &dtype) + : _model{model}, _dtype{dtype} + { + // DO NOTHING + } + +public: + void dump(pp::LinearDocument *doc) const override + { + doc->append("{"); + doc->indent(); + doc->append("ANeuralNetworksOperandType t;"); + doc->append(); + doc->append("t.type = ", scalar_operand_code(_dtype), ";"); + doc->append("t.dimensionCount = 0;"); + doc->append("t.dimensions = nullptr;"); + doc->append("t.scale = 1.0f;"); + doc->append("t.zeroPoint = 0;"); + doc->append(); + doc->append("ANeuralNetworksModel_addOperand(", _model, ", &t);"); + doc->unindent(); + doc->append("}"); + } + +private: + std::string _model; + ann::DType _dtype; +}; + +class TensorOperandDecl final : public CodeFragment +{ +public: + TensorOperandDecl(const std::string &model, const ann::DType &dtype, + const nncc::core::ADT::tensor::Shape &shape) + : _model{model}, _dtype{dtype}, _shape{shape} + { + // DO NOTHING + } + +public: + void dump(pp::LinearDocument *doc) const override + { + const auto rank = _shape.rank(); + const auto dims = as_dims(_shape); + + assert(rank == dims.size()); + + doc->append("{"); + doc->indent(); + doc->append("uint32_t d[", rank, "] = { ", concat(", ", dims.begin(), dims.end()), " };"); + doc->append(); + doc->append("ANeuralNetworksOperandType t;"); + doc->append(); + doc->append("t.type = ", tensor_operand_code(_dtype), ";"); + doc->append("t.dimensionCount = ", rank, ";"); + doc->append("t.dimensions = d;"); + doc->append("t.scale = 1.0f;"); + doc->append("t.zeroPoint = 0;"); + doc->append(); + doc->append("ANeuralNetworksModel_addOperand(", _model, ", &t);"); + doc->unindent(); + doc->append("}"); + } + +private: + std::string _model; + ann::DType _dtype; + nncc::core::ADT::tensor::Shape _shape; +}; + +/** + * @brief Code fragment that calls ANeuralNetworksModel_setOperandValue + */ +class WeightDecl final : public CodeFragment +{ +public: + WeightDecl(const std::string &model, const ann::OperandID &id, const std::string &base, + const std::string &size) + : _model{model}, _id{id}, _base{base}, _size{size} + { + // DO NOTHING + } + +public: + void dump(pp::LinearDocument *doc) const override + { + doc->append("ANeuralNetworksModel_setOperandValue(", _model, ", ", _id.value(), ", ", _base, + ", ", _size, ");"); + } + +private: + std::string _model; + ann::OperandID _id; + std::string _base; + std::string _size; +}; + +/** + * @brief Code fragment that calls ANeuralNetworksModel_addOperation + */ +class OperationDecl final : public CodeFragment +{ +public: + OperationDecl(const std::string &model, const ann::Operation *op) : _model{model}, _op{op} + { + // DO NOTHING + } + +private: + static std::string opcode(const ann::Operation::Code &code) + { + switch (code) + { +#define ANN_OPERATION(TAG, ENUM) \ + case ann::Operation::Code::TAG: \ + return #ENUM; +#include "ANN/IR/Operation.def" +#undef ANN_OPERATION + default: + throw std::invalid_argument{"code"}; + }; + } + +public: + void dump(pp::LinearDocument *doc) const override + { + const auto in_count = _op->inputs().size(); + auto in_beg = _op->inputs().begin(); + auto in_end = _op->inputs().end(); + + const auto out_count = _op->outputs().size(); + auto out_beg = _op->outputs().begin(); + auto out_end = _op->outputs().end(); + + auto op = opcode(_op->code()); + + doc->append("{"); + doc->indent(); + doc->append("uint32_t inputs[", in_count, "] = { ", concat(", ", in_beg, in_end), " };"); + doc->append("uint32_t outputs[", out_count, "] = { ", concat(", ", out_beg, out_end), " };"); + doc->append(); + doc->append("ANeuralNetworksModel_addOperation(", _model, ", ", op, ", ", in_count, + ", inputs, ", out_count, ", outputs);"); + doc->unindent(); + doc->append("}"); + } + +private: + std::string _model; + const ann::Operation *_op; +}; + +/** + * @brief Code fragment that calls ANeuralNetworksModel_identifyInputsAndOutputs + */ +class ArgumentDecl final : public CodeFragment +{ +public: + ArgumentDecl(const std::string &mname, const ANNBinder *binder) : _mname{mname}, _binder{binder} + { + // DO NOTHING + } + +public: + void dump(pp::LinearDocument *doc) const override + { + doc->append("{"); + doc->indent(); + + auto module = _binder->module(); + const uint32_t input_count = module->input()->size(); + + doc->append("uint32_t inputs[", input_count, "];"); + for (uint32_t n = 0; n < input_count; ++n) + { + doc->append("inputs[", n, "] = ", module->input()->at(n), ";"); + } + + const uint32_t output_count = module->output()->size(); + + doc->append("uint32_t outputs[", output_count, "];"); + for (uint32_t n = 0; n < output_count; ++n) + { + doc->append("outputs[", n, "] = ", module->output()->at(n), ";"); + } + + doc->append("ANeuralNetworksModel_identifyInputsAndOutputs(", _mname, ", ", input_count, + ", inputs, ", output_count, ", outputs);"); + doc->unindent(); + doc->append("}"); + } + +private: + std::string _mname; + const ANNBinder *_binder; +}; + +} // namespace + +namespace enco +{ + +std::unique_ptr SubnetStructBuilder::build(const ANNBinder *binder) const +{ + auto res = make_unique(); + + auto mname = res->model(); + auto cname = res->compilation(); + + res->def()->append("ANeuralNetworksModel *", mname, ";"); + res->def()->append("ANeuralNetworksCompilation *", cname, ";"); + + res->ctor()->append("ANeuralNetworksModel_create(&", mname, ");"); + res->dtor()->append("ANeuralNetworksModel_free(", mname, ");"); + + binder->module()->operand()->each([&](const ann::OperandID &id, const ann::Operand *info) { + // TODO Remove dynamic cast + if (auto scalar = dynamic_cast(info)) + { + res->ctor() << ScalarOperandDecl{mname, scalar->dtype()}; + } + else if (auto tensor = dynamic_cast(info)) + { + res->ctor() << TensorOperandDecl{mname, tensor->dtype(), tensor->shape()}; + } + else + { + throw std::runtime_error{"Unsupported"}; + } + + if (_weighted.find(info) != _weighted.end()) + { + const auto &base_exp = _base_exprs.at(info); + const auto &size_exp = _size_exprs.at(info); + + res->ctor() << WeightDecl{mname, id, base_exp, size_exp}; + } + }); + + for (unsigned n = 0; n < binder->module()->operation()->count(); ++n) + { + auto op = binder->module()->operation()->at(n); + res->ctor() << OperationDecl{mname, op}; + } + + // Emit ANeuralNetworksModel_identifyInputsAndOutputs call + res->ctor() << ArgumentDecl{mname, binder}; + + // Emit ANeuralNetworksModel_finish call + res->ctor()->append("ANeuralNetworksModel_finish(", mname, ");"); + + // Create compilation + res->ctor()->append("ANeuralNetworksCompilation_create(", mname, ", &", cname, ");"); + res->dtor()->append("ANeuralNetworksCompilation_free(", cname, ");"); + + // Finalize compilation + res->ctor()->append("ANeuralNetworksCompilation_finish(", cname, ");"); + + return std::move(res); +} + +std::unique_ptr SubnetBlockCompiler::compile(const ANNBinder *binder) const +{ + auto res = make_unique(); + + const auto compilation = _compilation_ctx.at(binder); + + res->append("ANeuralNetworksExecution *execution;"); + res->append("ANeuralNetworksEvent *event;"); + res->append(); + res->append("ANeuralNetworksExecution_create(", compilation, ", &execution);"); + + // Emit ANeuralNetworksExecution_setInput call(s) + for (uint32_t n = 0; n < binder->module()->input()->size(); ++n) + { + auto bag = binder->input(n); + auto base = _mem.base(bag); + auto size = _mem.size(bag); + + res->append("ANeuralNetworksExecution_setInput(execution, ", n, ", nullptr, ", base, ", ", size, + ");"); + } + + // Emit ANeuralNetworksExecution_setOutput call(s) + for (uint32_t n = 0; n < binder->module()->output()->size(); ++n) + { + auto bag = binder->output(n); + auto base = _mem.base(bag); + auto size = _mem.size(bag); + + res->append("ANeuralNetworksExecution_setOutput(execution, ", n, ", nullptr, ", base, ", ", + size, ");"); + } + + res->append("ANeuralNetworksExecution_startCompute(execution, &event);"); + res->append("ANeuralNetworksEvent_wait(event);"); + res->append("ANeuralNetworksEvent_free(event);"); + + res->append("ANeuralNetworksExecution_free(execution);"); + + return std::move(res); +} + +} // namespace enco diff --git a/compiler/enco/core/src/CppGen/Subnet.h b/compiler/enco/core/src/CppGen/Subnet.h new file mode 100644 index 000000000..4a5738876 --- /dev/null +++ b/compiler/enco/core/src/CppGen/Subnet.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CPP_GEN_SUBNET_H__ +#define __ENCO_CPP_GEN_SUBNET_H__ + +#include "ANN/Binder.h" +#include "CppGen/MemoryContext.h" + +#include +#include +#include + +namespace enco +{ + +/** + * @brief A C++ struct that provides Android NN model & compilation + */ +struct SubnetStruct +{ + virtual ~SubnetStruct() = default; + + /// @brief Return the field name of ANeuralNetworksModel value + virtual std::string model(void) const = 0; + /// @brief Return the field name of ANeuralNetworksCompilatoin value + virtual std::string compilation(void) const = 0; + + virtual const pp::MultiLineText &def(void) const = 0; + virtual const pp::MultiLineText &ctor(void) const = 0; + virtual const pp::MultiLineText &dtor(void) const = 0; +}; + +class SubnetStructBuilder +{ +public: + std::unique_ptr build(const ANNBinder *binder) const; + +public: + void expr(const ann::Operand *oper, const std::string &base, const std::string &size) + { + _weighted.insert(oper); + _base_exprs[oper] = base; + _size_exprs[oper] = size; + } + +private: + std::set _weighted; + std::map _base_exprs; + std::map _size_exprs; +}; + +/** + * @brief Generate C++ code that invokes Android NN subnet + */ +class SubnetBlockCompiler +{ +public: + SubnetBlockCompiler(const enco::MemoryContext &mem) : _mem(mem) + { + // DO NOTHING + } + +public: + /// @brief Specify how to access ANeuralNetworksCompilation value (C expression) + void bind(const ANNBinder *binder, const std::string &exp) { _compilation_ctx[binder] = exp; } + +public: + std::unique_ptr compile(const ANNBinder *binder) const; + +private: + const enco::MemoryContext &_mem; + std::map _compilation_ctx; +}; + +} // namespace enco + +#endif // __ENCO_CPP_GEN_SUBNET_H__ diff --git a/compiler/enco/core/src/Dims.h b/compiler/enco/core/src/Dims.h new file mode 100644 index 000000000..e0a4fd44d --- /dev/null +++ b/compiler/enco/core/src/Dims.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DIMS_H__ +#define __DIMS_H__ + +#include + +static inline std::vector as_dims(const nncc::core::ADT::tensor::Shape &shape) +{ + std::vector res; + + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + res.emplace_back(shape.dim(axis)); + } + + return res; +} + +#endif // __DIMS_H__ diff --git a/compiler/enco/core/src/IRUtils.cpp b/compiler/enco/core/src/IRUtils.cpp new file mode 100644 index 000000000..59f6b0dbe --- /dev/null +++ b/compiler/enco/core/src/IRUtils.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IRUtils.h" + +#include + +namespace enco +{ + +/** + * @brief Substitute all the USE occurrences of an object with another object + * @param from Object to be replaced + * @param into Object to be used instead + * NOTE This maybe used when something like -- 'from' will be removed so we need + * to replace object Consumers that use 'from' to 'into' + * EXAMPLE + * { + * subst(child, bigone); + * m->entity()->object()->destroy(child); + * } + * This code will change all the Consumers that use 'child' to 'bigone' and + * destroy the 'child' object. + */ +void subst(coco::Object *from, coco::Object *into) +{ + assert(from != into); + + while (!from->uses()->empty()) + { + auto use = *(from->uses()->begin()); + + use->value(into); + } +} + +std::vector instr_sequence(coco::Module *m) +{ + std::vector res; + + for (auto B = m->block()->head(); B; B = B->next()) + { + for (auto I = B->instr()->head(); I; I = I->next()) + { + res.emplace_back(I); + } + } + + return res; +} + +} // namespace enco diff --git a/compiler/enco/core/src/IRUtils.h b/compiler/enco/core/src/IRUtils.h new file mode 100644 index 000000000..da0754303 --- /dev/null +++ b/compiler/enco/core/src/IRUtils.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_IR_UTILS_H__ +#define __ENCO_IR_UTILS_H__ + +#include + +#include + +namespace enco +{ + +/** + * @brief Replace all the "USE" of 'from' with 'into' + * + * NOTE subst(from, into) WILL NOT update 'DEF' + */ +void subst(coco::Object *from, coco::Object *into); + +/** + * @brief Return instructions in execution order + */ +std::vector instr_sequence(coco::Module *m); + +} // namespace enco + +#endif // __ENCO_IR_UTILS_H__ diff --git a/compiler/enco/core/src/IRValidator.cpp b/compiler/enco/core/src/IRValidator.cpp new file mode 100644 index 000000000..1337b88e4 --- /dev/null +++ b/compiler/enco/core/src/IRValidator.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IRValidator.h" + +#include + +namespace enco +{ + +coco::FeatureShape output_shape(coco::Conv2D *conv2D) +{ + auto load = conv2D->arg()->asLoad(); + assert(load); + + auto ifm = load->object()->asFeature(); + assert(ifm); + + auto ker = conv2D->ker(); + auto stride = conv2D->stride(); + auto pad = conv2D->pad(); + + auto striding_width = ifm->shape().width() + pad->left() + pad->right() - ker->shape().width(); + auto striding_height = ifm->shape().height() + pad->top() + pad->bottom() - ker->shape().height(); + + // Normally the formula is round(striding_width)/stride->horizontal. + // in coco IR, striding_width should be a multiple of stride->horizontal(), so round(...) was + // removed. So does striding_height. + assert(striding_width % stride->horizontal() == 0); + assert(striding_height % stride->vertical() == 0); + + auto ofm_width = striding_width / stride->horizontal() + 1; + auto ofm_height = striding_height / stride->vertical() + 1; + + return coco::FeatureShape(ifm->shape().batch(), ker->shape().count(), ofm_height, ofm_width); +} + +bool validate_output_shape(Code *code) +{ + auto module = code->module(); + + // for each eval ( conv2d ( ... ) ), check the output shape of conv2D matches output of eval + for (auto blk = module->block()->head(); blk; blk = blk->next()) + { + for (auto instr = blk->instr()->head(); instr; instr = instr->next()) + { + auto eval = instr->asEval(); + if (eval == nullptr) + continue; + + auto op = eval->op(); + if (!op->asConv2D()) + continue; + + auto conv2D = op->asConv2D(); + auto expected_shape = output_shape(conv2D); + + auto eval_out = eval->out()->asFeature(); + assert(eval_out); + + auto actual_shape = eval_out->shape(); + + if (actual_shape != expected_shape) + return false; + } + } + return true; +} + +bool validate(Code *code) { return validate_output_shape(code); } + +} // namespace enco diff --git a/compiler/enco/core/src/IRValidator.h b/compiler/enco/core/src/IRValidator.h new file mode 100644 index 000000000..f4adb0a5e --- /dev/null +++ b/compiler/enco/core/src/IRValidator.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_IR_VALIDATOR_H__ +#define __ENCO_IR_VALIDATOR_H__ + +#include "Code.h" + +namespace enco +{ + +bool validate(Code *code); + +} // namespace enco + +#endif // __ENCO_IR_VALIDATOR_H__ diff --git a/compiler/enco/core/src/IRValidator.test.cpp b/compiler/enco/core/src/IRValidator.test.cpp new file mode 100644 index 000000000..14cda6173 --- /dev/null +++ b/compiler/enco/core/src/IRValidator.test.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IRValidator.h" + +#include "Code.h" + +#include + +#include + +namespace +{ + +using IntList4 = std::array; +using IntList2 = std::array; + +} // namespace + +// The layout of ifm, ker, ofm is NHWC, pad == {top, bottom, left, right}, and stride == {vertical, +// horizontal}. +std::unique_ptr get_conv2D(IntList4 ifm, IntList4 ker, IntList4 ofm, IntList4 pad, + IntList2 stride) +{ + auto module = coco::Module::create(); + auto block = module->entity()->block()->create(); + auto eval = module->entity()->instr()->create(); + auto load = module->entity()->op()->create(); + auto conv2D = module->entity()->op()->create(); + + auto ifm_obj = module->entity()->object()->create(); + coco::FeatureShape ifm_shape(ifm[0], ifm[3], ifm[1], ifm[2]); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(ifm_shape)); + + auto ofm_obj = module->entity()->object()->create(); + coco::FeatureShape ofm_shape(ofm[0], ofm[3], ofm[1], ofm[2]); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape)); + + auto ker_obj = module->entity()->object()->create(); + nncc::core::ADT::kernel::Shape ker_shape(ker[0], ker[3], ker[1], ker[2]); + ker_obj->layout(coco::KernelLayouts::NHWC::create(ker_shape)); + + // linking entities + module->block()->append(block); + block->instr()->append(eval); + eval->op(conv2D); + eval->out(ofm_obj); + load->object(ifm_obj); + conv2D->ker(ker_obj); + conv2D->arg(load); + + // param setting + conv2D->pad()->top(pad[0]).bottom(pad[1]).left(pad[2]).right(pad[3]); + conv2D->stride()->vertical(stride[0]).horizontal(stride[1]); + + return std::move(module); +} + +TEST(IRValidatorTest, conv2D_simple) +{ + auto ifm_nhwc = IntList4{1, 3, 3, 2}; + auto ker_nhwc = IntList4{1, 1, 1, 2}; + auto ofm_nhwc = IntList4{1, 3, 3, 1}; + + auto pad_tblr = IntList4{0, 0, 0, 0}; + auto stride_vh = IntList2{1, 1}; + + auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh); + enco::Code code{module.get(), nullptr}; + + ASSERT_TRUE(enco::validate(&code)); +} + +TEST(IRValidatorTest, conv2D_stride_2) +{ + auto ifm_nhwc = IntList4{1, 4, 4, 3}; + auto ker_nhwc = IntList4{2, 2, 2, 3}; + auto ofm_nhwc = IntList4{1, 3, 3, 2}; + + auto pad_tblr = IntList4{1, 1, 1, 1}; + auto stride_vh = IntList2{2, 2}; + + auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh); + enco::Code code{module.get(), nullptr}; + + ASSERT_TRUE(enco::validate(&code)); +} + +TEST(IRValidatorTest, conv2D_output_batch_check) +{ + auto ifm_nhwc = IntList4{1, 2, 2, 2}; + auto ker_nhwc = IntList4{3, 1, 1, 2}; // expected output depth is 3 + auto ofm_nhwc = IntList4{1, 2, 2, 1}; // but 1 + + auto pad_tblr = IntList4{0, 0, 0, 0}; + auto stride_vh = IntList2{1, 1}; + + auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh); + enco::Code code{module.get(), nullptr}; + + ASSERT_FALSE(enco::validate(&code)); +} + +TEST(IRValidatorTest, conv2D_wrong_HW) +{ + auto ifm_nhwc = IntList4{1, 2, 2, 1}; + auto ker_nhwc = IntList4{1, 2, 2, 1}; + auto ofm_nhwc = IntList4{1, 1, 1, 1}; // HW should be 2, 2 + + auto pad_tblr = IntList4{1, 1, 1, 1}; + auto stride_vh = IntList2{2, 2}; + + auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh); + enco::Code code{module.get(), nullptr}; + + ASSERT_FALSE(enco::validate(&code)); +} diff --git a/compiler/enco/core/src/Pass.h b/compiler/enco/core/src/Pass.h new file mode 100644 index 000000000..d78cfaad3 --- /dev/null +++ b/compiler/enco/core/src/Pass.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_PASS_H__ +#define __ENCO_PASS_H__ + +#include "Session.h" + +#include + +namespace enco +{ + +class Pass +{ +public: + class Name + { + public: + Name(const std::string &content) : _content{content} + { + // DO NOTHING + } + + Name(const Name &) = default; + Name(Name &&) = default; + + ~Name() = default; + + public: + const std::string &content(void) const { return _content; } + + private: + std::string _content; + }; + +public: + Pass(const Name &name) : _name{name} + { + // DO NOTHING + } + + Pass(const Pass &) = delete; + Pass(Pass &&) = delete; + + virtual ~Pass() = default; + +public: + const Name &name(void) const { return _name; } + +public: + virtual void run(const SessionID &) const = 0; + +private: + Name _name; +}; + +static inline Pass::Name pass_name(const std::string &name) { return Pass::Name{name}; } + +} // namespace enco + +#define PASS_CTOR(NAME) \ + NAME() : enco::Pass { enco::pass_name(#NAME) } + +#endif // __ENCO_PASS_H__ diff --git a/compiler/enco/core/src/Pass.test.cpp b/compiler/enco/core/src/Pass.test.cpp new file mode 100644 index 000000000..112bd7478 --- /dev/null +++ b/compiler/enco/core/src/Pass.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Pass.h" + +#include + +namespace +{ + +struct ExamplePass final : public enco::Pass +{ + PASS_CTOR(ExamplePass) + { + // DO NOTHING + } + + void run(const enco::SessionID &) const override { return; } +}; + +} // namespace + +TEST(PASS, ctor) +{ + ExamplePass pass; + + ASSERT_EQ(pass.name().content(), "ExamplePass"); +} diff --git a/compiler/enco/core/src/Pipeline.h b/compiler/enco/core/src/Pipeline.h new file mode 100644 index 000000000..8ab43c16a --- /dev/null +++ b/compiler/enco/core/src/Pipeline.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_PIPELINE_H__ +#define __ENCO_PIPELINE_H__ + +#include "Pass.h" + +#include +#include +#include + +namespace enco +{ + +class Pipeline +{ +public: + uint32_t size(void) const { return _passes.size(); } + +public: + const Pass &at(uint32_t n) const { return *(_passes.at(n)); } + +public: + void append(std::unique_ptr &&pass) { _passes.emplace_back(std::move(pass)); } + +private: + std::vector> _passes; +}; + +} // namespace enco + +#endif // __ENCO_PIPELINE_H__ diff --git a/compiler/enco/core/src/Pipeline.test.cpp b/compiler/enco/core/src/Pipeline.test.cpp new file mode 100644 index 000000000..1cd730e98 --- /dev/null +++ b/compiler/enco/core/src/Pipeline.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Pipeline.h" + +#include + +TEST(PIPELINE, default_ctor) +{ + enco::Pipeline pipeline; + + ASSERT_EQ(pipeline.size(), 0); +} diff --git a/compiler/enco/core/src/Session.cpp b/compiler/enco/core/src/Session.cpp new file mode 100644 index 000000000..034f23892 --- /dev/null +++ b/compiler/enco/core/src/Session.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Session.h" + +#include + +#include +#include + +using stdex::make_unique; + +namespace +{ + +std::map> sess_to_code; +std::map module_to_sess; +std::map data_to_sess; + +} // namespace + +namespace enco +{ + +SessionID make_session(coco::Module *m, coco::Data *d) +{ + static uint32_t sess = 0; + SessionID curr{sess++}; + + sess_to_code[curr] = make_unique(m, d); + module_to_sess[m] = curr; + data_to_sess[d] = curr; + + return curr; +} + +SessionID session(const coco::Module *m) { return module_to_sess.at(m); } +SessionID session(const coco::Data *d) { return data_to_sess.at(d); } + +coco::Module *module(const SessionID &sess) { return sess_to_code.at(sess)->module(); } +coco::Data *data(const SessionID &sess) { return sess_to_code.at(sess)->data(); } + +Code *code(const SessionID &sess) { return sess_to_code.at(sess).get(); } + +} // namespace enco diff --git a/compiler/enco/core/src/Session.h b/compiler/enco/core/src/Session.h new file mode 100644 index 000000000..b6d502f3b --- /dev/null +++ b/compiler/enco/core/src/Session.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_SESSION_H__ +#define __ENCO_SESSION_H__ + +#include "Code.h" + +namespace enco +{ + +// TODO Rewrite this definition +using SessionID = uint32_t; + +SessionID make_session(coco::Module *m, coco::Data *d); + +SessionID session(const coco::Module *m); +SessionID session(const coco::Data *d); + +coco::Module *module(const SessionID &); +coco::Data *data(const SessionID &); + +static inline coco::Module *module(const coco::Data *d) { return module(session(d)); } +static inline coco::Data *data(const coco::Module *m) { return data(session(m)); } + +// WARN This API is introduced just for backward compatibility +// Do NOT use this anymore as it will be removed +Code *code(const SessionID &); + +} // namespace enco + +#endif // __ENCO_SESSION_H__ diff --git a/compiler/enco/core/src/String.h b/compiler/enco/core/src/String.h new file mode 100644 index 000000000..0f04f1ffe --- /dev/null +++ b/compiler/enco/core/src/String.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_STRING_H__ +#define __ENCO_STRING_H__ + +// +// String-manipulating routines +// +#include +#include + +#include + +namespace enco +{ + +template void concat(std::ostream &os, const std::string &sep, It beg, It end) +{ + uint32_t count = 0; + + for (auto it = beg; it != end; ++it, ++count) + { + if (count == 0) + { + os << *it; + } + else + { + os << sep << *it; + } + } +} + +template std::string concat(const std::string &sep, It beg, It end) +{ + std::stringstream ss; + concat(ss, sep, beg, end); + return ss.str(); +} + +} // namespace enco + +#endif // __ENCO_STRING_H__ diff --git a/compiler/enco/core/src/Support/Debugging.cpp b/compiler/enco/core/src/Support/Debugging.cpp new file mode 100644 index 000000000..bd65a27d8 --- /dev/null +++ b/compiler/enco/core/src/Support/Debugging.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Debugging.h" + +#include +#include + +#include + +#include + +#define DEBUGGING_API_P(NAME, TYPE, VAR) \ + static void _##NAME(const TYPE *); \ + void NAME(long p) { NAME(reinterpret_cast(p)); } \ + void NAME(const TYPE *p) \ + { \ + if (p == nullptr) \ + { \ + std::cout << "(nullptr)" << std::endl; \ + } \ + else \ + { \ + _##NAME(p); \ + } \ + } \ + void _##NAME(const TYPE *VAR) + +namespace +{ + +class SectionBuilder +{ +public: + SectionBuilder(const std::string &tag) : _tag{tag} + { + // DO NOTHING + } + +public: + template pp::LinearDocument build(Callback cb) const + { + pp::LinearDocument res; + + res.append(_tag, " {"); + res.indent(); + + cb(res); + + res.unindent(); + res.append("}"); + + return res; + } + +private: + std::string _tag; +}; + +template +pp::LinearDocument operator<<(const SectionBuilder &builder, Callback cb) +{ + return builder.build(std::forward(cb)); +} + +SectionBuilder section(const std::string &tag) { return SectionBuilder{tag}; } +} + +/** + * SECTION: Bag + */ +namespace +{ + +pp::LinearDocument describe(const coco::Bag *bag) +{ + pp::LinearDocument doc; + + doc.append("addr: ", bag); + doc.append("size: ", bag->size()); + // TODO Print Read + // TODO Print Update + // TODO Print Dep + return doc; +} + +} // namespace + +DEBUGGING_API_P(enco_dump_all_bags, coco::Module, m) +{ + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + assert(bag != nullptr); + + auto set = [bag](pp::LinearDocument &doc) { doc.append(describe(bag)); }; + auto desc = section("bag").build(set); + + std::cout << desc << std::endl; + } +} + +/** + * SECTION: Object + */ +namespace +{ +std::string op_kind(const coco::Op *op); + +/** + * @brief Return the def(producer) type of object + */ +std::string def_kind(const coco::Def *def) +{ + if (def) + { + if (auto instr = dynamic_cast(def->producer())) + { + std::stringstream ss; + + if (auto eval = instr->asEval()) + { + ss << op_kind(eval->op()) << "(" << instr << ")"; + return ss.str(); + } + else if (instr->asCopy()) + { + ss << "Copy(" << instr << ")"; + return ss.str(); + } + else if (instr->asShuffle()) + { + ss << "Shuffle(" << instr << ")"; + return ss.str(); + } + } + else + { + return "(unknown)"; + } + } + + return "(none)"; +} + +pp::LinearDocument describe(const coco::Object *obj) +{ + pp::LinearDocument doc; + + doc.append("addr: ", obj); + doc.append("bag: ", obj->bag()); + doc.append("producer: ", def_kind(obj->def())); + // TODO Show Uses + // TODO Show FeatureObject/KernelObect info + + return doc; +} + +} // namespace + +DEBUGGING_API_P(enco_dump_all_objects, coco::Module, m) +{ + for (uint32_t n = 0; n < m->entity()->object()->size(); ++n) + { + auto obj = m->entity()->object()->at(n); + assert(obj != nullptr); + + auto set = [obj](pp::LinearDocument &doc) { doc.append(describe(obj)); }; + auto desc = section("object").build(set); + + std::cout << desc << std::endl; + } +} + +/** + * SECTION: Op + */ +namespace +{ + +struct OpTree +{ +public: + OpTree(const coco::Op *op) : _op{op} + { + // DO NOTHING + } + +public: + const coco::Op *root(void) const { return _op; } + +private: + const coco::Op *_op; +}; + +std::string op_kind(const coco::Op *op) +{ + struct OpKind : public coco::Op::Visitor + { + std::string visit(const coco::Load *) override { return "Load"; } + std::string visit(const coco::Conv2D *) override { return "Conv2D"; } + std::string visit(const coco::MaxPool2D *) override { return "MaxPool2D"; } + std::string visit(const coco::AvgPool2D *) override { return "AvgPool2D"; } + std::string visit(const coco::PadF *) override { return "PadF"; } + std::string visit(const coco::ReLU *) override { return "ReLU"; } + std::string visit(const coco::Add *) override { return "Add"; } + std::string visit(const coco::Mul *) override { return "Mul"; } + std::string visit(const coco::ConcatF *) override { return "ConcatF"; } + std::string visit(const coco::Sub *) override { return "Sub"; } + std::string visit(const coco::Sqrt *) override { return "Sqrt"; } + std::string visit(const coco::Div *) override { return "Div"; } + }; + + OpKind v; + + return op->accept(v); +} + +pp::LinearDocument describe(const coco::Padding2D *pad) +{ + pp::LinearDocument doc; + + doc.append("top: ", pad->top()); + doc.append("bottom: ", pad->bottom()); + doc.append("left: ", pad->left()); + doc.append("right: ", pad->right()); + + return doc; +} + +pp::LinearDocument describe(const coco::Stride2D *stride) +{ + pp::LinearDocument doc; + + doc.append("vertical: ", stride->vertical()); + doc.append("horizontal ", stride->horizontal()); + + return doc; +} + +pp::LinearDocument describe(const coco::Conv2D *conv) +{ + pp::LinearDocument doc; + + doc.append("arg: ", conv->arg()); + doc.append("ker: ", conv->ker()); + doc.append("group: ", conv->group()); + + if (auto pad = conv->pad()) + { + auto set = [pad](pp::LinearDocument &doc) { doc.append(describe(pad)); }; + auto desc = section("pad").build(set); + doc.append(desc); + } + + if (auto stride = conv->stride()) + { + auto set = [stride](pp::LinearDocument &doc) { doc.append(describe(stride)); }; + auto desc = section("stride").build(set); + doc.append(desc); + } + + return doc; +} + +pp::LinearDocument describe(const coco::Op *op) +{ + pp::LinearDocument doc; + + doc.append("addr: ", op); + doc.append("kind: ", op_kind(op)); + doc.append("parent(instr): ", op->parent()); + doc.append("up(op): ", op->up()); + + if (auto conv = op->asConv2D()) + { + auto set = [conv](pp::LinearDocument &doc) { doc.append(describe(conv)); }; + auto desc = section("conv2d").build(set); + doc.append(desc); + } + else if (auto load = op->asLoad()) + { + auto set = [load](pp::LinearDocument &doc) { doc.append(describe(load->object())); }; + auto desc = section("load").build(set); + doc.append(desc); + } + + return doc; +} + +pp::LinearDocument describe(const OpTree &t, bool verbose = false) +{ + pp::LinearDocument doc; + + struct Frame + { + public: + Frame(const coco::Op *op) : _op{op}, _indicator{0} + { + // op SHOULD BE valid + assert(_op != nullptr); + } + + public: + /** + * @brief Return a pointer to coco::Op of interest + */ + const coco::Op *op(void) const { return _op; } + + /** + * @brief Return the indicator + * + * Let's assume that the arity of a coco::Op of interest is N + * INDICATOR 0 -> Print the op itself + * INDICATOR 1 -> Print the first argument + * ... + * INDICATOR N -> Print the N-th argument + * INDICATOR N + 1 -> Done + */ + uint32_t indicator(void) const { return _indicator; } + + public: + void advance(void) { _indicator += 1; } + + private: + const coco::Op *_op; + uint32_t _indicator; + }; + + std::stack stack; + + stack.emplace(t.root()); + + while (stack.size() > 0) + { + auto op = stack.top().op(); + uint32_t indicator = stack.top().indicator(); + + if (indicator == 0) + { + doc.append(op_kind(op), " (", op, ")"); + + doc.indent(); + stack.top().advance(); + + // TODO Need to update it to better design for verbose flag + if (verbose) + { + auto set = [op](pp::LinearDocument &doc) { doc.append(describe(op)); }; + auto desc = section("op").build(set); + doc.append(desc); + } + } + else if (indicator < op->arity() + 1) + { + stack.top().advance(); + stack.emplace(op->arg(indicator - 1)); + } + else + { + assert(indicator == op->arity() + 1); + doc.unindent(); + stack.pop(); + } + } + + return doc; +} + +} // namespace + +DEBUGGING_API_P(enco_dump_op, coco::Op, op) +{ + { + std::cout << describe(op) << std::endl; + } +} + +DEBUGGING_API_P(enco_dump_op_tree, coco::Op, op) +{ + { + std::cout << describe(OpTree(op)) << std::endl; + } +} + +DEBUGGING_API_P(enco_dump_all_ops, coco::Module, m) +{ + SectionBuilder section_builder{"op"}; + + for (uint32_t n = 0; n < m->entity()->op()->size(); ++n) + { + auto op = m->entity()->op()->at(n); + assert(op != nullptr); + + auto desc = section("op").build([op](pp::LinearDocument &doc) { doc.append(describe(op)); }); + + std::cout << desc << std::endl; + } +} + +/** + * SECTION: Instr + */ +namespace +{ + +std::string kind(const coco::Instr *ins) +{ + struct InstrKind : public coco::Instr::Visitor + { + std::string visit(const coco::Eval *) override { return "Eval"; } + std::string visit(const coco::Copy *) override { return "Copy"; } + std::string visit(const coco::Shuffle *) override { return "Shuffle"; } + }; + + InstrKind v; + + return ins->accept(v); +} + +pp::LinearDocument describe(const coco::Instr *ins, bool verbose = false) +{ + pp::LinearDocument doc; + + doc.append("addr: ", ins); + doc.append("kind: ", kind(ins)); + doc.append("parent: ", ins->parent()); + + // TODO Need to update it to better design for verbose flag + if (verbose) + { + if (auto eval = ins->asEval()) + { + auto optset = [eval, verbose](pp::LinearDocument &doc) { + doc.append(describe(OpTree(eval->op()), verbose)); + }; + auto optdesc = section("op").build(optset); + doc.append(optdesc); + + auto outset = [eval](pp::LinearDocument &doc) { doc.append(describe(eval->out())); }; + auto outdesc = section("out").build(outset); + doc.append(outdesc); + } + else if (auto copy = ins->asCopy()) + { + auto from = [copy](pp::LinearDocument &doc) { doc.append(describe(copy->from())); }; + auto into = [copy](pp::LinearDocument &doc) { doc.append(describe(copy->into())); }; + + auto fdesc = section("from").build(from); + doc.append(fdesc); + + auto idesc = section("into").build(into); + doc.append(idesc); + } + } + + return doc; +} + +} // namespace + +DEBUGGING_API_P(enco_dump_all_instrs, coco::Module, m) +{ + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + auto ins = m->entity()->instr()->at(n); + assert(ins != nullptr); + + auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins)); }; + auto desc = section("instr").build(setter); + + std::cout << desc << std::endl; + } +} + +DEBUGGING_API_P(enco_dump_all_instrs_v, coco::Module, m) +{ + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + auto ins = m->entity()->instr()->at(n); + assert(ins != nullptr); + + auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins, true)); }; + auto desc = section("instr").build(setter); + + std::cout << desc << std::endl; + } +} + +DEBUGGING_API_P(enco_dump_instr, coco::Instr, ins) +{ + auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins, true)); }; + auto desc = section("instr").build(setter); + + std::cout << desc << std::endl; +} + +/** + * SECTION: Block + */ +namespace +{ + +pp::LinearDocument describe(const coco::Block *blk) +{ + pp::LinearDocument doc; + + for (auto ins = blk->instr()->head(); ins; ins = ins->next()) + { + auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins)); }; + auto desc = section("instr").build(setter); + doc.append(desc); + } + + return doc; +} + +} // namespace + +DEBUGGING_API_P(enco_dump_block, coco::Block, blk) { std::cout << describe(blk) << std::endl; } diff --git a/compiler/enco/core/src/Support/Debugging.h b/compiler/enco/core/src/Support/Debugging.h new file mode 100644 index 000000000..c28356e76 --- /dev/null +++ b/compiler/enco/core/src/Support/Debugging.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Debugging.h + * @brief This file includes various interactive debugging helpers + */ + +#ifndef __ENCO_SUPPORT_DEBUGGING_H__ +#define __ENCO_SUPPORT_DEBUGGING_H__ + +#include + +static_assert(sizeof(long) == sizeof(void *), "sizeof(long) == sizeof(pointer)"); + +/** + * Debugging API with a single pointer argument + */ +#define DEBUGGING_API_P(NAME, TYPE) \ + void NAME(const TYPE *); \ + void NAME(long); + +/** + * Print the details of all the allocated coco::Bag in coco::Module + * + * (gdb) call enco_dump_all_bags(bag->module()) + * (gdb) call enco_dump_all_bags(0x...) + */ +DEBUGGING_API_P(enco_dump_all_bags, coco::Module); + +/** + * Print the details of all the allocated coco::Object in coco::Module + * + * (gdb) call enco_dump_all_objects(obj->module()) + * (gdb) call enco_dump_all_objects(0x...) + */ +DEBUGGING_API_P(enco_dump_all_objects, coco::Module); + +/** + * Print the details of coco::Op + * + * (gdb) call enco_dump_op(op) + * (gdb) call enco_dump_op(0x....) + */ +DEBUGGING_API_P(enco_dump_op, coco::Op); + +/** + * Print the (simplified) tree layout of coco::Op + * + * (gdb) call enco_dump_op_tree(op) + * (gdb) call enco_dump_op_tree(0x....) + */ +DEBUGGING_API_P(enco_dump_op_tree, coco::Op); + +/** + * Print the details of all the allocated coco::Op in coco::Module + * + * (gdb) call enco_dump_all_ops(op->module()) + * (gdb) call enco_dump_all_ops(0x....) + */ +DEBUGGING_API_P(enco_dump_all_ops, coco::Module); + +/** + * Print the details of all the allocated coco::Instr in coco::Module + * + * (gdb) call enco_dump_all_instrs(instr->module()) + * (gdb) call enco_dump_all_instrs(0x...) + */ +DEBUGGING_API_P(enco_dump_all_instrs, coco::Module); + +/** + * Print the more details of all the allocated coco::Instr in coco::Module + * + * (gdb) call enco_dump_all_instrs_v(instr->module()) + * (gdb) call enco_dump_all_instrs_v(0x...) + */ +DEBUGGING_API_P(enco_dump_all_instrs_v, coco::Module); + +/** + * Print the details of a given coco::Instr + * + * (gdb) call enco_dump_instr(instr) + * (gdb) call enco_dump_instr(0x...) + */ +DEBUGGING_API_P(enco_dump_instr, coco::Instr); + +/** + * Print the details of all the instruction in a given block + * + * (gdb) call enco_dump_block(b) + * (gdb) call enco_dump_block(0x...) + */ +DEBUGGING_API_P(enco_dump_block, coco::Block); + +#undef DEBUGGING_API_P + +#endif // __ENCO_SUPPORT_DEBUGGING_H__ diff --git a/compiler/enco/core/src/Support/Debugging.test.cpp b/compiler/enco/core/src/Support/Debugging.test.cpp new file mode 100644 index 000000000..49a2ad162 --- /dev/null +++ b/compiler/enco/core/src/Support/Debugging.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Debugging.h" + +#include + +// This test aims to check whether debugging API is actually defined +TEST(DebuggingTest, defined) +{ + enco_dump_op(nullptr); + enco_dump_all_ops(nullptr); +} diff --git a/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp new file mode 100644 index 000000000..17502fb1f --- /dev/null +++ b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AvgPoolLowering.h" +#include "IRUtils.h" + +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using nncc::core::ADT::feature::num_elements; + +namespace +{ + +bool empty(coco::Padding2D *pad) +{ + return (pad->top() == 0) && (pad->bottom() == 0) && (pad->left() == 0) && (pad->right() == 0); +} + +/** + * @brief Return a set of AvgPool2D operations (in Eval instruction) that SHOULD be lowered + */ +std::set candidates(coco::Module *m) +{ + std::set res; + + for (auto I : enco::instr_sequence(m)) + { + if (auto eval = I->asEval()) + { + if (auto avgpool = eval->op()->asAvgPool2D()) + { + /* Originally it was preferred to use `auto load = avgpool->arg()->asLoad()' for + * consitent style with other if statements. + * Someone may think compiler will be happy because `load` in `if` statement can + * be considered as a use, however, it turend out that it is not the case. + */ + if (avgpool->arg()->asLoad()) + { + if (avgpool->divisor() == coco::AvgPool2D::Divisor::Static) + { + res.insert(avgpool); + } + } + } + } + } + + return res; +} + +} // namespace + +namespace +{ +namespace ShapeTransform +{ + +class Pad +{ +public: + Pad(const coco::Padding2D *pad) : _pad{pad} + { + // DO NOTHING + } + +public: + /// @brief Return the expected OFM shape for a given IFM shape + feature::Shape forward(const feature::Shape &ifm_shape) const + { + const uint32_t OFM_C = ifm_shape.depth(); + const uint32_t OFM_H = ifm_shape.height() + _pad->top() + _pad->bottom(); + const uint32_t OFM_W = ifm_shape.width() + _pad->left() + _pad->right(); + + return feature::Shape{OFM_C, OFM_H, OFM_W}; + } + +private: + const coco::Padding2D *_pad; +}; + +} // namespace ShapeTransform + +ShapeTransform::Pad shape_xform(const coco::Padding2D *pad) { return ShapeTransform::Pad{pad}; } + +} // namespace + +namespace +{ + +class PadInstrBuilder final +{ +public: + PadInstrBuilder(const coco::Padding2D *pad) : _pad{pad} + { + // DO NOTHING + } + +public: + coco::Instr *build(coco::FeatureObject *ifm_obj, coco::FeatureObject *ofm_obj) const + { + assert(ifm_obj->module() == ofm_obj->module()); + auto m = ifm_obj->module(); + assert(m != nullptr); + + auto load_op = m->entity()->op()->create(); + + load_op->object(ifm_obj); + + auto pad_op = m->entity()->op()->create(); + + pad_op->arg(load_op); + + pad_op->pad()->top(_pad->top()); + pad_op->pad()->bottom(_pad->bottom()); + pad_op->pad()->left(_pad->left()); + pad_op->pad()->right(_pad->right()); + + auto pad_instr = m->entity()->instr()->create(); + + pad_instr->out(ofm_obj); + pad_instr->op(pad_op); + + return pad_instr; + } + +private: + const coco::Padding2D *_pad; +}; + +PadInstrBuilder pad_instr_builder(const coco::Padding2D *pad) { return PadInstrBuilder{pad}; } + +} // namespace + +namespace +{ + +class AvgPoolRewritePass +{ +private: + void runOnModule(coco::Module *m) const; + +public: + void runOnCode(enco::Code *) const; +}; + +void AvgPoolRewritePass::runOnModule(coco::Module *m) const +{ + // Lower AvgPool2D op that resides in Eval instruction + for (auto avgpool : candidates(m)) + { + auto ins = avgpool->parent(); + auto load = avgpool->arg()->asLoad(); + + assert(ins != nullptr); + assert(load != nullptr); + assert(avgpool->divisor() == coco::AvgPool2D::Divisor::Static); + + if (empty(avgpool->pad())) + { + // NOTE If there is no padding, Static and PaddingExcluded schemes are equivalent + avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded); + } + else + { + // Before: Static AvgPool2D with Padding + // After: PadF; PaddingExcluded AvgPool2D without Padding + + // Create PadF + auto ifm_obj = load->object()->asFeature(); + assert(ifm_obj != nullptr); + + auto pad_shape = shape_xform(avgpool->pad()).forward(ifm_obj->shape()); + auto pad_bag = m->entity()->bag()->create(num_elements(pad_shape)); + auto pad_obj = m->entity()->object()->create(); + + pad_obj->bag(pad_bag); + pad_obj->layout(coco::FeatureLayouts::BHWC::create(pad_shape)); + + auto pad_instr = pad_instr_builder(avgpool->pad()).build(ifm_obj, pad_obj); + + // Insert PadF before AvgPool2D + pad_instr->insertBefore(ins); + + // Rewrite AvgPool2D as PaddingExcluded AvgPool2D without Padding + load->object(pad_obj); + + avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded); + avgpool->pad()->top(0); + avgpool->pad()->bottom(0); + avgpool->pad()->left(0); + avgpool->pad()->right(0); + } + } +} + +void AvgPoolRewritePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); } + +} // namespace + +namespace enco +{ + +void lower_avgpool(enco::Code *code) +{ + AvgPoolRewritePass pass; + pass.runOnCode(code); +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/AvgPoolLowering.h b/compiler/enco/core/src/Transforms/AvgPoolLowering.h new file mode 100644 index 000000000..71a5253df --- /dev/null +++ b/compiler/enco/core/src/Transforms/AvgPoolLowering.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __REWRITE_H__ +#define __REWRITE_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Rewrite NN API-incompatible average pooling + */ +void lower_avgpool(enco::Code *); + +struct AvgPoolLoweringPass final : public Pass +{ + PASS_CTOR(AvgPoolLoweringPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { lower_avgpool(code(sess)); } +}; + +} // namespace enco + +#endif // __REWRITE_H__ diff --git a/compiler/enco/core/src/Transforms/ConcatLowering.cpp b/compiler/enco/core/src/Transforms/ConcatLowering.cpp new file mode 100644 index 000000000..bf613c983 --- /dev/null +++ b/compiler/enco/core/src/Transforms/ConcatLowering.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CopyLowering.h" +#include "IRUtils.h" + +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace +{ + +inline uint32_t as_tensor_axis(const coco::ConcatF::Axis &axis) +{ + switch (axis) + { + case coco::ConcatF::Axis::Batch: + return 0; + case coco::ConcatF::Axis::Depth: + return 1; + case coco::ConcatF::Axis::Height: + return 2; + case coco::ConcatF::Axis::Width: + return 3; + default: + break; + }; + + throw std::invalid_argument{"axis is unknown value"}; +} + +tensor::Shape as_tensor_shape(const coco::FeatureLayout *l) +{ + assert(l != nullptr); + + tensor::Shape res; + + res.resize(4); + + res.dim(as_tensor_axis(coco::ConcatF::Axis::Batch)) = l->batch(); + res.dim(as_tensor_axis(coco::ConcatF::Axis::Depth)) = l->depth(); + res.dim(as_tensor_axis(coco::ConcatF::Axis::Height)) = l->height(); + res.dim(as_tensor_axis(coco::ConcatF::Axis::Width)) = l->width(); + + return res; +} + +coco::ElemID as_element_index(const coco::FeatureLayout *l, const tensor::Index &idx) +{ + assert(l != nullptr); + assert(idx.rank() == 4); + + const auto b = idx.at(as_tensor_axis(coco::ConcatF::Axis::Batch)); + const auto ch = idx.at(as_tensor_axis(coco::ConcatF::Axis::Depth)); + const auto row = idx.at(as_tensor_axis(coco::ConcatF::Axis::Height)); + const auto col = idx.at(as_tensor_axis(coco::ConcatF::Axis::Width)); + + return l->at(b, ch, row, col); +} + +std::set candidates(coco::Module *m) +{ + std::set res; + + for (auto ins : enco::instr_sequence(m)) + { + if (auto eval = ins->asEval()) + { + if (eval->op()->asConcatF()) + { + res.insert(eval); + } + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void lower_concat(enco::Code *code) +{ + auto m = code->module(); + + for (auto eval : candidates(m)) + { + auto concat_f = eval->op()->asConcatF(); + assert(concat_f != nullptr); + + auto left_feature = concat_f->left()->asLoad()->object()->asFeature(); + assert(left_feature != nullptr); + auto left_shape = as_tensor_shape(left_feature->layout()); + + auto right_feature = concat_f->right()->asLoad()->object()->asFeature(); + assert(right_feature != nullptr); + auto right_shape = as_tensor_shape(right_feature->layout()); + + auto out_feature = eval->out()->asFeature(); + assert(out_feature != nullptr); + auto out_shape = as_tensor_shape(out_feature->layout()); + + auto concat_axe = as_tensor_axis(concat_f->axis()); + + // Lower: Left -> Output + { + auto src_feature = left_feature; + auto src_shape = left_shape; + + auto ins = m->entity()->instr()->create(); + + assert(src_feature->bag() != nullptr); + assert(out_feature->bag() != nullptr); + + ins->from(src_feature->bag()); + ins->into(out_feature->bag()); + + for (tensor::IndexEnumerator e{src_shape}; e.valid(); e.advance()) + { + tensor::Index src_index = e.current(); + tensor::Index out_index = e.current(); + + auto from = as_element_index(src_feature->layout(), src_index); + auto into = as_element_index(out_feature->layout(), out_index); + + ins->insert(from, into); + } + + ins->insertAfter(eval); + } + + // Lower: Right -> Output + { + auto src_feature = right_feature; + auto src_shape = right_shape; + + auto ins = m->entity()->instr()->create(); + + assert(src_feature->bag() != nullptr); + assert(out_feature->bag() != nullptr); + + ins->from(src_feature->bag()); + ins->into(out_feature->bag()); + + for (tensor::IndexEnumerator e{src_shape}; e.valid(); e.advance()) + { + tensor::Index src_index = e.current(); + tensor::Index out_index = e.current(); + + out_index.at(concat_axe) = out_index.at(concat_axe) + left_shape.dim(concat_axe); + + auto from = as_element_index(src_feature->layout(), src_index); + auto into = as_element_index(out_feature->layout(), out_index); + + ins->insert(from, into); + } + + ins->insertAfter(eval); + } + + // Unlink "Eval" and "ConcatF" op tree + eval->op(nullptr); + + // Delete "Concat" op tree + m->entity()->op()->destroy(concat_f->left()); + m->entity()->op()->destroy(concat_f->right()); + m->entity()->op()->destroy(concat_f); + + // Deatch "Eval" instruction from the block + eval->detach(); + + // Delete "Eval" instruction + m->entity()->instr()->destroy(eval); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/ConcatLowering.h b/compiler/enco/core/src/Transforms/ConcatLowering.h new file mode 100644 index 000000000..5d20e627b --- /dev/null +++ b/compiler/enco/core/src/Transforms/ConcatLowering.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_CONCAT_LOWERING_H__ +#define __ENCO_CONCAT_LOWERING_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Lower eval(Concat(...)) as a sequence of shuffle instructions + */ +void lower_concat(enco::Code *code); + +struct ConcatLoweringPass final : public Pass +{ + PASS_CTOR(ConcatLoweringPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { lower_concat(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_CONCAT_LOWERING_H__ diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.cpp b/compiler/enco/core/src/Transforms/ConstantFolding.cpp new file mode 100644 index 000000000..cd6f22351 --- /dev/null +++ b/compiler/enco/core/src/Transforms/ConstantFolding.cpp @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConstantFolding.h" +#include "Session.h" + +#include +#include +#include + +namespace +{ + +/** + * @brief is_constant_bag(b) returns true if the bag "b" has corresponding weight + */ +bool is_constant_bag(coco::Bag *b) +{ + auto m = b->module(); + auto d = enco::data(m); + return d->allocated(b); +} + +class ConstantBagEnumerator +{ +public: + ConstantBagEnumerator(enco::Code *code) : _code{code} + { + // DO NOTHING + } + +public: + template void enumerate(Callable cb) const + { + auto m = _code->module(); + + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto b = m->entity()->bag()->at(n); + + if (is_constant_bag(b)) + { + cb(b); + } + } + } + +private: + enco::Code *_code; +}; + +template void operator<<(const ConstantBagEnumerator &e, Callable &&cb) +{ + e.enumerate(std::forward(cb)); +} + +ConstantBagEnumerator constant_bag_enumerator(enco::Code *code) +{ + return ConstantBagEnumerator{code}; +} + +} // namespace + +namespace +{ + +/** + * @brief Take the first element from the queue + * @note The queue SHOULD have at least one element. + */ +template T take(std::queue &q) +{ + assert(q.size() > 0); + auto res = q.front(); + q.pop(); + return res; +} + +} // namespace + +namespace +{ + +void fold_constant(std::queue &q, coco::Copy *copy) +{ + auto m = copy->module(); + auto d = enco::data(m); + + auto src_obj = copy->from(); + auto src_bag = src_obj->bag(); + + auto dst_obj = copy->into(); + auto dst_bag = dst_obj->bag(); + + // Output calculation should not be folded + // TODO Reduce code duplication of this kind + if (dst_bag->isOutput()) + { + return; + } + + // NOTE d->allocated(bag) returns true if bag has corresponding initial + // values (e.g. convolution kernel) + assert(d->allocated(src_bag)); + assert(!d->allocated(dst_bag)); + + // TODO Support other data type + auto src_span = d->f32()->weight(src_bag); + + assert(src_span.data() != nullptr); + + auto src_feature = src_obj->asFeature(); + auto dst_feature = dst_obj->asFeature(); + + // TODO Support other object type + if (src_feature == nullptr || dst_feature == nullptr) + { + return; + } + + assert(src_feature != nullptr); + assert(dst_feature != nullptr); + + // Allocate weight for destination + d->f32()->allocate(dst_bag); + + auto dst_span = d->f32()->weight(dst_bag); + + assert(src_feature->layout()->batch() == dst_feature->layout()->batch()); + assert(src_feature->layout()->depth() == dst_feature->layout()->depth()); + assert(src_feature->layout()->height() == dst_feature->layout()->height()); + assert(src_feature->layout()->width() == dst_feature->layout()->width()); + + uint32_t const B = src_feature->layout()->batch(); + uint32_t const C = src_feature->layout()->depth(); + uint32_t const H = src_feature->layout()->height(); + uint32_t const W = src_feature->layout()->width(); + + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + auto src_ind = src_feature->layout()->at(b, ch, row, col); + auto dst_ind = dst_feature->layout()->at(b, ch, row, col); + + dst_span[dst_ind.value()] = src_span[src_ind.value()]; + } + } + } + } + + // Let's detach copy + copy->from(nullptr); + copy->into(nullptr); + copy->detach(); + + // Let's visit destination bag! + q.push(dst_bag); +} + +template +void fold_constant_op(std::queue &q, coco::UnaryOp *op, Callable evaluate) +{ + auto m = op->module(); + auto d = enco::data(m); + + auto ins = op->parent(); + auto eval = ins->asEval(); + + // UnaryOp has only one arg + auto src_obj = *(op->uses().begin()); + auto src_bag = src_obj->bag(); + + auto dst_obj = eval->out(); + auto dst_bag = dst_obj->bag(); + + // Output calculation should not be folded + // TODO Reduce code duplication of this kind + if (dst_bag->isOutput()) + { + return; + } + + assert(d->allocated(src_bag)); + assert(!d->allocated(dst_bag)); + + // TODO Support other data type + auto src_span = d->f32()->weight(src_bag); + assert(src_span.data() != nullptr); + + auto src_feature = src_obj->asFeature(); + auto dst_feature = dst_obj->asFeature(); + + // TODO Support other object type + if (src_feature == nullptr || dst_feature == nullptr) + { + return; + } + + assert(src_feature != nullptr); + assert(dst_feature != nullptr); + + // Allocate weight for destination + d->f32()->allocate(dst_bag); + auto dst_span = d->f32()->weight(dst_bag); + + assert(src_feature->layout()->batch() == dst_feature->layout()->batch()); + assert(src_feature->layout()->depth() == dst_feature->layout()->depth()); + assert(src_feature->layout()->height() == dst_feature->layout()->height()); + assert(src_feature->layout()->width() == dst_feature->layout()->width()); + + uint32_t const B = src_feature->layout()->batch(); + uint32_t const C = src_feature->layout()->depth(); + uint32_t const H = src_feature->layout()->height(); + uint32_t const W = src_feature->layout()->width(); + + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + auto src_ind = src_feature->layout()->at(b, ch, row, col); + auto dst_ind = dst_feature->layout()->at(b, ch, row, col); + + evaluate(&dst_span[dst_ind.value()], src_span[src_ind.value()]); + } + } + } + } + + // Let's detach eval + eval->out(nullptr); + eval->detach(); + + // Let's visit destination bag! + q.push(dst_bag); +} + +template +void fold_constant_op(std::queue &q, coco::BinaryOp *op, Callable evaluate) +{ + auto m = op->module(); + auto d = enco::data(m); + + auto ins = op->parent(); + auto eval = ins->asEval(); + + // Already folded by the other bag + if (!eval->out()) + { + return; + } + + auto lhs_load = op->left()->asLoad(); + auto lhs_obj = lhs_load->object(); + auto lhs_bag = lhs_obj->bag(); + + auto rhs_load = op->right()->asLoad(); + auto rhs_obj = rhs_load->object(); + auto rhs_bag = rhs_obj->bag(); + + auto dst_obj = eval->out(); + auto dst_bag = dst_obj->bag(); + + // Output calculation should not be folded + // TODO Reduce code duplication of this kind + if (dst_bag->isOutput()) + { + return; + } + + // The other bag is non-constant + if (!d->allocated(lhs_bag) || !d->allocated(rhs_bag)) + { + return; + } + + assert(d->allocated(lhs_bag)); + assert(d->allocated(rhs_bag)); + assert(!d->allocated(dst_bag)); + + // TODO Support other data type + auto lhs_span = d->f32()->weight(lhs_bag); + auto rhs_span = d->f32()->weight(rhs_bag); + assert(lhs_span.data() != nullptr); + assert(rhs_span.data() != nullptr); + + auto lhs_feature = lhs_obj->asFeature(); + auto rhs_feature = rhs_obj->asFeature(); + auto dst_feature = dst_obj->asFeature(); + + // TODO Support other object type + if (lhs_feature == nullptr || rhs_feature == nullptr || dst_feature == nullptr) + { + return; + } + + assert(lhs_feature != nullptr); + assert(rhs_feature != nullptr); + assert(dst_feature != nullptr); + + // Allocate weight for destination + d->f32()->allocate(dst_bag); + auto dst_span = d->f32()->weight(dst_bag); + + assert(lhs_feature->layout()->batch() == rhs_feature->layout()->batch()); + assert(lhs_feature->layout()->depth() == rhs_feature->layout()->depth()); + assert(lhs_feature->layout()->height() == rhs_feature->layout()->height()); + assert(lhs_feature->layout()->width() == rhs_feature->layout()->width()); + + assert(lhs_feature->layout()->batch() == dst_feature->layout()->batch()); + assert(lhs_feature->layout()->depth() == dst_feature->layout()->depth()); + assert(lhs_feature->layout()->height() == dst_feature->layout()->height()); + assert(lhs_feature->layout()->width() == dst_feature->layout()->width()); + + uint32_t const B = lhs_feature->layout()->batch(); + uint32_t const C = lhs_feature->layout()->depth(); + uint32_t const H = lhs_feature->layout()->height(); + uint32_t const W = lhs_feature->layout()->width(); + + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + auto lhs_ind = lhs_feature->layout()->at(b, ch, row, col); + auto rhs_ind = rhs_feature->layout()->at(b, ch, row, col); + auto dst_ind = dst_feature->layout()->at(b, ch, row, col); + + evaluate(&dst_span[dst_ind.value()], lhs_span[lhs_ind.value()], + rhs_span[rhs_ind.value()]); + } + } + } + } + + // Let's detach eval + eval->out(nullptr); + eval->detach(); + + // Let's visit destination bag! + q.push(dst_bag); +} + +void fold_constant(std::queue &q, coco::Eval *eval) +{ + // TODO Support other data types + if (auto op = eval->op()->asSqrt()) + { + fold_constant_op(q, op, [](float *dst, float value) { *dst = std::sqrt(value); }); + } + else if (auto op = eval->op()->asAdd()) + { + fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs + rhs; }); + } + else if (auto op = eval->op()->asSub()) + { + fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs - rhs; }); + } + else if (auto op = eval->op()->asMul()) + { + fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs * rhs; }); + } + else if (auto op = eval->op()->asDiv()) + { + fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs / rhs; }); + } + else + { + // Not supported opteration, do nothing + // TODO Support other operations + } +} + +void fold_constant(std::queue &q, coco::Instr *ins) +{ + if (auto copy = coco::safe_cast(ins)) + { + fold_constant(q, copy); + return; + } + if (auto eval = coco::safe_cast(ins)) + { + fold_constant(q, eval); + return; + } + + // TODO Add more cases for constant folding +} + +} // namespace + +namespace enco +{ + +void fold_constants(enco::Code *code) +{ + std::queue q; + + // Collect the initial set of "constant" bag + constant_bag_enumerator(code) << [&q](coco::Bag *bag) { q.push(bag); }; + + while (!q.empty()) + { + auto candidate_bag = take(q); + + // Scan the readers of each candidate bag + for (auto reader : coco::readers(candidate_bag)) + { + // TODO Decide how to handle the reader with unknown instruction + if (auto ins = reader->loc()) + { + fold_constant(q, ins); + } + } + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.h b/compiler/enco/core/src/Transforms/ConstantFolding.h new file mode 100644 index 000000000..6faa9c876 --- /dev/null +++ b/compiler/enco/core/src/Transforms/ConstantFolding.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONSTANT_FOLDING_H__ +#define __CONSTANT_FOLDING_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Evaluate "constant" expressions at compile time + */ +void fold_constants(enco::Code *); + +struct ConstantFoldingPass final : public Pass +{ + PASS_CTOR(ConstantFoldingPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { fold_constants(code(sess)); } +}; + +} // namespace enco + +#endif // __CONSTANT_FOLDING_H__ diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp b/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp new file mode 100644 index 000000000..5ac71ac14 --- /dev/null +++ b/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConstantFolding.h" +#include "Session.h" + +#include +#include + +namespace +{ + +class BinaryNetwork +{ +public: + BinaryNetwork(coco::Module *module, coco::Data *data) : _module{module}, _data{data} + { + // DO NOTHING + } + + template void build(void); + + void fold(void) + { + // Execute constant folding + enco::make_session(_module, _data); + enco::Code code{_module, _data}; + enco::fold_constants(&code); + } + +public: + coco::Bag *out; + coco::Bag *lhs; + coco::Bag *rhs; + + coco::Eval *eval; + +private: + coco::Module *_module; + coco::Data *_data; +}; + +template void BinaryNetwork::build(void) +{ + // Create lhs bag and object + auto lhs_bag = _module->entity()->bag()->create(12); + auto lhs_obj = _module->entity()->object()->template create(); + coco::FeatureShape lhs_shape(1, 2, 2, 3); + lhs_obj->bag(lhs_bag); + lhs_obj->layout(coco::FeatureLayouts::BHWC::create(lhs_shape)); + + // Create rhs bag and object + auto rhs_bag = _module->entity()->bag()->create(12); + auto rhs_obj = _module->entity()->object()->template create(); + coco::FeatureShape rhs_shape(1, 2, 2, 3); + rhs_obj->bag(rhs_bag); + rhs_obj->layout(coco::FeatureLayouts::BHWC::create(rhs_shape)); + + // Create output bag and object + auto output_bag = _module->entity()->bag()->create(12); + auto output_obj = _module->entity()->object()->template create(); + coco::FeatureShape ofm_shape(1, 2, 2, 3); + output_obj->bag(output_bag); + output_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape)); + + // Create instruction and operations + auto block = _module->entity()->block()->create(); + auto eval = _module->entity()->instr()->template create(); + auto load_lhs = _module->entity()->op()->template create(); + auto load_rhs = _module->entity()->op()->template create(); + auto add_op = _module->entity()->op()->template create(); + + _module->block()->append(block); + block->instr()->append(eval); + + load_lhs->object(lhs_obj); + load_rhs->object(rhs_obj); + add_op->left(load_lhs); + add_op->right(load_rhs); + + eval->op(add_op); + eval->out(output_obj); + + // Create a handle + this->lhs = lhs_bag; + this->rhs = rhs_bag; + this->out = output_bag; + + this->eval = eval; +} + +} // namespace + +TEST(ConstantFoldingTest, sqrt) +{ + auto module = coco::Module::create(); + auto data = coco::Data::create(); + + // Create input bag and object + auto input_bag = module->entity()->bag()->create(12); + auto input_obj = module->entity()->object()->create(); + coco::FeatureShape ifm_shape(1, 2, 2, 3); + input_obj->bag(input_bag); + input_obj->layout(coco::FeatureLayouts::BHWC::create(ifm_shape)); + + // Create output bag and object + auto output_bag = module->entity()->bag()->create(12); + auto output_obj = module->entity()->object()->create(); + coco::FeatureShape ofm_shape(1, 2, 2, 3); + output_obj->bag(output_bag); + output_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape)); + + // Insert values into input bag + data->f32()->allocate(input_bag); + auto input = data->f32()->weight(input_bag); + for (uint32_t idx = 0; idx < input.size(); ++idx) + { + input[idx] = (float)idx; + } + + // Create instruction and operations + auto block = module->entity()->block()->create(); + auto eval = module->entity()->instr()->create(); + auto load = module->entity()->op()->create(); + auto sqrt_op = module->entity()->op()->create(); + + module->block()->append(block); + block->instr()->append(eval); + + load->object(input_obj); + sqrt_op->arg(load); + + eval->op(sqrt_op); + eval->out(output_obj); + + // Execute constant folding + enco::make_session(module.get(), data.get()); + enco::Code code{module.get(), data.get()}; + enco::fold_constants(&code); + + // Validate the result + ASSERT_EQ(data->allocated(output_bag), true); + ASSERT_EQ(eval->out(), nullptr); + + auto output = data->f32()->weight(output_bag); + for (uint32_t idx = 0; idx < output.size(); ++idx) + { + ASSERT_FLOAT_EQ(output[idx], std::sqrt(input[idx])); + } +} + +TEST(ConstantFoldingTest, element_wise_add) +{ + auto module = coco::Module::create(); + auto data = coco::Data::create(); + + BinaryNetwork net{module.get(), data.get()}; + + // Build a network + net.build(); + + // Create alises + auto lhs_bag = net.lhs; + auto rhs_bag = net.rhs; + auto output_bag = net.out; + auto eval = net.eval; + + // Insert values into lhs and rhs bag + data->f32()->allocate(lhs_bag); + data->f32()->allocate(rhs_bag); + auto lhs = data->f32()->weight(lhs_bag); + auto rhs = data->f32()->weight(rhs_bag); + for (uint32_t idx = 0; idx < lhs.size(); ++idx) + { + lhs[idx] = (float)idx; + rhs[idx] = 1.5; + } + + // Execute constant folding + net.fold(); + + // Validate the result + ASSERT_EQ(data->allocated(output_bag), true); + ASSERT_EQ(eval->out(), nullptr); + + auto output = data->f32()->weight(output_bag); + for (uint32_t idx = 0; idx < output.size(); ++idx) + { + ASSERT_FLOAT_EQ(output[idx], lhs[idx] + rhs[idx]); + } +} + +TEST(ConstantFoldingTest, element_wise_sub) +{ + auto module = coco::Module::create(); + auto data = coco::Data::create(); + + BinaryNetwork net{module.get(), data.get()}; + + // Build a network + net.build(); + + // Create alises + auto lhs_bag = net.lhs; + auto rhs_bag = net.rhs; + auto output_bag = net.out; + auto eval = net.eval; + + // Insert values into lhs and rhs bag + data->f32()->allocate(lhs_bag); + data->f32()->allocate(rhs_bag); + auto lhs = data->f32()->weight(lhs_bag); + auto rhs = data->f32()->weight(rhs_bag); + for (uint32_t idx = 0; idx < lhs.size(); ++idx) + { + lhs[idx] = (float)idx; + rhs[idx] = 1.5; + } + + // Execute constant folding + net.fold(); + + // Validate the result + ASSERT_EQ(data->allocated(output_bag), true); + ASSERT_EQ(eval->out(), nullptr); + + auto output = data->f32()->weight(output_bag); + for (uint32_t idx = 0; idx < output.size(); ++idx) + { + ASSERT_FLOAT_EQ(output[idx], lhs[idx] - rhs[idx]); + } +} + +TEST(ConstantFoldingTest, element_wise_mul) +{ + auto module = coco::Module::create(); + auto data = coco::Data::create(); + + BinaryNetwork net{module.get(), data.get()}; + + // Build a network + net.build(); + + // Create alises + auto lhs_bag = net.lhs; + auto rhs_bag = net.rhs; + auto output_bag = net.out; + auto eval = net.eval; + + // Insert values into lhs and rhs bag + data->f32()->allocate(lhs_bag); + data->f32()->allocate(rhs_bag); + auto lhs = data->f32()->weight(lhs_bag); + auto rhs = data->f32()->weight(rhs_bag); + for (uint32_t idx = 0; idx < lhs.size(); ++idx) + { + lhs[idx] = (float)idx; + rhs[idx] = 1.5; + } + + // Execute constant folding + net.fold(); + + // Validate the result + ASSERT_EQ(data->allocated(output_bag), true); + ASSERT_EQ(eval->out(), nullptr); + + auto output = data->f32()->weight(output_bag); + for (uint32_t idx = 0; idx < output.size(); ++idx) + { + ASSERT_FLOAT_EQ(output[idx], lhs[idx] * rhs[idx]); + } +} + +TEST(ConstantFoldingTest, element_wise_div) +{ + auto module = coco::Module::create(); + auto data = coco::Data::create(); + + BinaryNetwork net{module.get(), data.get()}; + + // Build a network + net.build(); + + // Create alises + auto lhs_bag = net.lhs; + auto rhs_bag = net.rhs; + auto output_bag = net.out; + auto eval = net.eval; + + // Insert values into lhs and rhs bag + data->f32()->allocate(lhs_bag); + data->f32()->allocate(rhs_bag); + auto lhs = data->f32()->weight(lhs_bag); + auto rhs = data->f32()->weight(rhs_bag); + for (uint32_t idx = 0; idx < lhs.size(); ++idx) + { + lhs[idx] = (float)idx; + rhs[idx] = 1.5; + } + + // Execute constant folding + net.fold(); + + // Validate the result + ASSERT_EQ(data->allocated(output_bag), true); + ASSERT_EQ(eval->out(), nullptr); + + auto output = data->f32()->weight(output_bag); + for (uint32_t idx = 0; idx < output.size(); ++idx) + { + ASSERT_FLOAT_EQ(output[idx], lhs[idx] / rhs[idx]); + } +} diff --git a/compiler/enco/core/src/Transforms/CopyLowering.cpp b/compiler/enco/core/src/Transforms/CopyLowering.cpp new file mode 100644 index 000000000..ceb3bbd5c --- /dev/null +++ b/compiler/enco/core/src/Transforms/CopyLowering.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CopyLowering.h" + +#include +#include + +// +// Lower Copy as Shuffle +// +namespace enco +{ + +void lower_copy(enco::Code *code) +{ + auto m = code->module(); + + std::set lowered_copies; + + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + auto ins = m->entity()->instr()->at(n); + + assert(ins != nullptr); + + if (ins->parent() == nullptr) + { + // Skip if instruction does not belong to a list + continue; + } + + auto copy = ins->asCopy(); + + if (copy == nullptr) + { + // Skip if instruction is not a copy + continue; + } + + // TODO Support non-Feature objects + auto ifm = copy->from()->asFeature(); + auto ofm = copy->into()->asFeature(); + + if ((ifm == nullptr) || (ofm == nullptr)) + { + continue; + } + + assert(ifm->layout()->batch() == ofm->layout()->batch()); + assert(ifm->layout()->shape() == ofm->layout()->shape()); + + auto shuffle = m->entity()->instr()->create(); + + shuffle->from(ifm->bag()); + shuffle->into(ofm->bag()); + + const uint32_t B = ifm->layout()->batch(); + const uint32_t C = ifm->layout()->shape().depth(); + const uint32_t H = ifm->layout()->shape().height(); + const uint32_t W = ifm->layout()->shape().width(); + + for (uint32_t b = 0; b < B; ++b) + { + for (uint32_t ch = 0; ch < C; ++ch) + { + for (uint32_t row = 0; row < H; ++row) + { + for (uint32_t col = 0; col < W; ++col) + { + const auto from = ifm->layout()->at(b, ch, row, col); + const auto into = ofm->layout()->at(b, ch, row, col); + + shuffle->insert(from, into); + } + } + } + } + + shuffle->insertBefore(copy); + lowered_copies.insert(copy); + } + + // Destroy lowered copy + for (const auto © : lowered_copies) + { + copy->detach(); + m->entity()->instr()->destroy(copy); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/CopyLowering.h b/compiler/enco/core/src/Transforms/CopyLowering.h new file mode 100644 index 000000000..51f0f83e2 --- /dev/null +++ b/compiler/enco/core/src/Transforms/CopyLowering.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_LOWER_H__ +#define __ENCO_LOWER_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Lower copy(...) instruction into shuffle(...) + */ +void lower_copy(enco::Code *code); + +struct CopyLoweringPass final : public Pass +{ + PASS_CTOR(CopyLoweringPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { lower_copy(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_LOWER_H__ diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp b/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp new file mode 100644 index 000000000..9d65d1c0b --- /dev/null +++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DataLayoutConversion.h" +#include "Session.h" +#include "IRUtils.h" + +#include "coex/IR.h" + +#include +#include + +#include +#include + +#include +#include + +#include + +namespace +{ + +coco::Copy *make_copy(coco::FeatureObject *from, coco::FeatureObject *into) +{ + auto m = from->module(); + assert(m != nullptr); + assert(from->module() == into->module()); + + auto copy = m->entity()->instr()->create(); + + copy->from(from); + copy->into(into); + + return copy; +} + +coco::FeatureObject *clone_feature(const coco::FeatureObject *oldobj) +{ + auto module = oldobj->module(); + auto newobj = module->entity()->object()->create(); + newobj->layout(coco::FeatureLayouts::BHWC::create(oldobj->shape())); + + if (oldobj->bag() != nullptr) + { + using nncc::core::ADT::feature::num_elements; + + // NOTE The size of bag should be at least "BxHxWxC" as "newobj" uses BHWC layout + const uint32_t batch = newobj->layout()->batch(); + const uint32_t count = num_elements(newobj->layout()->shape()); + const uint32_t bag_size = batch * count; + + // Clone bag only when there is a backing bag for a given feature object + auto newbag = module->entity()->bag()->create(bag_size); + newobj->bag(newbag); + } + + return newobj; +} + +/** + * @brief Insert Copy before Load if necessary + * + * @require "load" should be bounded + */ +void insert_copy_before_load(coco::Load *load) +{ + assert(load->parent() != nullptr); + assert(load->parent()->parent() != nullptr); + + if (auto obj = load->object()) + { + if (auto ifm = obj->asFeature()) + { + if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + auto oldobj = ifm; + auto newobj = clone_feature(oldobj); + + load->object(newobj); + + auto copy = make_copy(oldobj, newobj); + copy->insertBefore(load->parent()); + } + } + } +} + +/** + * @brief Insert Copy after Eval if necessary + */ +void insert_copy_after_eval(coco::Eval *eval) +{ + if (auto out = eval->out()) + { + if (auto ofm = out->asFeature()) + { + if (ofm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + auto oldobj = ofm; + auto newobj = clone_feature(oldobj); + + eval->out(newobj); + + auto copy = make_copy(newobj, oldobj); + copy->insertAfter(eval); + } + } + } +} + +/** + * @brief Insert copy (for data layout change) before/after ANNDepthConcatF if necessary + */ +void convert_data_layout(ANNDepthConcatF *concat) +{ + if (auto out = concat->out()) + { + if (auto ofm = out->asFeature()) + { + if (ofm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + auto oldobj = ofm; + auto newobj = clone_feature(oldobj); + + concat->out(newobj); + + auto copy = make_copy(newobj, oldobj); + copy->insertAfter(concat); + } + } + } + + if (auto obj = concat->fst()) + { + if (auto ifm = obj->asFeature()) + { + if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + auto oldobj = ifm; + auto newobj = clone_feature(oldobj); + + concat->fst(newobj); + + auto copy = make_copy(oldobj, newobj); + copy->insertBefore(concat); + } + } + } + + if (auto obj = concat->snd()) + { + if (auto ifm = obj->asFeature()) + { + if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + auto oldobj = ifm; + auto newobj = clone_feature(oldobj); + + concat->snd(newobj); + + auto copy = make_copy(oldobj, newobj); + copy->insertBefore(concat); + } + } + } +} + +/** + * @brief Update convolution kernel data layout + */ +void change_conv2d_kernel_layout(coco::Conv2D *conv) +{ + auto m = conv->module(); + assert(m != nullptr); + auto d = enco::data(enco::session(m)); + assert(d != nullptr); + + auto old_obj = conv->ker(); + assert(old_obj != nullptr); + auto old_bag = old_obj->bag(); + assert(old_bag != nullptr); + + if (old_obj->layout()->id() == coco::KernelLayouts::NHWC::uid()) + { + // Skip if kernel already uses NHWC layout + return; + } + + const auto &ker_shape = old_obj->shape(); + + assert(d->allocated(old_bag)); + + auto new_bag = m->entity()->bag()->create(old_bag->size()); + auto new_obj = m->entity()->object()->create(); + + new_obj->bag(new_bag); + new_obj->layout(coco::KernelLayouts::NHWC::create(ker_shape)); + + d->f32()->allocate(new_bag); + + auto src = d->f32()->read(old_obj); + auto dst = d->f32()->access(new_obj); + + const auto ker_N = ker_shape.count(); + const auto ker_C = ker_shape.depth(); + const auto ker_H = ker_shape.height(); + const auto ker_W = ker_shape.width(); + + for (uint32_t n = 0; n < ker_N; ++n) + { + for (uint32_t ch = 0; ch < ker_C; ++ch) + { + for (uint32_t row = 0; row < ker_H; ++row) + { + for (uint32_t col = 0; col < ker_W; ++col) + { + dst->at(n, ch, row, col) = src->at(n, ch, row, col); + } + } + } + } + + conv->ker(new_obj); + d->release(old_bag); +} + +} // namespace + +namespace +{ + +/** + * @brief Return the set of all of bounded Load Op(s) in a given module + * + * @note 'bounded' means it will be exectuted + */ +std::set loads(coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->op()->size(); ++n) + { + auto op = m->entity()->op()->at(n); + + // Skip if this op is dangling + if (op->parent() == nullptr) + { + continue; + } + + // Skip if eval instruction of this op is dangling + if (op->parent()->parent() == nullptr) + { + continue; + } + + if (auto load = m->entity()->op()->at(n)->asLoad()) + { + res.insert(load); + } + } + + return res; +} + +/** + * @brief Return the set of every (allocated) Eval instruction in a given module + */ +std::set evals(coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + if (auto eval = m->entity()->instr()->at(n)->asEval()) + { + res.insert(eval); + } + } + + return res; +} + +/** + * @brief Return the set of allocated Conv2D op in a given module + */ +std::set convs(coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->op()->size(); ++n) + { + if (auto op = m->entity()->op()->at(n)->asConv2D()) + { + res.insert(op); + } + } + + return res; +} + +/** + * @brief Return the set of "bounded" ANNDepthConcatF instructions + */ +std::set depth_concats(coco::Module *m) +{ + std::set res; + + for (auto ins : enco::instr_sequence(m)) + { + if (auto depth_concat_f = coco::safe_cast(ins)) + { + res.insert(depth_concat_f); + } + } + + return res; +} + +class NormalizePass +{ +private: + void runOnModule(coco::Module *m) const; + +public: + void runOnCode(enco::Code *) const; +}; + +void NormalizePass::runOnModule(coco::Module *m) const +{ + // Insert Copy before all Load Op (if necessary) + for (auto load : loads(m)) + { + insert_copy_before_load(load); + } + + // Insert Copy after all Eval Instr (if necessary) + for (auto eval : evals(m)) + { + insert_copy_after_eval(eval); + } + + // Change Kernel Layout of Conv2D opertion (if necessary) + for (auto conv : convs(m)) + { + change_conv2d_kernel_layout(conv); + } + + // Insert Copy (for Layout Conversion) before/after ANNDepthConcatF instructions (if necessary) + for (auto depth_concat : depth_concats(m)) + { + convert_data_layout(depth_concat); + } +} + +void NormalizePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); } + +} // namespace + +namespace enco +{ + +void convert_data_layout(enco::Code *code) +{ + NormalizePass pass; + pass.runOnCode(code); +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.h b/compiler/enco/core/src/Transforms/DataLayoutConversion.h new file mode 100644 index 000000000..ac4052c8b --- /dev/null +++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__ +#define __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Insert data reordering if necessary + */ +void convert_data_layout(enco::Code *code); + +struct DataLayoutConversionPass final : public enco::Pass +{ + PASS_CTOR(DataLayoutConversionPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { convert_data_layout(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__ diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp b/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp new file mode 100644 index 000000000..812e38a78 --- /dev/null +++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DataLayoutConversion.h" + +#include + +TEST(DataLayoutConversionTest, case_000) +{ + auto m = coco::Module::create(); + + // Create a "free" Load op + m->entity()->instr()->create(); + + enco::Code code{m.get(), nullptr}; + ASSERT_EQ(m->entity()->instr()->size(), 1); + + // "conver_data_layout" SHOULD NOT crash even if there is a "free" Load op + enco::convert_data_layout(&code); +} diff --git a/compiler/enco/core/src/Transforms/DeadBagElimination.cpp b/compiler/enco/core/src/Transforms/DeadBagElimination.cpp new file mode 100644 index 000000000..b3c598a55 --- /dev/null +++ b/compiler/enco/core/src/Transforms/DeadBagElimination.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeadBagElimination.h" + +#include + +namespace +{ + +/// @brief Return true if a given bag is marked as either input or output +bool is_public(const coco::Bag *b) { return b->isInput() || b->isOutput(); } + +/// @brief Return the set of "dead" bags in a given module +std::set dead_bags(const coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + if (coco::readers(bag).empty() && !is_public(bag)) + { + res.insert(bag); + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void eliminate_dead_bag(enco::Code *code) +{ + auto m = code->module(); + + // Destroy a dead bag and its updaters + for (auto bag : dead_bags(m)) + { + for (auto updater : coco::updaters(bag)) + { + auto ins = updater->loc(); + + assert(ins != nullptr); + + ins->detach(); + m->entity()->instr()->destroy(ins); + } + + bag->replaceWith(nullptr); + m->entity()->bag()->destroy(bag); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/DeadBagElimination.h b/compiler/enco/core/src/Transforms/DeadBagElimination.h new file mode 100644 index 000000000..87e03e8ac --- /dev/null +++ b/compiler/enco/core/src/Transforms/DeadBagElimination.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__ +#define __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Eliminate dead bags + * + * A bag is referred to as dead if it is neither input nor output, and has no read. If a bag is + * dead, it is unnecessary to updates its values as these values are never used. + * + * "eliminate_dead_bag" removes all the dead bags and its updaters from IR. + */ +void eliminate_dead_bag(enco::Code *code); + +struct DeadBagEliminationPass final : public Pass +{ + PASS_CTOR(DeadBagEliminationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { eliminate_dead_bag(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__ diff --git a/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp b/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp new file mode 100644 index 000000000..df8cc628a --- /dev/null +++ b/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeadObjectElimination.h" + +#include + +namespace +{ + +std::set dead_objects(const coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->object()->size(); ++n) + { + auto obj = m->entity()->object()->at(n); + + if (auto bag = obj->bag()) + { + if (coco::readers(bag).empty() && !(bag->isOutput())) + { + res.insert(obj); + } + } + else + { + // NOTE Just in case if there are Objects not related to Bags + if (obj->uses()->size() == 0) + { + res.insert(obj); + } + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void eliminate_dead_object(enco::Code *code) +{ + auto m = code->module(); + + // Destroy a dead object and its producer + for (auto obj : dead_objects(m)) + { + if (auto producer = coco::producer(obj)) + { + auto ins = producer->loc(); + assert(ins != nullptr); + + ins->detach(); + m->entity()->instr()->destroy(ins); + } + + m->entity()->object()->destroy(obj); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/DeadObjectElimination.h b/compiler/enco/core/src/Transforms/DeadObjectElimination.h new file mode 100644 index 000000000..4923e56fd --- /dev/null +++ b/compiler/enco/core/src/Transforms/DeadObjectElimination.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__ +#define __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Eliminate dead objects in IR + * + * An object whose backing bag is unused is referred to as a dead object. + * + * Dead Object Elimination (DOE) eliminates such dead objects along with their producer. + */ +void eliminate_dead_object(enco::Code *code); + +struct DeadObjectEliminationPass final : public Pass +{ + PASS_CTOR(DeadObjectEliminationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { eliminate_dead_object(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__ diff --git a/compiler/enco/core/src/Transforms/Duplicate.cpp b/compiler/enco/core/src/Transforms/Duplicate.cpp new file mode 100644 index 000000000..91f64a0ad --- /dev/null +++ b/compiler/enco/core/src/Transforms/Duplicate.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Duplicate.h" + +#include +#include + +#include + +namespace +{ + +coco::Block *find_or_create_first_block(coco::Module *m) +{ + if (m->block()->empty()) + { + auto blk = m->entity()->block()->create(); + m->block()->append(blk); + return blk; + } + + return m->block()->head(); +} + +} // namespace + +namespace +{ + +class DuplicatePass +{ +private: + void runOnModule(coco::Module *m) const; + +public: + void runOnCode(enco::Code *) const; +}; + +void DuplicatePass::runOnModule(coco::Module *m) const +{ + // Let's find candidates + std::set candidates; + + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + if (bag->isInput() && bag->isOutput()) + { + candidates.insert(bag); + } + } + + // Return if there is no candidate + if (candidates.empty()) + { + return; + } + + std::map input_map; + std::map output_map; + + for (uint32_t n = 0; n < m->input()->size(); ++n) + { + auto input = m->input()->at(n); + assert(input->bag() != nullptr); + input_map[input->bag()] = input; + } + + for (uint32_t n = 0; n < m->output()->size(); ++n) + { + auto output = m->output()->at(n); + assert(output->bag() != nullptr); + output_map[output->bag()] = output; + } + + // For each in/out bag, + // 1. Create a new bag of the same size + // 2. Copy the content from the original bag + // 3. Mark the newly created bag as an output + for (const auto &candidate : candidates) + { + assert(coco::updaters(candidate).empty()); + assert(input_map.find(candidate) != input_map.end()); + assert(output_map.find(candidate) != output_map.end()); + + auto src = candidate; + auto dst = m->entity()->bag()->create(src->size()); + + // Create a copy instruction + auto shuffle = m->entity()->instr()->create(); + + shuffle->from(src); + shuffle->into(dst); + + for (uint32_t n = 0; n < src->size(); ++n) + { + shuffle->insert(coco::ElemID{n} /* FROM */, coco::ElemID{n} /* INTO */); + } + + find_or_create_first_block(m)->instr()->prepend(shuffle); + + // Let's use the new bag as an output + output_map.at(src)->bag(dst); + } +} + +void DuplicatePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); } + +} // namespace + +namespace enco +{ + +void duplicate_inout_bag(enco::Code *code) +{ + DuplicatePass duplicate; + duplicate.runOnCode(code); +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/Duplicate.h b/compiler/enco/core/src/Transforms/Duplicate.h new file mode 100644 index 000000000..93baa4589 --- /dev/null +++ b/compiler/enco/core/src/Transforms/Duplicate.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DUPLICATE_H__ +#define __DUPLICATE_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Eliminate in/out bags by duplication + */ +void duplicate_inout_bag(enco::Code *code); + +struct BagDuplicationPass final : public Pass +{ + PASS_CTOR(BagDuplicationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { duplicate_inout_bag(code(sess)); } +}; + +} // namespace enco + +#endif // __DUPLICATE_H__ diff --git a/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp new file mode 100644 index 000000000..fa84c005c --- /dev/null +++ b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DuplicatedObjectReduction.h" + +#include "CodeIndex.h" +#include "IRUtils.h" + +#include + +namespace +{ + +/** + * @brief Collect feature objects in coco IR + */ +std::set features(const coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->object()->size(); ++n) + { + if (auto feature = m->entity()->object()->at(n)->asFeature()) + { + res.insert(feature); + } + } + + return res; +} + +std::set candidates(const coco::FeatureObject *src) +{ + std::set res; + + for (auto consumer : coco::consumers(src)) + { + if (auto copy = consumer->loc()->asCopy()) + { + auto dst = copy->into()->asFeature(); + assert(dst != nullptr); + + if (dst->layout()->id() == coco::FeatureLayouts::BHWC::uid()) + { + res.insert(dst); + } + } + } + + return res; +} + +CodeIndex code_index(coco::Object::Producer *p) +{ + if (auto ins = p->loc()) + { + return ::code_index(ins); + } + + return CodeIndex{}; +} + +} // namespace + +namespace enco +{ + +void reduce_duplicated_object(enco::Code *code) +{ + auto m = code->module(); + + for (const auto &src : features(m)) + { + auto copied = candidates(src); + + if (copied.size() <= 1) + { + continue; + } + + // Find the dominator + coco::FeatureObject *dominator = nullptr; + + for (auto candidate : copied) + { + if (dominator == nullptr) + { + dominator = candidate; + } + else if (code_index(coco::producer(candidate)) < code_index(coco::producer(dominator))) + { + dominator = candidate; + } + } + + // Replace all the occurunce of dominated objects with its dominator + copied.erase(dominator); + + for (auto dominatee : copied) + { + subst(dominatee, dominator); + } + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h new file mode 100644 index 000000000..3aa20058e --- /dev/null +++ b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__ +#define __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Reduce duplicated feature objects as its dominating feature object + * + * >>> BEFORE <<< + * %obj_0 = Feature(layout: ???) at ... + * %obj_1 = Feature(layout: BHWC) at ... + * %obj_2 = Feature(layout: BHWC) at ... + * + * copy(from: %obj_0, into: %obj_1) + * copy(from: %obj_0, into: %obj_2) + * + * ... + * Use(%obj_1) + * Use(%obj_2) + * ... + * + * >>> AFTER <<< + * %obj_0 = Feature(layout: ???) at ... + * %obj_1 = Feature(layout: BHWC) at ... + * %obj_2 = Feature(layout: BHWC) at ... + * + * copy(from: %obj_0, into: %obj_1) + * copy(from: %obj_0, into: %obj_2) + * + * ... + * Use(%obj_1) + * Use(%obj_1) <-- CHANGED + * ... + * + * NOTE Given a set of feature objects, a feature object referred to as a dominating + * feature object if its producer proceeds the producer of every feature object + * in the given set + */ +void reduce_duplicated_object(enco::Code *code); + +struct DuplicatedObjectReductionPass final : public Pass +{ + PASS_CTOR(DuplicatedObjectReductionPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { reduce_duplicated_object(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__ diff --git a/compiler/enco/core/src/Transforms/FeatureUnification.cpp b/compiler/enco/core/src/Transforms/FeatureUnification.cpp new file mode 100644 index 000000000..1a7a0a8a4 --- /dev/null +++ b/compiler/enco/core/src/Transforms/FeatureUnification.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FeatureUnification.h" +#include "IRUtils.h" + +#include + +#include +#include + +#include + +using stdex::make_unique; + +namespace +{ + +bool is_static_layout(const coco::FeatureLayout::ID *id) +{ + if (id == coco::FeatureLayouts::BHWC::uid()) + { + return true; + } + + if (id == coco::FeatureLayouts::BCHW::uid()) + { + return true; + } + + return false; +} + +bool is_static_layout(const coco::FeatureLayout *l) { return is_static_layout(l->id()); } +bool is_static_layout(const coco::FeatureObject *f) { return is_static_layout(f->layout()); } + +/** + * @brief Return ture if a given 'feature' is the candidate of unification + */ +bool candidate(const coco::FeatureObject *f) { return is_static_layout(f); } + +/** + * @brief Return true if two features are compatible + * + * Two features are referred to as compatible if these feature are interchangeable. + * + * NOTE The current implementation of "compatible" is sound, but incomplete. + * + * Soundness: + * For all feature objects "lhs" and "rhs" that "compatible(lhs, rhs)" returns true, + * "lhs" and "rhs" are interchangeable. + * + * Completeness: + * For all interchangeable feature objects "lhs" and "rhs", "compatible(lhs, rhs)" returns true. + */ +bool compatible(const coco::FeatureObject *lhs, const coco::FeatureObject *rhs) +{ + assert(candidate(lhs) && candidate(rhs)); + + if (lhs->layout()->id() != rhs->layout()->id()) + { + return false; + } + + if (lhs->layout()->batch() != rhs->layout()->batch()) + { + return false; + } + + if (!(lhs->layout()->shape() == rhs->layout()->shape())) + { + return false; + } + + return true; +} + +/** + * @brief A FeatureGroup denotes a group of FeatureObject(s) + * + * Each FeatureGroup includes at most 1 DEF FeatureObject (a FeatureObject that has a producer), + * and may include multiple USE FeatureObject(s) (a FeatureObject that has no producer). + * + * NOTE FeatureUnification pass internally uses this FeatureGroup to store a group of compatible + * FeatureObject(s) + */ +class FeatureGroup +{ +public: + explicit FeatureGroup(coco::FeatureObject *feature) { insert(feature); } + +public: + uint32_t size(void) const { return _uses.size() + (_def ? 1 : 0); } + +public: + void insert(coco::FeatureObject *feature) + { + if (feature->def() != nullptr) + { + assert(_def == nullptr); + _def = feature; + } + else + { + _uses.insert(feature); + } + } + +public: + coco::FeatureObject *parent(void) const + { + if (_def) + { + return _def; + } + + assert(_uses.size() > 0); + return *(_uses.begin()); + } + +public: + std::set children(void) const + { + auto res = _uses; + res.erase(parent()); + return res; + } + +private: + coco::FeatureObject *_def = nullptr; + std::set _uses; +}; + +} // namespace + +namespace enco +{ + +void unify_feature(enco::Code *code) +{ + auto m = code->module(); + + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + std::vector> groups; + + auto assign_group = [&](coco::FeatureObject *feature) { + // Find a compatible FeatureGroup + FeatureGroup *group = nullptr; + + for (const auto &g : groups) + { + FeatureGroup *candidate = g.get(); + + if (!compatible(candidate->parent(), feature)) + { + continue; + } + + group = candidate; + break; + } + + if (group == nullptr) + { + // Insert FeatureObject into a new FeatureGroup + groups.emplace_back(make_unique(feature)); + } + else + { + // Insert FeatureObject into the compatible FeatureGroup + group->insert(feature); + } + }; + + auto bag = m->entity()->bag()->at(n); + + for (auto o : coco::dependent_objects(bag)) + { + if (auto feature = o->asFeature()) + { + if (candidate(feature)) + { + assign_group(feature); + } + } + } + + for (const auto &g : groups) + { + auto group = g.get(); + for (const auto child : group->children()) + { + subst(child, group->parent()); + assert(child->def() == nullptr); + assert(child->uses()->size() == 0); + m->entity()->object()->destroy(child); + } + } + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/FeatureUnification.h b/compiler/enco/core/src/Transforms/FeatureUnification.h new file mode 100644 index 000000000..5ab0f9d7a --- /dev/null +++ b/compiler/enco/core/src/Transforms/FeatureUnification.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__ +#define __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Remove duplicated feature objects inside each bag + * + * >>> BEFORE <<< + * %b = Bag(...) + * + * %feature_0 = Feature(...) at %b + * %feature_1 = Feature(...) at %b + * + * ... + * Use(%feature_0) + * ... + * Use(%feature_1) + * ... + * + * >>> AFTER <<< + * %b = Bag(...) + * + * %feature_0 = Feature(...) at %b + * ~~%feature_1 = Feature(...) at %b~~ <- REMOVED + * + * ... + * Use(%feature_0) + * ... + * Use(%feature_0) + * ... + * + * Note that all the occurrences of "%feature_1" are replaced with "%feature_0" + */ +void unify_feature(enco::Code *code); + +struct FeatureUnificationPass final : public Pass +{ + PASS_CTOR(FeatureUnificationPass) + { + // DO NOTHING + } + void run(const SessionID &sess) const override { unify_feature(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__ diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp b/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp new file mode 100644 index 000000000..a62324b28 --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FreeInstrElimination.h" + +#include +#include + +namespace +{ + +/** + * @brief Return the set of "free" instructions in a given module + */ +std::set free_instrs(const coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + if (auto ins = m->entity()->instr()->at(n)) + { + if (ins->parent() == nullptr) + { + res.insert(ins); + } + } + } + + return res; +} + +void destroy(coco::Instr *ins) +{ + auto m = ins->module(); + m->entity()->instr()->destroy(ins); +} + +} // namespace + +namespace enco +{ + +void eliminate_free_instr(coco::Module *m) +{ + for (auto ins : free_instrs(m)) + { + destroy(ins); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.h b/compiler/enco/core/src/Transforms/FreeInstrElimination.h new file mode 100644 index 000000000..1d311cd35 --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__ +#define __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Eliminate free instructions + * + * An instruction is referred to as "free" if it is not bound to any "block" + */ +void eliminate_free_instr(coco::Module *mod); + +/** + * @brief Eliminate free instructions + */ +static inline void eliminate_free_instr(enco::Code *code) +{ + // This function is just a wrapper of the above "void eliminate_free_instr(coco::Module *mod)" + eliminate_free_instr(code->module()); +} + +struct FreeInstrEliminationPass final : public Pass +{ + PASS_CTOR(FreeInstrEliminationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { eliminate_free_instr(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__ diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp b/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp new file mode 100644 index 000000000..c15f32e7d --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FreeInstrElimination.h" + +#include + +TEST(FreeInstrEliminationTest, case_000) +{ + auto m = coco::Module::create(); + + // Create a "free" Eval instruction + m->entity()->instr()->create(); + + ASSERT_EQ(m->entity()->instr()->size(), 1); + + // Apply "Free Instruction Elimination" + enco::eliminate_free_instr(m.get()); + + ASSERT_EQ(m->entity()->instr()->size(), 0); +} diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.cpp b/compiler/enco/core/src/Transforms/FreeOpElimination.cpp new file mode 100644 index 000000000..25f2f44d0 --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeOpElimination.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FreeOpElimination.h" + +#include +#include + +namespace +{ + +/** + * @brief Return the set of Free Op Elimination candidates + */ +std::set candidates(const coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->op()->size(); ++n) + { + if (auto op = m->entity()->op()->at(n)) + { + if ((op->parent() == nullptr) && (op->up() == nullptr)) + { + res.insert(op); + } + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void eliminate_free_op(coco::Module *m) +{ + for (auto op : candidates(m)) + { + m->entity()->op()->destroy_all(op); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.h b/compiler/enco/core/src/Transforms/FreeOpElimination.h new file mode 100644 index 000000000..3aeacada5 --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeOpElimination.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__ +#define __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Eliminate free op + * + * An op is referred to as "free" if it is not bound to any "instruction" + */ +void eliminate_free_op(coco::Module *mod); + +/** + * @brief Eliminate free op + */ +static inline void eliminate_free_op(enco::Code *code) +{ + // This function is just a wrapper of the above "void eliminate_free_op(coco::Module *mod)" + eliminate_free_op(code->module()); +} + +struct FreeOpEliminationPass final : public Pass +{ + PASS_CTOR(FreeOpEliminationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { eliminate_free_op(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__ diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp b/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp new file mode 100644 index 000000000..41600526b --- /dev/null +++ b/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FreeOpElimination.h" + +#include + +TEST(FreeOpEliminationTest, case_000) +{ + auto m = coco::Module::create(); + + // Create a "free" Load op + m->entity()->op()->create(); + + ASSERT_EQ(m->entity()->op()->size(), 1); + + // Apply "Free Op Elimination" + enco::eliminate_free_op(m.get()); + + ASSERT_EQ(m->entity()->op()->size(), 0); +} diff --git a/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp new file mode 100644 index 000000000..152477a51 --- /dev/null +++ b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GlobalDataGeneration.h" +#include "Split.h" +#include "Dims.h" + +#include + +#include + +using stdex::make_unique; + +namespace +{ + +/** + * @brief Manage global variable declarations + */ +class Global +{ +public: + Global(std::ostream &os) : _os(os) + { + // DO NOTHING + } + +public: + /// @brief Create a global constant string (const char *) literal, and return variable name + enco::GlobalOffset constant(const std::string &value); + + /// @brief Create a global constant array variable of type T + template enco::GlobalOffset constant(const std::vector &values); + + /// @brief Create a global constant array variable of byte (uint8_t) type + enco::GlobalOffset constant(const uint8_t *base, uint32_t size); + +private: + uint32_t _offset = 0; + std::ostream &_os; +}; + +enco::GlobalOffset Global::constant(const std::string &s) +{ + auto const base = reinterpret_cast(s.c_str()); + auto const size = s.size() + 1 /* NUL */; + return constant(base, size); +} + +template <> enco::GlobalOffset Global::constant(const std::vector &values) +{ + auto const base = reinterpret_cast(values.data()); + auto const size = sizeof(uint32_t) * values.size(); + return constant(base, size); +} + +enco::GlobalOffset Global::constant(const uint8_t *base, uint32_t size) +{ + auto pos = _os.tellp(); + assert(pos != -1); + + _os.write(reinterpret_cast(base), size); + + return static_cast(pos); +} + +} // namespace + +namespace +{ + +std::map data_offset_ctx; +std::map bag_data_offset_ctx; + +std::map name_offset_ctx; +std::map dims_offset_ctx; + +} // namespace + +namespace enco +{ + +GlobalOffset GlobalData::data_offset(const ann::Operand *o) { return data_offset_ctx.at(o); } + +GlobalOffset GlobalData::data_offset(const coco::Bag *bag) +{ + assert(bag_data_offset_ctx.find(bag) != bag_data_offset_ctx.end()); + return bag_data_offset_ctx.at(bag); +} + +GlobalOffset GlobalData::name_offset(const coco::Input *in) { return name_offset_ctx.at(in); } +GlobalOffset GlobalData::dims_offset(const coco::Input *in) { return dims_offset_ctx.at(in); } + +GlobalOffset GlobalData::name_offset(const coco::Output *out) { return name_offset_ctx.at(out); } +GlobalOffset GlobalData::dims_offset(const coco::Output *out) { return dims_offset_ctx.at(out); } + +void generate_global_data(std::ostream &os, enco::Code *code) +{ + auto m = code->module(); + auto d = code->data(); + + auto ann_ctx = enco::SubnetManager::context(m); + + auto global = make_unique(os); + + // + // Emit Bag's weight + // + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + if (!d->allocated(bag)) + { + // Skip if the weight value does not exist for a given bag + continue; + } + + // NOTE The current implementation assumes that all the values are of float(fp32) type + // TODO Support non-float values + auto span = d->f32()->weight(bag); + + assert(span.data() != nullptr); + assert(span.size() > 0); + + auto const base = reinterpret_cast(span.data()); + uint32_t const size = span.size() * sizeof(float); + + assert(bag_data_offset_ctx.find(bag) == bag_data_offset_ctx.end()); + bag_data_offset_ctx[bag] = global->constant(base, size); + } + + for (uint32_t n = 0; n < ann_ctx->count(); ++n) + { + auto binder = ann_ctx->nth(n); + + auto emit = [&](const ann::OperandID & /*id*/, const ann::Operand *info) { + if (info->weight()) + { + auto base = info->weight()->base(); + auto size = info->weight()->size(); + + data_offset_ctx[info] = global->constant(base, size); + } + }; + binder->module()->operand()->each(emit); + } + + for (uint32_t n = 0; n < m->input()->size(); ++n) + { + auto input = m->input()->at(n); + auto dims = as_dims(input->shape()); + + name_offset_ctx[input] = global->constant(input->name()); + dims_offset_ctx[input] = global->constant(dims); + } + + for (uint32_t n = 0; n < m->output()->size(); ++n) + { + auto output = m->output()->at(n); + auto dims = as_dims(output->shape()); + + name_offset_ctx[output] = global->constant(output->name()); + dims_offset_ctx[output] = global->constant(dims); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/GlobalDataGeneration.h b/compiler/enco/core/src/Transforms/GlobalDataGeneration.h new file mode 100644 index 000000000..433431401 --- /dev/null +++ b/compiler/enco/core/src/Transforms/GlobalDataGeneration.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__ +#define __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__ + +#include "Code.h" + +#include + +namespace enco +{ + +using GlobalOffset = uint32_t; + +struct GlobalData +{ + static GlobalOffset data_offset(const ann::Operand *); + /** + * @brief Return the weight offset of a given bag + * + * @note The behavior of "data_offset" is undefined if a bag has no weight. + */ + static GlobalOffset data_offset(const coco::Bag *); + + static GlobalOffset name_offset(const coco::Input *); + static GlobalOffset dims_offset(const coco::Input *); + static GlobalOffset name_offset(const coco::Output *); + static GlobalOffset dims_offset(const coco::Output *); +}; + +/** + * @brief Generate 'Global' weight array. + * + * NOTE Succeeding passes can access offsets via "GlobalData" + */ +void generate_global_data(std::ostream &, enco::Code *); + +} // namespace enco + +#endif // __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__ diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp new file mode 100644 index 000000000..cb996d2ac --- /dev/null +++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IdenticalObjectReduction.h" +#include "IRUtils.h" + +#include + +namespace enco +{ + +void reduce_identical_object(enco::Code *code) +{ + auto m = code->module(); + + std::set detached; + + // Preceding optimizations may generate "free" instructions. + // - i.e. an instruction not linked to a block + // + // Let's iterate over only a sequence of "bounded" instructions. + for (auto ins : instr_sequence(m)) + { + assert(ins != nullptr); + assert(ins->parent() != nullptr); + + auto copy = ins->asCopy(); + + if (copy == nullptr) + { + // Skip if instruction is not a copy + continue; + } + + // TODO Support non-Feature Objects + auto ifm = copy->from()->asFeature(); + auto ofm = copy->into()->asFeature(); + + assert(ofm->bag() != nullptr); + + if (ifm->layout()->id() != ofm->layout()->id()) + { + continue; + } + + if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid()) + { + continue; + } + + // Skip if this copy produces network output + if (ofm->bag()->output()) + { + // TODO Optimize this case + // + // Note that the code under optimization is of the following form: + // + // %ifm <- Instr(...) + // %ofm <- Copy(%ifm) + // + // Let's assume that "Copy" is the only reader of %ifm (to be precise, its bag). + // + // Then, it is possible to rewrite the above fragment as follows: + // + // %ofm <- Instr(...) + // + continue; + } + + if (ofm->bag()->reads()->size() > 0) + { + // Let us consider the following code: + // + // Bag: + // %bag_0 = Bag(...) + // %bag_1 = Bag(...) + // %bag_2 = Bag(...) + // + // Object: + // %obj_0 = FeatureObject(bag: %bag_0) + // %obj_1 = FeatureObject(bag: %bag_1) + // + // Instr: + // copy an object from %obj_0 into %obj_1 + // shuffle values from %bag_1 into %bag_2 + // eval Conv2D with %obj_1 + // + // Identical Object Reduction (IOR) tries to eliminate the first copy via + // substitution (substitute all the occurrence of %obj_1 as use with %obj_0). + // + // Here is the code transformed by IOR: + // + // Bag: + // %bag_0 = Bag(...) + // %bag_1 = Bag(...) + // %bag_2 = Bag(...) + // + // Object: + // %obj_0 = FeatureObject(bag: %bag_0) + // %obj_1 = FeatureObject(bag: %bag_1) + // + // Instr: + // shuffle values from %bag_1 into %bag_2 + // eval Conv2D with %obj_0 + // + // Note that there is no updater of %bag_1 after IOR, and thus the behavior + // of the first shuffle instruction has changed. + // + // This examples shows that it is impossible to simply substitute %obj_1 + // with %obj_0 in the presence of readers over its backing bag. + continue; + } + + subst(copy->into(), copy->from()); + + copy->detach(); + detached.insert(copy); + } + + for (auto copy : detached) + { + m->entity()->instr()->destroy(copy); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h new file mode 100644 index 000000000..b5bb25d7c --- /dev/null +++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__ +#define __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Reduce identically copied objects as its original object + * + * >>> BEFORE <<< + * %bag_0 = Bag(size: N) + * %bag_1 = Bag(size: N) + * + * %obj_0 = Feature(layout: BHWC) at %bag_0 + * %obj_1 = Feature(layout: BHWC) at %bag_1 + * + * copy(from: %obj_0, into: %obj_1) + * ... + * Use(%obj_0) + * Use(%obj_1) + * ... + * + * >>> AFTER <<< + * %bag_0 = Bag(size: N) + * %bag_1 = Bag(size: N) + * + * %obj_0 = Feature(layout: BHWC) at %bag_0 + * %obj_1 = Feature(layout: BHWC) at %bag_1 + * + * copy(from: %obj_0, into: %obj_1) + * ... + * Use(%obj_0) + * Use(%obj_0) <- %obj_1 is replaced + * ... + */ +void reduce_identical_object(enco::Code *code); + +struct IdenticalObjectReductionPass final : public Pass +{ + PASS_CTOR(IdenticalObjectReductionPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { reduce_identical_object(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__ diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp new file mode 100644 index 000000000..772bea08e --- /dev/null +++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IdenticalObjectReduction.h" + +#include + +TEST(IdenticalObjectReductionTest, case_000) +{ + auto m = coco::Module::create(); + + // Create a "free" Eval instruction + m->entity()->instr()->create(); + + enco::Code code{m.get(), nullptr}; + + // NOTE This code SHOULD NOT crash + enco::reduce_identical_object(&code); +} diff --git a/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp b/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp new file mode 100644 index 000000000..b36620f61 --- /dev/null +++ b/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IndirectCopyElimination.h" + +#include + +namespace +{ + +coco::Copy *as_copy(coco::Instr *ins) { return ins ? ins->asCopy() : nullptr; } + +/** + * @brief Return a set of copy instructions that are accessible from top-level module + */ +std::set linked_copy_instrs(coco::Module *m) +{ + std::set res; + + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + auto ins = m->entity()->instr()->at(n); + assert(ins != nullptr); + + if (ins->parent() && ins->parent()->parent()) + { + if (auto copy = ins->asCopy()) + { + res.insert(copy); + } + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void eliminate_indirect_copy(enco::Code *code) +{ + auto m = code->module(); + + for (auto child : linked_copy_instrs(m)) + { + auto from = child->from(); + assert(from != nullptr); + + // Find the irreducible origin + while (true) + { + if (auto producer = coco::producer(from)) + { + if (auto parent = as_copy(producer->loc())) + { + assert(parent->from() != nullptr); + from = parent->from(); + continue; + } + } + + break; + } + + child->from(from); + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/IndirectCopyElimination.h b/compiler/enco/core/src/Transforms/IndirectCopyElimination.h new file mode 100644 index 000000000..acfdf569b --- /dev/null +++ b/compiler/enco/core/src/Transforms/IndirectCopyElimination.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__ +#define __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Convert all the indirect copies as a direct copy + * + * >>> BEFORE <<< + * %obj_0 = ... + * %obj_1 = ... + * %obj_2 = ... + * + * copy(from: %obj_0, into: %obj_1) + * copy(from: %obj_1, into: %obj_2) + * + * >>> AFTER <<< + * %obj_0 = ... + * %obj_1 = ... + * %obj_2 = ... + * + * copy(from: %obj_0, into: %obj_1) + * copy(from: %obj_0, into: %obj_2) + * + */ +void eliminate_indirect_copy(enco::Code *code); + +struct IndirectCopyEliminationPass final : public enco::Pass +{ + PASS_CTOR(IndirectCopyEliminationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { eliminate_indirect_copy(code(sess)); } +}; + +} // namespace enco + +#endif // __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__ diff --git a/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp b/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp new file mode 100644 index 000000000..7bf1c4926 --- /dev/null +++ b/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IntrinsicSelection.h" + +#include "coex/IR.h" + +namespace +{ + +/** + * @brief Return a backend-speicific coco (extend) instruction + * + * @note rewrite(ins) returns nullptr if selection fails + */ +coco::Instr *rewrite(coco::Instr *curr) +{ + auto m = curr->module(); + assert(m != nullptr); + + if (auto eval = coco::safe_cast(curr)) + { + if (auto concat_f = eval->op()->asConcatF()) + { + auto fst_load = concat_f->left()->asLoad(); + auto snd_load = concat_f->right()->asLoad(); + + if (fst_load && snd_load && (concat_f->axis() == coco::ConcatF::Axis::Depth)) + { + // Here is the pattern of interest + // + // %ofm = eval(ConcatF(Depth, Load(%left), Load(%right))) + // + auto fst_feature = fst_load->object()->asFeature(); + auto snd_feature = snd_load->object()->asFeature(); + assert((fst_feature != nullptr) && (snd_feature != nullptr)); + + auto out_feature = eval->out()->asFeature(); + assert(out_feature != nullptr); + + eval->out(nullptr); + + auto depth_concat = m->entity()->instr()->create(); + + depth_concat->out(out_feature); + depth_concat->fst(fst_feature); + depth_concat->snd(snd_feature); + + return depth_concat; + } + + return nullptr; + } + } + + return nullptr; +} + +} // namespace + +namespace enco +{ + +void select_intrinsic(enco::Code *code) +{ + auto m = code->module(); + + for (auto blk = m->block()->head(); blk; blk = blk->next()) + { + auto ins = blk->instr()->head(); + + while (ins) + { + if (auto rewritten_ins = rewrite(ins)) + { + rewritten_ins->insertBefore(ins); + ins->detach(); + + ins = rewritten_ins; + } + + ins = ins->next(); + } + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/IntrinsicSelection.h b/compiler/enco/core/src/Transforms/IntrinsicSelection.h new file mode 100644 index 000000000..67d38eaeb --- /dev/null +++ b/compiler/enco/core/src/Transforms/IntrinsicSelection.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __INTRINSIC_SELECTION_H__ +#define __INTRINSIC_SELECTION_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Select Intricsic (API) to be used + * + * This pass is analogue of "Instruction Selection" pass. This "Intrisic Selection" pass + * will replace a general coco IR instruction into a backend-specific coco (extended) IR + * instruction. + */ +void select_intrinsic(enco::Code *); + +struct IntrinsicSelectionPass final : public Pass +{ + PASS_CTOR(IntrinsicSelectionPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { select_intrinsic(code(sess)); } +}; + +} // namespace enco + +#endif // __INTRINSIC_SELECTION_H__ diff --git a/compiler/enco/core/src/Transforms/Optimizations.cpp b/compiler/enco/core/src/Transforms/Optimizations.cpp new file mode 100644 index 000000000..7f0974dd0 --- /dev/null +++ b/compiler/enco/core/src/Transforms/Optimizations.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Optimizations.h" +#include "CodeIndex.h" + +#include + +namespace enco +{ + +void generate_bypass_shuffle(enco::Code *code) +{ + auto m = code->module(); + + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + // NOTE The current implementation assumes that all the updates occurs before the first read + // TODO Remove this assumption + for (auto u : coco::updaters(bag)) + { + if ((u->loc() == nullptr) || (u->loc()->asShuffle() == nullptr)) + { + // Skip if updater is not a Shuffle instruction + continue; + } + + for (auto r : coco::readers(bag)) + { + if ((r->loc() == nullptr) || (r->loc()->asShuffle() == nullptr)) + { + // Skip if reader is not a Shuffle instruction + continue; + } + + auto shuffle_1 = u->loc()->asShuffle(); + auto shuffle_2 = r->loc()->asShuffle(); + + // Construct a shuffle instruction + auto shuffle_3 = m->entity()->instr()->create(); + + shuffle_3->from(shuffle_1->from()); + shuffle_3->into(shuffle_2->into()); + + // Attempt to construct a valid bypass shuffle instruction + bool valid = true; + + for (const auto &C : shuffle_2->range()) + { + auto B = shuffle_2->at(C); + + if (!shuffle_1->defined(B)) + { + valid = false; + break; + } + + auto A = shuffle_1->at(B); + + shuffle_3->insert(A, C); + } + + if (valid) + { + // Insert shuffle_3 before shuffle_2 if shuffle_3 is a valid bypass of shuffle_2 + shuffle_3->insertBefore(shuffle_2); + + // NOTE shuffle_2 SHOULD BE detached and destroyed after shuffle_3 is inserted + shuffle_2->detach(); + m->entity()->instr()->destroy(shuffle_2); + } + else + { + // Destroy shuffle_3 (bypass shuffle) if it is invalid + m->entity()->instr()->destroy(shuffle_3); + } + } + } + } +} + +} // namespace enco + +// +// Hoist Object +// +namespace +{ + +bool hoistable(const coco::Shuffle *shuffle) +{ + auto range = shuffle->range(); + + if (range.size() != shuffle->into()->size()) + { + return false; + } + + for (const auto &dst : range) + { + if (shuffle->at(dst).value() != dst.value()) + { + return false; + } + } + + return true; +} + +bool complete(const coco::Shuffle *s) { return s->range().size() == s->into()->size(); } + +bool compatible(const coco::Shuffle *s1, const coco::Shuffle *s2) +{ + if (s1->from() != s2->from()) + { + return false; + } + + if (s1->into()->size() != s2->into()->size()) + { + return false; + } + + auto range_1 = s1->range(); + auto range_2 = s2->range(); + + if (range_1.size() != range_2.size()) + { + return false; + } + + bool res = true; + + for (const auto &dst : range_2) + { + if (!s1->defined(dst)) + { + res = false; + break; + } + + auto src_1 = s1->at(dst); + auto src_2 = s2->at(dst); + + if (src_1.value() != src_2.value()) + { + res = false; + break; + } + } + + return res; +} + +} // namespace + +namespace enco +{ + +void hoist_object(enco::Code *code) +{ + auto m = code->module(); + + // + // Case 1 + // + for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n) + { + if (auto shuffle = m->entity()->instr()->at(n)->asShuffle()) + { + if (shuffle->parent() == nullptr) + { + continue; + } + + if (hoistable(shuffle)) + { + auto from = shuffle->from(); + auto into = shuffle->into(); + + into->replaceAllDepsWith(from); + } + } + } + + // + // Case 2 + // + for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n) + { + auto bag = m->entity()->bag()->at(n); + + std::map collected; + + for (auto reader : coco::readers(bag)) + { + if (auto ins = reader->loc()) + { + if (auto shuffle = ins->asShuffle()) + { + collected[code_index(shuffle)] = shuffle; + } + } + } + + std::vector sorted; + + for (auto it = collected.begin(); it != collected.end(); ++it) + { + sorted.emplace_back(it->second); + } + + for (uint32_t curr = 0; curr < sorted.size(); ++curr) + { + auto const curr_ins = sorted.at(curr); + auto const curr_bag = curr_ins->into(); + + if (!complete(curr_ins)) + { + continue; + } + + for (uint32_t next = curr + 1; next < sorted.size(); ++next) + { + auto const next_ins = sorted.at(next); + auto const next_bag = next_ins->into(); + + if (!complete(next_ins)) + { + continue; + } + + if (compatible(curr_ins, next_ins)) + { + next_bag->replaceAllDepsWith(curr_bag); + } + } + } + } +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/Optimizations.h b/compiler/enco/core/src/Transforms/Optimizations.h new file mode 100644 index 000000000..7cfc2305c --- /dev/null +++ b/compiler/enco/core/src/Transforms/Optimizations.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_OPTIMIZATIONS_H__ +#define __ENCO_OPTIMIZATIONS_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +/** + * @brief Add a bypass Shuffle if two continued Shuffles map same from-into + * + * %bag_1 = Bag(size: N) + * %bag_2 = Bag(size: N) + * %bag_3 = Bag(size: N) + * + * >>> BEFORE <<< + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) + * Shuffle(from: %bag_2, into: %bag_3, [0 -> 0]) + * + * Let's refer to the former shuffle as Shuffle 1 and the latter one as Shuffle 2. + * We can replace Shuffle 2 with new Shuffle 3 as follows when Shuffle 1 and + * Shuffle 2 map to the same position. + * + * >>> AFTER <<< + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- Shuffle 1 + * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- Shuffle 3 + * + * Note that Shuffle 1 can be eliminated when %bag_2 is not used + */ +void generate_bypass_shuffle(enco::Code *code); + +struct BypassGenerationPass final : public Pass +{ + PASS_CTOR(BypassGenerationPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { generate_bypass_shuffle(code(sess)); } +}; + +/** + * @brief Update the base bag of each object if possible + * + * --- Case 1 --- + * Let us consider the following code: + * + * %bag_1 = Bag(size: 4) + * %bag_2 = Bag(size: 1) + * + * %obj_1 = ... at %bag_1 + * %obj_2 = ... at %bag_2 + * + * ... + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle + * ... + * + * Note that the content of %bag_2 after shuffle is identical to a part of %bag_1, so + * the following code is identical to the above code + * + * %bag_1 = Bag(size: 4) + * %bag_2 = Bag(size: 1) + * + * %obj_1 = ... at %bag_1 + * %obj_2 = ... at %bag_1 + * + * ... + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) + * ... + * + * --- Case 2 --- + * Let us consider the following code: + * + * %bag_1 = Bag(size: 4) + * %bag_2 = Bag(size: 1) + * %bag_3 = Bag(size: 1) + * + * %obj_1 = ... at %bag_2 + * %obj_2 = ... at %bag_3 + * + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle_1 + * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- shuffle_2 + * + * Note that the content of %bag_3 after shuffle_2 is identical to that of %bag_2 after shuffle_1, + * so the following code is identical to the above one: + * + * %bag_1 = Bag(size: 4) + * %bag_2 = Bag(size: 1) + * %bag_3 = Bag(size: 1) + * + * %obj_1 = ... at %bag_2 + * %obj_2 = ... at %bag_2 <- HERE + * + * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle_1 + * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- shuffle_2 + * + * "hoist_object" optimization rewrites the former code as the latter one. + * + * NOTE "hoist_object" DOES NOT change any instruction. It just updates the base bag of objects of + * interest. + */ +void hoist_object(enco::Code *code); + +} // namespace enco + +#endif // __ENCO_OPTIMIZATIONS_H__ diff --git a/compiler/enco/core/src/Transforms/Split.cpp b/compiler/enco/core/src/Transforms/Split.cpp new file mode 100644 index 000000000..b57b8f882 --- /dev/null +++ b/compiler/enco/core/src/Transforms/Split.cpp @@ -0,0 +1,1233 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Split.h" +#include "Usage.h" +#include "Session.h" +#include "coex/IR.h" + +#include + +#include +#include + +#include +#include +#include + +using stdex::make_unique; + +namespace +{ + +std::map> _subnet_contexts; + +} // namespace + +namespace enco +{ + +const ANNContext *SubnetManager::context(const coco::Module *m) +{ + return _subnet_contexts.at(m).get(); +} + +} // namespace enco + +namespace +{ + +using Appender = std::function; + +struct ANNOpAppender +{ + virtual ~ANNOpAppender() = default; + + virtual void append(ANNBinder *binder) const = 0; +}; + +class ANNAddAppender final : public ANNOpAppender +{ +public: + void left(coco::FeatureObject *o) { _left = o; } + void right(coco::FeatureObject *o) { _right = o; } + void out(coco::FeatureObject *o) { _out = o; } + +public: + void append(ANNBinder *binder) const override + { + auto left = binder->addOperand(_left); + auto right = binder->addOperand(_right); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto out = binder->addOperand(_out); + + binder->addOperation(ann::Operation::Code::ADD, {left, right, fuse}, {out}); + } + +private: + coco::FeatureObject *_left = nullptr; + coco::FeatureObject *_right = nullptr; + coco::FeatureObject *_out = nullptr; +}; + +class ANNMulAppender final : public ANNOpAppender +{ +public: + void left(coco::FeatureObject *o) { _left = o; } + void right(coco::FeatureObject *o) { _right = o; } + void out(coco::FeatureObject *o) { _out = o; } + +public: + void append(ANNBinder *binder) const override + { + auto left = binder->addOperand(_left); + auto right = binder->addOperand(_right); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto out = binder->addOperand(_out); + + binder->addOperation(ann::Operation::Code::MUL, {left, right, fuse}, {out}); + } + +private: + coco::FeatureObject *_left = nullptr; + coco::FeatureObject *_right = nullptr; + coco::FeatureObject *_out = nullptr; +}; + +/** + * WARN The current implementation supports concatenation along depth only + */ +class ANNConcatAppender final : public ANNOpAppender +{ +public: + void left(coco::FeatureObject *o) { _left = o; } + void right(coco::FeatureObject *o) { _right = o; } + void out(coco::FeatureObject *o) { _out = o; } + +public: + void append(ANNBinder *binder) const override + { + auto left = binder->addOperand(_left); + auto right = binder->addOperand(_right); + auto axis = binder->addOperand(); + binder->setOperand(axis, 3 /* DEPTH */); + + auto out = binder->addOperand(_out); + + binder->addOperation(ann::Operation::Code::CONCAT, {left, right, axis}, {out}); + } + +private: + coco::FeatureObject *_left = nullptr; + coco::FeatureObject *_right = nullptr; + coco::FeatureObject *_out = nullptr; +}; + +class ANNConv2DAppender final : public ANNOpAppender +{ +public: + void session(const enco::SessionID &sess) { _sess = sess; } + + void pad(const coco::Padding2D *pad) { _pad = *pad; } + void stride(const coco::Stride2D *stride) { _stride = *stride; } + + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ker(coco::KernelObject *ker) { _ker = ker; } + // Q: Should we take a bias as a feature object? + // NOTE This interface is subject to change + void bias(coco::FeatureObject *bias) { _bias = bias; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + auto data = enco::data(_sess); + + auto ifm = binder->addOperand(_ifm); + auto ker = binder->addOperand(_ker); + + // Fill kernel data + { + auto ker_bag = _ker->bag(); + auto ker_weight = data->f32()->weight(ker_bag); + + assert(ker_weight.data() != nullptr); + + binder->setOperand(ker, ker_weight.data(), ker_weight.data() + ker_weight.size()); + } + + // Conv2D in coco IR has no bias, but bias is mandatory in Android NN API + auto bias = binder->addOperand(nncc::core::ADT::tensor::Shape{_ker->shape().count()}); + + // Fill bias data + if (_bias == nullptr) + { + // Use a fresh empty bias if "bias" is not specified + auto length = _ker->shape().count(); + + std::vector values; + values.resize(length, 0.0f); + + binder->setOperand(bias, values.begin(), values.end()); + } + else + { + // Use specified "bias" + auto bias_bag = _bias->bag(); + auto bias_weight = data->f32()->weight(bias_bag); + + assert(bias_weight.data() != nullptr); + assert(bias_weight.size() == _ker->shape().count()); + + binder->setOperand(bias, bias_weight.data(), bias_weight.data() + bias_weight.size()); + } + + auto left = binder->addOperand(); + binder->setOperand(left, _pad.left()); + auto right = binder->addOperand(); + binder->setOperand(right, _pad.right()); + auto top = binder->addOperand(); + binder->setOperand(top, _pad.top()); + auto bottom = binder->addOperand(); + binder->setOperand(bottom, _pad.bottom()); + auto hstride = binder->addOperand(); + binder->setOperand(hstride, _stride.horizontal()); + auto vstride = binder->addOperand(); + binder->setOperand(vstride, _stride.vertical()); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::CONV_2D, + {ifm, ker, bias, left, right, top, bottom, hstride, vstride, fuse}, {ofm}); + } + +private: + enco::SessionID _sess; + +private: + coco::Padding2D _pad; + coco::Stride2D _stride; + +private: + coco::FeatureObject *_ifm = nullptr; + coco::KernelObject *_ker = nullptr; + coco::FeatureObject *_bias = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNDepthwiseConv2DAppender final : public ANNOpAppender +{ +public: + void session(const enco::SessionID &sess) { _sess = sess; } + + void multiplier(const uint32_t &multiplier) { _multiplier = multiplier; } + void pad(const coco::Padding2D *pad) { _pad = *pad; } + void stride(const coco::Stride2D *stride) { _stride = *stride; } + + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ker(coco::KernelObject *ker) { _ker = ker; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + using namespace nncc::core::ADT; + + auto data = enco::data(_sess); + + const uint32_t ker_N = _ker->shape().count(); + const uint32_t ker_H = _ker->shape().height(); + const uint32_t ker_W = _ker->shape().width(); + + assert(ker_N % _multiplier == 0); + const uint32_t group = ker_N / _multiplier; + + auto ifm = binder->addOperand(_ifm); + auto ker = binder->addOperand(tensor::Shape{1, ker_H, ker_W, ker_N}); + + // Fill kernel data + { + auto obj = _ker; + auto shape = obj->shape(); + + auto ovl = data->f32()->read(obj); + assert(ovl != nullptr); + + // Flatten? + std::vector values; + + /** + * Android NN computes DEPTHWISE_CONV_2D as follows: + * + * output[b, i, j, k * channel_multiplier + q] = + * sum_{di, dj} ( + * input[b, strides[1] * i + di, strides[2] * j + dj, k] * + * filter[1, di, dj, k * channel_multiplier + q] + * ) + bias[k * channel_multiplier + q] + * + */ + for (uint32_t row = 0; row < shape.height(); ++row) + { + for (uint32_t col = 0; col < shape.width(); ++col) + { + for (uint32_t g = 0; g < group; ++g) + { + for (uint32_t m = 0; m < _multiplier; ++m) + { + const auto value = ovl->at(g * _multiplier + m, 0, row, col); + values.emplace_back(value); + } + } + } + } + + assert(values.size() == nncc::core::ADT::kernel::num_elements(shape)); + binder->setOperand(ker, values.begin(), values.end()); + } + + // Conv2D in coco IR has no bias, but bias is mandatory in Android NN API + auto bias = binder->addOperand(nncc::core::ADT::tensor::Shape{_ker->shape().count()}); + + // Fill bias data + { + auto length = _ker->shape().count(); + + std::vector values; + values.resize(length, 0.0f); + + binder->setOperand(bias, values.begin(), values.end()); + } + + auto left = binder->addOperand(); + binder->setOperand(left, _pad.left()); + auto right = binder->addOperand(); + binder->setOperand(right, _pad.right()); + auto top = binder->addOperand(); + binder->setOperand(top, _pad.top()); + auto bottom = binder->addOperand(); + binder->setOperand(bottom, _pad.bottom()); + auto hstride = binder->addOperand(); + binder->setOperand(hstride, _stride.horizontal()); + auto vstride = binder->addOperand(); + binder->setOperand(vstride, _stride.vertical()); + auto multiplier = binder->addOperand(); + binder->setOperand(multiplier, _multiplier); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto ofm = binder->addOperand(_ofm); + + binder->addOperation( + ann::Operation::Code::DEPTHWISE_CONV_2D, + {ifm, ker, bias, left, right, top, bottom, hstride, vstride, multiplier, fuse}, {ofm}); + } + +private: + enco::SessionID _sess; + +private: + uint32_t _multiplier; + coco::Padding2D _pad; + coco::Stride2D _stride; + +private: + coco::FeatureObject *_ifm = nullptr; + coco::KernelObject *_ker = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNReLUAppender final : public ANNOpAppender +{ +public: + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + auto ifm = binder->addOperand(_ifm); + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::RELU, {ifm}, {ofm}); + } + +private: + coco::FeatureObject *_ifm = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNReLU6Appender final : public ANNOpAppender +{ +public: + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + auto ifm = binder->addOperand(_ifm); + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::RELU6, {ifm}, {ofm}); + } + +private: + coco::FeatureObject *_ifm = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNMaxPool2DAppender final : public ANNOpAppender +{ +public: + void pad(const coco::Padding2D *pad) { _pad = *pad; } + void stride(const coco::Stride2D *stride) { _stride = *stride; } + void window(const coco::Window2D *window) { _window = *window; } + + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + auto ifm = binder->addOperand(_ifm); + + // Set padding + auto left = binder->addOperand(); + binder->setOperand(left, _pad.left()); + auto right = binder->addOperand(); + binder->setOperand(right, _pad.right()); + auto top = binder->addOperand(); + binder->setOperand(top, _pad.top()); + auto bottom = binder->addOperand(); + binder->setOperand(bottom, _pad.bottom()); + + // Set horizontal/vertical stride + auto hstride = binder->addOperand(); + binder->setOperand(hstride, _stride.horizontal()); + auto vstride = binder->addOperand(); + binder->setOperand(vstride, _stride.vertical()); + + // Set receptive field size + auto width = binder->addOperand(); + binder->setOperand(width, _window.width()); + auto height = binder->addOperand(); + binder->setOperand(height, _window.height()); + + // Set fuse code + // TODO Suport operation fusion + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::MAX_POOL_2D, + {ifm, left, right, top, bottom, hstride, vstride, width, height, fuse}, + {ofm}); + } + +private: + coco::Padding2D _pad; + coco::Stride2D _stride; + coco::Window2D _window; + +private: + coco::FeatureObject *_ifm = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNAvgPool2DAppender final : public ANNOpAppender +{ +public: + void pad(const coco::Padding2D *pad) { _pad = *pad; } + void stride(const coco::Stride2D *stride) { _stride = *stride; } + void window(const coco::Window2D *window) { _window = *window; } + + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + auto ifm = binder->addOperand(_ifm); + + // Set padding + auto left = binder->addOperand(); + binder->setOperand(left, _pad.left()); + auto right = binder->addOperand(); + binder->setOperand(right, _pad.right()); + auto top = binder->addOperand(); + binder->setOperand(top, _pad.top()); + auto bottom = binder->addOperand(); + binder->setOperand(bottom, _pad.bottom()); + + // Set horizontal/vertical stride + auto hstride = binder->addOperand(); + binder->setOperand(hstride, _stride.horizontal()); + auto vstride = binder->addOperand(); + binder->setOperand(vstride, _stride.vertical()); + + // Set receptive field size + auto width = binder->addOperand(); + binder->setOperand(width, _window.width()); + auto height = binder->addOperand(); + binder->setOperand(height, _window.height()); + + // Set fuse code + // TODO Suport operation fusion + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::AVG_POOL_2D, + {ifm, left, right, top, bottom, hstride, vstride, width, height, fuse}, + {ofm}); + } + +private: + coco::Padding2D _pad; + coco::Stride2D _stride; + coco::Window2D _window; + +private: + coco::FeatureObject *_ifm = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNPadFAppender final : public ANNOpAppender +{ +public: + void pad(const coco::Padding2D *pad) { _pad = *pad; } + +public: + void ifm(coco::FeatureObject *ifm) { _ifm = ifm; } + void ofm(coco::FeatureObject *ofm) { _ofm = ofm; } + +public: + void append(ANNBinder *binder) const override + { + using nncc::core::ADT::tensor::Shape; + + auto ifm = binder->addOperand(_ifm); + auto pad = binder->addOperand(Shape{4, 2}); + { + std::vector values; + values.resize(8); + // For 'N' + values.at(0) = values.at(1) = 0; + // For 'H' + values.at(2) = _pad.top(); + values.at(3) = _pad.bottom(); + // For 'W' + values.at(4) = _pad.left(); + values.at(5) = _pad.right(); + // For 'C' + values.at(6) = values.at(7) = 0; + + binder->setOperand(pad, values.begin(), values.end()); + } + + auto ofm = binder->addOperand(_ofm); + + binder->addOperation(ann::Operation::Code::PAD, {ifm, pad}, {ofm}); + } + +private: + coco::Padding2D _pad; + +private: + coco::FeatureObject *_ifm = nullptr; + coco::FeatureObject *_ofm = nullptr; +}; + +class ANNOpFunctionalAppender final : public ANNOpAppender +{ +public: + ANNOpFunctionalAppender(const Appender &fun) : _fun{fun} + { + // DO NOTHING + } + +public: + void append(ANNBinder *binder) const { _fun(binder); } + +private: + Appender _fun; +}; + +class ANNSubAppender final : public ANNOpAppender +{ +public: + void left(coco::FeatureObject *o) { _left = o; } + void right(coco::FeatureObject *o) { _right = o; } + void out(coco::FeatureObject *o) { _out = o; } + +public: + void append(ANNBinder *binder) const override + { + auto left = binder->addOperand(_left); + auto right = binder->addOperand(_right); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto out = binder->addOperand(_out); + + binder->addOperation(ann::Operation::Code::SUB, {left, right, fuse}, {out}); + } + +private: + coco::FeatureObject *_left = nullptr; + coco::FeatureObject *_right = nullptr; + coco::FeatureObject *_out = nullptr; +}; + +class ANNDivAppender final : public ANNOpAppender +{ +public: + void left(coco::FeatureObject *o) { _left = o; } + void right(coco::FeatureObject *o) { _right = o; } + void out(coco::FeatureObject *o) { _out = o; } + +public: + void append(ANNBinder *binder) const override + { + auto left = binder->addOperand(_left); + auto right = binder->addOperand(_right); + auto fuse = binder->addOperand(); + binder->setOperand(fuse, 0); + + auto out = binder->addOperand(_out); + + binder->addOperation(ann::Operation::Code::DIV, {left, right, fuse}, {out}); + } + +private: + coco::FeatureObject *_left = nullptr; + coco::FeatureObject *_right = nullptr; + coco::FeatureObject *_out = nullptr; +}; + +class ANNOpBuilder : public coco::Instr::Visitor> +{ +public: + std::unique_ptr visit(const coco::Eval *eval) + { + if (auto conv = eval->op()->asConv2D()) + { + if (auto load = conv->arg()->asLoad()) + { + auto sess = enco::session(eval->module()); + + auto ifm = load->object()->asFeature(); + auto ker = conv->ker(); + auto ofm = eval->out()->asFeature(); + + const auto group = conv->group(); + + if (group == 1) + { + auto app = make_unique(); + + app->session(sess); + + app->pad(conv->pad()); + app->stride(conv->stride()); + + app->ifm(ifm); + app->ofm(ofm); + app->ker(ker); + + return std::move(app); + } + else + { + assert(ifm->shape().depth() == group); + assert(ker->shape().count() % group == 0); + assert(ker->shape().depth() == 1); + + auto app = make_unique(); + + app->session(sess); + + app->multiplier(ker->shape().count() / group); + app->pad(conv->pad()); + app->stride(conv->stride()); + + app->ifm(ifm); + app->ofm(ofm); + app->ker(ker); + + return std::move(app); + } + } + } + else if (auto op = eval->op()->asAdd()) + { + auto left_load = op->left()->asLoad(); + auto right_load = op->right()->asLoad(); + + if (left_load && right_load) + { + // Let's compile the following code fragment: + // + // %ofm = eval(Add(Load(%left), Load(%right))) + // + auto left = left_load->object()->asFeature(); + auto right = right_load->object()->asFeature(); + assert(left != nullptr && right != nullptr); + + auto out = eval->out()->asFeature(); + assert(out != nullptr); + + auto app = make_unique(); + + app->left(left); + app->right(right); + app->out(out); + + return std::move(app); + } + } + else if (auto op = eval->op()->asMul()) + { + auto left_load = op->left()->asLoad(); + auto right_load = op->right()->asLoad(); + + if (left_load && right_load) + { + // Let's compile the following code fragment: + // + // %ofm = eval(Mul(Load(%left), Load(%right))) + // + auto left = left_load->object()->asFeature(); + auto right = right_load->object()->asFeature(); + assert(left != nullptr && right != nullptr); + + auto out = eval->out()->asFeature(); + assert(out != nullptr); + + auto app = make_unique(); + + app->left(left); + app->right(right); + app->out(out); + + return std::move(app); + } + } + else if (auto op = eval->op()->asPadF()) + { + if (auto load = op->arg()->asLoad()) + { + // Let's compile the following code fragment: + // + // %ofm = eval(PadF(Load(%ifm)) + // + auto ifm = load->object()->asFeature(); + auto ofm = eval->out()->asFeature(); + + assert(ifm != nullptr && ofm != nullptr); + + auto app = make_unique(); + + app->pad(op->pad()); + + app->ifm(ifm); + app->ofm(ofm); + + return std::move(app); + } + } + else if (auto maxpool = eval->op()->asMaxPool2D()) + { + if (auto load = maxpool->arg()->asLoad()) + { + // Let's compile the following code fragment: + // + // %ofm = eval(MaxPool2D(Load(%ifm)) + // + auto ifm = load->object()->asFeature(); + auto ofm = eval->out()->asFeature(); + + assert(ifm != nullptr && ofm != nullptr); + + auto app = make_unique(); + + app->pad(maxpool->pad()); + app->stride(maxpool->stride()); + app->window(maxpool->window()); + + app->ifm(ifm); + app->ofm(ofm); + + return std::move(app); + } + } + else if (auto avgpool = eval->op()->asAvgPool2D()) + { + if (auto load = avgpool->arg()->asLoad()) + { + // Let's compile the following code fragment: + // + // %ofm = eval(AvgPool2D(Load(%ifm)) + // + if (avgpool->divisor() == coco::AvgPool2D::Divisor::PaddingExcluded) + { + // When ANN runtime computes the average of each receptive field, + // it uses the number of valid(=non-padding) elements as a divisor. + auto ifm = load->object()->asFeature(); + auto ofm = eval->out()->asFeature(); + + assert(ifm != nullptr && ofm != nullptr); + + auto app = make_unique(); + + app->pad(avgpool->pad()); + app->stride(avgpool->stride()); + app->window(avgpool->window()); + + app->ifm(ifm); + app->ofm(ofm); + + return std::move(app); + } + } + } + else if (auto relu = eval->op()->asReLU()) + { + if (auto load = relu->arg()->asLoad()) + { + // Let's compile the following code fragment: + // + // %ofm = eval(ReLU(Load(%ifm)) + // + // TODO Support objects of other kinds, such as Tensor + auto ifm = load->object()->asFeature(); + auto ofm = eval->out()->asFeature(); + + assert(ifm != nullptr && ofm != nullptr); + + auto app = make_unique(); + + app->ifm(ifm); + app->ofm(ofm); + + return std::move(app); + } + } + else if (auto relu6 = eval->op()->asReLU6()) + { + if (auto load = relu6->arg()->asLoad()) + { + // Let's compile the following code fragment: + // + // %ofm = eval(ReLU6(Load(%ifm)) + // + // TODO Support objects of other kinds, such as Tensor + auto ifm = load->object()->asFeature(); + auto ofm = eval->out()->asFeature(); + + assert(ifm != nullptr && ofm != nullptr); + + auto app = make_unique(); + + app->ifm(ifm); + app->ofm(ofm); + + return std::move(app); + } + } + else if (auto op = eval->op()->asConcatF()) + { + auto left_load = op->left()->asLoad(); + auto right_load = op->right()->asLoad(); + + if (left_load && right_load && (op->axis() == coco::ConcatF::Axis::Depth)) + { + // Let's compile the following code fragment: + // + // %ofm = eval(ConcatF(Depth, Load(%left), Load(%right))) + // + auto left = left_load->object()->asFeature(); + auto right = right_load->object()->asFeature(); + assert(left != nullptr && right != nullptr); + + auto out = eval->out()->asFeature(); + assert(out != nullptr); + + auto app = make_unique(); + + app->left(left); + app->right(right); + app->out(out); + + return std::move(app); + } + } + else if (auto op = eval->op()->asSub()) + { + auto left_load = op->left()->asLoad(); + auto right_load = op->right()->asLoad(); + + if (left_load && right_load) + { + // Let's compile the following code fragment: + // + // %out = eval(Sub(Load(%left), Load(%right))) + // + auto left = left_load->object()->asFeature(); + auto right = right_load->object()->asFeature(); + assert(left != nullptr && right != nullptr); + + auto out = eval->out()->asFeature(); + assert(out != nullptr); + + auto app = make_unique(); + + app->left(left); + app->right(right); + app->out(out); + + return std::move(app); + } + } + else if (auto op = eval->op()->asDiv()) + { + auto left_load = op->left()->asLoad(); + auto right_load = op->right()->asLoad(); + + if (left_load && right_load) + { + // Let's compile the following code fragment: + // + // %out = eval(Div(Load(%left), Load(%right))) + // + auto left = left_load->object()->asFeature(); + auto right = right_load->object()->asFeature(); + assert(left != nullptr && right != nullptr); + + auto out = eval->out()->asFeature(); + assert(out != nullptr); + + auto app = make_unique(); + + app->left(left); + app->right(right); + app->out(out); + + return std::move(app); + } + } + + // Return nullptr if a given Eval instruction is incompatible + return nullptr; + } + +public: + std::unique_ptr visit(const coco::Shuffle *) { return nullptr; } +}; + +namespace +{ + +std::unique_ptr make_appender(coco::Instr *ins) +{ + ANNOpBuilder op_builder; + + if (auto eval = coco::safe_cast(ins)) + { + return eval->accept(op_builder); + } + + if (auto depth_concat = coco::safe_cast(ins)) + { + auto app = make_unique(); + + app->out(depth_concat->out()->asFeature()); + + app->left(depth_concat->fst()->asFeature()); + app->right(depth_concat->snd()->asFeature()); + + return std::move(app); + } + + // Build ANN IR from ANNConv2D instruction + if (auto conv2d = coco::safe_cast(ins)) + { + auto sess = enco::session(conv2d->module()); + auto app = make_unique(); + + app->session(sess); + + app->pad(conv2d->pad()); + app->stride(conv2d->stride()); + + app->ofm(conv2d->ofm()->asFeature()); + app->ifm(conv2d->ifm()->asFeature()); + app->ker(conv2d->ker()->asKernel()); + app->bias(coco::safe_cast(conv2d->bias())); + + return std::move(app); + } + + return nullptr; +} + +enum Compatibility +{ + COMPATIBLE, + INCOMPATIBLE +}; + +class ANNGroupBuilder +{ +public: + ANNGroupBuilder(ANNContext *ctx) : _ctx{ctx} + { + // DO NOTHING + } + +public: + Compatibility kind(const coco::Block *blk) const; + Compatibility kind(const std::unique_ptr &appender) const; + +public: + void build(enco::Code *code) const; + +private: + ANNContext *_ctx; +}; + +Compatibility ANNGroupBuilder::kind(const std::unique_ptr &app) const +{ + return app ? COMPATIBLE : INCOMPATIBLE; +} + +Compatibility ANNGroupBuilder::kind(const coco::Block *blk) const +{ + return (_ctx->find(blk) != nullptr) ? COMPATIBLE : INCOMPATIBLE; +} + +void ANNGroupBuilder::build(enco::Code *code) const +{ + auto m = code->module(); + + // ANNGroupBuilder will construct a sequence of blocks from the original block sequence, and + // a destination block (that dst_blk points to) is the tail of the generated sequence. + coco::Block *dst_blk = nullptr; + + auto append = [&](const Compatibility &t) { + auto blk = m->entity()->block()->create(); + + if (dst_blk == nullptr) + { + m->block()->prepend(blk); + } + else + { + blk->insertAfter(dst_blk); + } + + dst_blk = blk; + + if (COMPATIBLE == t) + { + _ctx->create(blk); + } + }; + + for (auto blk = m->block()->head(); blk;) + { + // Let's move instructions from a block of interest (referred to as source block) into + // a destination block + auto src_blk = blk; + blk = src_blk->next(); + src_blk->detach(); + + for (auto ins = src_blk->instr()->head(); ins;) + { + auto cur_ins = ins; + ins = cur_ins->next(); + cur_ins->detach(); + + auto cur_append = make_appender(cur_ins); + + // Create a new compatible block and use it as a destination block if the current + // destination block is absent or incompatible with the instruction of intereset. + if ((dst_blk == nullptr) || (kind(cur_append) != kind(dst_blk))) + { + append(kind(cur_append)); + } + + assert(dst_blk != nullptr); + assert(kind(cur_append) == kind(dst_blk)); + + // Append ins to the dst_blk block + dst_blk->instr()->append(cur_ins); + + if (cur_append) + { + // Update Android NN IR if the current instruction is compatible + auto binder = _ctx->find(dst_blk); + assert(binder != nullptr); + cur_append->append(binder); + } + } + + // Destroy the source block + assert(src_blk->instr()->empty()); + m->entity()->block()->destroy(src_blk); + } +} + +} // namespace + +class ANNModuleBuilder +{ +private: + std::set inputs(ANNBinder *binder) const; + std::set outputs(ANNBinder *binder) const; + +public: + void build(ANNContext *ann_ctx) const; +}; + +std::set ANNModuleBuilder::inputs(ANNBinder *binder) const +{ + std::set res; + + for (auto bag : binder->bags()) + { + auto u = enco::updaters(bag); + u.erase(binder->block()); + + /** + * A bag is the input of this block if + * 1. it is an input of the whole network, or + * 2. it is updated by preceding blocks during execution + */ + if (bag->isInput() || (u.size() > 0)) + { + res.insert(bag); + } + } + + return res; +} + +std::set ANNModuleBuilder::outputs(ANNBinder *binder) const +{ + std::set res; + + for (auto bag : binder->bags()) + { + auto u = enco::updaters(bag); + auto r = enco::readers(bag); + r.erase(binder->block()); + + /** + * Only a bag that this block updates can be the output of this block + */ + if (u.find(binder->block()) == u.end()) + { + continue; + } + + /** + * A bag is the output of this block if + * 1. it is an output of the whole network, or + * 2. it is read by following blocks during execution + */ + if (bag->isOutput() || (r.size() > 0)) + { + res.insert(bag); + } + } + + return res; +} + +void ANNModuleBuilder::build(ANNContext *ann_ctx) const +{ + for (uint32_t n = 0; n < ann_ctx->count(); ++n) + { + auto binder = ann_ctx->nth(n); + + // NOTE binder->module() returns an ANN IR module (not coco IR module) + auto m = binder->block()->module(); + auto d = enco::data(m); + + // Let's identify operands with initial values + for (auto bag : binder->bags()) + { + if (binder->associated(bag) && d->allocated(bag)) + { + // TODO Support other datatype + auto span = d->f32()->weight(bag); + assert(span.data() != nullptr); + + binder->setOperand(binder->operand(bag), span.data(), span.data() + span.size()); + } + } + + // Let's identify input/output bags + binder->identifyInputs(inputs(binder)); + binder->identifyOutputs(outputs(binder)); + } +} + +} // namespace + +namespace +{ + +class SplitPass +{ +public: + void runOnCode(enco::Code *code) const; +}; + +void SplitPass::runOnCode(enco::Code *code) const +{ + auto ann_ctx = make_unique(); + + ANNGroupBuilder group_builder{ann_ctx.get()}; + group_builder.build(code); + + ANNModuleBuilder module_builder; + module_builder.build(ann_ctx.get()); + + _subnet_contexts[code->module()] = std::move(ann_ctx); +} + +} // namespace + +namespace enco +{ + +void split_into_phases(enco::Code *code) +{ + SplitPass split; + split.runOnCode(code); +} + +} // namespace enco diff --git a/compiler/enco/core/src/Transforms/Split.h b/compiler/enco/core/src/Transforms/Split.h new file mode 100644 index 000000000..b4e1d7baf --- /dev/null +++ b/compiler/enco/core/src/Transforms/Split.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SPLIT_H__ +#define __SPLIT_H__ + +#include "Code.h" +#include "Pass.h" + +namespace enco +{ + +struct SubnetManager +{ + static const ANNContext *context(const coco::Module *m); +}; + +/** + * @brief Split instructions into a set of phases + */ +void split_into_phases(enco::Code *code); + +struct PhaseConstructionPass final : public Pass +{ + PASS_CTOR(PhaseConstructionPass) + { + // DO NOTHING + } + + void run(const SessionID &sess) const override { split_into_phases(code(sess)); } +}; + +} // namespace enco; + +#endif // __SPLIT_H__ diff --git a/compiler/enco/core/src/Usage.cpp b/compiler/enco/core/src/Usage.cpp new file mode 100644 index 000000000..92ccba5a0 --- /dev/null +++ b/compiler/enco/core/src/Usage.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Usage.h" + +namespace enco +{ + +std::set readers(const coco::Bag *bag) +{ + std::set res; + + for (auto read : coco::readers(bag)) + { + assert(read != nullptr); + auto instr = read->loc(); + assert(instr != nullptr); + auto block = instr->parent(); + assert(block != nullptr); + + res.insert(block); + } + + return res; +} + +std::set updaters(const coco::Bag *bag) +{ + std::set res; + + for (auto update : coco::updaters(bag)) + { + assert(update != nullptr); + auto instr = update->loc(); + assert(instr != nullptr); + auto block = instr->parent(); + assert(block != nullptr); + + res.insert(block); + } + + return res; +} + +} // namespace enco diff --git a/compiler/enco/core/src/Usage.h b/compiler/enco/core/src/Usage.h new file mode 100644 index 000000000..8fa05f9b9 --- /dev/null +++ b/compiler/enco/core/src/Usage.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_USAGE_H__ +#define __ENCO_USAGE_H__ + +#include "coco/IR.h" + +#include + +namespace enco +{ + +/// @brief Returns the set of blocks that reads a given bag +std::set readers(const coco::Bag *bag); +/// @brief Return the set of blocks that updates a given bag +std::set updaters(const coco::Bag *bag); + +} // namespace enco + +#endif // __ENCO_USAGE_H__ diff --git a/compiler/enco/core/src/coex/IR.h b/compiler/enco/core/src/coex/IR.h new file mode 100644 index 000000000..e81943f18 --- /dev/null +++ b/compiler/enco/core/src/coex/IR.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ENCO_COEX_IR_H__ +#define __ENCO_COEX_IR_H__ + +#include + +/** + * @brief 2D Convolution through Andoird NN API + * + * TODO Support FusedActivation + */ +class ANNConv2D : public coco::Instr, public coco::Object::Producer, public coco::Object::Consumer +{ +public: + ANNConv2D() : _ofm{this}, _ifm{this}, _ker{this}, _bias{this} + { + // DO NOTHING + } + +public: + coco::Instr *loc(void) override { return this; } + +public: + coco::Object *ofm(void) const { return _ofm.value(); } + void ofm(coco::Object *o) { _ofm.value(o); } + + coco::Object *ifm(void) const { return _ifm.value(); } + void ifm(coco::Object *o) { _ifm.value(o); } + + coco::Object *ker(void) const { return _ker.value(); } + void ker(coco::Object *o) { _ker.value(o); } + + /** + * Currently, this "bias" is a Feature object with channel-wise layout + * + * NOTE This design is subject to change + */ + coco::Object *bias(void) const { return _bias.value(); } + void bias(coco::Object *o) { _bias.value(o); } + +public: + coco::Padding2D *pad(void) { return &_pad; } + const coco::Padding2D *pad(void) const { return &_pad; } + + coco::Stride2D *stride(void) { return &_stride; } + const coco::Stride2D *stride(void) const { return &_stride; } + +private: + coco::Def _ofm; + + coco::Use _ifm; + coco::Use _ker; + coco::Use _bias; + +private: + coco::Padding2D _pad; + coco::Stride2D _stride; +}; + +/** + * @brief Concatenate feature maps along "depth" dimension through Andoird NN API + */ +class ANNDepthConcatF : public coco::Instr, + public coco::Object::Producer, + public coco::Object::Consumer +{ +public: + ANNDepthConcatF() : _out{this}, _fst{this}, _snd{this} + { + // DO NOTHING + } + +public: + coco::Instr *loc(void) override { return this; } + +public: + coco::Object *out(void) const { return _out.value(); } + void out(coco::Object *o) { _out.value(o); } + + coco::Object *fst(void) const { return _fst.value(); } + void fst(coco::Object *o) { _fst.value(o); } + + coco::Object *snd(void) const { return _snd.value(); } + void snd(coco::Object *o) { _snd.value(o); } + +private: + coco::Def _out; + + // TODO Support variadic-length inputs + coco::Use _fst; + coco::Use _snd; +}; + +#endif // __ENCO_COEX_IR_H__ diff --git a/compiler/enco/core/src/coex/IR.test.cpp b/compiler/enco/core/src/coex/IR.test.cpp new file mode 100644 index 000000000..e20cbe4fd --- /dev/null +++ b/compiler/enco/core/src/coex/IR.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IR.h" + +#include + +TEST(IRTest, ANNConv2D_default_constructor) +{ + ANNConv2D ins; + + ASSERT_EQ(ins.ofm(), nullptr); + ASSERT_EQ(ins.ifm(), nullptr); + ASSERT_EQ(ins.ker(), nullptr); + ASSERT_EQ(ins.bias(), nullptr); +} + +TEST(IRTest, ANNDepthConcatF_default_constructor) +{ + ANNDepthConcatF ins; + + ASSERT_EQ(ins.out(), nullptr); + ASSERT_EQ(ins.fst(), nullptr); + ASSERT_EQ(ins.snd(), nullptr); +} diff --git a/compiler/enco/frontend/CMakeLists.txt b/compiler/enco/frontend/CMakeLists.txt new file mode 100644 index 000000000..5ea6cdadd --- /dev/null +++ b/compiler/enco/frontend/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectories() diff --git a/compiler/enco/frontend/caffe/CMakeLists.txt b/compiler/enco/frontend/caffe/CMakeLists.txt new file mode 100644 index 000000000..ce43a41d3 --- /dev/null +++ b/compiler/enco/frontend/caffe/CMakeLists.txt @@ -0,0 +1,39 @@ +nnas_find_package(CaffeProto QUIET) + +if(NOT CaffeProto_FOUND) + return() +endif(NOT CaffeProto_FOUND) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(enco_caffe_frontend SHARED ${SOURCES}) +target_include_directories(enco_caffe_frontend PRIVATE src) +target_link_libraries(enco_caffe_frontend coco_core) +target_link_libraries(enco_caffe_frontend coco_generic) +target_link_libraries(enco_caffe_frontend enco_intf_frontend) +target_link_libraries(enco_caffe_frontend enco_intf_cmdline) +target_link_libraries(enco_caffe_frontend morph) +target_link_libraries(enco_caffe_frontend caffeproto) +target_link_libraries(enco_caffe_frontend stdex) + +nnas_find_package(GTest QUIET) + +if(NOT GTest_FOUND) + return() +endif(NOT GTest_FOUND) + +nnas_find_package(Caffe QUIET) + +if(NOT Caffe_FOUND) + return() +endif(NOT Caffe_FOUND) + +add_executable(enco_caffe_frontend_test ${TESTS}) +target_include_directories(enco_caffe_frontend_test PRIVATE src) +target_link_libraries(enco_caffe_frontend_test gtest_main) +target_link_libraries(enco_caffe_frontend_test enco_caffe_frontend) +target_link_libraries(enco_caffe_frontend_test morph) +target_link_libraries(enco_caffe_frontend_test caffe) +add_test(enco_caffe_frontend_test enco_caffe_frontend_test) diff --git a/compiler/enco/frontend/caffe/src/ConcatSpec.cpp b/compiler/enco/frontend/caffe/src/ConcatSpec.cpp new file mode 100644 index 000000000..b83a1f902 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConcatSpec.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConcatSpec.h" + +#include + +using namespace nncc::core::ADT::tensor; + +nncc::core::ADT::tensor::Shape ConcatSpec::forward(const ShapeList &inputs) const +{ + assert(inputs.size() > 0); + + Shape output_shape = inputs.at(0); + + for (uint32_t n = 1; n < inputs.size(); ++n) + { + // The current implementation assumes that "inputs" is well-formed + // TODO Verify whether "inputs" is really well-formed + const auto &input_shape = inputs.at(n); + output_shape.dim(_axis) += input_shape.dim(_axis); + } + + return output_shape; +} + +ConcatSpec concat_spec(uint32_t axis) { return ConcatSpec{axis}; } diff --git a/compiler/enco/frontend/caffe/src/ConcatSpec.h b/compiler/enco/frontend/caffe/src/ConcatSpec.h new file mode 100644 index 000000000..cc636c778 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConcatSpec.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONCAT_SPEC_H__ +#define __CONCAT_SPEC_H__ + +#include + +#include + +using ShapeList = std::vector; + +class ConcatSpec +{ +public: + explicit ConcatSpec(uint32_t axis) : _axis{axis} + { + // DO NOTHING + } + +public: + /** + * @brief Return the output shape when inputs of given shape are + * concatenated along _axis + */ + nncc::core::ADT::tensor::Shape forward(const ShapeList &) const; + +private: + uint32_t _axis; +}; + +ConcatSpec concat_spec(uint32_t axis); + +#endif // __CONCAT_SPEC_H__ diff --git a/compiler/enco/frontend/caffe/src/ConcatSpec.test.cpp b/compiler/enco/frontend/caffe/src/ConcatSpec.test.cpp new file mode 100644 index 000000000..1cb2ea5af --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConcatSpec.test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConcatSpec.h" + +#include + +using nncc::core::ADT::tensor::Shape; + +namespace +{ +class ConcatSpecTest : public ::testing::Test +{ + // FOR FUTURE USE +}; +} // namespace + +TEST_F(ConcatSpecTest, ifm_shape) +{ + const Shape in_1{1, 1, 4, 4}; + const Shape in_2{1, 2, 4, 4}; + const Shape in_3{1, 3, 4, 4}; + const Shape in_4{1, 4, 4, 4}; + + auto expected = Shape{1, 10, 4, 4}; + auto obtained = concat_spec(1).forward({in_1, in_2, in_3, in_4}); + + ASSERT_EQ(expected, obtained); +} diff --git a/compiler/enco/frontend/caffe/src/Context.cpp b/compiler/enco/frontend/caffe/src/Context.cpp new file mode 100644 index 000000000..9f7204b25 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Context.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @note: This cpp file exist to check compilation integrity + */ + +#include "Context.h" diff --git a/compiler/enco/frontend/caffe/src/Context.h b/compiler/enco/frontend/caffe/src/Context.h new file mode 100644 index 000000000..aca57ce6f --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Context.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +#include + +#include +#include + +#include +#include +#include + +namespace caffeimport +{ + +using LayerName = std::string; +using BlobName = std::string; +// Note: these two maybe evolved to a class +using ShapeContext = std::map; +using StoreContext = std::map; + +class WeightContext +{ +public: + WeightContext(::caffe::NetParameter *caffemodel) : _caffemodel(caffemodel) + { + for (uint32_t n = 0; n < _caffemodel->layer_size(); ++n) + { + auto layer = _caffemodel->mutable_layer(n); + + if (layer->has_name()) + { + _data[layer->name()] = layer; + } + } + } + +public: + int blob_count(const LayerName &name) + { + if (_data.find(name) != _data.end()) + return _data.at(name)->blobs_size(); + + assert(false); + return 0; + } + + ::caffe::BlobProto *blob_get(const LayerName &name, uint32_t n) + { + if (_data.find(name) != _data.end()) + return _data.at(name)->mutable_blobs(n); + + assert(false); + return nullptr; + }; + +private: + ::caffe::NetParameter *_caffemodel; + std::map _data; +}; + +class GraphBuilderContext +{ +public: + explicit GraphBuilderContext(coco::Module *module, coco::Data *data, coco::Block *block, + ShapeContext &shape_ctx, StoreContext &bag_ctx, + WeightContext &weight_ctx) + : _module(module), _data(data), _block(block), _shape_ctx(shape_ctx), _bag_ctx(bag_ctx), + _weight_ctx(weight_ctx) + { + // DO NOTHING + } + + GraphBuilderContext(const GraphBuilderContext &) = delete; + GraphBuilderContext(GraphBuilderContext &&) = delete; + +public: + coco::Module *module() { return _module; } + coco::Data *data() { return _data; } + coco::Block *block() { return _block; } + ShapeContext &shape_ctx() { return _shape_ctx; } + StoreContext &bag_ctx() { return _bag_ctx; } + WeightContext &weight_ctx() { return _weight_ctx; } + +private: + coco::Module *_module; + coco::Data *_data; + coco::Block *_block; + ShapeContext &_shape_ctx; + StoreContext &_bag_ctx; + WeightContext &_weight_ctx; +}; + +} // namespace caffeimport + +#endif // __CONTEXT_H__ diff --git a/compiler/enco/frontend/caffe/src/Convert.cpp b/compiler/enco/frontend/caffe/src/Convert.cpp new file mode 100644 index 000000000..d697b1bd8 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Convert.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Convert.h" + +using namespace nncc::core::ADT; + +namespace caffeimport +{ + +tensor::Shape as_tensor_shape(const ::caffe::BlobShape &blob_shape) +{ + const uint32_t rank = blob_shape.dim_size(); + + tensor::Shape res; + + res.resize(rank); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = blob_shape.dim(axis); + } + + return res; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Convert.h b/compiler/enco/frontend/caffe/src/Convert.h new file mode 100644 index 000000000..9f6f9f104 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Convert.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include + +#include + +namespace caffeimport +{ + +nncc::core::ADT::tensor::Shape as_tensor_shape(const ::caffe::BlobShape &blob_shape); + +inline nncc::core::ADT::tensor::Shape as_tensor_shape(const ::caffe::BlobProto *blob_proto) +{ + return as_tensor_shape(blob_proto->shape()); +} + +} // namespace caffeimport + +#endif // __CONVERT_H__ diff --git a/compiler/enco/frontend/caffe/src/ConvolutionSpec.cpp b/compiler/enco/frontend/caffe/src/ConvolutionSpec.cpp new file mode 100644 index 000000000..e13ada836 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConvolutionSpec.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConvolutionSpec.h" +#include "PaddingUtils.h" +#include "ShapeQuery.h" + +#include + +ConvolutionSpec::ConvolutionSpec(const ::caffe::ConvolutionParameter ¶m) : _param(param) +{ + // NOTE Dilation is not supported, yet + // TODO Support dilation + assert(param.dilation().size() == 0); +} + +uint32_t ConvolutionSpec::group(void) const { return _param.group(); } + +uint32_t ConvolutionSpec::channel_axis(void) const +{ + return query_on(ifm_shape()).axis(axis_specifier(_param.axis())); +} + +uint32_t ConvolutionSpec::pad(uint32_t spatial_axis) const +{ + assert(spatial_axis < num_spatial_axes()); + + auto raw_padding = build_raw_padding().with(_param); + auto spatial_padding = build_spatial_padding(num_spatial_axes()).with(raw_padding); + + return spatial_padding.value(spatial_axis); +} + +uint32_t ConvolutionSpec::stride(uint32_t spatial_axis) const +{ + assert(spatial_axis < num_spatial_axes()); + + // TODO Support stride_h/stride_w parameters + assert(!_param.has_stride_h()); + assert(!_param.has_stride_w()); + + if (_param.stride().size() == 0) + { + // NOTE default stride is 1 + return 1; + } + + if (_param.stride().size() == 1) + { + return _param.stride(0); + } + + assert(_param.stride().size() == num_spatial_axes()); + return _param.stride(spatial_axis); +} + +uint32_t ConvolutionSpec::ker_dim(uint32_t spatial_axis) const +{ + assert(spatial_axis < num_spatial_axes()); + if (_param.kernel_size().size() == 0) + { + if (_param.has_kernel_h() && (spatial_axis == 0)) + { + assert(num_spatial_axes() == 2); + return _param.kernel_h(); + } + + if (_param.has_kernel_w() && (spatial_axis == 1)) + { + assert(num_spatial_axes() == 2); + return _param.kernel_w(); + } + + return 0; + } + + assert(!_param.has_kernel_h()); + assert(!_param.has_kernel_w()); + if (_param.kernel_size().size() == 1) + { + return _param.kernel_size(0); + } + else + { + assert(_param.kernel_size().size() == num_spatial_axes()); + return _param.kernel_size(spatial_axis); + } +} + +nncc::core::ADT::tensor::Shape ConvolutionSpec::ker_shape(void) const +{ + nncc::core::ADT::tensor::Shape res; + + res.resize(2 + num_spatial_axes()); + + res.dim(0) = ker_count(); + assert(ifm_dim(channel_axis()) % group() == 0); + res.dim(1) = ifm_dim(channel_axis()) / group(); + for (uint32_t axis = 0; axis < num_spatial_axes(); ++axis) + { + res.dim(2 + axis) = ker_dim(axis); + } + + return res; +} + +nncc::core::ADT::tensor::Shape ConvolutionSpec::ofm_shape(void) const +{ + nncc::core::ADT::tensor::Shape res; + + res.resize(num_batch_axes() + 1 + num_spatial_axes()); + + for (uint32_t axis = 0; axis < num_batch_axes(); ++axis) + { + res.dim(axis) = ifm_dim(axis); + } + + res.dim(num_batch_axes()) = ker_count(); + + for (uint32_t spatial_axis = 0; spatial_axis < num_spatial_axes(); ++spatial_axis) + { + const uint32_t full_axis = num_batch_axes() + 1 + spatial_axis; + + uint32_t dim = 0; + + dim += ifm_dim(full_axis) - ker_dim(spatial_axis) + 2 * pad(spatial_axis); + dim /= stride(spatial_axis); + dim += 1; + + res.dim(full_axis) = dim; + } + + return res; +} diff --git a/compiler/enco/frontend/caffe/src/ConvolutionSpec.h b/compiler/enco/frontend/caffe/src/ConvolutionSpec.h new file mode 100644 index 000000000..c5c7c9024 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConvolutionSpec.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVOLUTION_SPEC_H__ +#define __CONVOLUTION_SPEC_H__ + +#include + +#include + +class ConvolutionSpec +{ +public: + ConvolutionSpec(const ::caffe::ConvolutionParameter ¶m); + +public: + uint32_t ifm_rank(void) const { return _ifm_shape.rank(); } + uint32_t ifm_dim(uint32_t axis) const { return _ifm_shape.dim(axis); } + + uint32_t group(void) const; + + uint32_t channel_axis(void) const; + + uint32_t num_batch_axes(void) const { return channel_axis(); } + uint32_t num_spatial_axes(void) const { return ifm_rank() - channel_axis() - 1; } + + uint32_t pad(uint32_t spatial_axis) const; + uint32_t stride(uint32_t spatial_axis) const; + uint32_t ker_dim(uint32_t spatial_axis) const; + +public: + const nncc::core::ADT::tensor::Shape &ifm_shape(void) const { return _ifm_shape; } + void ifm_shape(const nncc::core::ADT::tensor::Shape &shape) { _ifm_shape = shape; } + +public: + uint32_t ker_count(void) const { return _param.num_output(); } + nncc::core::ADT::tensor::Shape ker_shape(void) const; + +public: + nncc::core::ADT::tensor::Shape ofm_shape(void) const; + +private: + const ::caffe::ConvolutionParameter &_param; + nncc::core::ADT::tensor::Shape _ifm_shape; +}; +#endif // __CONVOLUTION_SPEC_H__ diff --git a/compiler/enco/frontend/caffe/src/ConvolutionSpec.test.cpp b/compiler/enco/frontend/caffe/src/ConvolutionSpec.test.cpp new file mode 100644 index 000000000..02670b0cc --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ConvolutionSpec.test.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConvolutionSpec.h" +#include "Importer.h" + +#include + +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; + +#define STRING(content) #content + +namespace +{ +class ConvolutionSpecTest : public ::testing::Test +{ +protected: + tensor::Shape as_tensor_shape(const std::vector &dims) + { + const uint32_t rank = dims.size(); + + tensor::Shape res; + + res.resize(rank); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = dims.at(axis); + } + + return res; + } + + bool load(const std::string &prototxt, ::caffe::NetParameter ¶m) + { + std::stringstream ss{prototxt}; + + return from_txt(ss, param); + } +}; +} // namespace + +TEST_F(ConvolutionSpecTest, ifm_shape) +{ + ::caffe::ConvolutionParameter param; + ConvolutionSpec spec{param}; + + const tensor::Shape ifm_shape{1, 3, 244, 244}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.ifm_shape(), ifm_shape); + ASSERT_EQ(spec.num_batch_axes(), 1); + ASSERT_EQ(spec.num_spatial_axes(), 2); +} + +namespace +{ +// clang-format off +const char *conv_0 = STRING( +layer { + name: "data" + type : "Input" + top : "data" + input_param { shape: { dim: 1 dim : 3 dim : 244 dim : 244 } } +} +layer{ + name : "conv" + type : "Convolution" + bottom : "data" + top : "conv" + convolution_param { + bias_term : false + num_output : 1 + kernel_size : 1 + } +}); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, conv_0) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(conv_0, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 244, 244}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'ker_shape' + { + auto expected = as_tensor_shape(net.layer_by_name("conv")->blobs().at(0)->shape()); + auto obtained = spec.ker_shape(); + + ASSERT_EQ(expected, obtained); + } + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +namespace +{ +// clang-format off +const char *conv_1 = STRING( +layer { + name: "data" + type : "Input" + top : "data" + input_param { shape: { dim: 1 dim : 3 dim : 244 dim : 244 } } +} +layer{ + name : "conv" + type : "Convolution" + bottom : "data" + top : "conv" + convolution_param { + bias_term : false + num_output : 1 + kernel_size : 1 + kernel_size : 3 + } +}); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, conv_1) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(conv_1, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 244, 244}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'ker_shape' + { + auto expected = as_tensor_shape(net.layer_by_name("conv")->blobs().at(0)->shape()); + auto obtained = spec.ker_shape(); + + ASSERT_EQ(expected, obtained); + } + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +namespace +{ +// NOTE This example is derived from conv1_3x3_s2 layer in reference inception v3 layer +// clang-format off +const char *conv_2 = STRING( +layer { + name: "data" + type: "Input" + top: "data" + input_param { + shape: { dim: 1 dim: 3 dim: 299 dim: 299 } + } +} +layer { + name: "conv" + type: "Convolution" + bottom: "data" + top: "conv" + convolution_param { + bias_term: false + num_output: 2 + stride: 2 + kernel_size: 3 + } +} +); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, conv_2) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(conv_2, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 299, 299}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'stride' + ASSERT_EQ(spec.stride(0), 2); + ASSERT_EQ(spec.stride(1), 2); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +namespace +{ +// clang-format off +const char *conv_pad = STRING( +layer { + name: "data" + type: "Input" + top: "data" + input_param { + shape: { dim: 1 dim: 3 dim: 16 dim: 16 } + } +} +layer { + name: "conv" + type: "Convolution" + bottom: "data" + top: "conv" + convolution_param { + bias_term: false + num_output: 2 + pad: 2 + kernel_size: 3 + } +} +); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, conv_pad) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(conv_pad, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 16, 16}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'pad' + ASSERT_EQ(spec.pad(0), 2); + ASSERT_EQ(spec.pad(1), 2); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +namespace +{ +// clang-format off +const char *conv_ker_hw = STRING( +layer { + name: "data" + type: "Input" + top: "data" + input_param { + shape: { dim: 1 dim: 3 dim: 16 dim: 16 } + } +} +layer { + name: "conv" + type: "Convolution" + bottom: "data" + top: "conv" + convolution_param { + bias_term: false + num_output: 2 + kernel_h: 3 + kernel_w: 1 + } +} +); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, conv_ker_hw) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(conv_ker_hw, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 16, 16}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'pad' + ASSERT_EQ(spec.ker_dim(0), 3); + ASSERT_EQ(spec.ker_dim(1), 1); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +namespace +{ +// clang-format off +const char *dconv = STRING( +layer { + name: "data" + type: "Input" + top: "data" + input_param { + shape: { dim: 1 dim: 3 dim: 16 dim: 16 } + } +} +layer { + name: "conv" + type: "Convolution" + bottom: "data" + top: "conv" + convolution_param { + bias_term: false + num_output: 3 + kernel_size: 3 + group: 3 + } +} +); +// clang-format on +} // namespace + +TEST_F(ConvolutionSpecTest, dconv) +{ + ::caffe::NetParameter param; + + ASSERT_TRUE(load(dconv, param)); + + ::caffe::Net net{param}; + + const tensor::Shape ifm_shape{1, 3, 16, 16}; + ConvolutionSpec spec{param.layer(1).convolution_param()}; + + spec.ifm_shape(ifm_shape); + + // Check 'ker_shape' + { + auto expected = as_tensor_shape(net.layer_by_name("conv")->blobs().at(0)->shape()); + auto obtained = spec.ker_shape(); + + ASSERT_EQ(expected, obtained); + } + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("conv")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} diff --git a/compiler/enco/frontend/caffe/src/Entry.cpp b/compiler/enco/frontend/caffe/src/Entry.cpp new file mode 100644 index 000000000..2bdb73eac --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Entry.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Frontend.h" +#include "Importer.h" + +#include + +#include + +#include +#include + +extern "C" std::unique_ptr make_frontend(const cmdline::View &cmdline) +{ + assert(cmdline.size() == 2); + + auto frontend = stdex::make_unique(); + + // Fill prototxt + { + std::ifstream ifs{cmdline.at(0)}; + if (!ifs.is_open()) + { + throw std::runtime_error("Prototxt file open fail"); + } + + if (!from_txt(ifs, *frontend->prototxt())) + { + throw std::runtime_error("Filling prototxt fail"); + } + } + + // Fill caffemodel + { + std::ifstream ifs{cmdline.at(1), std::ios::binary}; + if (!ifs.is_open()) + { + throw std::runtime_error("Caffemodel file open fail"); + } + + if (!from_bin(ifs, *frontend->caffemodel())) + { + throw std::runtime_error("Filling caffemodel fail"); + } + } + + return std::move(frontend); +} diff --git a/compiler/enco/frontend/caffe/src/Frontend.cpp b/compiler/enco/frontend/caffe/src/Frontend.cpp new file mode 100644 index 000000000..7d2b3d36c --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Frontend.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Frontend.h" +#include "Context.h" +#include "GraphBuilderRegistry.h" + +#include +#include + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +using tensor::LexicalLayout; + +Frontend::Frontend() : _prototxt{new ::caffe::NetParameter}, _caffemodel{new ::caffe::NetParameter} +{ + // DO NOTHING +} + +enco::Bundle Frontend::load(void) const +{ + auto module = coco::Module::create(); + auto blk = module->entity()->block()->create(); + module->block()->append(blk); + + auto data = coco::Data::create(); + + // For weight access + caffeimport::WeightContext weight_ctx(_caffemodel.get()); + + // For inter-layer communication + std::map shape_ctx; + std::map bag_ctx; + + std::set bags; + std::map def_count; + std::map use_count; + + auto def = [&bags, &def_count, &use_count](const std::string &name) { + if (bags.find(name) == bags.end()) + { + bags.insert(name); + def_count[name] = 0; + use_count[name] = 0; + } + + def_count.at(name) += 1; + }; + + auto use = [&use_count](const std::string &name) { use_count.at(name) += 1; }; + + auto outputs = [&bags, &def_count, &use_count](void) { + std::set res; + + for (const auto &bag : bags) + { + if (def_count.at(bag) > use_count.at(bag)) + { + res.insert(bag); + } + } + + return res; + }; + + caffeimport::GraphBuilderContext opbuilder_context(module.get(), data.get(), blk, shape_ctx, + bag_ctx, weight_ctx); + + for (const auto &layer : _prototxt->layer()) + { + assert(layer.has_name()); + assert(layer.has_type()); + + for (uint32_t n = 0; n < layer.top().size(); ++n) + { + def(layer.top(n)); + } + + for (uint32_t n = 0; n < layer.bottom().size(); ++n) + { + use(layer.bottom(n)); + } + + if (const auto *graph_builder = caffeimport::GraphBuilderRegistry::get().lookup(layer.type())) + { + graph_builder->build(layer, &opbuilder_context); + } + else + { + throw std::runtime_error{"Not supported: " + layer.type()}; + } + } + + // Finalize: Create output for each top blob + for (const auto &name : outputs()) + { + const auto &shape = shape_ctx.at(name); + auto bag = bag_ctx.at(name); + + auto output = module->entity()->output()->create(shape); + + output->bag(bag); + output->name(name); + output->reorder(); + + module->output()->insert(output); + } + + enco::Bundle bundle; + + bundle.module(std::move(module)); + bundle.data(std::move(data)); + + return std::move(bundle); +} diff --git a/compiler/enco/frontend/caffe/src/Frontend.h b/compiler/enco/frontend/caffe/src/Frontend.h new file mode 100644 index 000000000..34fe90eba --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Frontend.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FRONTEND_H__ +#define __FRONTEND_H__ + +#include + +#include + +#include + +class Frontend final : public enco::Frontend +{ +public: + Frontend(); + +public: + ::caffe::NetParameter *prototxt(void) { return _prototxt.get(); } + ::caffe::NetParameter *caffemodel(void) { return _caffemodel.get(); } + +public: + enco::Bundle load(void) const override; + +private: + std::unique_ptr<::caffe::NetParameter> _prototxt; + std::unique_ptr<::caffe::NetParameter> _caffemodel; +}; + +#endif // __FRONTEND_H__ diff --git a/compiler/enco/frontend/caffe/src/GraphBuilder.cpp b/compiler/enco/frontend/caffe/src/GraphBuilder.cpp new file mode 100644 index 000000000..18ba10c08 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/GraphBuilder.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @note: This cpp file exist to check compilation integrity + */ + +#include "GraphBuilder.h" diff --git a/compiler/enco/frontend/caffe/src/GraphBuilder.h b/compiler/enco/frontend/caffe/src/GraphBuilder.h new file mode 100644 index 000000000..04adb96f4 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/GraphBuilder.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_H__ +#define __GRAPH_BUILDER_H__ + +#include "Context.h" + +#include + +namespace caffeimport +{ + +class GraphBuilder +{ +public: + virtual void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const = 0; + virtual ~GraphBuilder() {} +}; + +} // namespace caffeimport + +#endif // __GRAPH_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp new file mode 100644 index 000000000..e9db31177 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GraphBuilderRegistry.h" + +#include "Layer/Concatenation.h" +#include "Layer/Convolution.h" +#include "Layer/Eltwise.h" +#include "Layer/Input.h" +#include "Layer/Pooling.h" +#include "Layer/ReLU.h" +#include "Layer/Scale.h" +#include "Layer/BatchNorm.h" + +#include + +using stdex::make_unique; + +namespace caffeimport +{ + +GraphBuilderRegistry::GraphBuilderRegistry() +{ + _builder_map["Concat"] = make_unique(); + _builder_map["Convolution"] = make_unique(); + _builder_map["Eltwise"] = make_unique(); + _builder_map["Input"] = make_unique(); + _builder_map["Pooling"] = make_unique(); + _builder_map["ReLU"] = make_unique(); + _builder_map["Scale"] = make_unique(); + _builder_map["BatchNorm"] = make_unique(); +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.h b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.h new file mode 100644 index 000000000..035d32a4b --- /dev/null +++ b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_REGISTRY_H__ +#define __GRAPH_BUILDER_REGISTRY_H__ + +#include "GraphBuilder.h" + +#include +#include + +namespace caffeimport +{ + +class GraphBuilderRegistry +{ +public: + const GraphBuilder *lookup(const std::string &layer) const + { + if (_builder_map.find(layer) == _builder_map.end()) + return nullptr; + + return _builder_map.at(layer).get(); + } + + static GraphBuilderRegistry &get() + { + static GraphBuilderRegistry me; + return me; + } + +private: + GraphBuilderRegistry(); + +private: + std::map> _builder_map; +}; + +} // namespace caffeimport + +#endif // __GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/enco/frontend/caffe/src/IRBuilder.h b/compiler/enco/frontend/caffe/src/IRBuilder.h new file mode 100644 index 000000000..fe34328af --- /dev/null +++ b/compiler/enco/frontend/caffe/src/IRBuilder.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IR_BUILDER_H__ +#define __IR_BUILDER_H__ + +#include "coco/IR/Module.h" + +#include + +/** + * coco IR builders + */ + +class OpBuilder +{ +public: + OpBuilder(coco::Module *module) : _module{module} + { + // module SHOULD BE valid + assert(_module != nullptr); + } + +public: + /** + * @brief Return true if the internal stack is empty + */ + bool empty(void) const { return _stack.empty(); } + + /** + * @brief Return the operation at the top of the internal stack + */ + coco::Op *top(void) const + { + assert(_stack.size() > 0); + return _stack.front(); + } + + /** + * @brief Push op onto the internal stack + * + * BEFORE| Stack + * AFTER | Op; Stack + */ + OpBuilder &push(coco::Op *op) + { + _stack.push_front(op); + return (*this); + } + + /** + * @brief Create "Load" op and push it onto the internal stack + * + * BEFORE| Stack + * AFTER | Load(obj); Stack + */ + OpBuilder &load(coco::Object *obj) + { + auto op = _module->entity()->op()->create(); + op->object(obj); + push(op); + return (*this); + } + + /** + * @brief Create "Add" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Add(Left, Right); Stack + */ + OpBuilder &add(void) { return binary(); } + + /** + * @brief Create "Sub" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Sub(Left, Right); Stack + */ + OpBuilder &sub(void) { return binary(); } + + /** + * @brief Create "Mul" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Mul(Left, Right); Stack + */ + OpBuilder &mul(void) { return binary(); } + + /** + * @brief Create "Div" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Div(Left, Right); Stack + */ + OpBuilder &div(void) { return binary(); } + + /** + * @brief Pop op from the internal stack + * + * BEFORE| Op; Stack + * AFTER | Stack + */ + coco::Op *pop(void) + { + assert(_stack.size() > 0); + auto op = _stack.front(); + _stack.pop_front(); + return op; + } + +private: + template OpBuilder &binary() + { + assert(_stack.size() >= 2); + auto left = pop(); + auto right = pop(); + + auto op = _module->entity()->op()->create(); + op->left(left); + op->right(right); + push(op); + + return (*this); + } + +private: + coco::Module *_module; + std::deque _stack; +}; + +inline OpBuilder op_builder(coco::Module *m) { return OpBuilder{m}; } +inline OpBuilder op_builder(const std::unique_ptr &m) { return op_builder(m.get()); } + +class InstrBuilder +{ +public: + InstrBuilder(coco::Module *module) : _module{module} + { + // NOTE _module SHOULD be valid + assert(_module != nullptr); + } + +public: + /** + * @brief Create "Eval" instruction with a given "Object" and "Op" + * + * @note "eval(out, op)" will create "%out <- Eval(op)" instruction + */ + coco::Eval *eval(coco::Object *out, coco::Op *op) const + { + auto ins = _module->entity()->instr()->create(); + ins->op(op); + ins->out(out); + return ins; + } + +private: + coco::Module *_module; +}; + +inline InstrBuilder instr_builder(coco::Module *m) { return InstrBuilder{m}; } +inline InstrBuilder instr_builder(const std::unique_ptr &m) +{ + return instr_builder(m.get()); +} + +#endif // __IR_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Importer.cpp b/compiler/enco/frontend/caffe/src/Importer.cpp new file mode 100644 index 000000000..943a54e5d --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Importer.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Importer.h" + +#include +#include +#include + +bool from_txt(std::istream &is, ::caffe::NetParameter ¶m) +{ + google::protobuf::io::IstreamInputStream iis{&is}; + + if (!google::protobuf::TextFormat::Parse(&iis, ¶m)) + { + return false; + } + + return true; +} + +bool from_bin(std::istream &is, ::caffe::NetParameter ¶m) +{ + google::protobuf::io::IstreamInputStream iis{&is}; + google::protobuf::io::CodedInputStream cis{&iis}; + + if (!param.ParseFromCodedStream(&cis)) + { + return false; + } + + return true; +} + +bool from_txt(std::istream &is, ::caffe::PoolingParameter ¶m) +{ + ::google::protobuf::io::IstreamInputStream iis{&is}; + return google::protobuf::TextFormat::Parse(&iis, ¶m); +} diff --git a/compiler/enco/frontend/caffe/src/Importer.h b/compiler/enco/frontend/caffe/src/Importer.h new file mode 100644 index 000000000..ac83c0b27 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Importer.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IMPORTER_H__ +#define __IMPORTER_H__ + +#include + +#include + +bool from_txt(std::istream &is, ::caffe::NetParameter ¶m); +bool from_bin(std::istream &is, ::caffe::NetParameter ¶m); + +bool from_txt(std::istream &is, ::caffe::PoolingParameter ¶m); + +#endif // __IMPORTER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/BatchNorm.cpp b/compiler/enco/frontend/caffe/src/Layer/BatchNorm.cpp new file mode 100644 index 000000000..ff1e86570 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/BatchNorm.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BatchNorm.h" +#include "IRBuilder.h" + +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +using tensor::num_elements; + +namespace caffeimport +{ + +void BatchNormBuilder::build(const ::caffe::LayerParameter &layer, + GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Data *data = context->data(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + WeightContext &weight_ctx = context->weight_ctx(); + + assert(layer.bottom().size() == 1); + assert(layer.top().size() == 1); + + assert(layer.has_batch_norm_param()); + const auto ¶m = layer.batch_norm_param(); + + // TODO Support training case + assert(param.use_global_stats() == true); + + // Create an object for an input feature map + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + auto ifm_bag = bag_ctx.at(ifm_name); + auto ifm_obj = module->entity()->object()->create(); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + const auto ofm_name = layer.top(0); + const auto ofm_shape = ifm_shape; + auto ofm_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto ofm_obj = module->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + // Create an object for the scaled mean estimates data + auto mean_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto mean_obj = module->entity()->object()->create(); + + mean_obj->bag(mean_bag); + mean_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Create an object for the scaled variance estimates data + auto variance_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto variance_obj = module->entity()->object()->create(); + + variance_obj->bag(variance_bag); + variance_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + if (param.use_global_stats()) + { + // Use the stored mean/variance estimates. + assert(weight_ctx.blob_count(layer.name()) == 3); + + // Create an object for scale factor data + auto factor_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto factor_obj = module->entity()->object()->create(); + + factor_obj->bag(factor_bag); + factor_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Fill "scale factor" data + { + data->f32()->allocate(factor_bag); + + auto dst = data->f32()->weight(factor_bag); + // Calculate scale factor + auto blob = weight_ctx.blob_get(layer.name(), 2); + const auto scale_factor = blob->data(0) == 0 ? 0.f : 1 / blob->data(0); + + for (uint32_t ch = 0; ch < factor_obj->shape().depth(); ++ch) + { + dst[ch] = scale_factor; + } + } + + // Create an object for saved mean data + auto saved_mean_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto saved_mean_obj = module->entity()->object()->create(); + + saved_mean_obj->bag(saved_mean_bag); + saved_mean_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Fill "saved mean estimates" data + { + data->f32()->allocate(saved_mean_bag); + + auto dst = data->f32()->weight(saved_mean_bag); + auto blob = weight_ctx.blob_get(layer.name(), 0); + + for (uint32_t ch = 0; ch < saved_mean_obj->shape().depth(); ++ch) + { + dst[ch] = blob->data(ch); + } + } + + // Multiply scale factor to mean data + { + auto mul_op = op_builder(module).load(factor_obj).load(saved_mean_obj).mul().pop(); + auto mul_ins = instr_builder(module).eval(mean_obj, mul_op); + + blk->instr()->append(mul_ins); + } + + // Create an object for saved variance data + auto saved_variance_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto saved_variance_obj = module->entity()->object()->create(); + + saved_variance_obj->bag(saved_variance_bag); + saved_variance_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Fill "saved variance estimates" data + { + data->f32()->allocate(saved_variance_bag); + + auto dst = data->f32()->weight(saved_variance_bag); + auto blob = weight_ctx.blob_get(layer.name(), 1); + + for (uint32_t ch = 0; ch < saved_variance_obj->shape().depth(); ++ch) + { + dst[ch] = blob->data(ch); + } + } + + // Multiply scale factor to variance data + { + auto mul_op = op_builder(module).load(factor_obj).load(saved_variance_obj).mul().pop(); + auto mul_ins = instr_builder(module).eval(variance_obj, mul_op); + + blk->instr()->append(mul_ins); + } + } + else + { + // TODO use_global_stats() == false case + } + + // Create an object for subtraction + auto sub_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto sub_obj = module->entity()->object()->create(); + + sub_obj->bag(sub_bag); + sub_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + // Subtract mean + { + auto sub_op = op_builder(module).load(mean_obj).load(ifm_obj).sub().pop(); + auto sub_ins = instr_builder(module).eval(sub_obj, sub_op); + + blk->instr()->append(sub_ins); + } + + // Create an object for normalize variance data + auto norm_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto norm_obj = module->entity()->object()->create(); + + norm_obj->bag(norm_bag); + norm_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Normalize variance + { + // Create an object for epsilon data + auto eps_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto eps_obj = module->entity()->object()->create(); + + eps_obj->bag(eps_bag); + eps_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Fill "epsilon" data + { + data->f32()->allocate(eps_bag); + + auto dst = data->f32()->weight(eps_bag); + auto eps = param.eps(); + + for (uint32_t ch = 0; ch < eps_obj->shape().depth(); ++ch) + { + dst[ch] = eps; + } + } + + // Create a temp object + auto temp_bag = module->entity()->bag()->create(ofm_shape.dim(1)); + auto temp_obj = module->entity()->object()->create(); + + temp_obj->bag(temp_bag); + temp_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + // Add epsilon to variance + { + auto add_op = op_builder(module).load(variance_obj).load(eps_obj).add().pop(); + auto add_ins = instr_builder(module).eval(temp_obj, add_op); + + blk->instr()->append(add_ins); + } + + // Sqrt variance + { + auto load = op_builder(module).load(temp_obj).pop(); + auto sqrt_op = module->entity()->op()->create(); + sqrt_op->arg(load); + auto sqrt_ins = instr_builder(module).eval(norm_obj, sqrt_op); + + blk->instr()->append(sqrt_ins); + } + } + + // Replicate variance to input size + { + auto div_op = op_builder(module).load(norm_obj).load(sub_obj).div().pop(); + auto div_ins = instr_builder(module).eval(ofm_obj, div_op); + + blk->instr()->append(div_ins); + } + + // Update bag and shape context + bag_ctx[ofm_name] = ofm_bag; + shape_ctx[ofm_name] = ofm_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/BatchNorm.h b/compiler/enco/frontend/caffe/src/Layer/BatchNorm.h new file mode 100644 index 000000000..613b6687e --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/BatchNorm.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BATCHNORM_BUILDER_H__ +#define __BATCHNORM_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class BatchNormBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __BATCHNORM_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Concatenation.cpp b/compiler/enco/frontend/caffe/src/Layer/Concatenation.cpp new file mode 100644 index 000000000..f05f5908a --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Concatenation.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Concatenation.h" +#include "IRBuilder.h" + +#include + +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +namespace caffeimport +{ + +void ConcatBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + + assert(layer.bottom().size() > 0); + assert(layer.top().size() == 1); + + // Assume default concat axis + // - Please refer to http://caffe.berkeleyvision.org/tutorial/layers/concat.html for details + // TODO Get concat axis from concat param + assert(!layer.has_concat_param()); + const uint32_t concat_axis = 1; + + // Construct a vector of input objects + std::vector input_objects; + + for (const auto &input_name : layer.bottom()) + { + const auto input_shape = as_feature_shape(shape_ctx.at(input_name)); + + auto input_bag = bag_ctx.at(input_name); + auto input_feature = module->entity()->object()->create(); + + input_feature->bag(input_bag); + input_feature->layout(coco::FeatureLayouts::BCHW::create(input_shape)); + + input_objects.emplace_back(input_feature); + } + + coco::FeatureObject *last_feature = input_objects.at(0); + + assert(last_feature != nullptr); + assert(last_feature->bag() != nullptr); + + // Update coco IR + // + // Given a sequence of input features %in[0] / %in[1] / ... / %in[N] + // the below code constructs a sequence of eval instructions + // - Load is omitted for simplicity + // + // %out[0] = eval(ConcatF(%in[0], %in[1])) + // %out[1] = eval(ConcatF(%out[0], %in[2])) + // ... + // %out[N - 1] = eval(ConcatF(%out[N - 2], %in[N])) + // + for (uint32_t n = 1; n < input_objects.size(); ++n) + { + auto const left_feature = last_feature; + auto const left_shape = left_feature->layout()->shape(); + + auto right_feature = input_objects.at(n); + auto right_shape = right_feature->layout()->shape(); + + // Batch is not supported, yet + assert(left_feature->layout()->batch() == 1); + assert(right_feature->layout()->batch() == 1); + + // Height and Width SHOULD BE IDENTICAL for depth concat + assert(left_shape.height() == right_shape.height()); + assert(left_shape.width() == right_shape.width()); + + const uint32_t C = left_shape.depth() + right_shape.depth(); + const uint32_t H = left_shape.height(); + const uint32_t W = left_shape.width(); + + const nncc::core::ADT::feature::Shape out_shape{C, H, W}; + + auto out_bag = module->entity()->bag()->create(num_elements(out_shape)); + auto out_feature = module->entity()->object()->create(); + + out_feature->bag(out_bag); + out_feature->layout(coco::FeatureLayouts::BCHW::create(out_shape)); + + auto left_load = op_builder(module).load(left_feature).pop(); + auto right_load = op_builder(module).load(right_feature).pop(); + + auto concat_f = module->entity()->op()->create(); + + concat_f->axis(coco::ConcatF::Axis::Depth); + concat_f->left(left_load); + concat_f->right(right_load); + + auto eval = instr_builder(module).eval(out_feature, concat_f); + + // Append the constructed Shuffle instruction + blk->instr()->append(eval); + + // Update 'last_feature' + last_feature = out_feature; + } + + assert(last_feature != nullptr); + assert(last_feature->bag() != nullptr); + + // Update bag and shape context + auto const out_name = layer.top(0); + auto const out_shape = as_tensor_shape(last_feature->layout()->shape()); + auto const out_bag = last_feature->bag(); + + bag_ctx[out_name] = out_bag; + shape_ctx[out_name] = out_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Concatenation.h b/compiler/enco/frontend/caffe/src/Layer/Concatenation.h new file mode 100644 index 000000000..85e04000d --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Concatenation.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONCAT_BUILDER_H__ +#define __CONCAT_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class ConcatBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __CONCAT_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp b/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp new file mode 100644 index 000000000..9fb096d49 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Convolution.h" +#include "ConvolutionSpec.h" +#include "Convert.h" +#include "IRBuilder.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +using tensor::num_elements; + +namespace caffeimport +{ + +void ConvolutionBuilder::build(const ::caffe::LayerParameter &layer, + GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Data *data = context->data(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + WeightContext &weight_ctx = context->weight_ctx(); + + assert(layer.bottom().size() == 1); + assert(layer.top().size() == 1); + + assert(layer.has_convolution_param()); + const auto ¶m = layer.convolution_param(); + + ConvolutionSpec spec{param}; + { + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + spec.ifm_shape(ifm_shape); + } + + // NOTE The current implementation focuses on 2D convolution + // TODO Support general ND convolution + assert(spec.num_batch_axes() == 1); + assert(spec.num_spatial_axes() == 2); + + // Create an object for an input feature map + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + auto ifm_bag = bag_ctx.at(ifm_name); + auto ifm_obj = module->entity()->object()->create(); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + const auto ofm_name = layer.top(0); + const auto ofm_shape = spec.ofm_shape(); + auto ofm_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto ofm_obj = module->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + // Create an object for kernel + using namespace coco::KernelLayouts; + + const auto ker_shape = spec.ker_shape(); + auto ker_bag = module->entity()->bag()->create(num_elements(ker_shape)); + auto ker_obj = module->entity()->object()->create(); + + ker_obj->bag(ker_bag); + ker_obj->layout(NCHW::create(as_kernel_shape(ker_shape))); + + // Create a kernel overlay for the kernel object + data->f32()->allocate(ker_bag); + + // Initialize the kernel overlay + assert(weight_ctx.blob_count(layer.name()) >= 1); + auto ker_blob = weight_ctx.blob_get(layer.name(), 0); + + assert(ker_shape == caffeimport::as_tensor_shape(ker_blob)); + + auto ker_dst = data->f32()->access(ker_obj); + auto ker_src = kernel::OverlayFactory::make( + ker_obj->shape(), ker_blob->mutable_data()->begin()); + + for (uint32_t n = 0; n < ker_obj->shape().count(); ++n) + { + for (uint32_t ch = 0; ch < ker_obj->shape().depth(); ++ch) + { + for (uint32_t row = 0; row < ker_obj->shape().height(); ++row) + { + for (uint32_t col = 0; col < ker_obj->shape().width(); ++col) + { + ker_dst->at(n, ch, row, col) = ker_src.at(n, ch, row, col); + } + } + } + } + + // Create a Load op + auto load = op_builder(module).load(ifm_obj).pop(); + + // Create a Conv2D op + auto op = module->entity()->op()->create(); + + op->group(spec.group()); + + op->ker(ker_obj); + op->stride()->vertical(spec.stride(0)); + op->stride()->horizontal(spec.stride(1)); + + op->pad()->top(spec.pad(0)); + op->pad()->bottom(spec.pad(0)); + op->pad()->left(spec.pad(1)); + op->pad()->right(spec.pad(1)); + + op->arg(load); + + // Create an Eval instruction + auto ins = instr_builder(module).eval(ofm_obj, op); + + // Append the instruction to the block + blk->instr()->append(ins); + + // + // coco IR allows Conv2D fused with Add, but the current implementation of enco backend + // is unable to process such a tree. + // + // As a workaround, caffe frontend constructs a instruction for Conv2D and Add. + // + if (param.bias_term()) + { + assert(weight_ctx.blob_count(layer.name()) >= 2); + + // Create Bag & Object + auto bias_bag = module->entity()->bag()->create(ker_shape.dim(0)); + auto bias_obj = module->entity()->object()->create(); + + bias_obj->bag(bias_bag); + bias_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(ofm_shape))); + + auto added_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto added_obj = module->entity()->object()->create(); + + added_obj->bag(added_bag); + added_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + // Create Op + auto bias_add = op_builder(module).load(bias_obj).load(ofm_obj).add().pop(); + + // Create Instr + auto bias_add_ins = instr_builder(module).eval(added_obj, bias_add); + + // Append the instruction + blk->instr()->append(bias_add_ins); + + // Fill bias data + data->f32()->allocate(bias_bag); + + auto bias_span = data->f32()->weight(bias_bag); + auto bias_blob = weight_ctx.blob_get(layer.name(), 1); + + for (uint32_t ch = 0; ch < ker_obj->shape().count(); ++ch) + { + bias_span[ch] = bias_blob->data(ch); + } + + // Update output + ofm_bag = added_bag; + } + + // Update bag and shape context + bag_ctx[ofm_name] = ofm_bag; + shape_ctx[ofm_name] = ofm_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Convolution.h b/compiler/enco/frontend/caffe/src/Layer/Convolution.h new file mode 100644 index 000000000..a944f12a3 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Convolution.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVOLUTION_BUILDER_H__ +#define __CONVOLUTION_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class ConvolutionBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __CONVOLUTION_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Eltwise.cpp b/compiler/enco/frontend/caffe/src/Layer/Eltwise.cpp new file mode 100644 index 000000000..6a5d4f196 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Eltwise.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Eltwise.h" +#include "IRBuilder.h" + +#include + +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +namespace caffeimport +{ + +void EltwiseBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + + using coco::FeatureLayouts::BCHW; + + assert(layer.bottom().size() > 1); + assert(layer.top().size() == 1); + + assert(layer.has_eltwise_param()); + const auto ¶m = layer.eltwise_param(); + + using ::caffe::EltwiseParameter_EltwiseOp; + using ::caffe::EltwiseParameter_EltwiseOp_SUM; + using ::caffe::EltwiseParameter_EltwiseOp_PROD; + + using Reducer = std::function; + using ReducerRegistry = std::map; + + ReducerRegistry registry; + + // MAX are not supported, yet + registry[EltwiseParameter_EltwiseOp_SUM] = [](coco::Op *lhs, coco::Op *rhs) -> coco::Op * { + if (lhs == nullptr) + { + assert(rhs != nullptr); + return rhs; + } + + assert(lhs != nullptr && rhs != nullptr); + assert(lhs->module() == rhs->module()); + assert(lhs->module() != nullptr); + + auto m = lhs->module(); + return op_builder(m).push(rhs).push(lhs).add().pop(); + }; + + registry[EltwiseParameter_EltwiseOp_PROD] = [](coco::Op *lhs, coco::Op *rhs) -> coco::Op * { + if (lhs == nullptr) + { + assert(rhs != nullptr); + return rhs; + } + + assert(lhs != nullptr && rhs != nullptr); + assert(lhs->module() == rhs->module()); + assert(lhs->module() != nullptr); + + auto m = lhs->module(); + return op_builder(m).push(rhs).push(lhs).mul().pop(); + }; + + // coeff is not supported, yet + assert(!param.coeff().size()); + + // Decide appropriate reduce function + auto reduce = registry.at(param.operation()); + + coco::Op *op = nullptr; + + for (const auto &ifm_name : layer.bottom()) + { + auto ifm_shape = shape_ctx.at(ifm_name); + + // NOTE The current implementation does not work in general + auto ifm_bag = bag_ctx.at(ifm_name); + auto ifm_obj = module->entity()->object()->create(); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(BCHW::create(as_feature_shape(ifm_shape))); + + auto load = op_builder(module).load(ifm_obj).pop(); + + op = reduce(op, load); + } + + assert(op != nullptr); + + const auto ofm_name = layer.top(0); + const auto ofm_shape = shape_ctx.at(layer.bottom(0)); + + auto ofm_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto ofm_obj = module->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(BCHW::create(as_feature_shape(ofm_shape))); + + // Create "Eval" instruction + auto eval = instr_builder(module).eval(ofm_obj, op); + + // Append the instruction to the block + blk->instr()->append(eval); + + // Update bag and shape context + bag_ctx[ofm_name] = ofm_bag; + shape_ctx[ofm_name] = ofm_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Eltwise.h b/compiler/enco/frontend/caffe/src/Layer/Eltwise.h new file mode 100644 index 000000000..e717077ec --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Eltwise.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ELTWISE_BUILDER_H__ +#define __ELTWISE_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class EltwiseBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __ELTWISE_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Input.cpp b/compiler/enco/frontend/caffe/src/Layer/Input.cpp new file mode 100644 index 000000000..39e44fa31 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Input.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Input.h" +#include "Convert.h" + +#include + +#include + +using namespace nncc::core::ADT; + +using tensor::num_elements; +using tensor::LexicalLayout; + +namespace caffeimport +{ + +void InputBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + + assert(layer.has_input_param()); + const auto ¶m = layer.input_param(); + + for (uint32_t n = 0; n < layer.top_size(); ++n) + { + const auto &name = layer.top(n); + const auto shape = as_tensor_shape(param.shape(n)); + + auto bag = module->entity()->bag()->create(num_elements(shape)); + auto input = module->entity()->input()->create(shape); + + input->bag(bag); + input->name(name); + input->reorder(); + + module->input()->insert(input); + + bag_ctx[name] = bag; + shape_ctx[name] = shape; + } +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Input.h b/compiler/enco/frontend/caffe/src/Layer/Input.h new file mode 100644 index 000000000..2f464748d --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Input.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __INPUT_BUILDER_H__ +#define __INPUT_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class InputBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __INPUT_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Pooling.cpp b/compiler/enco/frontend/caffe/src/Layer/Pooling.cpp new file mode 100644 index 000000000..36220d841 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Pooling.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Pooling.h" +#include "PoolingSpec.h" +#include "IRBuilder.h" + +#include + +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +namespace caffeimport +{ + +void PoolingBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + + assert(layer.bottom().size() == 1); + assert(layer.top().size() == 1); + + assert(layer.has_pooling_param()); + const auto ¶m = layer.pooling_param(); + + PoolingSpec spec{param}; + { + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + spec.ifm_shape(ifm_shape); + } + + // Create an object for an input feature map + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + auto ifm_bag = bag_ctx.at(ifm_name); + auto ifm_obj = module->entity()->object()->create(); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + const auto ofm_name = layer.top(0); + const auto ofm_shape = spec.ofm_shape(); + auto ofm_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto ofm_obj = module->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + using PoolingOpBuilder = std::function; + + std::map builders; + + // MaxPool2D op builder + builders[PoolingMethod::Max] = [ifm_obj](coco::Module *module, const PoolingSpec &spec) { + auto load = op_builder(module).load(ifm_obj).pop(); + + auto op = module->entity()->op()->create(); + + op->arg(load); + + op->window()->height(spec.window_height()); + op->window()->width(spec.window_width()); + + op->stride()->vertical(spec.vertical_stride()); + op->stride()->horizontal(spec.horizontal_stride()); + + op->pad()->top(spec.vertical_pad()); + op->pad()->bottom(spec.vertical_pad()); + op->pad()->left(spec.horizontal_pad()); + op->pad()->right(spec.horizontal_pad()); + + return op; + }; + + // AvgPool2D op builder + builders[PoolingMethod::Avg] = [ifm_obj](coco::Module *module, const PoolingSpec &spec) { + auto load = op_builder(module).load(ifm_obj).pop(); + + auto op = module->entity()->op()->create(); + + op->arg(load); + + // NOTE Caffe use static divisor on average pooling + op->divisor(coco::AvgPool2D::Divisor::Static); + + op->window()->height(spec.window_height()); + op->window()->width(spec.window_width()); + + op->stride()->vertical(spec.vertical_stride()); + op->stride()->horizontal(spec.horizontal_stride()); + + op->pad()->top(spec.vertical_pad()); + op->pad()->bottom(spec.vertical_pad()); + op->pad()->left(spec.horizontal_pad()); + op->pad()->right(spec.horizontal_pad()); + + return op; + }; + + // Create a pooling op + auto builder = builders.at(spec.method()); + auto op = builder(module, spec); + + // Create a UnitF instruction + auto ins = instr_builder(module).eval(ofm_obj, op); + + // Append the instruction to the block + blk->instr()->append(ins); + + // Update bag and shape context + bag_ctx[ofm_name] = ofm_bag; + shape_ctx[ofm_name] = ofm_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Pooling.h b/compiler/enco/frontend/caffe/src/Layer/Pooling.h new file mode 100644 index 000000000..e72fd7aef --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Pooling.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __POOLING_BUILDER_H__ +#define __POOLING_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class PoolingBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __POOLING_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/ReLU.cpp b/compiler/enco/frontend/caffe/src/Layer/ReLU.cpp new file mode 100644 index 000000000..61e206dc2 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/ReLU.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReLU.h" +#include "IRBuilder.h" + +#include + +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +namespace caffeimport +{ + +void ReLUBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + + assert(layer.bottom().size() == 1); + assert(layer.top().size() == 1); + + // PReLU is not supported, yet + // TODO Support PReLU + assert(!layer.has_relu_param()); + + // NOTE The current implementation treats ReLU as Feature op + // TODO Support ReLU over general tensor + const auto ifm_name = layer.bottom(0); + const auto ifm_shape = shape_ctx.at(ifm_name); + auto ifm_bag = bag_ctx.at(ifm_name); + auto ifm_obj = module->entity()->object()->create(); + + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ifm_shape))); + + const auto ofm_name = layer.top(0); + const auto ofm_shape = ifm_shape; + auto ofm_bag = module->entity()->bag()->create(num_elements(ofm_shape)); + auto ofm_obj = module->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(ofm_shape))); + + // Create a Load Op + auto load = op_builder(module).load(ifm_obj).pop(); + + // Create a ReLU op + auto op = module->entity()->op()->create(); + + op->arg(load); + + // Create a Eval instruction + auto ins = instr_builder(module).eval(ofm_obj, op); + + // Append the instruction to the block + blk->instr()->append(ins); + + // Update bag and shape context + bag_ctx[ofm_name] = ofm_bag; + shape_ctx[ofm_name] = ofm_shape; +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/ReLU.h b/compiler/enco/frontend/caffe/src/Layer/ReLU.h new file mode 100644 index 000000000..94836fd8e --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/ReLU.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RELU_BUILDER_H__ +#define __RELU_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class ReLUBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __RELU_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Layer/Scale.cpp b/compiler/enco/frontend/caffe/src/Layer/Scale.cpp new file mode 100644 index 000000000..b9925978c --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Scale.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Scale.h" +#include "IRBuilder.h" + +#include + +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::caffe; + +namespace caffeimport +{ + +void ScaleBuilder::build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const +{ + coco::Module *module = context->module(); + coco::Data *data = context->data(); + coco::Block *blk = context->block(); + std::map &shape_ctx = context->shape_ctx(); + std::map &bag_ctx = context->bag_ctx(); + WeightContext &weight_ctx = context->weight_ctx(); + + // TODO Support Scale layer with 2 bottoms + assert(layer.bottom().size() == 1); + assert(layer.top().size() == 1); + + assert(layer.has_scale_param()); + const auto ¶m = layer.scale_param(); + + assert(param.axis() == 1); + assert(!param.has_num_axes()); + + assert(weight_ctx.blob_count(layer.name()) >= 1); + + // NOTE The shape of "Scale" output is same as that of its input + // NOTE The current implementation assumes that input/output is of feature type + // TODO Support generic tensor arguments + auto shape = shape_ctx.at(layer.bottom(0)); + + coco::Bag *last_bag = bag_ctx.at(layer.bottom(0)); + + // Create channel-wise multiplication + { + auto in_bag = last_bag; + auto in_obj = module->entity()->object()->create(); + + in_obj->bag(in_bag); + in_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(shape))); + + auto factor_bag = module->entity()->bag()->create(num_elements(shape)); + auto factor_obj = module->entity()->object()->create(); + + factor_obj->bag(factor_bag); + factor_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(shape))); + + auto out_bag = module->entity()->bag()->create(num_elements(shape)); + auto out_obj = module->entity()->object()->create(); + + out_obj->bag(out_bag); + out_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(shape))); + + auto mul_op = op_builder(module).load(factor_obj).load(in_obj).mul().pop(); + auto mul_ins = instr_builder(module).eval(out_obj, mul_op); + + blk->instr()->append(mul_ins); + + // Fill "factor" data + { + data->f32()->allocate(factor_bag); + + auto span = data->f32()->weight(factor_bag); + auto blob = weight_ctx.blob_get(layer.name(), 0); + + for (uint32_t ch = 0; ch < factor_obj->shape().depth(); ++ch) + { + span[ch] = blob->data(ch); + } + } + + // Update "last_bag" + last_bag = out_bag; + } + + assert(last_bag != nullptr); + + // Create bias addition (as channel-wise addition) + if (param.bias_term()) + { + assert(weight_ctx.blob_count(layer.name()) >= 2); + + auto in_bag = last_bag; /* Use the output of the last computation as an input */ + auto in_obj = module->entity()->object()->create(); + + in_obj->bag(in_bag); + in_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(shape))); + + auto bias_bag = module->entity()->bag()->create(num_elements(shape)); + auto bias_obj = module->entity()->object()->create(); + + bias_obj->bag(bias_bag); + bias_obj->layout(coco::FeatureLayouts::BC::create(as_feature_shape(shape))); + + auto out_bag = module->entity()->bag()->create(num_elements(shape)); + auto out_obj = module->entity()->object()->create(); + + out_obj->bag(out_bag); + out_obj->layout(coco::FeatureLayouts::BCHW::create(as_feature_shape(shape))); + + auto add_op = op_builder(module).load(bias_obj).load(in_obj).add().pop(); + auto add_ins = instr_builder(module).eval(out_obj, add_op); + + blk->instr()->append(add_ins); + + // Fill bias data + { + data->f32()->allocate(bias_bag); + + auto bias_span = data->f32()->weight(bias_bag); + auto bias_blob = weight_ctx.blob_get(layer.name(), 1); + + for (uint32_t ch = 0; ch < bias_obj->shape().depth(); ++ch) + { + bias_span[ch] = bias_blob->data(ch); + } + } + + // Update "last_bag" + last_bag = out_bag; + } + + // Update bag and shape context + { + const auto &out_name = layer.top(0); + const auto &out_bag = last_bag; + const auto &out_shape = shape; + + bag_ctx[out_name] = out_bag; + shape_ctx[out_name] = out_shape; + } +} + +} // namespace caffeimport diff --git a/compiler/enco/frontend/caffe/src/Layer/Scale.h b/compiler/enco/frontend/caffe/src/Layer/Scale.h new file mode 100644 index 000000000..491cc31cf --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Layer/Scale.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SCALE_BUILDER_H__ +#define __SCALE_BUILDER_H__ + +#include "GraphBuilder.h" + +#include "Context.h" + +namespace caffeimport +{ + +class ScaleBuilder final : public GraphBuilder +{ +public: + void build(const ::caffe::LayerParameter &layer, GraphBuilderContext *context) const override; +}; + +} // namespace caffeimport + +#endif // __SCALE_BUILDER_H__ diff --git a/compiler/enco/frontend/caffe/src/Padding.h b/compiler/enco/frontend/caffe/src/Padding.h new file mode 100644 index 000000000..98b018117 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Padding.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Padding.h + * @brief This file declares padding-related data structures. + */ +#ifndef __PADDING_H__ +#define __PADDING_H__ + +#include +#include + +/** + * @brief A PaddingBase encapsulates common implementation for derived Padding classes + */ +template class PaddingBase +{ +public: + virtual ~PaddingBase() = default; + +public: + uint32_t count(void) const { return _values.size(); } + +public: + uint32_t &value(uint32_t n) { return _values.at(n); } + const uint32_t &value(uint32_t n) const { return _values.at(n); } + +public: + void resize(uint32_t len) { return _values.resize(len); } + +private: + std::vector _values; +}; + +/** + * @brief A RawPadding denotes padding values stored in Caffe model + * + * @note There may be a mismatch between the number of values in RawPadding and spatial rank + */ +struct RawPadding final : public PaddingBase +{ + // Empty +}; + +/** + * @brief A SpatialPadding denotes padding values for each "spatial" dimension + * + * @note The number of values in SpatialPadding should be matched with spatial rank + */ +struct SpatialPadding final : public PaddingBase +{ + // Empty +}; + +#endif // __PADDING_H__ diff --git a/compiler/enco/frontend/caffe/src/Padding.test.cpp b/compiler/enco/frontend/caffe/src/Padding.test.cpp new file mode 100644 index 000000000..cb2495d06 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/Padding.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Padding.h" + +#include + +namespace +{ + +struct DerivedPadding : PaddingBase +{ + // Empty +}; + +} // namespace + +TEST(PaddingTest, PaddingBase) +{ + DerivedPadding pad; + + ASSERT_EQ(pad.count(), 0); + + pad.resize(2); + + ASSERT_EQ(pad.count(), 2); + ASSERT_EQ(pad.value(0), 0); + ASSERT_EQ(pad.value(1), 0); + + pad.value(1) = 4; + + ASSERT_EQ(pad.count(), 2); + ASSERT_EQ(pad.value(0), 0); + ASSERT_EQ(pad.value(1), 4); +} diff --git a/compiler/enco/frontend/caffe/src/PaddingUtils.cpp b/compiler/enco/frontend/caffe/src/PaddingUtils.cpp new file mode 100644 index 000000000..ffb4bfbfd --- /dev/null +++ b/compiler/enco/frontend/caffe/src/PaddingUtils.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PaddingUtils.h" + +#include + +// +// Section: Raw Padding Builder +// +RawPadding RawPaddingBuilder::with(const ::caffe::ConvolutionParameter ¶m) const +{ + RawPadding res; + + if (param.has_pad_h() || param.has_pad_w()) + { + assert(param.pad().size() == 0); + assert(param.has_pad_h() && param.has_pad_w()); + + res.resize(2); + res.value(0) = param.pad_h(); + res.value(1) = param.pad_w(); + } + else + { + // NOTE pad and pad_h/pad_w cannot be specified at the same time + // + // Reference: BaseConvolutionLayer::LayerSetUp in base_conv_layer.cpp + assert(!param.has_pad_h() && !param.has_pad_w()); + + uint32_t rank = param.pad().size(); + + res.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.value(axis) = param.pad(axis); + } + } + + return res; +} + +RawPadding RawPaddingBuilder::with(const ::caffe::PoolingParameter ¶m) const +{ + RawPadding res; + + if (param.has_pad_h() || param.has_pad_w()) + { + assert(!param.has_pad()); + assert(param.has_pad_h() && param.has_pad_w()); + + res.resize(2); + res.value(0) = param.pad_h(); + res.value(1) = param.pad_w(); + } + else + { + // NOTE pad and pad_h/pad_w cannot be specified at the same time + // + // Reference: PoolingLayer::LayerSetUp in pooling_layer.cpp + assert(!param.has_pad_h() && !param.has_pad_w()); + + if (param.has_pad()) + { + res.resize(1); + res.value(0) = param.pad(); + } + } + + return res; +} + +RawPaddingBuilder build_raw_padding(void) { return RawPaddingBuilder{}; } + +// +// Section: Spatial Padding Builder +// +SpatialPadding SpatialPaddingBuilder::with(const RawPadding &raw) const +{ + const auto spatial_rank = _spatial_rank; + + SpatialPadding res; + + res.resize(spatial_rank); + + if (raw.count() == 0) + { + // NOTE default padding is 0 + for (uint32_t spatial_axis = 0; spatial_axis < spatial_rank; ++spatial_axis) + { + res.value(spatial_axis) = 0; + } + } + else if (raw.count() == 1) + { + // NOTE One-for-all scheme + for (uint32_t spatial_axis = 0; spatial_axis < spatial_rank; ++spatial_axis) + { + res.value(spatial_axis) = raw.value(0); + } + } + else + { + // NOTE One-to-one scheme + assert(raw.count() == spatial_rank); + for (uint32_t spatial_axis = 0; spatial_axis < spatial_rank; ++spatial_axis) + { + res.value(spatial_axis) = raw.value(spatial_axis); + } + } + + return res; +} + +SpatialPaddingBuilder build_spatial_padding(uint32_t spatial_rank) +{ + return SpatialPaddingBuilder{spatial_rank}; +} diff --git a/compiler/enco/frontend/caffe/src/PaddingUtils.h b/compiler/enco/frontend/caffe/src/PaddingUtils.h new file mode 100644 index 000000000..81f32aaa8 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/PaddingUtils.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PADDING_UTILS_H__ +#define __PADDING_UTILS_H__ + +#include "Padding.h" + +#include + +/** + * @brief Construct a raw padding from each Layer parameter + * + * @note This class is an auxiliary class for build_raw_padding function below + */ +class RawPaddingBuilder +{ +public: + friend RawPaddingBuilder build_raw_padding(void); + +private: + RawPaddingBuilder() = default; + +public: + RawPadding with(const ::caffe::ConvolutionParameter &) const; + RawPadding with(const ::caffe::PoolingParameter &) const; +}; + +/** + * RawPaddingBuilder is introduced to support the following code pattern: + * + * auto raw_padding = build_raw_padding().with(conv_param); + * ... + */ +RawPaddingBuilder build_raw_padding(void); + +/** + * @brief Convert a raw padding to a spatial padding of a given spatial rank + * + * @note This class is an auxiliary class for build_raw_padding function below + */ +class SpatialPaddingBuilder +{ +public: + friend SpatialPaddingBuilder build_spatial_padding(uint32_t spatial_rank); + +private: + SpatialPaddingBuilder(uint32_t spatial_rank) : _spatial_rank{spatial_rank} + { + // DO NOTHING + } + +public: + SpatialPadding with(const RawPadding &raw) const; + +private: + uint32_t _spatial_rank = 0; +}; + +/** + * SpatialPaddingBuilder is introduced to support the following code pattern: + * + * auto raw_padding = build_raw_padding().with(conv_param); + * auto spatial_padding = build_spatial_padding(4).with(raw_padding); + */ +SpatialPaddingBuilder build_spatial_padding(uint32_t spatial_rank); + +#endif // __PADDING_UTILS_H__ diff --git a/compiler/enco/frontend/caffe/src/PoolingSpec.cpp b/compiler/enco/frontend/caffe/src/PoolingSpec.cpp new file mode 100644 index 000000000..36216a2da --- /dev/null +++ b/compiler/enco/frontend/caffe/src/PoolingSpec.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PoolingSpec.h" +#include "PaddingUtils.h" + +#include +#include + +PoolingSpec::PoolingSpec(const ::caffe::PoolingParameter ¶m) : _param(param) +{ + // DO NOTHING +} + +PoolingMethod PoolingSpec::method(void) const +{ + if (!_param.has_pool()) + { + // Default pooling method is MAX + // Reference: http://caffe.berkeleyvision.org/tutorial/layers/pooling.html + return PoolingMethod::Max; + } + + std::map<::caffe::PoolingParameter_PoolMethod, PoolingMethod> methods; + + // NOTE STOCHASTIC Pooling is not supported, yet + // TODO Support STOCHASTIC Pooling + methods[::caffe::PoolingParameter_PoolMethod_MAX] = PoolingMethod::Max; + methods[::caffe::PoolingParameter_PoolMethod_AVE] = PoolingMethod::Avg; + + assert(_param.has_pool()); + return methods.at(_param.pool()); +} + +uint32_t PoolingSpec::window_height(void) const +{ + // NOTE Global pooling is not supported, yet + // TODO Support global pooling + assert(!_param.global_pooling()); + + if (_param.has_kernel_h()) + { + return _param.kernel_h(); + } + + assert(_param.has_kernel_size()); + return _param.kernel_size(); +} + +uint32_t PoolingSpec::window_width(void) const +{ + // NOTE Global pooling is not supported, yet + // TODO Support global pooling + assert(!_param.global_pooling()); + + if (_param.has_kernel_w()) + { + return _param.kernel_w(); + } + + assert(_param.has_kernel_size()); + return _param.kernel_size(); +} + +uint32_t PoolingSpec::vertical_pad(void) const +{ + // NOTE The input of Pooling SHOULD BE a rank-4 tensor. + // Reference: PoolingLayer::Reshape in pooling_layer.cpp + auto raw_padding = build_raw_padding().with(_param); + auto spatial_padding = build_spatial_padding(2 /* SPATIAL RANK */).with(raw_padding); + return spatial_padding.value(0 /* H */); +} + +uint32_t PoolingSpec::horizontal_pad(void) const +{ + // NOTE The input of Pooling SHOULD BE a rank-4 tensor. + // Reference: PoolingLayer::Reshape in pooling_layer.cpp + auto raw_padding = build_raw_padding().with(_param); + auto spatial_padding = build_spatial_padding(2 /* SPATIAL RANK */).with(raw_padding); + return spatial_padding.value(1 /* W */); +} + +uint32_t PoolingSpec::vertical_stride(void) const +{ + if (_param.has_stride_h()) + { + return _param.stride_h(); + } + + if (_param.has_stride()) + { + return _param.stride(); + } + + return 1; +} + +uint32_t PoolingSpec::horizontal_stride(void) const +{ + if (_param.has_stride_w()) + { + return _param.stride_w(); + } + + if (_param.has_stride()) + { + return _param.stride(); + } + + return 1; +} + +nncc::core::ADT::tensor::Shape PoolingSpec::ofm_shape(void) const +{ + nncc::core::ADT::tensor::Shape res; + + // NOTE Caffe supports only pooling over rank-4 tensor + assert(_ifm_shape.rank() == 4); + res.resize(4); + + // N (= the number of bacths) SHOULD be same + res.dim(0) = _ifm_shape.dim(0); + // C (= the number of chaanels) SHOULD be same + res.dim(1) = _ifm_shape.dim(1); + + // H and W are derived from IFM, Window, and Padding + const auto effective_input_height = _ifm_shape.dim(2) + 2 * vertical_pad() - window_height(); + const auto effective_input_width = _ifm_shape.dim(3) + 2 * horizontal_pad() - window_width(); + // TODO Remove the following asserts + assert(effective_input_height % vertical_stride() == 0); + assert(effective_input_width % horizontal_stride() == 0); + res.dim(2) = effective_input_height / vertical_stride() + 1; + res.dim(3) = effective_input_width / horizontal_stride() + 1; + return res; +} diff --git a/compiler/enco/frontend/caffe/src/PoolingSpec.h b/compiler/enco/frontend/caffe/src/PoolingSpec.h new file mode 100644 index 000000000..655a773ba --- /dev/null +++ b/compiler/enco/frontend/caffe/src/PoolingSpec.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __POOLING_SPEC_H__ +#define __POOLING_SPEC_H__ + +#include + +#include + +enum class PoolingMethod +{ + Max, + Avg +}; + +class PoolingSpec +{ +public: + PoolingSpec(const ::caffe::PoolingParameter ¶m); + +public: + const nncc::core::ADT::tensor::Shape &ifm_shape(void) const { return _ifm_shape; } + void ifm_shape(const nncc::core::ADT::tensor::Shape &shape) { _ifm_shape = shape; } + +public: + PoolingMethod method(void) const; + +public: + uint32_t window_height(void) const; + uint32_t window_width(void) const; + +public: + uint32_t vertical_pad(void) const; + uint32_t horizontal_pad(void) const; + +public: + uint32_t vertical_stride(void) const; + uint32_t horizontal_stride(void) const; + +public: + nncc::core::ADT::tensor::Shape ofm_shape(void) const; + +private: + const ::caffe::PoolingParameter &_param; + nncc::core::ADT::tensor::Shape _ifm_shape; +}; + +#endif // __POOLING_SPEC_H__ diff --git a/compiler/enco/frontend/caffe/src/PoolingSpec.test.cpp b/compiler/enco/frontend/caffe/src/PoolingSpec.test.cpp new file mode 100644 index 000000000..26bcaa09b --- /dev/null +++ b/compiler/enco/frontend/caffe/src/PoolingSpec.test.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PoolingSpec.h" +#include "Importer.h" + +#include + +#include + +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; + +#define STRING(content) #content + +bool from_txt(const std::string &txt, ::caffe::PoolingParameter &out) +{ + std::stringstream ss{txt}; + return from_txt(ss, out); +} + +namespace +{ + +class SequentialBuilder +{ +public: + SequentialBuilder(::caffe::NetParameter *net) : _net{net} + { + // DO NOTHING + } + +public: + bool addLayer(const std::string &prototxt) + { + auto layer = _net->add_layer(); + std::stringstream ss{prototxt}; + ::google::protobuf::io::IstreamInputStream iis{&ss}; + return google::protobuf::TextFormat::Parse(&iis, layer); + } + + bool addInputLayer(const tensor::Shape &shape) + { + auto param = new ::caffe::InputParameter; + { + auto s = param->add_shape(); + for (uint32_t n = 0; n < shape.rank(); ++n) + { + s->add_dim(shape.dim(n)); + } + } + + auto layer = _net->add_layer(); + + layer->set_name("data"); + layer->set_type("Input"); + layer->add_top("data"); + layer->set_allocated_input_param(param); + + return true; + } + +private: + ::caffe::NetParameter *_net; +}; + +} // namespace + +namespace +{ + +class PoolingSpecTest : public ::testing::Test +{ +protected: + tensor::Shape as_tensor_shape(const std::vector &dims) + { + const uint32_t rank = dims.size(); + + tensor::Shape res; + + res.resize(rank); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = dims.at(axis); + } + + return res; + } +}; +} // namespace + +TEST_F(PoolingSpecTest, ifm_shape) +{ + ::caffe::PoolingParameter param; + PoolingSpec spec{param}; + + const tensor::Shape ifm_shape{1, 3, 244, 244}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.ifm_shape(), ifm_shape); +} + +namespace +{ +} // namespace + +TEST_F(PoolingSpecTest, kernel_size_same_for_all) +{ + const tensor::Shape ifm_shape{1, 3, 16, 16}; + + ::caffe::NetParameter param; + { + SequentialBuilder builder{¶m}; + + builder.addInputLayer(ifm_shape); + + // clang-format off + const char *prototxt = STRING( + name : "pool" + type : "Pooling" + bottom : "data" + top : "pool" + pooling_param { kernel_size : 3 } + ); + // clang-format on + + builder.addLayer(prototxt); + } + + ::caffe::Net net{param}; + + PoolingSpec spec{param.layer(1).pooling_param()}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.window_height(), 3); + ASSERT_EQ(spec.window_width(), 3); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("pool")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +TEST_F(PoolingSpecTest, pad_for_all) +{ + const tensor::Shape ifm_shape{1, 3, 15, 15}; + + ::caffe::NetParameter param; + { + SequentialBuilder builder{¶m}; + + builder.addInputLayer(ifm_shape); + + // clang-format off + const char *prototxt = STRING( + name : "pool" + type : "Pooling" + bottom : "data" + top : "pool" + pooling_param { + pool: MAX + kernel_size : 3 + pad: 2 + } + ); + // clang-format on + + builder.addLayer(prototxt); + } + + ::caffe::Net net{param}; + + PoolingSpec spec{param.layer(1).pooling_param()}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.vertical_pad(), 2); + ASSERT_EQ(spec.horizontal_pad(), 2); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("pool")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +TEST_F(PoolingSpecTest, stride_for_all) +{ + const tensor::Shape ifm_shape{1, 3, 15, 15}; + + ::caffe::NetParameter param; + { + SequentialBuilder builder{¶m}; + + builder.addInputLayer(ifm_shape); + + // clang-format off + const char *prototxt = STRING( + name : "pool" + type : "Pooling" + bottom : "data" + top : "pool" + pooling_param { + pool: MAX + kernel_size : 3 + stride: 2 + } + ); + // clang-format on + + builder.addLayer(prototxt); + } + + ::caffe::Net net{param}; + + PoolingSpec spec{param.layer(1).pooling_param()}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.vertical_stride(), 2); + ASSERT_EQ(spec.horizontal_stride(), 2); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("pool")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +} + +TEST_F(PoolingSpecTest, method_none) +{ + const char *prototxt = ""; + + ::caffe::PoolingParameter param; + from_txt(prototxt, param); + + PoolingSpec spec{param}; + + ASSERT_EQ(spec.method(), PoolingMethod::Max); +} + +TEST_F(PoolingSpecTest, method_max) +{ + const char *prototxt = "pool: MAX"; + + ::caffe::PoolingParameter param; + from_txt(prototxt, param); + + PoolingSpec spec{param}; + + ASSERT_EQ(spec.method(), PoolingMethod::Max); +} + +TEST_F(PoolingSpecTest, method_avg) +{ + const char *prototxt = "pool: AVE"; + + ::caffe::PoolingParameter param; + from_txt(prototxt, param); + + PoolingSpec spec{param}; + + ASSERT_EQ(spec.method(), PoolingMethod::Avg); +} diff --git a/compiler/enco/frontend/caffe/src/ShapeQuery.cpp b/compiler/enco/frontend/caffe/src/ShapeQuery.cpp new file mode 100644 index 000000000..1166453b6 --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ShapeQuery.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShapeQuery.h" + +#include + +// +// AxisSpecifier +// +AxisSpecifier axis_specifier(int32_t value) { return AxisSpecifier{value}; } + +// +// ShapeQuery +// +uint32_t ShapeQuery::axis(const AxisSpecifier &specifier) const +{ + if (specifier.value() > 0) + { + return static_cast(specifier.value()); + } + + assert(_shape->rank() >= static_cast(-specifier.value())); + return static_cast(_shape->rank() + specifier.value()); +} + +ShapeQuery query_on(const nncc::core::ADT::tensor::Shape &shape) { return ShapeQuery{&shape}; } diff --git a/compiler/enco/frontend/caffe/src/ShapeQuery.h b/compiler/enco/frontend/caffe/src/ShapeQuery.h new file mode 100644 index 000000000..260b6ad4d --- /dev/null +++ b/compiler/enco/frontend/caffe/src/ShapeQuery.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SHAPE_QUERY_H__ +#define __SHAPE_QUERY_H__ + +#include + +/** + * @brief A wrapper class for an integer number that specifies axis + * + * Several Caffe layers includes 'axis' parameter (which may be negative) which specifies + * some axis required for operation. + * + * Here are several examples: + * - Convolution layer uses 'axis' parameter to specify "channel" axis + * (http://caffe.berkeleyvision.org/tutorial/layers/convolution.html) + * - Concat layer uses 'axis' parameter to specify axis to be concatenated + * (http://caffe.berkeleyvision.org/tutorial/layers/concat.html) + * + * AxisSpecifier class is introduced to distinguish this 'axis' parameter from other integers + * (to prevent possible mistake). + */ +class AxisSpecifier +{ +public: + explicit AxisSpecifier(int32_t value) : _value{value} + { + // DO NOTHING + } + +public: + int32_t value(void) const { return _value; } + +private: + int32_t _value = 1; +}; + +AxisSpecifier axis_specifier(int32_t value); + +/** + * @brief A wrapper class that allows additional queries over tensor shape. + */ +class ShapeQuery +{ +public: + explicit ShapeQuery(const nncc::core::ADT::tensor::Shape *shape) : _shape{shape} + { + // DO NOTHING + } + +public: + /// @brief Return the dimension number (axis) specified by a given axis specifier + uint32_t axis(const AxisSpecifier &) const; + +private: + const nncc::core::ADT::tensor::Shape *_shape; +}; + +ShapeQuery query_on(const nncc::core::ADT::tensor::Shape &); + +#endif // __SHAPE_QUERY_H__ diff --git a/compiler/enco/frontend/tflite/CMakeLists.txt b/compiler/enco/frontend/tflite/CMakeLists.txt new file mode 100644 index 000000000..77159879e --- /dev/null +++ b/compiler/enco/frontend/tflite/CMakeLists.txt @@ -0,0 +1,36 @@ +nnas_find_package(FlatBuffers QUIET) + +if(NOT FlatBuffers_FOUND) + return() +endif(NOT FlatBuffers_FOUND) + +FlatBuffers_Target(enco_tflite_schema + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" + SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema" + SCHEMA_FILES schema.fbs) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(enco_tflite_frontend SHARED ${SOURCES}) +target_include_directories(enco_tflite_frontend PRIVATE src) +target_link_libraries(enco_tflite_frontend enco_intf_frontend) +target_link_libraries(enco_tflite_frontend enco_intf_cmdline) +target_link_libraries(enco_tflite_frontend flatbuffers) +target_link_libraries(enco_tflite_frontend enco_tflite_schema) +target_link_libraries(enco_tflite_frontend stdex) +target_link_libraries(enco_tflite_frontend morph) +target_link_libraries(enco_tflite_frontend cwrap) + +nnas_find_package(GTest QUIET) + +if(NOT GTest_FOUND) + return() +endif(NOT GTest_FOUND) + +add_executable(enco_tflite_frontend_test ${TESTS}) +target_include_directories(enco_tflite_frontend_test PRIVATE src) +target_link_libraries(enco_tflite_frontend_test gtest_main) +target_link_libraries(enco_tflite_frontend_test enco_tflite_frontend) +add_test(enco_tflite_frontend_test enco_tflite_frontend_test) diff --git a/compiler/enco/frontend/tflite/schema/schema.fbs b/compiler/enco/frontend/tflite/schema/schema.fbs new file mode 100644 index 000000000..3045351f2 --- /dev/null +++ b/compiler/enco/frontend/tflite/schema/schema.fbs @@ -0,0 +1,734 @@ +// Copyright 2017 The TensorFlow Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Revision History +// Version 0: Initial version. +// Version 1: Add subgraphs to schema. +// Version 2: Rename operators to conform to NN API. +// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers. + +namespace tflite; + +// This corresponds to the version. +file_identifier "TFL3"; +// File extension of any written files. +file_extension "tflite"; + +// IMPORTANT: All new members of tables, enums and unions must be added at the +// end to ensure backwards compatibility. + +// The type of data stored in a tensor. +enum TensorType : byte { + FLOAT32 = 0, + FLOAT16 = 1, + INT32 = 2, + UINT8 = 3, + INT64 = 4, + STRING = 5, + BOOL = 6, + INT16 = 7, + COMPLEX64 = 8, +} + +// Parameters for converting a quantized tensor back to float. Given a +// quantized value q, the corresponding float value f should be: +// f = scale * (q - zero_point) +table QuantizationParameters { + min:[float]; // For importing back into tensorflow. + max:[float]; // For importing back into tensorflow. + scale:[float]; // For dequantizing the tensor's values. + zero_point:[long]; +} + +table Tensor { + // The tensor shape. The meaning of each entry is operator-specific but + // builtin ops use: [batch size, height, width, number of channels] (That's + // Tensorflow's NHWC). + shape:[int]; + type:TensorType; + // An index that refers to the buffers table at the root of the model. Or, + // if there is no data buffer associated (i.e. intermediate results), then + // this is 0 (which refers to an always existent empty buffer). + // + // The data_buffer itself is an opaque container, with the assumption that the + // target device is little-endian. In addition, all builtin operators assume + // the memory is ordered such that if `shape` is [4, 3, 2], then index + // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k]. + buffer:uint; + name:string; // For debugging and importing back into tensorflow. + quantization:QuantizationParameters; // Optional. + + is_variable:bool = false; +} + +// A list of builtin operators. Builtin operators are slightly faster than custom +// ones, but not by much. Moreover, while custom operators accept an opaque +// object containing configuration parameters, builtins have a predetermined +// set of acceptable options. +enum BuiltinOperator : byte { + ADD = 0, + AVERAGE_POOL_2D = 1, + CONCATENATION = 2, + CONV_2D = 3, + DEPTHWISE_CONV_2D = 4, + // DEPTH_TO_SPACE = 5, + DEQUANTIZE = 6, + EMBEDDING_LOOKUP = 7, + FLOOR = 8, + FULLY_CONNECTED = 9, + HASHTABLE_LOOKUP = 10, + L2_NORMALIZATION = 11, + L2_POOL_2D = 12, + LOCAL_RESPONSE_NORMALIZATION = 13, + LOGISTIC = 14, + LSH_PROJECTION = 15, + LSTM = 16, + MAX_POOL_2D = 17, + MUL = 18, + RELU = 19, + // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed + // since different model developers use RELU1 in different ways. Never + // create another op called RELU1. + RELU_N1_TO_1 = 20, + RELU6 = 21, + RESHAPE = 22, + RESIZE_BILINEAR = 23, + RNN = 24, + SOFTMAX = 25, + SPACE_TO_DEPTH = 26, + SVDF = 27, + TANH = 28, + // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS + CONCAT_EMBEDDINGS = 29, + SKIP_GRAM = 30, + CALL = 31, + CUSTOM = 32, + EMBEDDING_LOOKUP_SPARSE = 33, + PAD = 34, + UNIDIRECTIONAL_SEQUENCE_RNN = 35, + GATHER = 36, + BATCH_TO_SPACE_ND = 37, + SPACE_TO_BATCH_ND = 38, + TRANSPOSE = 39, + MEAN = 40, + SUB = 41, + DIV = 42, + SQUEEZE = 43, + UNIDIRECTIONAL_SEQUENCE_LSTM = 44, + STRIDED_SLICE = 45, + BIDIRECTIONAL_SEQUENCE_RNN = 46, + EXP = 47, + TOPK_V2 = 48, + SPLIT = 49, + LOG_SOFTMAX = 50, + // DELEGATE is a special op type for the operations which are delegated to + // other backends. + // WARNING: Experimental interface, subject to change + DELEGATE = 51, + BIDIRECTIONAL_SEQUENCE_LSTM = 52, + CAST = 53, + PRELU = 54, + MAXIMUM = 55, + ARG_MAX = 56, + MINIMUM = 57, + LESS = 58, + NEG = 59, + PADV2 = 60, + GREATER = 61, + GREATER_EQUAL = 62, + LESS_EQUAL = 63, + SELECT = 64, + SLICE = 65, + SIN = 66, + TRANSPOSE_CONV = 67, + SPARSE_TO_DENSE = 68, + TILE = 69, + EXPAND_DIMS = 70, + EQUAL = 71, + NOT_EQUAL = 72, + LOG = 73, + SUM = 74, + SQRT = 75, + RSQRT = 76, + SHAPE = 77, + POW = 78, + ARG_MIN = 79, + FAKE_QUANT = 80, + REDUCE_PROD = 81, + REDUCE_MAX = 82, + PACK = 83, + LOGICAL_OR = 84, + ONE_HOT = 85, + LOGICAL_AND = 86, + LOGICAL_NOT = 87, + UNPACK = 88, + REDUCE_MIN = 89, + FLOOR_DIV = 90, + REDUCE_ANY = 91, + SQUARE = 92, + ZEROS_LIKE = 93, + FILL = 94, + FLOOR_MOD = 95, + RANGE = 96, +} + +// Options for the builtin operators. +union BuiltinOptions { + Conv2DOptions, + DepthwiseConv2DOptions, + ConcatEmbeddingsOptions, + LSHProjectionOptions, + Pool2DOptions, + SVDFOptions, + RNNOptions, + FullyConnectedOptions, + SoftmaxOptions, + ConcatenationOptions, + AddOptions, + L2NormOptions, + LocalResponseNormalizationOptions, + LSTMOptions, + ResizeBilinearOptions, + CallOptions, + ReshapeOptions, + SkipGramOptions, + SpaceToDepthOptions, + EmbeddingLookupSparseOptions, + MulOptions, + PadOptions, + GatherOptions, + BatchToSpaceNDOptions, + SpaceToBatchNDOptions, + TransposeOptions, + ReducerOptions, + SubOptions, + DivOptions, + SqueezeOptions, + SequenceRNNOptions, + StridedSliceOptions, + ExpOptions, + TopKV2Options, + SplitOptions, + LogSoftmaxOptions, + CastOptions, + DequantizeOptions, + MaximumMinimumOptions, + ArgMaxOptions, + LessOptions, + NegOptions, + PadV2Options, + GreaterOptions, + GreaterEqualOptions, + LessEqualOptions, + SelectOptions, + SliceOptions, + TransposeConvOptions, + SparseToDenseOptions, + TileOptions, + ExpandDimsOptions, + EqualOptions, + NotEqualOptions, + ShapeOptions, + PowOptions, + ArgMinOptions, + FakeQuantOptions, + PackOptions, + LogicalOrOptions, + OneHotOptions, + LogicalAndOptions, + LogicalNotOptions, + UnpackOptions, + FloorDivOptions, + SquareOptions, + ZerosLikeOptions, + FillOptions, + BidirectionalSequenceLSTMOptions, + BidirectionalSequenceRNNOptions, + UnidirectionalSequenceLSTMOptions, + FloorModOptions, + RangeOptions, +} + +enum Padding : byte { SAME, VALID } + +enum ActivationFunctionType : byte { + NONE = 0, + RELU = 1, + RELU_N1_TO_1 = 2, + RELU6 = 3, + TANH = 4, + SIGN_BIT = 5, +} + +table Conv2DOptions { + padding:Padding; + stride_w:int; + stride_h:int; + fused_activation_function:ActivationFunctionType; + dilation_w_factor:int = 1; + dilation_h_factor:int = 1; +} + +table Pool2DOptions { + padding:Padding; + stride_w:int; + stride_h:int; + filter_width:int; + filter_height:int; + fused_activation_function:ActivationFunctionType; +} + +table DepthwiseConv2DOptions { + // Parameters for DepthwiseConv version 1 or above. + padding:Padding; + stride_w:int; + stride_h:int; + depth_multiplier:int; + fused_activation_function:ActivationFunctionType; + // Parameters for DepthwiseConv version 2 or above. + dilation_w_factor:int = 1; + dilation_h_factor:int = 1; +} + +table ConcatEmbeddingsOptions { + num_channels:int; + num_columns_per_channel:[int]; + embedding_dim_per_channel:[int]; // This could be inferred from parameters. +} + +enum LSHProjectionType: byte { + UNKNOWN = 0, + SPARSE = 1, + DENSE = 2, +} + +table LSHProjectionOptions { + type: LSHProjectionType; +} + +table SVDFOptions { + rank:int; + fused_activation_function:ActivationFunctionType; +} + +// An implementation of TensorFlow RNNCell. +table RNNOptions { + fused_activation_function:ActivationFunctionType; +} + +// An implementation of TensorFlow dynamic_rnn with RNNCell. +table SequenceRNNOptions { + time_major:bool; + fused_activation_function:ActivationFunctionType; +} + +// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell. +table BidirectionalSequenceRNNOptions { + time_major:bool; + fused_activation_function:ActivationFunctionType; + merge_outputs: bool; +} + +enum FullyConnectedOptionsWeightsFormat: byte { + DEFAULT = 0, + SHUFFLED4x16INT8 = 1, +} + +// An implementation of TensorFlow fully_connected (a.k.a Dense) layer. +table FullyConnectedOptions { + // Parameters for FullyConnected version 1 or above. + fused_activation_function:ActivationFunctionType; + + // Parameters for FullyConnected version 2 or above. + weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT; +} + +table SoftmaxOptions { + beta: float; +} + +// An implementation of TensorFlow concat. +table ConcatenationOptions { + axis:int; + fused_activation_function:ActivationFunctionType; +} + +table AddOptions { + fused_activation_function:ActivationFunctionType; +} + +table MulOptions { + fused_activation_function:ActivationFunctionType; +} + +table L2NormOptions { + fused_activation_function:ActivationFunctionType; +} + +table LocalResponseNormalizationOptions { + radius:int; + bias:float; + alpha:float; + beta:float; +} + +enum LSTMKernelType : byte { + // Full LSTM kernel which supports peephole and projection. + FULL = 0, + // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell. + BASIC = 1, +} + +// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell +table LSTMOptions { + // Parameters for LSTM version 1 or above. + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // Parameters for LSTM version 2 or above. + // Basic kernel is only supported in version 2 or above. + kernel_type: LSTMKernelType = FULL; +} + +// An implementation of TensorFlow dynamic_rnn with LSTMCell. +table UnidirectionalSequenceLSTMOptions { + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // If true then first dimension is sequence, otherwise batch. + time_major:bool; +} + +table BidirectionalSequenceLSTMOptions { + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // If true, store the outputs of both directions into the first output. + merge_outputs: bool; +} + +table ResizeBilinearOptions { + new_height: int (deprecated); + new_width: int (deprecated); + align_corners: bool; +} + +// A call operation options +table CallOptions { + // The subgraph index that needs to be called. + subgraph:uint; +} + +table PadOptions { +} + +table PadV2Options { +} + +table ReshapeOptions { + new_shape:[int]; +} + +table SpaceToBatchNDOptions { +} + +table BatchToSpaceNDOptions { +} + +table SkipGramOptions { + ngram_size: int; + max_skip_size: int; + include_all_ngrams: bool; +} + +table SpaceToDepthOptions { + block_size: int; +} + +table SubOptions { + fused_activation_function:ActivationFunctionType; +} + +table DivOptions { + fused_activation_function:ActivationFunctionType; +} + +table TopKV2Options { +} + +enum CombinerType : byte { + SUM = 0, + MEAN = 1, + SQRTN = 2, +} + +table EmbeddingLookupSparseOptions { + combiner:CombinerType; +} + +table GatherOptions { + axis: int; +} + +table TransposeOptions { +} + +table ExpOptions { +} + +table ReducerOptions { + keep_dims: bool; +} + +table SqueezeOptions { + squeeze_dims:[int]; +} + +table SplitOptions { + num_splits: int; +} + +table StridedSliceOptions { + begin_mask: int; + end_mask: int; + ellipsis_mask: int; + new_axis_mask: int; + shrink_axis_mask: int; +} + +table LogSoftmaxOptions { +} + +table CastOptions { + in_data_type: TensorType; + out_data_type: TensorType; +} + +table DequantizeOptions { +} + +table MaximumMinimumOptions { +} + +table TileOptions { +} + +table ArgMaxOptions { + output_type : TensorType; +} + +table ArgMinOptions { + output_type : TensorType; +} + +table GreaterOptions { +} + +table GreaterEqualOptions { +} + +table LessOptions { +} + +table LessEqualOptions { +} + +table NegOptions { +} + +table SelectOptions { +} + +table SliceOptions { +} + +table TransposeConvOptions { + padding:Padding; + stride_w:int; + stride_h:int; +} + +table ExpandDimsOptions { +} + +table SparseToDenseOptions { + validate_indices:bool; +} + +table EqualOptions { +} + +table NotEqualOptions { +} + +table ShapeOptions { + // Optional output type of the operation (int32 or int64). Defaults to int32. + out_type : TensorType; +} + +table PowOptions { +} + +table FakeQuantOptions { + // Parameters supported by version 1: + min:float; + max:float; + num_bits:int; + + // Parameters supported by version 2: + narrow_range:bool; +} + +table PackOptions { + values_count:int; + axis:int; +} + +table LogicalOrOptions { +} + +table OneHotOptions { + axis:int; +} + +table LogicalAndOptions { +} + +table LogicalNotOptions { +} + +table UnpackOptions { + num:int; + axis:int; +} + +table FloorDivOptions { +} + +table SquareOptions { +} + +table ZerosLikeOptions { +} + +table FillOptions { +} + +table FloorModOptions { +} + +table RangeOptions { +} + +// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a +// builtin, or a string if the operator is custom. +table OperatorCode { + builtin_code:BuiltinOperator; + custom_code:string; + + // The version of the operator. The version need to be bumped whenever new + // parameters are introduced into an op. + version:int = 1; +} + +enum CustomOptionsFormat : byte { + FLEXBUFFERS = 0, +} + +// An operator takes tensors as inputs and outputs. The type of operation being +// performed is determined by an index into the list of valid OperatorCodes, +// while the specifics of each operations is configured using builtin_options +// or custom_options. +table Operator { + // Index into the operator_codes array. Using an integer here avoids + // complicate map lookups. + opcode_index:uint; + + // Optional input and output tensors are indicated by -1. + inputs:[int]; + outputs:[int]; + + builtin_options:BuiltinOptions; + custom_options:[ubyte]; + custom_options_format:CustomOptionsFormat; + + // A list of booleans indicating the input tensors which are being mutated by + // this operator.(e.g. used by RNN and LSTM). + // For example, if the "inputs" array refers to 5 tensors and the second and + // fifth are mutable variables, then this list will contain + // [false, true, false, false, true]. + // + // If the list is empty, no variable is mutated in this operator. + // The list either has the same length as `inputs`, or is empty. + mutating_variable_inputs:[bool]; +} + +// The root type, defining a subgraph, which typically represents an entire +// model. +table SubGraph { + // A list of all tensors used in this subgraph. + tensors:[Tensor]; + + // Indices of the tensors that are inputs into this subgraph. Note this is + // the list of non-static tensors that feed into the subgraph for inference. + inputs:[int]; + + // Indices of the tensors that are outputs out of this subgraph. Note this is + // the list of output tensors that are considered the product of the + // subgraph's inference. + outputs:[int]; + + // All operators, in execution order. + operators:[Operator]; + + // Name of this subgraph (used for debugging). + name:string; +} + +// Table of raw data buffers (used for constant tensors). Referenced by tensors +// by index. The generous alignment accommodates mmap-friendly data structures. +table Buffer { + data:[ubyte] (force_align: 16); +} + +table Model { + // Version of the schema. + version:uint; + + // A list of all operator codes used in this model. This is + // kept in order because operators carry an index into this + // vector. + operator_codes:[OperatorCode]; + + // All the subgraphs of the model. The 0th is assumed to be the main + // model. + subgraphs:[SubGraph]; + + // A description of the model. + description:string; + + // Buffers of the model. + // 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. + buffers:[Buffer]; + + // Metadata about the model. Indirects into the existings buffers list. + metadata_buffer:[int]; +} + +root_type Model; diff --git a/compiler/enco/frontend/tflite/schema/schema.meta b/compiler/enco/frontend/tflite/schema/schema.meta new file mode 100644 index 000000000..8cc1f4e62 --- /dev/null +++ b/compiler/enco/frontend/tflite/schema/schema.meta @@ -0,0 +1,2 @@ +Commit: 24963954a84a3e85dc8dfe79a15a01dc33fedab4 +URL: https://github.com/tensorflow/tensorflow/blob/2496395/tensorflow/contrib/lite/schema/schema.fbs diff --git a/compiler/enco/frontend/tflite/src/Context.cpp b/compiler/enco/frontend/tflite/src/Context.cpp new file mode 100644 index 000000000..ef030dc5d --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Context.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Context.h" + +#include "Convert.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +void TensorContext::prepare(const tflite::SubGraph *graph) +{ + for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) + { + auto const tensor_info = graph->tensors()->Get(tensor_id); + auto const tensor_name = tensor_info->name()->str(); + auto const tensor_shape = as_tensor_shape(tensor_info->shape()); + auto const tensor_type = tensor_info->type(); + + _name_ctx[tensor_id] = tensor_name; + _shape_ctx[tensor_id] = tensor_shape; + _type_ctx[tensor_id] = tensor_type; + } +} + +TflOpCodeContext::TflOpCodeContext( + const flatbuffers::Vector> *opcodes) +{ + for (const tflite::OperatorCode *opcode : *opcodes) + { + _opcodes.push_back(opcode); + } +} + +tflite::BuiltinOperator TflOpCodeContext::builtin_code(const tflite::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _opcodes.size()); + const tflite::OperatorCode *opcode = _opcodes.at(index); + return opcode->builtin_code(); +} + +std::string TflOpCodeContext::opcode_name(const tflite::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _opcodes.size()); + const tflite::OperatorCode *opcode = _opcodes.at(index); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + return opcode->custom_code()->c_str(); + } + + tflite::BuiltinOperator code = opcode->builtin_code(); + return EnumNameBuiltinOperator(code); +} + +bool TflOpCodeContext::is_valid(const tflite::OperatorCode *opcode) +{ + tflite::BuiltinOperator code = opcode->builtin_code(); + return (tflite::BuiltinOperator_MIN <= code && code <= tflite::BuiltinOperator_MAX); +} + +bool TflOpCodeContext::is_custom(const tflite::OperatorCode *opcode) +{ + tflite::BuiltinOperator code = opcode->builtin_code(); + return (code == tflite::BuiltinOperator_CUSTOM); +} + +TflBufferContext::TflBufferContext(const tflite::Model *tfl_model) +{ + const flatbuffers::Vector> *tfl_buffers; + + tfl_buffers = tfl_model->buffers(); + + for (uint32_t buffer_id = 0; buffer_id < tfl_buffers->size(); ++buffer_id) + { + _buffer_ctx[buffer_id] = (*tfl_buffers)[buffer_id]; + } +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Context.h b/compiler/enco/frontend/tflite/src/Context.h new file mode 100644 index 000000000..f72385f9a --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Context.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +#include "Convert.h" +#include "TensorBags.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +/** + * @brief Extracts and holds operand(tensor) information such as name, shape, and type + */ +class TensorContext +{ +public: + void prepare(const tflite::SubGraph *graph); + + const std::string &name(uint32_t tensor_id) { return _name_ctx[tensor_id]; } + const tensor::Shape &shape(uint32_t tensor_id) { return _shape_ctx[tensor_id]; } + const tflite::TensorType &type(uint32_t tensor_id) { return _type_ctx[tensor_id]; } + +private: + std::map _name_ctx; + std::map _shape_ctx; + std::map _type_ctx; +}; + +/** + * @brief Class that holds operator codes and related methods + */ +class TflOpCodeContext +{ +public: + TflOpCodeContext(const flatbuffers::Vector> *opcodes); + + /** + * @brief Returns BuiltinOperator value of the operator + */ + tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const; + + /** + * @brief Returns human readable name of the operator code of the operator + * + * @note TF lite InterpreterBuilder sets an error state and returns error code + * for invalid opcode. Here we just return human readable message as + * this method returns a name for the operator code. + */ + std::string opcode_name(const tflite::Operator *op) const; + +public: + static bool is_valid(const tflite::OperatorCode *opcode); + static bool is_custom(const tflite::OperatorCode *opcode); + +private: + std::vector _opcodes; +}; + +/** + * @brief Class to read and provide buffer information of tflite + */ +class TflBufferContext +{ +public: + template struct TflBuffer + { + TflBuffer(const T *p, size_t s) : ptr{p}, len{s} {}; + const T *ptr; + size_t len; + }; + +public: + explicit TflBufferContext(const tflite::Model *tfl_model); + +public: + template + TflBuffer tensor_buffer(const tflite::SubGraph *graph, uint32_t tensor_idx) const + { + TflBufferContext::TflBuffer res{nullptr, 0}; + const auto *tensor = graph->tensors()->Get(tensor_idx); + uint32_t tfl_buf_id = tensor->buffer(); + + assert(_buffer_ctx.size() > tfl_buf_id); + + const tflite::Buffer *tfl_buffer = _buffer_ctx.at(tfl_buf_id); + + if (auto *array = tfl_buffer->data()) + { + if (size_t size = array->size()) + { + assert(size % sizeof(T) == 0); + + res.len = size / sizeof(T); + res.ptr = reinterpret_cast(array->data()); + } + } + + return res; + } + +private: + std::map _buffer_ctx; +}; + +/** + * @brief Class to store context to build IR from tflite + */ +class GraphBuilderContext +{ +public: + explicit GraphBuilderContext(coco::Module *m, coco::Data *d, coco::Block *block, + TensorBags &tensor_bags, TensorContext &tensor_context, + TflBufferContext &buffer_context, const tflite::SubGraph *graph) + : _m(m), _d(d), _block(block), _tensor_bags(tensor_bags), _tensor_context(tensor_context), + _buffer_context(buffer_context), _graph(graph) + { + // DO NOTHING + } + + GraphBuilderContext() = delete; + GraphBuilderContext(const GraphBuilderContext &) = delete; + GraphBuilderContext(GraphBuilderContext &&) = delete; + +public: + coco::Module *m() { return _m; } + coco::Data *d() { return _d; } + coco::Block *block() { return _block; } + TensorContext &tensor() { return _tensor_context; } + TensorBags &bags() { return _tensor_bags; } + TflBufferContext &buffer() { return _buffer_context; } + const tflite::SubGraph *graph() { return _graph; } + +private: + coco::Module *_m; + coco::Data *_d; + coco::Block *_block; + TensorContext &_tensor_context; + TensorBags &_tensor_bags; + TflBufferContext &_buffer_context; + const tflite::SubGraph *_graph; +}; + +} // namespace tflimport + +#endif // __CONTEXT_H__ diff --git a/compiler/enco/frontend/tflite/src/Convert.cpp b/compiler/enco/frontend/tflite/src/Convert.cpp new file mode 100644 index 000000000..ffae95d01 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Convert.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Convert.h" + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +IndexVector as_index_vector(const flatbuffers::Vector *array) +{ + const uint32_t size = array->size(); + + std::vector res(size); + + for (uint32_t i = 0; i < size; i++) + { + res[i] = array->Get(i); + } + + return res; +} + +tensor::Shape as_tensor_shape(const flatbuffers::Vector *shape) +{ + const uint32_t rank = shape->size(); + + tensor::Shape res; + + res.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = shape->Get(axis); + } + + return res; +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Convert.h b/compiler/enco/frontend/tflite/src/Convert.h new file mode 100644 index 000000000..fb4c248bf --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Convert.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +using IndexVector = std::vector; + +/** + * @brief Converts flatbuffers::Vector to IndexVector + */ +IndexVector as_index_vector(const flatbuffers::Vector *array); + +/** + * @brief Converts flatbuffers::Vector to nncc::core::ADT::tensor::Shape + */ +tensor::Shape as_tensor_shape(const flatbuffers::Vector *shape); + +} // namespace tflimport + +#endif // __CONVERT_H__ diff --git a/compiler/enco/frontend/tflite/src/Entry.cpp b/compiler/enco/frontend/tflite/src/Entry.cpp new file mode 100644 index 000000000..c69e18074 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Entry.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Frontend.h" +#include "RawModelLoader.h" + +#include + +#include + +#include +#include + +using stdex::make_unique; + +extern "C" std::unique_ptr make_frontend(const cmdline::View &cmdline) +{ + assert(cmdline.size() == 1); // tflite file name + + auto model = load_from(cmdline.at(0)); + + return make_unique(std::move(model)); +} diff --git a/compiler/enco/frontend/tflite/src/Frontend.cpp b/compiler/enco/frontend/tflite/src/Frontend.cpp new file mode 100644 index 000000000..c64f181f4 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Frontend.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Frontend.h" +#include "Context.h" +#include "Convert.h" +#include "TensorBags.h" +#include "GraphBuilderRegistry.h" + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +/** + * @brief Set module input operands and its information + */ +void set_module_inputs(coco::Module *m, TensorContext &ctx, TensorBags &bags, + const IndexVector &inputs) +{ + for (uint32_t n = 0; n < inputs.size(); ++n) + { + auto const tensor_id = inputs.at(n); + + auto const tensor_name = ctx.name(tensor_id); + auto const tensor_shape = ctx.shape(tensor_id); + auto const tensor_bag = bags.bag(tensor_id); + + auto input = m->entity()->input()->create(tensor_shape); + + input->name(tensor_name); + input->bag(tensor_bag); + input->reorder(); + + m->input()->insert(input); + } +} + +/** + * @brief Set module output operands and its information + */ +void set_module_outputs(coco::Module *m, TensorContext &ctx, TensorBags &bags, + const IndexVector &outputs) +{ + for (uint32_t n = 0; n < outputs.size(); ++n) + { + auto const tensor_id = outputs.at(n); + + auto const tensor_name = ctx.name(tensor_id); + auto const tensor_shape = ctx.shape(tensor_id); + auto const tensor_bag = bags.bag(tensor_id); + + auto output = m->entity()->output()->create(tensor_shape); + + output->name(tensor_name); + output->bag(tensor_bag); + output->reorder(); + + m->output()->insert(output); + } +} + +/** + * @brief Copy values of tfl tensors into coco::Data if the data was not copied + */ +void copy_tensors(GraphBuilderContext *ctx) +{ + auto d = ctx->d(); + + // for each bag, check if bag is not allocated but tflite tensor has values + for (auto &iter : ctx->bags()) + { + auto tfl_tensor_id = iter.first; + auto bag = iter.second; + + auto tfl_buffer = ctx->buffer().tensor_buffer(ctx->graph(), tfl_tensor_id); + + // TODO remove this line when support int32 is ready + if (ctx->tensor().type(tfl_tensor_id) == tflite::TensorType::TensorType_INT32) + { + std::cout << "*** INT32 COPYING IS NOT SUPPORTED ***" << std::endl; + continue; + } + + assert(ctx->tensor().type(tfl_tensor_id) == tflite::TensorType::TensorType_FLOAT32); + + auto span = d->f32()->weight(bag); // TODO support other type + + if (!(span.data() == nullptr && span.size() == 0)) // already allocated + continue; + + if (tfl_buffer.ptr == nullptr || tfl_buffer.len == 0) // no data to copy + continue; + + d->f32()->allocate(bag); + + auto ifm_span = d->f32()->weight(bag); + for (uint32_t idx = 0; idx < tfl_buffer.len; ++idx) + { + ifm_span[idx] = tfl_buffer.ptr[idx]; + } + } +} + +} // namespace tflimport + +Frontend::Frontend(std::unique_ptr &&raw) : _raw{std::move(raw)} +{ + // DO NOTHING +} + +enco::Bundle Frontend::load(void) const +{ + auto model = _raw->model(); + + assert(model->version() == 3); + assert(model->subgraphs()->size() == 1); + + auto graph = model->subgraphs()->Get(0); + + auto m = coco::Module::create(); + auto d = coco::Data::create(); + + tflimport::TensorContext tensor_context; + tflimport::TensorBags tensor_bags; + + tensor_context.prepare(graph); + tensor_bags.prepare(graph, m); + + auto inputs = tflimport::as_index_vector(graph->inputs()); + auto outputs = tflimport::as_index_vector(graph->outputs()); + + tflimport::set_module_inputs(m.get(), tensor_context, tensor_bags, inputs); + tflimport::set_module_outputs(m.get(), tensor_context, tensor_bags, outputs); + + auto blk = m->entity()->block()->create(); + m->block()->append(blk); + + auto opcodes = model->operator_codes(); + + tflimport::TflBufferContext buffer_context(model); + tflimport::TflOpCodeContext opcode_context(opcodes); + + auto operators = graph->operators(); + + tflimport::GraphBuilderContext opbuilder_context(m.get(), d.get(), blk, tensor_bags, + tensor_context, buffer_context, graph); + + for (int i = 0; i < operators->Length(); ++i) + { + const auto *op = operators->Get(i); + tflite::BuiltinOperator builtincode = opcode_context.builtin_code(op); + + if (const auto *graph_builder = tflimport::GraphBuilderRegistry::get().lookup(builtincode)) + { + if (!graph_builder->validate(op)) + { + throw std::runtime_error{"Invalid operator"}; + } + + graph_builder->build(op, &opbuilder_context); + } + else + { + std::string opcodename = opcode_context.opcode_name(op); + throw std::runtime_error{"Not supported: " + opcodename}; + } + + // copying unfilled tensor value + copy_tensors(&opbuilder_context); + } + + // Create "Bundle" + enco::Bundle bundle; + + bundle.module(std::move(m)); + bundle.data(std::move(d)); + + return std::move(bundle); +} diff --git a/compiler/enco/frontend/tflite/src/Frontend.h b/compiler/enco/frontend/tflite/src/Frontend.h new file mode 100644 index 000000000..bb0c9cd2c --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Frontend.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FRONTEND_H__ +#define __FRONTEND_H__ + +#include "RawModel.h" + +#include + +#include + +#include + +class Frontend final : public enco::Frontend +{ +public: + Frontend(std::unique_ptr &&raw); + +public: + enco::Bundle load(void) const override; + +private: + std::unique_ptr _raw; +}; + +#endif // __FRONTEND_H__ diff --git a/compiler/enco/frontend/tflite/src/Frontend.test.cpp b/compiler/enco/frontend/tflite/src/Frontend.test.cpp new file mode 100644 index 000000000..aee6099e7 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Frontend.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Frontend.h" + +#include + +#include + +using stdex::make_unique; + +namespace +{ + +struct MockRawModel final : public RawModel +{ + const tflite::Model *model(void) const override { return nullptr; } +}; + +} // namespace + +TEST(FrontendTest, constructor) +{ + // Let's test whether Frontend is actually constructible. + auto frontend = make_unique(make_unique()); + + ASSERT_NE(frontend, nullptr); +} diff --git a/compiler/enco/frontend/tflite/src/GraphBuilder.h b/compiler/enco/frontend/tflite/src/GraphBuilder.h new file mode 100644 index 000000000..f2cb57848 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/GraphBuilder.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_H__ +#define __GRAPH_BUILDER_H__ + +#include "Context.h" + +#include + +namespace tflimport +{ + +/** + * @brief Parent class of tflite operation graph builders (e.g., Conv2DGraphBuilder) + */ +class GraphBuilder +{ +public: + /** + * TODO Declare "validate" method as a pure virtual method + * + * Q: Is it possible to validate T/F Lite model only with this interface? + */ + virtual bool validate(const tflite::Operator *) const { return true; } + + virtual void build(const tflite::Operator *op, GraphBuilderContext *context) const = 0; + virtual ~GraphBuilder() {} +}; + +} // namespace tflimport + +#endif // __GRAPH_BUILDER_H__ diff --git a/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h b/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h new file mode 100644 index 000000000..1ae882e89 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_REGISTRY_H__ +#define __GRAPH_BUILDER_REGISTRY_H__ + +#include "Op/Conv2D.h" +#include "Op/DepthwiseConv2D.h" +#include "Op/AveragePool2D.h" +#include "Op/MaxPool2D.h" +#include "Op/Concatenation.h" +#include "Op/ReLU.h" +#include "Op/ReLU6.h" +#include "Op/Reshape.h" +#include "Op/Sub.h" +#include "Op/Div.h" + +#include +#include + +#include + +using stdex::make_unique; + +namespace tflimport +{ + +/** + * @brief Class to return graph builder for passed tflite::builtinOperator + */ +class GraphBuilderRegistry +{ +public: + /** + * @brief Returns registered GraphBuilder pointer for BuiltinOperator or + * nullptr if not registered + */ + const GraphBuilder *lookup(tflite::BuiltinOperator op) const + { + if (_builder_map.find(op) == _builder_map.end()) + return nullptr; + + return _builder_map.at(op).get(); + } + + static GraphBuilderRegistry &get() + { + static GraphBuilderRegistry me; + return me; + } + +private: + GraphBuilderRegistry() + { + // add GraphBuilder for each tflite operation. + _builder_map[tflite::BuiltinOperator_CONV_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = + make_unique(); + _builder_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_CONCATENATION] = make_unique(); + _builder_map[tflite::BuiltinOperator_RELU] = make_unique(); + _builder_map[tflite::BuiltinOperator_RELU6] = make_unique(); + _builder_map[tflite::BuiltinOperator_RESHAPE] = make_unique(); + _builder_map[tflite::BuiltinOperator_SUB] = make_unique(); + _builder_map[tflite::BuiltinOperator_DIV] = make_unique(); + } + +private: + std::map> _builder_map; +}; + +} // namespace tflimport + +#endif // __GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/enco/frontend/tflite/src/IRBuilder.h b/compiler/enco/frontend/tflite/src/IRBuilder.h new file mode 100644 index 000000000..edfe247e1 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/IRBuilder.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file IRBuilder.h + * @brief coco IR builders. This is code is copied from enco caffe frontend. + */ +#ifndef __IR_BUILDER_H__ +#define __IR_BUILDER_H__ + +#include "coco/IR/Module.h" + +#include + +using namespace nncc::core::ADT; + +class OpBuilder +{ +public: + OpBuilder(coco::Module *module) : _module{module} + { + // module SHOULD BE valid + assert(_module != nullptr); + } + +public: + /** + * @brief Return true if the internal stack is empty + */ + bool empty(void) const { return _stack.empty(); } + + /** + * @brief Return the operation at the top of the internal stack + */ + coco::Op *top(void) const + { + assert(_stack.size() > 0); + return _stack.front(); + } + + /** + * @brief Push op onto the internal stack + * + * BEFORE| Stack + * AFTER | Op; Stack + */ + OpBuilder &push(coco::Op *op) + { + _stack.push_front(op); + return (*this); + } + + /** + * @brief Create "Load" op and push it onto the internal stack + * + * BEFORE| Stack + * AFTER | Load(obj); Stack + */ + OpBuilder &load(coco::Object *obj) + { + auto op = _module->entity()->op()->create(); + op->object(obj); + push(op); + return (*this); + } + + /** + * @brief Create "Add" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Add(Left, Right); Stack + */ + OpBuilder &add(void) { return binary(); } + + /** + * @brief Create "Mul" op and push it onto the internal stack + * + * BEFORE| Left; Right; Stack + * AFTER | Mul(Left, Right); Stack + */ + OpBuilder &mul(void) { return binary(); } + + /** + * @brief Pop op from the internal stack + * + * BEFORE| Op; Stack + * AFTER | Stack + */ + coco::Op *pop(void) + { + assert(_stack.size() > 0); + auto op = _stack.front(); + _stack.pop_front(); + return op; + } + +private: + template OpBuilder &binary() + { + assert(_stack.size() >= 2); + auto left = pop(); + auto right = pop(); + + auto op = _module->entity()->op()->create(); + op->left(left); + op->right(right); + push(op); + + return (*this); + } + +private: + coco::Module *_module; + std::deque _stack; +}; + +inline OpBuilder op_builder(coco::Module *m) { return OpBuilder{m}; } +inline OpBuilder op_builder(const std::unique_ptr &m) { return op_builder(m.get()); } + +class InstrBuilder +{ +public: + InstrBuilder(coco::Module *module) : _module{module} + { + // NOTE _module SHOULD be valid + assert(_module != nullptr); + } + +public: + /** + * @brief Create "Eval" instruction with a given "Object" and "Op" + * + * @note "eval(out, op)" will create "%out <- Eval(op)" instruction + */ + coco::Eval *eval(coco::Object *out, coco::Op *op) const + { + auto ins = _module->entity()->instr()->create(); + ins->op(op); + ins->out(out); + return ins; + } + + /** + * @brief Create "Copy" instruction with given two "Object" + * + * @note "copy(into, from)" will create "%into <- Copy(%from)" instruction + */ + coco::Copy *copy(coco::Object *into, coco::Object *from) const + { + auto ins = _module->entity()->instr()->create(); + ins->from(from); + ins->into(into); + return ins; + } + +private: + coco::Module *_module; +}; + +using ModuleHandle = std::unique_ptr; + +inline InstrBuilder instr_builder(coco::Module *m) { return InstrBuilder{m}; } +inline InstrBuilder instr_builder(const ModuleHandle &m) { return instr_builder(m.get()); } + +#endif // __IR_BUILDER_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Activation.cpp b/compiler/enco/frontend/tflite/src/Op/Activation.cpp new file mode 100644 index 000000000..d6215ba34 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Activation.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Activation.h" + +#include + +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::FeatureObject *build_activation(tflite::ActivationFunctionType act, coco::Block *block, + coco::FeatureObject *ifm) +{ + assert(ifm != nullptr && ifm->asFeature() != nullptr); // support feature only in this version + + coco::Module *m = block->module(); + + auto shape = ifm->asFeature()->shape(); + + // creates output object + auto output_obj = m->entity()->object()->create(); + auto output_bag = m->entity()->bag()->create(num_elements(shape)); + output_obj->bag(output_bag); + output_obj->layout(coco::FeatureLayouts::BHWC::create(shape)); + + switch (act) + { + case tflite::ActivationFunctionType::ActivationFunctionType_NONE: + { + // Create Copy Instr (copying from ifm to output_obj), + // redundant layer but optimized by backend + auto copy_ins = instr_builder(m).copy(output_obj, ifm); + + // Append the instruction to the block + block->instr()->append(copy_ins); + break; + } + case tflite::ActivationFunctionType::ActivationFunctionType_RELU: + { + // Create Eval(output_obj, ReLU(load(ifm))) + auto load_op = op_builder(m).load(ifm).pop(); + auto relu_op = m->entity()->op()->create(); + relu_op->arg(load_op); + + auto eval_ins = instr_builder(m).eval(output_obj, relu_op); + + // Append the instruction to the block + block->instr()->append(eval_ins); + break; + } + case tflite::ActivationFunctionType::ActivationFunctionType_RELU6: + { + // Create Eval(output_obj, ReLU6(load(ifm))) + auto load_op = op_builder(m).load(ifm).pop(); + auto relu6_op = m->entity()->op()->create(); + relu6_op->arg(load_op); + + auto eval_ins = instr_builder(m).eval(output_obj, relu6_op); + + // Append the instruction to the block + block->instr()->append(eval_ins); + break; + } + default: + // TODO support other fused activations + assert(false); + break; + } + + return output_obj; +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Activation.h b/compiler/enco/frontend/tflite/src/Op/Activation.h new file mode 100644 index 000000000..05306dd41 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Activation.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_ACTIVATION_H__ +#define __OP_ACTIVATION_H__ + +#include +#include + +#include + +namespace tflimport +{ + +/** + * @brief Add coco::Eval for fused activation. + * This method creates an ofm object, appends Eval(ofm object, RELU(...)) into block, + * and returns ofm object. + */ +coco::FeatureObject *build_activation(tflite::ActivationFunctionType act, coco::Block *block, + coco::FeatureObject *ifm); +} // namespace tflimport + +#endif // __OP_ACTIVATION_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp new file mode 100644 index 000000000..16f68fcdb --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AveragePool2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +bool AvgPool2DGraphBuilder::validate(const tflite::Operator *op) const +{ + auto const options = op->builtin_options_as_Pool2DOptions(); + + if ((options->stride_h() == 0) || (options->stride_w() == 0)) + { + return false; + } + + return true; +} + +void AvgPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a AvgPool2D + auto coco_avgpool2d = m->entity()->op()->create(); + auto *params = op->builtin_options_as_Pool2DOptions(); + + // NOTE For Tensorflow lite, PaddingExcluded is needed + coco_avgpool2d->divisor(coco::AvgPool2D::Divisor::PaddingExcluded); + + coco_avgpool2d->window()->height(params->filter_height()); + coco_avgpool2d->window()->width(params->filter_width()); + + coco_avgpool2d->stride()->vertical(params->stride_h()); + coco_avgpool2d->stride()->horizontal(params->stride_w()); + + coco::Padding2D padding = + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + + coco_avgpool2d->pad()->top(padding.top()); + coco_avgpool2d->pad()->bottom(padding.bottom()); + coco_avgpool2d->pad()->left(padding.left()); + coco_avgpool2d->pad()->right(padding.right()); + + // Link ops + coco_avgpool2d->arg(coco_load); + + // Create an Eval instruction + auto ins = instr_builder(m).eval(ofm_obj, coco_avgpool2d); + + // Append the instruction to the block + blk->instr()->append(ins); + + // TODO activation, e.g., relu + assert(params->fused_activation_function() == + tflite::ActivationFunctionType::ActivationFunctionType_NONE); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/AveragePool2D.h b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.h new file mode 100644 index 000000000..3e37e3cad --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_AVERAGEPOOL2D_H__ +#define __OP_AVERAGEPOOL2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for AvgPool2D operator + */ +class AvgPool2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tflite::Operator *op) const override; + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_AVERAGEPOOL2D_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Concatenation.cpp b/compiler/enco/frontend/tflite/src/Op/Concatenation.cpp new file mode 100644 index 000000000..ce0f47b21 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Concatenation.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Concatenation.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace +{ + +/** + * @brief Convert a numeric tensor axis as a ConcatF FeatureAxis value + */ +coco::ConcatF::Axis as_ConcatF_axis(uint32_t axis) +{ + // NOTE The feature map (in TensorFlow) is a rank-4 (NHWC) tensor + assert(axis < 4); + + coco::ConcatF::Axis res = coco::ConcatF::Axis::Unknown; + + switch (axis) + { + case 0: + res = coco::ConcatF::Axis::Batch; + break; + case 1: + res = coco::ConcatF::Axis::Height; + break; + case 2: + res = coco::ConcatF::Axis::Width; + break; + case 3: + res = coco::ConcatF::Axis::Depth; + break; + default: + break; + } + + return res; +} + +/** + * @brief Convert a coco FeatureShape as an array of 'uint32_t' values + */ +std::array as_dims(const coco::FeatureShape &shape) +{ + std::array res; + + res[0] = shape.batch(); + res[1] = shape.height(); + res[2] = shape.width(); + res[3] = shape.depth(); + + return res; +} + +/** + * @brief Convert a tensor shape as a coco FeatureShape + */ +coco::FeatureShape as_feature_shape(const tensor::Shape &shape) +{ + assert(shape.rank() == 4); + + auto const B = shape.dim(0); + auto const C = shape.dim(3); + auto const H = shape.dim(1); + auto const W = shape.dim(2); + + return coco::FeatureShape{B, C, H, W}; +} + +} // namespace + +namespace tflimport +{ + +void ConcatenationGraphBuilder::build(const tflite::Operator *op, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 ~ N : any number of input features + // output index 0 : one output feature + assert(opinputs.size() > 0); + assert(opoutputs.size() == 1); + + // Default parameter values are referenced from schema_generated.h + int32_t concat_axis = 0; + tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE; + + if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions()) + { + activation = concatenation_params->fused_activation_function(); + concat_axis = concatenation_params->axis(); + + const int32_t rank = static_cast(tensor_context.shape(opinputs.at(0)).rank()); + if (concat_axis < 0) + { + concat_axis += rank; + } + assert(concat_axis >= 0); + assert(concat_axis < rank); + } + assert(as_ConcatF_axis(concat_axis) != coco::ConcatF::Axis::Unknown); + assert(activation == tflite::ActivationFunctionType_NONE); + + // Construct a vector of input objects + std::vector input_objects; + + for (auto &input_index : opinputs) + { + const tensor::Shape &input_shape = tensor_context.shape(input_index); + coco::FeatureObject *input_obj = m->entity()->object()->create(); + coco::Bag *input_bag = bags.bag(input_index); + input_obj->bag(input_bag); + input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape))); + + input_objects.emplace_back(input_obj); + } + + coco::FeatureObject *last_feature = input_objects.at(0); + + assert(last_feature != nullptr); + assert(last_feature->bag() != nullptr); + + // Update coco IR + // + // Given a sequence of input features %in[0] / %in[1] / ... / %in[N] + // the below code constructs a sequence of eval instructions + // - Load is omitted for simplicity + // + // %tmp = eval(ConcatF(%in[0], %in[1])) + // %tmp = eval(ConcatF(%tmp, %in[2])) + // ... + // %tmp = eval(ConcatF(%tmp, %in[N])) + // %out[0] = copy(%tmp) + // + for (uint32_t n = 1; n < input_objects.size(); ++n) + { + auto const left_feature = last_feature; + auto const left_shape = left_feature->layout()->shape(); + + auto right_feature = input_objects.at(n); + auto right_shape = right_feature->layout()->shape(); + + // Compute output dimensionalities + auto compute_out_dims = [&left_shape, &right_shape, concat_axis](void) { + std::array out_dims; + + const auto left_dims = as_dims(left_shape); + const auto right_dims = as_dims(right_shape); + + for (uint32_t axis = 0; axis < 4 /* FEATURE MAP RANK */; ++axis) + { + // The dimensionality of all the axises except 'concat' axis SHOULD BE INDETICAL + assert((concat_axis == axis) || (left_dims[axis] == right_dims[axis])); + + out_dims[axis] = left_dims[axis]; + if (axis == concat_axis) + { + out_dims[axis] += right_dims[axis]; + } + } + + return out_dims; + }; + + const auto out_dims = compute_out_dims(); + + const uint32_t B = out_dims[0 /* BATCH */]; + const uint32_t C = out_dims[3 /* DEPTH */]; + const uint32_t H = out_dims[1 /* HEIGHT */]; + const uint32_t W = out_dims[2 /* WIDTH */]; + + const coco::FeatureShape out_shape{B, C, H, W}; + + auto out_bag = m->entity()->bag()->create(B * num_elements(out_shape)); + auto out_feature = m->entity()->object()->create(); + + out_feature->bag(out_bag); + out_feature->layout(coco::FeatureLayouts::BHWC::create(out_shape)); + + auto left_load = op_builder(m).load(left_feature).pop(); + auto right_load = op_builder(m).load(right_feature).pop(); + + auto concat_f = m->entity()->op()->create(); + + concat_f->axis(as_ConcatF_axis(concat_axis)); + concat_f->left(left_load); + concat_f->right(right_load); + + auto eval = instr_builder(m).eval(out_feature, concat_f); + + // Append the constructed Shuffle instruction + blk->instr()->append(eval); + + // Update 'last_feature' + last_feature = out_feature; + } + + // Insert copy instruction from last_feature to output operand + int const ofm_idx = opoutputs.at(0); + auto const ofm_shape = tensor_context.shape(ofm_idx); + + auto ofm_bag = bags.bag(ofm_idx); + auto ofm_obj = m->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Copy instruction from last into ofm + auto copy_ins = instr_builder(m).copy(ofm_obj, last_feature); + + // Append the instruction + blk->instr()->append(copy_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Concatenation.h b/compiler/enco/frontend/tflite/src/Op/Concatenation.h new file mode 100644 index 000000000..eb7625a85 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Concatenation.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONCATENATION_H__ +#define __OP_CONCATENATION_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Concatenation operator + */ +class ConcatenationGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_CONCATENATION_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp b/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp new file mode 100644 index 000000000..e9516c0e9 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Conv2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +bool Conv2DGraphBuilder::validate(const tflite::Operator *op) const +{ + auto const options = op->builtin_options_as_Conv2DOptions(); + + if ((options->stride_h() == 0) || (options->stride_w() == 0)) + { + return false; + } + + return true; +} + +void Conv2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + // preparation + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + TflBufferContext &buffer_context = context->buffer(); + const tflite::SubGraph *graph = context->graph(); + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // input index 1 : kernel + // input index 2 : bias (optional) + bool hasBias = (opinputs.size() == 3); + assert(opinputs.size() == 2 || hasBias); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ker_idx = opinputs.at(1); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + const tensor::Shape &ker_shape = tensor_context.shape(ker_idx); + + // Create an input feature map object + auto *ifm_obj = m->entity()->object()->create(); + auto *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an an output feature map object + auto *ofm_obj = m->entity()->object()->create(); + auto *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an kernel object + auto *ker_obj = m->entity()->object()->create(); + auto *ker_bag = bags.bag(ker_idx); + ker_obj->bag(ker_bag); + ker_obj->layout(coco::KernelLayouts::NHWC::create(as_kernel_shape(ker_shape))); + + // Create a Load op + auto load = op_builder(m).load(ifm_obj).pop(); + + // Create a Conv2D op + auto coco_conv2d = m->entity()->op()->create(); + + // populating Conv2D objects and options such as stride and padding + coco_conv2d->ker(ker_obj); + + auto *conv_params = op->builtin_options_as_Conv2DOptions(); + + coco_conv2d->stride()->vertical(conv_params->stride_h()); + coco_conv2d->stride()->horizontal(conv_params->stride_w()); + + // conv_params->padding() to left, top, right, bottom + coco::Padding2D padding = conv2D_padding(conv_params, ifm_shape, ker_shape); + + coco_conv2d->pad()->top(padding.top()); + coco_conv2d->pad()->bottom(padding.bottom()); + coco_conv2d->pad()->left(padding.left()); + coco_conv2d->pad()->right(padding.right()); + + // Link ops + coco_conv2d->arg(load); + + // Object to store Conv2D output + auto *conv2d_obj = m->entity()->object()->create(); + auto *conv2d_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + conv2d_obj->bag(conv2d_bag); + conv2d_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an Eval instruction for Conv2D + auto conv2d_ins = instr_builder(m).eval(conv2d_obj, coco_conv2d); + + // Append the instruction to the block + blk->instr()->append(conv2d_ins); + + // Last Object to make a copy to Output Object + coco::FeatureObject *last_obj = conv2d_obj; + + if (hasBias) + { + // When there is a bias, use btmp_obj as bias add output + // Bias is adding last_obj with bias weight values + auto *btmp_obj = m->entity()->object()->create(); + auto *btmp_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + btmp_obj->bag(btmp_bag); + btmp_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_obj->shape())); + + int bias_idx = opinputs.at(2); + + // Create an object for bias + auto bias_obj = m->entity()->object()->create(); + coco::Bag *bias_bag = bags.bag(bias_idx); + bias_obj->bag(bias_bag); + bias_obj->layout(coco::FeatureLayouts::BC::create(ofm_obj->shape())); + + // Create Op of conv2d output (last_obj) + bias values(bias_obj) + auto bias_add = op_builder(m).load(last_obj).load(bias_obj).add().pop(); + + // Create Instr as bias add result write to btmp_obj + auto bias_add_ins = instr_builder(m).eval(btmp_obj, bias_add); + + // Append the instruction + blk->instr()->append(bias_add_ins); + + // Update last_obj to btmp_obj + last_obj = btmp_obj; + } + + // fused activation + coco::FeatureObject *act_output = + build_activation(conv_params->fused_activation_function(), blk, last_obj); + + // Create Copy Instr of last_obj to Output Object + auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); + blk->instr()->append(copy_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Conv2D.h b/compiler/enco/frontend/tflite/src/Op/Conv2D.h new file mode 100644 index 000000000..018815bd4 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Conv2D.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_CONV2D_H__ +#define __OP_CONV2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Conv2D operator + */ +class Conv2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tflite::Operator *op) const override; + void build(const tflite::Operator *op, GraphBuilderContext *context) const override; +}; + +} // namespace tflimport + +#endif // __OP_CONV2D_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp new file mode 100644 index 000000000..e3d7b263e --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DepthwiseConv2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +bool DepthwiseConv2DGraphBuilder::validate(const tflite::Operator *op) const +{ + auto const options = op->builtin_options_as_DepthwiseConv2DOptions(); + + if ((options->stride_h() == 0) || (options->stride_w() == 0)) + { + return false; + } + + return true; +} + +void DepthwiseConv2DGraphBuilder::build(const tflite::Operator *op, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + // preparation + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + TflBufferContext &buffer_context = context->buffer(); + const tflite::SubGraph *graph = context->graph(); + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // input index 1 : kernel + // input index 2 : bias (optional) + bool hasBias = (opinputs.size() == 3); + assert(opinputs.size() == 2 || hasBias); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ker_idx = opinputs.at(1); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + tensor::Shape &ker_shape = const_cast(tensor_context.shape(ker_idx)); + + assert(ifm_shape.rank() == 4); + assert(ofm_shape.rank() == 4); + assert(ker_shape.rank() == 4); + + assert(ker_shape.dim(0) == 1); // value > 1 was not tested. This value seems 1 in DepthwiseConv2D + assert(ifm_shape.dim(3) == ofm_shape.dim(3)); + assert(ofm_shape.dim(3) == ker_shape.dim(3)); + + // Create an input feature map object + auto *ifm_obj = m->entity()->object()->create(); + auto *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an an output feature map object + auto *ofm_obj = m->entity()->object()->create(); + auto *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an kernel object + auto *ker_obj = m->entity()->object()->create(); + auto *ker_bag = bags.bag(ker_idx); + ker_obj->bag(ker_bag); + + // Adjust tflite kernel shape [1, h, w, channel_out] for coco::Kernel. + // coco::Kernel will have kernel.count = channel_out, kernel.depth = 1 ( == ker_shape.dim(0)) + kernel::Shape new_shape{ker_shape.dim(3), 1, ker_shape.dim(1), ker_shape.dim(2)}; + ker_obj->layout(coco::KernelLayouts::NHWC::create(new_shape)); + + // Create a kernel overlay for the kernel object + // TODO : support for other types + d->f32()->allocate(ker_bag); + + TflBufferContext::TflBuffer buffer = buffer_context.tensor_buffer(graph, ker_idx); + + auto ker_spn = d->f32()->weight(ker_bag); + + // Copy data from tflBuffer of [1, h, w, channel_out] shape to coco::Data, which will be accessed + // by coco::KernelLayouts::NHWC + for (auto n = 0; n < new_shape.count(); n++) + { + auto tfl_c = n; + for (auto h = 0; h < new_shape.height(); h++) + { + for (auto w = 0; w < new_shape.width(); w++) + { + auto hw = new_shape.height() * new_shape.width(); + for (auto c = 0; c < new_shape.depth(); c++) + { + auto tfl_n = c; + auto hwc = hw * new_shape.depth(); + auto wc = new_shape.width() * new_shape.depth(); + + ker_spn[n * hwc + h * wc + w * new_shape.depth() + c] = + buffer.ptr[tfl_n * hw * new_shape.count() + /* new_shape.count() is old c */ + h * new_shape.width() * new_shape.count() + w * new_shape.count() + tfl_c]; + } + } + } + } + + // Create a Load op + auto load = op_builder(m).load(ifm_obj).pop(); + + // Create a coco::Conv2D op for DepthwiseConv2D + auto coco_dconv2d = m->entity()->op()->create(); + + // populating objects and options such as stride and padding for DepthwiseConv2D + coco_dconv2d->ker(ker_obj); + + // setting params passed from TFLITE DepthwiseConv2DOptions + auto dconv_params = op->builtin_options_as_DepthwiseConv2DOptions(); + + assert(dconv_params->depth_multiplier() == 1); // other depth_multiplier was not tested + + coco_dconv2d->group(ifm_obj->asFeature()->shape().depth()); + + coco_dconv2d->stride()->vertical(dconv_params->stride_h()); + coco_dconv2d->stride()->horizontal(dconv_params->stride_w()); + + coco::Padding2D padding = depthwiseConv2D_padding(dconv_params, ifm_shape, ker_shape); + coco_dconv2d->pad()->top(padding.top()); + coco_dconv2d->pad()->bottom(padding.bottom()); + coco_dconv2d->pad()->left(padding.left()); + coco_dconv2d->pad()->right(padding.right()); + + // Link ops + coco_dconv2d->arg(load); + + // Object to store output for DepthwiseConv2D + auto *dconv2d_obj = m->entity()->object()->create(); + auto *dconv2d_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + dconv2d_obj->bag(dconv2d_bag); + dconv2d_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an Eval instruction for DepthwiseConv2D + auto dconv2d_ins = instr_builder(m).eval(dconv2d_obj, coco_dconv2d); + + // Append the instruction to the block + blk->instr()->append(dconv2d_ins); + + // Last Object to make a copy to Output Object + coco::FeatureObject *last_obj = dconv2d_obj; + + if (hasBias) + { + // When there is a bias, use btmp_obj as bias add output + // Bias is adding last_obj with bias weight values + auto *btmp_obj = m->entity()->object()->create(); + auto *btmp_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + btmp_obj->bag(btmp_bag); + btmp_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_obj->shape())); + + int bias_idx = opinputs.at(2); + + // Create an object for bias + auto bias_obj = m->entity()->object()->create(); + coco::Bag *bias_bag = bags.bag(bias_idx); + bias_obj->bag(bias_bag); + bias_obj->layout(coco::FeatureLayouts::BC::create(ofm_obj->shape())); + + // Create Op of conv2d output (last_obj) + bias values(bias_obj) + auto bias_add = op_builder(m).load(last_obj).load(bias_obj).add().pop(); + + // Create Instr as bias add result write to btmp_obj + auto bias_add_ins = instr_builder(m).eval(btmp_obj, bias_add); + + // Append the instruction + blk->instr()->append(bias_add_ins); + + // Update last_obj to btmp_obj + last_obj = btmp_obj; + } + + // fused activation + coco::FeatureObject *act_output = + build_activation(dconv_params->fused_activation_function(), blk, last_obj); + + // Create Copy Instr of last_obj to Output Object + auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); + blk->instr()->append(copy_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.h b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.h new file mode 100644 index 000000000..b36b36b8f --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DEPTHWISECONV2D_H__ +#define __OP_DEPTHWISECONV2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for DepthwiseConv2D operator + */ +class DepthwiseConv2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tflite::Operator *op) const override; + void build(const tflite::Operator *op, GraphBuilderContext *context) const override; +}; + +} // namespace tflimport + +#endif // __OP_DEPTHWISECONV2D_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Div.cpp b/compiler/enco/frontend/tflite/src/Op/Div.cpp new file mode 100644 index 000000000..6b71be2e6 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Div.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Div.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void DivGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : numerator + // input index 1 : denominator + // output index 0 : result + assert(opinputs.size() == 2); + assert(opoutputs.size() == 1); + + tflite::ActivationFunctionType activation; + if (auto *options = op->builtin_options_as_DivOptions()) + { + activation = options->fused_activation_function(); + } + else + { + activation = tflite::ActivationFunctionType_NONE; + } + + // TODO activation, e.g. ReLU + assert(activation == tflite::ActivationFunctionType_NONE); + + auto num_idx = opinputs.at(0); + auto denom_idx = opinputs.at(1); + auto out_idx = opoutputs.at(0); + + const tensor::Shape &num_shape = tensor_context.shape(num_idx); + const tensor::Shape &denom_shape = tensor_context.shape(denom_idx); + const tensor::Shape &out_shape = tensor_context.shape(out_idx); + + // TODO Now input/output assumes Feature map, but Div should support generic object type + // Create an object for an input + auto *num_obj = m->entity()->object()->create(); + auto *num_bag = bags.bag(num_idx); + num_obj->bag(num_bag); + num_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(num_shape))); + + auto *denom_obj = m->entity()->object()->create(); + auto *denom_bag = bags.bag(denom_idx); + denom_obj->bag(denom_bag); + denom_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(denom_shape))); + + // Create an object for an output + auto *out_obj = m->entity()->object()->create(); + auto *out_bag = bags.bag(out_idx); + out_obj->bag(out_bag); + out_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(out_shape))); + + // Create a Load ops for each input + auto coco_load_num = op_builder(m).load(num_obj).pop(); + auto coco_load_denom = op_builder(m).load(denom_obj).pop(); + + // Create a Div op + auto coco_div = m->entity()->op()->create(); + + // Link ops + coco_div->left(coco_load_num); + coco_div->right(coco_load_denom); + + // Create an Eval instruction + auto eval_ins = instr_builder(m).eval(out_obj, coco_div); + + // Append the instruction to the block + blk->instr()->append(eval_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Div.h b/compiler/enco/frontend/tflite/src/Op/Div.h new file mode 100644 index 000000000..053d1a441 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Div.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_DIV_H__ +#define __OP_DIV_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Div operator + */ +class DivGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_DIV_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp new file mode 100644 index 000000000..ee4406425 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MaxPool2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +bool MaxPool2DGraphBuilder::validate(const tflite::Operator *op) const +{ + auto const options = op->builtin_options_as_Pool2DOptions(); + + if ((options->stride_h() == 0) || (options->stride_w() == 0)) + { + return false; + } + + return true; +} + +void MaxPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + coco::Op *coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a MaxPool2D + coco::MaxPool2D *coco_maxpool2d = m->entity()->op()->create(); + const tflite::Pool2DOptions *params = op->builtin_options_as_Pool2DOptions(); + + coco_maxpool2d->window()->height(params->filter_height()); + coco_maxpool2d->window()->width(params->filter_width()); + + coco_maxpool2d->stride()->vertical(params->stride_h()); + coco_maxpool2d->stride()->horizontal(params->stride_w()); + + coco::Padding2D padding = + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + + coco_maxpool2d->pad()->top(padding.top()); + coco_maxpool2d->pad()->bottom(padding.bottom()); + coco_maxpool2d->pad()->left(padding.left()); + coco_maxpool2d->pad()->right(padding.right()); + + // Link ops + coco_maxpool2d->arg(coco_load); + + // Create an Eval instruction + coco::Eval *ins = instr_builder(m).eval(ofm_obj, coco_maxpool2d); + + // Append the instruction to the block + blk->instr()->append(ins); + + // TODO activation, e.g., relu + assert(params->fused_activation_function() == + tflite::ActivationFunctionType::ActivationFunctionType_NONE); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/MaxPool2D.h b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.h new file mode 100644 index 000000000..06a828528 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_MAXPOOL2D_H__ +#define __OP_MAXPOOL2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for AvgPool2D operator + */ +class MaxPool2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tflite::Operator *op) const override; + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_MAXPOOL2D_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Padding.cpp b/compiler/enco/frontend/tflite/src/Op/Padding.cpp new file mode 100644 index 000000000..9a0e4ef41 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Padding.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Padding.h" + +#include "Convert.h" +#include "TensorBags.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::Padding2D get_padding(const tensor::Shape &ifm_shape, const int kernel_w, const int kernel_h, + tflite::Padding padding, int stride_w, int stride_h, + int dilation_w_factor, int dilation_h_factor) +{ + assert(stride_w != 0); + assert(stride_h != 0); + assert(ifm_shape.rank() == 4); + + /** + * Compute [top padding + bottom padding] (or [left padding + right padding]). + * If this returns an even number, top = return value / 2 and bottom = return value - top + * If this returns an odd number, top = return value / 2 and bottom = return value - top (so, + * bottom = top + 1) + * + * Code based on https://www.tensorflow.org/api_guides/python/nn#Convolution + */ + auto compute_padding = [](tflite::Padding padding, int stride, int dilation_rate, int in_size, + int filter_size) { + int effective_filter_size = (filter_size - 1) * dilation_rate + 1; + if (padding == tflite::Padding_SAME) + { + if (in_size % stride == 0) + return std::max(effective_filter_size - stride, 0); + else + return std::max(effective_filter_size - (in_size % stride), 0); + } + else // padding == VALID + { + return 0; + } + }; + + // ifm shape is from order of NHWC. ifm W = dim(2), ifm H = dim(1) + int padding_w = compute_padding(padding, stride_w, dilation_w_factor, ifm_shape.dim(2), kernel_w); + int padding_h = compute_padding(padding, stride_h, dilation_h_factor, ifm_shape.dim(1), kernel_h); + + coco::Padding2D coco_padding; + coco_padding.top(padding_h / 2).bottom(padding_h - padding_h / 2); + coco_padding.left(padding_w / 2).right(padding_w - padding_w / 2); + + return coco_padding; +} + +coco::Padding2D pool2D_padding(const tflite::Pool2DOptions *options, const tensor::Shape &ifm_shape, + const int filter_w, const int filter_h) +{ + return get_padding(ifm_shape, filter_w, filter_h, options->padding(), options->stride_w(), + options->stride_h(), 1, 1); +} + +coco::Padding2D conv2D_padding(const tflite::Conv2DOptions *options, const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape) +{ + return get_padding(ifm_shape, kernel_shape.dim(2), kernel_shape.dim(1), /* kernel layout: NHWC */ + options->padding(), options->stride_w(), options->stride_h(), + options->dilation_w_factor(), options->dilation_h_factor()); +} + +coco::Padding2D depthwiseConv2D_padding(const tflite::DepthwiseConv2DOptions *options, + const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape) +{ + return get_padding(ifm_shape, kernel_shape.dim(2), kernel_shape.dim(1), /* kernel layout: NHWC */ + options->padding(), options->stride_w(), options->stride_h(), + options->dilation_w_factor(), options->dilation_h_factor()); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Padding.h b/compiler/enco/frontend/tflite/src/Op/Padding.h new file mode 100644 index 000000000..ac84adeb7 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Padding.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_PADDING_H__ +#define __OP_PADDING_H__ + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::Padding2D pool2D_padding(const tflite::Pool2DOptions *options, const tensor::Shape &ifm_shape, + const int filter_w, const int filter_h); + +coco::Padding2D conv2D_padding(const tflite::Conv2DOptions *options, const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape); + +coco::Padding2D depthwiseConv2D_padding(const tflite::DepthwiseConv2DOptions *options, + const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape); + +} // namespace tflimport + +#endif // __OP_PADDING_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/ReLU.cpp b/compiler/enco/frontend/tflite/src/Op/ReLU.cpp new file mode 100644 index 000000000..4922f4d1f --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/ReLU.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReLU.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReLUGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + auto ifm_idx = opinputs.at(0); + auto ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a ReLU + auto coco_relu = m->entity()->op()->create(); + + // Link ops + coco_relu->arg(coco_load); + + // Create an Eval instruction + auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu); + + // Append the instruction to the block + blk->instr()->append(eval_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/ReLU.h b/compiler/enco/frontend/tflite/src/Op/ReLU.h new file mode 100644 index 000000000..c78400d7e --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/ReLU.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU_H__ +#define __OP_RELU_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for ReLU operator + */ +class ReLUGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RELU_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/ReLU6.cpp b/compiler/enco/frontend/tflite/src/Op/ReLU6.cpp new file mode 100644 index 000000000..936fda3e2 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/ReLU6.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReLU6.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReLU6GraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a ReLU6 + auto coco_relu6 = m->entity()->op()->create(); + + // Link ops + coco_relu6->arg(coco_load); + + // Create an Eval instruction + auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu6); + + // Append the instruction to the block + blk->instr()->append(eval_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/ReLU6.h b/compiler/enco/frontend/tflite/src/Op/ReLU6.h new file mode 100644 index 000000000..10bcd4f71 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/ReLU6.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RELU6_H__ +#define __OP_RELU6_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for ReLU6 operator + */ +class ReLU6GraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RELU6_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Reshape.cpp b/compiler/enco/frontend/tflite/src/Op/Reshape.cpp new file mode 100644 index 000000000..9bd473fa9 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Reshape.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Reshape.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReshapeGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // input index 1 : output shape (int32_t), (optional or not, is not clear) + // output index 0 : output feature + assert(opinputs.size() == 1 || opinputs.size() == 2); + assert(opoutputs.size() == 1); + + // Note: there are actually 3 places where we can get output shape from + // current TF lite implementation. From output operand shape, second input, + // and ReshapeOption (new_shape). Here we use output operand shape + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + auto ifm_bag = bags.bag(ifm_idx); + auto ofm_bag = bags.bag(ofm_idx); + + // TODO: move to InstrBuilder as 'shuffle_elements()' + // Create a 1:1 shuffle instruction from ifm into ofm + // Note: Reshape is change of shape information and there is no value change + // in the bag itself. We implement this as just make a element wise copy of + // the bag from input to output. So there is no need of 'reshape' operator + auto shuffle_ins = m->entity()->instr()->create(); + auto num_elem = ifm_bag->size(); + + assert(num_elem == ofm_bag->size()); + + shuffle_ins->from(ifm_bag); + shuffle_ins->into(ofm_bag); + + for (uint32_t n = 0; n < num_elem; ++n) + { + const auto from = coco::ElemID(n); + const auto into = coco::ElemID(n); + + shuffle_ins->insert(from, into); + } + + // Append the instruction + blk->instr()->append(shuffle_ins); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Reshape.h b/compiler/enco/frontend/tflite/src/Op/Reshape.h new file mode 100644 index 000000000..7447b56c8 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Reshape.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_RESHAPE_H__ +#define __OP_RESHAPE_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Reshape operator + */ +class ReshapeGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RESHAPE_H__ diff --git a/compiler/enco/frontend/tflite/src/Op/Sub.cpp b/compiler/enco/frontend/tflite/src/Op/Sub.cpp new file mode 100644 index 000000000..62973bb22 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Sub.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Sub.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void SubGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : left input feature + // input index 1 : right input feature + // output index 0 : output feature + assert(opinputs.size() == 2); + assert(opoutputs.size() == 1); + + // Default parameter values are referenced from schema_generated.h + auto *params = op->builtin_options_as_SubOptions(); + tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE; + + if (auto *params = op->builtin_options_as_SubOptions()) + { + activation = params->fused_activation_function(); + } + assert(activation == tflite::ActivationFunctionType_NONE); + + // Construct a vector of input objects + std::vector input_objects; + + for (auto &input_index : opinputs) + { + // Add objects for input feature map + const tensor::Shape &input_shape = tensor_context.shape(input_index); + coco::FeatureObject *input_obj = m->entity()->object()->create(); + coco::Bag *input_bag = bags.bag(input_index); + input_obj->bag(input_bag); + input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape))); + + input_objects.emplace_back(input_obj); + } + + // Create an object for an output feature map + int const output_index = opoutputs.at(0); + const tensor::Shape &output_shape = tensor_context.shape(output_index); + coco::FeatureObject *output_obj = m->entity()->object()->create(); + coco::Bag *output_bag = bags.bag(output_index); + output_obj->bag(output_bag); + output_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(output_shape))); + + // Create Load ops + auto left_load = op_builder(m).load(input_objects[0]).pop(); + auto right_load = op_builder(m).load(input_objects[1]).pop(); + + // Create a Sub + auto coco_sub = m->entity()->op()->create(); + + coco_sub->left(left_load); + coco_sub->right(right_load); + + // Create an Eval instruction + auto eval = instr_builder(m).eval(output_obj, coco_sub); + + // Append the instruction to the block + blk->instr()->append(eval); + + // TODO activation, e.g., relu + assert(params->fused_activation_function() == + tflite::ActivationFunctionType::ActivationFunctionType_NONE); +} + +} // namespace tflimport diff --git a/compiler/enco/frontend/tflite/src/Op/Sub.h b/compiler/enco/frontend/tflite/src/Op/Sub.h new file mode 100644 index 000000000..580d8baa3 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/Op/Sub.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_SUB_H__ +#define __OP_SUB_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Sub operator + */ +class SubGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_SUB_H__ diff --git a/compiler/enco/frontend/tflite/src/RawModel.h b/compiler/enco/frontend/tflite/src/RawModel.h new file mode 100644 index 000000000..02946f1d7 --- /dev/null +++ b/compiler/enco/frontend/tflite/src/RawModel.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RAW_MODEL_H__ +#define __RAW_MODEL_H__ + +#include "schema_generated.h" + +struct RawModel +{ + virtual ~RawModel() = default; + + virtual const tflite::Model *model(void) const = 0; +}; + +#endif // __RAW_MODEL_H__ diff --git a/compiler/enco/frontend/tflite/src/RawModelLoader.cpp b/compiler/enco/frontend/tflite/src/RawModelLoader.cpp new file mode 100644 index 000000000..5c127f37c --- /dev/null +++ b/compiler/enco/frontend/tflite/src/RawModelLoader.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RawModelLoader.h" + +#include "cwrap/Fildes.h" + +#include +#include +#include +#include + +namespace +{ + +class MemoryMappedRawModel final : public RawModel +{ +public: + /** + * @require fd and data SHOULD be valid + */ + explicit MemoryMappedRawModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} + { + // DO NOTHING + } + +public: + ~MemoryMappedRawModel() + { + munmap(_data, _size); + close(_fd); + } + +public: + MemoryMappedRawModel(const MemoryMappedRawModel &) = delete; + MemoryMappedRawModel(MemoryMappedRawModel &&) = delete; + +public: + const tflite::Model *model(void) const override { return tflite::GetModel(_data); } + +private: + int _fd = -1; + void *_data = nullptr; + size_t _size = 0; +}; + +} // namespace + +std::unique_ptr load_from(const std::string &path) +{ + cwrap::Fildes fildes{open(path.c_str(), O_RDONLY)}; + + if (fildes.get() == -1) + { + // Return nullptr on open failure + return nullptr; + } + + struct stat st; + if (fstat(fildes.get(), &st) == -1) + { + // Return nullptr on fstat failure + return nullptr; + } + + auto size = st.st_size; + auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fildes.get(), 0); + + if (data == MAP_FAILED) + { + // Return nullptr on mmap failure + return nullptr; + } + + return std::unique_ptr{new MemoryMappedRawModel(fildes.release(), data, size)}; +} diff --git a/compiler/enco/frontend/tflite/src/RawModelLoader.h b/compiler/enco/frontend/tflite/src/RawModelLoader.h new file mode 100644 index 000000000..5d93528de --- /dev/null +++ b/compiler/enco/frontend/tflite/src/RawModelLoader.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RAW_MODEL_LOADER_H__ +#define __RAW_MODEL_LOADER_H__ + +#include "RawModel.h" + +/** + * @brief Load TensorFlow Lite model (as a RawModel) from a given path + * + * @note May return a nullptr + */ +std::unique_ptr load_from(const std::string &path); + +#endif // __RAW_MODEL_LOADER_H__ diff --git a/compiler/enco/frontend/tflite/src/TensorBags.h b/compiler/enco/frontend/tflite/src/TensorBags.h new file mode 100644 index 000000000..29558b85e --- /dev/null +++ b/compiler/enco/frontend/tflite/src/TensorBags.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TENSOR_BAGS_H__ +#define __TENSOR_BAGS_H__ + +#include "Convert.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +/** + * @brief Pre-creates coco:Bags for each operands(tensors) + */ +class TensorBags +{ +public: + void prepare(const tflite::SubGraph *graph, std::unique_ptr &m) + { + for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) + { + auto const tensor_info = graph->tensors()->Get(tensor_id); + auto const tensor_shape = as_tensor_shape(tensor_info->shape()); + auto const tensor_bag = m->entity()->bag()->create(num_elements(tensor_shape)); + + _bag_ctx[tensor_id] = tensor_bag; + } + } + + coco::Bag *bag(int32_t tensor_id) { return _bag_ctx[tensor_id]; } + +public: + std::map::iterator begin() { return _bag_ctx.begin(); } + + std::map::iterator end() { return _bag_ctx.end(); } + +private: + std::map _bag_ctx; +}; + +} // namespace tflimport + +#endif // __TENSOR_BAGS_H__ diff --git a/compiler/enco/requires.cmake b/compiler/enco/requires.cmake new file mode 100644 index 000000000..fee0e18e5 --- /dev/null +++ b/compiler/enco/requires.cmake @@ -0,0 +1,8 @@ +require("coco") +require("caffegen") +require("tflchef") +require("ann-api") +require("ann-ref") +require("nnkit") +require("cwrap") +require("enco-intf") diff --git a/compiler/enco/test/CMakeLists.txt b/compiler/enco/test/CMakeLists.txt new file mode 100644 index 000000000..5ea6cdadd --- /dev/null +++ b/compiler/enco/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectories() diff --git a/compiler/enco/test/basic/000/CMakeLists.txt b/compiler/enco/test/basic/000/CMakeLists.txt new file mode 100644 index 000000000..20ba3c571 --- /dev/null +++ b/compiler/enco/test/basic/000/CMakeLists.txt @@ -0,0 +1,26 @@ +### +### This test first generates C++ code from an empty model, and check whether is has compile error +### +set(PREFIX enco-basic-test-000) +set(GENERATED_CPP ${PREFIX}.cpp) +set(GENERATED_ASM ${PREFIX}.embed.S) +set(GENERATED_BIN ${PREFIX}.bin) +set(SOURCE_TARGET ${PREFIX}-src) +set(LIB_TARGET ${PREFIX}-lib) + +add_library(${PREFIX}-frontend SHARED enco.test.cpp) +target_link_libraries(${PREFIX}-frontend enco_intf_cmdline) +target_link_libraries(${PREFIX}-frontend enco_intf_frontend) +target_link_libraries(${PREFIX}-frontend stdex) + +# NOTE BYPRODUCTS are not specified in order to enforce source code generation +add_custom_command(OUTPUT ${GENERATED_CPP} ${GENERATED_ASM} ${GENERATED_BIN} + COMMAND $ + --frontend $ + --backend-arg ${PREFIX} + DEPENDS enco-cli ${PREFIX}-frontend) +set_source_files_properties(${GENERATED_ASM} PROPERTIES GENERATED TRUE LANGUAGE C) +add_library(${LIB_TARGET} SHARED ${GENERATED_CPP} ${GENERATED_ASM}) +# NOTE This line is necessary to compile the generated assembly (it includes the generated bin file) +target_include_directories(${LIB_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(${LIB_TARGET} PRIVATE ann_api) diff --git a/compiler/enco/test/basic/000/enco.test.cpp b/compiler/enco/test/basic/000/enco.test.cpp new file mode 100644 index 000000000..3dbf96613 --- /dev/null +++ b/compiler/enco/test/basic/000/enco.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +namespace +{ + +// +// Dummy frontend for testing +// +struct Frontend final : public enco::Frontend +{ + enco::Bundle load(void) const override + { + auto m = coco::Module::create(); + auto d = coco::Data::create(); + + // Create an input + { + const tensor::Shape shape{1, 3, 3, 1}; + + auto bag = m->entity()->bag()->create(9); + auto input = m->entity()->input()->create(shape); + + input->bag(bag); + input->name("input"); + input->reorder(); + + m->input()->insert(input); + } + + // Create an output + { + const tensor::Shape shape{1, 3, 3, 1}; + + auto bag = m->entity()->bag()->create(9); + auto output = m->entity()->output()->create(shape); + + output->bag(bag); + output->name("output"); + output->reorder(); + + m->output()->insert(output); + } + + enco::Bundle bundle; + + bundle.module(std::move(m)); + bundle.data(std::move(d)); + + return std::move(bundle); + } +}; + +} // namespace + +extern "C" std::unique_ptr make_frontend(const cmdline::View &cmdline) +{ + return stdex::make_unique(); +} diff --git a/compiler/enco/test/basic/CMakeLists.txt b/compiler/enco/test/basic/CMakeLists.txt new file mode 100644 index 000000000..5ea6cdadd --- /dev/null +++ b/compiler/enco/test/basic/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectories() diff --git a/compiler/enco/test/binder.cpp b/compiler/enco/test/binder.cpp new file mode 100644 index 000000000..c8c72fc8b --- /dev/null +++ b/compiler/enco/test/binder.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Generated API +// +struct Network; + +Network *Network_construct(); +void Network_destruct(Network *net); + +unsigned Network_input_count(const Network *); +const char *Network_input_name(const Network *, unsigned n); +unsigned Network_input_rank(const Network *, unsigned n); +unsigned Network_input_dim(const Network *, unsigned n, unsigned axis); +void Network_input_bind(Network *net, unsigned n, const void *ptr, unsigned len); + +unsigned Network_output_count(const Network *net); +const char *Network_output_name(const Network *, unsigned n); +unsigned Network_output_rank(const Network *, unsigned n); +unsigned Network_output_dim(const Network *, unsigned n, unsigned axis); +void Network_output_bind(Network *net, unsigned n, void *ptr, unsigned len); + +void Network_invoke(Network *net); + +// +// nnkit backend +// +#include +#include +#include + +#include +#include + +#include + +using stdex::make_unique; +using namespace nncc::core::ADT; + +namespace +{ + +class TensorContext final : public nnkit::TensorContext +{ +public: + TensorContext() = default; + +public: + void allocate(const std::string &name, const tensor::Shape &shape) + { + using nncc::core::ADT::tensor::num_elements; + + auto blob = make_unique>(); + blob->resize(num_elements(shape) * sizeof(float)); + + _names.emplace_back(name); + _shapes.emplace_back(shape); + _blobs.emplace_back(std::move(blob)); + } + +public: + uint8_t *base(uint32_t n) const { return _blobs.at(n)->data(); } + +public: + uint32_t size(void) const override { return _blobs.size(); } + +public: + std::string name(uint32_t n) const override { return _names.at(n); } + +public: + tensor::Shape shape(uint32_t n) const override { return _shapes.at(n); } + +public: + uint32_t size(uint32_t n) const { return _blobs.at(n)->size(); } + +public: + // Float (fp32) tensor support + bool isFloatTensor(uint32_t n) const override { return true; } + void getMutableFloatTensor(uint32_t n, const TensorContext::TypedAccessor &f) override + { + using nncc::core::ADT::tensor::LexicalLayout; + using nncc::core::ADT::tensor::make_overlay; + + auto base = reinterpret_cast(this->base(n)); + auto view = make_overlay(shape(n), base); + + f(*this, n, view); + } + + void getConstFloatTensor(uint32_t n, const TensorContext::TypedReader &f) const override + { + using nncc::core::ADT::tensor::LexicalLayout; + using nncc::core::ADT::tensor::make_overlay; + + auto base = reinterpret_cast(this->base(n)); + auto view = make_overlay(shape(n), base); + + f(*this, n, view); + } + +private: + std::vector _names; + std::vector _shapes; + std::vector>> _blobs; +}; + +class Backend final : public nnkit::Backend +{ +public: + Backend() + { + _net = Network_construct(); + + // Allocate and bind inputs + for (uint32_t n = 0; n < Network_input_count(_net); ++n) + { + const uint32_t rank = Network_input_rank(_net, n); + const std::string name = Network_input_name(_net, n); + + tensor::Shape shape; + + shape.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + shape.dim(axis) = Network_input_dim(_net, n, axis); + } + + _inputs.allocate(name, shape); + + Network_input_bind(_net, n, reinterpret_cast(_inputs.base(n)), _inputs.size(n)); + } + + // Allocate and bind outputs + for (uint32_t n = 0; n < Network_output_count(_net); ++n) + { + const uint32_t rank = Network_output_rank(_net, n); + const std::string name = Network_output_name(_net, n); + + tensor::Shape shape; + + shape.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + shape.dim(axis) = Network_output_dim(_net, n, axis); + } + + _outputs.allocate(name, shape); + + Network_output_bind(_net, n, reinterpret_cast(_outputs.base(n)), _outputs.size(n)); + } + } + +public: + ~Backend() { Network_destruct(_net); } + +public: + void prepare(const std::function &f) override { f(_inputs); } + void run(void) override { Network_invoke(_net); } + void teardown(const std::function &f) override { f(_outputs); } + +private: + Network *_net; + +private: + TensorContext _inputs; + TensorContext _outputs; +}; + +} // namespace + +extern "C" std::unique_ptr make_backend(const nnkit::CmdlineArguments &args) +{ + return make_unique<::Backend>(); +} diff --git a/compiler/enco/test/caffe/CMakeLists.txt b/compiler/enco/test/caffe/CMakeLists.txt new file mode 100644 index 000000000..ee49b6b28 --- /dev/null +++ b/compiler/enco/test/caffe/CMakeLists.txt @@ -0,0 +1,141 @@ +option(ENCO_CAFFE_TEST "Enable enco test for caffe" ON) + +if(NOT ENCO_CAFFE_TEST) + return() +endif(NOT ENCO_CAFFE_TEST) + +# TODO Use REQUIRED if supported +nncc_find_resource(BVLCCaffeTests) + +if(NOT BVLCCaffeTests_FOUND) + message(FATAL_ERROR "Fail to find BVLCCaffeTests") +endif(NOT BVLCCaffeTests_FOUND) + +# TESTCASE_BASE_DIR indicates where all the testcases are located +set(TESTCASE_BASE_DIR "${BVLCCaffeTests_DIR}") + +### +### Common function(s) +### +function(get_test_configuration PREFIX) + set(PROTOTXT_FILE "${PREFIX}.prototxt") + set(PROTOTXT_FILE "${PROTOTXT_FILE}" PARENT_SCOPE) + set(PROTOTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PROTOTXT_FILE}" PARENT_SCOPE) + set(CAFFEMODEL_FILE "${PREFIX}.caffemodel") + set(CAFFEMODEL_FILE "${CAFFEMODEL_FILE}" PARENT_SCOPE) + set(CAFFEMODEL_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CAFFEMODEL_FILE}" PARENT_SCOPE) + set(SOURCE_FILE ${PREFIX}.cpp) + set(SOURCE_FILE "${SOURCE_FILE}" PARENT_SCOPE) + set(SOURCE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_FILE}" PARENT_SCOPE) + set(ASM_FILE ${PREFIX}.embed.S) + set(ASM_FILE "${ASM_FILE}" PARENT_SCOPE) + set(ASM_PATH "${CMAKE_CURRENT_BINARY_DIR}/${ASM_FILE}" PARENT_SCOPE) + set(BIN_FILE ${PREFIX}.bin) + set(BIN_FILE "${BIN_FILE}" PARENT_SCOPE) + set(BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BIN_FILE}" PARENT_SCOPE) +endfunction(get_test_configuration) + +### +### Prepare test(s) +### +if(NOT TARGET caffegen) + return() +endif(NOT TARGET caffegen) + +if(NOT TARGET enco_caffe_frontend) + return() +endif(NOT TARGET enco_caffe_frontend) + +# TODO Use "whitelist" instead +# +# WHY? +# +# Tests are now shared by multiple frameworks (not private), and thus +# some tests may be unsupported. +# +file(GLOB MODELS RELATIVE "${TESTCASE_BASE_DIR}" "${TESTCASE_BASE_DIR}/*/test.prototxt") + +foreach(MODEL IN ITEMS ${MODELS}) + get_filename_component(PREFIX ${MODEL} DIRECTORY) + get_test_configuration(${PREFIX}) + + set(MODEL_FILE ${TESTCASE_BASE_DIR}/${MODEL}) + + # Copy prototxt + # TODO Fix indentation + add_custom_command(OUTPUT ${PROTOTXT_PATH} + COMMAND ${CMAKE_COMMAND} -E copy "${MODEL_FILE}" "${PROTOTXT_PATH}" + DEPENDS "${MODEL_FILE}" + COMMENT "Generating ${PROTOTXT_FILE}") + + # Generate caffemodel + # TODO Fix indentation + add_custom_command(OUTPUT ${CAFFEMODEL_PATH} + COMMAND cat ${PROTOTXT_PATH} + | GLOG_minloglevel=2 $ init + | GLOG_minloglevel=2 $ encode + > ${CAFFEMODEL_PATH} + DEPENDS caffegen ${PROTOTXT_PATH} + COMMENT "Generating ${CAFFEMODEL_FILE}") + + # Generate C++ code + # TODO Fix indentation + add_custom_command(OUTPUT ${SOURCE_PATH} ${ASM_PATH} ${BIN_PATH} + COMMAND $ + --frontend $ + --frontend-arg ${PROTOTXT_FILE} + --frontend-arg ${CAFFEMODEL_FILE} + --backend-arg ${PREFIX} + DEPENDS enco-cli enco_caffe_frontend ${CAFFEMODEL_PATH} + COMMENT "Generating ${SOURCE_FILE}") + set_source_files_properties(${ASM_PATH} PROPERTIES GENERATED TRUE LANGUAGE C) + + list(APPEND CANDIDATES ${PREFIX}) +endforeach(MODEL) + +### +### Inference test +### +if(NOT TARGET ann_ref_static) + return() +endif(NOT TARGET ann_ref_static) + +find_program(H5DIFF h5diff) + +if (NOT H5DIFF) + return() +endif(NOT H5DIFF) + +message(STATUS "Enable enco(caffe) inference test") + +foreach(PREFIX IN ITEMS ${CANDIDATES}) + if(NOT EXISTS "${TESTCASE_BASE_DIR}/${PREFIX}/INFERENCE") + continue() + endif() + + get_test_configuration(${PREFIX}) + + set(BINDER_TARGET enco_caffe_test_${PREFIX}_binder) + + # Compile nnkit binder (from generated C++ code) + add_library(${BINDER_TARGET} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../binder.cpp ${SOURCE_PATH} ${ASM_PATH}) + target_include_directories(${BINDER_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(${BINDER_TARGET} nnkit_intf_backend) + target_link_libraries(${BINDER_TARGET} ann_api) + target_link_libraries(${BINDER_TARGET} ann_ref_static) + target_link_libraries(${BINDER_TARGET} stdex) + set_target_properties(${BINDER_TARGET} PROPERTIES OUTPUT_NAME ${PREFIX}) + + list(APPEND TESTS ${PREFIX}) +endforeach(PREFIX) + +# Run tests +add_test(NAME enco_test_caffe + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runall.sh" + $ + $ + $ + $ + $ + "${CMAKE_CURRENT_BINARY_DIR}" + ${TESTS}) diff --git a/compiler/enco/test/caffe/runall.sh b/compiler/enco/test/caffe/runall.sh new file mode 100755 index 000000000..3b18f1c6b --- /dev/null +++ b/compiler/enco/test/caffe/runall.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +if [[ $# -le 6 ]]; then + echo "USAGE: $0 [nnkit-run path] [reference backend path] [randomize action path] [HDF5 export action path] [HDF5 import action path] [WORKDIR] [Prefix1] [Prefix2]..." + exit 255 +fi + +NNKIT_RUN_PATH="$1"; shift +REFERENCE_BACKEND_PATH="$1"; shift +RANDOMIZE_ACTION_PATH="$1"; shift +HDF5_EXPORT_ACTION_PATH="$1"; shift +HDF5_IMPORT_ACTION_PATH="$1"; shift +WORKDIR="$1"; shift + +echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}" +echo "-- Found reference backend: ${REFERENCE_BACKEND_PATH}" +echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}" +echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}" +echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}" +echo "-- Found workdir: ${WORKDIR}" + +TESTED=() +PASSED=() +FAILED=() + +pushd "${WORKDIR}" +while [[ $# -ne 0 ]]; do + PREFIX="$1"; shift + + TESTED+=("${PREFIX}") + + PASSED_TAG="${PREFIX}.passed" + + rm -f "${PASSED_TAG}" + + cat > "${PREFIX}.log" <( + exec 2>&1 + + echo "-- Found prototxt: ${PREFIX}.prototxt" + echo "-- Found caffemodel: ${PREFIX}.caffemodel" + echo "-- Found backend: lib${PREFIX}.so" + + "${NNKIT_RUN_PATH}" \ + --backend "${REFERENCE_BACKEND_PATH}" \ + --backend-arg "${WORKDIR}/${PREFIX}.prototxt" \ + --backend-arg "${WORKDIR}/${PREFIX}.caffemodel" \ + --pre "${RANDOMIZE_ACTION_PATH}" \ + --pre "${HDF5_EXPORT_ACTION_PATH}" \ + --pre-arg "${PREFIX}.input.h5" \ + --post "${HDF5_EXPORT_ACTION_PATH}" \ + --post-arg "${PREFIX}.expected.h5" + + "${NNKIT_RUN_PATH}" \ + --backend "./lib${PREFIX}.so" \ + --pre "${HDF5_IMPORT_ACTION_PATH}" \ + --pre-arg "${PREFIX}.input.h5" \ + --post "${HDF5_EXPORT_ACTION_PATH}" \ + --post-arg "${PREFIX}.obtained.h5" + + h5diff -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("$PREFIX") + else + FAILED+=("$PREFIX") + fi +done +popd + +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/enco/test/tflite/AveragePool2D_000/INFERENCE b/compiler/enco/test/tflite/AveragePool2D_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/AveragePool2D_000/test.recipe b/compiler/enco/test/tflite/AveragePool2D_000/test.recipe new file mode 100644 index 000000000..746c34334 --- /dev/null +++ b/compiler/enco/test/tflite/AveragePool2D_000/test.recipe @@ -0,0 +1,24 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 7 dim: 7 dim: 1 } +} +operation { + type: "AveragePool2D" + averagepool2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + filter_width: 2 + filter_height: 2 + } + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/AveragePool2D_001/INFERENCE b/compiler/enco/test/tflite/AveragePool2D_001/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/AveragePool2D_001/test.recipe b/compiler/enco/test/tflite/AveragePool2D_001/test.recipe new file mode 100644 index 000000000..36bbda78c --- /dev/null +++ b/compiler/enco/test/tflite/AveragePool2D_001/test.recipe @@ -0,0 +1,24 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 5 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 5 } +} +operation { + type: "AveragePool2D" + averagepool2d_options { + padding: SAME + stride_w: 1 + stride_h: 1 + filter_width: 3 + filter_height: 3 + } + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/CMakeLists.txt b/compiler/enco/test/tflite/CMakeLists.txt new file mode 100644 index 000000000..d5a96a6da --- /dev/null +++ b/compiler/enco/test/tflite/CMakeLists.txt @@ -0,0 +1,108 @@ +option(ENCO_TFLITE_TEST "Enable enco test for TFLite" ON) + +if(NOT ENCO_TFLITE_TEST) + return() +endif(NOT ENCO_TFLITE_TEST) + +### +### Common function(s) +### +function(get_test_configuration PREFIX) + set(RECIPE_FILE "${PREFIX}.recipe" PARENT_SCOPE) + set(TFLITEMODEL_FILE "${PREFIX}.tflite" PARENT_SCOPE) + set(SOURCE_FILE ${PREFIX}.cpp PARENT_SCOPE) + set(ASM_FILE ${PREFIX}.embed.S PARENT_SCOPE) + set(BIN_FILE ${PREFIX}.bin PARENT_SCOPE) +endfunction(get_test_configuration) + +### +### Prepare test(s) +### +if(NOT TARGET tflchef-file) + return() +endif(NOT TARGET tflchef-file) + +if(NOT TARGET enco_tflite_frontend) + return() +endif(NOT TARGET enco_tflite_frontend) + +file(GLOB MODELS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe") + +foreach(MODEL IN ITEMS ${MODELS}) + get_filename_component(PREFIX ${MODEL} DIRECTORY) + get_test_configuration(${PREFIX}) + + set(MODEL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MODEL}) + + # Copy recipe + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${RECIPE_FILE} + COMMAND ${CMAKE_COMMAND} -E copy "${MODEL_FILE}" + "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE_FILE}" + DEPENDS "${MODEL_FILE}" + COMMENT "Copying ${RECIPE_FILE}") + + # Generate tflitemodel + add_custom_command(OUTPUT ${TFLITEMODEL_FILE} + COMMAND $ ${RECIPE_FILE} ${TFLITEMODEL_FILE} + DEPENDS tflchef ${CMAKE_CURRENT_BINARY_DIR}/${RECIPE_FILE} + COMMENT "Generating ${TFLITEMODEL_FILE}") + + # Generate C++ code + add_custom_command(OUTPUT ${SOURCE_FILE} ${ASM_FILE} ${BIN_FILE} + COMMAND $ + --frontend $ + --frontend-arg ${TFLITEMODEL_FILE} + --backend-arg ${PREFIX} + DEPENDS enco-cli enco_caffe_frontend ${TFLITEMODEL_FILE} + COMMENT "Generating ${SOURCE_FILE}") + set_source_files_properties(${ASM_FILE} PROPERTIES GENERATED TRUE LANGUAGE C) + + list(APPEND CANDIDATES ${PREFIX}) +endforeach(MODEL) + +### +### Inference test +### +if(NOT TARGET ann_ref_static) + return() +endif(NOT TARGET ann_ref_static) + +find_program(H5DIFF h5diff) + +if (NOT H5DIFF) + return() +endif(NOT H5DIFF) + +message(STATUS "Enable enco(tflite) inference test") + +foreach(PREFIX IN ITEMS ${CANDIDATES}) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PREFIX}/INFERENCE") + continue() + endif() + + get_test_configuration(${PREFIX}) + + set(BINDER_TARGET enco_tflite_test_${PREFIX}_binder) + + # Compile nnkit binder (from generated C++ code) + add_library(${BINDER_TARGET} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../binder.cpp ${SOURCE_FILE} ${ASM_FILE}) + target_include_directories(${BINDER_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(${BINDER_TARGET} nnkit_intf_backend) + target_link_libraries(${BINDER_TARGET} ann_api) + target_link_libraries(${BINDER_TARGET} ann_ref_static) + target_link_libraries(${BINDER_TARGET} stdex) + set_target_properties(${BINDER_TARGET} PROPERTIES OUTPUT_NAME ${PREFIX}) + + list(APPEND TESTS ${PREFIX}) +endforeach(PREFIX) + +# Run tests +add_test(NAME enco_test_tflite + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runall.sh" + $ + $ + $ + $ + $ + "${CMAKE_CURRENT_BINARY_DIR}" + ${TESTS}) diff --git a/compiler/enco/test/tflite/Concat_000/INFERENCE b/compiler/enco/test/tflite/Concat_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Concat_000/test.recipe b/compiler/enco/test/tflite/Concat_000/test.recipe new file mode 100644 index 000000000..35641bd07 --- /dev/null +++ b/compiler/enco/test/tflite/Concat_000/test.recipe @@ -0,0 +1,28 @@ +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 1 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 2 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operation { + type: "Concatenation" + concatenation_options { + axis: 3 + activation: NONE + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/compiler/enco/test/tflite/Concat_001/INFERENCE b/compiler/enco/test/tflite/Concat_001/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Concat_001/test.recipe b/compiler/enco/test/tflite/Concat_001/test.recipe new file mode 100644 index 000000000..7adaf1645 --- /dev/null +++ b/compiler/enco/test/tflite/Concat_001/test.recipe @@ -0,0 +1,29 @@ +# Concatenate two feature maps along "width" dimension +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 1 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 2 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 3 dim: 1 } +} +operation { + type: "Concatenation" + concatenation_options { + axis: 2 + activation: NONE + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/compiler/enco/test/tflite/Concat_002/INFERENCE b/compiler/enco/test/tflite/Concat_002/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Concat_002/test.recipe b/compiler/enco/test/tflite/Concat_002/test.recipe new file mode 100644 index 000000000..918cb13d3 --- /dev/null +++ b/compiler/enco/test/tflite/Concat_002/test.recipe @@ -0,0 +1,29 @@ +# Concatenate two feature maps along "height" dimension +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 1 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 2 dim: 1 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 1 dim: 1 } +} +operation { + type: "Concatenation" + concatenation_options { + axis: 1 + activation: NONE + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/compiler/enco/test/tflite/Concat_003/INFERENCE b/compiler/enco/test/tflite/Concat_003/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Concat_003/test.recipe b/compiler/enco/test/tflite/Concat_003/test.recipe new file mode 100644 index 000000000..8f1b64ea6 --- /dev/null +++ b/compiler/enco/test/tflite/Concat_003/test.recipe @@ -0,0 +1,29 @@ +# Concatenate two feature maps along "batch" dimension +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 1 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 2 dim: 1 dim: 1 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 3 dim: 1 dim: 1 dim: 1 } +} +operation { + type: "Concatenation" + concatenation_options { + axis: 0 + activation: NONE + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/compiler/enco/test/tflite/Conv2D_000/INFERENCE b/compiler/enco/test/tflite/Conv2D_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Conv2D_000/test.recipe b/compiler/enco/test/tflite/Conv2D_000/test.recipe new file mode 100644 index 000000000..9f0841819 --- /dev/null +++ b/compiler/enco/test/tflite/Conv2D_000/test.recipe @@ -0,0 +1,45 @@ +# Test for basic case: VALID padding, no activation layer, stride=[1,1] +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Conv2D_001/INFERENCE b/compiler/enco/test/tflite/Conv2D_001/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Conv2D_001/test.recipe b/compiler/enco/test/tflite/Conv2D_001/test.recipe new file mode 100644 index 000000000..d9d4904da --- /dev/null +++ b/compiler/enco/test/tflite/Conv2D_001/test.recipe @@ -0,0 +1,45 @@ +# Test for SAME padding +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 5 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 5 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: SAME + stride_w: 1 + stride_h: 1 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Conv2D_002/INFERENCE b/compiler/enco/test/tflite/Conv2D_002/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Conv2D_002/test.recipe b/compiler/enco/test/tflite/Conv2D_002/test.recipe new file mode 100644 index 000000000..55976c9b9 --- /dev/null +++ b/compiler/enco/test/tflite/Conv2D_002/test.recipe @@ -0,0 +1,46 @@ +# Test for RELU activation layer +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + activation: RELU + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Conv2D_003/INFERENCE b/compiler/enco/test/tflite/Conv2D_003/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Conv2D_003/test.recipe b/compiler/enco/test/tflite/Conv2D_003/test.recipe new file mode 100644 index 000000000..30c9473b7 --- /dev/null +++ b/compiler/enco/test/tflite/Conv2D_003/test.recipe @@ -0,0 +1,45 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + activation: RELU6 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Conv2D_004/INFERENCE b/compiler/enco/test/tflite/Conv2D_004/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Conv2D_004/test.recipe b/compiler/enco/test/tflite/Conv2D_004/test.recipe new file mode 100644 index 000000000..20f4a9908 --- /dev/null +++ b/compiler/enco/test/tflite/Conv2D_004/test.recipe @@ -0,0 +1,45 @@ +# Conv2D with ifm w, h = 14, 14 && ofm w, h = 7, 7 && stride = 2, 2 && padding = SAME (similar case from Mobile) +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 14 dim: 14 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 7 dim: 7 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: SAME + stride_w: 2 + stride_h: 2 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/DepthwiseConv2D_000/INFERENCE b/compiler/enco/test/tflite/DepthwiseConv2D_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/DepthwiseConv2D_000/test.recipe b/compiler/enco/test/tflite/DepthwiseConv2D_000/test.recipe new file mode 100644 index 000000000..27bc767fc --- /dev/null +++ b/compiler/enco/test/tflite/DepthwiseConv2D_000/test.recipe @@ -0,0 +1,48 @@ +# SAME padding, stride = [1,1], activation=RELU6. +# In mobilenet, there are two cases using depthwiseConv2D : A case like this one, and another case with stride=[2,2] +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 5 dim: 5 dim: 4 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 4 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 4 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 5 dim: 5 dim: 4 } +} +operation { + type: "DepthwiseConv2D" + depthwiseconv2d_options { + padding: SAME + stride_w: 1 + stride_h: 1 + depth_multiplier: 1 + activation: RELU6 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/DepthwiseConv2D_001/INFERENCE b/compiler/enco/test/tflite/DepthwiseConv2D_001/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/DepthwiseConv2D_001/test.recipe b/compiler/enco/test/tflite/DepthwiseConv2D_001/test.recipe new file mode 100644 index 000000000..0166474d8 --- /dev/null +++ b/compiler/enco/test/tflite/DepthwiseConv2D_001/test.recipe @@ -0,0 +1,46 @@ +# depthwiseConv2D with ifm w, h = 14, 14 && ofm w, h = 7, 7 && stride = 2, 2 && padding = SAME (similar case from Mobile) +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 14 dim: 14 dim: 5 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 5 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 5 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 7 dim: 7 dim: 5 } +} +operation { + type: "DepthwiseConv2D" + depthwiseconv2d_options { + padding: SAME + stride_w: 2 + stride_h: 2 + activation: RELU6 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Div_000/INFERENCE b/compiler/enco/test/tflite/Div_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Div_000/test.recipe b/compiler/enco/test/tflite/Div_000/test.recipe new file mode 100644 index 000000000..a6335de46 --- /dev/null +++ b/compiler/enco/test/tflite/Div_000/test.recipe @@ -0,0 +1,27 @@ +operand { + name: "ifm0" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operation { + type: "Div" + input: "ifm0" + input: "ifm1" + output: "ofm" + div_options { + activation: NONE + } +} +input: "ifm0" +input: "ifm1" +output: "ofm" diff --git a/compiler/enco/test/tflite/MaxPool2D_000/INFERENCE b/compiler/enco/test/tflite/MaxPool2D_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/MaxPool2D_000/test.recipe b/compiler/enco/test/tflite/MaxPool2D_000/test.recipe new file mode 100644 index 000000000..718630f08 --- /dev/null +++ b/compiler/enco/test/tflite/MaxPool2D_000/test.recipe @@ -0,0 +1,24 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 7 dim: 7 dim: 1 } +} +operation { + type: "MaxPool2D" + maxpool2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + filter_width: 2 + filter_height: 2 + } + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/ReLU6_000/INFERENCE b/compiler/enco/test/tflite/ReLU6_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/ReLU6_000/test.recipe b/compiler/enco/test/tflite/ReLU6_000/test.recipe new file mode 100644 index 000000000..226593593 --- /dev/null +++ b/compiler/enco/test/tflite/ReLU6_000/test.recipe @@ -0,0 +1,17 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operation { + type: "ReLU6" + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/ReLU_000/INFERENCE b/compiler/enco/test/tflite/ReLU_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/ReLU_000/test.recipe b/compiler/enco/test/tflite/ReLU_000/test.recipe new file mode 100644 index 000000000..8eaa3602f --- /dev/null +++ b/compiler/enco/test/tflite/ReLU_000/test.recipe @@ -0,0 +1,17 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operation { + type: "ReLU" + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Regression_0000/INFERENCE b/compiler/enco/test/tflite/Regression_0000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Regression_0000/test.recipe b/compiler/enco/test/tflite/Regression_0000/test.recipe new file mode 100644 index 000000000..2f3c03670 --- /dev/null +++ b/compiler/enco/test/tflite/Regression_0000/test.recipe @@ -0,0 +1,84 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 2 } +} +operand { + name: "ker_0" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias_0" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "0.1" + } +} +operand { + name: "ofm_0" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + activation: NONE + } + input: "ifm" + input: "ker_0" + input: "bias_0" + output: "ofm_0" +} +operand { + name: "ker_1" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias_1" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "0.1" + } +} +operand { + name: "ofm_1" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + activation: NONE + } + input: "ifm" + input: "ker_1" + input: "bias_1" + output: "ofm_1" +} +input: "ifm" +output: "ofm_0" +output: "ofm_1" diff --git a/compiler/enco/test/tflite/Regression_0001/INFERENCE b/compiler/enco/test/tflite/Regression_0001/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Regression_0001/test.recipe b/compiler/enco/test/tflite/Regression_0001/test.recipe new file mode 100644 index 000000000..e6f4eca8f --- /dev/null +++ b/compiler/enco/test/tflite/Regression_0001/test.recipe @@ -0,0 +1,50 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { tag: "gaussian" arg: "0.0" arg: "1.0" } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { tag: "gaussian" arg: "0.0" arg: "1.0" } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operand { + name: "arr" + type: FLOAT32 + shape { dim: 1 dim: 9 } +} +operand { + name: "shape" + type: INT32 + shape { dim: 2 } + filler { tag: "explicit" arg: "-1" arg: "9" } +} +operation { + type: "Conv2D" + conv2d_options { padding: VALID stride_w: 1 stride_h: 1 activation: RELU6 } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +operation { + type: "Reshape" + input: "ofm" + input: "shape" + output: "arr" + reshape_options { new_shape: [-1, 9] } +} +input: "ifm" +output: "arr" diff --git a/compiler/enco/test/tflite/Regression_0002/INFERENCE b/compiler/enco/test/tflite/Regression_0002/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Regression_0002/test.recipe b/compiler/enco/test/tflite/Regression_0002/test.recipe new file mode 100644 index 000000000..8234c7996 --- /dev/null +++ b/compiler/enco/test/tflite/Regression_0002/test.recipe @@ -0,0 +1,45 @@ +# Compilation SHOULD NOT fail even when there is no effective calcualtion +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "0.1" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 8 dim: 8 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + activation: NONE + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" diff --git a/compiler/enco/test/tflite/Regression_0003/INFERENCE b/compiler/enco/test/tflite/Regression_0003/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Regression_0003/test.recipe b/compiler/enco/test/tflite/Regression_0003/test.recipe new file mode 100644 index 000000000..693c45543 --- /dev/null +++ b/compiler/enco/test/tflite/Regression_0003/test.recipe @@ -0,0 +1,33 @@ +# Compilation SHOULD NOT fail even if all the inputs are constant +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } + filler { tag: "constant" arg: "0.1" } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { tag: "constant" arg: "0.2" } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { tag: "constant" arg: "0.3" } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { padding: VALID } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +output: "ofm" diff --git a/compiler/enco/test/tflite/Regression_0004/INFERENCE b/compiler/enco/test/tflite/Regression_0004/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Regression_0004/test.recipe b/compiler/enco/test/tflite/Regression_0004/test.recipe new file mode 100644 index 000000000..80705efd5 --- /dev/null +++ b/compiler/enco/test/tflite/Regression_0004/test.recipe @@ -0,0 +1,27 @@ +operand { + name: "ifm0" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } + filler { tag: "constant" arg: "0.1" } +} +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } + filler { tag: "constant" arg: "0.1" } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operation { + type: "Div" + input: "ifm0" + input: "ifm1" + output: "ofm" + div_options { + activation: NONE + } +} +output: "ofm" diff --git a/compiler/enco/test/tflite/Reshape_000/INFERENCE b/compiler/enco/test/tflite/Reshape_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Reshape_000/test.recipe b/compiler/enco/test/tflite/Reshape_000/test.recipe new file mode 100644 index 000000000..bb7ce48a9 --- /dev/null +++ b/compiler/enco/test/tflite/Reshape_000/test.recipe @@ -0,0 +1,21 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 10 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 10 } +} +operation { + type: "Reshape" + reshape_options { + new_shape: -1 + new_shape: 10 + } + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/enco/test/tflite/Sub_000/INFERENCE b/compiler/enco/test/tflite/Sub_000/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/Sub_000/test.recipe b/compiler/enco/test/tflite/Sub_000/test.recipe new file mode 100644 index 000000000..0397c9c2b --- /dev/null +++ b/compiler/enco/test/tflite/Sub_000/test.recipe @@ -0,0 +1,27 @@ +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 5 dim:2 dim:3 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 5 dim:2 dim:3 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 5 dim:2 dim:3 } +} +operation { + type: "Sub" + sub_options { + activation: NONE + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/compiler/enco/test/tflite/empty/INFERENCE b/compiler/enco/test/tflite/empty/INFERENCE new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/empty/test.recipe b/compiler/enco/test/tflite/empty/test.recipe new file mode 100644 index 000000000..e69de29bb diff --git a/compiler/enco/test/tflite/runall.sh b/compiler/enco/test/tflite/runall.sh new file mode 100755 index 000000000..c274f724b --- /dev/null +++ b/compiler/enco/test/tflite/runall.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +if [[ $# -le 6 ]]; then + echo "USAGE: $0 [nnkit-run path] [reference backend path] [randomize action path] [HDF5 export action path] [HDF5 import action path] [WORKDIR] [Prefix1] [Prefix2] ..." + exit 255 +fi + +NNKIT_RUN_PATH="$1"; shift +REFERENCE_BACKEND_PATH="$1"; shift +RANDOMIZE_ACTION_PATH="$1"; shift +HDF5_EXPORT_ACTION_PATH="$1"; shift +HDF5_IMPORT_ACTION_PATH="$1"; shift +WORKDIR="$1"; shift + +echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}" +echo "-- Found reference backend: ${REFERENCE_BACKEND_PATH}" +echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}" +echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}" +echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}" +echo "-- Found workdir: ${WORKDIR}" + +TESTED=() +PASSED=() +FAILED=() + +pushd "${WORKDIR}" +while [[ $# -ne 0 ]]; do + PREFIX="$1"; shift + + TESTED+=("${PREFIX}") + + PASSED_TAG="${PREFIX}.passed" + + rm -f "${PASSED_TAG}" + + cat > "${PREFIX}.log" <( + exec 2>&1 + + echo "-- Found tflite: ${PREFIX}.tflite" + echo "-- Found backend: lib${PREFIX}.so" + + "${NNKIT_RUN_PATH}" \ + --backend "${REFERENCE_BACKEND_PATH}" \ + --backend-arg "${WORKDIR}/${PREFIX}.tflite" \ + --pre "${RANDOMIZE_ACTION_PATH}" \ + --pre "${HDF5_EXPORT_ACTION_PATH}" \ + --pre-arg "${PREFIX}.input.h5" \ + --post "${HDF5_EXPORT_ACTION_PATH}" \ + --post-arg "${PREFIX}.expected.h5" + + "${NNKIT_RUN_PATH}" \ + --backend "./lib${PREFIX}.so" \ + --pre "${HDF5_IMPORT_ACTION_PATH}" \ + --pre-arg "${PREFIX}.input.h5" \ + --post "${HDF5_EXPORT_ACTION_PATH}" \ + --post-arg "${PREFIX}.obtained.h5" + + h5diff -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("$PREFIX") + else + FAILED+=("$PREFIX") + fi +done +popd + +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/encodump/CMakeLists.txt b/compiler/encodump/CMakeLists.txt new file mode 100644 index 000000000..58fe17a51 --- /dev/null +++ b/compiler/encodump/CMakeLists.txt @@ -0,0 +1,17 @@ +if(NOT TARGET enco_intf_frontend) + return() +endif(NOT TARGET enco_intf_frontend) + +if(NOT TARGET enco_core) + return() +endif(NOT TARGET enco_core) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(encodump ${SOURCES}) +target_include_directories(encodump PRIVATE src) +target_link_libraries(encodump enco_intf_frontend) +target_link_libraries(encodump enco_core) +target_link_libraries(encodump safemain) +target_link_libraries(encodump stdex) +target_link_libraries(encodump dl) diff --git a/compiler/encodump/README.md b/compiler/encodump/README.md new file mode 100644 index 000000000..1a2b44969 --- /dev/null +++ b/compiler/encodump/README.md @@ -0,0 +1,69 @@ +# encodump + +_encodump_ is a dumper for coco IR generated by enco + +## How to use +Sources for _encodump_ are: +1. enco frontend library `*.so` file +1. model description file for matching to enco frontend +``` +$ path/to/encodump \ + --frontend [enco frontend library .so file] + --frontend-arg [model file] ... +``` + +Currently supported enco frontends are Caffe and tensorflow lite. For Caffe, both `*.prototxt` and `*.caffemodel` are required, and for TFlite, `*.tflite` flatbuffers file is required. + +Output is dumped into terminal. + +## Example +``` +nncc$ ./build/compiler/encodump/encodump \ + --frontend ./build/compiler/enco/frontend/tflite/libenco_tflite_frontend.so \ + --frontend-arg ./build/compiler/enco/test/tflite/Conv2D_000.tflite +``` + +Output: +``` + + (index: 0) + : + Eval (0x10cfa90) + out: 0x10cf960 + : + Load(0x10cf600, obj: 0x10cd670) + Conv2D(0x10cf8a0, ker obj: 0x10cf2d0, padding [T/B/L/R=0,0,0,0], stride [V/H = 1,1]) + : + Eval (0x10cff80) + out: 0x10cfb20 + : + Load(0x10cfe70, obj: 0x10cfcc0) + Load(0x10cfdd0, obj: 0x10cf960) + Add + : + Copy (0x10d0120) + from: 0x10cfb20 + into: 0x10cfff0 + : + Copy (0x10d01f0) + from: 0x10cfff0 + into: 0x10cf210 + : bag 0x10ce650, name=ifm + : bag 0x10ce9c0, name=ofm + : + 0x10ce650, obj: [0x10cd670], size: 18, input, const, reader: [x], updater: [x], + 0x10ce770, obj: [0x10cf2d0], size: 2, const, reader: [x], updater: [x], + 0x10ce890, obj: [0x10cfcc0], size: 1, const, reader: [x], updater: [x], + 0x10ce9c0, obj: [0x10cf210], size: 9, output, const, reader: [x], updater: [x], + 0x10cf9d0, obj: [0x10cf960], size: 9, const, reader: [x], updater: [x], + 0x10cfbe0, obj: [0x10cfb20], size: 9, const, reader: [x], updater: [x], + 0x10d0060, obj: [0x10cfff0], size: 9, const, reader: [x], updater: [x], + : + 0x10cd670, bag: 0x10ce650, kind: Feature, Shape [H/W/D=3,3,2], producer: x, comsumer: [op: 0x10cf600] + 0x10cf210, bag: 0x10ce9c0, kind: Feature, Shape [H/W/D=3,3,1], producer: instr: 0x10d01f0, comsumer: [x] + 0x10cf2d0, bag: 0x10ce770, kind: Kernel, Shape [N/H/W/D=1,1,1,2], producer: x, comsumer: [op: 0x10cf8a0] + 0x10cf960, bag: 0x10cf9d0, kind: Feature, Shape [H/W/D=3,3,1], producer: instr: 0x10cfa90, comsumer: [op: 0x10cfdd0] + 0x10cfb20, bag: 0x10cfbe0, kind: Feature, Shape [H/W/D=3,3,1], producer: instr: 0x10cff80, comsumer: [inst: 0x10d0120] + 0x10cfcc0, bag: 0x10ce890, kind: Feature, Shape [H/W/D=3,3,1], producer: x, comsumer: [op: 0x10cfe70] + 0x10cfff0, bag: 0x10d0060, kind: Feature, Shape [H/W/D=3,3,1], producer: instr: 0x10d0120, comsumer: [inst: 0x10d01f0] +``` diff --git a/compiler/encodump/requires.cmake b/compiler/encodump/requires.cmake new file mode 100644 index 000000000..3d1ca094e --- /dev/null +++ b/compiler/encodump/requires.cmake @@ -0,0 +1 @@ +require("safemain") diff --git a/compiler/encodump/src/Driver.cpp b/compiler/encodump/src/Driver.cpp new file mode 100644 index 000000000..f27cbe904 --- /dev/null +++ b/compiler/encodump/src/Driver.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include + +#include "Dump.h" + +namespace cmdline +{ + +// TODO Extract this helper class +class Vector : public cmdline::View +{ +public: + uint32_t size(void) const { return _args.size(); } + +public: + const char *at(uint32_t nth) const { return _args.at(nth).c_str(); } + +public: + Vector &append(const std::string &arg) + { + _args.emplace_back(arg); + return (*this); + } + +private: + std::vector _args; +}; + +} // namespace cmdline + +namespace +{ + +class Zone +{ +public: + Zone() = default; + +public: + const cmdline::View *args(void) const { return &_args; } + +public: + void append(const std::string &arg) { _args.append(arg); } + +private: + cmdline::Vector _args; +}; + +} // namespace + +#include + +namespace +{ + +class FrontendFactory +{ +public: + FrontendFactory(const std::string &path) + { + _handle = dlopen(path.c_str(), RTLD_LAZY); + assert(_handle != nullptr); + } + +public: + // Copy is not allowed to avoid double close + FrontendFactory(const FrontendFactory &) = delete; + FrontendFactory(FrontendFactory &&) = delete; + +public: + ~FrontendFactory() { dlclose(_handle); } + +private: + using Entry = std::unique_ptr (*)(const cmdline::View &); + +private: + Entry entry(void) const + { + auto entry = reinterpret_cast(dlsym(_handle, "make_frontend")); + assert(entry != nullptr); + return entry; + } + +public: + std::unique_ptr make(const cmdline::View *args) const + { + auto fn = entry(); + return fn(*args); + } + +private: + void *_handle; +}; + +} // namespace + +namespace +{ + +class FrontendZone : public Zone +{ +public: + FrontendZone(const std::string &path) : _factory{path} + { + // DO NOTHING + } + +public: + const FrontendFactory *factory(void) const { return &_factory; } + +private: + FrontendFactory _factory; +}; + +} // namespace + +#include + +#include + +#include +#include + +/** + * @brief Dump IR for given arguments + * + * Call example: + * $ ./build/compiler/encodump/encodump \ + * --frontend build/compiler/enco/frontend/caffe/libenco_caffe_frontend.so \ + * --frontend-arg build/compiler/enco/test/caffe/Convolution_003.prototxt \ + * --frontend-arg build/compiler/enco/test/caffe/Convolution_003.caffemodel + */ +int entry(int argc, char **argv) +{ + // Usage: + // [Command] --frontend [Frontend .so path] --frontend-arg ... + std::unique_ptr frontend_zone; + + // Simple argument parser (based on map) + std::map> argparse; + + argparse["--frontend"] = [&](const std::string &path) { + frontend_zone = stdex::make_unique(path); + }; + + argparse["--frontend-arg"] = [&](const std::string &arg) { frontend_zone->append(arg); }; + + if (argc < 2) + { + std::cerr << "Usage:" << std::endl; + std::cerr << "[Command] --frontend [.so path]" << std::endl; + std::cerr << " --frontend-arg [argument] ..." << std::endl; + return 255; + } + + for (int n = 1; n < argc; n += 2) + { + const std::string tag{argv[n]}; + const std::string arg{argv[n + 1]}; + + auto it = argparse.find(tag); + + if (it == argparse.end()) + { + std::cerr << "Option '" << tag << "' is not supported" << std::endl; + return 255; + } + + it->second(arg); + } + + assert(frontend_zone != nullptr); + + auto frontend = frontend_zone->factory()->make(frontend_zone->args()); + + auto bundle = frontend->load(); + + // dump + dump(bundle.module()); + + // todo : dump data + + return 0; +} diff --git a/compiler/encodump/src/Dump.cpp b/compiler/encodump/src/Dump.cpp new file mode 100644 index 000000000..7ec00e2e2 --- /dev/null +++ b/compiler/encodump/src/Dump.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Dump.cpp + * @brief Print coco IR produced from enco frontend + * + * @note Some object inherits multiple parents. + * For example, coco:Conv2D inherits coco::Consumer and more. Assume that op is an instance + * of coco::Conv2D. In this case, the printing results of the following may be different: + * 1) cout << op; // printing address of type coco::Conv2D + * 2) cout << reinterpret_cast(op); + * 3) cout << object->consumer(); // assume that this object->consumer() returns op + * 4) cout << dynamic_cast(op); + * 1) and 2) prints same address. 3) and 4) prints same address but different from 1) and 2). + * For details, refer to + * https://stackoverflow.com/questions/22256620/why-pointers-to-the-same-object-have-different-values + * For dumping, we will use 3), 4) + */ +#include "Dump.h" + +#include +#include + +std::string tab(int n) { return std::string(n * 2, ' '); } + +struct OpPrinter final : public coco::Op::Visitor +{ +public: + OpPrinter(std::ostream &os, int indent) : _os(os), _indent(indent) {} + +public: + void visit(const coco::Load *op) override + { + _os << tab(_indent) << "Load(" << dynamic_cast(op) + << ", obj: " << op->object() << ")" << std::endl; + } + + void visit(const coco::PadF *op) override + { + op->arg()->accept(this); + _os << tab(_indent) << "PadF" << std::endl; + } + + void visit(const coco::Conv2D *op) override + { + op->arg()->accept(this); + const coco::Padding2D *pad = op->pad(); + const coco::Stride2D *stride = op->stride(); + + _os << tab(_indent) << "Conv2D(" << dynamic_cast(op) + << ", ker obj: " << op->ker() << ", padding [T/B/L/R=" << pad->top() << "," << pad->bottom() + << "," << pad->left() << "," << pad->right() << "]" + << ", stride [V/H = " << stride->vertical() << "," << stride->horizontal() << "]" + << ")" << std::endl; + } + + void visit(const coco::MaxPool2D *op) override + { + op->arg()->accept(this); + _os << tab(_indent) << "MaxPool2D" << std::endl; + } + + void visit(const coco::AvgPool2D *op) override + { + op->arg()->accept(this); + _os << tab(_indent) << "AvgPool2D" << std::endl; + } + + void visit(const coco::Add *op) override + { + op->left()->accept(this); + op->right()->accept(this); + _os << tab(_indent) << "Add" << std::endl; + } + + void visit(const coco::Mul *op) override + { + op->left()->accept(this); + op->right()->accept(this); + _os << tab(_indent) << "Mul" << std::endl; + } + + void visit(const coco::ReLU *op) override + { + op->arg()->accept(this); + _os << tab(_indent) << "ReLU" << std::endl; + } + + void visit(const coco::ReLU6 *op) override + { + op->arg()->accept(this); + _os << tab(_indent) << "ReLU6" << std::endl; + } + + void visit(const coco::Sub *op) override + { + op->left()->accept(this); + op->right()->accept(this); + _os << tab(_indent) << "Sub" << std::endl; + } + + void visit(const coco::ConcatF *op) override + { + op->left()->accept(this); + op->right()->accept(this); + _os << tab(_indent) << "ConcatF" << std::endl; + } + + void visit(const coco::Div *op) override + { + op->left()->accept(this); + op->right()->accept(this); + _os << tab(_indent) << "Div" << std::endl; + } + +private: + std::ostream &_os; + +private: + int _indent; +}; + +struct InstrPrinter final : public coco::Instr::Visitor +{ +public: + InstrPrinter() = delete; + + InstrPrinter(int indent) : _indent(indent) {} + + void visit(const coco::Eval *ins) override + { + std::cout << tab(_indent) << "Eval (" << dynamic_cast(ins) << ")" + << std::endl; + std::cout << tab(_indent + 1) << "out: " << ins->out() << std::endl; + std::cout << tab(_indent + 1) << ": " << std::endl; + { + OpPrinter prn(std::cout, _indent + 2); + ins->op()->accept(prn); + } + } + + void visit(const coco::Copy *ins) override + { + // copy is Producer and also Customer. We will use address for Producer + std::cout << tab(_indent) << "Copy (" << dynamic_cast(ins) << ")" + << std::endl; + std::cout << tab(_indent) << " from: " << ins->from() << std::endl; + std::cout << tab(_indent) << " into: " << ins->into() << std::endl; + } + + void visit(const coco::Shuffle *ins) override + { + std::cout << tab(_indent) << "Shuffle (" << dynamic_cast(ins) << ")" + << std::endl; + std::cout << tab(_indent) << " from: " << ins->from() << std::endl; + std::cout << tab(_indent) << " into: " << ins->into() << std::endl; + } + +private: + int _indent; +}; + +void dump(const coco::Op *op, int indent) +{ + OpPrinter prn(std::cout, indent); + op->accept(prn); +} + +void dump(const coco::Instr *ins, int indent) +{ + std::cout << tab(indent) << ":" << std::endl; + + static InstrPrinter prn(indent + 1); + + ins->accept(prn); +} + +void dump(const coco::Block *B, int indent) +{ + std::cout << tab(indent) << " (index: " << B->index().value() << ")" << std::endl; + for (auto I = B->instr()->head(); I != nullptr; I = I->next()) + { + dump(I, indent + 1); + } +} + +void dump(const coco::BlockList *L, int indent) +{ + for (auto B = L->head(); B != nullptr; B = B->next()) + { + dump(B, indent); + } +} + +template +void dump(std::string header, SetT set, EntityF print_addr_f) +{ + std::cout << header << ": ["; + if (set->size() == 0) + std::cout << "x"; + else + { + int idx = 0; + for (auto entity : *set) + { + if (idx++ != 0) + std::cout << ", "; + print_addr_f(entity); + } + } + std::cout << "]"; +} + +void dump(const coco::BagManager *l, int indent) +{ + std::cout << tab(indent) << ":" << std::endl; + + for (auto n = 0; n < l->size(); ++n) + { + auto bag = l->at(n); + + std::cout << tab(indent + 1) << bag << ", "; + + // print objects in bag->deps() + auto print_dep_object = [](coco::Dep *dep) { std::cout << dep->object(); }; + dump("obj", bag->deps(), print_dep_object); + std::cout << ", "; + + std::cout << "size: " << bag->size() << ", "; + + if (bag->isInput()) + std::cout << "input, "; + if (bag->isOutput()) + std::cout << "output, "; + if ((!bag->isInput()) || (!bag->isOutput())) + std::cout << "const, "; + + // print readers in bag->reads() + auto print_read_reader = [](coco::Read *read) { + if (coco::Op *op = dynamic_cast(read->reader())) + std::cout << "op: " << op; + else if (coco::Instr *instr = dynamic_cast(read->reader())) + std::cout << "instr: " << instr; + else + std::cout << "x"; + }; + dump("reader", bag->reads(), print_read_reader); + std::cout << ", "; + + // print updaters in bag->updates() + auto print_update_updater = [](coco::Update *update) { + if (coco::Op *op = dynamic_cast(update->updater())) + std::cout << "op: " << op; + else if (coco::Instr *instr = dynamic_cast(update->updater())) + std::cout << "instr: " << instr; + else + std::cout << "x"; + }; + dump("updater", bag->updates(), print_update_updater); + std::cout << ", "; + + std::cout << std::endl; + } +} + +void dump(coco::FeatureObject *feature_ob) +{ + auto shape = feature_ob->shape(); + std::cout << "kind: Feature, Shape [H/W/D=" << shape.height() << "," << shape.width() << "," + << shape.depth() << "]"; +} + +void dump(coco::KernelObject *kernel_ob) +{ + auto shape = kernel_ob->shape(); + std::cout << "kind: Kernel, Shape [N/H/W/D=" << shape.count() << "," << shape.height() << "," + << shape.width() << "," << shape.depth() << "]"; +} + +void dump(const coco::ObjectManager *l, int indent) +{ + std::cout << tab(indent) << ":" << std::endl; + for (auto n = 0; n < l->size(); ++n) + { + auto obj = l->at(n); + std::cout << tab(indent + 1) << obj << ", bag: " << obj->bag() << ", "; + + using ObDumpers = std::function; + + std::map ob_dumpers; + + ob_dumpers[coco::Object::Kind::Feature] = [](coco::Object *ob) { dump(ob->asFeature()); }; + ob_dumpers[coco::Object::Kind::Kernel] = [](coco::Object *ob) { dump(ob->asKernel()); }; + ob_dumpers[coco::Object::Kind::Unknown] = [](coco::Object *ob) { + std::cout << "kind: Unknown"; + }; + + ob_dumpers[obj->kind()](obj); + + std::cout << ", producer: "; + auto def = obj->def(); + if (def) + { + if (coco::Op *op = dynamic_cast(def->producer())) + std::cout << "op: " << op; + else if (coco::Instr *instr = dynamic_cast(def->producer())) + std::cout << "instr: " << instr; + else + std::cout << "x"; + } + else + std::cout << "x"; + std::cout << ", "; + + // print consumers in obj->uses() + auto print_consumer = [](coco::Use *use) { + if (coco::Op *op = dynamic_cast(use->consumer())) + std::cout << "op: " << op; + else if (coco::Instr *instr = dynamic_cast(use->consumer())) + std::cout << "inst: " << instr; + else + std::cout << "x"; + }; + dump("comsumer", obj->uses(), print_consumer); + std::cout << std::endl; + } +} + +template void head(int indent); + +template <> void head(int indent) { std::cout << tab(indent) << ": "; } + +template <> void head(int indent) { std::cout << tab(indent) << ": "; } + +template void dump(const coco::PtrList *list, int indent) +{ + head(indent); + for (int n = 0; n < list->size(); n++) + { + const PtrItemT *item = list->at(n); + if (n != 0) + std::cout << ", "; + std::cout << "bag " << item->bag() << ", name=" << item->name(); + } + std::cout << std::endl; +} + +void dump(const coco::Module *module) +{ + std::cout << "" << std::endl; + + dump(module->block(), 1); + dump(module->input(), 1); + dump(module->output(), 1); + dump(module->entity()->bag(), 1); + dump(module->entity()->object(), 1); +} diff --git a/compiler/encodump/src/Dump.h b/compiler/encodump/src/Dump.h new file mode 100644 index 000000000..6ea69b978 --- /dev/null +++ b/compiler/encodump/src/Dump.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DUMP_H__ +#define __DUMP_H__ + +#include + +void dump(const coco::Module *module); + +#endif // __DUMP_H__ diff --git a/compiler/exo/CMakeLists.txt b/compiler/exo/CMakeLists.txt new file mode 100644 index 000000000..79c75ef2e --- /dev/null +++ b/compiler/exo/CMakeLists.txt @@ -0,0 +1,73 @@ +nnas_find_package(FlatBuffers QUIET) + +if(NOT FlatBuffers_FOUND) + message(STATUS "Build exo: FALSE (missing FlatBuffers)") + return() +endif(NOT FlatBuffers_FOUND) + +nnas_find_package(TensorFlowSource EXACT 1.14 QUIET) + +if(NOT TensorFlowSource_FOUND) + message(STATUS "Build exo: FALSE (missing TensorFlowSource)") + return() +endif(NOT TensorFlowSource_FOUND) + +message(STATUS "Build exo: TRUE") + +set(TFLITE_SCHEMA_DIR "${TensorFlowSource_DIR}/tensorflow/lite/schema") +set(CIRCLE_SCHEMA_DIR "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema") + +FlatBuffers_Target(exo_tflite_fbs + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${TFLITE_SCHEMA_DIR}" + SCHEMA_FILES schema.fbs +) + +FlatBuffers_Target(exo_circle_fbs + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CIRCLE_SCHEMA_DIR}" + SCHEMA_FILES circle_schema.fbs +) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(exo SHARED ${SOURCES}) +target_include_directories(exo PUBLIC include) +target_include_directories(exo PRIVATE src) +target_link_libraries(exo PUBLIC exo_tflite_fbs) +target_link_libraries(exo PUBLIC exo_circle_fbs) +target_link_libraries(exo PUBLIC loco) +target_link_libraries(exo PRIVATE stdex) +target_link_libraries(exo PRIVATE pepper_str) +target_link_libraries(exo PRIVATE pepper_strcast) +target_link_libraries(exo PRIVATE locoex_customop) +target_link_libraries(exo PRIVATE locop) +target_link_libraries(exo PRIVATE hermes_std) +target_link_libraries(exo PRIVATE logo) +target_link_libraries(exo PRIVATE oops) +install(TARGETS exo DESTINATION lib) + +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(exo PRIVATE nncc_common) + +if (NOT ENABLE_TEST) + return() +endif (NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(exo_test ${TESTS}) +target_include_directories(exo_test PRIVATE src) +target_link_libraries(exo_test stdex) +target_link_libraries(exo_test pepper_str) +target_link_libraries(exo_test exo) +target_link_libraries(exo_test hermes_std) +target_link_libraries(exo_test logo) +target_link_libraries(exo_test oops) +target_link_libraries(exo_test locoex_customop) diff --git a/compiler/exo/README.md b/compiler/exo/README.md new file mode 100644 index 000000000..bfa7fcfd3 --- /dev/null +++ b/compiler/exo/README.md @@ -0,0 +1,12 @@ +# exo + +_exo_ includes _loco_-to-_T/F Lite_ exporter (as a library). + +## How to add a new TFL node + +1. Add a new TFL node into `TFLNodes.lst` and `TFLNodes.h` +1. Define a knob in `Knob.lst` if you need a knob. +1. Add appropriate methods in `TFLShapeInferenceRule.cpp` and `TFLTypeInferenceRule.cpp` +1. Add a new converter under `Conversion` directory +1. Add an appropriate method in `OperationExporter.cpp` +1. Register the converter into `Convert.cpp` diff --git a/compiler/exo/include/exo/CircleExporter.h b/compiler/exo/include/exo/CircleExporter.h new file mode 100644 index 000000000..7ec159303 --- /dev/null +++ b/compiler/exo/include/exo/CircleExporter.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXO_CIRCLE_EXPORTER_H__ +#define __EXO_CIRCLE_EXPORTER_H__ + +#include + +#include + +namespace exo +{ + +/** + * HOW TO USE: + * + * loco::Graph *g = ...; + * + * CircleExporter e(g); + * e.dumpToFile("model.circle"); + * + * HOW TO USE (simplified): + * + * CircleExporter(g).dumpToFile("model.circle"); + * + */ +class CircleExporter +{ +public: + class Impl; + +public: + explicit CircleExporter(loco::Graph *graph); + ~CircleExporter(); + + /** + * @brief write to a file + * @param path path to file where to write data + * @throws any file related exceptions + */ + void dumpToFile(const char *path) const; + +private: + std::unique_ptr _impl; +}; + +} // namespace exo + +#endif // __EXO_CIRCLE_EXPORTER_H__ diff --git a/compiler/exo/include/exo/LoggingContext.h b/compiler/exo/include/exo/LoggingContext.h new file mode 100644 index 000000000..5f10ceb93 --- /dev/null +++ b/compiler/exo/include/exo/LoggingContext.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXO_LOGGING_CONTEXT_H__ +#define __EXO_LOGGING_CONTEXT_H__ + +#include + +namespace exo +{ + +/** + * @brief Global logging context + */ +struct LoggingContext +{ + static hermes::Context *get(void); +}; + +} // namespace exo + +#endif // __EXO_LOGGING_CONTEXT_H__ diff --git a/compiler/exo/include/exo/TFLExporter.h b/compiler/exo/include/exo/TFLExporter.h new file mode 100644 index 000000000..49cce2af5 --- /dev/null +++ b/compiler/exo/include/exo/TFLExporter.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXO_TFL_EXPORTER_H__ +#define __EXO_TFL_EXPORTER_H__ + +#include + +#include + +namespace exo +{ + +/** + * HOW TO USE: + * + * loco::Graph *g = ...; + * + * TFLExporter e(g); + * e.dumpToFile("model.tflite"); + * + * HOW TO USE (simplified): + * + * TFLExporter(g).dumpToFile("model.tflite"); + * + */ +class TFLExporter +{ +public: + class Impl; + +public: + explicit TFLExporter(loco::Graph *graph); + ~TFLExporter(); + + /** + * @brief write to a file + * @param path path to file where to write data + * @throws any file related exceptions + */ + void dumpToFile(const char *path) const; + +private: + std::unique_ptr _impl; +}; + +} // namespace exo + +#endif // __EXO_TFL_EXPORTER_H__ diff --git a/compiler/exo/requires.cmake b/compiler/exo/requires.cmake new file mode 100644 index 000000000..6378b942d --- /dev/null +++ b/compiler/exo/requires.cmake @@ -0,0 +1,6 @@ +require("stdex") +require("loco") +require("locoex-customop") +require("logo") +require("pepper-str") +require("oops") diff --git a/compiler/exo/src/Check.h b/compiler/exo/src/Check.h new file mode 100644 index 000000000..79dac50dd --- /dev/null +++ b/compiler/exo/src/Check.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CHECK_H__ +#define __CHECK_H__ + +#include + +#include +#include +#include + +// TODO Add macro for Release version + +#define EXO_ASSERT(condition, msg) \ + { \ + if (!(condition)) \ + { \ + std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \ + assert((condition)); \ + } \ + } + +#endif // __CHECK_H__ diff --git a/compiler/exo/src/Circle/CircleExporter.cpp b/compiler/exo/src/Circle/CircleExporter.cpp new file mode 100644 index 000000000..797749090 --- /dev/null +++ b/compiler/exo/src/Circle/CircleExporter.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "exo/CircleExporter.h" + +#include "CircleExporterImpl.h" + +#include + +#include + +#include + +namespace exo +{ + +CircleExporter::CircleExporter(loco::Graph *graph) : _impl(stdex::make_unique(graph)) +{ + // NOTHING TO DO +} + +CircleExporter::~CircleExporter() = default; + +void CircleExporter::dumpToFile(const char *path) const +{ + const char *ptr = _impl->getBufferPointer(); + const size_t size = _impl->getBufferSize(); + + if (!ptr) + INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason"); + + std::ofstream file(path, std::ofstream::binary); + file.write(ptr, size); +} + +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleExporterImpl.cpp b/compiler/exo/src/Circle/CircleExporterImpl.cpp new file mode 100644 index 000000000..4cba33da1 --- /dev/null +++ b/compiler/exo/src/Circle/CircleExporterImpl.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleExporterImpl.h" + +#include "Convert.h" +#include "ExoOptimize.h" + +#include "CircleTensorExporter.h" +#include "CircleOperationExporter.h" +#include "CircleExporterUtils.h" + +#include "Log.h" +#include "Knob.h" + +#include + +#include +#include +#include +#include + +namespace +{ + +using namespace exo::circle_detail; + +void registerGraphInputTensors(loco::Graph *graph, SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->inputs()->size(); ++n) + { + auto node = loco::pull_node(graph, n); + assert(node != nullptr); + ctx._inputs.push_back(get_tensor_index(node)); + } +} + +void registerGraphOutputTensors(loco::Graph *graph, SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto push = loco::push_node(graph, n); + assert(push != nullptr); + auto node = push->from(); + assert(node != nullptr); + ctx._outputs.push_back(get_tensor_index(node)); + } +} + +} // namespace + +namespace +{ + +using namespace circle; +using namespace flatbuffers; + +Offset>> +encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map &opcodes, + std::unordered_map &custom_opcodes) +{ + std::vector> 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 exo +{ + +using namespace exo::circle_detail; +using namespace circle; +using namespace flatbuffers; + +CircleExporter::Impl::Impl(loco::Graph *graph) { exportGraph(graph); } + +::flatbuffers::Offset<::circle::SubGraph> +CircleExporter::Impl::exportSubgraph(SerializedModelData &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 CircleExporter::Impl::exportGraph(loco::Graph *graph) +{ + LOGGER(l); + + // IR-level conversion and optimization + { + convert_to_TFLNodes(graph); + set(Dialect::CIRCLE); + optimize(graph); + } + + _builder.Clear(); + + SerializedModelData gd; + + // This version is taken from comment in fbs + constexpr uint32_t version = 0; + + registerGraphIOName(graph, gd); + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, 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, gd); + + // encode operator codes + auto operator_codes = + encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); + + // Subgraphs + Offset subgraph = exportSubgraph(gd); + auto subgraphs = _builder.CreateVector(std::vector>{subgraph}); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(gd._buffers); + + // empty metadata + std::vector 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); +} + +const char *CircleExporter::Impl::getBufferPointer() const +{ + return reinterpret_cast(_builder.GetBufferPointer()); +} + +size_t CircleExporter::Impl::getBufferSize() const { return _builder.GetSize(); } + +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleExporterImpl.h b/compiler/exo/src/Circle/CircleExporterImpl.h new file mode 100644 index 000000000..b1138fbad --- /dev/null +++ b/compiler/exo/src/Circle/CircleExporterImpl.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_EXPORTER_IMPL_H__ +#define __CIRCLE_EXPORTER_IMPL_H__ + +#include "exo/CircleExporter.h" +#include "circle_schema_generated.h" + +#include + +namespace exo +{ + +namespace circle_detail +{ + +struct SerializedModelData; + +} // namespace circle_detail + +using namespace circle_detail; + +/** + * internal implementation of interface exporter class + */ +class CircleExporter::Impl +{ +public: + Impl() = delete; + ~Impl() = default; + + explicit Impl(loco::Graph *graph); + + /** + * @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 SerializedModelData + * @param gd information about serializer parts of model + * @return offset in buffer corresponding to serialized subgraph + */ + flatbuffers::Offset exportSubgraph(SerializedModelData &gd); + + /** + * @brief root function that writes graph into internal buffer + * @param graph + */ + void exportGraph(loco::Graph *graph); + +private: + flatbuffers::FlatBufferBuilder _builder; +}; + +} // namespace exo + +#endif // __CIRCLE_EXPORTER_IMPL_H__ diff --git a/compiler/exo/src/Circle/CircleExporterUtils.cpp b/compiler/exo/src/Circle/CircleExporterUtils.cpp new file mode 100644 index 000000000..12b204ce7 --- /dev/null +++ b/compiler/exo/src/Circle/CircleExporterUtils.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleExporterUtils.h" + +#include + +namespace exo +{ + +circle::ActivationFunctionType to_circle_actfunc(locoex::FusedActFunc func) +{ + switch (func) + { + case locoex::FusedActFunc::NONE: + return circle::ActivationFunctionType_NONE; + case locoex::FusedActFunc::RELU: + return circle::ActivationFunctionType_RELU; + case locoex::FusedActFunc::RELU6: + return circle::ActivationFunctionType_RELU6; + default: + INTERNAL_EXN_V("trying to convert unsupported locoex::FusedActFunc", oops::to_uint32(func)); + } +} + +} // namespace exo + +namespace exo +{ +namespace circle_detail +{ + +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(_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(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast(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 locoex::Padding pad) +{ + if (pad == locoex::Padding::VALID) + return circle::Padding_VALID; + if (pad == locoex::Padding::SAME) + return circle::Padding_SAME; + + INTERNAL_EXN_V("Unsupported locoex::Padding", oops::to_uint32(pad)); +} + +void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd) +{ + for (uint32_t in = 0; in < graph->inputs()->size(); ++in) + { + auto pull = loco::pull_node(graph, in); + auto name = graph->inputs()->at(in)->name(); + + gd._pull_to_name[pull] = name; + } + for (uint32_t out = 0; out < graph->outputs()->size(); ++out) + { + auto push = loco::push_node(graph, out); + auto name = graph->outputs()->at(out)->name(); + + gd._push_to_name[push] = name; + } + + // TODO set this value properly + gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; +} + +#include + +#include + +namespace +{ + +class TFLTensorIndexAnnotation final : public loco::NodeAnnotation +{ +public: + TFLTensorIndexAnnotation(const TFLTensorIndex &index) : _index{index} + { + // DO NOTHING + } + +public: + const TFLTensorIndex &index(void) const { return _index; } + +private: + TFLTensorIndex _index; +}; + +} // namespace + +void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id) +{ + assert(node->annot() == nullptr); + node->annot(stdex::make_unique(tensor_id)); +} + +TFLTensorIndex get_tensor_index(loco::Node *node) +{ + assert(node->annot() != nullptr); + return node->annot()->index(); +} + +} // namespace circle_detail +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleExporterUtils.h b/compiler/exo/src/Circle/CircleExporterUtils.h new file mode 100644 index 000000000..fdd162bae --- /dev/null +++ b/compiler/exo/src/Circle/CircleExporterUtils.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_EXPORTER_UTILS_H__ +#define __CIRCLE_EXPORTER_UTILS_H__ + +#include "ExporterUtils.h" + +#include "circle_schema_generated.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +#include + +namespace exo +{ +namespace circle_detail +{ + +struct OpCode +{ + circle::BuiltinOperator opcode; + + bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; } +}; + +} // namespace circle_detail +} // namespace exo + +namespace exo +{ + +circle::ActivationFunctionType to_circle_actfunc(locoex::FusedActFunc func); + +} // namespace exo + +namespace std +{ + +template <> struct hash +{ + size_t operator()(const exo::circle_detail::OpCode &x) const { return hash()(x.opcode); } +}; + +} // namespace std + +namespace exo +{ +namespace circle_detail +{ + +/** + * @breif Record the information of T/F Lite SubGraph and its mapping to loco + */ +struct SubGraphContext +{ + /// @brief SubGraph input tensor id + std::vector _inputs; + /// @brief SubGraph output tensor id + std::vector _outputs; + /// @DataFormat for SubGraph + circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST}; +}; + +// Prerequisites for circle::Model object creation +struct SerializedModelData final : public SubGraphContext +{ + SerializedModelData() = default; + SerializedModelData(const SerializedModelData &) = delete; + + std::unordered_map _operator_codes; + std::unordered_map _custom_operator_codes; + std::vector> _operators; + std::vector> _tensors; + std::vector> _buffers; + + // Graph input and output names + std::unordered_map _pull_to_name; + std::unordered_map _push_to_name; + + /** + * @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); +}; + +circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm); +circle::Padding getOpPadding(const locoex::Padding pad); + +/// @brief Register graph input and output names to SerializedModelData +void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd); + +using TFLTensorIndex = int32_t; + +void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id); +TFLTensorIndex get_tensor_index(loco::Node *node); + +} // namespace circle_detail +} // namespace exo + +#endif // __TFL_EXPORTER_UTILS_H__ diff --git a/compiler/exo/src/Circle/CircleOperationExporter.cpp b/compiler/exo/src/Circle/CircleOperationExporter.cpp new file mode 100644 index 000000000..390e2ec99 --- /dev/null +++ b/compiler/exo/src/Circle/CircleOperationExporter.cpp @@ -0,0 +1,1228 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleOperationExporter.h" +#include "CircleExporterUtils.h" +#include "ShapeInference.h" + +#include "Dialect/IR/TFLNode.h" +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include "Dialect/IR/CircleNode.h" +#include "Dialect/IR/CircleNodes.h" +#include "Dialect/IR/CircleNodeVisitor.h" + +#include "Check.h" + +#include +#include +#include +#include + +#include + +#include + +using namespace flatbuffers; +using namespace circle; + +namespace +{ + +using namespace exo; +using namespace exo::circle_detail; + +class OperationExporter final : public locoex::TFLNodeMutableVisitor, + public locoex::CircleNodeMutableVisitor, + public loco::CanonicalNodeMutableVisitor +{ +public: + OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &ctx) : builder{fbb}, gd{ctx} + { + // DO NOTHING + } + +public: + // FOR TFLNodes + void visit(locoex::TFLAdd *) final; + void visit(locoex::TFLAveragePool2D *) final; + void visit(locoex::TFLConcatenation *) final; + void visit(locoex::TFLConst *) final{/* skip, everything is done in exportOpDefinedTensors */}; + void visit(locoex::TFLConv2D *) final; + void visit(locoex::TFLDepthwiseConv2D *) final; + void visit(locoex::TFLDiv *) final; + void visit(locoex::TFLFullyConnected *) final; + void visit(locoex::TFLMaximum *) final; + void visit(locoex::TFLMaxPool2D *) final; + void visit(locoex::TFLMean *) final; + void visit(locoex::TFLMul *) final; + void visit(locoex::TFLRelu *) final; + void visit(locoex::TFLRelu6 *) final; + // TODO TFLReshape + void visit(locoex::TFLRsqrt *) final; + // TODO TFLSoftmax + void visit(locoex::TFLSqrt *) final; + void visit(locoex::TFLSquaredDifference *) final; + void visit(locoex::TFLSub *) final; + // TODO TFLTanh + void visit(locoex::TFLTranspose *) final; + void visit(locoex::TFLTransposeConv *) final; + + // FOR CircleNodes + void visit(locoex::CircleInstanceNorm *) final; + + // FOR canonical nodes. These will be removed later + void visit(loco::ReLU *) final; + void visit(loco::ReLU6 *) final; + void visit(loco::Tanh *) final; + void visit(loco::Push *) final { /* DO NOTHING */} + void visit(loco::Pull *) final { /* DO NOTHING */} + void visit(loco::FeatureEncode *) final; + void visit(loco::FeatureDecode *) final; + void visit(loco::FilterEncode *) final; + void visit(loco::DepthwiseFilterEncode *) final; + void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */} + void visit(loco::MaxPool2D *) final; + void visit(loco::AvgPool2D *) final; + void visit(loco::Conv2D *) final; + void visit(loco::TransposedConv2D *) final; + void visit(loco::DepthwiseConv2D *) final; + void visit(loco::TensorConcat *) final; + void visit(loco::TensorReduce *) final; + void visit(loco::TensorSoftmax *) final; + void visit(loco::BiasEncode *) final; + void visit(loco::TensorBiasAdd *) final; + void visit(loco::FeatureBiasAdd *) final; + void visit(loco::EltwiseAdd *) final; + void visit(loco::EltwiseMax *) final; + void visit(loco::EltwiseMul *) final; + void visit(loco::EltwiseSub *) final; + void visit(loco::EltwiseDiv *) final; + void visit(loco::EltwiseSqrt *) final; + void visit(loco::FixedReshape *) final; + void visit(loco::TensorBroadcast *) final; + void visit(loco::TensorConstantPad *) final; + + void visit(locoex::COpCall *); + +private: + /** + * @brief Exports TFLMaxPool2D or TFLAveragePool2D + * + * @note TFLPool2D should be one of TFLMaxPool2D or TFLAveragePool2D + */ + template + void export_pool_2d(TFLPool2D *node, circle::BuiltinOperator builtin_op); + +private: + FlatBufferBuilder &builder; + SerializedModelData &gd; +}; + +void OperationExporter::visit(locoex::TFLAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLAveragePool2D *node) +{ + export_pool_2d(node, circle::BuiltinOperator_AVERAGE_POOL_2D); +} + +void OperationExporter::visit(locoex::TFLConcatenation *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + std::vector inputs_vec; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLDepthwiseConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLDiv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DIV); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLFullyConnected *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->weights()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLMaximum *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLMaxPool2D *node) +{ + export_pool_2d(node, circle::BuiltinOperator_MAX_POOL_2D); +} + +void OperationExporter::visit(locoex::TFLMean *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MEAN); + std::vector inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->reduction_indices())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLMul *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MUL); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLRelu *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU); + std::vector inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLRelu6 *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU6); + std::vector inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +// TODO TFLReshape + +void OperationExporter::visit(locoex::TFLRsqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT); + std::vector inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +// TODO TFLSoftmax + +void OperationExporter::visit(locoex::TFLSqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQRT); + std::vector inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLSquaredDifference *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQUARED_DIFFERENCE); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLSub *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SUB); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 TFLTanh + +void OperationExporter::visit(locoex::TFLTranspose *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE); + std::vector inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))}; + std::vector 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(locoex::TFLTransposeConv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->inputSizes()), + get_tensor_index(node->filter()), + get_tensor_index(node->outBackprop())}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +template +void OperationExporter::export_pool_2d(TFLPool2D *node, circle::BuiltinOperator builtin_op) +{ + EXO_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D || + builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, + "should be maxpool or avgpool"); + EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set"); + + uint32_t op_idx = gd.registerBuiltinOpcode(builtin_op); + std::vector inputs_vec{get_tensor_index(node->value())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::CircleInstanceNorm *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_INSTANCE_NORM); + std::vector inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()), + get_tensor_index(node->beta())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::ReLU *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::ReLU6 *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU6); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::Tanh *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TANH); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::MaxPool2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAX_POOL_2D); + std::vector inputs_vec{get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), node->window()->horizontal(), + node->window()->vertical()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::AvgPool2D *node) +{ + // Circle only support Valid convention of average pooling + assert(node->convention() == loco::AvgPool2D::Convention::Valid); + + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_AVERAGE_POOL_2D); + std::vector inputs_vec{get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), node->window()->horizontal(), + node->window()->vertical()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::Conv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D); + + // Third input of CONV_2D of Circle should be bias. We will make (and register to gd) dummy zero + // bias. Bias would be rank 1, have size of output kernel count, and have all zero values, i.e. + // zero bias. + auto *ker = dynamic_cast(node->ker()); + assert(ker); + int32_t bias_vec_size = ShapeInference::get(ker)._dims[0]; // output kernel count + + auto bias_vec_shape_offset = builder.CreateVector(std::vector{bias_vec_size}); + size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); + + std::vector bias_vec_data(bias_vec_size); // initialized as zero vector + + auto bias_vec_offset = + builder.CreateVector(reinterpret_cast(bias_vec_data.data()), raw_bias_vec_size); + + auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); + + const auto bias_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(bias_buffer_offset); + + auto bias_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); + + auto bias_tensor_offset = + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + gd._tensors.push_back(bias_tensor_offset); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), + bias_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical()); + + // 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(loco::TransposedConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV); + + // TRANSPOSE_CONV's first input is output shape array. + const int32_t outshape_vec_size = 4; + auto outshape_vec_shape_offset = builder.CreateVector(std::vector{outshape_vec_size}); + size_t raw_outshape_vec_size = outshape_vec_size * sizeof(int32_t); + + std::vector outshape_vec_data(outshape_vec_size); + { + // Copy inferred output shape of node + auto out_feature_shape = loco::shape_get(node).as(); + + // Feature tensor in Circle is NHWC + outshape_vec_data.at(0) = out_feature_shape.count().value(); + outshape_vec_data.at(1) = out_feature_shape.height().value(); + outshape_vec_data.at(2) = out_feature_shape.width().value(); + outshape_vec_data.at(3) = out_feature_shape.depth().value(); + } + + auto outshape_vec_offset = builder.CreateVector( + reinterpret_cast(outshape_vec_data.data()), raw_outshape_vec_size); + + auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset); + + const auto outshape_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(outshape_buffer_offset); + + auto outshape_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(outshape_tensor_id)); + + auto outshape_tensor_offset = CreateTensor(builder, outshape_vec_shape_offset, TensorType_INT32, + outshape_buffer_id, name_offset); + gd._tensors.push_back(outshape_tensor_offset); + + // Make input, output and options for operator + std::vector inputs_vec{outshape_tensor_id, get_tensor_index(node->ker()), + get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + // NOTE input and output is inversed to use this function + circle::Padding padding = getOpPadding(node->pad(), node->stride(), ShapeInference::get(node), + ShapeInference::get(node->ifm())); + auto options = CreateTransposeConvOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical()); + + // 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(loco::DepthwiseConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D); + + // Third input of DEPTHWISE_CONV2D of Circle should be bias. We will make (and register to gd) + // dummy zero bias. Bias would be rank 1, have size of output kernel count, and have all zero + // values, i.e. zero bias. + auto *ker = dynamic_cast(node->ker()); + assert(ker); + + int32_t bias_vec_size = ShapeInference::get(ker)._dims[3]; // output_size(C*M) + auto bias_vec_shape_offset = builder.CreateVector(std::vector{bias_vec_size}); + + size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); + std::vector bias_vec_data(bias_vec_size); + auto bias_vec_offset = + builder.CreateVector(reinterpret_cast(bias_vec_data.data()), raw_bias_vec_size); + + auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); + + const auto bias_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(bias_buffer_offset); + + auto bias_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); + + auto bias_tensor_offset = + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + gd._tensors.push_back(bias_tensor_offset); + + std::vector inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), + bias_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + + int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3]; + // multiplier = bias_vec_size(output_size)/ifm_channel_size + auto options = + CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), bias_vec_size / ifm_channel_size); + + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::TensorReduce *node) +{ + uint32_t op_idx; + + switch (node->func()) + { + case loco::ReduceFunc::Mean: + op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MEAN); + break; + + // TODO Support more reduce type operation + default: + INTERNAL_EXN_V("Unsupported reduce type", oops::to_uint32(node->func())); + } + + // Create a vector for axes data + std::vector axes_vec; + auto rank = ShapeInference::get(node->input())._dims.size(); + for (uint32_t i = 0; i < rank; ++i) + if (node->axes()->defined(i)) + axes_vec.push_back(i); + + int32_t axes_vec_size = axes_vec.size(); + auto axes_vec_shape_offset = builder.CreateVector(std::vector{axes_vec_size}); + + size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t); + auto axes_vec_offset = + builder.CreateVector(reinterpret_cast(axes_vec.data()), raw_axes_vec_size); + + auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset); + + const auto axes_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(axes_buffer_offset); + + auto axes_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id)); + + auto axes_tensor_offset = + CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); + gd._tensors.push_back(axes_tensor_offset); + + std::vector inputs_vec{get_tensor_index(node->input()), axes_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateReducerOptions(builder, true); // true is for keep_dims option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ReducerOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::TensorSoftmax *node) +{ + // TODO Support when the input rank of TensorSoftmax is not 2 + assert(ShapeInference::get(node->input())._dims.size() == 2); + + // NOTE Circle only accepts axis when the value is last dimension + assert(node->axis() == ShapeInference::get(node->input())._dims.size() - 1); + + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SOFTMAX); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSoftmaxOptions(builder, 1.0f); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SoftmaxOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +/// @brief Export given node into identity, i.e. CONCATENATION with one input +template +void exportIdentity(NodeT *node, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + std::vector inputs_vec{get_tensor_index(node->arg(0))}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder); // use dummy 0 axis and NONE activation + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ConcatenationOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +/// @brief Export loco nodes as TRANSPOSE +void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, + std::vector &perm_vec_data, SerializedModelData &gd) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE); + + auto options = CreateTransposeOptions(builder); + + // Create constant tensor with perm vector + constexpr int perm_vec_size = 4; + assert(perm_vec_data.size() == perm_vec_size); + auto perm_vec_shape_offset = builder.CreateVector(std::vector{perm_vec_size}); + constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t); + + auto perm_vec_offset = + builder.CreateVector(reinterpret_cast(perm_vec_data.data()), raw_perm_vec_size); + + auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset); + + const auto perm_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(perm_buffer_offset); + + auto perm_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id)); + + auto perm_tensor_offset = + CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); + gd._tensors.push_back(perm_tensor_offset); + + // Create permutation node + + std::vector inputs_vec{get_tensor_index(node->arg(0)), perm_tensor_id}; + std::vector outputs_vec{get_tensor_index(node)}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + constexpr auto options_type = circle::BuiltinOptions::BuiltinOptions_TransposeOptions; + + auto transpose_offset = + CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); + gd._operators.push_back(transpose_offset); +} + +void OperationExporter::visit(loco::FeatureEncode *node) +{ + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + + if (isNHWC(perm)) + { + // Note that Circle represents feature as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + perm_vec_data[0] = perm->axis(loco::FeatureAxis::Count); + perm_vec_data[1] = perm->axis(loco::FeatureAxis::Height); + perm_vec_data[2] = perm->axis(loco::FeatureAxis::Width); + perm_vec_data[3] = perm->axis(loco::FeatureAxis::Depth); + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void OperationExporter::visit(loco::FeatureDecode *node) +{ + auto decoder = dynamic_cast *>(node->decoder()); + auto perm = decoder->perm(); + + if (isNHWC(perm)) + { + // Note that Circle represents feature as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + perm_vec_data[perm->axis(loco::FeatureAxis::Count)] = 0; + perm_vec_data[perm->axis(loco::FeatureAxis::Height)] = 1; + perm_vec_data[perm->axis(loco::FeatureAxis::Width)] = 2; + perm_vec_data[perm->axis(loco::FeatureAxis::Depth)] = 3; + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void OperationExporter::visit(loco::FilterEncode *node) +{ + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + + if (isNHWC(perm)) + { + // Note that Circle represents filter as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + // NOTE In Circle, all tensors means NHWC, so 0 = N, 1 = H, 2 = W, 3 = C + perm_vec_data[0] = perm->axis(loco::FilterAxis::Count); + perm_vec_data[1] = perm->axis(loco::FilterAxis::Height); + perm_vec_data[2] = perm->axis(loco::FilterAxis::Width); + perm_vec_data[3] = perm->axis(loco::FilterAxis::Depth); + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, + std::vector &new_shape_vec, SerializedModelData &gd) +{ + // NOTE Circle currently follows TFLite for this. + // NOTE TFLite has two ways to get new shape paramter, + // one is by attribute 'new_shape' and the other is by input 'shape'. + // Therefore TFLite interpreter calculates Reshape operation correctly + // if one of them is valid. + // However, since NN runtime usually get new shape parameter by input 'shape', + // passing new shape only by attribute can cause some problems. + // Of course, the opposite situation can be occurred in the future. + // To prevent those problems, we pass new shape parameter not only by attribute + // but also by input. + + auto input_shape_shape_vec_offset = + builder.CreateVector(std::vector{(int32_t)new_shape_vec.size()}); + + size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t); + auto input_shape_input_vec_offset = + builder.CreateVector(reinterpret_cast(new_shape_vec.data()), input_shape_vec_size); + auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset); + + const auto input_shape_buffer_id = static_cast(gd._buffers.size()); + gd._buffers.push_back(input_shape_buffer_offset); + + auto input_shape_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id)); + auto input_shape_tensor_offset = CreateTensor( + builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); + gd._tensors.push_back(input_shape_tensor_offset); + + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE); + + std::vector inputs_vec{get_tensor_index(node->arg(0)), input_shape_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + auto new_shape_vec_offset = builder.CreateVector(new_shape_vec); + auto options = CreateReshapeOptions(builder, new_shape_vec_offset); + + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ReshapeOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::DepthwiseFilterEncode *node) +{ + auto ker = node->input(); // [H, W, C, M] + + // Circle represents filter as [1, H, W, C*M] where M is multiplier. + std::vector new_shape_vec(4); + new_shape_vec[0] = 1; + new_shape_vec[1] = ShapeInference::get(ker)._dims[0]; + new_shape_vec[2] = ShapeInference::get(ker)._dims[1]; + new_shape_vec[3] = ShapeInference::get(ker)._dims[2] * ShapeInference::get(ker)._dims[3]; + + exportAsReshape(node, builder, new_shape_vec, gd); +} + +void OperationExporter::visit(loco::BiasAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::FeatureBiasAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +/// @brief Export CONCATENATION of **TWO** tensors only +void OperationExporter::visit(loco::TensorConcat *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder, node->axis()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ConcatenationOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::BiasEncode *encode) { exportIdentity(encode, builder, gd); } + +void OperationExporter::visit(loco::EltwiseAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseMax *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMaximumMinimumOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_MaximumMinimumOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseMul *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MUL); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMulOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_MulOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseSub *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SUB); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSubOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SubOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseDiv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DIV); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateDivOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_DivOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseSqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQRT); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::FixedReshape *node) +{ + std::vector new_shape_vec; + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + assert(node->dim(axis).known()); + new_shape_vec.push_back(node->dim(axis).value()); + } + + exportAsReshape(node, builder, new_shape_vec, gd); +} + +void OperationExporter::visit(loco::TensorBroadcast *) +{ + INTERNAL_EXN("loco graph has loco::TensorBroadcast, which should not exist in the graph"); +} + +void OperationExporter::visit(loco::TensorConstantPad *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_PAD); + + // make padding attribute an input + auto padding = node->padding(); + // get padding vector size + int32_t padding_vec_size = padding->rank(); + // get byte size of vector + size_t padding_vec_byte_size = padding_vec_size * sizeof(int32_t) * 2; // [rank, 2] + // create vector for data + std::vector padding_vec_data(padding_vec_size * 2); + // set data + for (int32_t i = 0; i < padding_vec_size; i++) + { + padding_vec_data.at(i * 2) = padding->front(i); + padding_vec_data.at(i * 2 + 1) = padding->back(i); + } + // create FlatBuffer vector + auto padding_vec_ptr = builder.CreateVector(reinterpret_cast(padding_vec_data.data()), + padding_vec_byte_size); + + // create buffer + auto padding_buffer_ptr = CreateBuffer(builder, padding_vec_ptr); + // get buffer id + const auto padding_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(padding_buffer_ptr); + + // create padding shape vector + auto padding_shape_vec_ptr = builder.CreateVector(std::vector{padding_vec_size, 2}); + // create tensor + auto padding_tensor_ptr = + CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); + // get tensor id + const auto padding_tensor_id = static_cast(gd._tensors.size()); + + gd._tensors.push_back(padding_tensor_ptr); + + std::vector inputs_vec{get_tensor_index(node->input()), padding_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +inline flatbuffers::Offset> +CreateCOpCallOptions(flatbuffers::FlatBufferBuilder &fbb, locoex::COpCall *copCall) +{ + // read attrs in FlexBuffer format and pass them to FlatBuffer builder + flexbuffers::Builder flexbuf; + { + size_t map_start = flexbuf.StartMap(); + + // Note: among attrs of COpCall, 'op' and 'name' won't be included into tflite file + auto names = copCall->attr_names(); + for (auto name : names) + { + if (auto int_val = copCall->attr(name)) + flexbuf.Int(name.c_str(), int_val->val()); + else if (auto float_val = copCall->attr(name)) + flexbuf.Float(name.c_str(), float_val->val()); + else + // TODO Support more attribute types + INTERNAL_EXN_V("Unsupported dtype while writing flexbuffer for customop attr", name); + } + + flexbuf.EndMap(map_start); + flexbuf.Finish(); + } + + auto offset = fbb.CreateVector(flexbuf.GetBuffer()); + + return offset; +} + +void OperationExporter::visit(locoex::COpCall *call) +{ + // Registering this custom op name into tflite Operator Codes table + uint32_t op_idx = gd.registerCustomOpcode(call->op()); + + std::vector inputs_vec; + { + inputs_vec.resize(call->arity()); + for (uint32_t i = 0; i < call->arity(); i++) + inputs_vec[i] = get_tensor_index(call->arg(i)); + } + + std::vector outputs_vec{get_tensor_index(static_cast(call))}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + auto custom_options = CreateCOpCallOptions(builder, call); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_NONE, // builtin_options_type + 0, // built-in option + custom_options, // custom options + circle::CustomOptionsFormat_FLEXBUFFERS); + + gd._operators.push_back(op_offset); +} + +void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, + SerializedModelData &data) +{ + // TODO Use explicit tagging to prevent possible mistake + auto isNoOp = [](loco::Node *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 canonical_node = dynamic_cast(node)) + { // TODO Consider removing this later + OperationExporter exporter{builder, data}; + canonical_node->accept(&exporter); + } + else if (auto tfl_node = dynamic_cast(node)) + { + OperationExporter exporter{builder, data}; + tfl_node->accept(&exporter); + } + else if (auto circle_node = dynamic_cast(node)) + { + OperationExporter exporter{builder, data}; + circle_node->accept(&exporter); + } + else if (dynamic_cast(node)) + { + OperationExporter exporter{builder, data}; + exporter.visit(dynamic_cast(node)); + } + else + { + INTERNAL_EXN("Node with unsupported dialect found"); + } +} + +} // namespace + +namespace exo +{ +namespace circle_detail +{ + +void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + exportNode(node, builder, gd); + } +} + +} // namespace circle_detail +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleOperationExporter.h b/compiler/exo/src/Circle/CircleOperationExporter.h new file mode 100644 index 000000000..19dadbfd1 --- /dev/null +++ b/compiler/exo/src/Circle/CircleOperationExporter.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_OPERATION_EXPORTER_H__ +#define __CIRCLE_OPERATION_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include + +namespace exo +{ +namespace circle_detail +{ + +/** + * @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 &gd); + +} // namespace circle_detail +} // namespace exo + +#endif // __CIRCLE_OPERATION_EXPORTER_H__ diff --git a/compiler/exo/src/Circle/CircleTensorExporter.cpp b/compiler/exo/src/Circle/CircleTensorExporter.cpp new file mode 100644 index 000000000..efceae55d --- /dev/null +++ b/compiler/exo/src/Circle/CircleTensorExporter.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleTensorExporter.h" +#include "CircleTypeInference.h" +#include "ShapeInference.h" + +// TODO Fix include style +#include "loco/IR/Algorithm.h" +#include "loco/IR/CanonicalNode.h" +#include "loco/IR/CanonicalNodeVisitor.h" +#include "loco/IR/DataTypeTraits.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +using namespace circle; +using namespace flatbuffers; + +namespace +{ + +using namespace exo; +using namespace exo::circle_detail; + +class TFLTensorInfo +{ +public: + TFLTensorInfo() = 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: + locoex::TFLConst *tfl_content(void) const { return _tfl_content; } + void tfl_content(locoex::TFLConst *c) { _tfl_content = c; } + +private: + std::string _name; + + circle::TensorType _dtype; + ShapeDescription _shape; + + // TODO Find a better design + loco::ConstGen *_content = nullptr; // TODO deprecate + locoex::TFLConst *_tfl_content = nullptr; +}; + +using TFLTensorContext = std::vector; + +struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor +{ + bool visit(loco::BiasEncode *) final + { + // BiasEncode is always noop + return true; + } + + bool visit(loco::FilterEncode *node) final + { + auto encoder = dynamic_cast *>(node->encoder()); + if (encoder != nullptr) + { + auto perm = encoder->perm(); + return isNHWC(perm); + } + return false; + } + + bool visit(loco::FeatureEncode *node) final + { + auto encoder = dynamic_cast *>(node->encoder()); + if (encoder != nullptr) + { + auto perm = encoder->perm(); + return isNHWC(perm); + } + return false; + } + + bool visit(loco::FeatureDecode *node) final + { + auto decoder = dynamic_cast *>(node->decoder()); + if (decoder != nullptr) + { + auto perm = decoder->perm(); + return isNHWC(perm); + } + return false; + } + + // Return false by default + bool visit(loco::Node *) final { return false; } +}; + +bool isNoOp(loco::Node *node) +{ + if (auto canonical_node = dynamic_cast(node)) + { + NoOpDetector d; + return canonical_node->accept(&d); + } + return false; +} + +void allocateCircleTensor(loco::Node *node, TFLTensorContext &ctx) +{ + if (isNoOp(node)) + { + assert(node->arity() == 1 && node->arg(0) != nullptr); + set_tensor_index(node, get_tensor_index(node->arg(0))); + return; + } + + auto tensor_index = static_cast(ctx.size()); + // TODO Use Graph-level metadata for Input & Output + auto tensor_name = "t_" + std::to_string(tensor_index); + + TFLTensorInfo tensor_info; + + tensor_info.name(tensor_name); + tensor_info.dtype(TypeInference::get(node)); + tensor_info.shape(ShapeInference::get(node)); + + tensor_info.tfl_content(dynamic_cast(node)); + + set_tensor_index(node, tensor_index); + + ctx.emplace_back(tensor_info); +} + +} // namespace + +namespace +{ + +flatbuffers::Offset> encodeShape(FlatBufferBuilder &builder, + const ShapeDescription &shape) +{ + assert(shape._rank_known && "unknown number of dimensions is not supported"); + return builder.CreateVector(shape._dims); +} + +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, NodeT *) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBufferByDType(FlatBufferBuilder &builder, + locoex::TFLConst *c) +{ + using NativeType = typename loco::DataTypeImpl
::Type; + + std::vector raw_data; + const uint32_t size = c->size
(); + raw_data.reserve(size); + for (uint32_t i = 0; i < size; ++i) + { + raw_data.push_back(c->at
(i)); + } + const size_t raw_size = size * sizeof(NativeType); + auto array_offset = builder.CreateVector(reinterpret_cast(raw_data.data()), raw_size); + return CreateBuffer(builder, array_offset); +} + +template <> +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, locoex::TFLConst *c) +{ + if (c->dtype() == loco::DataType::FLOAT32) + { + return encodeOpBufferByDType(builder, c); + } + else if (c->dtype() == loco::DataType::S32) + { + return encodeOpBufferByDType(builder, c); + } + + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); +} + +} // namespace + +namespace exo +{ +namespace circle_detail +{ + +void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder, + SerializedModelData &gd) +{ + // Create and register output tensor shape + auto shape_offset = encodeShape(builder, info.shape()); + + // encode and register output tensor buffer + auto buffer = info.tfl_content() == nullptr ? encodeOpBuffer(builder) + : encodeOpBuffer(builder, info.tfl_content()); + + auto buffer_id = static_cast(gd._buffers.size()); + gd._buffers.push_back(buffer); + + auto name_offset = builder.CreateString(info.name()); + auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, + /*quantization*/ 0, /*is_variable*/ false); + gd._tensors.push_back(tensor_offset); +} + +void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + TFLTensorContext tensor_ctx; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + allocateCircleTensor(node, tensor_ctx); + } + + // 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); + gd._buffers.push_back(buffer); + + for (const auto &tensor_info : tensor_ctx) + { + exportOpDefinedTensor(tensor_info, builder, gd); + } +} + +} // namespace circle_detail +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleTensorExporter.h b/compiler/exo/src/Circle/CircleTensorExporter.h new file mode 100644 index 000000000..39d8e1b86 --- /dev/null +++ b/compiler/exo/src/Circle/CircleTensorExporter.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_TENSOR_EXPORTER_H__ +#define __CIRCLE_TENSOR_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include + +#include + +namespace exo +{ +namespace circle_detail +{ + +/** + * @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 &gd); + +} // namespace circle_detail +} // namespace exo + +#endif // __CIRCLE_TENSOR_EXPORTER_H__ diff --git a/compiler/exo/src/Circle/CircleTypeInference.cpp b/compiler/exo/src/Circle/CircleTypeInference.cpp new file mode 100644 index 000000000..a1e92b884 --- /dev/null +++ b/compiler/exo/src/Circle/CircleTypeInference.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleTypeInference.h" + +#include "circle_schema_generated.h" + +#include "Dialect/Service/TFLTypeInferenceRule.h" +#include "Dialect/IR/TFLDialect.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +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 + default: + break; + } + + INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype)); +} + +} // namespace + +namespace exo +{ +namespace circle_detail +{ + +circle::TensorType TypeInference::get(loco::Node *node) +{ + assert(loco::dtype_known(node)); + return translateLocoTypeToCircle(loco::dtype_get(node)); +} + +} // namespace circle_detail +} // namespace exo diff --git a/compiler/exo/src/Circle/CircleTypeInference.h b/compiler/exo/src/Circle/CircleTypeInference.h new file mode 100644 index 000000000..9c1730233 --- /dev/null +++ b/compiler/exo/src/Circle/CircleTypeInference.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_TYPE_INFERENCE_H__ +#define __CIRCLE_TYPE_INFERENCE_H__ + +#include "CircleExporterUtils.h" + +#include + +namespace exo +{ +namespace circle_detail +{ + +/** + * @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 circle_detail +} // namespace exo + +#endif // __CIRCLE_TYPE_INFERENCE_H__ diff --git a/compiler/exo/src/Conversion/AvgPool2DConverter.cpp b/compiler/exo/src/Conversion/AvgPool2DConverter.cpp new file mode 100644 index 000000000..a95518ac6 --- /dev/null +++ b/compiler/exo/src/Conversion/AvgPool2DConverter.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AvgPool2DConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include + +namespace exo +{ +/** + * @brief Converts loco::AvgPool2D to locoex::TFLAveragePool2D + * + * How it works: (note: ten->fea means input: tensor, output: feature) + * + * Before: + * Foo ---- FeatureEncode ---- AvgPool2D ---- FeatureDecode ---- Bar + * ten->ten ten->fea fea->fea fea->ten ten->ten + * + * After: AvgPool2D + * / + * Foo -- FeatureEncode - FeatureDecode - TFLAvgPool2D - FeatureEncode - FeatureDecode -- Bar + * ten->ten ten->fea fea->ten ten->ten ten->fea fea->ten ten->ten + * + * @note This method replaces AvgPool2D with "FeatureDecode -- TFLAvgPool2D -- FeatureEncode". + * Redundant nodes will be removed during transforms. + */ +bool AvgPool2DConverter::convert(loco::AvgPool2D *origin) +{ + auto *graph = origin->graph(); + + auto dec = make_feature_decode(origin->ifm()); + auto tfl_average = graph->nodes()->create(); + { + tfl_average->value(dec); + + // set attributes + tfl_average->stride()->w(origin->stride()->horizontal()); + tfl_average->stride()->h(origin->stride()->vertical()); + + tfl_average->filter()->w(origin->window()->horizontal()); + tfl_average->filter()->h(origin->window()->vertical()); + + auto pad = origin->pad(); + if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0) + tfl_average->padding(locoex::Padding::VALID); + else + // TODO This is necessary, but not sufficient condition. More rigorous check required + tfl_average->padding(locoex::Padding::SAME); + + tfl_average->fusedActivationFunction(locoex::FusedActFunc::NONE); + } + auto enc = make_feature_encode(tfl_average); + + // replace canonical node + loco::replace(origin).with(enc); + origin->ifm(nullptr); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/AvgPool2DConverter.h b/compiler/exo/src/Conversion/AvgPool2DConverter.h new file mode 100644 index 000000000..f66d02eb6 --- /dev/null +++ b/compiler/exo/src/Conversion/AvgPool2DConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_AVGPOOL2D_CONVERTER__ +#define __CONVERSION_AVGPOOL2D_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::AvgPool2D to locoex::TFLAveragePool2D + */ +class AvgPool2DConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::AvgPool2DConverter"; } + +public: + bool convert(loco::AvgPool2D *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_AVGPOOL2D_CONVERTER__ diff --git a/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp b/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp new file mode 100644 index 000000000..4daf905f8 --- /dev/null +++ b/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanonicalNodeConverter.h" + +// This file is to make sure compilation of "CanonicalNodeConverter.h" diff --git a/compiler/exo/src/Conversion/CanonicalNodeConverter.h b/compiler/exo/src/Conversion/CanonicalNodeConverter.h new file mode 100644 index 000000000..76f73d888 --- /dev/null +++ b/compiler/exo/src/Conversion/CanonicalNodeConverter.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_CANONICAL_NODE_CONVERTER_H__ +#define __CONVERSION_CANONICAL_NODE_CONVERTER_H__ + +#include "Convert.h" + +#include +#include +#include + +namespace exo +{ + +/** + * @brief Class to convert a canonical node to TFL node + * + * TODO Find a better name + */ +template class CanonicalNodeConverter : public logo::Pass +{ +public: + virtual const char *name(void) const { return nullptr; } + +public: + bool run(loco::Graph *graph); + +protected: + virtual bool convert(CanonicalType *node) = 0; +}; + +template +bool CanonicalNodeConverter::run(loco::Graph *graph) +{ + auto active_nodes = loco::active_nodes(loco::output_nodes(graph)); + bool changed = false; + + for (auto node : active_nodes) + { + // TODO Generalize this to all loco dialects + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto the_node = dynamic_cast(node); + if (the_node != nullptr) + { + if (convert(the_node)) + changed = true; + } + } + } + + return changed; +} + +} // namespace exo + +#endif //__CONVERSION_CANONICAL_NODE_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/ConstGenConverter.cpp b/compiler/exo/src/Conversion/ConstGenConverter.cpp new file mode 100644 index 000000000..b2e2b4bdb --- /dev/null +++ b/compiler/exo/src/Conversion/ConstGenConverter.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConstGenConverter.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Check.h" + +#include + +#include + +namespace exo +{ + +bool ConstGenConverter::convert(loco::ConstGen *constgen) +{ + auto *graph = constgen->graph(); + + auto tfl_const = graph->nodes()->create(); + { + if (constgen->dtype() == loco::DataType::FLOAT32) + { + tfl_const->dtype(loco::DataType::FLOAT32); + + tfl_const->rank(constgen->rank()); + for (uint32_t axis = 0; axis < constgen->rank(); axis++) + tfl_const->dim(axis) = constgen->dim(axis); + + auto size = constgen->size(); + tfl_const->size(size); + + for (uint32_t i = 0; i < size; ++i) + { + tfl_const->at(i) = constgen->at(i); + } + } + else + INTERNAL_EXN_V("Unsupported DataType", oops::to_uint32(constgen->dtype())); + } + + loco::replace(constgen).with(tfl_const); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/ConstGenConverter.h b/compiler/exo/src/Conversion/ConstGenConverter.h new file mode 100644 index 000000000..613ccd0e6 --- /dev/null +++ b/compiler/exo/src/Conversion/ConstGenConverter.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_CONSTGEN_CONVERTER_H__ +#define __CONVERSION_CONSTGEN_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +class ConstGenConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::ConstGenConverter"; } + +public: + bool convert(loco::ConstGen *constgen) final; +}; + +} // namespace exo + +#endif // __CONVERSION_CONSTGEN_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/ConstGenConverter.test.cpp b/compiler/exo/src/Conversion/ConstGenConverter.test.cpp new file mode 100644 index 000000000..f7a577242 --- /dev/null +++ b/compiler/exo/src/Conversion/ConstGenConverter.test.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConstGenConverter.h" +#include "ReluConverter.h" + +#include "Dialect/IR/TFLNodes.h" +#include "TestGraph.h" +#include "TestHelper.h" + +#include + +#include + +TEST(TFLConstGenConverterTest, ConstGen_Relu) +{ + exo::test::ExampleGraph g; + + // set constgen + { + g.constgen->dtype(loco::DataType::FLOAT32); + g.constgen->shape({2, 1}); + g.constgen->size(2); + + g.constgen->at(0) = 0.5; + g.constgen->at(1) = -0.5; + } + + // let's convert + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.add_pass(); + + test_phase.run(g.graph()); + } + + auto tfl_const = exo::test::find_first_node_bytype(g.graph()); + auto tfl_relu = exo::test::find_first_node_bytype(g.graph()); + + ASSERT_TRUE(tfl_const != nullptr and tfl_relu != nullptr); + ASSERT_TRUE(tfl_relu->features() == tfl_const); + + ASSERT_TRUE(tfl_const->rank() == g.constgen->rank()); + ASSERT_TRUE(tfl_const->dim(0) == g.constgen->dim(0)); + ASSERT_TRUE(tfl_const->dim(1) == g.constgen->dim(1)); + ASSERT_TRUE(tfl_const->at(0) == + g.constgen->at(0)); + ASSERT_TRUE(tfl_const->at(1) == + g.constgen->at(1)); +} diff --git a/compiler/exo/src/Conversion/Conv2DConverter.cpp b/compiler/exo/src/Conversion/Conv2DConverter.cpp new file mode 100644 index 000000000..c8120171d --- /dev/null +++ b/compiler/exo/src/Conversion/Conv2DConverter.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Conv2DConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include +#include +#include + +namespace exo +{ +/** + * @brief Converts loco::Conv2D to locoex::TFLConv2D + * @note Because TFLConv2D accepts input and filter of loco::Domain::Tensor, + * loco::FeatureDecode and loco::FilterDecode will be inserted as an inputs + * to meet domain invariant. + * Please refer to the comment in AvgPool2DConvert. + */ +bool Conv2DConverter::convert(loco::Conv2D *origin) +{ + auto *graph = origin->graph(); + + assert(origin->ifm()); + assert(origin->ker()); + + auto tfl_conv2d = graph->nodes()->create(); + { + tfl_conv2d->stride()->w(origin->stride()->horizontal()); + tfl_conv2d->stride()->h(origin->stride()->vertical()); + + auto pad = origin->pad(); + if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0) + tfl_conv2d->padding(locoex::Padding::VALID); + else + // TODO This is necessary, but not sufficient condition. More rigorous check required + tfl_conv2d->padding(locoex::Padding::SAME); + + tfl_conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE); + } + + // let's create a new graph connection with tfl_conv2d + { + // input + auto feature_dec = make_feature_decode(origin->ifm()); + tfl_conv2d->input(feature_dec); + + // filter + auto filter_dec = make_filter_decode(origin->ker()); + tfl_conv2d->filter(filter_dec); + + // bias + auto zero_const = graph->nodes()->create(); + { + assert(loco::shape_known(origin)); + assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32); + + auto output_depth = loco::shape_get(origin->ker()).as().count(); + + zero_const->dtype(loco::DataType::FLOAT32); + zero_const->rank(1); + zero_const->dim(0) = output_depth; + zero_const->size(output_depth.value()); + for (uint32_t x = 0; x < output_depth.value(); x++) + zero_const->at(x) = 0.0; + } + tfl_conv2d->bias(zero_const); + + // output + auto feature_enc = make_feature_encode(tfl_conv2d); + + // replace canonical node + loco::replace(origin).with(feature_enc); + origin->ifm(nullptr); + } + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/Conv2DConverter.h b/compiler/exo/src/Conversion/Conv2DConverter.h new file mode 100644 index 000000000..95b3fbfae --- /dev/null +++ b/compiler/exo/src/Conversion/Conv2DConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_CONV2D_CONVERTER__ +#define __CONVERSION_CONV2D_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::Conv2D to locoex::TFLConv2D + */ +class Conv2DConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::Conv2DConverter"; } + +public: + bool convert(loco::Conv2D *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_CONV2D_CONVERTER__ diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp new file mode 100644 index 000000000..5959fcc45 --- /dev/null +++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DepthwiseConv2DConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include +#include +#include + +namespace exo +{ + +bool DepthwiseConv2DConverter::convert(loco::DepthwiseConv2D *origin) +{ + // Filter shape is required + if (not loco::shape_known(origin->ker())) + return false; + + auto filter_shape = loco::shape_get(origin->ker()).as(); + + if ((origin->ifm() == nullptr) or (origin->ker() == nullptr)) + return false; + + auto *graph = origin->graph(); + + auto tfl_dw_conv2d = graph->nodes()->create(); + { + tfl_dw_conv2d->stride()->w(origin->stride()->horizontal()); + tfl_dw_conv2d->stride()->h(origin->stride()->vertical()); + + auto pad = origin->pad(); + if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0) + tfl_dw_conv2d->padding(locoex::Padding::VALID); + else + // TODO This is necessary, but not sufficient condition. More rigorous check required + tfl_dw_conv2d->padding(locoex::Padding::SAME); + + tfl_dw_conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE); + + uint32_t multiplier = filter_shape.multiplier().value(); + EXO_ASSERT(multiplier < std::numeric_limits::max(), + "Multiplier is too big that casting may occur unintended behavior") + + tfl_dw_conv2d->depthMultiplier(static_cast(multiplier)); + } + + // let's create a new graph connection with tfl_dw_conv2d + { + // ifm --- feature_dec --- tfl_dw_conv2d + auto feature_dec = make_feature_decode(origin->ifm()); + tfl_dw_conv2d->input(feature_dec); + + // ker --- filter_dec(H x W x C x M) --- reshape(1 x H x W x CM) --- tfl_dw_conv2d + auto filter_dec = make_dw_filter_decode(origin->ker()); + + auto reshape = graph->nodes()->create(); + reshape->tensor(filter_dec); + + int32_t new_shape[4] = { + 1, static_cast(filter_shape.height().value()), + static_cast(filter_shape.width().value()), + static_cast(filter_shape.depth().value() * filter_shape.multiplier().value())}; + locoex::set_new_shape(reshape, new_shape, 4); + + tfl_dw_conv2d->filter(reshape); + + // bias + auto zero_const = graph->nodes()->create(); + { + assert(loco::shape_known(origin)); + assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32); + + // bias size is C * M + uint32_t bias_size = filter_shape.depth().value() * filter_shape.multiplier().value(); + + zero_const->dtype(loco::DataType::FLOAT32); + zero_const->rank(1); + zero_const->dim(0) = bias_size; + zero_const->size(bias_size); + for (uint32_t x = 0; x < bias_size; x++) + zero_const->at(x) = 0.0; + } + tfl_dw_conv2d->bias(zero_const); + + // output + auto feature_enc = make_feature_encode(tfl_dw_conv2d); + + // replace canonical node + loco::replace(origin).with(feature_enc); + origin->ifm(nullptr); + } + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h new file mode 100644 index 000000000..57cc01e5e --- /dev/null +++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_DEPTHWISECONV2D_CONVERTER__ +#define __CONVERSION_DEPTHWISECONV2D_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::DepthwiseConv2D to locoex::TFLDepthwiseConv2D and auxiliary + * + * + * + * + * IFM -------- DepthwiseConv2D --- Out + * [Feature] / [Feature] + * / + * KER ------- + * [DWFilter] + * + * + * + * TFLConst (bias) --------------------------- + * \ + * IFM ------ FeatureDecode ------------------ TFLDepthwiseConv2D --- FeatureEncode --- Out + * [Feature] [Tensor] / [Tensor] [Feature] + * / + * KER ------- DepthwiseFilterDecode --- TFLReshape + * [DWFilter] [Tensor / H W C M] [Tensor / 1 H W CM] + * + */ +class DepthwiseConv2DConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::DepthwiseConv2DConverter"; } + +public: + bool convert(loco::DepthwiseConv2D *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_DEPTHWISECONV2D_CONVERTER__ diff --git a/compiler/exo/src/Conversion/EltwiseAddConverter.cpp b/compiler/exo/src/Conversion/EltwiseAddConverter.cpp new file mode 100644 index 000000000..557f47944 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseAddConverter.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EltwiseAddConverter.h" + +#include "EltwiseBinaryConverter.h" + +namespace exo +{ + +bool EltwiseAddConverter::convert(loco::EltwiseAdd *origin) +{ + return EltwiseBinaryConvert(origin); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseAddConverter.h b/compiler/exo/src/Conversion/EltwiseAddConverter.h new file mode 100644 index 000000000..97e1071b5 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseAddConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_ELTWISEADD_CONVERTER_H__ +#define __CONVERSION_ELTWISEADD_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseAdd to TFLAdd + */ +class EltwiseAddConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseAddConverter"; } + +public: + bool convert(loco::EltwiseAdd *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_ELTWISEADD_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseBinaryConverter.h b/compiler/exo/src/Conversion/EltwiseBinaryConverter.h new file mode 100644 index 000000000..095da9e5c --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseBinaryConverter.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_ELTWISEBINARY_CONVERTER_H__ +#define __CONVERSION_ELTWISEBINARY_CONVERTER_H__ + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +#include + +namespace +{ + +template +class EltwiseBinInputHandler : public exo::InputHandler +{ +public: + void handover(ELTWISEBIN *origin, TFLBIN *replacer) override + { + assert(origin && replacer); + replacer->x(origin->lhs()); + replacer->y(origin->rhs()); + } + + std::vector getInputsToConvert(ELTWISEBIN *origin) override + { + assert(origin); + std::vector inputs({origin->lhs(), origin->rhs()}); + return inputs; + } + + void set(TFLBIN *replacer, std::vector &to) override + { + assert(to.size() == 2); + + replacer->x(to.at(0)); + replacer->y(to.at(1)); + } + + void nullify(ELTWISEBIN *origin) override + { + assert(origin); + origin->lhs(nullptr); + origin->rhs(nullptr); + } +}; + +template void init_fused_act_func(TFLBIN *); + +template <> inline void init_fused_act_func(locoex::TFLAdd *node) +{ + node->fusedActivationFunction(locoex::FusedActFunc::NONE); +} + +template <> inline void init_fused_act_func(locoex::TFLMul *node) +{ + node->fusedActivationFunction(locoex::FusedActFunc::NONE); +} + +template <> inline void init_fused_act_func(locoex::TFLSub *node) +{ + node->fusedActivationFunction(locoex::FusedActFunc::NONE); +} + +template <> inline void init_fused_act_func(locoex::TFLDiv *node) +{ + node->fusedActivationFunction(locoex::FusedActFunc::NONE); +} + +} // namespace + +namespace exo +{ + +template bool EltwiseBinaryConvert(ELTWISEBIN *origin) +{ + EltwiseBinInputHandler input_handler; + exo::DomainConverter domain_converter; + + auto tfl_node = domain_converter.template convert(origin, input_handler); + + if (tfl_node == nullptr) + return false; + + init_fused_act_func(tfl_node); + + return true; +} + +} // namespace exo + +#endif // __CONVERSION_ELTWISEBINARY_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseDivConverter.cpp b/compiler/exo/src/Conversion/EltwiseDivConverter.cpp new file mode 100644 index 000000000..dc8eae461 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseDivConverter.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EltwiseDivConverter.h" + +#include "EltwiseBinaryConverter.h" + +namespace exo +{ + +bool EltwiseDivConverter::convert(loco::EltwiseDiv *origin) +{ + return EltwiseBinaryConvert(origin); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseDivConverter.h b/compiler/exo/src/Conversion/EltwiseDivConverter.h new file mode 100644 index 000000000..06b2d685b --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseDivConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_ELTWISEDIV_CONVERTER_H__ +#define __CONVERSION_ELTWISEDIV_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseDiv to TFLDiv + */ +class EltwiseDivConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseDivConverter"; } + +public: + bool convert(loco::EltwiseDiv *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_ELTWISEDIV_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp b/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp new file mode 100644 index 000000000..dd7d34440 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp @@ -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. + */ + +#include "EltwiseMaxConverter.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace +{ + +class EltwiseMaxInputHandler : public exo::InputHandler +{ +public: + void handover(loco::EltwiseMax *origin, locoex::TFLMaximum *replacer) override + { + replacer->x(origin->lhs()); + replacer->y(origin->rhs()); + } + + std::vector getInputsToConvert(loco::EltwiseMax *origin) override + { + std::vector inputs({origin->lhs(), origin->rhs()}); + return inputs; + } + + void set(locoex::TFLMaximum *replacer, std::vector &to) override + { + assert(to.size() == 2); + + replacer->x(to.at(0)); + replacer->y(to.at(1)); + } + + void nullify(loco::EltwiseMax *origin) override + { + assert(origin); + origin->lhs(nullptr); + origin->rhs(nullptr); + } +}; + +} // namespace + +namespace exo +{ + +bool EltwiseMaxConverter::convert(loco::EltwiseMax *origin) +{ + EltwiseMaxInputHandler input_handler; + exo::DomainConverter domain_converter; + + auto tfl_new = domain_converter.convert(origin, input_handler); + + return (tfl_new != nullptr); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseMaxConverter.h b/compiler/exo/src/Conversion/EltwiseMaxConverter.h new file mode 100644 index 000000000..708745419 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseMaxConverter.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 __CONVERSION_ELTWISEMAX_CONVERTER_H__ +#define __CONVERSION_ELTWISEMAX_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseMax to TFLMaximum + */ +class EltwiseMaxConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseMaxConverter"; } + +public: + bool convert(loco::EltwiseMax *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_ELTWISEMAX_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseMulConverter.cpp b/compiler/exo/src/Conversion/EltwiseMulConverter.cpp new file mode 100644 index 000000000..f7a4b8298 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseMulConverter.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EltwiseMulConverter.h" + +#include "EltwiseBinaryConverter.h" + +namespace exo +{ + +bool EltwiseMulConverter::convert(loco::EltwiseMul *origin) +{ + return EltwiseBinaryConvert(origin); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseMulConverter.h b/compiler/exo/src/Conversion/EltwiseMulConverter.h new file mode 100644 index 000000000..4f73484c0 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseMulConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_ELTWISEMUL_CONVERTER_H__ +#define __CONVERSION_ELTWISEMUL_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseMul to TFLMul + */ +class EltwiseMulConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseMulConverter"; } + +public: + bool convert(loco::EltwiseMul *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_ELTWISEMUL_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp b/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp new file mode 100644 index 000000000..6dead7dc6 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EltwiseSqrtConverter.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace +{ + +class EltwiseSqrtInputHandler : public exo::InputHandler +{ +public: + void handover(loco::EltwiseSqrt *origin, locoex::TFLSqrt *replacer) override + { + replacer->x(origin->input()); + } + + std::vector getInputsToConvert(loco::EltwiseSqrt *origin) override + { + std::vector inputs({origin->input()}); + return inputs; + } + + void set(locoex::TFLSqrt *replacer, std::vector &to) override + { + assert(to.size() == 1); + + replacer->x(to.at(0)); + } + + void nullify(loco::EltwiseSqrt *origin) override { origin->input(nullptr); } +}; + +} // namespace + +namespace exo +{ + +bool EltwiseSqrtConverter::convert(loco::EltwiseSqrt *origin) +{ + EltwiseSqrtInputHandler input_handler; + exo::DomainConverter domain_converter; + + auto tfl_new = domain_converter.convert(origin, input_handler); + + return (tfl_new != nullptr); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseSqrtConverter.h b/compiler/exo/src/Conversion/EltwiseSqrtConverter.h new file mode 100644 index 000000000..5ee3185ff --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseSqrtConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ELTWISE_SQRT_CONVERTER_H__ +#define __ELTWISE_SQRT_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseSqrt to TFLSqrt + */ +class EltwiseSqrtConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseSqrtConverter"; } + +public: + bool convert(loco::EltwiseSqrt *origin) final; +}; + +} // namespace exo + +#endif // __ELTWISE_SQRT_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/EltwiseSubConverter.cpp b/compiler/exo/src/Conversion/EltwiseSubConverter.cpp new file mode 100644 index 000000000..5647c47a2 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseSubConverter.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EltwiseSubConverter.h" + +#include "EltwiseBinaryConverter.h" + +namespace exo +{ + +bool EltwiseSubConverter::convert(loco::EltwiseSub *origin) +{ + return EltwiseBinaryConvert(origin); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/EltwiseSubConverter.h b/compiler/exo/src/Conversion/EltwiseSubConverter.h new file mode 100644 index 000000000..d61b76ec0 --- /dev/null +++ b/compiler/exo/src/Conversion/EltwiseSubConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_ELTWISESUB_CONVERTER_H__ +#define __CONVERSION_ELTWISESUB_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::EltwiseSub to TFLSub + */ +class EltwiseSubConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::EltwiseSubConverter"; } + +public: + bool convert(loco::EltwiseSub *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_ELTWISESUB_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp b/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp new file mode 100644 index 000000000..b9aaf140b --- /dev/null +++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FeatureBiasAddConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" + +#include +#include + +#include + +namespace +{ + +inline void init_fused_act_func(locoex::TFLAdd *node) +{ + node->fusedActivationFunction(locoex::FusedActFunc::NONE); +} + +} // namespace + +namespace exo +{ + +/** + * @brief Converts loco::FeatureBiasAdd to locoex::TFLAdd + * + * Before: + * Foo ---+ + * | + * loco::FeatureBiasAdd - FeatureDecode - ... + * | + * Bar - BiasEncode --+ + * + * After: + * + * Foo - loco::FeatureDecode --+ loco::FeatureBiasAdd + * |(x) + * TFLAdd -- loco::FeatureEncode - FeatureDecode - ... + * |(y) + * Bar - BiasEncode - loco::BiasDecode --+ + */ +bool FeatureBiasAddConverter::convert(loco::FeatureBiasAdd *origin) +{ + auto *graph = origin->graph(); + + auto tfl_add = graph->nodes()->create(); + + // handling input x + assert(loco::shape_get(origin->value()).domain() == loco::Domain::Feature); + + auto fea_dec = make_feature_decode(origin->value()); + tfl_add->x(fea_dec); + + // handling input y + auto bias_dec = graph->nodes()->create(); + assert(bias_dec != nullptr); + + bias_dec->input(origin->bias()); + + tfl_add->y(bias_dec); + + // fused activation function + init_fused_act_func(tfl_add); + + // handling output + auto fea_enc = make_feature_encode(tfl_add); + + loco::replace(origin).with(fea_enc); + origin->value(nullptr); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/FeatureBiasAddConverter.h b/compiler/exo/src/Conversion/FeatureBiasAddConverter.h new file mode 100644 index 000000000..5c4f10213 --- /dev/null +++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_FEATUREBIASADD_CONVERTER__ +#define __CONVERSION_FEATUREBIASADD_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +class FeatureBiasAddConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::TFLAddConverter"; } + +public: + bool convert(loco::FeatureBiasAdd *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_FEATUREBIASADD_CONVERTER__ diff --git a/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp b/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp new file mode 100644 index 000000000..f3c4a5f81 --- /dev/null +++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FeatureBiasAddConverter.h" + +#include "GraphBlock.h" +#include "Dialect/IR/TFLNodes.h" + +#include "TestGraph.h" +#include "TestHelper.h" + +#include + +#include + +TEST(FeatureBiasAddConverterTest, basic_test) +{ + exo::test::ExampleGraph g; + + { // attrib setting + // pull + g.pull->dtype(loco::DataType::FLOAT32); + g.pull->shape({1, 2, 2, 3}); + + // bias value + g.constgen->dtype(loco::DataType::FLOAT32); + g.constgen->shape({3}); + g.constgen->size(3); + + g.constgen->at(0) = 0.5; + g.constgen->at(1) = 1; + g.constgen->at(2) = 1.5; + } + + EXO_TEST_ASSERT_NODE_COUNT({g.push}, 7); // sanity check + + // let's convert!! + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + + test_phase.run(g.graph()); + + /* + Expected: + + Pull - FeatureEncoder - FeatureDecode - TFLAdd - FeatureEncode - FeatureDecode - Push + | + ConstGen - BiasEncode - BiasDecode ---+ + */ + } + + // check surroundings + auto tfl_add = exo::test::find_first_node_bytype(g.graph()); + { + ASSERT_TRUE(tfl_add != nullptr); + + // input x and its pred + { + auto actual_fea_dec = dynamic_cast(tfl_add->x()); + ASSERT_TRUE(actual_fea_dec != nullptr); + + auto actual_fea_enc = dynamic_cast(actual_fea_dec->input()); + ASSERT_TRUE(actual_fea_enc != nullptr); + ASSERT_TRUE(actual_fea_enc == g.fea_enc); + } + + // input y and its pred + { + auto actual_bias_dec = dynamic_cast(tfl_add->y()); + ASSERT_TRUE(actual_bias_dec != nullptr); + + auto actual_bias_enc = dynamic_cast(actual_bias_dec->input()); + ASSERT_TRUE(actual_bias_enc != nullptr); + ASSERT_TRUE(actual_bias_enc == g.bias_enc); + } + + // output check + { + auto actual_fea_enc = exo::test::get_only_succ(tfl_add); + ASSERT_TRUE(actual_fea_enc != nullptr); + + auto actual_fea_dec = exo::test::get_only_succ(actual_fea_enc); + ASSERT_TRUE(actual_fea_dec != nullptr); + ASSERT_TRUE(actual_fea_dec == g.fea_dec); + } + } +} diff --git a/compiler/exo/src/Conversion/MatMulConverter.cpp b/compiler/exo/src/Conversion/MatMulConverter.cpp new file mode 100644 index 000000000..b1158b73d --- /dev/null +++ b/compiler/exo/src/Conversion/MatMulConverter.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MatMulConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include +#include +#include + +namespace exo +{ +/** + * @brief Converts loco::MatMul to locoex::TFLFullyConnected + * @note Because TFLFullyConnected accepts input and weights of loco::Domain::Matrix, + * loco::MatrixDecode will be inserted as an input and weights + * to meet domain invariant. + * + * How it works: + * + * Before: + * Foo1 ---- MatrixEncode ---- MatMul ---- MatrixDecode ---- Bar + * Foo2 ---- MatrixEncode ----/ + * + * After: + * + * Foo1 - MatrixEncode - MatrixDecode - TFLFullyConnected - MatrixEncode - MatrixDecode - Bar + * Foo2 - MatrixEncode - MatrixDecode -/ + * + * @note This method replaces MatMul with "- MatrixDecode - TFLFullyConnected - MatrixEncode -". + * - MatrixDecode -/ + * Redundant nodes will be removed during transforms. + * + * @ref + * https://github.com/tensorflow/tensorflow/blob/v1.13.1/tensorflow/lite/kernels/internal/reference/fully_connected.h + */ +bool MatMulConverter::convert(loco::MatMul *origin) +{ + auto *graph = origin->graph(); + + assert(origin->lhs()); + assert(origin->rhs()); + + auto tfl_fc = graph->nodes()->create(); + tfl_fc->fusedActivationFunction(locoex::FusedActFunc::NONE); + + // let's create a new graph connection with tfl_fc + { + // input + auto lhs_matrix_dec = make_matrix_decode(origin->lhs()); + tfl_fc->input(lhs_matrix_dec); + + // weights (WH format on TFLite) + auto rhs_matrix_dec = make_matrix_decode(origin->rhs()); + tfl_fc->weights(rhs_matrix_dec); + + // bias + auto zero_const = graph->nodes()->create(); + { // TODO Create optimization pass which fuse additional Add into bias of Conv or FC + assert(loco::shape_known(origin)); + assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32); + + auto output_depth = loco::shape_get(origin->rhs()).as().width(); + // TODO Fix it with type inference + zero_const->dtype(loco::DataType::FLOAT32); + zero_const->rank(1); + zero_const->dim(0) = output_depth; + zero_const->size(output_depth.value()); + for (uint32_t x = 0; x < output_depth.value(); x++) + zero_const->at(x) = 0.0; + } + tfl_fc->bias(zero_const); + + // output + auto matrix_enc = make_matrix_encode(tfl_fc); + + // replace canonical node + loco::replace(origin).with(matrix_enc); + origin->lhs(nullptr); + origin->rhs(nullptr); + } + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/MatMulConverter.h b/compiler/exo/src/Conversion/MatMulConverter.h new file mode 100644 index 000000000..e64c4a0f2 --- /dev/null +++ b/compiler/exo/src/Conversion/MatMulConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_FULLY_CONNECTED_CONVERTER__ +#define __CONVERSION_FULLY_CONNECTED_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::MatMul to locoex::TFLFullyConnected + */ +class MatMulConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::MatMulConverter"; } + +public: + bool convert(loco::MatMul *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_FULLY_CONNECTED_CONVERTER__ diff --git a/compiler/exo/src/Conversion/MaxPool2DConverter.cpp b/compiler/exo/src/Conversion/MaxPool2DConverter.cpp new file mode 100644 index 000000000..67e5ab833 --- /dev/null +++ b/compiler/exo/src/Conversion/MaxPool2DConverter.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MaxPool2DConverter.h" + +#include "Dialect/IR/TFLNodes.h" +#include "GraphBlock.h" + +#include + +namespace exo +{ + +/** + * @brief Converts loco::MaxPool2D to locoex::TFLMaxPool2D + * + * @note This works similar to AvgPool2DConverter. Please refer to the comment in + * AvgPool2DConverter. + */ +bool MaxPool2DConverter::convert(loco::MaxPool2D *origin) +{ + auto *graph = origin->graph(); + + auto dec = make_feature_decode(origin->ifm()); + auto tfl_max = graph->nodes()->create(); + { + tfl_max->value(dec); + + // set attributes + tfl_max->stride()->w(origin->stride()->horizontal()); + tfl_max->stride()->h(origin->stride()->vertical()); + + tfl_max->filter()->w(origin->window()->horizontal()); + tfl_max->filter()->h(origin->window()->vertical()); + + auto pad = origin->pad(); + if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0) + tfl_max->padding(locoex::Padding::VALID); + else + // TODO This is necessary, but not sufficient condition. More rigorous check required + tfl_max->padding(locoex::Padding::SAME); + + tfl_max->fusedActivationFunction(locoex::FusedActFunc::NONE); + } + + auto enc = make_feature_encode(tfl_max); + + loco::replace(origin).with(enc); + origin->ifm(nullptr); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/MaxPool2DConverter.h b/compiler/exo/src/Conversion/MaxPool2DConverter.h new file mode 100644 index 000000000..3f526d88f --- /dev/null +++ b/compiler/exo/src/Conversion/MaxPool2DConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_MAXPOOL2D_CONVERTER__ +#define __CONVERSION_MAXPOOL2D_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::MaxPool2D to locoex::TFLMaxPool2D + */ +class MaxPool2DConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::MaxPool2DConverter"; } + +public: + bool convert(loco::MaxPool2D *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_MAXPOOL2D_CONVERTER__ diff --git a/compiler/exo/src/Conversion/Relu6Converter.cpp b/compiler/exo/src/Conversion/Relu6Converter.cpp new file mode 100644 index 000000000..b694511f5 --- /dev/null +++ b/compiler/exo/src/Conversion/Relu6Converter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Relu6Converter.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace +{ + +class Relu6InputHandler : public exo::InputHandler +{ +public: + void handover(loco::ReLU6 *origin, locoex::TFLRelu6 *replacer) override + { + replacer->features(origin->input()); + } + + std::vector getInputsToConvert(loco::ReLU6 *origin) override + { + std::vector inputs({origin->input()}); + return inputs; + } + + void set(locoex::TFLRelu6 *replacer, std::vector &to) override + { + assert(to.size() == 1); + + replacer->features(to.at(0)); + } + + void nullify(loco::ReLU6 *origin) override { origin->input(nullptr); } +}; + +} // namespace + +namespace exo +{ + +bool Relu6Converter::convert(loco::ReLU6 *origin) +{ + Relu6InputHandler input_handler; + exo::DomainConverter domain_converter; + + auto tfl_node = domain_converter.convert(origin, input_handler); + + return (tfl_node != nullptr); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/Relu6Converter.h b/compiler/exo/src/Conversion/Relu6Converter.h new file mode 100644 index 000000000..d987b42d0 --- /dev/null +++ b/compiler/exo/src/Conversion/Relu6Converter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_RELU6_CONVERTER_H__ +#define __CONVERSION_RELU6_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::Relu6 to TFLRelu6 + */ +class Relu6Converter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::Relu6Converter"; } + +public: + bool convert(loco::ReLU6 *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_RELU6_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/ReluConverter.cpp b/compiler/exo/src/Conversion/ReluConverter.cpp new file mode 100644 index 000000000..92adef94d --- /dev/null +++ b/compiler/exo/src/Conversion/ReluConverter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReluConverter.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace +{ + +class ReluInputHandler : public exo::InputHandler +{ +public: + void handover(loco::ReLU *origin, locoex::TFLRelu *replacer) override + { + replacer->features(origin->input()); + } + + std::vector getInputsToConvert(loco::ReLU *origin) override + { + std::vector inputs({origin->input()}); + return inputs; + } + + void set(locoex::TFLRelu *replacer, std::vector &to) override + { + assert(to.size() == 1); + + replacer->features(to.at(0)); + } + + void nullify(loco::ReLU *origin) override { origin->input(nullptr); } +}; + +} // namespace + +namespace exo +{ + +bool ReluConverter::convert(loco::ReLU *origin) +{ + ReluInputHandler input_handler; + exo::DomainConverter domain_converter; + + auto tfl_node = domain_converter.convert(origin, input_handler); + + return (tfl_node != nullptr); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/ReluConverter.h b/compiler/exo/src/Conversion/ReluConverter.h new file mode 100644 index 000000000..e1e82ae4b --- /dev/null +++ b/compiler/exo/src/Conversion/ReluConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_RELU_CONVERTER_H__ +#define __CONVERSION_RELU_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::Relu to TFLRelu + */ +class ReluConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::ReluConverter"; } + +public: + bool convert(loco::ReLU *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_RELU_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/ReluConverter.test.cpp b/compiler/exo/src/Conversion/ReluConverter.test.cpp new file mode 100644 index 000000000..f53d656b4 --- /dev/null +++ b/compiler/exo/src/Conversion/ReluConverter.test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReluConverter.h" + +#include "GraphBlock.h" +#include "Dialect/IR/TFLNodes.h" + +#include "TestHelper.h" +#include "TestGraph.h" + +#include + +TEST(ReluConverterTest, relu_tensor_inout) +{ + exo::test::TestGraph graph; + { + auto tanh = graph.append(graph.pull); + auto relu = graph.append(tanh); + auto relu6 = graph.append(relu); + graph.complete(); + + auto pull = graph.pull; + { + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2, 2}); + } + } + + // let's convert + exo::test::TypeShapeReadyPhase test_phase; + { + test_phase.add_pass(); + test_phase.run(graph.g.get()); + } + + loco::Node *node = exo::test::find_first_node_bytype(graph.g.get()); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); +} + +TEST(ReluConverterTest, relu_feature_inout) +{ + // g = Pull - FeatureEncode - Relu - FeatureDecode - Push + exo::test::TestGraph graph; + { + auto enc = exo::make_feature_encode(graph.pull); + auto relu = graph.append(enc); + auto dec = exo::make_feature_decode(relu); + graph.complete(dec); + } + + auto pull = graph.pull; + { + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1, 2, 3, 4}); + } + + exo::test::TypeShapeReadyPhase test_phase; + { + test_phase.add_pass(); + test_phase.run(graph.g.get()); + } + + // now, g = Pull - FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode - Push + + // Check + EXO_TEST_ASSERT_NODE_COUNT({graph.push}, 7); + + // Check [FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode] chunk + loco::Node *node = exo::test::find_first_node_bytype(graph.g.get()); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); + node = exo::test::get_only_succ(node); + ASSERT_TRUE(node != nullptr); +} diff --git a/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp b/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp new file mode 100644 index 000000000..532332742 --- /dev/null +++ b/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TensorBroadcastConverter.h" + +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include +#include +#include + +#include + +namespace +{ + +template loco::TensorBroadcast *input_as_tbc(T *node) +{ + loco::TensorBroadcast *tbc = dynamic_cast(node->x()); + if (tbc == nullptr) + tbc = dynamic_cast(node->y()); + + return tbc; +} + +struct Collector final : public locoex::TFLNodeMutableVisitor +{ + using NodePair = std::pair; + + void visit(locoex::TFLAdd *node) final + { + if (auto tbc = input_as_tbc(node)) + { + NodePair pair(tbc, node); + candidates.insert(pair); + } + } + + void visit(locoex::TFLDiv *node) final + { + if (auto tbc = input_as_tbc(node)) + { + NodePair pair(tbc, node); + candidates.insert(pair); + } + } + + void visit(locoex::TFLMul *node) final + { + if (auto tbc = input_as_tbc(node)) + { + NodePair pair(tbc, node); + candidates.insert(pair); + } + } + + void visit(locoex::TFLSub *node) final + { + if (auto tbc = input_as_tbc(node)) + { + NodePair pair(tbc, node); + candidates.insert(pair); + } + } + + void visit(locoex::TFLMaximum *node) final + { + if (auto tbc = input_as_tbc(node)) + { + NodePair pair(tbc, node); + candidates.insert(pair); + } + } + + void visit(locoex::TFLNode *) final { return; } + + std::set candidates; +}; + +bool mapping_condition(Collector::NodePair &) +{ + // TODO fill condition + + return true; +} + +template void jump_connection(loco::TensorBroadcast *tbc, T *tflnode) +{ + if (tflnode->x() == tbc) + tflnode->x(tbc->input()); + else if (tflnode->y() == tbc) + tflnode->y(tbc->input()); + else + assert(false); + + tbc->input(nullptr); +} + +} // namespace + +namespace exo +{ + +/** + * @brief Disconnects loco::TensorBroadcast from the graph if following node + * is one of binary node: TFLAdd, TFLSub, TFLMul, TFLDiv, TFLMaximum + * and meets condition (TBA) + * @note + * Before: + * x --- TensorBroadcast --- TFLXXX --- output + * y ----------------------/ + * + * After: + * --- TensorBroadcast --- + * x --- TFLXXX --- output + * y --/ + */ +bool TensorBroadcastConverter::run(loco::Graph *graph) +{ + Collector collector; + + auto active_nodes = loco::active_nodes(loco::output_nodes(graph)); + + for (auto node : active_nodes) + { + if (node->dialect() == locoex::TFLDialect::get()) + { + auto tfl_node = dynamic_cast(node); + tfl_node->accept(&collector); + } + } + + bool changed = false; + + for (auto pair : collector.candidates) + { + if (mapping_condition(pair)) + { + loco::TensorBroadcast *tensorbroadcast = pair.first; + if (auto tfladd = dynamic_cast(pair.second)) + { + jump_connection(tensorbroadcast, tfladd); + changed = true; + } + else if (auto tfldiv = dynamic_cast(pair.second)) + { + jump_connection(tensorbroadcast, tfldiv); + changed = true; + } + else if (auto tflmul = dynamic_cast(pair.second)) + { + jump_connection(tensorbroadcast, tflmul); + changed = true; + } + else if (auto tflsub = dynamic_cast(pair.second)) + { + jump_connection(tensorbroadcast, tflsub); + changed = true; + } + else if (auto tflmaximum = dynamic_cast(pair.second)) + { + jump_connection(tensorbroadcast, tflmaximum); + changed = true; + } + else + { + assert(false); + } + } + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/TensorBroadcastConverter.h b/compiler/exo/src/Conversion/TensorBroadcastConverter.h new file mode 100644 index 000000000..3cf79b0ba --- /dev/null +++ b/compiler/exo/src/Conversion/TensorBroadcastConverter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TENSOR_BROADCAST_CONVERTER_H__ +#define __TENSOR_BROADCAST_CONVERTER_H__ + +#include +#include + +namespace exo +{ + +/** + * @brief Pass to resolve TensorBroadcast IR + */ +class TensorBroadcastConverter : public logo::Pass +{ +public: + virtual const char *name(void) const { return "exo::TensorBroadcastConverter"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace exo + +#endif //__TENSOR_BROADCAST_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/TensorConcatConverter.cpp b/compiler/exo/src/Conversion/TensorConcatConverter.cpp new file mode 100644 index 000000000..1c36b11f8 --- /dev/null +++ b/compiler/exo/src/Conversion/TensorConcatConverter.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TensorConcatConverter.h" + +#include "GraphBlock.h" +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace exo +{ +/** + * @brief Converts loco::TensorConcat to locoex::TFLConcatenate + * + * Before: + * input:0 ----- loco::TensorConcat ------- C + * input:1 ----/ + * + * After: + * input:0 ----- locoex::TFLConcatenate --- C + * input:1 ----/ + * + * input:0 ----- loco::TensorConcat --- + * input:1 ----/ + * + */ +bool TensorConcatConverter::convert(loco::TensorConcat *origin) +{ + assert(loco::shape_get(origin).domain() == loco::Domain::Tensor); + + if (!loco::shape_known(origin)) + { + return false; + } + + auto tfl_concat = origin->graph()->nodes()->create(2); + tfl_concat->values(0, origin->lhs()); + tfl_concat->values(1, origin->rhs()); + tfl_concat->axis(origin->axis()); + tfl_concat->fusedActivationFunction(locoex::FusedActFunc::NONE); + + loco::replace(origin).with(tfl_concat); + + origin->lhs(nullptr); + origin->rhs(nullptr); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/TensorConcatConverter.h b/compiler/exo/src/Conversion/TensorConcatConverter.h new file mode 100644 index 000000000..6b90f4731 --- /dev/null +++ b/compiler/exo/src/Conversion/TensorConcatConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_TENSORCONCAT_CONVERTER_H__ +#define __CONVERSION_TENSORCONCAT_CONVERTER_H__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::TensorConcat to TFLConcatenate + */ +class TensorConcatConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::TensorConcatConverter"; } + +public: + bool convert(loco::TensorConcat *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_TENSORCONCAT_CONVERTER_H__ diff --git a/compiler/exo/src/Conversion/TensorReduceConverter.cpp b/compiler/exo/src/Conversion/TensorReduceConverter.cpp new file mode 100644 index 000000000..8fcb1682d --- /dev/null +++ b/compiler/exo/src/Conversion/TensorReduceConverter.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TensorReduceConverter.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Check.h" + +#include + +#include +#include + +namespace +{ + +/** + * @brief Convert given TensorReduce as TFLMean + * + * + * In --- loco::TensorReduce --- Out(s) + * + * + * In -------- locoex::TFLMean --- Out(s) + * / + * TFLConst --- + * (reduction indices) + */ +bool convert_as_mean(loco::TensorReduce *origin) +{ + EXO_ASSERT(origin->func() == loco::ReduceFunc::Mean, "func should be Mean for this helper"); + EXO_ASSERT(origin->input(), "TensorReduce has no input"); + + auto *graph = origin->graph(); + + // Make reduction indicies TFLConst node + auto reduction = graph->nodes()->create(); + { + auto input_rank = loco::shape_get(origin->input()).as().rank(); + + std::vector red_vec; + for (uint32_t axis = 0; axis < input_rank; ++axis) + if (origin->axes()->defined(axis)) + red_vec.push_back(static_cast(axis)); + + const loco::DataType S32 = loco::DataType::S32; + + reduction->dtype(S32); + reduction->rank(1); + reduction->dim(0) = red_vec.size(); + reduction->size(red_vec.size()); + for (uint32_t i = 0; i < red_vec.size(); ++i) + reduction->at(i) = red_vec.at(i); + } + + // Make TFLMean node to replace + auto mean = graph->nodes()->create(); + mean->input(origin->input()); + mean->reduction_indices(reduction); + mean->keep_dims(true); // Canonical TensorReduce always keep dimensions + + // replace canonical node + loco::replace(origin).with(mean); + origin->input(nullptr); + + return true; +} + +} // namespace + +namespace exo +{ + +bool TensorReduceConverter::convert(loco::TensorReduce *origin) +{ + if (origin->func() == loco::ReduceFunc::Mean) + return convert_as_mean(origin); + else + INTERNAL_EXN_V("Unsupported ReduceFunc", oops::to_uint32(origin->func())); +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/TensorReduceConverter.h b/compiler/exo/src/Conversion/TensorReduceConverter.h new file mode 100644 index 000000000..dfd65ad2d --- /dev/null +++ b/compiler/exo/src/Conversion/TensorReduceConverter.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TENSOR_REDUCE_CONVERTER__ +#define __TENSOR_REDUCE_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::TensorReduce to appropriate TFL reduce operation + * @note loco::TensorReduce always keep dimensions + * + * Currently support: + * - When loco::TensorReduce::func() == Mean, convert to TFLMean + TFLConst + * - TODO Support other cases + */ +class TensorReduceConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::TensorReduceConverter"; } + +public: + bool convert(loco::TensorReduce *origin) final; +}; + +} // namespace exo + +#endif // __TENSOR_REDUCE_CONVERTER__ diff --git a/compiler/exo/src/Conversion/TensorTransposeConverter.cpp b/compiler/exo/src/Conversion/TensorTransposeConverter.cpp new file mode 100644 index 000000000..25c27fe7e --- /dev/null +++ b/compiler/exo/src/Conversion/TensorTransposeConverter.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TensorTransposeConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include +#include + +#include + +#include +#include +#include + +namespace +{ + +void validate_perm(loco::TensorTranspose *origin) +{ + // check perm values are correct + std::vector base_perms; // such as {0, 1, 2, 3, ... } + std::vector perms; // perm values in TensorTranspose + + base_perms.resize(origin->perm()->size()); + perms.resize(origin->perm()->size()); + for (loco::TensorAxis x = 0; x < origin->perm()->size(); x++) + { + base_perms[x] = x; + perms[x] = origin->perm()->axis(x); + } + + if (!std::is_permutation(base_perms.begin(), base_perms.end(), perms.begin())) + INTERNAL_EXN("wrong perm value"); +} + +} // namespace + +namespace exo +{ +/** + * @brief Converts loco::TensorTranspose to locoex::TFLTranspose + */ +bool TensorTransposeConverter::convert(loco::TensorTranspose *origin) +{ + auto *graph = origin->graph(); + + auto tfl_transpose = graph->nodes()->create(); + { + // validation + { + assert(origin->input() != nullptr); + + auto input_rank = loco::shape_get(origin->input()).as().rank(); + if (input_rank != origin->perm()->size()) + INTERNAL_EXN_V("perm size should be same with input rank", + oops::to_uint32(origin->perm()->size())); + + validate_perm(origin); + } + + tfl_transpose->a(origin->input()); + + // perm : set TFLConst + auto perm_const = graph->nodes()->create(); + { + perm_const->dtype(loco::DataType::S32); + perm_const->rank(1); + perm_const->dim(0) = origin->perm()->size(); + perm_const->size(origin->perm()->size()); + + // add perm values into perm TFLConst + for (loco::TensorAxis x = 0; x < origin->perm()->size(); x++) + { + perm_const->at(x) = origin->perm()->axis(x); + } + } + tfl_transpose->perm(perm_const); + } + + // replace canonical node + loco::replace(origin).with(tfl_transpose); + origin->input(nullptr); + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/TensorTransposeConverter.h b/compiler/exo/src/Conversion/TensorTransposeConverter.h new file mode 100644 index 000000000..9b61ff38d --- /dev/null +++ b/compiler/exo/src/Conversion/TensorTransposeConverter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_TENSORTRANSPOSE_CONVERTER__ +#define __CONVERSION_TENSORTRANSPOSE_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::TensorTranspose to locoex::TFLTranspose + */ +class TensorTransposeConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::TensorTransposeConverter"; } + +public: + bool convert(loco::TensorTranspose *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_TENSORTRANSPOSE_CONVERTER__ diff --git a/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp b/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp new file mode 100644 index 000000000..c03b64f48 --- /dev/null +++ b/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TransposedConv2DConverter.h" + +#include "Dialect/IR/TFLNodes.h" + +#include "GraphBlock.h" + +#include +#include + +namespace exo +{ + +bool TransposedConv2DConverter::convert(loco::TransposedConv2D *origin) +{ + // Shape is required to set origin->inputSizes() + if (not loco::shape_known(origin)) + return false; + + if ((origin->ifm() == nullptr) or (origin->ker() == nullptr)) + return false; + + auto *graph = origin->graph(); + + auto tfl_tr_conv = graph->nodes()->create(); + { + tfl_tr_conv->stride()->w(origin->stride()->horizontal()); + tfl_tr_conv->stride()->h(origin->stride()->vertical()); + + auto pad = origin->pad(); + if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0) + tfl_tr_conv->padding(locoex::Padding::VALID); + else + // TODO This is necessary, but not sufficient condition. More rigorous check required + tfl_tr_conv->padding(locoex::Padding::SAME); + } + + // let's create a new graph connection with tfl_tr_conv + { + // Make inputSizes from shape of origin + auto input_sizes_const = graph->nodes()->create(); + auto origin_shape = loco::shape_get(origin).as(); + + const loco::DataType S32 = loco::DataType::S32; + + input_sizes_const->dtype(S32); + input_sizes_const->rank(1); + input_sizes_const->dim(0) = 4; + input_sizes_const->size(4); + // Note that NHWC is layout for inputSizes determined by tflite format + input_sizes_const->at(0) = origin_shape.count().value(); // N + input_sizes_const->at(1) = origin_shape.height().value(); // H + input_sizes_const->at(2) = origin_shape.width().value(); // W + input_sizes_const->at(3) = origin_shape.depth().value(); // C + + tfl_tr_conv->inputSizes(input_sizes_const); + + // filter + auto filter_dec = make_filter_decode(origin->ker()); + tfl_tr_conv->filter(filter_dec); + + // outBackprop + auto feature_dec = make_feature_decode(origin->ifm()); + tfl_tr_conv->outBackprop(feature_dec); + + // output + auto feature_enc = make_feature_encode(tfl_tr_conv); + + // replace canonical node + loco::replace(origin).with(feature_enc); + origin->ifm(nullptr); + } + + return true; +} + +} // namespace exo diff --git a/compiler/exo/src/Conversion/TransposedConv2DConverter.h b/compiler/exo/src/Conversion/TransposedConv2DConverter.h new file mode 100644 index 000000000..f51e0a5bc --- /dev/null +++ b/compiler/exo/src/Conversion/TransposedConv2DConverter.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__ +#define __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__ + +#include "CanonicalNodeConverter.h" + +#include + +namespace exo +{ + +/** + * @brief Convert loco::TransposedConv2D to locoex::TFLTransposeConv and auxiliary + * + * + * + * + * IFM ------- TransposedConv2D --- OFM + * (Feature) / (Feature) + * / + * KER ------ + * (Filter) + * + * + * + * + * out_backprop : IFM ------- FeatureDecode --- TFLTransposeConv --- FeatureEncode --- OFM + * [Feature] [Tensor] / / [Tensor] [Feature] + * / / + * filter: KER ------- FilterDecode --- / + * [Filter] [Tensor] / + * / + * input_sizes : TFLConst (new) ------------ + * [Tensor] + */ +class TransposedConv2DConverter : public CanonicalNodeConverter +{ +public: + const char *name(void) const final { return "exo::TransposedConv2DConverter"; } + +public: + bool convert(loco::TransposedConv2D *origin) final; +}; + +} // namespace exo + +#endif // __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__ diff --git a/compiler/exo/src/Conversions.h b/compiler/exo/src/Conversions.h new file mode 100644 index 000000000..8eb4ed2e4 --- /dev/null +++ b/compiler/exo/src/Conversions.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERSIONS_H__ +#define __CONVERSIONS_H__ + +#include "Conversion/AvgPool2DConverter.h" +#include "Conversion/ConstGenConverter.h" +#include "Conversion/Conv2DConverter.h" +#include "Conversion/DepthwiseConv2DConverter.h" +// TODO loco::DepthwiseFilterEncode +#include "Conversion/EltwiseAddConverter.h" +#include "Conversion/EltwiseDivConverter.h" +#include "Conversion/EltwiseMaxConverter.h" +#include "Conversion/EltwiseMulConverter.h" +#include "Conversion/EltwiseSqrtConverter.h" +#include "Conversion/EltwiseSubConverter.h" +#include "Conversion/FeatureBiasAddConverter.h" +// TODO loco::FixedReshape +#include "Conversion/MatMulConverter.h" +#include "Conversion/MaxPool2DConverter.h" +#include "Conversion/ReluConverter.h" +#include "Conversion/Relu6Converter.h" +// TODO loco::Tanh +#include "Conversion/TensorConcatConverter.h" +// TODO loco::TensorBiasAdd +#include "Conversion/TensorBroadcastConverter.h" +#include "Conversion/TensorReduceConverter.h" +// TODO loco::TensorSoftmax +#include "Conversion/TensorTransposeConverter.h" +#include "Conversion/TransposedConv2DConverter.h" + +#endif // __CONVERSIONS_H__ diff --git a/compiler/exo/src/Convert.cpp b/compiler/exo/src/Convert.cpp new file mode 100644 index 000000000..45f0481f4 --- /dev/null +++ b/compiler/exo/src/Convert.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Convert.h" + +#include "Conversions.h" +#include "Pass/ShapeInferencePass.h" +#include "Pass/TypeInferencePass.h" +#include "ProgressReporter.h" +#include "Knob.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace exo +{ + +void convert_to_TFLNodes(loco::Graph *graph) +{ + // run Shape and Type inference must be run before conversion + loco::CanonicalShapeInferenceRule shape_rule; + loco::apply(&shape_rule).to(graph); + + loco::CanonicalTypeInferenceRule type_rule; + loco::apply(&type_rule).to(graph); + + logo::Phase phase; + { + // prepare type and shape before conversion + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + + // Add converters for canonical nodes. Note: Not all loco canonical nodes are listed. + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + // TODO loco::DepthwiseFilterEncode + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + // TODO loco::FixedReshape + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + // TODO loco::Tanh + phase.emplace_back(stdex::make_unique()); + // TODO loco::TensorBiasAdd + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + // TODO loco::TensorSoftmax + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + + // Add optimization below + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + } + + logo::PhaseRunner phase_runner{graph}; + + ProgressReporter prog(graph, logo::PhaseStrategy::Restart); + phase_runner.attach(&prog); + phase_runner.run(phase); + + // TODO Assert if all canonical nodes are converted to TFL node +} + +} // namespace exo diff --git a/compiler/exo/src/Convert.h b/compiler/exo/src/Convert.h new file mode 100644 index 000000000..7038f9cf7 --- /dev/null +++ b/compiler/exo/src/Convert.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include + +namespace exo +{ + +void convert_to_TFLNodes(loco::Graph *graph); + +} // namespace exo + +#endif // __CONVERT_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.cpp b/compiler/exo/src/Dialect/IR/CircleDialect.cpp new file mode 100644 index 000000000..ecd43b0a3 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleDialect.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleDialect.h" + +namespace locoex +{ + +loco::Dialect *CircleDialect::get(void) +{ + static CircleDialect d; + return &d; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.h b/compiler/exo/src/Dialect/IR/CircleDialect.h new file mode 100644 index 000000000..9857d9e6d --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleDialect.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLEDIALECT_H__ +#define __LOCOEX_IR_CIRCLEDIALECT_H__ + +#include + +namespace locoex +{ + +class CircleDialect final : public loco::Dialect +{ +private: + CircleDialect() = default; + +public: + CircleDialect(const CircleDialect &) = delete; + CircleDialect(CircleDialect &&) = delete; + +public: + static loco::Dialect *get(void); +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLEDIALECT_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp b/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp new file mode 100644 index 000000000..6132eb361 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleDialect.h" + +#include + +TEST(CircleDialectTest, get) +{ + using locoex::CircleDialect; + + auto d = 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, CircleDialect::get()); +} diff --git a/compiler/exo/src/Dialect/IR/CircleNode.cpp b/compiler/exo/src/Dialect/IR/CircleNode.cpp new file mode 100644 index 000000000..cdcd434ea --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNode.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleNode.h" + +#include "CircleDialect.h" + +namespace locoex +{ + +const loco::Dialect *CircleNode::dialect(void) const { return CircleDialect::get(); } + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/IR/CircleNode.h b/compiler/exo/src/Dialect/IR/CircleNode.h new file mode 100644 index 000000000..1ae9d38bd --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODE_H__ +#define __LOCOEX_IR_CIRCLENODE_H__ + +#include "CircleNodeDecl.h" +#include "CircleNodeImpl.h" + +#endif // __LOCOEX_IR_CIRCLENODE_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodeDecl.h b/compiler/exo/src/Dialect/IR/CircleNodeDecl.h new file mode 100644 index 000000000..358b1f0ce --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodeDecl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODEDECL_H__ +#define __LOCOEX_IR_CIRCLENODEDECL_H__ + +#include +#include + +#include "CircleOpcode.h" +#include "CircleNodeVisitor.forward.h" + +namespace locoex +{ + +struct CircleNode : public loco::Node +{ + virtual ~CircleNode() = default; + + const loco::Dialect *dialect(void) const final; + virtual CircleOpcode opcode(void) const = 0; + + template T accept(CircleNodeVisitorBase *) const; + template T accept(CircleNodeMutableVisitorBase *); +}; + +template struct CircleNodeImpl : public CircleNode +{ + virtual ~CircleNodeImpl() = default; + + uint32_t opnum(void) const final { return static_cast(Code); } + CircleOpcode opcode(void) const final { return Code; } +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLENODEDECL_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodeImpl.h b/compiler/exo/src/Dialect/IR/CircleNodeImpl.h new file mode 100644 index 000000000..d9f487111 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodeImpl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODEIMPL_H__ +#define __LOCOEX_IR_CIRCLENODEIMPL_H__ + +#include "CircleNodes.h" +#include "CircleNodeVisitor.h" + +#include + +#include + +namespace locoex +{ + +template T CircleNode::accept(CircleNodeVisitorBase *v) const +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + default: + break; + } + + INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled"); +} + +template T CircleNode::accept(CircleNodeMutableVisitorBase *v) +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + default: + break; + } + + INTERNAL_EXN("CircleNode::accept(CircleNodeMutableVisitorBase) not handled"); +} + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLENODEIMPL_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h new file mode 100644 index 000000000..8ae28abf3 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__ +#define __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__ + +namespace locoex +{ + +// NOTE These forward declarations SHOULD BE aligned with Node delcarations in +// "CircleNodeVisitor.h" +template struct CircleNodeVisitorBase; +template struct CircleNodeMutableVisitorBase; + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h new file mode 100644 index 000000000..fc70c9ebc --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODE_VISITOR_H__ +#define __LOCOEX_IR_CIRCLENODE_VISITOR_H__ + +#include "CircleNode.h" +#include "CircleNodes.h" + +#include + +namespace locoex +{ + +/** + * DO NOT use this class. Use CircleNodeVisitor instead. + */ +template 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 struct CircleNodeVisitor : public CircleNodeVisitorBase +{ + virtual ~CircleNodeVisitor() = default; + +#define CIRCLE_NODE(OPCODE, Circle_CLASS) \ + \ + virtual T visit(const Circle_CLASS *node) { return visit(static_cast(node)); } + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + /// @brief Default fallback + virtual T visit(const CircleNode *) { INTERNAL_EXN("CircleNodeVisistor: NYI node"); } +}; + +/** + * DO NOT use this class. Use CircleNodeMutableVisitor instead. + */ +template struct CircleNodeMutableVisitorBase +{ + virtual ~CircleNodeMutableVisitorBase() = default; + +#define CIRCLE_NODE(OPCODE, Circle_CLASS) virtual T visit(Circle_CLASS *) = 0; + +#include "CircleNodes.lst" +#undef CIRCLE_NODE +}; + +template struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase +{ + virtual ~CircleNodeMutableVisitor() = default; + +#define CIRCLE_NODE(OPCODE, Circle_CLASS) \ + \ + virtual T visit(Circle_CLASS *node) { return visit(static_cast(node)); } + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + /// @brief Default fallback + virtual T visit(CircleNode *) { INTERNAL_EXN("CircleMutableNodeVisistor: NYI node"); } +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLENODE_VISITOR_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.cpp b/compiler/exo/src/Dialect/IR/CircleNodes.cpp new file mode 100644 index 000000000..bba59ff4d --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodes.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is to validate CircleNodes.h +#include "CircleNodes.h" diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.h b/compiler/exo/src/Dialect/IR/CircleNodes.h new file mode 100644 index 000000000..7be093103 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodes.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLENODES_H__ +#define __LOCOEX_IR_CIRCLENODES_H__ + +#include "CircleNodeDecl.h" +#include "CircleOpcode.h" + +#include "FusedActFunc.h" +#include "NodeMixins.h" // FixedArityNode + +#include + +namespace locoex +{ + +/// @brief enumeration of mixin class +enum class CircleNodeTrait +{ + FusedActFunc, +}; + +template class CircleNodeMixin; + +template <> class CircleNodeMixin +{ +public: + CircleNodeMixin() = 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 INSTANCE_NORM in circle + */ +class CircleInstanceNorm final + : public FixedArityNode<3, CircleNodeImpl>, + public CircleNodeMixin +{ +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 locoex + +#endif // __LOCOEX_IR_CIRCLENODES_H__ diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.lst b/compiler/exo/src/Dialect/IR/CircleNodes.lst new file mode 100644 index 000000000..96baf2917 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodes.lst @@ -0,0 +1,8 @@ +#ifndef CIRCLE_NODE +#error "Define CIRCLE_NODE" +#endif // CIRCLE_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// +CIRCLE_NODE(INSTANCE_NORM, locoex::CircleInstanceNorm) diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp b/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp new file mode 100644 index 000000000..b63e7ccae --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleNodes.h" + +#include "CircleDialect.h" +#include "CircleOpcode.h" + +#include + +TEST(CircleInstanceNormTest, constructor) +{ + locoex::CircleInstanceNorm instance_norm; + + ASSERT_EQ(instance_norm.dialect(), locoex::CircleDialect::get()); + ASSERT_EQ(instance_norm.opcode(), locoex::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(), locoex::FusedActFunc::UNDEFINED); +} diff --git a/compiler/exo/src/Dialect/IR/CircleOpcode.h b/compiler/exo/src/Dialect/IR/CircleOpcode.h new file mode 100644 index 000000000..264304049 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/CircleOpcode.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_CIRCLEOPCODE_H__ +#define __LOCOEX_IR_CIRCLEOPCODE_H__ + +namespace locoex +{ + +enum class CircleOpcode +{ +#define CIRCLE_NODE(OPCODE, CLASS) OPCODE, +#include "CircleNodes.lst" +#undef CIRCLE_NODE +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_CIRCLEOPCODE_H__ diff --git a/compiler/exo/src/Dialect/IR/FusedActFunc.h b/compiler/exo/src/Dialect/IR/FusedActFunc.h new file mode 100644 index 000000000..b73a0799e --- /dev/null +++ b/compiler/exo/src/Dialect/IR/FusedActFunc.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DIALECT_IR_FUSEDACTFUNC_H__ +#define __DIALECT_IR_FUSEDACTFUNC_H__ + +namespace locoex +{ + +// 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, + RELU6 +}; + +} // namespace locoex + +#endif // __DIALECT_IR_FUSEDACTFUNC_H__ diff --git a/compiler/exo/src/Dialect/IR/NodeMixins.cpp b/compiler/exo/src/Dialect/IR/NodeMixins.cpp new file mode 100644 index 000000000..cdfe0d8d1 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/NodeMixins.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is to validate NodeMixins.h +#include "NodeMixins.h" diff --git a/compiler/exo/src/Dialect/IR/NodeMixins.h b/compiler/exo/src/Dialect/IR/NodeMixins.h new file mode 100644 index 000000000..c35daebc6 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/NodeMixins.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DIALECT_IR_NODEMIXINS_H__ +#define __DIALECT_IR_NODEMIXINS_H__ + +#include + +namespace locoex +{ + +/** + * @brief Nodes with the fixed number of inputs + * + * TODO Deprecated this class, and use loco::FixedArity instead + */ +template class FixedArityNode : public Base +{ +public: + FixedArityNode() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::unique_ptr(new 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, N> _args; +}; + +} // namespace locoex + +#endif // __DIALECT_IR_NODEMIXINS_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLDialect.cpp b/compiler/exo/src/Dialect/IR/TFLDialect.cpp new file mode 100644 index 000000000..8cbf9a364 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLDialect.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLDialect.h" + +namespace locoex +{ + +loco::Dialect *TFLDialect::get(void) +{ + static TFLDialect d; + return &d; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/IR/TFLDialect.h b/compiler/exo/src/Dialect/IR/TFLDialect.h new file mode 100644 index 000000000..96463a9f9 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLDialect.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLDIALECT_H__ +#define __LOCOEX_IR_TFLDIALECT_H__ + +#include + +namespace locoex +{ + +class TFLDialect final : public loco::Dialect +{ +private: + TFLDialect() = default; + +public: + TFLDialect(const TFLDialect &) = delete; + TFLDialect(TFLDialect &&) = delete; + +public: + static loco::Dialect *get(void); +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLDIALECT_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp b/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp new file mode 100644 index 000000000..136721e2d --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLDialect.h" + +#include + +TEST(TFLDialectTest, get) +{ + using locoex::TFLDialect; + + auto d = TFLDialect::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, TFLDialect::get()); +} diff --git a/compiler/exo/src/Dialect/IR/TFLNode.cpp b/compiler/exo/src/Dialect/IR/TFLNode.cpp new file mode 100644 index 000000000..82d5f1eba --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNode.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLNode.h" + +#include "TFLDialect.h" + +namespace locoex +{ + +const loco::Dialect *TFLNode::dialect(void) const { return TFLDialect::get(); } + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/IR/TFLNode.h b/compiler/exo/src/Dialect/IR/TFLNode.h new file mode 100644 index 000000000..eff69b1a5 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODE_H__ +#define __LOCOEX_IR_TFLNODE_H__ + +#include "TFLNodeDecl.h" +#include "TFLNodeImpl.h" + +#endif // __LOCOEX_IR_TFLNODE_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodeDecl.h b/compiler/exo/src/Dialect/IR/TFLNodeDecl.h new file mode 100644 index 000000000..d13900ab3 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodeDecl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODEDECL_H__ +#define __LOCOEX_IR_TFLNODEDECL_H__ + +#include +#include + +#include "TFLOpcode.h" +#include "TFLNodeVisitor.forward.h" + +namespace locoex +{ + +struct TFLNode : public loco::Node +{ + virtual ~TFLNode() = default; + + const loco::Dialect *dialect(void) const final; + virtual TFLOpcode opcode(void) const = 0; + + template T accept(TFLNodeVisitorBase *) const; + template T accept(TFLNodeMutableVisitorBase *); +}; + +template struct TFLNodeImpl : public TFLNode +{ + virtual ~TFLNodeImpl() = default; + + uint32_t opnum(void) const final { return static_cast(Code); } + TFLOpcode opcode(void) const final { return Code; } +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLNODEDECL_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodeImpl.h b/compiler/exo/src/Dialect/IR/TFLNodeImpl.h new file mode 100644 index 000000000..63388279a --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodeImpl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODEIMPL_H__ +#define __LOCOEX_IR_TFLNODEIMPL_H__ + +#include "TFLNodes.h" +#include "TFLNodeVisitor.h" + +#include + +#include + +namespace locoex +{ + +template T TFLNode::accept(TFLNodeVisitorBase *v) const +{ + switch (this->opcode()) + { +#define TFL_NODE(OPCODE, CLASS) \ + \ + case TFLOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "TFLNodes.lst" +#undef TFL_NODE + + default: + break; + } + + INTERNAL_EXN("TFLNode::accept(TFLNodeVisitorBase) not handled"); +} + +template T TFLNode::accept(TFLNodeMutableVisitorBase *v) +{ + switch (this->opcode()) + { +#define TFL_NODE(OPCODE, CLASS) \ + \ + case TFLOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "TFLNodes.lst" +#undef TFL_NODE + + default: + break; + } + + INTERNAL_EXN("TFLNode::accept(TFLNodeMutableVisitorBase) not handled"); +} + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLNODEIMPL_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h new file mode 100644 index 000000000..e98057bc3 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODE_VISITOR_FORWARD_H__ +#define __LOCOEX_IR_TFLNODE_VISITOR_FORWARD_H__ + +namespace locoex +{ + +// NOTE These forward declarations SHOULD BE aligned with Node delcarations in +// "TFLNodeVisitor.h" +template struct TFLNodeVisitorBase; +template struct TFLNodeMutableVisitorBase; + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLNODE_VISITOR_FORWARD_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h new file mode 100644 index 000000000..e1f5959c0 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODE_VISITOR_H__ +#define __LOCOEX_IR_TFLNODE_VISITOR_H__ + +#include "TFLNode.h" +#include "TFLNodes.h" + +#include + +namespace locoex +{ + +/** + * DO NOT use this class. Use TFLNodeVisitor instead. + */ +template struct TFLNodeVisitorBase +{ + virtual ~TFLNodeVisitorBase() = default; + +#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(const TFL_CLASS *) = 0; + +#include "TFLNodes.lst" +#undef TFL_NODE +}; + +template struct TFLNodeVisitor : public TFLNodeVisitorBase +{ + virtual ~TFLNodeVisitor() = default; + +#define TFL_NODE(OPCODE, TFL_CLASS) \ + \ + virtual T visit(const TFL_CLASS *node) { return visit(static_cast(node)); } + +#include "TFLNodes.lst" +#undef TFL_NODE + + /// @brief Default fallback + virtual T visit(const TFLNode *) { INTERNAL_EXN("TFLNodeVisitor: NYI node"); } +}; + +/** + * DO NOT use this class. Use TFLNodeMutableVisitor instead. + */ +template struct TFLNodeMutableVisitorBase +{ + virtual ~TFLNodeMutableVisitorBase() = default; + +#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(TFL_CLASS *) = 0; + +#include "TFLNodes.lst" +#undef TFL_NODE +}; + +template struct TFLNodeMutableVisitor : public TFLNodeMutableVisitorBase +{ + virtual ~TFLNodeMutableVisitor() = default; + +#define TFL_NODE(OPCODE, TFL_CLASS) \ + \ + virtual T visit(TFL_CLASS *node) { return visit(static_cast(node)); } + +#include "TFLNodes.lst" +#undef TFL_NODE + + /// @brief Default fallback + virtual T visit(TFLNode *) { INTERNAL_EXN("TFLNodeMutableVisitor: NYI node"); } +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLNODE_VISITOR_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.cpp b/compiler/exo/src/Dialect/IR/TFLNodes.cpp new file mode 100644 index 000000000..f385ce0d9 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodes.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLNodes.h" + +#include "Check.h" + +#include + +#include + +namespace locoex +{ + +template uint32_t TFLConst::size(void) const +{ + assert(dtype() == DT); + assert(_data.size() % sizeof(typename loco::DataTypeImpl
::Type) == 0); + return _data.size() / sizeof(typename loco::DataTypeImpl
::Type); +} + +template void TFLConst::size(uint32_t l) +{ + assert(dtype() == DT); + _data.resize(l * sizeof(typename loco::DataTypeImpl
::Type)); +} + +template +const typename loco::DataTypeImpl
::Type &TFLConst::at(uint32_t n) const +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +template typename loco::DataTypeImpl
::Type &TFLConst::at(uint32_t n) +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +#define INSTANTIATE(DT) \ + template uint32_t TFLConst::size
(void) const; \ + template void TFLConst::size
(uint32_t); \ + template const typename loco::DataTypeImpl
::Type &TFLConst::at
(uint32_t) const; \ + template typename loco::DataTypeImpl
::Type &TFLConst::at
(uint32_t); + +INSTANTIATE(loco::DataType::S32); +INSTANTIATE(loco::DataType::FLOAT32); + +#undef INSTANTIATE + +void set_new_shape(locoex::TFLReshape *node, int32_t *base, uint32_t size) +{ + // Check node does not have both of new shape infos + EXO_ASSERT(node->shape() == nullptr, "node already has shape input"); + EXO_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute"); + + const loco::DataType S32 = loco::DataType::S32; + + // Set 2nd input as TFLConst + auto const_shape_node = node->graph()->nodes()->create(); + const_shape_node->rank(1); + const_shape_node->dim(0) = size; + const_shape_node->dtype(S32); + const_shape_node->size(size); + for (uint32_t axis = 0; axis < size; ++axis) + const_shape_node->at(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 locoex diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.h b/compiler/exo/src/Dialect/IR/TFLNodes.h new file mode 100644 index 000000000..5f521a0a6 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodes.h @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLNODES_H__ +#define __LOCOEX_IR_TFLNODES_H__ + +#include "TFLNodeDecl.h" +#include "TFLOpcode.h" + +#include "FusedActFunc.h" +#include "NodeMixins.h" + +#include +#include +#include + +#include + +#include + +namespace locoex +{ + +enum class Padding +{ + UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error. + SAME, + VALID, +}; + +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; +}; + +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; +}; + +/// @brief enumeration of mixin class +enum class TFLNodeTrait +{ + FusedActFunc, + Bias +}; + +template class TFLNodeMixin; + +template <> class TFLNodeMixin +{ +public: + TFLNodeMixin() = 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 TFLNodeMixin +{ +public: + TFLNodeMixin() = 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 ADD in TensorFlow Lite + */ +class TFLAdd final : public FixedArityNode<2, TFLNodeImpl>, + public TFLNodeMixin +{ +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); } +}; + +/** + * @brief AVERAGE_POOL_2D in TensorFlow Lite + */ +class TFLAveragePool2D final : public FixedArityNode<1, TFLNodeImpl>, + public TFLNodeMixin +{ +public: + TFLAveragePool2D() : _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; +}; + +/** + * @brief CONCATENATION in TensorFlow Lite + */ +class TFLConcatenation final : public VariadicArityNode>, + public TFLNodeMixin +{ +public: + TFLConcatenation(uint32_t arity) : VariadicArityNode>(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: + uint32_t axis(void) const { return _axis; } + void axis(uint32_t axis) { _axis = axis; } + +private: + uint32_t _axis; +}; + +/** + * @brief Class to build tensor data + * @note This will not be exported as a specific op + */ +class TFLConst final : public FixedArityNode<0, TFLNodeImpl>, + public loco::NodeMixin, + public loco::NodeMixin +{ +public: + TFLConst() = default; + +public: + template uint32_t size(void) const; + template void size(uint32_t size); + template const typename loco::DataTypeImpl
::Type &at(uint32_t n) const; + template typename loco::DataTypeImpl
::Type &at(uint32_t n); + +private: + std::vector _data; +}; + +/** + * @brief CONV_2D in TensorFlow Lite + */ +class TFLConv2D final : public FixedArityNode<3, TFLNodeImpl>, + public TFLNodeMixin, + public TFLNodeMixin +{ +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; +}; + +/** + * @brief DEPTHWISE_CONV_2D in TensorFlow Lite + */ +class TFLDepthwiseConv2D final + : public FixedArityNode<3, TFLNodeImpl>, + public TFLNodeMixin, + public TFLNodeMixin +{ +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; +}; + +/** + * @brief DIV in TensorFlow Lite + */ +class TFLDiv final : public FixedArityNode<2, TFLNodeImpl>, + public TFLNodeMixin +{ +public: + TFLDiv() = 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); } +}; + +/** + * @brief FULLY_CONNECTED in TensorFlow Lite + */ +class TFLFullyConnected final : public FixedArityNode<3, TFLNodeImpl>, + public TFLNodeMixin, + public TFLNodeMixin +{ +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); } +}; + +/** + * @brief MAXIMUM in TensorFlow Lite + */ +class TFLMaximum final : public FixedArityNode<2, TFLNodeImpl> +{ +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); } +}; + +/** + * @brief MAX_POOL_2D in TensorFlow Lite + */ +class TFLMaxPool2D final : public FixedArityNode<1, TFLNodeImpl>, + public TFLNodeMixin +{ +public: + TFLMaxPool2D() : _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; +}; + +class TFLMean final : public FixedArityNode<2, TFLNodeImpl> +{ +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; +}; + +/** + * @brief MUL in TensorFlow Lite + */ +class TFLMul final : public FixedArityNode<2, TFLNodeImpl>, + public TFLNodeMixin +{ +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); } +}; + +class TFLRelu final : public FixedArityNode<1, TFLNodeImpl> +{ +public: + TFLRelu() = default; + +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } +}; + +class TFLRelu6 final : public FixedArityNode<1, TFLNodeImpl> +{ +public: + TFLRelu6() = default; + +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } +}; + +class TFLReshape final : public FixedArityNode<2, TFLNodeImpl> +{ +public: + TFLReshape() = 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 _shape; + }; + + const Shape *newShape(void) const { return &_new_shape; } + Shape *newShape(void) { return &_new_shape; } + +private: + Shape _new_shape; +}; + +/** + * @brief Set both TFLReshape's 2nd input as TFLConst, 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(locoex::TFLReshape *node, int32_t *base, uint32_t size); + +class TFLRsqrt final : public FixedArityNode<1, TFLNodeImpl> +{ +public: + TFLRsqrt() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +// TODO TFLSoftmax + +class TFLSqrt final : public FixedArityNode<1, TFLNodeImpl> +{ +public: + TFLSqrt() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +class TFLSquaredDifference final + : public FixedArityNode<2, TFLNodeImpl> +{ +public: + TFLSquaredDifference() = 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); } +}; + +/** + * @brief SUB in TensorFlow Lite + */ +class TFLSub final : public FixedArityNode<2, TFLNodeImpl>, + public TFLNodeMixin +{ +public: + TFLSub() = 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); } +}; + +// TODO TFLTanh + +/** + * @brief TRANSPOSE in TensorFlow Lite + */ +class TFLTranspose final : public FixedArityNode<2, TFLNodeImpl> +{ +public: + TFLTranspose() = 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); } +}; + +/** + * @brief TRANSPOSE_CONV in TensorFlow Lite + * + * @note Argument node function names are from TensorFlow. So refering 'in' and + * 'out' acutally means 'out' and 'in' of the this node. + */ +class TFLTransposeConv final : public FixedArityNode<3, TFLNodeImpl> +{ +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; +}; + +// TODO define more children of TFLNode + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLNODES_H__ diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.lst b/compiler/exo/src/Dialect/IR/TFLNodes.lst new file mode 100644 index 000000000..225e2be3b --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodes.lst @@ -0,0 +1,30 @@ +#ifndef TFL_NODE +#error "Define TFL_NODE" +#endif // TFL_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// +TFL_NODE(ADD, locoex::TFLAdd) +TFL_NODE(AVERAGE_POOL_2D, locoex::TFLAveragePool2D) +TFL_NODE(CONCATENATION, locoex::TFLConcatenation) +TFL_NODE(CONST, locoex::TFLConst) +TFL_NODE(CONV_2D, locoex::TFLConv2D) +TFL_NODE(DEPTHWISE_CONV_2D, locoex::TFLDepthwiseConv2D) +TFL_NODE(DIV, locoex::TFLDiv) +TFL_NODE(FULLY_CONNECTED, locoex::TFLFullyConnected) +TFL_NODE(MAXIMUM, locoex::TFLMaximum) +TFL_NODE(MAX_POOL_2D, locoex::TFLMaxPool2D) +TFL_NODE(MEAN, locoex::TFLMean) +TFL_NODE(MUL, locoex::TFLMul) +TFL_NODE(RELU, locoex::TFLRelu) +TFL_NODE(RELU6, locoex::TFLRelu6) +TFL_NODE(RESHAPE, locoex::TFLReshape) +TFL_NODE(RSQRT, locoex::TFLRsqrt) +// TODO TFLSoftmax +TFL_NODE(SQRT, locoex::TFLSqrt) +TFL_NODE(SQUARED_DIFFERENCE, locoex::TFLSquaredDifference) +TFL_NODE(SUB, locoex::TFLSub) +// TODO TFLTanh +TFL_NODE(TRANSPOSE, locoex::TFLTranspose) +TFL_NODE(TRANSPOSE_CONV, locoex::TFLTransposeConv) diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp b/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp new file mode 100644 index 000000000..09c5c83a0 --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLNodes.h" + +#include "TFLDialect.h" +#include "TFLOpcode.h" + +#include + +TEST(TFLAddTest, constructor) +{ + locoex::TFLAdd add_node; + + ASSERT_EQ(add_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(add_node.opcode(), locoex::TFLOpcode::ADD); + + ASSERT_EQ(add_node.x(), nullptr); + ASSERT_EQ(add_node.y(), nullptr); +} + +// TODO TFLAveragePool2D + +TEST(TFLConcatTest, constructor) +{ + locoex::TFLConcatenation concat_node(3); + + ASSERT_EQ(concat_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(concat_node.opcode(), locoex::TFLOpcode::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(), locoex::FusedActFunc::UNDEFINED); +} + +// TODO TFLConv2D + +TEST(TFLDepthwiseConv2DTest, constructor) +{ + locoex::TFLDepthwiseConv2D dw_conv2d_node; + + ASSERT_EQ(dw_conv2d_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(dw_conv2d_node.opcode(), locoex::TFLOpcode::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(), locoex::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(), locoex::FusedActFunc::UNDEFINED); +} + +TEST(TFLDivTest, constructor) +{ + locoex::TFLDiv div_node; + + ASSERT_EQ(div_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(div_node.opcode(), locoex::TFLOpcode::DIV); + + ASSERT_EQ(div_node.x(), nullptr); + ASSERT_EQ(div_node.y(), nullptr); +} + +// TODO TFLMaxPool2D + +TEST(TFLMulTest, constructor) +{ + locoex::TFLMul mul_node; + + ASSERT_EQ(mul_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(mul_node.opcode(), locoex::TFLOpcode::MUL); + + ASSERT_EQ(mul_node.x(), nullptr); + ASSERT_EQ(mul_node.y(), nullptr); +} + +TEST(TFLReluTest, constructor) +{ + locoex::TFLRelu relu_node; + + ASSERT_EQ(relu_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(relu_node.opcode(), locoex::TFLOpcode::RELU); + + ASSERT_EQ(relu_node.features(), nullptr); +} + +// TODO TFLRelu6 + +TEST(TFLReshapeTest, constructor) +{ + locoex::TFLReshape reshape; + + ASSERT_EQ(reshape.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(reshape.opcode(), locoex::TFLOpcode::RESHAPE); + + ASSERT_EQ(reshape.tensor(), nullptr); + ASSERT_EQ(reshape.shape(), nullptr); + ASSERT_EQ(reshape.newShape()->rank(), 0); +} + +TEST(TFLReshapeTest, alloc_new_shape) +{ + locoex::TFLReshape 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(reshape); + ASSERT_EQ(const_reshape.newShape()->dim(0), 0); + ASSERT_EQ(const_reshape.newShape()->dim(1), 1); +} + +// TODO TFLSoftmax + +// TODO TFLSqrt + +TEST(TFLSubTest, constructor) +{ + locoex::TFLSub sub_node; + + ASSERT_EQ(sub_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(sub_node.opcode(), locoex::TFLOpcode::SUB); + + ASSERT_EQ(sub_node.x(), nullptr); + ASSERT_EQ(sub_node.y(), nullptr); +} + +// TODO TFLTanh + +TEST(TFLTransposeTest, constructor) +{ + locoex::TFLTranspose tr_node; + + ASSERT_EQ(tr_node.dialect(), locoex::TFLDialect::get()); + ASSERT_EQ(tr_node.opcode(), locoex::TFLOpcode::TRANSPOSE); + + ASSERT_EQ(tr_node.a(), nullptr); + ASSERT_EQ(tr_node.perm(), nullptr); +} diff --git a/compiler/exo/src/Dialect/IR/TFLOpcode.h b/compiler/exo/src/Dialect/IR/TFLOpcode.h new file mode 100644 index 000000000..0c0ab64bd --- /dev/null +++ b/compiler/exo/src/Dialect/IR/TFLOpcode.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_IR_TFLOPCODE_H__ +#define __LOCOEX_IR_TFLOPCODE_H__ + +namespace locoex +{ + +enum class TFLOpcode +{ +#define TFL_NODE(OPCODE, CLASS) OPCODE, +#include "TFLNodes.lst" +#undef TFL_NODE +}; + +} // namespace locoex + +#endif // __LOCOEX_IR_TFLOPCODE_H__ diff --git a/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp new file mode 100644 index 000000000..2e71aa000 --- /dev/null +++ b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleShapeInferenceRule.h" + +#include "Dialect/IR/CircleNodes.h" +#include "Dialect/IR/CircleDialect.h" +#include "Dialect/IR/CircleNodeVisitor.h" + +#include "Check.h" + +#include + +namespace +{ + +/** + * @brief Class to infer the shape of CircleNode + * + * @note All CircleNode's inputs and outputs are always loco::Domain::Tensor + */ +class ShapeInferenceAlgorithm final : public locoex::CircleNodeVisitor +{ +public: + loco::NodeShape visit(const locoex::CircleInstanceNorm *node) final + { + auto input_shape = loco::shape_get(node->input()).as(); + + return loco::NodeShape{input_shape}; + } +}; + +} // namespace + +namespace locoex +{ + +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(node) != nullptr); + + ShapeInferenceAlgorithm alg; + shape = dynamic_cast(node)->accept(&alg); + + return true; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h new file mode 100644 index 000000000..92f23c9dd --- /dev/null +++ b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__ +#define __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__ + +#include + +namespace locoex +{ + +struct CircleShapeInferenceRule final : public loco::ShapeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::NodeShape &) const final; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__ diff --git a/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp new file mode 100644 index 000000000..6bc95a1b5 --- /dev/null +++ b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleTypeInferenceRule.h" + +#include "Dialect/IR/CircleDialect.h" +#include "Dialect/IR/CircleNodeVisitor.h" +#include "Dialect/IR/CircleNodes.h" + +#include + +namespace +{ + +struct TypeInferenceAlgorithm final : public locoex::CircleNodeVisitor +{ + loco::DataType visit(const locoex::CircleInstanceNorm *node) final + { + return loco::dtype_get(node->input()); + } +}; + +} // namespace + +namespace locoex +{ + +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(node)->accept(&alg); + assert(dtype != loco::DataType::Unknown); + + return true; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h new file mode 100644 index 000000000..c073dfc54 --- /dev/null +++ b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__ +#define __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__ + +#include + +namespace locoex +{ + +/** + * @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 locoex + +#endif // __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__ diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp new file mode 100644 index 000000000..f4bb10364 --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLShapeInferenceRule.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include "Check.h" + +#include + +#include +#include +#include + +namespace +{ + +// Call this for TFLAvgPool2D and TFLMaxPool2D only +template loco::NodeShape infer_pool_2d_shape(const Pool2DType *node) +{ + EXO_ASSERT(loco::shape_known(node->value()), "Shape must be known"); + + auto ifm_shape = loco::shape_get(node->value()).template as(); + 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 TFLAvgPool2D and TFLMaxPool2D 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() == locoex::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() == locoex::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + EXO_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 TFLNode + * + * @note All TFLNode's inputs and outputs are always loco::Domain::Tensor + */ +class ShapeInferenceAlgorithm final : public locoex::TFLNodeVisitor +{ +public: + loco::NodeShape visit(const locoex::TFLAdd *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const locoex::TFLAveragePool2D *node) final + { + return infer_pool_2d_shape(node); + } + + loco::NodeShape visit(const locoex::TFLConcatenation *node) final + { + // TODO Support when TFLConcatenation has 0 input + assert(node->numValues() > 0); + + auto axis = node->axis(); + auto first_shape = loco::shape_get(node->values(0)).as(); + + 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(); + + for (uint32_t j = 0; j < output_shape.rank(); ++j) + { + if (j == 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 locoex::TFLConst *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 locoex::TFLConv2D *node) final + { + auto ifm_shape = loco::shape_get(node->input()).as(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as(); // in OHWI + + 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() == locoex::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() == locoex::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + EXO_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 locoex::TFLDepthwiseConv2D *node) final + { + auto ifm_shape = loco::shape_get(node->input()).as(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as(); // 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() == locoex::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() == locoex::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + EXO_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 locoex::TFLDiv *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const locoex::TFLFullyConnected *node) final + { + auto input_shape = loco::shape_get(node->input()).as(); + auto weights_shape = loco::shape_get(node->weights()).as(); + + // Checking shape capability for multiplication + EXO_ASSERT(input_shape.rank() == 2, "NYI for input shape rank > 2"); + EXO_ASSERT(weights_shape.rank() == 2, "Incompatible weights rank for fully connected"); + EXO_ASSERT(input_shape.dim(1) == weights_shape.dim(1), + "Incompatible shapes for fully connected"); + + loco::TensorShape out_shape; + out_shape.rank(2); + + out_shape.dim(0) = input_shape.dim(0); + out_shape.dim(1) = weights_shape.dim(0); + + return loco::NodeShape{out_shape}; + } + + loco::NodeShape visit(const locoex::TFLMaximum *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const locoex::TFLMaxPool2D *node) final + { + return infer_pool_2d_shape(node); + } + + loco::NodeShape visit(const locoex::TFLMean *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + auto input_shape = loco::shape_get(node->input()).as(); + auto reduction_indices = dynamic_cast(node->reduction_indices()); + + { // Exceptions + // TODO support non-const case + EXO_ASSERT(reduction_indices, "Only support constant reduction_indices"); + // TODO support other data type + EXO_ASSERT(reduction_indices->dtype() == S32, "Only support int 32"); + } + + std::vector reduction_values; + + for (uint32_t i = 0; i < reduction_indices->size(); ++i) + { + int32_t axis = reduction_indices->at(i); + if (axis < 0) + axis += input_shape.rank(); + if (not(0 <= axis and axis < static_cast(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 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 locoex::TFLMul *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const locoex::TFLRelu *node) final + { + auto input_shape = loco::shape_get(node->features()).as(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const locoex::TFLRelu6 *node) final + { + auto input_shape = loco::shape_get(node->features()).as(); + + return loco::NodeShape{input_shape}; + } + + /** + * @note TFLReshape 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 locoex::TFLReshape *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + loco::TensorShape shape_by_input; + { + EXO_ASSERT(node->shape(), "2nd input shape() should not be nullptr"); + + // Only support node's shape() is TFLConst with S32 + // TODO support other node with other types + auto const_shape_node = dynamic_cast(node->shape()); + EXO_ASSERT(const_shape_node, "Only support TFLConst for shape of TFLReshape"); + EXO_ASSERT(const_shape_node->dtype() == S32, "Only support int32 TFLConst"); + + if (const_shape_node->rank() != 1) + INTERNAL_EXN_V("Only support rank 1 TFLConst", 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) + { + EXO_ASSERT(const_shape_node->at(axis) > 0, "Dimension should be > 0") + shape_by_input.dim(axis) = const_shape_node->at(axis); + } + } + + loco::TensorShape shape_by_attr; + { + shape_by_attr.rank(node->newShape()->rank()); + + for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis) + { + EXO_ASSERT(node->newShape()->dim(axis) > 0, "Dimension should be > 0") + shape_by_attr.dim(axis) = node->newShape()->dim(axis); + } + } + + EXO_ASSERT(shape_by_input == shape_by_attr, + "Warning: Two new shape information mismatched for TFLReshape"); + + return loco::NodeShape{shape_by_input}; + } + + loco::NodeShape visit(const locoex::TFLRsqrt *node) final + { + auto input_shape = loco::shape_get(node->x()).as(); + + return loco::NodeShape{input_shape}; + } + + // TODO TFLSoftmax + + loco::NodeShape visit(const locoex::TFLSqrt *node) final + { + auto input_shape = loco::shape_get(node->x()).as(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const locoex::TFLSquaredDifference *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const locoex::TFLSub *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + // TODO TFLTanh + + /// @brief Returns output shape of transpose. Use loco::ConstGen and locoex::TFLConst for ConstT. + template + 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()); + + for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++) + { + auto new_dim = perm_node->template at(out_axis); + output_shape.dim(new_dim) = input_shape.dim(out_axis); + } + + return output_shape; + } + + loco::NodeShape visit(const locoex::TFLTranspose *node) final + { + auto input_shape = loco::shape_get(node->a()).as(); + + auto canon_perm = dynamic_cast(node->perm()); + auto tfl_perm = dynamic_cast(node->perm()); + + if (canon_perm) + { + return loco::NodeShape{output_shape_of_transpose(input_shape, canon_perm)}; + } + else if (tfl_perm) + { + return loco::NodeShape{output_shape_of_transpose(input_shape, tfl_perm)}; + } + else + INTERNAL_EXN("perm of TFLTranspose should be either ConstGen or TFLConst"); + } + + loco::NodeShape visit(const locoex::TFLTransposeConv *node) final + { + // TransposeConv's output shape is written in its 'inputSizes' argument + auto input_sizes_const = dynamic_cast(node->inputSizes()); + EXO_ASSERT(input_sizes_const, "Only support when TFLTransposeConv's inputSizes is TFLConst") + EXO_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype") + EXO_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(axis); + + return loco::NodeShape{shape}; + } +}; + +} // namespace + +namespace locoex +{ + +bool TFLShapeInferenceRule::recognize(const loco::Dialect *d) const +{ + return TFLDialect::get() == d; +} + +bool TFLShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const +{ + assert(node->dialect() == TFLDialect::get()); + assert(dynamic_cast(node) != nullptr); + + ShapeInferenceAlgorithm alg; + shape = dynamic_cast(node)->accept(&alg); + + return true; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h new file mode 100644 index 000000000..434a145cc --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_TFLSHAPE_INFERENCE_RULE_H__ +#define __LOCOEX_SERVICE_TFLSHAPE_INFERENCE_RULE_H__ + +#include + +namespace locoex +{ + +struct TFLShapeInferenceRule final : public loco::ShapeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::NodeShape &) const final; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_TFLSHAPE_INFERENCE_RULE_H__ diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp new file mode 100644 index 000000000..35c8f0b2a --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestGraph.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/Service/TFLShapeInferenceRule.h" + +#include +#include +#include +#include +#include + +#include + +#include + +TEST(TFLShapeInferenceRuleTest, minimal_with_TFLRelu) +{ + // Create a simple network + exo::test::TestGraph graph; + auto tfl_node = graph.append(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 + locoex::TFLShapeInferenceRule tfl_rule; + loco::CanonicalShapeInferenceRule canonical_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(locoex::TFLDialect::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(); + 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(TFLShapeInferenceRuleTest, avgpool2d_valid) +{ + exo::test::TestGraph graph; + auto tfl_node = graph.append(graph.pull); + graph.complete(); + + auto pull = graph.pull; + { + pull->shape({1, 4, 3, 1}); + } + // setting TFLAveragePool2D + { + tfl_node->filter()->h(2); + tfl_node->filter()->w(2); + tfl_node->stride()->h(2); + tfl_node->stride()->w(2); + tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE); + tfl_node->padding(locoex::Padding::VALID); + } + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + locoex::TFLShapeInferenceRule tfl_rule; + loco::CanonicalShapeInferenceRule canonical_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(locoex::TFLDialect::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(); + 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(TFLShapeInferenceRuleTest, avgpool2d_same) +{ + exo::test::TestGraph graph; + auto tfl_node = graph.append(graph.pull); + graph.complete(); + + auto pull = graph.pull; + { + pull->shape({1, 4, 3, 1}); + } + + // setting TFLAveragePool2D + { + tfl_node->filter()->h(2); + tfl_node->filter()->w(2); + tfl_node->stride()->h(2); + tfl_node->stride()->w(2); + tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE); + tfl_node->padding(locoex::Padding::SAME); + } + + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + locoex::TFLShapeInferenceRule tfl_rule; + loco::CanonicalShapeInferenceRule canonical_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(locoex::TFLDialect::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(); + 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(TFLShapeInferenceRuleTest, TFAdd_shapeinf_different) +{ + auto g = loco::make_graph(); + + auto x_node = g->nodes()->create(); + { + 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(); + { + y_node->rank(2); + y_node->dim(0) = 3; + y_node->dim(1) = 5; + } + auto tfl_node = g->nodes()->create(); + { + tfl_node->x(x_node); + tfl_node->y(y_node); + } + auto push_node = g->nodes()->create(); + { + 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)); + + exo::ShapeInferencePass pass; + while (pass.run(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(); + ASSERT_EQ(shape.rank(), 3); + ASSERT_EQ(shape.dim(0), 2); + ASSERT_EQ(shape.dim(1), 3); + ASSERT_EQ(shape.dim(2), 5); + } +} + +TEST(TFLShapeInferenceRuleTest, TFLTranspose_simple) +{ + exo::test::ExampleGraph g; + + g.pull->rank(4); + g.pull->dim(0) = 10; + g.pull->dim(1) = 20; + g.pull->dim(2) = 30; + g.pull->dim(3) = 40; + + g.const_perm->dtype(loco::DataType::S32); + g.const_perm->rank(1); + g.const_perm->dim(0) = 4; + g.const_perm->size(4); + g.const_perm->at(0) = 2; + g.const_perm->at(1) = 3; + g.const_perm->at(2) = 0; + g.const_perm->at(3) = 1; + + // pre-check + ASSERT_FALSE(loco::shape_known(g.tfl_transpose)); + + exo::ShapeInferencePass pass; + while (pass.run(g.graph()) == true) + ; + + // Verify + { + ASSERT_TRUE(loco::shape_known(g.tfl_transpose)); + + auto shape = loco::shape_get(g.tfl_transpose).as(); + ASSERT_EQ(shape.rank(), 4); + ASSERT_EQ(shape.dim(0), 30); + ASSERT_EQ(shape.dim(1), 40); + ASSERT_EQ(shape.dim(2), 10); + ASSERT_EQ(shape.dim(3), 20); + } +} diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp new file mode 100644 index 000000000..3f123a6db --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLTypeInferenceRule.h" + +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodeVisitor.h" +#include "Dialect/IR/TFLNodes.h" + +#include + +namespace +{ + +struct TypeInferenceAlgorithm final : public locoex::TFLNodeVisitor +{ + loco::DataType visit(const locoex::TFLAdd *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const locoex::TFLAveragePool2D *node) final + { + return loco::dtype_get(node->value()); + } + + loco::DataType visit(const locoex::TFLConcatenation *node) final + { + // TODO Support when TFLConcatenation 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 locoex::TFLConst *node) final { return node->dtype(); } + + loco::DataType visit(const locoex::TFLConv2D *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const locoex::TFLDepthwiseConv2D *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const locoex::TFLDiv *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const locoex::TFLFullyConnected *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const locoex::TFLMaximum *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const locoex::TFLMaxPool2D *node) final + { + return loco::dtype_get(node->value()); + } + + loco::DataType visit(const locoex::TFLMean *node) final { return loco::dtype_get(node->input()); } + + loco::DataType visit(const locoex::TFLMul *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const locoex::TFLRelu *node) final + { + return loco::dtype_get(node->features()); + } + + loco::DataType visit(const locoex::TFLRelu6 *node) final + { + return loco::dtype_get(node->features()); + } + + loco::DataType visit(const locoex::TFLReshape *node) final + { + return loco::dtype_get(node->tensor()); + } + + loco::DataType visit(const locoex::TFLRsqrt *node) final { return loco::dtype_get(node->x()); } + + // TODO TFLSoftmax + + loco::DataType visit(const locoex::TFLSqrt *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const locoex::TFLSquaredDifference *node) final + { + return loco::dtype_get(node->x()); + } + + loco::DataType visit(const locoex::TFLSub *node) final { return loco::dtype_get(node->x()); } + + // TODO TFLTanh + + loco::DataType visit(const locoex::TFLTranspose *node) final + { + return loco::dtype_get(node->a()); + } + + loco::DataType visit(const locoex::TFLTransposeConv *node) final + { + return loco::dtype_get(node->outBackprop()); + } +}; + +} // namespace + +namespace locoex +{ + +bool TFLTypeInferenceRule::recognize(const loco::Dialect *d) const +{ + return TFLDialect::get() == d; +} + +bool TFLTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const +{ + assert(node->dialect() == TFLDialect::get()); + + TypeInferenceAlgorithm alg; + + dtype = dynamic_cast(node)->accept(&alg); + assert(dtype != loco::DataType::Unknown); + + return true; +} + +} // namespace locoex diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h new file mode 100644 index 000000000..31765dcba --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_TFLTYPE_INFERENCE_RULE_H__ +#define __LOCOEX_SERVICE_TFLTYPE_INFERENCE_RULE_H__ + +#include + +namespace locoex +{ + +/** + * @brief Type Inference Rule for TFLDialect + */ +struct TFLTypeInferenceRule final : public loco::TypeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + + bool infer(const loco::Node *, loco::DataType &) const final; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_TFLTYPE_INFERENCE_RULE_H__ diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp new file mode 100644 index 000000000..dd1f93c4d --- /dev/null +++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/Service/TFLTypeInferenceRule.h" + +#include "TestGraph.h" + +#include +#include +#include + +#include + +#include + +TEST(TFLTypeInferenceRuleTest, minimal_with_TFLRelu) +{ + // Create a simple network + exo::test::TestGraph graph; + auto tfl_node = graph.append(graph.pull); + graph.complete(tfl_node); + + graph.pull->dtype(loco::DataType::S32); + + // pre-check + ASSERT_FALSE(loco::dtype_known(tfl_node)); + + // type inference + locoex::TFLTypeInferenceRule tfl_rule; + loco::CanonicalTypeInferenceRule canon_rule; + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canon_rule); + rules.bind(locoex::TFLDialect::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/exo/src/ExoFormattedGraph.cpp b/compiler/exo/src/ExoFormattedGraph.cpp new file mode 100644 index 000000000..5d3b18be1 --- /dev/null +++ b/compiler/exo/src/ExoFormattedGraph.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExoFormattedGraph.h" + +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodes.h" + +#include "Dialect/IR/CircleDialect.h" +#include "Dialect/IR/CircleNodes.h" + +#include +#include + +#include +#include + +// For TF lite +namespace +{ + +const char *to_str(locoex::FusedActFunc fused) +{ + switch (fused) + { + case locoex::FusedActFunc::NONE: + return "NONE"; + case locoex::FusedActFunc::RELU: + return "RELU"; + case locoex::FusedActFunc::RELU6: + return "RELU6"; + default: + return "Error"; + } +} + +const char *to_str(locoex::Padding padding) +{ + switch (padding) + { + case locoex::Padding::SAME: + return "SAME"; + case locoex::Padding::VALID: + return "VALID"; + default: + return "Error"; + } +} + +std::string to_str(const locoex::Stride *stride) +{ + return pepper::str(stride->h(), ",", stride->w()); +} + +std::string to_str(const locoex::Filter *filter) +{ + return pepper::str(filter->h(), ",", filter->w()); +} + +std::string tfl_opname(uint32_t opnum) +{ + static std::string prefix{"tfl."}; + + switch (static_cast(opnum)) + { +#define TFL_NODE(OPCODE, CLASS) \ + case locoex::TFLOpcode::OPCODE: \ + return prefix + #OPCODE; +#include "Dialect/IR/TFLNodes.lst" +#undef TFL_NODE + default: + break; + }; + + return prefix + "Invalid"; +} + +// TFLNodeSummaryBuilder with default implementation +class TFLNodeSummaryBuilderBase : public locop::NodeSummaryBuilder +{ +public: + TFLNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *, locop::NodeSummary &s) const final; + +protected: +#define TFL_NODE(OPCODE, CLASS) \ + virtual bool summary(const CLASS *, locop::NodeSummary &s) const \ + { \ + s.comments().append("Emitted by Default TFLNodeSummaryBuilder"); \ + s.state(locop::NodeSummary::State::PartiallyKnown); \ + return true; \ + } +#include "Dialect/IR/TFLNodes.lst" +#undef TFL_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 TFLNodeSummaryBuilder final : public TFLNodeSummaryBuilderBase +{ +public: + TFLNodeSummaryBuilder(const locop::SymbolTable *tbl) : TFLNodeSummaryBuilderBase(tbl) + { + // DO NOTHING + } + +private: +#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final; + IMPLEMENT(locoex::TFLAdd) + IMPLEMENT(locoex::TFLAveragePool2D) + IMPLEMENT(locoex::TFLConcatenation) + IMPLEMENT(locoex::TFLConst) + IMPLEMENT(locoex::TFLConv2D) + IMPLEMENT(locoex::TFLDepthwiseConv2D) + IMPLEMENT(locoex::TFLDiv) + IMPLEMENT(locoex::TFLMaximum) + IMPLEMENT(locoex::TFLMaxPool2D) + IMPLEMENT(locoex::TFLMean) + IMPLEMENT(locoex::TFLMul) + IMPLEMENT(locoex::TFLRelu) + IMPLEMENT(locoex::TFLRelu6) + IMPLEMENT(locoex::TFLReshape) + IMPLEMENT(locoex::TFLRsqrt) + IMPLEMENT(locoex::TFLSqrt) + IMPLEMENT(locoex::TFLSquaredDifference) + IMPLEMENT(locoex::TFLSub) + IMPLEMENT(locoex::TFLTranspose) + IMPLEMENT(locoex::TFLTransposeConv) +#undef IMPLEMENT +}; + +bool TFLNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (node->dialect() != locoex::TFLDialect::get()) + return false; + +#define TFL_NODE(OPCODE, CLASS) \ + if (dynamic_cast(node)) \ + { \ + s.opname(tfl_opname(node->opnum())); \ + return summary(dynamic_cast(node), s); \ + } +#include "Dialect/IR/TFLNodes.lst" +#undef TFL_NODE + + return false; +} + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLAdd *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLAveragePool2D *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLConcatenation *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLConst *, locop::NodeSummary &s) const +{ + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLConv2D *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED); + assert(node->padding() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLDepthwiseConv2D *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED); + assert(node->padding() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLDiv *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 TFLNodeSummaryBuilder::summary(const locoex::TFLMaximum *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 TFLNodeSummaryBuilder::summary(const locoex::TFLMaxPool2D *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLMean *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 TFLNodeSummaryBuilder::summary(const locoex::TFLMul *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != locoex::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 TFLNodeSummaryBuilder::summary(const locoex::TFLRelu *node, locop::NodeSummary &s) const +{ + s.args().append("features", tbl()->lookup(node->features())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLRelu6 *node, locop::NodeSummary &s) const +{ + s.args().append("features", tbl()->lookup(node->features())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLReshape *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 TFLNodeSummaryBuilder::summary(const locoex::TFLRsqrt *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +// TODO TFLSoftmax + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLSqrt *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFLNodeSummaryBuilder::summary(const locoex::TFLSquaredDifference *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 TFLNodeSummaryBuilder::summary(const locoex::TFLSub *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 TFLNodeSummaryBuilder::summary(const locoex::TFLTranspose *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 TFLNodeSummaryBuilder::summary(const locoex::TFLTransposeConv *node, + locop::NodeSummary &s) const +{ + assert(node->padding() != locoex::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; +} + +} // namespace + +// For Circle +namespace +{ + +std::string circle_opname(uint32_t opnum) +{ + static std::string prefix{"circle."}; + + switch (static_cast(opnum)) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + case locoex::CircleOpcode::OPCODE: \ + return prefix + #OPCODE; +#include "Dialect/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 "Dialect/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(locoex::CircleInstanceNorm) +#undef IMPLEMENT +}; + +bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (node->dialect() != locoex::CircleDialect::get()) + return false; + +#define CIRCLE_NODE(OPCODE, CLASS) \ + if (dynamic_cast(node)) \ + { \ + s.opname(circle_opname(node->opnum())); \ + return summary(dynamic_cast(node), s); \ + } +#include "Dialect/IR/CircleNodes.lst" +#undef CIRCLE_NODE + + return false; +} + +bool CircleNodeSummaryBuilder::summary(const locoex::CircleInstanceNorm *node, + locop::NodeSummary &s) const +{ + auto fused = node->fusedActivationFunction(); + assert(fused != locoex::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 exo +{ + +bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (TFLNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (CircleNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (locoex::COpNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + return false; +} + +} // namespace exo diff --git a/compiler/exo/src/ExoFormattedGraph.h b/compiler/exo/src/ExoFormattedGraph.h new file mode 100644 index 000000000..714e483b5 --- /dev/null +++ b/compiler/exo/src/ExoFormattedGraph.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXO_FORMATTED_GRAPH_H__ +#define __EXO_FORMATTED_GRAPH_H__ + +#include + +#include + +namespace exo +{ + +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 create(const locop::SymbolTable *tlb) const final + { + return stdex::make_unique(tlb); + } +}; + +} // namespace exo + +#endif // __EXO_FORMATTED_GRAPH_H__ diff --git a/compiler/exo/src/ExoOptimize.cpp b/compiler/exo/src/ExoOptimize.cpp new file mode 100644 index 000000000..d7278e900 --- /dev/null +++ b/compiler/exo/src/ExoOptimize.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExoOptimize.h" + +#include "Knob.h" +#include "Passes.h" +#include "ProgressReporter.h" + +#include + +#include + +namespace exo +{ + +void optimize(loco::Graph *g) +{ + logo::Phase phase; + { + // prepare type and shape before optimization + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + + phase.emplace_back(stdex::make_unique()); + phase.emplace_back(stdex::make_unique()); + + if (get()) + { + phase.emplace_back(stdex::make_unique()); + } + + if (get()) + { + phase.emplace_back(stdex::make_unique()); + } + + if (get()) + { + phase.emplace_back(stdex::make_unique()); + } + phase.emplace_back(stdex::make_unique()); + + if (get()) + { + phase.emplace_back(stdex::make_unique()); + } + + phase.emplace_back(stdex::make_unique()); + + phase.emplace_back(stdex::make_unique()); + } + + logo::PhaseRunner phase_runner{g}; + + ProgressReporter prog(g, logo::PhaseStrategy::Restart); + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace exo diff --git a/compiler/exo/src/ExoOptimize.h b/compiler/exo/src/ExoOptimize.h new file mode 100644 index 000000000..4769c1193 --- /dev/null +++ b/compiler/exo/src/ExoOptimize.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPTIMIZE_H__ +#define __OPTIMIZE_H__ + +#include + +namespace exo +{ + +/** + * @brief Run passes for a graph after completion of converting canonical nodes into TFL nodes. + * + * TODO Separate optimize pass dedicated to TFL and Circle dialect when necessary + */ +void optimize(loco::Graph *); + +} // namespace exo + +#endif // __OPTIMIZE_H__ diff --git a/compiler/exo/src/ExporterUtils.cpp b/compiler/exo/src/ExporterUtils.cpp new file mode 100644 index 000000000..41ccdcd71 --- /dev/null +++ b/compiler/exo/src/ExporterUtils.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExporterUtils.h" + +#include + +#include + +namespace exo +{ + +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()); + case loco::Domain::Feature: + return to_shape_description(shape.as()); + case loco::Domain::Filter: + return to_shape_description(shape.as()); + case loco::Domain::DepthwiseFilter: + return to_shape_description(shape.as()); + case loco::Domain::Bias: + return to_shape_description(shape.as()); + case loco::Domain::Matrix: + return to_shape_description(shape.as()); + default: + break; + } + + INTERNAL_EXN_V("Unsupported loco domain", oops::to_uint32(shape.domain())); +} + +} // namespace exo diff --git a/compiler/exo/src/ExporterUtils.h b/compiler/exo/src/ExporterUtils.h new file mode 100644 index 000000000..e1f1f66a8 --- /dev/null +++ b/compiler/exo/src/ExporterUtils.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXPORTER_UTILS_H__ +#define __EXPORTER_UTILS_H__ + +#include "loco.h" + +#include "loco/IR/PermutingCodec.h" +#include "loco/IR/NodeShape.h" + +namespace exo +{ + +struct ShapeDescription +{ + std::vector _dims; + bool _rank_known; +}; + +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 inline bool isNHWC(Permutation *perm); + +template <> inline bool isNHWC(loco::Permutation *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 *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 exo + +#endif // __EXPORTER_UTILS_H__ diff --git a/compiler/exo/src/GraphBlock.cpp b/compiler/exo/src/GraphBlock.cpp new file mode 100644 index 000000000..0a45ce8ad --- /dev/null +++ b/compiler/exo/src/GraphBlock.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GraphBlock.h" + +#include "Check.h" + +#include +#include + +namespace +{ + +template loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + // Make NHWC permutation for encoder and decoder + loco::Permutation 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 loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + loco::Permutation 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 perm() +{ + + // Make NHWC permutation for encoder and decoder + loco::Permutation 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 loco::Permutation perm(); + +template <> +loco::Permutation perm() +{ + loco::Permutation 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 loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + loco::Permutation HW; + + HW.axis(loco::MatrixAxis::Height) = 0; + HW.axis(loco::MatrixAxis::Width) = 1; + + return HW; +} + +template <> loco::Permutation perm() +{ + loco::Permutation WH; + + WH.axis(loco::MatrixAxis::Height) = 1; + WH.axis(loco::MatrixAxis::Width) = 0; + + return WH; +} + +} // namespace + +namespace exo +{ + +template loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode) +{ + EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = stdex::make_unique>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode) +{ + EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = stdex::make_unique>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode) +{ + EXO_ASSERT(input_for_encode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = stdex::make_unique>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode) +{ + EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = stdex::make_unique>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template +loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode) +{ + EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = stdex::make_unique>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode) +{ + EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = stdex::make_unique>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode) +{ + EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = stdex::make_unique>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +// template instantiation +template loco::FeatureEncode * +make_feature_encode(loco::Node *input_for_encode); + +template loco::FeatureDecode * +make_feature_decode(loco::Node *input_for_encode); + +template loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode); +template loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode); + +template loco::DepthwiseFilterDecode * +make_dw_filter_decode(loco::Node *input_for_decode); + +template loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); +template loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); +template loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); +template loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); + +} // namespace exo diff --git a/compiler/exo/src/GraphBlock.h b/compiler/exo/src/GraphBlock.h new file mode 100644 index 000000000..b771c821b --- /dev/null +++ b/compiler/exo/src/GraphBlock.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BLOCK_H__ +#define __GRAPH_BLOCK_H__ + +#include +#include + +#include + +#include + +namespace exo +{ + +/// @brief feature layout of TFLITE file +enum class FeatureLayout +{ + NHWC, +}; + +/// @brief Creates a loco::FeatureEncode with T layout (NHWC for tflite) and add it to graph. +template 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 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 loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::FilterDecode of given layout +template loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode); + +enum class DepthwiseFilterLayout +{ + HWCM, +}; + +/// @brief Create a loco::DepthwiseFilterDecode of given layout +template +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 loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::MatrixDecode of given layout +template loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); + +} // exo + +// +// 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 exo +{ + +/** + * @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 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 getInputsToConvert(CanonicalT *origin) = 0; + + /// @brief Set the inputs of replacer to new_inputs + virtual void set(TFLT *replacer, std::vector &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 DomainConverter +{ +public: + template + TFLT *convert(CanonicalT *origin, InputHandler &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 +template +TFLT *DomainConverter::convert(CanonicalT *origin, + InputHandler &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(); + + // 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 feature_decodes; + + for (auto input : input_handler.getInputsToConvert(origin)) + { + auto dec = make_feature_decode(input); + feature_decodes.emplace_back(dec); + } + + input_handler.set(tfl_node, feature_decodes); + + auto enc = make_feature_encode(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 exo + +#endif //__GRAPH_BLOCK_H__ diff --git a/compiler/exo/src/Knob.cpp b/compiler/exo/src/Knob.cpp new file mode 100644 index 000000000..50d78f4b7 --- /dev/null +++ b/compiler/exo/src/Knob.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Knob.h" + +#include + +#include +#include +#include + +// Basic Infrastructure to declare and access Knob values +namespace +{ + +using KnobName = std::string; + +/** + * @brief Load configuration (from somewhere) + */ +struct KnobLoader +{ + virtual ~KnobLoader() = default; + + virtual bool load(const KnobName &name, bool default_value) const = 0; +}; + +/** + * @brief Load configuration from environment variables + * + * Given a prefix P, EnvKnobLoader reads a configuration K from concat(P, K). + * + * For example, let us assume that P is "MY_" and K is "CONFIG". + * + * Then, EnvKnobLoader reads configuration CONFIG from environment variable MY_CONFIG. + */ +class EnvKnobLoader final : public KnobLoader +{ +public: + EnvKnobLoader() = default; + +public: + bool load(const KnobName &knob_name, bool default_value) const override + { + auto envvar = _prefix + knob_name; + auto s = std::getenv(envvar.c_str()); + + return pepper::safe_strcast(s, default_value ? 1 : 0) != 0; + } + void knob_set(const KnobName &knob_name, bool value) { _knob[knob_name] = value; } + void dialect_set(const exo::Dialect &dialect_name) { _prefix = _label[dialect_name]; } + bool knob_get(const KnobName &knob_name) { return load(knob_name, _knob[knob_name]); } + +private: + /// @brief Environment variable prefix + std::string _prefix; + std::map _knob; + std::map _label = {{exo::Dialect::TFLITE, "TFL_"}, + {exo::Dialect::CIRCLE, "CIRCLE_"}}; +}; + +} // namespace + +namespace +{ + +EnvKnobLoader &knob_loader(void) +{ + // TODO separate "EXOTFLITE_" and "EXOCIRCLE_" when necessary + static EnvKnobLoader loader; + return loader; +} + +} // namespace + +namespace exo +{ + +#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \ + template <> typename KnobTrait::ValueType get(void) \ + { \ + return ::knob_loader().knob_get(#NAME); \ + } +#include "Knob.lst" +#undef KNOB_BOOL + +void set(Dialect d) +{ + ::knob_loader().dialect_set(d); + switch (d) + { + case Dialect::TFLITE: +#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \ + ::knob_loader().knob_set(#NAME, TFL_DEFAULT); +#include "Knob.lst" +#undef KNOB_BOOL + break; + case Dialect::CIRCLE: +#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \ + ::knob_loader().knob_set(#NAME, CIRCLE_DEFAULT); +#include "Knob.lst" +#undef KNOB_BOOL + break; + default: + std::runtime_error("UnKnown dialect"); + } +} + +} // namespace exo diff --git a/compiler/exo/src/Knob.h b/compiler/exo/src/Knob.h new file mode 100644 index 000000000..98613120c --- /dev/null +++ b/compiler/exo/src/Knob.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __KNOB_H__ +#define __KNOB_H__ + +namespace exo +{ + +enum class Dialect +{ + TFLITE, + CIRCLE +}; + +enum class Knob +{ +#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) NAME, +#include "Knob.lst" +#undef KNOB_BOOL +}; + +template struct KnobTrait; + +#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \ + template <> struct KnobTrait \ + { \ + using ValueType = bool; \ + }; +#include "Knob.lst" +#undef KNOB_BOOL + +template typename KnobTrait::ValueType get(void); +void set(Dialect); + +} // namespace exo + +#endif // __KNOB_H__ diff --git a/compiler/exo/src/Knob.lst b/compiler/exo/src/Knob.lst new file mode 100644 index 000000000..7f59c93f3 --- /dev/null +++ b/compiler/exo/src/Knob.lst @@ -0,0 +1,11 @@ +#ifndef KNOB_BOOL +#error "KNOB_BOOL is not defined" +#endif // KNOB_BOOL + +// KNOB_BOOL(KNOB_NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESCRIPTION) + +// Optimization pass +KNOB_BOOL(UseFuseBiasAddPass, true, true, Fuse TFLAdd or TFLSub into TFLConv2D) +KNOB_BOOL(UseFuseInstanceNormPass, false, true, Fuse InstanceNorm pattern) +KNOB_BOOL(UseFuseReluPass, true, true, Fuse TFLAdd or TFLSub into TFLConv2D or so) +KNOB_BOOL(UseFuseSquaredDifferencePass, false, true, Fuse SquaredDifference pattern) diff --git a/compiler/exo/src/Log.cpp b/compiler/exo/src/Log.cpp new file mode 100644 index 000000000..aa762968b --- /dev/null +++ b/compiler/exo/src/Log.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Log.h" + +#include +#include + +#include +#include + +// 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 T safecast(const char *, const T &); + +template <> bool safecast(const char *s, const bool &value) +{ + return (s == nullptr) ? value : (std::stoi(s) != 0); +} + +} // namespace + +namespace exo +{ + +// +// Logger +// +Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); } +Logger::~Logger() { deactivate(); } + +// +// LoggerConfig +// +LoggerConfig::LoggerConfig() +{ + // Turn on logging if EXO_LOG is set as non-zero value + _enabled = safecast(std::getenv("EXO_LOG"), false); +} + +void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const +{ + // Let's ignore hermes::Sources if that is not a exo logger + if (auto logger = dynamic_cast(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 exo diff --git a/compiler/exo/src/Log.h b/compiler/exo/src/Log.h new file mode 100644 index 000000000..8ca38c3ec --- /dev/null +++ b/compiler/exo/src/Log.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + +#include "exo/LoggingContext.h" + +#include + +namespace exo +{ + +/** + * @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 EXO_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 exo + +/** + * HOW TO USE: + * + * LOGGER(l); + * + * INFO(l) << "Hello, World" << std::endl; + * + */ +#define LOGGER(name) ::exo::Logger name{::exo::LoggingContext::get()}; + +// TODO Support FATAL, ERROR, WARN, and VERBOSE +#define INFO(name) HERMES_INFO(name) + +// WARNING! +// +// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE. +// + +#endif // __LOG_H__ diff --git a/compiler/exo/src/LogHelper.cpp b/compiler/exo/src/LogHelper.cpp new file mode 100644 index 000000000..7520b7ec8 --- /dev/null +++ b/compiler/exo/src/LogHelper.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LogHelper.h" + +namespace loco +{ + +std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape) +{ + os << "[" << feature_shape.count().value() << "," << feature_shape.height().value() << "," + << feature_shape.width().value() << "," << feature_shape.depth().value() << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape) +{ + os << "[" << filter_shape.height().value() << "," << filter_shape.width().value() << "," + << filter_shape.depth().value() << "," << filter_shape.count().value() << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape) +{ + os << "["; + for (uint32_t r = 0; r < tensor_shape.rank(); ++r) + { + if (r) + os << ","; + os << tensor_shape.dim(r).value(); + } + os << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad) +{ + os << "[TLBR " << pad.top() << "," << pad.left() << "," << pad.bottom() << "," << pad.right() + << "]"; + + return os; +} + +} // namespace loco + +std::ostream &operator<<(std::ostream &os, const std::vector &vi64) +{ + for (auto vi : vi64) + { + os << vi << " "; + } + return os; +} + +#include "ExoFormattedGraph.h" + +namespace exo +{ + +FormattedGraph fmt(loco::Graph *g) +{ + auto node_summary_builder = stdex::make_unique(); + return std::move(locop::fmt(g).with(std::move(node_summary_builder))); +} + +} // namespace exo diff --git a/compiler/exo/src/LogHelper.h b/compiler/exo/src/LogHelper.h new file mode 100644 index 000000000..69d81af9e --- /dev/null +++ b/compiler/exo/src/LogHelper.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOG_HELPER_H__ +#define __LOG_HELPER_H__ + +#include + +#include +#include +#include + +#include +#include + +namespace loco +{ + +/** + * @brief dump FeatureShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape); + +/** + * @brief dump FilterShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape); + +/** + * @brief dump TensorShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape); + +/** + * @brief dump Padding2D values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad); + +} // namespace loco + +/** + * @brief dump std::vector values to stream + */ +std::ostream &operator<<(std::ostream &os, const std::vector &vi64); + +namespace exo +{ + +using FormattedGraph = locop::FormattedGraphImpl; + +FormattedGraph fmt(loco::Graph *g); + +static inline FormattedGraph fmt(const std::unique_ptr &g) { return fmt(g.get()); } + +} // namespace exo + +#endif // __LOG_HELPER_H__ diff --git a/compiler/exo/src/LoggingContext.cpp b/compiler/exo/src/LoggingContext.cpp new file mode 100644 index 000000000..1c14d97b9 --- /dev/null +++ b/compiler/exo/src/LoggingContext.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "exo/LoggingContext.h" +#include "Log.h" // To use LoggerConfig + +#include +#include + +namespace exo +{ + +hermes::Context *LoggingContext::get(void) +{ + static hermes::Context *ctx = nullptr; + + if (ctx == nullptr) + { + ctx = new hermes::Context; + ctx->sinks()->append(stdex::make_unique()); + ctx->config(stdex::make_unique()); + } + + return ctx; +} + +} // namespac exo diff --git a/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp b/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp new file mode 100644 index 000000000..0fdcea939 --- /dev/null +++ b/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FoldReshapeOfConstPass.h" + +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include + +#include + +namespace +{ + +/** + * @brief Check if node is TFLReshape and its input is TFLConst + * @return Casted TFLReshape for foldable candidate, nullptr otherwise + */ +locoex::TFLReshape *as_candidate(loco::Node *node) +{ + auto reshape = dynamic_cast(node); + if (not reshape) + return nullptr; + + // Only accept Constant input of Reshape + if (not dynamic_cast(reshape->tensor())) + return nullptr; + + return reshape; +} + +uint32_t volume(loco::Node *tensor_node) +{ + auto shape = loco::shape_get(tensor_node).as(); + + uint32_t vol = 1; + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + vol *= shape.dim(axis).value(); + + return vol; +} + +void fold_reshape_of_const(locoex::TFLReshape *reshape) +{ + const loco::DataType FLOAT32 = loco::DataType::FLOAT32; + + auto const_orig = dynamic_cast(reshape->tensor()); + + // Exceptions + { + EXO_ASSERT(const_orig, "Only support for Reshape-Const pair"); + // TODO support other data types + if (const_orig->dtype() != FLOAT32) + INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(const_orig->dtype())); + + if (volume(const_orig) != volume(reshape)) + INTERNAL_EXN("New shape of Reshape is not matched"); + } + + auto new_shape = loco::shape_get(reshape).as(); + + // TFLConst to replace + auto const_new = reshape->graph()->nodes()->create(); + + const_new->dtype(FLOAT32); + const_new->rank(new_shape.rank()); + const_new->size(const_orig->size()); + for (uint32_t axis = 0; axis < new_shape.rank(); ++axis) + const_new->dim(axis) = new_shape.dim(axis); + + for (uint32_t i = 0; i < const_new->size(); ++i) + { + const_new->at(i) = const_orig->at(i); + } + + // replace + loco::replace(reshape).with(const_new); +} + +} // namespace + +namespace exo +{ + +bool FoldReshapeOfConstPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto reshape = as_candidate(node)) + { + fold_reshape_of_const(reshape); + changed = true; + } + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FoldReshapeOfConstPass.h b/compiler/exo/src/Pass/FoldReshapeOfConstPass.h new file mode 100644 index 000000000..10f8004bf --- /dev/null +++ b/compiler/exo/src/Pass/FoldReshapeOfConstPass.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__ +#define __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse TFLReshape + TFLConst into one equivalent TFLConst + * + * + * TFLConst --- TFLReshape --- Out + * + * + * TFLConst --- TFLReshape --- + * TFLConst (new) ------------ Out + * + * TODO This pass is for temporary. Deprecate this pass. + */ +struct FoldReshapeOfConstPass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FoldReshapeOfConstPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__ diff --git a/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp new file mode 100644 index 000000000..005c42944 --- /dev/null +++ b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FoldTransposeOfConstPass.h" + +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +// TODO remove dependency to angkor +#include +#include + +#include + +namespace +{ + +/** + * @brief Check if node is TFLTranspose and its input is TFLConst + * @return Casted TFLTranspose for foldable candidate, nullptr otherwise + */ +locoex::TFLTranspose *as_candidate(loco::Node *node) +{ + auto transpose = dynamic_cast(node); + if (not transpose) + return nullptr; + + // Only accept Constant input of Transpose + if (not dynamic_cast(transpose->a())) + return nullptr; + + // Only accept Constant permutation of Transpose + if (not dynamic_cast(transpose->perm())) + return nullptr; + + return transpose; +} + +nncc::core::ADT::tensor::Shape angkor_shape(locoex::TFLConst *node) +{ + nncc::core::ADT::tensor::Shape ret; + + ret.resize(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + ret.dim(axis) = node->dim(axis).value(); + } + + return ret; +} + +void fold_transpose_of_const(locoex::TFLTranspose *transpose) +{ + const loco::DataType FLOAT32 = loco::DataType::FLOAT32; + const loco::DataType S32 = loco::DataType::S32; + + auto const_orig = dynamic_cast(transpose->a()); + auto perm = dynamic_cast(transpose->perm()); + + // Exceptions + { + EXO_ASSERT(const_orig, "Only support for Transpose-Const pair"); + // TODO support other data types + if (const_orig->dtype() != FLOAT32) + INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(const_orig->dtype())); + + EXO_ASSERT(perm, "Only support for constant permutation for Transpose"); + // TODO support other data types + if (perm->dtype() != S32) + INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(perm->dtype())); + + auto okay = [&]() { + if (perm->rank() != 1) + return false; + if (perm->dim(0).value() != const_orig->rank()) + return false; + return true; + }; + if (not okay()) + INTERNAL_EXN("Input and permutation for Transpose is not congruent"); + } + + uint32_t rank = const_orig->rank(); + + // TFLConst to replace + auto const_new = transpose->graph()->nodes()->create(); + + const_new->dtype(FLOAT32); + const_new->rank(rank); + const_new->size(const_orig->size()); + for (uint32_t axis = 0; axis < rank; ++axis) + const_new->dim(axis) = const_orig->dim(perm->at(axis)).value(); + + // TODO remove dependency to angkor + auto shape_orig = angkor_shape(const_orig); + auto shape_new = angkor_shape(const_new); + + nncc::core::ADT::tensor::LexicalLayout l; + nncc::core::ADT::tensor::IndexEnumerator e{shape_new}; + + for (; e.valid(); e.advance()) + { + loco::TensorIndex index_new = e.current(); + loco::TensorIndex index_orig; + + // Set original index from matching new index + index_orig.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + index_orig.at(perm->at(axis)) = index_new.at(axis); + + const_new->at(l.offset(shape_new, index_new)) = + const_orig->at(l.offset(shape_orig, index_orig)); + } + + // replace + loco::replace(transpose).with(const_new); +} + +} // namespace + +namespace exo +{ + +bool FoldTransposeOfConstPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto transpose = as_candidate(node)) + { + fold_transpose_of_const(transpose); + changed = true; + } + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FoldTransposeOfConstPass.h b/compiler/exo/src/Pass/FoldTransposeOfConstPass.h new file mode 100644 index 000000000..26656a118 --- /dev/null +++ b/compiler/exo/src/Pass/FoldTransposeOfConstPass.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__ +#define __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse TFLTranspose + TFLConst into one equivalent TFLConst + * + * + * TFLConst --- TFLTranspose --- Out + * + * + * TFLConst --- TFLTranspose --- + * TFLConst (new) -------------- Out + * + * TODO This pass is for temporary. Deprecate this pass. + */ +struct FoldTransposeOfConstPass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FoldTransposeOfConstPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__ diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.cpp b/compiler/exo/src/Pass/FuseBiasAddPass.cpp new file mode 100644 index 000000000..aab820995 --- /dev/null +++ b/compiler/exo/src/Pass/FuseBiasAddPass.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseBiasAddPass.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include +#include + +#include + +#include + +/* + Note: Terms for variables in this implementation is as follows: + + ex) subgraph handled: TFLConv2D -------- TFLAdd + (or TFLDepthwiseConv2D) (or TFLSub) + | | + \|/ \|/ + variable name : former latter + Type : FormerT LatterT + (shortened name from Mixin) (template type) +*/ +namespace +{ + +using FormerT = locoex::TFLNodeMixin; + +loco::Node *as_loco_node(FormerT *former) +{ + auto loco_node = dynamic_cast(former); + assert(loco_node != nullptr); + + return loco_node; +} + +locoex::TFLConst *get_const(loco::Node *x, loco::Node *y) +{ + if (auto const_node = dynamic_cast(x)) + return const_node; + else if (auto const_node = dynamic_cast(y)) + return const_node; + + return nullptr; +} + +FormerT *get_former(loco::Node *x, loco::Node *y) +{ + if (auto node = dynamic_cast(x)) + return node; + else if (auto node = dynamic_cast(y)) + return node; + + return nullptr; +} + +/// @brief Finds input that is TFLConst and set it to new_input +void set_const_input(locoex::TFLNode *node, locoex::TFLConst *new_input) +{ + if (auto add = dynamic_cast(node)) + { + if (dynamic_cast(add->x())) + add->x(new_input); + else if (dynamic_cast(add->y())) + add->y(new_input); + else + assert(false and "One node should be TFLConst"); + + return; + } + + if (auto sub = dynamic_cast(node)) + { + if (dynamic_cast(sub->x())) + sub->x(new_input); + else if (dynamic_cast(sub->y())) + sub->y(new_input); + else + assert(false and "One node should be TFLConst"); + + return; + } + + assert(false and "Param should be TFLAdd or TFLSub"); +} + +/** + * @brief Creates a TFLConst whose shape is [to] and values are all const_node->at(0), + * where const_node has only one element(a scalar or a tensor of shape [1]) + */ +locoex::TFLConst *create_widened(locoex::TFLConst *const_node, uint32_t to) +{ + auto const_shape = loco::shape_get(const_node).as(); + + assert(const_shape.rank() == 0 or (const_shape.rank() == 1 and const_shape.dim(0) == 1)); + + auto g = const_node->graph(); + + auto widened_const = g->nodes()->create(); + { + widened_const->dtype(loco::DataType::FLOAT32); + widened_const->rank(1); + widened_const->dim(0) = to; + widened_const->size(to); + for (uint32_t x = 0; x < to; x++) + widened_const->at(x) = const_node->at(0); + } + return widened_const; +} + +template float calc(float, float); + +template <> float calc(float x, float y) { return x + y; } +template <> float calc(float x, float y) { return x - y; } + +template class Fuser +{ +public: + Fuser(LatterT *latter) + { + static_assert(std::is_same::value || + std::is_same::value, + "wrong template type"); + + _latter = latter; + _graph = _latter->graph(); + _const_node = get_const(_latter->x(), _latter->y()); + _former = get_former(_latter->x(), _latter->y()); + + assert(_const_node && _former); + } + + void fuse(void); + +private: + loco::Graph *_graph; + LatterT *_latter; + locoex::TFLConst *_const_node; + FormerT *_former; + + locoex::TFLConst *create_fused_bias_const(); +}; + +// instantiation +template class Fuser; +template class Fuser; + +template locoex::TFLConst *Fuser::create_fused_bias_const() +{ + // we have to create a new bias const by adding/substracting bias and const node (of TFLAdd or + // TFLSub) + auto bias = dynamic_cast(_former->bias()); + assert(bias->dtype() == loco::DataType::FLOAT32 && + _const_node->dtype() == loco::DataType::FLOAT32); + + assert(bias->rank() == 1 && _const_node->rank() == 1); + assert(bias->dim(0) == _const_node->dim(0)); + + // build a new bias const + auto new_bias = _graph->nodes()->create(); + { + new_bias->dtype(loco::DataType::FLOAT32); + + new_bias->rank(1); + new_bias->dim(0) = bias->dim(0); + + new_bias->size(bias->dim(0).value()); + + for (uint32_t x = 0; x < bias->dim(0).value(); x++) + new_bias->at(x) = calc( + bias->at(x), _const_node->at(x)); + } + + return new_bias; +} + +// FuseBiasAddPass works when former->fusedActivationFunction() == NONE +bool check_act_func(FormerT *former) +{ + using FusedActFuncMixin = locoex::TFLNodeMixin; + + if (auto node = dynamic_cast(former)) + return node->fusedActivationFunction() == locoex::FusedActFunc::NONE; + else + return true; +} + +template void set_act_func(FormerT *former, LatterT *latter) +{ + using FusedActFuncMixin = locoex::TFLNodeMixin; + + if (auto node = dynamic_cast(former)) + node->fusedActivationFunction(latter->fusedActivationFunction()); +} + +// instantiation +template void set_act_func(FormerT *, locoex::TFLAdd *); +template void set_act_func(FormerT *, locoex::TFLSub *); + +/** + * @brief Fuse TFLAdd or TFLSub (latter) into TFLConv2d or TFLDepthwiseConv2D (former). + * All conditions should be checked before calling this. + * + * @note TFLAdd can have fused activation function (let's call this FAF for simplicity). + * + * Conv2D's FAF | TFLAdd's FAF => FAF after fusing TFLAdd into TFLConv2D + * ----------------|--------------- -------------------------------------- + * NONE | NONE, RELU or RELU6 => TFLAdd's FAF + * other than NONE | anything => cannot be fused + */ +template void Fuser::fuse(void) +{ + // check fused activation function + { + assert(check_act_func(_former)); + + set_act_func(_former, _latter); + } + + auto new_bias = create_fused_bias_const(); + + // replace node with new_bias + // note that loco::replace() is not used because bias could be input of other op just in case + _former->bias(new_bias); + + // remove TFLAdd or TFLSub node + loco::replace(_latter).with(as_loco_node(_former)); + _latter->x(nullptr); + _latter->y(nullptr); +} + +struct Collector final : public locoex::TFLNodeMutableVisitor +{ + template + void setCandidate(FormerT *former, LatterT *latter, locoex::TFLConst *const_node) + { + static_assert(std::is_same::value || + std::is_same::value, + "wrong template type"); + + if (!check_act_func(former)) + return; + + auto depth = + loco::shape_get(as_loco_node(former)).template as().dim(3).value(); + auto const_shape = loco::shape_get(const_node).template as(); + + if (const_shape.rank() == 1 and const_shape.dim(0) == depth) + { + candidates.insert(latter); + } + // when Const has only one value, create a new const with shape [depth] + else if (const_shape.rank() == 0 or (const_shape.rank() == 1 and const_shape.dim(0) == 1)) + { + if (!(loco::dtype_get(as_loco_node(former)) == loco::DataType::FLOAT32)) + INTERNAL_EXN_V("Unsupported data type", + oops::to_uint32(loco::dtype_get(as_loco_node(former)))); + if (!(const_node->dtype() == loco::DataType::FLOAT32)) + INTERNAL_EXN_V("Unsupported data type", oops::to_uint32(const_node->dtype())); + + auto new_bias_node = create_widened(const_node, depth); + + // Replacing TFLConst input of TFLAdd or TFLSub. + // Note that calling loco::replace(const_node).with(new_bias_node) could be dangerous + // because const_node could be the input of many nodes + set_const_input(latter, new_bias_node); + + candidates.insert(latter); + } + } + + void visit(locoex::TFLAdd *latter) final + { + auto former = get_former(latter->x(), latter->y()); + auto const_node = get_const(latter->x(), latter->y()); + + if (former && const_node) + setCandidate(former, latter, const_node); + } + + void visit(locoex::TFLSub *latter) final + { + // TFLSub, of which x() = TFLConv2D or TFLDepthwiseConv2D, y() = TFLConst, is fusing target + auto former = dynamic_cast(latter->x()); + auto const_node = dynamic_cast(latter->y()); + + if (former && const_node) + setCandidate(former, latter, const_node); + } + + void visit(locoex::TFLNode *) final { return; } + + std::set candidates; +}; + +struct Performer final : public locoex::TFLNodeMutableVisitor +{ + void visit(locoex::TFLAdd *latter) final + { + assert(get_former(latter->x(), latter->y())); + + Fuser fuser(latter); + fuser.fuse(); + } + + void visit(locoex::TFLSub *latter) final + { + assert(get_former(latter->x(), latter->y())); + + Fuser fuser(latter); + fuser.fuse(); + } + + void visit(locoex::TFLNode *) final { assert(false && "should not be called"); } +}; + +} // namespace + +namespace exo +{ + +bool FuseBiasAddPass::run(loco::Graph *g) +{ + Collector collector; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (node->dialect() == locoex::TFLDialect::get()) + { + auto tfl_node = dynamic_cast(node); + tfl_node->accept(&collector); + } + } + + Performer performer; + + for (auto node : collector.candidates) + { + node->accept(&performer); + } + + return collector.candidates.size() > 0; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.h b/compiler/exo/src/Pass/FuseBiasAddPass.h new file mode 100644 index 000000000..68e624c6b --- /dev/null +++ b/compiler/exo/src/Pass/FuseBiasAddPass.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_FUSE_BIASADD_PASS_H__ +#define __PASS_FUSE_BIASADD_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse TFLAdd or TFLSub into Bias input of the following ops: + * - TFLConv2D, TFLDepthwiseConv2D + * - TODO Consider to add FullyConnected, etc. + * + * Case 1. Conv2D and TFLAdd + * + * BEFORE: + * + * TFLConst A (a scalar or a tensor of shape [1] or [depth of TFLConv2D]) + * | + * Foo -- TFLConv2D -- TFLAdd (or TFLSub) -- Bar + * | + * TFLConst B --+ (bias) + * + * AFTER: + * Foo ----- TFLConv2D ----- Bar + * | + * TFLConst A' --+ (bias) + * + * TFLConst B (dead node) + * + * TFLAdd (or TFLSub) (dead node) + * + * @note TFLSub, of which x() == TFLConv2D and y() == TFLConst, will be fused. + * If x() == TFLConst and y() == TFLConv2D, it won't be fused. + */ +struct FuseBiasAddPass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FuseBiasAddPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __PASS_FUSE_BIASADD_PASS_H__ diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp b/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp new file mode 100644 index 000000000..6ba728de0 --- /dev/null +++ b/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseBiasAddPass.h" + +#include "Dialect/IR/TFLNodes.h" +#include "TestGraph.h" +#include "TestHelper.h" + +#include + +#include + +namespace +{ + +void init(loco::Pull *pull) +{ + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2, 3, 3, 2}); +} + +/// @brief Initializes TFLConv2D and related filter and bias +void init(locoex::TFLConv2D *conv2d, locoex::TFLConst *filter, locoex::TFLConst *bias) +{ + // set conv2d + { + conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE); + conv2d->padding(locoex::Padding::VALID); + } + + // set filter + { + filter->dtype(loco::DataType::FLOAT32); + filter->shape({2, 3, 3, 2}); + filter->size(2 * 3 * 3 * 2); + + for (uint32_t x = 0; x < 2 * 3 * 3 * 2; x++) + filter->at(x) = 0.0; + } + + // set bias + { + bias->dtype(loco::DataType::FLOAT32); + bias->shape({2}); + bias->size(2); + + for (uint32_t x = 0; x < 2; x++) + bias->at(x) = 0.0; + } +} + +template void init(T *node, locoex::FusedActFunc f) +{ + static_assert(std::is_same::value || std::is_same::value, + "wrong template type"); + + node->fusedActivationFunction(f); +} + +/// @brief Initializes one param of TFLAdd or TFLSub +void init(locoex::TFLConst *addsub_param) +{ + // set addsub_param : y() value of TFLAdd or TFLSub + addsub_param->dtype(loco::DataType::FLOAT32); + addsub_param->shape({2}); + addsub_param->size(2); + + for (uint32_t x = 0; x < 2; x++) + addsub_param->at(x) = (x + 1) * 1.5; // 1.5, 3 +} + +} // namespace + +// A case when +// - TFLConv2D has bias (0, 0) +// - TFLAdd, of which x() or y() == TFLConv2D +// - Another param of TFLAdd is TFLConst, (1.5, 3) +// +// After fusion, bias shold be (1.5, 3) +TEST(FuseBiasAddPassTest, Conv2D_Add_01_basic) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto add_y = g.append(); + auto add = g.append(conv2d, add_y); + + g.complete(add); + + init(g.pull); + init(conv2d, filter, bias); + init(add, locoex::FusedActFunc::NONE); + init(add_y); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + + auto a_bias = dynamic_cast(a_conv2d->bias()); + ASSERT_TRUE(a_bias != nullptr); + + ASSERT_TRUE(a_bias->dim(0) == 2); + ASSERT_FLOAT_EQ(a_bias->at(0), + bias->at(0) + add_y->at(0)); + ASSERT_FLOAT_EQ(a_bias->at(1), + bias->at(1) + add_y->at(1)); +} + +// A case when +// - TFLConv2D has bias (0, 0) +// - TFLAdd, of which x() or y() == TFLConv2D +// - Another param of TFLAdd is TFLConst, (1.5) <-- scalar +// +// After fusion, bias shold be (1.5, 1.5) +TEST(FuseBiasAddPassTest, Conv2D_Add_02_TFLAdd_y_is_scalar) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto add_y = g.append(); + auto add = g.append(conv2d, add_y); + + g.complete(add); + + init(g.pull); + init(conv2d, filter, bias); // channel of conv2d is 2 + + { + // Size of this TFLConst is 1. + // Note that this should be widened later to the shape of [channel of Conv2D], which is [2] + add_y->dtype(loco::DataType::FLOAT32); + add_y->shape({1}); + add_y->size(1); + add_y->at(0) = 1.5; + } + init(add, locoex::FusedActFunc::NONE); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + + auto a_bias = dynamic_cast(a_conv2d->bias()); + ASSERT_TRUE(a_bias != nullptr); + + ASSERT_TRUE(a_bias->dim(0) == 2); + ASSERT_FLOAT_EQ(a_bias->at(0), + bias->at(0) + 1.5); + ASSERT_FLOAT_EQ(a_bias->at(1), + bias->at(1) + 1.5); +} + +// A case when +// - TFLConv2D has bias (0, 0) +// - TFLSub.x() == TFLConv2D +// - TFLSub.y() == TFLConst, (1.5, 3) +// +// After fusion, bias shold be (-1.5, -3) +TEST(FuseBiasAddPassTest, Conv2D_Sub_01_basic) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto sub_y = g.append(); + auto sub = g.append(conv2d, sub_y); + + g.complete(sub); + + init(g.pull); + init(conv2d, filter, bias); + init(sub, locoex::FusedActFunc::NONE); + init(sub_y); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + + auto a_bias = dynamic_cast(a_conv2d->bias()); + ASSERT_TRUE(a_bias != nullptr); + + ASSERT_TRUE(a_bias->dim(0) == 2); + ASSERT_FLOAT_EQ(a_bias->at(0), + bias->at(0) - sub_y->at(0)); + ASSERT_FLOAT_EQ(a_bias->at(1), + bias->at(1) - sub_y->at(1)); +} + +// A case when TFLConv2D is input of TFLSub but fusion cannot be performed. +// - TFLSub.x() == TFLConst +// - TFLSub.y() == TFLConv2D +// +// Here, TFLSub cannot be fused into TFLConst. To be fused, TFLSub.x() should be TFLConv2D and +// TFLSub.y() should be TFLConst. So fusion will NOT happen. +TEST(FuseBiasAddPassTest, Conv2D_Sub_02_fusing_will_not_performed) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto sub_y = g.append(); + auto sub = g.append(sub_y, conv2d); // This WON'T be fused + + g.complete(sub); + + init(g.pull); + init(conv2d, filter, bias); + init(sub, locoex::FusedActFunc::NONE); + init(sub_y); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + + auto a_bias = dynamic_cast(a_conv2d->bias()); + ASSERT_TRUE(a_bias != nullptr); + + ASSERT_TRUE(a_bias->dim(0) == 2); + ASSERT_FLOAT_EQ(a_bias->at(0), 0); + ASSERT_FLOAT_EQ(a_bias->at(1), 0); + + auto a_sub = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_sub != nullptr); + ASSERT_TRUE(a_sub->y() == a_conv2d); // Checking 'not-fused' state +} + +// A case when +// - TFLConv2D has an activation function with Relu +// - TFLAdd, has no activation function +// +// No fusion should happen +TEST(FuseBiasAddPassTest, Regression_Conv2D_Add_fused_action_00) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto add_y = g.append(); + auto add = g.append(conv2d, add_y); + + g.complete(add); + + init(g.pull); + init(conv2d, filter, bias); + init(add, locoex::FusedActFunc::NONE); + init(add_y); + + // Updating Fused Activation for this test + conv2d->fusedActivationFunction(locoex::FusedActFunc::RELU); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + ASSERT_TRUE(a_conv2d->fusedActivationFunction() == locoex::FusedActFunc::RELU); + + auto an_add = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(an_add != nullptr); + ASSERT_TRUE(an_add->fusedActivationFunction() == locoex::FusedActFunc::NONE); + + ASSERT_TRUE(an_add->x() == a_conv2d or an_add->y() == a_conv2d); +} + +// A case when +// - TFLConv2D has NONE activation function +// - TFLAdd has Relu activation function +// +// TFLConv2D should have Relu activation function, TFLAdd is fused into bias input +TEST(FuseBiasAddPassTest, Regression_Conv2D_Add_fused_action_01) +{ + exo::test::TestGraph g; + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto add_y = g.append(); + auto add = g.append(conv2d, add_y); + + g.complete(add); + + init(g.pull); + init(conv2d, filter, bias); + init(add, locoex::FusedActFunc::RELU); + init(add_y); + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + + auto a_bias = dynamic_cast(a_conv2d->bias()); + ASSERT_TRUE(a_bias != nullptr); + + ASSERT_TRUE(a_bias->dim(0) == 2); + ASSERT_FLOAT_EQ(a_bias->at(0), + bias->at(0) + add_y->at(0)); + ASSERT_FLOAT_EQ(a_bias->at(1), + bias->at(1) + add_y->at(1)); + + ASSERT_TRUE(a_conv2d->fusedActivationFunction() == locoex::FusedActFunc::RELU); +} diff --git a/compiler/exo/src/Pass/FuseInstanceNormPass.cpp b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp new file mode 100644 index 000000000..04d4a62cd --- /dev/null +++ b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseInstanceNormPass.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/CircleNodes.h" + +#include + +#include +#include + +// 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 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 bool with_commutative_args_of(const COMM_NODE *node); + +private: + ARG_TYPE_1 **_arg_1; + ARG_TYPE_2 **_arg_2; +}; + +template +inline NodeFiller fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) +{ + return NodeFiller{arg_1, arg_2}; +} + +template +template +bool NodeFiller::with_commutative_args_of(const COMM_NODE *node) +{ + // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2 + { + auto x = dynamic_cast(node->x()); + auto y = dynamic_cast(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(node->x()); + auto y = dynamic_cast(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(locoex::TFLConst *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(locoex::TFLMean *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(); + if (input_shape.rank() != 4) + return false; + + // + // CHECK 2) 'reduction indices' is TFLConst 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(mean->reduction_indices()); + if (not red_indices) + return false; + if (red_indices->rank() != 1) + return false; + std::set 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(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(locoex::TFLAdd *candidate) + { + assert(candidate); + add_as_terminal = candidate; + } + +public: + bool matched(); + bool matched() const { return _matched; } + +public: + // Context + loco::Node *ifm = nullptr; + locoex::TFLMean *mean_of_ifm = nullptr; + locoex::TFLSquaredDifference *sqdiff = nullptr; + locoex::TFLMean *mean_as_variance = nullptr; + locoex::TFLConst *const_as_epsilon = nullptr; + locoex::TFLAdd *add_as_variance = nullptr; + locoex::TFLRsqrt *rsqrt = nullptr; + locoex::TFLConst *const_as_gamma = nullptr; + locoex::TFLMul *mul_gamma = nullptr; + locoex::TFLMul *mul_as_scaled_ifm = nullptr; + locoex::TFLMul *mul_as_scaled_mean = nullptr; + locoex::TFLConst *const_as_beta = nullptr; + locoex::TFLSub *sub = nullptr; + locoex::TFLAdd *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(); + 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(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() == 1); + + CHECK_OR_FALSE(is_instance_mean(mean_as_variance)); + sqdiff = dynamic_cast(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(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(sub->y()); + CHECK_OR_FALSE(mul_as_scaled_mean); + + locoex::TFLMul *mul_gamma_should_be = nullptr; + locoex::TFLMean *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 + * - 'TFLConst --- 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(); + auto reshape_beta = graph->nodes()->create(); + { + auto ifm_shape = loco::shape_get(p.ifm).as(); + uint32_t ifm_channel_depth = ifm_shape.dim(3).value(); + + int32_t new_shape[1] = {static_cast(ifm_channel_depth)}; + + reshape_gamma->tensor(p.const_as_gamma); + reshape_beta->tensor(p.const_as_beta); + + locoex::set_new_shape(reshape_gamma, new_shape, 1); + locoex::set_new_shape(reshape_beta, new_shape, 1); + } + + // Make Instance Norm to replace + auto instance_norm = graph->nodes()->create(); + instance_norm->input(p.ifm); + instance_norm->gamma(reshape_gamma); + instance_norm->beta(reshape_beta); + float epsilon = p.const_as_epsilon->at(0); + instance_norm->epsilon(epsilon); + instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction()); + + replace(p.add_as_terminal).with(instance_norm); +} + +} // namespace + +namespace exo +{ + +bool FuseInstanceNormPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto add = dynamic_cast(node); + if (not add) + continue; + + InstanceNormPattern pattern(add); + if (not pattern.matched()) + continue; + + fuse_instance_norm(pattern); + changed = true; + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FuseInstanceNormPass.h b/compiler/exo/src/Pass/FuseInstanceNormPass.h new file mode 100644 index 000000000..e6361021c --- /dev/null +++ b/compiler/exo/src/Pass/FuseInstanceNormPass.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FUSE_INSTANCE_NORM_PASS_H__ +#define __FUSE_INSTANCE_NORM_PASS_H__ + +#include + +namespace exo +{ + +/** + * @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 "exo::FuseInstanceNormPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __FUSE_INSTANCE_NORM_PASS_H__ diff --git a/compiler/exo/src/Pass/FuseReluPass.cpp b/compiler/exo/src/Pass/FuseReluPass.cpp new file mode 100644 index 000000000..d7af0c506 --- /dev/null +++ b/compiler/exo/src/Pass/FuseReluPass.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseReluPass.h" + +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include + +namespace +{ + +bool is_pred_fusable(loco::Node *node) +{ + using namespace locoex; + + auto fusable_node = dynamic_cast *>(node); + + return (fusable_node and fusable_node->fusedActivationFunction() == FusedActFunc::NONE); +}; + +struct Collector final : public locoex::TFLNodeMutableVisitor +{ + void visit(locoex::TFLRelu *node) final + { + if (is_pred_fusable(node->features())) + candidates.insert(node); + } + + void visit(locoex::TFLRelu6 *node) final + { + if (is_pred_fusable(node->features())) + candidates.insert(node); + } + + void visit(locoex::TFLNode *) final { return; } + + std::set candidates; +}; + +void set_activation_fusion(loco::Node *node, locoex::FusedActFunc f) +{ + using namespace locoex; + + if (auto fusable_node = dynamic_cast *>(node)) + fusable_node->fusedActivationFunction(f); + else + assert(false); +} + +struct Performer final : public locoex::TFLNodeMutableVisitor +{ + void visit(locoex::TFLRelu *the_relu) final + { + set_activation_fusion(the_relu->features(), locoex::FusedActFunc::RELU); + + loco::replace(the_relu).with(the_relu->features()); + the_relu->features(nullptr); + } + + void visit(locoex::TFLRelu6 *the_relu6) final + { + set_activation_fusion(the_relu6->features(), locoex::FusedActFunc::RELU6); + + loco::replace(the_relu6).with(the_relu6->features()); + the_relu6->features(nullptr); + } + + void visit(locoex::TFLNode *) final { assert(false && "should not be called"); } +}; + +} // namespace + +namespace exo +{ + +bool FuseReluPass::run(loco::Graph *g) +{ + Collector collector; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (node->dialect() == locoex::TFLDialect::get()) + { + auto tfl_node = dynamic_cast(node); + tfl_node->accept(&collector); + } + } + + Performer performer; + + for (auto node : collector.candidates) + { + node->accept(&performer); + } + + return collector.candidates.size() > 0; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FuseReluPass.h b/compiler/exo/src/Pass/FuseReluPass.h new file mode 100644 index 000000000..1cd276b29 --- /dev/null +++ b/compiler/exo/src/Pass/FuseReluPass.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_FUSE_RELU_PASS_H__ +#define __PASS_FUSE_RELU_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse TFLRelu or TFLRelu6 into the TensorFlow Lite ops below: + * + * ADD, AVERAGE_POOL_2D, CONCATENATION, CONV_2D, DEPTHWISE_CONV_2D, + * FULLY_CONNECTED, L2_NORMALIZATION, L2_POOL_2D, MAX_POOL_2D, MUL + */ +struct FuseReluPass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FuseReluPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __PASS_FUSE_RELU_PASS_H__ diff --git a/compiler/exo/src/Pass/FuseReluPass.test.cpp b/compiler/exo/src/Pass/FuseReluPass.test.cpp new file mode 100644 index 000000000..6f83d4dd0 --- /dev/null +++ b/compiler/exo/src/Pass/FuseReluPass.test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseReluPass.h" + +#include "Dialect/IR/TFLNodes.h" +#include "TestGraph.h" + +#include +#include + +#include + +#include // for std::is_same + +namespace +{ + +void init(loco::Pull *pull) +{ + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2, 3, 3, 2}); +} + +/// @brief Initializes TFLConv2D and related filter and bias +void init(locoex::TFLConv2D *conv2d, locoex::TFLConst *filter, locoex::TFLConst *bias) +{ + // set conv2d + { + conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE); + conv2d->padding(locoex::Padding::VALID); + } + + // set filter + { + filter->dtype(loco::DataType::FLOAT32); + filter->shape({2, 3, 3, 2}); + filter->size(2 * 3 * 3 * 2); + + for (uint32_t x = 0; x < 2 * 3 * 3 * 2; x++) + filter->at(x) = 0.0; + } + + // set bias + { + bias->dtype(loco::DataType::FLOAT32); + bias->shape({2}); + bias->size(2); + + for (uint32_t x = 0; x < 2; x++) + bias->at(x) = 0.0; + } +} + +} // namespace + +/// Test code called by TEST(..) +/// This tests whether Conv2D - FusedTFLType is fused. +template void test() +{ + static_assert((std::is_same::value && + FusedActFunc == locoex::FusedActFunc::RELU) || + (std::is_same::value && + FusedActFunc == locoex::FusedActFunc::RELU6), + "wrong template type"); + + exo::test::TestGraph g; + { + auto filter = g.append(); + auto bias = g.append(); + auto conv2d = g.append(g.pull, filter, bias); + + auto fusable_node = g.append(conv2d); + + g.complete(fusable_node); + + init(g.pull); + init(conv2d, filter, bias); + } + + // let's run fusion + { + exo::test::TypeShapeReadyPhase test_phase; + + test_phase.add_pass(); + test_phase.add_pass(); // to remove TFLRelu + test_phase.run(g.graph()); + } + + auto a_conv2d = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(a_conv2d != nullptr); + ASSERT_TRUE(a_conv2d->fusedActivationFunction() == FusedActFunc); + + auto removed_fusable_node = exo::test::find_first_node_bytype(g.graph()); + ASSERT_TRUE(removed_fusable_node == nullptr); +} + +// A case with Conv2D-Relu +TEST(FuseReluTest, Conv2D_Relu_basic) { test(); } + +// A case with Conv2D-Relu6 +TEST(FuseReluTest, Conv2D_Relu6_basic) { test(); } diff --git a/compiler/exo/src/Pass/FuseRsqrtPass.cpp b/compiler/exo/src/Pass/FuseRsqrtPass.cpp new file mode 100644 index 000000000..08d704139 --- /dev/null +++ b/compiler/exo/src/Pass/FuseRsqrtPass.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseRsqrtPass.h" + +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +namespace +{ + +/** + * @return Casted TFLDiv for fusable candidate, nullptr otherwise + * + * This helper checkes fusability with following conditions: + * - TFLDiv has no activation + * - TFLDiv's first argument is TFLConst with all value 1 + * - TFLDiv's second argument is TFLSqrt + */ +locoex::TFLDiv *as_candidate(loco::Node *node) +{ + auto div = dynamic_cast(node); + if (not div) + return nullptr; + + // Cannot fuse Div with activation function + if (div->fusedActivationFunction() != locoex::FusedActFunc::NONE) + return nullptr; + + auto const_one = dynamic_cast(div->x()); + if (not const_one) + return nullptr; + + const loco::DataType FLOAT32 = loco::DataType::FLOAT32; + // TODO Support other dtype + EXO_ASSERT(const_one->dtype() == FLOAT32, "Only support FLOAT32 now"); + for (uint32_t i = 0; i < const_one->size(); ++i) + if (const_one->at(i) != 1.0f) + return nullptr; + + auto sqrt = dynamic_cast(div->y()); + if (not sqrt) + return nullptr; + + return div; +} + +void fuse_rsqrt(locoex::TFLDiv *div) +{ + auto sqrt = dynamic_cast(div->y()); + EXO_ASSERT(sqrt, "sqrt should be valid at this point"); + + // TFLRsqrt to replace + auto rsqrt = div->graph()->nodes()->create(); + rsqrt->x(sqrt->x()); + + // replace + loco::replace(div).with(rsqrt); +} + +} // namespace + +namespace exo +{ + +bool FuseRsqrtPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto div = as_candidate(node)) + { + fuse_rsqrt(div); + changed = true; + } + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FuseRsqrtPass.h b/compiler/exo/src/Pass/FuseRsqrtPass.h new file mode 100644 index 000000000..1e60e4a49 --- /dev/null +++ b/compiler/exo/src/Pass/FuseRsqrtPass.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FUSE_RSQRT_PASS_H__ +#define __FUSE_RSQRT_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse TFLSqrt that is divided(TFLDiv) by 1, into TFLRsqrt + * + * + * + * TFLConst(1) ------ + * \ + * A --- TFLSqrt --- TFLDiv --- B + * + * + * + * A --- TFLRsqrt --- B + */ +struct FuseRsqrtPass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FuseRsqrtPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __FUSE_RSQRT_PASS_H__ diff --git a/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp b/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp new file mode 100644 index 000000000..3f985a505 --- /dev/null +++ b/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FuseSquaredDifferencePass.h" + +#include "Check.h" + +#include "Dialect/IR/TFLNodes.h" + +namespace +{ + +/** + * @return Casted TFLMul for fusable candidate, nullptr otherwise + * + * This helper checkes fusability with following conditions: + * - TFLMul has no activation + * - TFLMul's first and second arguments are equal and TFLSub + */ +locoex::TFLMul *as_candidate(loco::Node *node) +{ + auto mul = dynamic_cast(node); + if (not mul) + return nullptr; + + // Cannot fuse mul with activation function + if (mul->fusedActivationFunction() != locoex::FusedActFunc::NONE) + return nullptr; + + if (mul->x() != mul->y()) + return nullptr; + + if (not dynamic_cast(mul->x())) + return nullptr; + + return mul; +} + +void fuse_squared_difference(locoex::TFLMul *mul) +{ + auto sub = dynamic_cast(mul->x()); + EXO_ASSERT(sub, "sub should be valid at this point"); + + // TFLSquaredDifference to replace + auto sq_diff = mul->graph()->nodes()->create(); + sq_diff->x(sub->x()); + sq_diff->y(sub->y()); + + // replace + loco::replace(mul).with(sq_diff); +} + +} // namespace + +namespace exo +{ + +bool FuseSquaredDifferencePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto mul = as_candidate(node)) + { + fuse_squared_difference(mul); + changed = true; + } + } + + return changed; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/FuseSquaredDifferencePass.h b/compiler/exo/src/Pass/FuseSquaredDifferencePass.h new file mode 100644 index 000000000..dbc15149f --- /dev/null +++ b/compiler/exo/src/Pass/FuseSquaredDifferencePass.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FUSE_SQUARED_DIFFERENCE_PASS_H__ +#define __FUSE_SQUARED_DIFFERENCE_PASS_H__ + +#include + +namespace exo +{ + +/** + * @brief Class to fuse SquaredDifference pattern + * + * + * + * A --- TFLSub --- TFLMul --- C + * / \ / + * B ---- ----- + * + * + * + * A --- TFLSquaredDifference --- C + * / + * B ---- + */ +struct FuseSquaredDifferencePass final : public logo::Pass +{ + const char *name(void) const final { return "exo::FuseSquaredDifferencePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace exo + +#endif // __FUSE_SQUARED_DIFFERENCE_PASS_H__ diff --git a/compiler/exo/src/Pass/MergeConcatNodesPass.cpp b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp new file mode 100644 index 000000000..8945fcfce --- /dev/null +++ b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MergeConcatNodesPass.h" +#include "Dialect/IR/TFLNodes.h" + +#include + +#include + +namespace +{ + +bool canMerge(locoex::TFLConcatenation *node1, locoex::TFLConcatenation *node2) +{ + if (node1->fusedActivationFunction() != node2->fusedActivationFunction()) + return false; + + if (node1->axis() != node2->axis()) + return false; + + switch (node1->fusedActivationFunction()) + { + case locoex::FusedActFunc::NONE: + case locoex::FusedActFunc::RELU: + case locoex::FusedActFunc::RELU6: + return true; + + // case locoex::FusedActFunc::TANH: + // return false; + + default: + INTERNAL_EXN_V("Unknown FusedActFunc", oops::to_uint32(node1->fusedActivationFunction())); + } +} + +/** + * @brief Collect all the inputs of newly created TFLConcatenation nodes + * + * in:0 -------------------------------\ + * in:1 ---- TFLConcatenation:0 -------- TFLConcatenation:3 --- C + * (axis = 0, NONE) (axis = 0, NONE) + * in:2 ---/ / + * in:3 ---- TFLConcatenation:1 ------/ + * (axis = 1, NONE) / + * in:4 ---/ / + * in:5 ---- TFLConcatenation:2 ---/ + * (axis = 0, RELU) + * in:6 ---/ + * + * For exmaple, if graph is like above, dfs(TFLConcatenation:3) will + * return [in:0, in:1, in:2, TFLConcatenation:1, TFLConcatenation:2] + * + * TFLConcatenation:0 can be merged to TFLConcatenation:3, + * because axis and fusedActivationFunction are same. + * It means that [in:1, in:2] will be linked as inputs of new TFLConcatenation. + * + * However, TFLConcatenation:1 and TFLConcatenation:2 cannot be merged to + * TFLConcatenation:3 because axis and fusedActivationFunction of each are different. + * So [in:3, in:4, in:5, in:6] will not be linked as inputs of new TFLConcatenation + * and [TFLConcatenation:1, TFLConcatenation:2] will be linked instead. + * + * Therefore, inputs of newly created TFLConcatenation node for merging + * TFLConcatenation:3 will be [in:0, in:1, in:2, TFLConcatenation:1, TFLConcatenation:2] + * and dfs(TFLConcatenation:3) will return it. + * + * + * @note The input nodes should be traversed by LRV, + * which is from left to right (input:0 --> input:N) + */ +std::vector dfs(locoex::TFLConcatenation *root) +{ + std::vector res; + + for (uint32_t i = 0; i < root->numValues(); ++i) + { + auto input = dynamic_cast(root->values(i)); + if (input != nullptr && canMerge(input, root)) + { + auto children = dfs(input); + for (auto child : children) + res.push_back(child); + } + else + { + res.push_back(root->values(i)); + } + } + + return res; +} + +} // namespace + +namespace exo +{ + +/** + * @brief Merge TFLConcatenate nodes whose axis and fusedActivationFunction are same + * + * [Before] + * in:0 -------------------------------\ + * in:1 ---- TFLConcatenation:0 -------- TFLConcatenation:3 --- C + * (axis = 0, NONE) (axis = 0, NONE) + * in:2 ---/ / + * in:3 ---- TFLConcatenation:1 ------/ + * (axis = 1, NONE) / + * in:4 ---/ / + * in:5 ---- TFLConcatenation:2 ---/ + * (axis = 0, RELU) + * in:6 ---/ + * + * [After] + * in:0 -------------------------------\ + * in:1 -------------------------------- TFLConcatenation:4 --- C + * (axis = 0, NONE) + * in:2 -------------------------------/ + * in:3 ---- TFLConcatenation:1 ------/ + * (axis = 1, NONE) / + * in:4 ---/ / + * in:5 ---- TFLConcatenation:2 ---/ + * (axis = 0, RELU) + * in:6 ---/ + * + * + * in:1 ---- TFLConcatenation:0 ---- + * (axis = 0, NONE) + * in:2 ---/ + * + * + * ---- TFLConcatenation:3 ---- + * (axis = 0, NONE) + */ +bool MergeConcatNodesPass::run(loco::Graph *graph) +{ + // Let's enumerate nodes required to compute output nodes + auto active_nodes = loco::active_nodes(loco::output_nodes(graph)); + + // Find TFLConcatenation nodes which have another TFLConcatenation nodes + // as inputs, with same axis and same fusedActivationFunction + std::vector candidates; + for (auto node : active_nodes) + { + if (auto concat = dynamic_cast(node)) + { + for (uint32_t i = 0; i < concat->numValues(); ++i) + { + auto input = dynamic_cast(concat->values(i)); + if (input != nullptr && canMerge(input, concat)) + { + candidates.push_back(concat); + break; + } + } + } + } + + // Merge multiple TFLConcatenation nodes as one TFLConcatenation node + for (auto node : candidates) + { + auto inputs = dfs(node); + + auto new_concat = graph->nodes()->create(inputs.size()); + new_concat->axis(node->axis()); + new_concat->fusedActivationFunction(node->fusedActivationFunction()); + + for (uint32_t i = 0; i < inputs.size(); ++i) + new_concat->values(i, inputs.at(i)); + + loco::replace(node).with(new_concat); + for (uint32_t i = 0; i < node->numValues(); ++i) + node->values(i, nullptr); + } + + return candidates.size() > 0; +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/MergeConcatNodesPass.h b/compiler/exo/src/Pass/MergeConcatNodesPass.h new file mode 100644 index 000000000..823214f43 --- /dev/null +++ b/compiler/exo/src/Pass/MergeConcatNodesPass.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_MERGE_CONCAT_NODES_H__ +#define __PASS_MERGE_CONCAT_NODES_H__ + +#include +#include + +namespace exo +{ + +/** + * @brief Merge concat nodes whose axis and fusedActivationFunction are same + * + */ +class MergeConcatNodesPass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "exo::MergeConcatNodesPass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace exo + +#endif // __PASS_MERGE_CONCAT_NODES_H__ diff --git a/compiler/exo/src/Pass/ShapeInferencePass.cpp b/compiler/exo/src/Pass/ShapeInferencePass.cpp new file mode 100644 index 000000000..bc60f91c4 --- /dev/null +++ b/compiler/exo/src/Pass/ShapeInferencePass.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShapeInferencePass.h" + +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/Service/TFLShapeInferenceRule.h" + +#include "Dialect/IR/CircleDialect.h" +#include "Dialect/Service/CircleShapeInferenceRule.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace exo +{ + +/** + * @note Currently, TFL and Circle backend share this inference. However, TFL + * backend does not require rule for Circle dialect. + * TODO Make dedicated inference pass for Circle Dialect. + */ +bool ShapeInferencePass::run(loco::Graph *g) +{ + loco::CanonicalShapeInferenceRule canonical_rule; + locoex::TFLShapeInferenceRule tfl_rule; + locoex::CircleShapeInferenceRule circle_rule; + locoex::COpShapeInferenceRule cop_rule; + + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(locoex::TFLDialect::get(), &tfl_rule) + .bind(locoex::CircleDialect::get(), &circle_rule) + .bind(locoex::COpDialect::get(), &cop_rule); + + return loco::apply(&rules).to(g); +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/ShapeInferencePass.h b/compiler/exo/src/Pass/ShapeInferencePass.h new file mode 100644 index 000000000..518c87403 --- /dev/null +++ b/compiler/exo/src/Pass/ShapeInferencePass.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_SHAPE_INFERENCE_PASS_H__ +#define __PASS_SHAPE_INFERENCE_PASS_H__ + +#include +#include + +namespace exo +{ + +/** + * @brief Pass to infer shape of nodes + */ +class ShapeInferencePass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "exo::ShapeInferencePass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace exo + +#endif //__PASS_SHAPE_INFERENCE_PASS_H__ diff --git a/compiler/exo/src/Pass/TypeInferencePass.cpp b/compiler/exo/src/Pass/TypeInferencePass.cpp new file mode 100644 index 000000000..31d4f13b6 --- /dev/null +++ b/compiler/exo/src/Pass/TypeInferencePass.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TypeInferencePass.h" + +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/Service/TFLTypeInferenceRule.h" + +#include "Dialect/IR/CircleDialect.h" +#include "Dialect/Service/CircleTypeInferenceRule.h" + +#include +#include +#include + +#include +#include + +namespace exo +{ + +/** + * @note Currently, TFL and Circle backend share this inference. However, TFL + * backend does not require rule for Circle dialect. + * TODO Make dedicated inference pass for Circle Dialect. + */ +bool TypeInferencePass::run(loco::Graph *g) +{ + loco::CanonicalTypeInferenceRule canonical_rule; + locoex::TFLTypeInferenceRule tfl_rule; + locoex::CircleTypeInferenceRule circle_rule; + locoex::COpTypeInferenceRule cop_rule; + + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(locoex::TFLDialect::get(), &tfl_rule) + .bind(locoex::CircleDialect::get(), &circle_rule) + .bind(locoex::COpDialect::get(), &cop_rule); + + return loco::apply(&rules).to(g); +} + +} // namespace exo diff --git a/compiler/exo/src/Pass/TypeInferencePass.h b/compiler/exo/src/Pass/TypeInferencePass.h new file mode 100644 index 000000000..3ede587a0 --- /dev/null +++ b/compiler/exo/src/Pass/TypeInferencePass.h @@ -0,0 +1,42 @@ + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASS_TYPE_INFERENCE_PASS_H__ +#define __PASS_TYPE_INFERENCE_PASS_H__ + +#include + +#include + +namespace exo +{ + +/** + * @brief Pass to infer type of nodes + */ +class TypeInferencePass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "exo::TypeInferencePass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace exo + +#endif //__PASS_TYPE_INFERENCE_PASS_H__ diff --git a/compiler/exo/src/Passes.cpp b/compiler/exo/src/Passes.cpp new file mode 100644 index 000000000..99d229c9c --- /dev/null +++ b/compiler/exo/src/Passes.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Passes.h" + +// This file is to make sure that Passes.h be compiled diff --git a/compiler/exo/src/Passes.h b/compiler/exo/src/Passes.h new file mode 100644 index 000000000..2a702d01d --- /dev/null +++ b/compiler/exo/src/Passes.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PASSES_H__ +#define __PASSES_H__ + +// Please add in alphabetical order +// Please append 'Pass' suffix to Pass class and file names + +#include "Pass/FoldReshapeOfConstPass.h" +#include "Pass/FoldTransposeOfConstPass.h" +#include "Pass/FuseBiasAddPass.h" +#include "Pass/FuseInstanceNormPass.h" +#include "Pass/FuseReluPass.h" +#include "Pass/FuseRsqrtPass.h" +#include "Pass/FuseSquaredDifferencePass.h" +#include "Pass/MergeConcatNodesPass.h" +#include "Pass/ShapeInferencePass.h" +#include "Pass/TypeInferencePass.h" + +#include +#include +#include + +#endif // __PASSES_H__ diff --git a/compiler/exo/src/ProgressReporter.cpp b/compiler/exo/src/ProgressReporter.cpp new file mode 100644 index 000000000..ff919dae8 --- /dev/null +++ b/compiler/exo/src/ProgressReporter.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ProgressReporter.h" + +#include "Log.h" +#include "LogHelper.h" + +#include +#include + +#include + +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 exo +{ + +void ProgressReporter::notify(const logo::PhaseEventInfo *) +{ + LOGGER(prime); + + INFO(prime) << "=============================================================="; + INFO(prime) << "exo::PhaseRunner<" << to_str(strategy()) << ">"; + INFO(prime) << "Initial graph"; + INFO(prime) << fmt(graph()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *) +{ + LOGGER(prime); + + INFO(prime) << "exo::PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *info) +{ + LOGGER(prime); + + INFO(prime) << "After " << logo::pass_name(info->pass()) + << " (changed: " << to_char(info->changed()) << ")"; + INFO(prime) << fmt(graph()); +} + +} // namespace exo diff --git a/compiler/exo/src/ProgressReporter.h b/compiler/exo/src/ProgressReporter.h new file mode 100644 index 000000000..b0f420df9 --- /dev/null +++ b/compiler/exo/src/ProgressReporter.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PROGRESSREPORTER_H__ +#define __PROGRESSREPORTER_H__ + +#include + +#include + +namespace exo +{ + +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 *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) override; + +public: + loco::Graph *graph(void) const { return _graph; } + logo::PhaseStrategy strategy(void) const { return _strategy; } + +private: + loco::Graph *_graph; + logo::PhaseStrategy _strategy; +}; + +} // namespace exo + +#endif // __PROGRESSREPORTER_H__ diff --git a/compiler/exo/src/ShapeInference.cpp b/compiler/exo/src/ShapeInference.cpp new file mode 100644 index 000000000..bceb1495f --- /dev/null +++ b/compiler/exo/src/ShapeInference.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShapeInference.h" +#include "Dialect/IR/TFLDialect.h" +#include "Dialect/Service/TFLShapeInferenceRule.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace exo +{ + +ShapeDescription ShapeInference::get(loco::Node *node) +{ + // TODO Adjust indentation level + { + assert(loco::shape_known(node)); + return to_shape_description(loco::shape_get(node)); + } +} + +} // namespace exo diff --git a/compiler/exo/src/ShapeInference.h b/compiler/exo/src/ShapeInference.h new file mode 100644 index 000000000..ec141ccfc --- /dev/null +++ b/compiler/exo/src/ShapeInference.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SHAPE_INFERENCE_H__ +#define __SHAPE_INFERENCE_H__ + +#include "ExporterUtils.h" + +#include + +namespace exo +{ + +/** + * @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 exo + +#endif // __SHAPE_INFERENCE_H__ diff --git a/compiler/exo/src/TFLite/TFLExporter.cpp b/compiler/exo/src/TFLite/TFLExporter.cpp new file mode 100644 index 000000000..cf002b3e1 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporter.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "exo/TFLExporter.h" + +#include "TFLExporterImpl.h" + +#include + +#include + +#include + +namespace exo +{ + +TFLExporter::TFLExporter(loco::Graph *graph) : _impl(stdex::make_unique(graph)) +{ + // NOTHING TO DO +} + +TFLExporter::~TFLExporter() = default; + +void TFLExporter::dumpToFile(const char *path) const +{ + const char *ptr = _impl->getBufferPointer(); + const size_t size = _impl->getBufferSize(); + + if (!ptr) + INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason"); + + std::ofstream file(path, std::ofstream::binary); + file.write(ptr, size); +} + +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.cpp new file mode 100644 index 000000000..07adbfb9d --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterImpl.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLExporterImpl.h" + +#include "Convert.h" +#include "ExoOptimize.h" + +#include "TFLTensorExporter.h" +#include "TFLOperationExporter.h" +#include "TFLExporterUtils.h" + +#include "Log.h" +#include "Knob.h" + +#include + +#include +#include +#include +#include + +namespace +{ + +using namespace exo; +using namespace exo::tflite_detail; + +void registerGraphInputTensors(loco::Graph *graph, SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->inputs()->size(); ++n) + { + auto node = loco::pull_node(graph, n); + assert(node != nullptr); + ctx._inputs.push_back(get_tensor_index(node)); + } +} + +void registerGraphOutputTensors(loco::Graph *graph, SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto push = loco::push_node(graph, n); + assert(push != nullptr); + auto node = push->from(); + assert(node != nullptr); + ctx._outputs.push_back(get_tensor_index(node)); + } +} + +} // namespace + +namespace +{ +using namespace tflite; +using namespace flatbuffers; + +Offset>> +encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map &opcodes, + std::unordered_map &custom_opcodes) +{ + std::vector> 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 custom op"); + + operator_codes_vec[idx] = + CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); + } + } + return builder.CreateVector(operator_codes_vec); +} + +} // namespace + +namespace exo +{ + +using namespace exo::tflite_detail; +using namespace tflite; +using namespace flatbuffers; + +TFLExporter::Impl::Impl(loco::Graph *graph) { exportGraph(graph); } + +::flatbuffers::Offset<::tflite::SubGraph> TFLExporter::Impl::exportSubgraph(SerializedModelData &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 subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators); + return subgraph; +} + +void TFLExporter::Impl::exportGraph(loco::Graph *graph) +{ + LOGGER(l); + + // IR-level conversion and optimization + { + convert_to_TFLNodes(graph); + set(Dialect::TFLITE); + optimize(graph); + } + + _builder.Clear(); + + SerializedModelData gd; + + // This version is taken from comment in fbs + constexpr uint32_t version = 3; + + registerGraphIOName(graph, gd); + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, 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, gd); + + // encode operator codes + auto operator_codes = + encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); + + // Subgraphs + Offset subgraph = exportSubgraph(gd); + auto subgraphs = _builder.CreateVector(std::vector>{subgraph}); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(gd._buffers); + + // empty metadata + std::vector 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); +} + +const char *TFLExporter::Impl::getBufferPointer() const +{ + return reinterpret_cast(_builder.GetBufferPointer()); +} + +size_t TFLExporter::Impl::getBufferSize() const { return _builder.GetSize(); } + +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.h b/compiler/exo/src/TFLite/TFLExporterImpl.h new file mode 100644 index 000000000..01c549a43 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterImpl.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFL_EXPORTER_IMPL_H__ +#define __TFL_EXPORTER_IMPL_H__ + +#include "exo/TFLExporter.h" +#include "schema_generated.h" + +#include + +namespace exo +{ + +namespace tflite_detail +{ + +struct SerializedModelData; + +} // namespace tflite_detail + +using namespace tflite_detail; + +/** + * internal implementation of interface exporter class + */ +class TFLExporter::Impl +{ +public: + Impl() = delete; + ~Impl() = default; + + explicit Impl(loco::Graph *graph); + + /** + * @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 SerializedModelData + * @param gd information about serializer parts of model + * @return offset in buffer corresponding to serialized subgraph + */ + flatbuffers::Offset exportSubgraph(SerializedModelData &gd); + + /** + * @brief root function that writes graph into internal buffer + * @param graph + */ + void exportGraph(loco::Graph *graph); + +private: + flatbuffers::FlatBufferBuilder _builder; +}; + +} // namespace exo + +#endif // __TFL_EXPORTER_IMPL_H__ diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp new file mode 100644 index 000000000..7d74223c5 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLExporterImpl.h" + +#include "schema_generated.h" + +#include "TestGraph.h" +#include "GraphBlock.h" +#include "Knob.h" + +#include +#include + +#include + +namespace +{ + +class TFLExporterImplTests : public ::testing::Test +{ +public: + TFLExporterImplTests() { _graph = loco::make_graph(); } + +public: + virtual ~TFLExporterImplTests() = default; + +protected: + loco::Graph *graph(void) { return _graph.get(); } + + template NodeT *make_node(void); + +private: + std::unique_ptr _graph; +}; + +template NodeT *TFLExporterImplTests::make_node(void) +{ + return graph()->nodes()->create(); +} + +template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void) +{ + loco::FeatureEncode *encode_layer = graph()->nodes()->create(); + + auto encoder = stdex::make_unique>(); + (*encoder->perm())[loco::FeatureAxis::Count] = 0; + (*encoder->perm())[loco::FeatureAxis::Depth] = 1; + (*encoder->perm())[loco::FeatureAxis::Height] = 2; + (*encoder->perm())[loco::FeatureAxis::Width] = 3; + encode_layer->encoder(std::move(encoder)); + + return encode_layer; +} + +template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void) +{ + loco::FeatureDecode *decode_layer = graph()->nodes()->create(); + + auto decoder = stdex::make_unique>(); + (*decoder->perm())[loco::FeatureAxis::Count] = 0; + (*decoder->perm())[loco::FeatureAxis::Depth] = 1; + (*decoder->perm())[loco::FeatureAxis::Height] = 2; + (*decoder->perm())[loco::FeatureAxis::Width] = 3; + decode_layer->decoder(std::move(decoder)); + + return decode_layer; +} + +} // namespace + +// TODO TFLAdd + +// TODO TFLAveragePool2D + +TEST_F(TFLExporterImplTests, Concatenate) +{ + auto pull1 = make_node(); + { + pull1->dtype(loco::DataType::FLOAT32); + pull1->shape({1, 2, 3, 4}); + } + auto pull2 = make_node(); + { + pull2->dtype(loco::DataType::FLOAT32); + pull2->shape({1, 2, 3, 4}); + } + auto concat = make_node(); + { + concat->lhs(pull1); + concat->rhs(pull2); + } + auto push = make_node(); + { + push->from(concat); + } + + auto input1 = graph()->inputs()->create(); + { + input1->name("input1"); + loco::link(input1, pull1); + } + auto input2 = graph()->inputs()->create(); + { + input2->name("input2"); + loco::link(input2, pull2); + } + auto output = graph()->outputs()->create(); + { + output->name("output"); + loco::link(output, push); + } + + exo::TFLExporter::Impl exporter{graph()}; + + // TODO Add more checks + SUCCEED(); +} + +// TODO TFLConv2D + +// TODO TFLDepthwiseConv2D + +// TODO TFLDiv + +// TODO TFLMaxPool2D + +// TODO TFLMul + +TEST_F(TFLExporterImplTests, Relu6) +{ + auto pull = make_node(); + { + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1, 8, 8, 3}); + } + auto relu6 = make_node(); + { + relu6->input(pull); + } + auto push = make_node(); + { + push->from(relu6); + } + + auto input = graph()->inputs()->create(); + { + input->name("input"); + loco::link(input, pull); + } + auto output = graph()->outputs()->create(); + { + output->name("output"); + loco::link(output, push); + } + + exo::TFLExporter::Impl exporter{graph()}; + + // TODO Add more checks + SUCCEED(); +} + +// TODO TFLRelu6 + +// TODO TFLReshape + +// TODO TFLSoftmax + +// TODO TFLSqrt + +// TODO TFLSub + +// TODO TFLTanh + +TEST(TFLExporterImplTest, Transpose_simple) +{ + exo::test::ExampleGraph g; + + // pull attribute + { + g.pull->dtype(loco::DataType::FLOAT32); + g.pull->shape({1, 2, 2, 3}); + } + + // transpose attribute + { + g.transpose->perm()->size(4); + g.transpose->perm()->axis(0) = 1; + g.transpose->perm()->axis(1) = 2; + g.transpose->perm()->axis(2) = 3; + g.transpose->perm()->axis(3) = 0; + } + + exo::TFLExporter::Impl exporter{g.graph()}; + { + auto model = tflite::GetModel(exporter.getBufferPointer()); + auto operators = model->subgraphs()->Get(0)->operators(); + + assert(operators->Length() == 1); + + int n = 0; // op index of Transpose in tflite file + + auto opcode_index = operators->Get(n)->opcode_index(); + + ASSERT_EQ(model->operator_codes()->Get(opcode_index)->builtin_code(), + tflite::BuiltinOperator_TRANSPOSE); + + auto perm = operators->Get(n)->inputs()->Get(1); + + auto perm_tensor = model->subgraphs()->Get(0)->tensors()->Get(perm); + ASSERT_EQ(perm_tensor->type(), tflite::TensorType::TensorType_INT32); + ASSERT_EQ(perm_tensor->shape()->size(), 1); + ASSERT_EQ(perm_tensor->shape()->Get(0), 4); + + auto bufs = (model->buffers()); + auto *perm_buf = + reinterpret_cast(bufs->Get(perm_tensor->buffer())->data()->data()); + + ASSERT_EQ(perm_buf[0], 1); + ASSERT_EQ(perm_buf[1], 2); + ASSERT_EQ(perm_buf[2], 3); + ASSERT_EQ(perm_buf[3], 0); + } +} + +/* + test case: + Pull ----- FeatureEncode ---- FeatureDecode --- Push + 0 -----------> H ---------+ O 0 + 1 W +----> H -----------> 1 + 2 I(depth) W 2 + 3 O(coutn) I 3 + + axis 0 ----------> H --------------> H -----------> 1 + axis 1 ----------> W --------------> W -----------> 2 + axis 2 ----------> I --------------> I -----------> 3 + axis 3 ----------> O --------------> O -----------> 0 + + So, perm vector of Tranpose = [3, 0, 1, 2]. + Please refer to loco::TensorTranspose about the definition of perm vector. +*/ +TEST(TFLExporterImplTest, Transpose_from_FilterEncode_FilterDecode) +{ + exo::test::ExampleGraph g; + + // pull attribute + { + g.pull->dtype(loco::DataType::FLOAT32); + g.pull->shape({1, 2, 3, 4}); // whatever value of rank 4 + } + + exo::TFLExporter::Impl exporter{g.graph()}; + { + auto model = tflite::GetModel(exporter.getBufferPointer()); + auto operators = model->subgraphs()->Get(0)->operators(); + + assert(operators->Length() == 1); + + int n = 0; // op index of Transpose in tflite file + + auto opcode_index = operators->Get(n)->opcode_index(); + + ASSERT_EQ(model->operator_codes()->Get(opcode_index)->builtin_code(), + tflite::BuiltinOperator_TRANSPOSE); + + auto perm = operators->Get(n)->inputs()->Get(1); + + auto perm_tensor = model->subgraphs()->Get(0)->tensors()->Get(perm); + ASSERT_EQ(perm_tensor->type(), tflite::TensorType::TensorType_INT32); + ASSERT_EQ(perm_tensor->shape()->size(), 1); + ASSERT_EQ(perm_tensor->shape()->Get(0), 4); + + auto bufs = (model->buffers()); + auto *perm_buf = + reinterpret_cast(bufs->Get(perm_tensor->buffer())->data()->data()); + ASSERT_EQ(perm_buf[0], 3); + ASSERT_EQ(perm_buf[1], 0); + ASSERT_EQ(perm_buf[2], 1); + ASSERT_EQ(perm_buf[3], 2); + } +} + +/** + * What happens when there is a mismatch between generation and execution order!? + */ +TEST_F(TFLExporterImplTests, Regression_0000) +{ + // This test was written without considering fusion. + // For this reason, this check is needed. + // TODO Rewrite this test + if (exo::get()) + return; + + // Execution Order: MaxPool2D -> ReLU + // Generation Order: ReLU -> MaxPool2D + auto pull = make_node(); + { + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1, 8, 8, 3}); + } + auto relu = make_node(); + auto encode = exo::make_feature_encode(pull); + auto maxpool = make_node(); + auto decode = exo::make_feature_decode(relu); + auto push = make_node(); + + ASSERT_EQ(maxpool->window()->vertical(), 1); + ASSERT_EQ(maxpool->window()->horizontal(), 1); + + maxpool->ifm(encode); + relu->input(maxpool); + push->from(decode); + + auto input = graph()->inputs()->create(); + { + input->name("input"); + loco::link(input, pull); + } + auto output = graph()->outputs()->create(); + { + output->name("output"); + loco::link(output, push); + } + + exo::TFLExporter::Impl exporter{graph()}; + { + int64_t maxpool_execution_index = -1; + int64_t relu_exeuction_index = -1; + + auto model = tflite::GetModel(exporter.getBufferPointer()); + auto operators = model->subgraphs()->Get(0)->operators(); + + for (uint32_t n = 0; n < operators->Length(); ++n) + { + auto opcode_index = operators->Get(n)->opcode_index(); + + switch (model->operator_codes()->Get(opcode_index)->builtin_code()) + { + case tflite::BuiltinOperator_RELU: + ASSERT_EQ(relu_exeuction_index, -1); + relu_exeuction_index = static_cast(n); + break; + case tflite::BuiltinOperator_MAX_POOL_2D: + ASSERT_EQ(maxpool_execution_index, -1); + maxpool_execution_index = static_cast(n); + break; + default: + break; + } + } + + ASSERT_NE(maxpool_execution_index, -1); + ASSERT_NE(relu_exeuction_index, -1); + // maxpool SHOULD precede ReLU + ASSERT_LT(maxpool_execution_index, relu_exeuction_index); + } +} + +/** + * @brief Test exporter buffer generation + */ +TEST_F(TFLExporterImplTests, Regression_0001) +{ + auto cgen = make_node(); + cgen->rank(1); + cgen->dim(0) = 2; + cgen->dtype(loco::DataType::FLOAT32); + cgen->size(2); + cgen->at(0) = 3.3f; + cgen->at(1) = 1.1f; + + auto push = make_node(); + push->from(cgen); + + auto output = graph()->outputs()->create(); + { + output->name("output"); + loco::link(output, push); + } + + exo::TFLExporter::Impl exporter{graph()}; + { + auto model = tflite::GetModel(exporter.getBufferPointer()); + auto buffers = model->buffers(); + + // 0'th empty buffer + ConstGen data + ConstGen node output + ASSERT_EQ(buffers->Length(), 3); + + // 0'th should be empty buffer + auto buffer_0 = (*buffers)[0]; + auto array_0 = buffer_0->data(); + ASSERT_EQ(array_0, nullptr); + + // 1'st should be ConstGen data which is two float + auto buffer_1 = (*buffers)[1]; + auto array_1 = buffer_1->data(); + size_t size_1 = array_1->size(); + ASSERT_EQ(size_1, 2 * sizeof(float)); + } +} diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.cpp b/compiler/exo/src/TFLite/TFLExporterUtils.cpp new file mode 100644 index 000000000..d35afc9aa --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterUtils.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLExporterUtils.h" + +#include + +namespace exo +{ + +tflite::ActivationFunctionType to_tflite_actfunc(locoex::FusedActFunc func) +{ + switch (func) + { + case locoex::FusedActFunc::NONE: + return tflite::ActivationFunctionType_NONE; + case locoex::FusedActFunc::RELU: + return tflite::ActivationFunctionType_RELU; + case locoex::FusedActFunc::RELU6: + return tflite::ActivationFunctionType_RELU6; + default: + INTERNAL_EXN_V("Unsupported locoex FusedActFunc Type", oops::to_uint32(func)); + } +} + +} // namespace exo + +namespace exo +{ +namespace tflite_detail +{ + +uint32_t SerializedModelData::registerBuiltinOpcode(tflite::BuiltinOperator builtin_code) +{ + auto it = _operator_codes.find(OpCode{builtin_code}); + if (it != _operator_codes.end()) + { + return it->second; + } + auto idx = static_cast(_operator_codes.size()); + _operator_codes.emplace(OpCode{builtin_code}, idx); + return idx; +} + +uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op) +{ + tflite::BuiltinOperator custom_code = tflite::BuiltinOperator_CUSTOM; + auto idx = registerBuiltinOpcode(custom_code); + _custom_operator_codes.emplace(OpCode{custom_code}, custom_op); + return idx; +} + +tflite::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 tflite::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(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast(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 tflite::Padding_SAME; + + INTERNAL_EXN("NYI for custom PAD"); +} + +tflite::Padding getOpPadding(const locoex::Padding pad) +{ + if (pad == locoex::Padding::VALID) + return tflite::Padding_VALID; + if (pad == locoex::Padding::SAME) + return tflite::Padding_SAME; + + INTERNAL_EXN_V("Unknown padding", oops::to_uint32(pad)); +} + +void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd) +{ + for (uint32_t in = 0; in < graph->inputs()->size(); ++in) + { + auto pull = loco::pull_node(graph, in); + auto name = graph->inputs()->at(in)->name(); + + gd._pull_to_name[pull] = name; + } + for (uint32_t out = 0; out < graph->outputs()->size(); ++out) + { + auto push = loco::push_node(graph, out); + auto name = graph->outputs()->at(out)->name(); + + gd._push_to_name[push] = name; + } +} + +#include + +#include + +namespace +{ + +class TFLTensorIndexAnnotation final : public loco::NodeAnnotation +{ +public: + TFLTensorIndexAnnotation(const TFLTensorIndex &index) : _index{index} + { + // DO NOTHING + } + +public: + const TFLTensorIndex &index(void) const { return _index; } + +private: + TFLTensorIndex _index; +}; + +} // namespace + +void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id) +{ + assert(node->annot() == nullptr); + node->annot(stdex::make_unique(tensor_id)); +} + +TFLTensorIndex get_tensor_index(loco::Node *node) +{ + assert(node->annot() != nullptr); + return node->annot()->index(); +} + +} // namespace tflite_detail +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.h b/compiler/exo/src/TFLite/TFLExporterUtils.h new file mode 100644 index 000000000..dbd7a52fb --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterUtils.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFL_EXPORTER_UTILS_H__ +#define __TFL_EXPORTER_UTILS_H__ + +#include "ExporterUtils.h" + +#include "schema_generated.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +#include + +namespace exo +{ +namespace tflite_detail +{ + +struct OpCode +{ + tflite::BuiltinOperator opcode; + + bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; } +}; + +} // namespace tflite_detail +} // namespace exo + +namespace exo +{ + +tflite::ActivationFunctionType to_tflite_actfunc(locoex::FusedActFunc func); + +} // namespace exo + +namespace std +{ + +template <> struct hash +{ + size_t operator()(const exo::tflite_detail::OpCode &x) const { return hash()(x.opcode); } +}; + +} // namespace std + +namespace exo +{ +namespace tflite_detail +{ + +/** + * @breif Record the information of T/F Lite SubGraph and its mapping to loco + */ +struct SubGraphContext +{ + /// @brief SubGraph input tensor id + std::vector _inputs; + /// @brief SubGraph output tensor id + std::vector _outputs; +}; + +// Prerequisites for tflite::Model object creation +struct SerializedModelData final : public SubGraphContext +{ + SerializedModelData() = default; + SerializedModelData(const SerializedModelData &) = delete; + + std::unordered_map _operator_codes; + std::unordered_map _custom_operator_codes; + std::vector> _operators; + std::vector> _tensors; + std::vector> _buffers; + + // Graph input and output names + std::unordered_map _pull_to_name; + std::unordered_map _push_to_name; + + /** + * @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(tflite::BuiltinOperator builtin_code); + uint32_t registerCustomOpcode(const std::string &custom_op); +}; + +tflite::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm); +tflite::Padding getOpPadding(const locoex::Padding pad); + +/// @brief Register graph input and output names to SerializedModelData +void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd); + +using TFLTensorIndex = int32_t; + +void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id); +TFLTensorIndex get_tensor_index(loco::Node *node); + +} // namespace tflite_detail +} // namespace exo + +#endif // __TFL_EXPORTER_UTILS_H__ diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp b/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp new file mode 100644 index 000000000..d19f87d25 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLExporterUtils.h" + +#include + +using namespace exo::tflite_detail; + +TEST(ExporterUtilsTests, getOpPadding) +{ + loco::Padding2D pad; + loco::Stride<2> stride; + exo::ShapeDescription ifm; + exo::ShapeDescription ofm; + + ifm._dims.resize(4); + ofm._dims.resize(4); + + // VALID padding + { + pad.top(0); + pad.bottom(0); + pad.left(0); + pad.right(0); + + stride.vertical(2); + stride.horizontal(2); + + ifm._dims[1] = 5; + ifm._dims[2] = 5; + + ofm._dims[1] = 2; + ofm._dims[2] = 2; + + ASSERT_EQ(getOpPadding(&pad, &stride, ifm, ofm), tflite::Padding_VALID); + } + + // SAME padding + { + pad.top(1); + pad.bottom(1); + pad.left(1); + pad.right(1); + + stride.vertical(2); + stride.horizontal(2); + + ifm._dims[1] = 5; + ifm._dims[2] = 5; + + ofm._dims[1] = 3; + ofm._dims[2] = 3; + + ASSERT_EQ(getOpPadding(&pad, &stride, ifm, ofm), tflite::Padding_SAME); + } + + // Custom padding 1 - Not supported by tflite + { + pad.top(2); + pad.bottom(0); + pad.left(1); + pad.right(1); + + stride.vertical(2); + stride.horizontal(2); + + ifm._dims[1] = 5; + ifm._dims[2] = 5; + + ofm._dims[1] = 3; + ofm._dims[2] = 3; + + ASSERT_ANY_THROW(getOpPadding(&pad, &stride, ifm, ofm)); + } + + // Custom padding 2 - Not supported by tflite + { + pad.top(2); + pad.bottom(2); + pad.left(2); + pad.right(2); + + stride.vertical(2); + stride.horizontal(2); + + ifm._dims[1] = 5; + ifm._dims[2] = 5; + + ofm._dims[1] = 4; + ofm._dims[2] = 4; + + ASSERT_ANY_THROW(getOpPadding(&pad, &stride, ifm, ofm)); + } +} diff --git a/compiler/exo/src/TFLite/TFLOperationExporter.cpp b/compiler/exo/src/TFLite/TFLOperationExporter.cpp new file mode 100644 index 000000000..79b5b6287 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLOperationExporter.cpp @@ -0,0 +1,1199 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLOperationExporter.h" +#include "TFLExporterUtils.h" +#include "ShapeInference.h" + +#include "Dialect/IR/TFLNode.h" +#include "Dialect/IR/TFLNodes.h" +#include "Dialect/IR/TFLNodeVisitor.h" + +#include "Check.h" + +#include +#include +#include +#include + +#include + +#include + +using namespace flatbuffers; +using namespace tflite; + +namespace +{ + +using namespace exo; +using namespace exo::tflite_detail; + +class OperationExporter final : public locoex::TFLNodeMutableVisitor, + public loco::CanonicalNodeMutableVisitor +{ +public: + OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &ctx) : builder{fbb}, gd{ctx} + { + // DO NOTHING + } + +public: + // FOR TFLNodes + void visit(locoex::TFLAdd *) final; + void visit(locoex::TFLAveragePool2D *) final; + void visit(locoex::TFLConcatenation *) final; + void visit(locoex::TFLConst *) final{/* skip, everything is done in exportOpDefinedTensors */}; + void visit(locoex::TFLConv2D *) final; + void visit(locoex::TFLDepthwiseConv2D *) final; + void visit(locoex::TFLDiv *) final; + void visit(locoex::TFLFullyConnected *) final; + void visit(locoex::TFLMaximum *) final; + void visit(locoex::TFLMaxPool2D *) final; + void visit(locoex::TFLMean *) final; + void visit(locoex::TFLMul *) final; + void visit(locoex::TFLRelu *) final; + void visit(locoex::TFLRelu6 *) final; + // TODO TFLReshape + void visit(locoex::TFLRsqrt *) final; + // TODO TFLSoftmax + void visit(locoex::TFLSqrt *) final; + void visit(locoex::TFLSquaredDifference *) final; + void visit(locoex::TFLSub *) final; + // TODO TFLTanh + void visit(locoex::TFLTranspose *) final; + void visit(locoex::TFLTransposeConv *) final; + + // FOR canonical nodes. These will be removed later + void visit(loco::ReLU *) final; + void visit(loco::ReLU6 *) final; + void visit(loco::Tanh *) final; + void visit(loco::Push *) final { /* DO NOTHING */} + void visit(loco::Pull *) final { /* DO NOTHING */} + void visit(loco::FeatureEncode *) final; + void visit(loco::FeatureDecode *) final; + void visit(loco::FilterEncode *) final; + void visit(loco::DepthwiseFilterEncode *) final; + void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */} + void visit(loco::MaxPool2D *) final; + void visit(loco::AvgPool2D *) final; + void visit(loco::Conv2D *) final; + void visit(loco::TransposedConv2D *) final; + void visit(loco::DepthwiseConv2D *) final; + void visit(loco::TensorConcat *) final; + void visit(loco::TensorReduce *) final; + void visit(loco::TensorSoftmax *) final; + void visit(loco::BiasEncode *) final; + void visit(loco::TensorBiasAdd *) final; + void visit(loco::FeatureBiasAdd *) final; + void visit(loco::EltwiseAdd *) final; + void visit(loco::EltwiseMax *) final; + void visit(loco::EltwiseMul *) final; + void visit(loco::EltwiseSub *) final; + void visit(loco::EltwiseDiv *) final; + void visit(loco::EltwiseSqrt *) final; + void visit(loco::FixedReshape *) final; + void visit(loco::TensorBroadcast *) final; + void visit(loco::TensorConstantPad *) final; + + void visit(locoex::COpCall *); + +private: + /** + * @brief Exports TFLMaxPool2D or TFLAveragePool2D + * + * @note TFLPool2D should be one of TFLMaxPool2D or TFLAveragePool2D + */ + template + void export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op); + +private: + FlatBufferBuilder &builder; + SerializedModelData &gd; +}; + +void OperationExporter::visit(locoex::TFLAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLAveragePool2D *node) +{ + export_pool_2d(node, tflite::BuiltinOperator_AVERAGE_POOL_2D); +} + +void OperationExporter::visit(locoex::TFLConcatenation *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION); + std::vector inputs_vec; + std::vector outputs_vec{get_tensor_index(static_cast(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_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_ConcatenationOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONV_2D); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding(node->padding()); + auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(), + to_tflite_actfunc(node->fusedActivationFunction())); + + // Make CONV_2D operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_Conv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLDepthwiseConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DEPTHWISE_CONV_2D); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding(node->padding()); + auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(), + node->stride()->h(), node->depthMultiplier(), + to_tflite_actfunc(node->fusedActivationFunction())); + + // Make DEPTHWISE_CONV_2D operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLDiv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DIV); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateDivOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_DivOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLFullyConnected *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_FULLY_CONNECTED); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->weights()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = + CreateFullyConnectedOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + + // Make FULLY_CONNECTED operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_FullyConnectedOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLMaximum *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAXIMUM); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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, + tflite::BuiltinOptions_MaximumMinimumOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLMaxPool2D *node) +{ + export_pool_2d(node, tflite::BuiltinOperator_MAX_POOL_2D); +} + +void OperationExporter::visit(locoex::TFLMean *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MEAN); + std::vector inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->reduction_indices())}; + std::vector outputs_vec{get_tensor_index(static_cast(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, + tflite::BuiltinOptions_ReducerOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLMul *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMulOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_MulOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLRelu *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU); + std::vector inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLRelu6 *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU6); + std::vector inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +// TODO TFLReshape + +void OperationExporter::visit(locoex::TFLRsqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RSQRT); + std::vector inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +// TODO TFLSoftmax + +void OperationExporter::visit(locoex::TFLSqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQRT); + std::vector inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(locoex::TFLSquaredDifference *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQUARED_DIFFERENCE); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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, + tflite::BuiltinOptions_SquaredDifferenceOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLSub *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SUB); + std::vector inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSubOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_SubOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +// TODO TFLTanh + +void OperationExporter::visit(locoex::TFLTranspose *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE); + std::vector inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))}; + std::vector 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, + tflite::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(locoex::TFLTransposeConv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE_CONV); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->inputSizes()), + get_tensor_index(node->filter()), + get_tensor_index(node->outBackprop())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::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, + tflite::BuiltinOptions_TransposeConvOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +template +void OperationExporter::export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op) +{ + EXO_ASSERT(builtin_op == tflite::BuiltinOperator_MAX_POOL_2D || + builtin_op == tflite::BuiltinOperator_AVERAGE_POOL_2D, + "should be maxpool or avgpool"); + EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set"); + + uint32_t op_idx = gd.registerBuiltinOpcode(builtin_op); + std::vector inputs_vec{get_tensor_index(node->value())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + tflite::Padding padding = getOpPadding(node->padding()); + + auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(), + node->filter()->w(), node->filter()->h(), + to_tflite_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::ReLU *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::ReLU6 *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU6); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::Tanh *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TANH); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::MaxPool2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAX_POOL_2D); + std::vector inputs_vec{get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), node->window()->horizontal(), + node->window()->vertical()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::AvgPool2D *node) +{ + // TFlite only support Valid convention of average pooling + assert(node->convention() == loco::AvgPool2D::Convention::Valid); + + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_AVERAGE_POOL_2D); + std::vector inputs_vec{get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), node->window()->horizontal(), + node->window()->vertical()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::Conv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONV_2D); + + // Third input of CONV_2D of tflite should be bias. We will make (and register to gd) dummy zero + // bias. Bias would be rank 1, have size of output kernel count, and have all zero values, i.e. + // zero bias. + auto *ker = dynamic_cast(node->ker()); + assert(ker); + int32_t bias_vec_size = ShapeInference::get(ker)._dims[0]; // output kernel count + + auto bias_vec_shape_offset = builder.CreateVector(std::vector{bias_vec_size}); + size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); + + std::vector bias_vec_data(bias_vec_size); // initialized as zero vector + + auto bias_vec_offset = + builder.CreateVector(reinterpret_cast(bias_vec_data.data()), raw_bias_vec_size); + + auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); + + const auto bias_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(bias_buffer_offset); + + auto bias_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); + + auto bias_tensor_offset = + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + gd._tensors.push_back(bias_tensor_offset); + + // Make input, output and options for operator + std::vector inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), + bias_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical()); + + // Make CONV_2D operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_Conv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::TransposedConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE_CONV); + + // TRANSPOSE_CONV's first input is output shape array. + const int32_t outshape_vec_size = 4; + auto outshape_vec_shape_offset = builder.CreateVector(std::vector{outshape_vec_size}); + size_t raw_outshape_vec_size = outshape_vec_size * sizeof(int32_t); + + std::vector outshape_vec_data(outshape_vec_size); + { + // Copy inferred output shape of node + auto out_feature_shape = loco::shape_get(node).as(); + + // Feature tensor in TFlite is NHWC + outshape_vec_data.at(0) = out_feature_shape.count().value(); + outshape_vec_data.at(1) = out_feature_shape.height().value(); + outshape_vec_data.at(2) = out_feature_shape.width().value(); + outshape_vec_data.at(3) = out_feature_shape.depth().value(); + } + + auto outshape_vec_offset = builder.CreateVector( + reinterpret_cast(outshape_vec_data.data()), raw_outshape_vec_size); + + auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset); + + const auto outshape_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(outshape_buffer_offset); + + auto outshape_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(outshape_tensor_id)); + + auto outshape_tensor_offset = CreateTensor(builder, outshape_vec_shape_offset, TensorType_INT32, + outshape_buffer_id, name_offset); + gd._tensors.push_back(outshape_tensor_offset); + + // Make input, output and options for operator + std::vector inputs_vec{outshape_tensor_id, get_tensor_index(node->ker()), + get_tensor_index(node->ifm())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + // NOTE input and output is inversed to use this function + tflite::Padding padding = getOpPadding(node->pad(), node->stride(), ShapeInference::get(node), + ShapeInference::get(node->ifm())); + auto options = CreateTransposeConvOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical()); + + // Make TRANSPOSE_CONV operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_TransposeConvOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::DepthwiseConv2D *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DEPTHWISE_CONV_2D); + + // Third input of DEPTHWISE_CONV2D of tflite should be bias. We will make (and register to gd) + // dummy zero bias. Bias would be rank 1, have size of output kernel count, and have all zero + // values, i.e. zero bias. + auto *ker = dynamic_cast(node->ker()); + assert(ker); + + int32_t bias_vec_size = ShapeInference::get(ker)._dims[3]; // output_size(C*M) + auto bias_vec_shape_offset = builder.CreateVector(std::vector{bias_vec_size}); + + size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); + std::vector bias_vec_data(bias_vec_size); + auto bias_vec_offset = + builder.CreateVector(reinterpret_cast(bias_vec_data.data()), raw_bias_vec_size); + + auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); + + const auto bias_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(bias_buffer_offset); + + auto bias_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); + + auto bias_tensor_offset = + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + gd._tensors.push_back(bias_tensor_offset); + + std::vector inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), + bias_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + tflite::Padding padding = getOpPadding( + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + + int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3]; + // multiplier = bias_vec_size(output_size)/ifm_channel_size + auto options = + CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), bias_vec_size / ifm_channel_size); + + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::TensorReduce *node) +{ + uint32_t op_idx; + + switch (node->func()) + { + case loco::ReduceFunc::Mean: + op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MEAN); + break; + + // TODO Support more reduce type operation + default: + INTERNAL_EXN_V("Not supported reduce type", oops::to_uint32(node->func())); + } + + // Create a vector for axes data + std::vector axes_vec; + auto rank = ShapeInference::get(node->input())._dims.size(); + for (uint32_t i = 0; i < rank; ++i) + if (node->axes()->defined(i)) + axes_vec.push_back(i); + + int32_t axes_vec_size = axes_vec.size(); + auto axes_vec_shape_offset = builder.CreateVector(std::vector{axes_vec_size}); + + size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t); + auto axes_vec_offset = + builder.CreateVector(reinterpret_cast(axes_vec.data()), raw_axes_vec_size); + + auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset); + + const auto axes_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(axes_buffer_offset); + + auto axes_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id)); + + auto axes_tensor_offset = + CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); + gd._tensors.push_back(axes_tensor_offset); + + std::vector inputs_vec{get_tensor_index(node->input()), axes_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateReducerOptions(builder, true); // true is for keep_dims option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_ReducerOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::TensorSoftmax *node) +{ + // TODO Support when the input rank of TensorSoftmax is not 2 + assert(ShapeInference::get(node->input())._dims.size() == 2); + + // NOTE TFLite only accepts axis when the value is last dimension + assert(node->axis() == ShapeInference::get(node->input())._dims.size() - 1); + + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SOFTMAX); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSoftmaxOptions(builder, 1.0f); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_SoftmaxOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +/// @brief Export given node into identity, i.e. CONCATENATION with one input +template +void exportIdentity(NodeT *node, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION); + std::vector inputs_vec{get_tensor_index(node->arg(0))}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder); // use dummy 0 axis and NONE activation + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_ConcatenationOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +/// @brief Export loco nodes as TRANSPOSE +void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, + std::vector &perm_vec_data, SerializedModelData &gd) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE); + + auto options = CreateTransposeOptions(builder); + + // Create constant tensor with perm vector + constexpr int perm_vec_size = 4; + assert(perm_vec_data.size() == perm_vec_size); + auto perm_vec_shape_offset = builder.CreateVector(std::vector{perm_vec_size}); + constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t); + + auto perm_vec_offset = + builder.CreateVector(reinterpret_cast(perm_vec_data.data()), raw_perm_vec_size); + + auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset); + + const auto perm_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(perm_buffer_offset); + + auto perm_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id)); + + auto perm_tensor_offset = + CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); + gd._tensors.push_back(perm_tensor_offset); + + // Create permutation node + + std::vector inputs_vec{get_tensor_index(node->arg(0)), perm_tensor_id}; + std::vector outputs_vec{get_tensor_index(node)}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + constexpr auto options_type = tflite::BuiltinOptions::BuiltinOptions_TransposeOptions; + + auto transpose_offset = + CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); + gd._operators.push_back(transpose_offset); +} + +void OperationExporter::visit(loco::FeatureEncode *node) +{ + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + + if (isNHWC(perm)) + { + // Note that tflite represents feature as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + perm_vec_data[0] = perm->axis(loco::FeatureAxis::Count); + perm_vec_data[1] = perm->axis(loco::FeatureAxis::Height); + perm_vec_data[2] = perm->axis(loco::FeatureAxis::Width); + perm_vec_data[3] = perm->axis(loco::FeatureAxis::Depth); + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void OperationExporter::visit(loco::FeatureDecode *node) +{ + auto decoder = dynamic_cast *>(node->decoder()); + auto perm = decoder->perm(); + + if (isNHWC(perm)) + { + // Note that tflite represents feature as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + perm_vec_data[perm->axis(loco::FeatureAxis::Count)] = 0; + perm_vec_data[perm->axis(loco::FeatureAxis::Height)] = 1; + perm_vec_data[perm->axis(loco::FeatureAxis::Width)] = 2; + perm_vec_data[perm->axis(loco::FeatureAxis::Depth)] = 3; + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void OperationExporter::visit(loco::FilterEncode *node) +{ + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + + if (isNHWC(perm)) + { + // Note that tflite represents filter as NHWC + exportIdentity(node, builder, gd); + } + else + { + std::vector perm_vec_data(4); + // NOTE In tflite, all tensors means NHWC, so 0 = N, 1 = H, 2 = W, 3 = C + perm_vec_data[0] = perm->axis(loco::FilterAxis::Count); + perm_vec_data[1] = perm->axis(loco::FilterAxis::Height); + perm_vec_data[2] = perm->axis(loco::FilterAxis::Width); + perm_vec_data[3] = perm->axis(loco::FilterAxis::Depth); + + exportAsTranspose(node, builder, perm_vec_data, gd); + } +} + +void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, + std::vector &new_shape_vec, SerializedModelData &gd) +{ + // NOTE TFLite has two ways to get new shape paramter, + // one is by attribute 'new_shape' and the other is by input 'shape'. + // Therefore TFLite interpreter calculates Reshape operation correctly + // if one of them is valid. + // However, since NN runtime usually get new shape parameter by input 'shape', + // passing new shape only by attribute can cause some problems. + // Of course, the opposite situation can be occurred in the future. + // To prevent those problems, we pass new shape parameter not only by attribute + // but also by input. + + auto input_shape_shape_vec_offset = + builder.CreateVector(std::vector{(int32_t)new_shape_vec.size()}); + + size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t); + auto input_shape_input_vec_offset = + builder.CreateVector(reinterpret_cast(new_shape_vec.data()), input_shape_vec_size); + auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset); + + const auto input_shape_buffer_id = static_cast(gd._buffers.size()); + gd._buffers.push_back(input_shape_buffer_offset); + + auto input_shape_tensor_id = static_cast(gd._tensors.size()); + auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id)); + auto input_shape_tensor_offset = CreateTensor( + builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); + gd._tensors.push_back(input_shape_tensor_offset); + + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RESHAPE); + + std::vector inputs_vec{get_tensor_index(node->arg(0)), input_shape_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + auto new_shape_vec_offset = builder.CreateVector(new_shape_vec); + auto options = CreateReshapeOptions(builder, new_shape_vec_offset); + + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_ReshapeOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::DepthwiseFilterEncode *node) +{ + auto ker = node->input(); // [H, W, C, M] + + // tflite represents filter as [1, H, W, C*M] where M is multiplier. + std::vector new_shape_vec(4); + new_shape_vec[0] = 1; + new_shape_vec[1] = ShapeInference::get(ker)._dims[0]; + new_shape_vec[2] = ShapeInference::get(ker)._dims[1]; + new_shape_vec[3] = ShapeInference::get(ker)._dims[2] * ShapeInference::get(ker)._dims[3]; + + exportAsReshape(node, builder, new_shape_vec, gd); +} + +void OperationExporter::visit(loco::BiasAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::FeatureBiasAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +/// @brief Export CONCATENATION of **TWO** tensors only +void OperationExporter::visit(loco::TensorConcat *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder, node->axis()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_ConcatenationOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::BiasEncode *encode) { exportIdentity(encode, builder, gd); } + +void OperationExporter::visit(loco::EltwiseAdd *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseMax *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAXIMUM); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMaximumMinimumOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_MaximumMinimumOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseMul *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMulOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_MulOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseSub *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SUB); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSubOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_SubOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseDiv *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DIV); + std::vector inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())}; + std::vector outputs_vec{get_tensor_index(static_cast(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateDivOptions(builder); // dummy option + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_DivOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(loco::EltwiseSqrt *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQRT); + std::vector inputs_vec{get_tensor_index(node->input())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(loco::FixedReshape *node) +{ + std::vector new_shape_vec; + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + assert(node->dim(axis).known()); + new_shape_vec.push_back(node->dim(axis).value()); + } + + exportAsReshape(node, builder, new_shape_vec, gd); +} + +void OperationExporter::visit(loco::TensorBroadcast *) +{ + INTERNAL_EXN("TensorBroadcast should not exist in the graph"); +} + +void OperationExporter::visit(loco::TensorConstantPad *node) +{ + uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_PAD); + + // make padding attribute an input + auto padding = node->padding(); + // get padding vector size + int32_t padding_vec_size = padding->rank(); + // get byte size of vector + size_t padding_vec_byte_size = padding_vec_size * sizeof(int32_t) * 2; // [rank, 2] + // create vector for data + std::vector padding_vec_data(padding_vec_size * 2); + // set data + for (int32_t i = 0; i < padding_vec_size; i++) + { + padding_vec_data.at(i * 2) = padding->front(i); + padding_vec_data.at(i * 2 + 1) = padding->back(i); + } + // create FlatBuffer vector + auto padding_vec_ptr = builder.CreateVector(reinterpret_cast(padding_vec_data.data()), + padding_vec_byte_size); + + // create buffer + auto padding_buffer_ptr = CreateBuffer(builder, padding_vec_ptr); + // get buffer id + const auto padding_buffer_id = static_cast(gd._buffers.size()); + + gd._buffers.push_back(padding_buffer_ptr); + + // create padding shape vector + auto padding_shape_vec_ptr = builder.CreateVector(std::vector{padding_vec_size, 2}); + // create tensor + auto padding_tensor_ptr = + CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); + // get tensor id + const auto padding_tensor_id = static_cast(gd._tensors.size()); + + gd._tensors.push_back(padding_tensor_ptr); + + std::vector inputs_vec{get_tensor_index(node->input()), padding_tensor_id}; + std::vector outputs_vec{get_tensor_index(static_cast(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); +} + +inline flatbuffers::Offset> +CreateCOpCallOptions(flatbuffers::FlatBufferBuilder &fbb, locoex::COpCall *copCall) +{ + // read attrs in FlexBuffer format and pass them to FlatBuffer builder + flexbuffers::Builder flexbuf; + { + size_t map_start = flexbuf.StartMap(); + + // Note: among attrs of COpCall, 'op' and 'name' won't be included into tflite file + auto names = copCall->attr_names(); + for (auto name : names) + { + if (auto int_val = copCall->attr(name)) + flexbuf.Int(name.c_str(), int_val->val()); + else if (auto float_val = copCall->attr(name)) + flexbuf.Float(name.c_str(), float_val->val()); + else + // TODO Support more attribute types + INTERNAL_EXN("Not supported type while writing flexbuffer"); + } + + flexbuf.EndMap(map_start); + flexbuf.Finish(); + } + + auto offset = fbb.CreateVector(flexbuf.GetBuffer()); + + return offset; +} + +void OperationExporter::visit(locoex::COpCall *call) +{ + // Registering this custom op name into tflite Operator Codes table + uint32_t op_idx = gd.registerCustomOpcode(call->op()); + + std::vector inputs_vec; + { + inputs_vec.resize(call->arity()); + for (uint32_t i = 0; i < call->arity(); i++) + inputs_vec[i] = get_tensor_index(call->arg(i)); + } + + std::vector outputs_vec{get_tensor_index(static_cast(call))}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + auto custom_options = CreateCOpCallOptions(builder, call); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions_NONE, // builtin_options_type + 0, // built-in option + custom_options, // custom options + tflite::CustomOptionsFormat_FLEXBUFFERS); + + gd._operators.push_back(op_offset); +} + +void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, + SerializedModelData &data) +{ + // TODO Use explicit tagging to prevent possible mistake + auto isNoOp = [](loco::Node *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 canonical_node = dynamic_cast(node)) + { // TODO Consider removing this later + OperationExporter exporter{builder, data}; + canonical_node->accept(&exporter); + } + else if (auto tfl_node = dynamic_cast(node)) + { + OperationExporter exporter{builder, data}; + tfl_node->accept(&exporter); + } + else if (dynamic_cast(node)) + { + OperationExporter exporter{builder, data}; + exporter.visit(dynamic_cast(node)); + } + else + { + assert(false && "unsupported node found"); + } +} + +} // namespace + +namespace exo +{ +namespace tflite_detail +{ + +void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + exportNode(node, builder, gd); + } +} + +} // namespace tflite_detail +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLOperationExporter.h b/compiler/exo/src/TFLite/TFLOperationExporter.h new file mode 100644 index 000000000..60f2b5eb2 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLOperationExporter.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFL_OPERATION_EXPORTER_H__ +#define __TFL_OPERATION_EXPORTER_H__ + +#include "TFLExporterUtils.h" + +#include + +namespace exo +{ +namespace tflite_detail +{ + +/** + * @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 &gd); + +} // namespace tflite_detail +} // namespace exo + +#endif // __TFL_OPERATION_EXPORTER_H__ diff --git a/compiler/exo/src/TFLite/TFLTensorExporter.cpp b/compiler/exo/src/TFLite/TFLTensorExporter.cpp new file mode 100644 index 000000000..66854ef87 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLTensorExporter.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLTensorExporter.h" +#include "TFLTypeInference.h" +#include "ShapeInference.h" + +// TODO Fix include style +#include "loco/IR/Algorithm.h" +#include "loco/IR/CanonicalNode.h" +#include "loco/IR/CanonicalNodeVisitor.h" +#include "loco/IR/DataTypeTraits.h" + +#include "Dialect/IR/TFLNodes.h" + +#include + +using namespace tflite; +using namespace flatbuffers; + +namespace +{ + +using namespace exo; +using namespace exo::tflite_detail; + +class TFLTensorInfo +{ +public: + TFLTensorInfo() = default; + +public: + void name(const std::string &name) { _name = name; } + const std::string &name(void) const { return _name; } + +public: + const tflite::TensorType &dtype(void) const { return _dtype; } + void dtype(const tflite::TensorType &dtype) { _dtype = dtype; } + + const ShapeDescription &shape(void) const { return _shape; } + void shape(const ShapeDescription &shape) { _shape = shape; } + +public: + locoex::TFLConst *tfl_content(void) const { return _tfl_content; } + void tfl_content(locoex::TFLConst *c) { _tfl_content = c; } + +private: + std::string _name; + + tflite::TensorType _dtype; + ShapeDescription _shape; + + // TODO Find a better design + loco::ConstGen *_content = nullptr; // TODO deprecate + locoex::TFLConst *_tfl_content = nullptr; +}; + +using TFLTensorContext = std::vector; + +struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor +{ + bool visit(loco::BiasEncode *) final + { + // BiasEncode is always noop + return true; + } + + bool visit(loco::FilterEncode *node) final + { + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + + return isNHWC(perm); + } + + bool visit(loco::FeatureEncode *node) final + { + auto encoder = dynamic_cast *>(node->encoder()); + auto perm = encoder->perm(); + return isNHWC(perm); + } + + bool visit(loco::FeatureDecode *node) final + { + auto decoder = dynamic_cast *>(node->decoder()); + auto perm = decoder->perm(); + return isNHWC(perm); + } + + // Return false by default + bool visit(loco::Node *) final { return false; } +}; + +bool isNoOp(loco::Node *node) +{ + if (auto canonical_node = dynamic_cast(node)) + { + NoOpDetector d; + return canonical_node->accept(&d); + } + return false; +} + +void allocateTFLiteTensor(loco::Node *node, TFLTensorContext &ctx) +{ + if (isNoOp(node)) + { + assert(node->arity() == 1 && node->arg(0) != nullptr); + set_tensor_index(node, get_tensor_index(node->arg(0))); + return; + } + + auto tensor_index = static_cast(ctx.size()); + // TODO Use Graph-level metadata for Input & Output + auto tensor_name = "t_" + std::to_string(tensor_index); + + TFLTensorInfo tensor_info; + + tensor_info.name(tensor_name); + tensor_info.dtype(TypeInference::get(node)); + tensor_info.shape(ShapeInference::get(node)); + + tensor_info.tfl_content(dynamic_cast(node)); + + set_tensor_index(node, tensor_index); + + ctx.emplace_back(tensor_info); +} + +} // namespace + +namespace +{ + +flatbuffers::Offset> encodeShape(FlatBufferBuilder &builder, + const ShapeDescription &shape) +{ + assert(shape._rank_known && "unknown number of dimensions is not supported"); + return builder.CreateVector(shape._dims); +} + +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, NodeT *) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBufferByDType(FlatBufferBuilder &builder, + locoex::TFLConst *c) +{ + using NativeType = typename loco::DataTypeImpl
::Type; + + std::vector raw_data; + const uint32_t size = c->size
(); + raw_data.reserve(size); + for (uint32_t i = 0; i < size; ++i) + { + raw_data.push_back(c->at
(i)); + } + const size_t raw_size = size * sizeof(NativeType); + auto array_offset = builder.CreateVector(reinterpret_cast(raw_data.data()), raw_size); + return CreateBuffer(builder, array_offset); +} + +template <> +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, locoex::TFLConst *c) +{ + if (c->dtype() == loco::DataType::FLOAT32) + { + return encodeOpBufferByDType(builder, c); + } + else if (c->dtype() == loco::DataType::S32) + { + return encodeOpBufferByDType(builder, c); + } + + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); +} + +} // namespace + +namespace exo +{ +namespace tflite_detail +{ + +void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder, + SerializedModelData &gd) +{ + // Create and register output tensor shape + auto shape_offset = encodeShape(builder, info.shape()); + + // encode and register output tensor buffer + auto buffer = info.tfl_content() == nullptr ? encodeOpBuffer(builder) + : encodeOpBuffer(builder, info.tfl_content()); + + auto buffer_id = static_cast(gd._buffers.size()); + gd._buffers.push_back(buffer); + + auto name_offset = builder.CreateString(info.name()); + auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, + /*quantization*/ 0, /*is_variable*/ false); + gd._tensors.push_back(tensor_offset); +} + +void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd) +{ + TFLTensorContext tensor_ctx; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + allocateTFLiteTensor(node, tensor_ctx); + } + + // add one empty buffer + // 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); + gd._buffers.push_back(buffer); + + for (const auto &tensor_info : tensor_ctx) + { + exportOpDefinedTensor(tensor_info, builder, gd); + } +} + +} // namespace tflite_detail +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLTensorExporter.h b/compiler/exo/src/TFLite/TFLTensorExporter.h new file mode 100644 index 000000000..97e702665 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLTensorExporter.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFL_TENSOR_EXPORTER_H__ +#define __TFL_TENSOR_EXPORTER_H__ + +#include "TFLExporterUtils.h" + +#include + +#include + +namespace exo +{ +namespace tflite_detail +{ + +/** + * @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 &gd); + +} // namespace tflite_detail +} // namespace exo + +#endif // __TFL_TENSOR_EXPORTER_H__ diff --git a/compiler/exo/src/TFLite/TFLTypeInference.cpp b/compiler/exo/src/TFLite/TFLTypeInference.cpp new file mode 100644 index 000000000..8d6bb8d8c --- /dev/null +++ b/compiler/exo/src/TFLite/TFLTypeInference.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLTypeInference.h" + +#include "schema_generated.h" + +#include "Dialect/Service/TFLTypeInferenceRule.h" +#include "Dialect/IR/TFLDialect.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +namespace +{ + +tflite::TensorType translateLocoTypeToTFLite(loco::DataType dtype) +{ + switch (dtype) + { + case loco::DataType::U8: + return tflite::TensorType_UINT8; + // case loco::DataType::U16: unsupported + // case loco::DataType::U32: unsupported + // case loco::DataType::U64: unsupported + case loco::DataType::S8: + return tflite::TensorType_INT8; + case loco::DataType::S16: + return tflite::TensorType_INT16; + case loco::DataType::S32: + return tflite::TensorType_INT32; + case loco::DataType::S64: + return tflite::TensorType_INT64; + case loco::DataType::FLOAT16: + return tflite::TensorType_FLOAT16; + case loco::DataType::FLOAT32: + return tflite::TensorType_FLOAT32; + // case loco::DataType::FLOAT64: unsupported + default: + break; + } + + INTERNAL_EXN_V("Trying to converte unsupported loco dtype", oops::to_uint32(dtype)); +} + +} // namespace + +namespace exo +{ + +tflite::TensorType TypeInference::get(loco::Node *node) +{ + assert(loco::dtype_known(node)); + return translateLocoTypeToTFLite(loco::dtype_get(node)); +} + +} // namespace exo diff --git a/compiler/exo/src/TFLite/TFLTypeInference.h b/compiler/exo/src/TFLite/TFLTypeInference.h new file mode 100644 index 000000000..3d3a2e480 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLTypeInference.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFL_TYPE_INFERENCE_H__ +#define __TFL_TYPE_INFERENCE_H__ + +#include "TFLExporterUtils.h" + +#include + +namespace exo +{ + +/** + * @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 tflite::TensorType get(loco::Node *node); +}; + +} // namespace exo + +#endif // __TFL_TYPE_INFERENCE_H__ diff --git a/compiler/exo/src/TFLite/TFLTypeInference.test.cpp b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp new file mode 100644 index 000000000..0712f0a25 --- /dev/null +++ b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFLTypeInference.h" +#include "Pass/TypeInferencePass.h" + +#include +#include + +#include + +using stdex::make_unique; + +namespace +{ + +class Sequential +{ +public: + loco::Pull *addPullLayer(const loco::DataType &dtype = loco::DataType::FLOAT32) + { + loco::Pull *pull = _graph.nodes()->create(); + + auto graph_input = _graph.inputs()->create(); + graph_input->name("graph_input"); + loco::link(graph_input, pull); + + pull->dtype(dtype); + setSampleShape(pull); + + return last(pull); + } + + loco::ReLU *addReLULayer(void) + { + loco::ReLU *relu = _graph.nodes()->create(); + + relu->input(_last); + + return last(relu); + } + + loco::Push *addPushLayer(void) + { + loco::Push *push = _graph.nodes()->create(); + + auto graph_output = _graph.outputs()->create(); + graph_output->name("graph_output"); + loco::link(graph_output, push); + + push->from(_last); + + return last(push); + } + + loco::Graph *graph() { return &_graph; } + +private: + template uint32_t setSampleShape(T *op) + { + const uint32_t n = 1; + const uint32_t h = 100; + const uint32_t w = 100; + const uint32_t c = 3; + op->rank(4); + op->dim(0).set(n); + op->dim(1).set(c); + op->dim(2).set(h); + op->dim(3).set(w); + return n * h * w * c; + } + + template T *last(T *node) + { + _last = node; + return node; + } + +private: + loco::Graph _graph; + loco::Node *_last; +}; + +struct TypeInferenceTest : public Sequential, public ::testing::Test +{ + virtual ~TypeInferenceTest() = default; +}; + +} // namespace + +// TypeInference SHOULD PROPAGATE type information properly +TEST_F(TypeInferenceTest, Regression_0000) +{ + auto pull = addPullLayer(loco::DataType::S8); + auto relu = addReLULayer(); + auto push = addPushLayer(); + + using namespace exo; + + TypeInferencePass type_inf_pass; + type_inf_pass.run(graph()); + + ASSERT_EQ(TypeInference::get(relu), tflite::TensorType_INT8); + ASSERT_EQ(TypeInference::get(push), tflite::TensorType_INT8); +} diff --git a/compiler/exo/src/TestGraph.h b/compiler/exo/src/TestGraph.h new file mode 100644 index 000000000..f919cc9ae --- /dev/null +++ b/compiler/exo/src/TestGraph.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_GRAPH_H__ +#define __TEST_GRAPH_H__ + +#include "Dialect/IR/TFLNodes.h" +#include "GraphBlock.h" +#include "TestHelper.h" + +#include + +#include + +#include + +namespace exo +{ +namespace test +{ + +class TestGraph +{ +public: + std::unique_ptr g; + loco::Pull *pull; + loco::Push *push; + + TestGraph() // creates Pull and Push + { + g = loco::make_graph(); + + pull = g->nodes()->create(); + + push = g->nodes()->create(); + + 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 T *append() + { + auto node = g->nodes()->create(); + _next_input = node; + + return node; + } + + /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph + template T *append(loco::Node *arg1) + { + auto node = g->nodes()->create(); + 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 T *append(loco::Node *arg1, loco::Node *arg2) + { + auto node = g->nodes()->create(); + 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 T *append(loco::Node *arg1, loco::Node *arg2, loco::Node *arg3) + { + auto node = g->nodes()->create(); + 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(locoex::TFLAveragePool2D *node, loco::Node *input) { node->value(input); }; + void setInput(locoex::TFLMaxPool2D *node, loco::Node *input) { node->value(input); }; + void setInput(locoex::TFLRelu *node, loco::Node *input) { node->features(input); }; + void setInput(locoex::TFLRelu6 *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(locoex::TFLAdd *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(locoex::TFLMul *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(locoex::TFLSub *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(locoex::TFLTranspose *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(locoex::TFLConv2D *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, + + TFLTranspose, +}; + +template class ExampleGraph; + +/** + * @brief Class to create the following: + * + * Pull - FeatureEncoder - FeatureBiasAdd - FeatureDecode - Push + * | + * ConstGen - BiasEncode --+ + */ +template <> class ExampleGraph : 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 = exo::make_feature_encode(pull); + constgen = append(); + bias_enc = append(constgen); + fea_bias_add = append(fea_enc, bias_enc); + fea_dec = exo::make_feature_decode(fea_bias_add); + complete(fea_dec); + } +}; + +/** + * @brief Class to creates the following: + * + * ConstGen -- ReLU -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::ConstGen *constgen = nullptr; + loco::ReLU *relu = nullptr; + +public: + ExampleGraph() + { + constgen = append(); + relu = append(constgen); + complete(relu); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- Transpose -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::TensorTranspose *transpose = nullptr; + +public: + ExampleGraph() + { + transpose = append(pull); + complete(transpose); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- FilterEncode -- FilterDecode -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::FilterEncode *filterEncode = nullptr; + loco::FilterDecode *filterDecode = nullptr; + +public: + ExampleGraph() + { + filterEncode = exo::make_filter_encode(pull); // from Tensorflow + filterDecode = + exo::make_filter_decode(filterEncode); // to Tensorflow Lite + complete(filterDecode); + } +}; + +/** + * @brief Class to create the following: + * + * Pull -- TFLTranspose -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::ConstGen *const_perm = nullptr; + locoex::TFLTranspose *tfl_transpose = nullptr; + +public: + ExampleGraph() + { + const_perm = append(); + tfl_transpose = append(pull, const_perm); + complete(tfl_transpose); + } +}; + +} // namespace test +} // namespace exo + +#endif // __TEST_GRAPH_H__ diff --git a/compiler/exo/src/TestHelper.h b/compiler/exo/src/TestHelper.h new file mode 100644 index 000000000..1a3de50f5 --- /dev/null +++ b/compiler/exo/src/TestHelper.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_HELPER_H__ +#define __TEST_HELPER_H__ + +#include "Check.h" +#include "ProgressReporter.h" +#include "Passes.h" + +#include +#include + +#include + +#include + +#include + +/** + * @brief Check the number of nodes in a graph starting from OUTPUTS + */ +#define EXO_TEST_ASSERT_NODE_COUNT(OUTPUTS, COUNT) \ + { \ + auto v = loco::postorder_traversal(OUTPUTS); \ + ASSERT_EQ(v.size(), (COUNT)); \ + } + +namespace exo +{ +namespace test +{ + +/** + * @brief Phase for test, that is used to test pass. This phase initially adds TypeInferencePass + * and ShapeInferencePass + */ +class TypeShapeReadyPhase +{ +public: + TypeShapeReadyPhase() + { + // Type and Shape inference is prerequisite for run other test + _phase.emplace_back(stdex::make_unique<::exo::TypeInferencePass>()); + _phase.emplace_back(stdex::make_unique<::exo::ShapeInferencePass>()); + } + + template void add_pass() { _phase.emplace_back(stdex::make_unique()); } + + void run(loco::Graph *g) + { + const auto restart = logo::PhaseStrategy::Restart; + logo::PhaseRunner phase_runner{g}; + + ::exo::ProgressReporter prog(g, restart); + phase_runner.attach(&prog); + phase_runner.run(_phase); + } + +private: + logo::Phase _phase; +}; + +/** + * @brief Get the only succ object of type LocoNodeT. (The name `only succ` comes from English word + * `only child`.) + * parent must have 1 succ only. + * When there is no succ of type LocoNodeT, nullptr will be returned. + */ +template inline LocoNodeT *get_only_succ(loco::Node *parent) +{ + auto succs = loco::succs(parent); + EXO_ASSERT(succs.size() == 1, "parent has more than 1 succs."); + + return dynamic_cast(*succs.begin()); +} + +template inline T *find_first_node_bytype(loco::Graph *g) +{ + T *first_node = nullptr; + loco::Graph::NodeContext *nodes = g->nodes(); + uint32_t count = nodes->size(); + + for (uint32_t i = 0; i < count; ++i) + { + first_node = dynamic_cast(nodes->at(i)); + if (first_node != nullptr) + break; + } + + return first_node; +} + +} // namespace test +} // namespace exo + +#endif // __TEST_HELPER_H__ diff --git a/compiler/fipe/CMakeLists.txt b/compiler/fipe/CMakeLists.txt new file mode 100644 index 000000000..2cabf6279 --- /dev/null +++ b/compiler/fipe/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(fipe INTERFACE) +target_include_directories(fipe INTERFACE include) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(fipe_test fipe.test.cpp) +target_link_libraries(fipe_test fipe) diff --git a/compiler/fipe/fipe.test.cpp b/compiler/fipe/fipe.test.cpp new file mode 100644 index 000000000..347f26f9b --- /dev/null +++ b/compiler/fipe/fipe.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fipe.h" + +#include + +#include + +namespace +{ + +int dec(int n) { return n - 1; } + +} // namespace + +TEST(FunctionPipeTests, top_level_function) +{ + // GCC rejects this code if dec is not wrapped by "fipe::wrap" + // TODO Find a better way + ASSERT_EQ(4 | fipe::wrap(dec), 3); +} + +TEST(FunctionPipeTests, static_method) +{ + struct Sample + { + static int dbl(int n) { return n * 2; } + }; + + ASSERT_EQ(4 | fipe::wrap(Sample::dbl), 8); +} + +TEST(FunctionPipeTests, normal_method) +{ + struct Sample + { + public: + int shift(int n) { return n + shiftamt; } + + private: + int shiftamt = 6; + }; + + using namespace std::placeholders; + + Sample s; + + auto value = 4 | std::bind(&Sample::shift, &s, _1); + + ASSERT_EQ(value, 10); +} + +TEST(FunctionPipeTests, lambda) +{ + auto inc = [](int n) { return n + 1; }; + ASSERT_EQ(4 | inc, 5); +} + +TEST(FunctionPipeTests, functor) { ASSERT_EQ(4 | std::negate(), -4); } diff --git a/compiler/fipe/include/fipe.h b/compiler/fipe/include/fipe.h new file mode 100644 index 000000000..0a661aa04 --- /dev/null +++ b/compiler/fipe/include/fipe.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FIPE_H__ +#define __FIPE_H__ + +#include +#include + +namespace fipe +{ + +/** + * @brief Convert a function pointer as a callable std::function + * + * NOTE "fipe" works only for unary functions. + */ +template std::function wrap(Ret (*p)(Arg)) { return p; } + +} // namespace fipe + +template auto operator|(T &&v, Callable &&f) -> decltype(f(v)) +{ + return std::forward(f)(v); +} + +#endif // __FIPE_H__ diff --git a/compiler/gen-core/CMakeLists.txt b/compiler/gen-core/CMakeLists.txt new file mode 100644 index 000000000..3732f493b --- /dev/null +++ b/compiler/gen-core/CMakeLists.txt @@ -0,0 +1,17 @@ +find_package(HDF5 COMPONENTS CXX QUIET) + +if(NOT HDF5_FOUND) + return() +endif(NOT HDF5_FOUND) + +nnas_find_package(TensorFlow QUIET) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(gen_core STATIC ${SOURCES}) +set_target_properties(gen_core PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(gen_core PUBLIC include) +target_include_directories(gen_core PRIVATE ${HDF5_INCLUDE_DIRS}) +target_link_libraries(gen_core ${HDF5_CXX_LIBRARIES}) +target_link_libraries(gen_core tfinfo_v2) +target_link_libraries(gen_core angkor) diff --git a/compiler/gen-core/README.md b/compiler/gen-core/README.md new file mode 100644 index 000000000..cc98ef00b --- /dev/null +++ b/compiler/gen-core/README.md @@ -0,0 +1,3 @@ +# gen-core + +_gen-core_ is a common library used by _gen-tf-input_, _gen-tf-output_, and _gen-tflite-output_. diff --git a/compiler/gen-core/include/gencore/HDF5Common.h b/compiler/gen-core/include/gencore/HDF5Common.h new file mode 100644 index 000000000..87367c99c --- /dev/null +++ b/compiler/gen-core/include/gencore/HDF5Common.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HDF5COMMON_H__ +#define __HDF5COMMON_H__ + +#include + +namespace gencore +{ + +/** + * @brief Construct HDF5-compatible dataset name from a given string + * + * When someone attempts to access 'A/B/C' dataset, HDF5 tries to open + * dataset C in group B in top-level group A, which means that dataset + * names SHOULD NOT contain '/' in it. + * + * This mangle function replaces all the occurence of '/' in a given + * string with '_' to construct HDF5-compatible dataset name. + */ +std::string mangle(const std::string &); + +#if 0 +Let us assume that a tensor context includes N + 1 tensors. + +Then, HDF5 export will generate a HDF5 file whose structure is given as follows: +[value group]/ + [file 0] <- A dataset that contains the value of 1st (=0) tensor + [file 1] + ... + [file N] +[name group]/ + [file 0] <- An attribute that contains the name of 1st (=0) tensor + [file 1] + ... + [file N] +#endif + +/// @brief Return the name of "value group" +std::string value_grpname(void); +/// @brief Return the name of n-th tensor dataset +std::string value_filename(uint32_t n); + +/// @brief Return the name of "name group" +std::string name_grpname(void); +/// @brief Return the name of n-th tensor attribute +std::string name_filename(uint32_t n); + +} // namespace gencore + +#endif // __HDF5COMMON_H__ diff --git a/compiler/gen-core/include/gencore/HDF5Exporter.h b/compiler/gen-core/include/gencore/HDF5Exporter.h new file mode 100644 index 000000000..10cc1c613 --- /dev/null +++ b/compiler/gen-core/include/gencore/HDF5Exporter.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GENCORE_HDF5EXPORTER_H__ +#define __GENCORE_HDF5EXPORTER_H__ + +#include "HDF5Common.h" + +#include +#include + +#include + +namespace gencore +{ + +class H5Exporter +{ +public: + H5Exporter(const std::string &path) : _file{path.c_str(), H5F_ACC_TRUNC} + { + _value_grp = _file.createGroup(value_grpname()); + _name_grp = _file.createGroup(name_grpname()); + } + +public: + template + void write(uint32_t nth, const std::string &name, const angkor::TensorShape &shape, + const nncc::core::ADT::tensor::Reader
&buf_reader); + +private: + H5::H5File _file; + H5::Group _value_grp; + H5::Group _name_grp; +}; + +} // namespace gencore + +#endif // __GENCORE_HDF5EXPORTER_H__ diff --git a/compiler/gen-core/include/gencore/HDF5Importer.h b/compiler/gen-core/include/gencore/HDF5Importer.h new file mode 100644 index 000000000..853744199 --- /dev/null +++ b/compiler/gen-core/include/gencore/HDF5Importer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GENCORE_HDF5IMPORTER_H__ +#define __GENCORE_HDF5IMPORTER_H__ + +#include "HDF5Common.h" + +#include + +#include +#include + +#include + +namespace gencore +{ + +class HDF5Importer +{ +public: + HDF5Importer(const std::string &path) : _file{path, H5F_ACC_RDONLY} + { + _value_grp = _file.openGroup(value_grpname()); + } + +public: + /** + * @brief Reads tensor data from file and store it into buf_accessor + */ + template + void read(uint32_t nth, const std::string &name, const angkor::TensorShape &shape, + nncc::core::ADT::tensor::Accessor
*buf_accessor); + +private: + H5::H5File _file; + H5::Group _value_grp; +}; + +} // namespace gencore + +#endif // __GENCORE_HDF5IMPORTER_H__ diff --git a/compiler/gen-core/requires.cmake b/compiler/gen-core/requires.cmake new file mode 100644 index 000000000..a424f1f4a --- /dev/null +++ b/compiler/gen-core/requires.cmake @@ -0,0 +1,2 @@ +require("tfinfo-v2") +require("angkor") diff --git a/compiler/gen-core/src/HDF5Common.cpp b/compiler/gen-core/src/HDF5Common.cpp new file mode 100644 index 000000000..c254d9e1e --- /dev/null +++ b/compiler/gen-core/src/HDF5Common.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gencore/HDF5Common.h" + +namespace gencore +{ + +std::string mangle(const std::string &name) +{ + std::string res = name; + + for (uint32_t n = 0; n < res.size(); ++n) + { + if (res.at(n) == '/') + { + res.at(n) = '_'; + } + } + + return res; +} + +std::string value_grpname(void) { return "value"; } +std::string value_filename(uint32_t n) { return std::to_string(n); } + +std::string name_grpname(void) { return "name"; } +std::string name_filename(uint32_t n) { return std::to_string(n); } + +} // namespace gencore diff --git a/compiler/gen-core/src/HDF5Exporter.cpp b/compiler/gen-core/src/HDF5Exporter.cpp new file mode 100644 index 000000000..6b77710c4 --- /dev/null +++ b/compiler/gen-core/src/HDF5Exporter.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gencore/HDF5Exporter.h" + +#include +#include +#include +#include + +#include + +namespace +{ + +template H5::PredType get_h5_datatype(); + +template <> H5::PredType get_h5_datatype() { return H5::PredType::NATIVE_FLOAT; } + +template H5::PredType get_h5_store_format(); + +template <> H5::PredType get_h5_store_format() { return H5::PredType::IEEE_F32BE; } + +} // namespace + +namespace gencore +{ + +template +void H5Exporter::write(uint32_t nth, const std::string &name, const angkor::TensorShape &shape, + const nncc::core::ADT::tensor::Reader
&buf_reader) +{ + // Record tensor values + { + const auto rank = shape.rank(); + + hsize_t dims[rank]; + + for (uint32_t axis = 0; axis < rank; ++axis) + { + dims[axis] = shape.dim(axis); + } + + H5::DataSpace dataspace(rank, dims); + + auto dataset = + _value_grp.createDataSet(value_filename(nth), get_h5_store_format
(), dataspace); + + DT *h5_data = new DT[nncc::core::ADT::tensor::num_elements(shape)]; + { + using nncc::core::ADT::tensor::IndexEnumerator; + using nncc::core::ADT::tensor::LexicalLayout; + + LexicalLayout layout{}; + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + auto i = e.current(); + h5_data[layout.offset(shape, i)] = buf_reader.at(i); + } + } + + dataset.write(h5_data, get_h5_datatype
()); + + delete[] h5_data; + } + + // Record name + { + H5::DataSpace name_dataspace(H5S_SCALAR); + H5::StrType name_datatype(H5::PredType::C_S1, name.size()); + + auto name_attr = _name_grp.createAttribute(value_filename(nth), name_datatype, name_dataspace); + + name_attr.write(name_datatype, name); + } +} + +// template instantiation +template void H5Exporter::write(uint32_t, const std::string &, const angkor::TensorShape &, + const nncc::core::ADT::tensor::Reader &); + +} // namespace gencore diff --git a/compiler/gen-core/src/HDF5Importer.cpp b/compiler/gen-core/src/HDF5Importer.cpp new file mode 100644 index 000000000..83691b20b --- /dev/null +++ b/compiler/gen-core/src/HDF5Importer.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gencore/HDF5Importer.h" +#include "gencore/HDF5Common.h" + +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + +template H5::PredType get_h5_datatype(); + +template <> H5::PredType get_h5_datatype() { return H5::PredType::NATIVE_FLOAT; } + +template H5::PredType get_h5_store_format(); + +template <> H5::PredType get_h5_store_format() { return H5::PredType::IEEE_F32BE; } + +} // namespace + +namespace gencore +{ + +template +void HDF5Importer::read(uint32_t nth, const std::string &name, const angkor::TensorShape &shape, + nncc::core::ADT::tensor::Accessor
*buf_accessor) +{ + assert(buf_accessor != nullptr); + + try + { + auto dataset = _value_grp.openDataSet(value_filename(nth)); + + assert(dataset.getDataType() == get_h5_store_format
()); + + std::vector
file_buf; + { + file_buf.resize(nncc::core::ADT::tensor::num_elements(shape)); + dataset.read(file_buf.data(), get_h5_datatype
()); + } + + using nncc::core::ADT::tensor::IndexEnumerator; + using nncc::core::ADT::tensor::LexicalLayout; + + LexicalLayout layout{}; + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + auto i = e.current(); + buf_accessor->at(i) = file_buf[layout.offset(shape, i)]; + } + } + catch (const H5::FileIException &) + { + // Skip if data is not present in HDF5 file + } +} + +// template instantiation +template void HDF5Importer::read(uint32_t, const std::string &, const angkor::TensorShape &, + nncc::core::ADT::tensor::Accessor *); + +} // namespace gencore diff --git a/compiler/gen-tf-input/CMakeLists.txt b/compiler/gen-tf-input/CMakeLists.txt new file mode 100644 index 000000000..12b78b5b3 --- /dev/null +++ b/compiler/gen-tf-input/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +# making gen-tf-input +add_executable(gen-tf-input ${SOURCES}) diff --git a/compiler/gen-tf-input/README.md b/compiler/gen-tf-input/README.md new file mode 100644 index 000000000..2ea6f71b4 --- /dev/null +++ b/compiler/gen-tf-input/README.md @@ -0,0 +1,11 @@ +# gen-tf-input + +_gen-tf-input_ generates random input data for testing in HDF5 format. + +# How to use + +Use the following to generate a file that contains random values of input tensors: + +``` +$ gen-tf-input +``` diff --git a/compiler/gen-tf-input/src/Driver.cpp b/compiler/gen-tf-input/src/Driver.cpp new file mode 100644 index 000000000..f2ce20f16 --- /dev/null +++ b/compiler/gen-tf-input/src/Driver.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 +#include + +namespace +{ + +void print_help() +{ + std::cerr << "This generates a file that contains random values of input tensors" << std::endl + << "Usage:" << std::endl + << " gen-tf-input " << std::endl; +} + +} // namespace + +namespace +{ + +void gen_input(const std::string info_v2_path, const std::string pb_path, + const std::string input_path) +{ + // TODO write code + assert("Not yet written" && nullptr); +} + +} // namespace + +int main(int argc, char **argv) +{ + // TODO We need better args parsing in future + if (argc != 4) + { + print_help(); + return 255; + } + + gen_input(argv[1], argv[2], argv[3]); + + return 0; +} diff --git a/compiler/gen-tf-output/CMakeLists.txt b/compiler/gen-tf-output/CMakeLists.txt new file mode 100644 index 000000000..c2b91a9cd --- /dev/null +++ b/compiler/gen-tf-output/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(gen-tf-output ${SOURCES}) diff --git a/compiler/gen-tf-output/README.md b/compiler/gen-tf-output/README.md new file mode 100644 index 000000000..ca54c75d5 --- /dev/null +++ b/compiler/gen-tf-output/README.md @@ -0,0 +1,13 @@ +# gen-tf-output + +_gen-tf-output_ generates a file containing the result of running TensorFlow in HDF5 format. + +# How to use + +Use the following: + +``` +$ gen-tf-output +``` + +Use _gen_tf_input_ to generate `` file. diff --git a/compiler/gen-tf-output/src/Driver.cpp b/compiler/gen-tf-output/src/Driver.cpp new file mode 100644 index 000000000..209651987 --- /dev/null +++ b/compiler/gen-tf-output/src/Driver.cpp @@ -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. + */ + +#include +#include +#include + +namespace +{ + +void print_help() +{ + std::cerr << "This generates a file that contains result of running TensorFlow" << std::endl + << "Usage:" << std::endl + << "\t" + << "gen-tf-output " + "" + << std::endl; +} + +void gen_tf_output(const std::string info_v2_path, const std::string pb_path, + const std::string input_path, const std::string output_path) +{ + throw std::runtime_error("Not Yet Implemented"); +} + +} // namespace + +int main(int argc, char **argv) +{ + // TODO We need better args parsing in future + if (argc != 5) + { + print_help(); + return 255; + } + + gen_tf_output(argv[1], argv[2], argv[3], argv[4]); + + return 0; +} diff --git a/compiler/gen-tflite-output/CMakeLists.txt b/compiler/gen-tflite-output/CMakeLists.txt new file mode 100644 index 000000000..1c9d2601d --- /dev/null +++ b/compiler/gen-tflite-output/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(gen-tflite-output ${SOURCES}) diff --git a/compiler/gen-tflite-output/README.md b/compiler/gen-tflite-output/README.md new file mode 100644 index 000000000..a9c985006 --- /dev/null +++ b/compiler/gen-tflite-output/README.md @@ -0,0 +1,14 @@ +# gen-tflite-output + +_gen-tflite-output_ generates a file containing the result of running TensorFlow Lite interpreter +in HDF5 format. + +# How to use + +Use the following: + +``` +$ gen-tflite-output +``` + +Use _gen_tf_input_ to generate `` file. diff --git a/compiler/gen-tflite-output/src/Driver.cpp b/compiler/gen-tflite-output/src/Driver.cpp new file mode 100644 index 000000000..90559ec2f --- /dev/null +++ b/compiler/gen-tflite-output/src/Driver.cpp @@ -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. + */ + +#include +#include +#include + +namespace +{ + +void print_help() +{ + std::cerr << "This generates a file that contains result of running TensorFlow Lite interpreter" + << std::endl + << "Usage:" << std::endl + << "\t" + << "$ gen-tflite-output " + << std::endl; +} + +void gen_tflite_output(const std::string tflite_path, const std::string input_path, + const std::string output_path) +{ + throw std::runtime_error("Not Yet Implemented"); +} + +} // namespace + +int main(int argc, char **argv) +{ + // TODO We need better args parsing in future + if (argc != 4) + { + print_help(); + return 255; + } + + gen_tflite_output(argv[1], argv[2], argv[3]); + + return 0; +} diff --git a/compiler/hermes-std/CMakeLists.txt b/compiler/hermes-std/CMakeLists.txt new file mode 100644 index 000000000..c7b02e14c --- /dev/null +++ b/compiler/hermes-std/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(hermes_std STATIC ${SOURCES}) +set_target_properties(hermes_std PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(hermes_std PUBLIC include) +target_link_libraries(hermes_std PUBLIC hermes) +target_link_libraries(hermes_std PRIVATE stdex) +target_link_libraries(hermes_std PRIVATE pepper_strcast) +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(hermes_std PRIVATE nncc_common) +target_link_libraries(hermes_std PUBLIC nncc_coverage) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(hermes_std_test ${TESTS}) +target_link_libraries(hermes_std_test stdex) +target_link_libraries(hermes_std_test hermes_std) diff --git a/compiler/hermes-std/README.md b/compiler/hermes-std/README.md new file mode 100644 index 000000000..f5f4b860f --- /dev/null +++ b/compiler/hermes-std/README.md @@ -0,0 +1,3 @@ +# hermes-std + +_hermes-std_ is a collection of **primitive** _hermes_ extensions. diff --git a/compiler/hermes-std/include/hermes/ConsoleReporter.h b/compiler/hermes-std/include/hermes/ConsoleReporter.h new file mode 100644 index 000000000..e09dd5785 --- /dev/null +++ b/compiler/hermes-std/include/hermes/ConsoleReporter.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_STD_CONSOLE_REPORTER_H__ +#define __HERMES_STD_CONSOLE_REPORTER_H__ + +#include + +namespace hermes +{ + +/** + * @brief Print messages into standard console + */ +struct ConsoleReporter final : public hermes::Sink +{ + void notify(const Message *m) final; +}; + +} // namespace hermes + +#endif // __HERMES_STD_CONSOLE_REPORTER_H__ diff --git a/compiler/hermes-std/include/hermes/EnvConfig.h b/compiler/hermes-std/include/hermes/EnvConfig.h new file mode 100644 index 000000000..e4c392fd6 --- /dev/null +++ b/compiler/hermes-std/include/hermes/EnvConfig.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_STD_ENV_CONFIG_H__ +#define __HERMES_STD_ENV_CONFIG_H__ + +#include + +#include + +namespace hermes +{ + +using EnvName = std::string; + +enum class EnvFormat +{ + // Non-zero -> Enable + // Zero -> Diable + BooleanNumber, +}; + +template class EnvConfig; + +template <> class EnvConfig : public Config +{ +public: + EnvConfig(const EnvName &name); + +public: + virtual ~EnvConfig() = default; + +public: + void configure(const Source *, SourceSetting &) const final; + +private: + bool _enabled = false; +}; + +} // namespace hermes + +#endif // __HERMES_STD_ENV_CONFIG_H__ diff --git a/compiler/hermes-std/requires.cmake b/compiler/hermes-std/requires.cmake new file mode 100644 index 000000000..4aa6b1528 --- /dev/null +++ b/compiler/hermes-std/requires.cmake @@ -0,0 +1 @@ +require("pepper-strcast") diff --git a/compiler/hermes-std/src/ConsoleReporter.cpp b/compiler/hermes-std/src/ConsoleReporter.cpp new file mode 100644 index 000000000..3cc9f09ed --- /dev/null +++ b/compiler/hermes-std/src/ConsoleReporter.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/ConsoleReporter.h" + +#include + +namespace hermes +{ + +void ConsoleReporter::notify(const hermes::Message *m) +{ + for (uint32_t n = 0; n < m->text()->lines(); ++n) + { + std::cout << m->text()->line(n) << std::endl; + } +} + +} // namespace hermes diff --git a/compiler/hermes-std/src/ConsoleReporter.test.cpp b/compiler/hermes-std/src/ConsoleReporter.test.cpp new file mode 100644 index 000000000..c2e1f1c85 --- /dev/null +++ b/compiler/hermes-std/src/ConsoleReporter.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/ConsoleReporter.h" + +#include + +#include + +#include + +TEST(ConsoleReporterTest, constructor) +{ + hermes::ConsoleReporter r; + + SUCCEED(); +} + +TEST(ConsoleReporterTest, notify) +{ + hermes::Message m; + { + std::stringstream ss; + + ss << "Hello" << std::endl; + + m.text(stdex::make_unique(ss)); + } + + hermes::ConsoleReporter r; + + ASSERT_NO_THROW(r.notify(&m)); +} diff --git a/compiler/hermes-std/src/EnvConfig.cpp b/compiler/hermes-std/src/EnvConfig.cpp new file mode 100644 index 000000000..e8f7fcda4 --- /dev/null +++ b/compiler/hermes-std/src/EnvConfig.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/EnvConfig.h" + +#include + +namespace hermes +{ + +EnvConfig::EnvConfig(const EnvName &name) +{ + auto s = std::getenv(name.c_str()); + _enabled = (pepper::safe_strcast(s, 0 /* DISABLE BY DEFAULT */) != 0); +} + +void EnvConfig::configure(const Source *, SourceSetting &setting) const +{ + if (_enabled) + { + // Enable all the sources + setting.accept_all(); + } + else + { + // Disable all the sources + setting.reject_all(); + } +} + +} // namespace hermes diff --git a/compiler/hermes/CMakeLists.txt b/compiler/hermes/CMakeLists.txt new file mode 100644 index 000000000..5debfbca0 --- /dev/null +++ b/compiler/hermes/CMakeLists.txt @@ -0,0 +1,28 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(hermes STATIC ${SOURCES}) +set_target_properties(hermes PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(hermes PUBLIC include) +target_link_libraries(hermes PRIVATE stdex) +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(hermes PRIVATE nncc_common) +target_link_libraries(hermes PUBLIC nncc_coverage) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +add_executable(hermes_test ${TESTS}) +target_link_libraries(hermes_test gtest_main) +target_link_libraries(hermes_test stdex) +target_link_libraries(hermes_test hermes) + +add_test(hermes_test hermes_test) diff --git a/compiler/hermes/README.md b/compiler/hermes/README.md new file mode 100644 index 000000000..c896abf6c --- /dev/null +++ b/compiler/hermes/README.md @@ -0,0 +1,3 @@ +# hermes + +An **extensible** logging framework diff --git a/compiler/hermes/include/hermes.h b/compiler/hermes/include/hermes.h new file mode 100644 index 000000000..13202e621 --- /dev/null +++ b/compiler/hermes/include/hermes.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_H__ +#define __HERMES_H__ + +#include "hermes/core/Severity.h" +#include "hermes/core/Message.h" +#include "hermes/core/Context.h" +// TO BE FILLED + +#endif // __HERMES_H__ diff --git a/compiler/hermes/include/hermes/core/Config.h b/compiler/hermes/include/hermes/core/Config.h new file mode 100644 index 000000000..d937a36b8 --- /dev/null +++ b/compiler/hermes/include/hermes/core/Config.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_CONFIG_H__ +#define __HERMES_CONFIG_H__ + +#include "hermes/core/Severity.h" // TODO Put this into SourceSetting.h +#include "hermes/core/SourceSetting.h" + +namespace hermes +{ + +// TODO Introduce Source.forward.h +class Source; + +/** + * @brief Top-level configuration interface + * + * All Hermes configurations SHOULD inherit this interface. + */ +struct Config +{ + virtual ~Config() = default; + + virtual void configure(const Source *, SourceSetting &) const = 0; +}; + +} // namespace hermes + +#endif // __HERMES_CONFIG_H__ diff --git a/compiler/hermes/include/hermes/core/Context.h b/compiler/hermes/include/hermes/core/Context.h new file mode 100644 index 000000000..4054587a4 --- /dev/null +++ b/compiler/hermes/include/hermes/core/Context.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_CONTEXT_H__ +#define __HERMES_CONTEXT_H__ + +#include "hermes/core/Config.h" +#include "hermes/core/Source.h" +#include "hermes/core/Sink.h" +#include "hermes/core/MessageBus.h" + +#include +#include + +namespace hermes +{ + +/** + * @brief Logging controller + * + * This "Context" serves as a controller for associated logging source/sink. + * + * WARNING This "Context" is not yet thread-safe. + * TODO Support multi-threaded application logging + */ +class Context final : private MessageBus, private Source::Registry, private Sink::Registry +{ +public: + /// @brief Get the global configuration + const Config *config(void) const; + /// @brief Update the global configuration + void config(std::unique_ptr &&); + +public: + MessageBus *bus(void) { return this; } + +private: + /// This implements "post" method that MessageBus interface requires. + void post(std::unique_ptr &&msg) override; + +public: + Source::Registry *sources(void) { return this; } + +private: + /// This implements "attach" method that "Source::Registry" interface requires. + void attach(Source *source) override; + /// This implements "detach" method that "Source::Registry" interface requires. + void detach(Source *source) override; + +public: + Sink::Registry *sinks(void) { return this; } + +private: + /// This implements "append" method that "Sink::Registry" interface requires. + void append(std::unique_ptr &&sink) override; + +private: + std::unique_ptr _config; + std::set _sources; + std::set> _sinks; +}; + +} // namespace hermes + +#endif // __HERMES_CONTEXT_H__ diff --git a/compiler/hermes/include/hermes/core/Message.h b/compiler/hermes/include/hermes/core/Message.h new file mode 100644 index 000000000..28cfd7942 --- /dev/null +++ b/compiler/hermes/include/hermes/core/Message.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_MESSAGE_H__ +#define __HERMES_MESSAGE_H__ + +#include +#include +#include +#include + +namespace hermes +{ + +/** + * @brief Mutie-line text message + */ +class MessageText +{ +public: + /// WARNING! Be careful. This constructor updates "ss". + MessageText(std::stringstream &ss); + +public: + /// @brief The number of lines + uint32_t lines(void) const { return _lines.size(); } + /// @breif The content of a specific line + const std::string &line(uint32_t n) const { return _lines.at(n); } + +private: + std::vector _lines; +}; + +/** + * @brief Message with metadata + * + * TODO Add "Timestamp" field + * TODO Add "Severity" field + * TODO Support extensible "attribute" annotation + */ +class Message final +{ +public: + Message() = default; + +public: + void text(std::unique_ptr &&text) { _text = std::move(text); } + const MessageText *text(void) const { return _text.get(); } + +private: + std::unique_ptr _text; +}; + +} // namespace hermes + +#endif // __HERMES_MESSAGE_H__ diff --git a/compiler/hermes/include/hermes/core/MessageBuffer.h b/compiler/hermes/include/hermes/core/MessageBuffer.h new file mode 100644 index 000000000..a2f1de74d --- /dev/null +++ b/compiler/hermes/include/hermes/core/MessageBuffer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_MESSAGE_BUFFER_H__ +#define __HERMES_MESSAGE_BUFFER_H__ + +#include "hermes/core/MessageBus.h" + +#include +#include + +namespace hermes +{ + +/** + * @brief A buffer for a message under construction + * + * MessageBuffer will post the buffered message on destruction. + */ +class MessageBuffer final +{ +public: + MessageBuffer(MessageBus *); + ~MessageBuffer(); + +public: + std::ostream &os(void) { return _ss; } + +private: + MessageBus *_bus; + + /// @brief Content buffer + std::stringstream _ss; +}; + +} // namespace hermes + +#endif // __HERMES_MESSAGE_BUFFER_H__ diff --git a/compiler/hermes/include/hermes/core/MessageBus.h b/compiler/hermes/include/hermes/core/MessageBus.h new file mode 100644 index 000000000..4ec272352 --- /dev/null +++ b/compiler/hermes/include/hermes/core/MessageBus.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_MESSAGE_BUS_H__ +#define __HERMES_MESSAGE_BUS_H__ + +#include "hermes/core/Message.h" + +#include + +namespace hermes +{ + +/** + * @brief A bridge between Source and Sink + */ +struct MessageBus +{ + virtual ~MessageBus() = default; + + // "post" takes the ownership of posted messages. + virtual void post(std::unique_ptr &&msg) = 0; +}; + +} // namespace hermes + +#endif // __HERMES_MESSAGE_BUS_H__ diff --git a/compiler/hermes/include/hermes/core/Severity.h b/compiler/hermes/include/hermes/core/Severity.h new file mode 100644 index 000000000..25de35d80 --- /dev/null +++ b/compiler/hermes/include/hermes/core/Severity.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_SEVERITY_H__ +#define __HERMES_SEVERITY_H__ + +#include + +namespace hermes +{ + +/** + * FATAL > ERROR > WARN > INFO > VERBOSE + * + * Hermes deliberately declares SeverityCategory as "enum" (instead of "enum class") + * in order to reduce namespace nesting. + */ +enum SeverityCategory : uint16_t +{ + FATAL = 0, + ERROR = 1, + WARN = 2, + INFO = 3, + VERBOSE = 4, +}; + +class Severity final +{ +public: + friend Severity fatal(void); + friend Severity error(void); + friend Severity warn(void); + friend Severity info(void); + friend Severity verbose(uint16_t level); + +private: + /** + * Use below "factory" helpers. + */ + Severity(SeverityCategory cat, uint16_t lvl) : _cat{cat}, _lvl{lvl} + { + // DO NOTHING + } + +public: + const SeverityCategory &category(void) const { return _cat; } + + /** + * @brief Verbose level + * + * "level" is fixed as 0 for all the categories except VERBOSE. + * + * 0 (most significant) <--- level ---> 65535 (least significant) + */ + const uint16_t &level(void) const { return _lvl; } + +private: + SeverityCategory _cat; + uint16_t _lvl; +}; + +inline Severity fatal(void) { return Severity{FATAL, 0}; } +inline Severity error(void) { return Severity{ERROR, 0}; } +inline Severity warn(void) { return Severity{WARN, 0}; } +inline Severity info(void) { return Severity{INFO, 0}; } +inline Severity verbose(uint16_t level) { return Severity{VERBOSE, level}; } + +} // namespace hermes + +#endif // __HERMES_SEVERITY_H__ diff --git a/compiler/hermes/include/hermes/core/Sink.h b/compiler/hermes/include/hermes/core/Sink.h new file mode 100644 index 000000000..f53aff9fc --- /dev/null +++ b/compiler/hermes/include/hermes/core/Sink.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_SINK_H__ +#define __HERMES_SINK_H__ + +#include "hermes/core/Message.h" + +#include + +namespace hermes +{ + +/** + * @brief Message consumer interface + * + * All message consumers should inherit this interface. + */ +struct Sink +{ + struct Registry + { + virtual ~Registry() = default; + + // NOTE SinkRegistry takes the ownership of all the appended Sink objects + virtual void append(std::unique_ptr &&) = 0; + }; + + virtual ~Sink() = default; + + virtual void notify(const Message *) = 0; +}; + +} // namespace hermes + +#endif // __HERMES_SINK_H__ diff --git a/compiler/hermes/include/hermes/core/Source.h b/compiler/hermes/include/hermes/core/Source.h new file mode 100644 index 000000000..b28532b2d --- /dev/null +++ b/compiler/hermes/include/hermes/core/Source.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_SOURCE_H__ +#define __HERMES_SOURCE_H__ + +#include "hermes/core/Config.h" +#include "hermes/core/Severity.h" +#include "hermes/core/MessageBus.h" +#include "hermes/core/MessageBuffer.h" +#include "hermes/core/SourceSetting.h" + +namespace hermes +{ + +/** + * @brief Message Source + * + * "Source" is the actual interface for users. "Source" accepts log messages from client. + */ +class Source +{ +public: + struct Registry + { + virtual ~Registry() = default; + + // NOTE Each "Source" SHOULD outlive "Registry" + virtual void attach(Source *) = 0; + virtual void detach(Source *) = 0; + }; + + // NOTE This using statement is introduced for backward compatibility + // TODO Remove this using declaration after migration + using Setting = SourceSetting; + +protected: + Source(); + virtual ~Source(); + +protected: + // Each "Source" implementation SHOULD invoke activate/deactivate appropriately + void activate(Registry *, MessageBus *); + void deactivate(void); + +protected: + Setting &setting(void) { return _setting; } + +public: + /** + * @brief Check whether a message with a given severity is acceptable or not + * + * + * NOTE This routine is performance critical as app always invokes this routine + * (even when logging is disabled). + */ + inline bool check(const Severity &s) const + { + return static_cast(s.level()) < _setting.limit(s.category()).level(); + } + +public: + /** + * @brief Update Source with a given configuration + * + * WARNING Do NOT invoke this manually. + * + * TODO Remove virtual after migration + */ + virtual void reload(const Config *); + +public: + std::unique_ptr buffer(const Severity &) const; + +private: + Setting _setting; + +private: + Registry *_reg = nullptr; + MessageBus *_bus = nullptr; +}; + +} // namespace hermes + +#define HERMES_FATAL(s) \ + if ((s).check(::hermes::fatal())) \ + (s).buffer(::hermes::fatal())->os() + +#define HERMES_ERROR(s) \ + if ((s).check(::hermes::error())) \ + (s).buffer(::hermes::error())->os() + +#define HERMES_WARN(s) \ + if ((s).check(::hermes::warn())) \ + (s).buffer(::hermes::warn())->os() + +#define HERMES_INFO(s) \ + if ((s).check(::hermes::info())) \ + (s).buffer(::hermes::info())->os() + +#define HERMES_VERBOSE(s, lv) \ + if ((s).check(::hermes::verbose((lv)))) \ + (s).buffer(::hermes::verbose((lv)))->os() + +#endif // __HERMES_SOURCE_H__ diff --git a/compiler/hermes/include/hermes/core/SourceSetting.h b/compiler/hermes/include/hermes/core/SourceSetting.h new file mode 100644 index 000000000..3beaaa196 --- /dev/null +++ b/compiler/hermes/include/hermes/core/SourceSetting.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HERMES_SOURCE_SETTING_H__ +#define __HERMES_SOURCE_SETTING_H__ + +#include +#include + +namespace hermes +{ + +class Filter final +{ +public: + Filter(int32_t *ptr) : _ptr{ptr} + { + // DO NOTHING + } + +public: + inline void reject_all(void) { *_ptr = -1; } + inline void accept_upto(uint16_t lv) { *_ptr = static_cast(lv); } + inline void accept_all(void) { *_ptr = 65536; } + +private: + int32_t *_ptr; +}; + +class Limit final +{ +public: + Limit(const int32_t *ptr) : _ptr{ptr} + { + // DO NOTHING + } + +public: + inline int32_t level(void) const { return *_ptr; } + +private: + const int32_t *_ptr; +}; + +class SourceSetting final +{ +public: + SourceSetting() + { + // Reject all the messages by default + reject_all(); + } + +public: + void reject_all(void) + { + filter(FATAL).reject_all(); + filter(ERROR).reject_all(); + filter(WARN).reject_all(); + filter(INFO).reject_all(); + filter(VERBOSE).reject_all(); + } + + void accept_all(void) + { + filter(FATAL).accept_all(); + filter(ERROR).accept_all(); + filter(WARN).accept_all(); + filter(INFO).accept_all(); + filter(VERBOSE).accept_all(); + } + + inline Filter filter(const SeverityCategory &cat) + { + return _ulimits.data() + static_cast(cat); + } + + inline Limit limit(const SeverityCategory &cat) const + { + return _ulimits.data() + static_cast(cat); + } + +private: + /** + * @brief Allowed message level for each category + * + * This source will accept all the messages whose level belongs to [0, ulimit) + * where ulimit corresdpons to "limit(cat).value()" + */ + std::array _ulimits; +}; + +} // namespace hermes + +#endif // __HERMES_SOURCE_SETTING_H__ diff --git a/compiler/hermes/requires.cmake b/compiler/hermes/requires.cmake new file mode 100644 index 000000000..a4855289c --- /dev/null +++ b/compiler/hermes/requires.cmake @@ -0,0 +1 @@ +require("stdex") diff --git a/compiler/hermes/src/core/Context.cpp b/compiler/hermes/src/core/Context.cpp new file mode 100644 index 000000000..a6970f093 --- /dev/null +++ b/compiler/hermes/src/core/Context.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Context.h" + +#include + +namespace hermes +{ + +const Config *Context::config(void) const +{ + // Return the current configuration + return _config.get(); +} + +void Context::config(std::unique_ptr &&config) +{ + _config = std::move(config); + + // Apply updated configurations + for (auto source : _sources) + { + source->reload(_config.get()); + } +} + +void Context::post(std::unique_ptr &&msg) +{ + // Validate message + assert((msg != nullptr) && "invalid message"); + assert((msg->text() != nullptr) && "missing text"); + + // Take the ownership of a given message + auto m = std::move(msg); + + // Notify appended sinks + for (const auto &sink : _sinks) + { + sink->notify(m.get()); + } + + // TODO Stop the process if "FATAL" message is posted +} + +void Context::attach(Source *source) +{ + // Configure source first + source->reload(config()); + // Insert source + _sources.insert(source); +} + +void Context::detach(Source *source) +{ + // Remove source + _sources.erase(source); +} + +void Context::append(std::unique_ptr &&sink) +{ + // Append sink + _sinks.insert(std::move(sink)); +} + +} // namespace hermes diff --git a/compiler/hermes/src/core/Context.test.cpp b/compiler/hermes/src/core/Context.test.cpp new file mode 100644 index 000000000..0c8defd2a --- /dev/null +++ b/compiler/hermes/src/core/Context.test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Context.h" + +#include + +TEST(ContextTest, constructor) +{ + hermes::Context ctx; + + ASSERT_NE(ctx.bus(), nullptr); + ASSERT_NE(ctx.sources(), nullptr); + ASSERT_NE(ctx.sinks(), nullptr); +} diff --git a/compiler/hermes/src/core/Message.cpp b/compiler/hermes/src/core/Message.cpp new file mode 100644 index 000000000..63fe12b5f --- /dev/null +++ b/compiler/hermes/src/core/Message.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Message.h" + +#include + +namespace hermes +{ + +MessageText::MessageText(std::stringstream &ss) +{ + while (!ss.eof()) + { + assert(ss.good()); + + std::string line; + std::getline(ss, line); + + // Trim the last empty line (by std::endl) + if (ss.eof() && line.empty()) + { + break; + } + + _lines.emplace_back(line); + } +} + +} // namespace hermes diff --git a/compiler/hermes/src/core/Message.test.cpp b/compiler/hermes/src/core/Message.test.cpp new file mode 100644 index 000000000..1db88c711 --- /dev/null +++ b/compiler/hermes/src/core/Message.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Message.h" + +#include + +TEST(MessageTextTest, multiline) +{ + std::stringstream ss; + + ss << "Hello, World" << std::endl; + ss << "Nice to meet you" << std::endl; + + hermes::MessageText text{ss}; + + ASSERT_EQ(text.lines(), 2); + ASSERT_EQ(text.line(0), "Hello, World"); + ASSERT_EQ(text.line(1), "Nice to meet you"); +} + +TEST(MessageTest, ctor) +{ + hermes::Message msg; + + // Text is empty at the beginning + ASSERT_EQ(msg.text(), nullptr); +} diff --git a/compiler/hermes/src/core/MessageBuffer.cpp b/compiler/hermes/src/core/MessageBuffer.cpp new file mode 100644 index 000000000..175a45d3f --- /dev/null +++ b/compiler/hermes/src/core/MessageBuffer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/MessageBuffer.h" + +#include + +namespace hermes +{ + +MessageBuffer::MessageBuffer(MessageBus *bus) : _bus{bus} +{ + // DO NOTHING +} + +MessageBuffer::~MessageBuffer() +{ + // NOTE The current implementation is unsafe as it may throw an excpetion. + // TODO Find a better safe implementation. + auto msg = stdex::make_unique(); + + msg->text(stdex::make_unique(_ss)); + + _bus->post(std::move(msg)); +} + +} // namespace hermes diff --git a/compiler/hermes/src/core/MessageBuffer.test.cpp b/compiler/hermes/src/core/MessageBuffer.test.cpp new file mode 100644 index 000000000..ff08eaa98 --- /dev/null +++ b/compiler/hermes/src/core/MessageBuffer.test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/MessageBuffer.h" + +#include + +#include + +namespace +{ + +class MockMessageBus final : public hermes::MessageBus +{ +public: + MockMessageBus() = default; + +public: + void post(std::unique_ptr &&msg) override + { + _count += 1; + _message = std::move(msg); + } + +public: + uint32_t count(void) const { return _count; } + const hermes::Message *message(void) const { return _message.get(); } + +private: + unsigned _count = 0; + std::unique_ptr _message = nullptr; +}; + +} // namespace + +TEST(MessageBufferTest, pass_constructed_message_on_descturction) +{ + MockMessageBus bus; + + { + hermes::MessageBuffer buf{&bus}; + + buf.os() << "Hello" << std::endl; + buf.os() << "Nice to meet you" << std::endl; + } + + ASSERT_EQ(bus.count(), 1); + ASSERT_NE(bus.message(), nullptr); + ASSERT_NE(bus.message()->text(), nullptr); + ASSERT_EQ(bus.message()->text()->lines(), 2); + ASSERT_EQ(bus.message()->text()->line(0), "Hello"); + ASSERT_EQ(bus.message()->text()->line(1), "Nice to meet you"); +} diff --git a/compiler/hermes/src/core/MessageBus.cpp b/compiler/hermes/src/core/MessageBus.cpp new file mode 100644 index 000000000..05101089e --- /dev/null +++ b/compiler/hermes/src/core/MessageBus.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/MessageBus.h" + +// NOTE This empty file validates "MessageBus.h" diff --git a/compiler/hermes/src/core/Severity.test.cpp b/compiler/hermes/src/core/Severity.test.cpp new file mode 100644 index 000000000..44fb800cb --- /dev/null +++ b/compiler/hermes/src/core/Severity.test.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Severity.h" + +#include + +TEST(SeverityTest, fatal) +{ + auto severity = hermes::fatal(); + + ASSERT_EQ(severity.category(), hermes::FATAL); + ASSERT_EQ(severity.level(), 0); +} + +TEST(SeverityTest, error) +{ + auto severity = hermes::error(); + + ASSERT_EQ(severity.category(), hermes::ERROR); + ASSERT_EQ(severity.level(), 0); +} + +TEST(SeverityTest, warn) +{ + auto severity = hermes::warn(); + + ASSERT_EQ(severity.category(), hermes::WARN); + ASSERT_EQ(severity.level(), 0); +} + +TEST(SeverityTest, info) +{ + auto severity = hermes::info(); + + ASSERT_EQ(severity.category(), hermes::INFO); + ASSERT_EQ(severity.level(), 0); +} + +TEST(SeverityTest, verbose) +{ + auto severity = hermes::verbose(100); + + ASSERT_EQ(severity.category(), hermes::VERBOSE); + ASSERT_EQ(severity.level(), 100); +} diff --git a/compiler/hermes/src/core/Sink.cpp b/compiler/hermes/src/core/Sink.cpp new file mode 100644 index 000000000..1677073b1 --- /dev/null +++ b/compiler/hermes/src/core/Sink.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Sink.h" + +// NOTE This empty file validates "Sink.h" diff --git a/compiler/hermes/src/core/Source.cpp b/compiler/hermes/src/core/Source.cpp new file mode 100644 index 000000000..33f8b0570 --- /dev/null +++ b/compiler/hermes/src/core/Source.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Source.h" + +#include + +#include + +namespace hermes +{ + +Source::Source() +{ + assert(_reg == nullptr); + assert(_bus == nullptr); +} + +Source::~Source() +{ + assert(_bus == nullptr); + assert(_reg == nullptr); +} + +void Source::activate(Registry *reg, MessageBus *bus) +{ + assert((_reg == nullptr) && (_bus == nullptr)); + + _reg = reg; + _bus = bus; + + _reg->attach(this); + + assert((_bus != nullptr) && (_reg != nullptr)); +} + +void Source::deactivate(void) +{ + assert((_bus != nullptr) && (_reg != nullptr)); + + _reg->detach(this); + + _bus = nullptr; + _reg = nullptr; + + assert((_reg == nullptr) && (_bus == nullptr)); +} + +void Source::reload(const Config *c) { c->configure(this, _setting); } + +std::unique_ptr Source::buffer(const Severity &) const +{ + // TODO Pass Severity + return stdex::make_unique(_bus); +} + +} // namespace hermes diff --git a/compiler/hermes/src/core/Source.test.cpp b/compiler/hermes/src/core/Source.test.cpp new file mode 100644 index 000000000..f98a64509 --- /dev/null +++ b/compiler/hermes/src/core/Source.test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes/core/Source.h" + +#include + +namespace +{ + +struct MockSourceRegistry final : public hermes::Source::Registry +{ + void attach(hermes::Source *) override { return; } + void detach(hermes::Source *) override { return; } +}; + +struct MockMessageBus final : public hermes::MessageBus +{ + void post(std::unique_ptr &&msg) override + { + msg.reset(); + ++cnt; + } + + uint32_t cnt = 0; +}; + +struct MockSource final : public hermes::Source +{ + MockSource(hermes::Source::Registry *r, hermes::MessageBus *b) { activate(r, b); } + ~MockSource() { deactivate(); } + + void reload(const hermes::Config *) override { return; } + + void enable(void) { setting().accept_all(); } +}; + +} // namespace + +TEST(SourceTest, construct) +{ + MockSourceRegistry registry; + MockMessageBus bus; + + MockSource source{®istry, &bus}; + + // Source are off at the beginning + ASSERT_FALSE(source.check(::hermes::fatal())); + ASSERT_FALSE(source.check(::hermes::error())); + ASSERT_FALSE(source.check(::hermes::warn())); + ASSERT_FALSE(source.check(::hermes::info())); + ASSERT_FALSE(source.check(::hermes::verbose(100))); +} + +TEST(SourceTest, macro) +{ + MockSourceRegistry registry; + + MockMessageBus bus; + + MockSource source{®istry, &bus}; + + source.enable(); + + uint32_t expected_count = 0; + + // No message at the beginning + ASSERT_EQ(bus.cnt, 0); + + HERMES_ERROR(source) << "A"; + ASSERT_EQ(bus.cnt, ++expected_count); + + HERMES_WARN(source) << "A"; + ASSERT_EQ(bus.cnt, ++expected_count); + + HERMES_INFO(source) << "A"; + ASSERT_EQ(bus.cnt, ++expected_count); + + HERMES_VERBOSE(source, 100) << "A"; + ASSERT_EQ(bus.cnt, ++expected_count); + +// FATAL message should terminate the execution. Let's check how to check this! +// TODO Enable FATAL feature and enable this test +#if 0 + HERMES_FATAL(source) << "A"; + ASSERT_EQ(bus.cnt, 1); +#endif +} diff --git a/compiler/hermes/src/hermes.cpp b/compiler/hermes/src/hermes.cpp new file mode 100644 index 000000000..048521a32 --- /dev/null +++ b/compiler/hermes/src/hermes.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes.h" + +// NOTE This empty file validates "hermes.h" diff --git a/compiler/hermes/src/hermes.test.cpp b/compiler/hermes/src/hermes.test.cpp new file mode 100644 index 000000000..2cbc0939d --- /dev/null +++ b/compiler/hermes/src/hermes.test.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hermes.h" + +#include + +TEST(HermesTest, simple_usecase) +{ + // TO BE FILLED +} diff --git a/compiler/i5diff/CMakeLists.txt b/compiler/i5diff/CMakeLists.txt new file mode 100644 index 000000000..321ae49a0 --- /dev/null +++ b/compiler/i5diff/CMakeLists.txt @@ -0,0 +1,15 @@ +find_package(HDF5 COMPONENTS CXX QUIET) + +if(NOT HDF5_FOUND) + return() +endif(NOT HDF5_FOUND) + +message(STATUS "Enable i5diff: TRUE") + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(i5diff ${SOURCES}) +target_include_directories(i5diff PRIVATE ${HDF5_INCLUDE_DIRS}) +target_link_libraries(i5diff PRIVATE ${HDF5_CXX_LIBRARIES}) +target_link_libraries(i5diff PRIVATE angkor) +target_link_libraries(i5diff PRIVATE safemain) diff --git a/compiler/i5diff/README.md b/compiler/i5diff/README.md new file mode 100644 index 000000000..35d81884a --- /dev/null +++ b/compiler/i5diff/README.md @@ -0,0 +1,20 @@ +# i5diff + +_i5diff_ compares two HDF5 files that _nnkit_ HDF5 export action generates. + +**DISCLAIMER** _i5diff_ is not designed as a general diff tool. +It works only for HDF5 files that _nnkit_ HDF5 export action generates. + +## Yet Another Diff? + +_i5diff_ is able to detect _shape mismatch_ that _h5diff_ cannot detect. + +To be precise, _h5diff_ is also able to detect _shape mismatch_. +Unfortunately, however, _h5diff_ ends with 0 exitcode in the presence of _shape mismatch_, and thus +it is impossible to use _h5diff_ for continuous integration. + +## How to use + +``` +$ /path/to/i5diff -d 0.001 /path/to/fst.h5 /path/to/snd.h5 +``` diff --git a/compiler/i5diff/requires.cmake b/compiler/i5diff/requires.cmake new file mode 100644 index 000000000..a6222db76 --- /dev/null +++ b/compiler/i5diff/requires.cmake @@ -0,0 +1,2 @@ +require("angkor") +require("safemain") diff --git a/compiler/i5diff/src/entry.cpp b/compiler/i5diff/src/entry.cpp new file mode 100644 index 000000000..456467f54 --- /dev/null +++ b/compiler/i5diff/src/entry.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +enum class ErrorCode +{ + CountMismatch, + TypeMismatch, + ShapeMismatch, + ValueMismatch, +}; + +template class ErrorDetail; + +// TODO Record the details +template <> class ErrorDetail +{ +public: + ErrorDetail() = default; +}; + +// TODO Record the details +template <> class ErrorDetail +{ +public: + ErrorDetail() = default; +}; + +// TODO Record the details +template <> class ErrorDetail +{ +public: + ErrorDetail() = default; +}; + +// TODO Record the details +template <> class ErrorDetail +{ +public: + ErrorDetail() = default; +}; + +struct Observer +{ + virtual ~Observer() = default; + + virtual void notify(const ErrorDetail &) = 0; + virtual void notify(const ErrorDetail &) = 0; + virtual void notify(const ErrorDetail &) = 0; + virtual void notify(const ErrorDetail &) = 0; +}; + +class Mux final : public Observer +{ +public: + Mux() = default; + +public: + void attach(Observer *o) { _observers.insert(o); } + +private: + template void notify_all(const ErrorDetail &e) + { + for (auto o : _observers) + { + o->notify(e); + } + } + +public: + void notify(const ErrorDetail &e) final { notify_all(e); } + void notify(const ErrorDetail &e) final { notify_all(e); } + void notify(const ErrorDetail &e) final { notify_all(e); } + void notify(const ErrorDetail &e) final { notify_all(e); } + +public: + std::set _observers; +}; + +class ExitcodeTracker final : public Observer +{ +public: + const int &exitcode(void) const { return _exitcode; } + +public: + void notify(const ErrorDetail &) { _exitcode = 1; } + void notify(const ErrorDetail &) { _exitcode = 1; } + void notify(const ErrorDetail &) { _exitcode = 1; } + void notify(const ErrorDetail &) { _exitcode = 1; } + +public: + int _exitcode = 0; +}; + +} // namespace + +// +// HDF5 helpers +// +namespace +{ + +enum class DataType +{ + UNKNOWN, + FLOAT32, + /* TO BE ADDED */ +}; + +DataType to_internal_dtype(const H5::DataType &dtype) +{ + if (dtype == H5::PredType::IEEE_F32BE) + { + return DataType::FLOAT32; + } + return DataType::UNKNOWN; +} + +using TensorShape = nncc::core::ADT::tensor::Shape; + +TensorShape to_internal_shape(const H5::DataSpace &dataspace) +{ + int rank = dataspace.getSimpleExtentNdims(); + + std::vector dims; + + dims.resize(rank, 0); + + dataspace.getSimpleExtentDims(dims.data()); + + TensorShape res; + + res.resize(rank); + for (int axis = 0; axis < rank; ++axis) + { + res.dim(axis) = dims[axis]; + } + + return res; +} + +uint32_t element_count(const H5::DataSpace &dataspace) +{ + return nncc::core::ADT::tensor::num_elements(to_internal_shape(dataspace)); +} + +std::vector as_float_vector(const H5::DataSet &dataset) +{ + std::vector buffer; + + buffer.resize(element_count(dataset.getSpace())); + dataset.read(buffer.data(), H5::PredType::NATIVE_FLOAT); + + return buffer; +} + +using LexicalLayout = nncc::core::ADT::tensor::LexicalLayout; +using TensorIndexEnumerator = nncc::core::ADT::tensor::IndexEnumerator; + +} // namespace + +// TODO Report the details +int entry(int argc, char **argv) +{ + // The current implementation works only for command-line of the following form: + // + // i5diff -d 0.001 /path/to/left.h5 /path/to/right.h5 + // + // TODO Support more options + assert(argc == 5); + assert(std::string(argv[1]) == "-d"); + assert(std::string(argv[2]) == "0.001"); + + H5::H5File lhs{argv[3], H5F_ACC_RDONLY}; + H5::H5File rhs{argv[4], H5F_ACC_RDONLY}; + + ExitcodeTracker exitcode_tracker; + + Mux mux; + mux.attach(&exitcode_tracker); + + // Compare values + do + { + // NOTE The name of value group SHOULD BE aligned with nnkit HDF5 actions + const std::string value_grpname{"value"}; + + H5::Group lhs_value_grp = lhs.openGroup(value_grpname); + H5::Group rhs_value_grp = rhs.openGroup(value_grpname); + + // Compare value count + int64_t value_count = -1; + { + uint32_t lhs_value_count = static_cast(lhs_value_grp.getNumObjs()); + uint32_t rhs_value_count = static_cast(rhs_value_grp.getNumObjs()); + + if (lhs_value_count != rhs_value_count) + { + ErrorDetail error{}; + mux.notify(error); + break; + } + + value_count = std::max(lhs_value_count, rhs_value_count); + } + assert(value_count >= 0); + + // Compare each dataset + for (int64_t n = 0; n < value_count; ++n) + { + // NOTE The name of dataset SHOULD BE aligned with nnkit HDF5 actions + const std::string dataset_name = std::to_string(n); + + auto lhs_dataset = lhs_value_grp.openDataSet(dataset_name); + auto rhs_dataset = rhs_value_grp.openDataSet(dataset_name); + + auto lhs_dtype = to_internal_dtype(lhs_dataset.getDataType()); + auto rhs_dtype = to_internal_dtype(rhs_dataset.getDataType()); + + // TODO Support other data types + assert(rhs_dtype == DataType::FLOAT32); + assert(lhs_dtype == DataType::FLOAT32); + + if (lhs_dtype != rhs_dtype) + { + ErrorDetail error{}; + mux.notify(error); + continue; + } + + auto lhs_shape = to_internal_shape(lhs_dataset.getSpace()); + auto rhs_shape = to_internal_shape(rhs_dataset.getSpace()); + + if (!(lhs_shape == rhs_shape)) + { + ErrorDetail error{}; + mux.notify(error); + continue; + } + + assert(lhs_shape == rhs_shape); + assert(lhs_dtype == rhs_dtype); + const auto &shape = lhs_shape; + const auto &dtype = lhs_dtype; + + switch (dtype) + { + case DataType::FLOAT32: + { + auto lhs_vector = as_float_vector(lhs_dataset); + auto rhs_vector = as_float_vector(rhs_dataset); + + assert(lhs_vector.size() == rhs_vector.size()); + + LexicalLayout layout; + + for (TensorIndexEnumerator e{shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + auto lhs_value = lhs_vector.at(layout.offset(shape, ind)); + auto rhs_value = rhs_vector.at(layout.offset(shape, ind)); + + // TODO Abstract equality criterion + if (std::abs(lhs_value - rhs_value) >= 0.001f) + { + ErrorDetail error{}; + mux.notify(error); + continue; + } + } + + break; + } + default: + throw std::runtime_error{"Not supported, yet"}; + }; + } + } while (false); + + // TODO Compare names (if requested) + + return exitcode_tracker.exitcode(); +} diff --git a/compiler/kuma/CMakeLists.txt b/compiler/kuma/CMakeLists.txt new file mode 100644 index 000000000..e705bfedb --- /dev/null +++ b/compiler/kuma/CMakeLists.txt @@ -0,0 +1,19 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(kuma STATIC ${SOURCES}) +set_target_properties(kuma PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(kuma PUBLIC include) +target_link_libraries(kuma PRIVATE nncc_common) +target_link_libraries(kuma PUBLIC nncc_coverage) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for test +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(kuma_test ${TESTS}) +target_link_libraries(kuma_test kuma) diff --git a/compiler/kuma/README.md b/compiler/kuma/README.md new file mode 100644 index 000000000..7e5123968 --- /dev/null +++ b/compiler/kuma/README.md @@ -0,0 +1,7 @@ +# kuma + +_kuma_ is a collection of offline memory allocators. + +## What does "kuma" mean? + +_kuma_ originates from _cooma_ which is an abbreviation of **C**ollection **O**f **O**ffline **M**emory **A**lloators. diff --git a/compiler/kuma/include/kuma.h b/compiler/kuma/include/kuma.h new file mode 100644 index 000000000..a3d9a2e91 --- /dev/null +++ b/compiler/kuma/include/kuma.h @@ -0,0 +1,98 @@ +/* + * 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 __KUMA_H__ +#define __KUMA_H__ + +#include +#include + +namespace kuma +{ + +// Supported algorithms +enum Algorithm +{ + // No reuse + Greedy, + LinearScanFirstFit, +}; + +/** + * Each algorithm defines its own context. The context describes its in and out. + */ +template class Context; + +using ItemID = uint32_t; +using ItemSize = uint32_t; + +using MemoryOffset = uint32_t; +using MemorySize = uint32_t; + +// +// Greedy Algorithm +// +template <> class Context +{ +public: + virtual ~Context() = default; + +public: // Inputs + // count() returns the number of items to be allocated + virtual uint32_t item_count(void) const = 0; + + // size(N) returns the size of the N-th item + virtual ItemSize item_size(const ItemID &) const = 0; + +public: // Outputs + virtual void mem_offset(const ItemID &, const MemoryOffset &) = 0; + virtual void mem_total(const MemorySize &) = 0; +}; + +void solve(Context *); + +// +// Linear Scan First-Fit Algorithm +// +template <> class Context +{ +public: + virtual ~Context() = default; + +public: // Inputs + // count() returns the number of items to be allocated + virtual uint32_t item_count(void) const = 0; + + // size(N) returns the size of the N-th item + virtual ItemSize item_size(const ItemID &) const = 0; + + // conflict_with(N) returns all the items that are in conflict with item N + // - An item N is said to be in conflict with item M if item M and N cannot have overlap + // + // NOTE + // - conflict_with(N) SHOULD NOT include N itself + virtual std::set conflict_with(const ItemID &) const = 0; + +public: // Outputs + virtual void mem_offset(const ItemID &, const MemoryOffset &) = 0; + virtual void mem_total(const MemorySize &) = 0; +}; + +void solve(Context *); + +} // namespace kuma + +#endif // __KUMA_H__ diff --git a/compiler/kuma/src/IntervalSet.cpp b/compiler/kuma/src/IntervalSet.cpp new file mode 100644 index 000000000..f6790c654 --- /dev/null +++ b/compiler/kuma/src/IntervalSet.cpp @@ -0,0 +1,92 @@ +/* + * 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 "IntervalSet.h" + +#include +#include + +namespace kuma +{ +namespace details +{ + +IntervalSet::IntervalSet(uint32_t len) +{ + // Update _content + _content[len] = len; +} + +void IntervalSet::insert(const IntervalMask &m) +{ + auto s = m.s; + auto e = m.e; + + assert(s <= e); + + if (s == e) + { + // Empty region, nothing to do + return; + } + + // lower_bound() returns an iterator to the first element not less than the given key + auto lb = _content.lower_bound(s); + + // NOTE 1. "lower_bound" ensures "prev_s < s <= curr_e" + // NOTE 2. "e" points to somewhere after "s" + auto curr_s = lb->first - lb->second; + auto curr_e = lb->first; + + if (curr_s < s) + { + // Split the current interval + _content[s] = s - curr_s; + // NOTE The invariant over "_content" is temporarily broken here. + } + + if (e < curr_e) + { + // Adjust the current interval + _content[curr_e] = curr_e - e; + } + else + { + // Remove the current interval + _content.erase(curr_e); + // Check the next interval (e > curr_e) + // + // TODO Remove this recursive call (to prevent stack overflow issue) + insert(mask(curr_e, e)); + } +} + +uint32_t IntervalSet::firstfit(uint32_t len) const +{ + for (auto it = _content.begin(); it != _content.end(); ++it) + { + if (it->second >= len) + { + // Got it! This interval is larger than "len". + return it->first - it->second; + } + } + + throw std::runtime_error{"infeasible"}; +} + +} // namespace details +} // namespace kuma diff --git a/compiler/kuma/src/IntervalSet.h b/compiler/kuma/src/IntervalSet.h new file mode 100644 index 000000000..3b6c5f666 --- /dev/null +++ b/compiler/kuma/src/IntervalSet.h @@ -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. + */ + +#ifndef __KUMA_DETAILS_LIVE_INTERVAL_SET_H__ +#define __KUMA_DETAILS_LIVE_INTERVAL_SET_H__ + +#include + +namespace kuma +{ +namespace details +{ + +struct IntervalMask +{ + uint32_t s; + uint32_t e; +}; + +inline IntervalMask mask(uint32_t s, uint32_t e) +{ + IntervalMask mask; + + mask.s = s; + mask.e = e; + + return mask; +} + +class IntervalSet +{ +public: + // [0, len) is live at the beginning + IntervalSet(uint32_t len = 0xffffffff); + +public: + void insert(const IntervalMask &); + + /** + * "firstfit(l)" returns the offset of an interval whose length is larger than "l". + * + * When multiple intervals meet this condition, "firstfit(l)" chooses the interval + * with the smallest offset as its name suggests. + * + * NOTE This method throws std::runtime_error if fails to find a proper region + */ + uint32_t firstfit(uint32_t len) const; + +private: + using End = uint32_t; + using Len = uint32_t; + + // If [e -> l] is in _content, it means that [e - l, e) is a valid interval. + // + // INVARIANT + // + // If key m and n (m <= n) are consecutive in _content, "m <= n - _content.at(n)" holds. + // + std::map _content; +}; + +} // namespace details +} // namespace kuma + +#endif // __KUMA_DETAILS_LIVE_INTERVAL_SET_H__ diff --git a/compiler/kuma/src/IntervalSet.test.cpp b/compiler/kuma/src/IntervalSet.test.cpp new file mode 100644 index 000000000..848ddee03 --- /dev/null +++ b/compiler/kuma/src/IntervalSet.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 "IntervalSet.h" + +#include + +using namespace kuma::details; + +TEST(IntervalSetTests, mask_and_firstfit) +{ + IntervalSet intervals; + + // Exclude [0, 16) from available region + intervals.insert(mask(0, 16)); + + ASSERT_EQ(intervals.firstfit(4), 16); +} diff --git a/compiler/kuma/src/kuma.cpp b/compiler/kuma/src/kuma.cpp new file mode 100644 index 000000000..6fad96b26 --- /dev/null +++ b/compiler/kuma/src/kuma.cpp @@ -0,0 +1,93 @@ +/* + * 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 "kuma.h" + +// +// Greedy Allocation Algorithm +// +namespace kuma +{ + +void solve(Context *ctx) +{ + uint32_t next = 0; + + for (uint32_t n = 0; n < ctx->item_count(); ++n) + { + ctx->mem_offset(n, next); + next += ctx->item_size(n); + } + + ctx->mem_total(next); +}; + +} // namespace kuma + +// +// Linear Scan First Fit Algorithm +// +#include "IntervalSet.h" + +namespace kuma +{ + +void solve(Context *ctx) +{ + using namespace kuma::details; + + uint32_t upper_bound = 0; + std::map> committed_items; + + // Allocate items in linear order (from item 0, item 1, ...) + // + // The implementor of Context is responsible for item ordering. + for (uint32_t n = 0; n < ctx->item_count(); ++n) + { + IntervalSet intervals; + + for (auto item_in_conflict : ctx->conflict_with(n)) + { + auto it = committed_items.find(item_in_conflict); + + // Skip if item_in_conflict is not committed yet + if (it == committed_items.end()) + { + continue; + } + + auto const alloc_s = it->second.first; + auto const alloc_e = it->second.second; + intervals.insert(mask(alloc_s, alloc_e)); + } + + uint32_t const item_size = ctx->item_size(n); + uint32_t const item_alloc_s = intervals.firstfit(item_size); + uint32_t const item_alloc_e = item_alloc_s + item_size; + + // Notify "mem_offset" + ctx->mem_offset(n, item_alloc_s); + + // Update "upper bound" and commit allocation + upper_bound = std::max(upper_bound, item_alloc_e); + committed_items[n] = std::make_pair(item_alloc_s, item_alloc_e); + } + + // Notify "mem_total" + ctx->mem_total(upper_bound); +} + +} // namespace kuma diff --git a/compiler/kuma/src/kuma.test.cpp b/compiler/kuma/src/kuma.test.cpp new file mode 100644 index 000000000..5d947ea6b --- /dev/null +++ b/compiler/kuma/src/kuma.test.cpp @@ -0,0 +1,89 @@ +/* + * 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 "kuma.h" + +#include + +using namespace kuma; + +TEST(GreedyAlgorithmTests, empty) +{ + struct ContextImpl : public Context + { + uint32_t item_count(void) const final { return 0; } + ItemSize item_size(const ItemID &) const final { throw std::runtime_error{"error"}; } + + void mem_offset(const ItemID &, const MemoryOffset &) { throw std::runtime_error{"error"}; }; + void mem_total(const MemorySize &total) final { _total = total; } + + uint32_t _total = 0xffffffff; + }; + + ContextImpl ctx; + + solve(&ctx); + + ASSERT_EQ(ctx._total, 0); +} + +TEST(LinearScanFirstFitTests, reuse) +{ + struct ContextImpl : public Context + { + uint32_t item_count(void) const final { return 3; } + ItemSize item_size(const ItemID &) const final { return 4; } + + std::set conflict_with(const ItemID &id) const + { + // 0 <-> 1 <-> 2 + switch (id) + { + case 0: + return std::set({1}); + case 1: + return std::set({0, 2}); + case 2: + return std::set({1}); + default: + break; + }; + + throw std::runtime_error{"Invalid"}; + } + + void mem_offset(const ItemID &id, const MemoryOffset &offset) { _offsets[id] = offset; }; + void mem_total(const MemorySize &total) final { _total = total; } + + uint32_t _offsets[3]; + uint32_t _total = 0xffffffff; + }; + + ContextImpl ctx; + + solve(&ctx); + + // EXPECTED MEMORY LAYOUT: + // ------------------ 0 + // | ITEM 0, ITEM 2 | + // ------------------ 4 + // | ITEM 1 | + // ------------------ 8 + ASSERT_EQ(ctx._total, 8); + ASSERT_EQ(ctx._offsets[0], 0); + ASSERT_EQ(ctx._offsets[1], 4); + ASSERT_EQ(ctx._offsets[2], 0); +} diff --git a/compiler/loco/CMakeLists.txt b/compiler/loco/CMakeLists.txt new file mode 100644 index 000000000..f94052840 --- /dev/null +++ b/compiler/loco/CMakeLists.txt @@ -0,0 +1,28 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(loco SHARED ${SOURCES}) +target_include_directories(loco PUBLIC include) +# TODO Remove dependencies on angkor library +target_link_libraries(loco PUBLIC angkor) +target_link_libraries(loco PRIVATE stdex) +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(loco PRIVATE nncc_common) +target_link_libraries(loco PUBLIC nncc_coverage) +# Q. HOW TO MAKE DEV PACKAGE(?) +install(TARGETS loco DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(loco_test ${TESTS}) +target_link_libraries(loco_test stdex) +target_link_libraries(loco_test loco) diff --git a/compiler/loco/README.md b/compiler/loco/README.md new file mode 100644 index 000000000..a388d8e48 --- /dev/null +++ b/compiler/loco/README.md @@ -0,0 +1,3 @@ +# loco + +_loco_ is a graph-based intermediate representation (IR) for neural network compilers. diff --git a/compiler/loco/doc/LEP_000_Dialect_Service.md b/compiler/loco/doc/LEP_000_Dialect_Service.md new file mode 100644 index 000000000..f6f6dc809 --- /dev/null +++ b/compiler/loco/doc/LEP_000_Dialect_Service.md @@ -0,0 +1,116 @@ +# Dialect Service + +This loco enhancement proposal (_LEP_) discusses how to permit a _loco_ graph without canonical dialect. + +## Revision + +| Date | Status | +| --- | --- | +| 2019/09/03 | Proposed | + +## Motivation + +One of key design principles behind _loco_ is to allow users (= NN compiler writers) to easily define their own intermediate representation (IR) on top of shared infrastructure. + +Unfortunately, however, there is a gap between dream and reality. +It is currently impossible to create a _loco_ graph only with non-canonical dialects; +there is no way to express the interaction between graph-level output without _canonical.Push_ node. + +This proposal aims to remove this restriction in order to bridge the gap between dream and reality. + +## Design + +Each dialect is now allowed to expose its internal to its client (such as transformations and core algorithms) through a so-called "Service" interface. + +Although this proposal focuses on ``output_nodes`` helper in _loco.core_, its coverage is not limited to this helper. +Any pass and algorithm can take an advantage of this generic infrastructure. + +Let us dive into some details. + +### What is "service"? + +A service declares a collection of APIs that each **client** (not dialect) needs. + +Let us consider ``output_nodes``. ``output_nodes`` needs to check whether a node is associated with any graph-level output. + +Here is one possible service design that satisfies this need. +```cxx +virtual bool associated(const Node *node) const = 0; +virtual GraphOutputIndex index(const Node *node) const = 0; +``` + +### How to declare a service + +All of these service interfaces should inherit ``loco::DialectService`` interface that _loco.core_ defines. +```cxx +struct DialectService +{ + virtual ~DialectService() = default; +}; +``` + +For example, it is possible to declare the service that ``output_nodes`` needs as follows: +```cxx +struct GraphOutputIndexQueryService : public DialectService +{ + virtual ~GraphOutputIndexQueryService() = default; + + virtual bool associated(const Node *node) const = 0; + virtual GraphOutputIndex index(const Node *node) const = 0; +}; +``` + +### How to access a service + +This proposal extends ``Dialect`` class with ``service`` method. + +Each dialect SHOULD return a valid pointer on ``service`` method call if it implements that service. Otherwise, it SHOULD return a null pointer otherwise. + +**WARNING** It is impossible to use ``get``. ``get`` is currently reserved for singleton accessor. + +Given a ``GraphOutputIndexQueryService``, it is possible to revise ``output_nodes`` as follows: +```cxx +std::vector output_nodes(loco::Graph *g) +{ + std::map table; + + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + auto node = g->nodes()->at(n); + + if (auto service = node->dialect()->service()) + { + if (service->associated(node)) + { + auto output_index = service->index(node); + assert(table.find(output_index) == table.end()); + table[output_index] = node; + } + } + } + + std::vector res; + + for (uint32_t n = 0; n < g->outputs()->size(); ++n) + { + auto it = table.find(n); + // NOTE This behavior originates from the current implementation of output_nodes + res.emplace_back(it == table.end() ? nullptr : it->second); + } + + return res; +} +``` + +**PLEASE NOTE THAT** ``output_nodes`` now works with all the dialects that implement ``GraphOutputIndexQueryService``. + +### How to register a service + +Each dialect should invoke protected ``service`` method during its construction. +```cxx +AwesomeDialect::AwesomeDialect() +{ + std::unique_ptr impl = ...; + service(std::move(impl)); +} +``` diff --git a/compiler/loco/include/loco.h b/compiler/loco/include/loco.h new file mode 100644 index 000000000..5cc4487ea --- /dev/null +++ b/compiler/loco/include/loco.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_H__ +#define __LOCO_H__ + +#include "loco/IR/Graph.h" +#include "loco/IR/Algorithm.h" +#include "loco/IR/Verifier.h" + +#include "loco/IR/PermutingCodec.h" + +#endif // __LOCO_H__ diff --git a/compiler/loco/include/loco/ADT/AnnotatedItem.h b/compiler/loco/include/loco/ADT/AnnotatedItem.h new file mode 100644 index 000000000..be0d9ac1d --- /dev/null +++ b/compiler/loco/include/loco/ADT/AnnotatedItem.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_ADT_ANNOTATED_ITEM_H__ +#define __LOCO_ADT_ANNOTATED_ITEM_H__ + +#include +#include +#include + +namespace loco +{ + +template class AnnotatedItem +{ +public: + AnnotatedItem() = default; + +public: + virtual ~AnnotatedItem() = default; + +public: + /** + * @brief Retrieve a stored annotation of type T + * + * @note This method returns nullptr if annotation does not exist + */ + template const T *annot(void) const + { + // TODO Insert static_assert(T derives Annotation); + + auto it = _attrs.find(typeid(T)); + + if (it == _attrs.end()) + { + return nullptr; + } + + // TODO Insert null check + return dynamic_cast(it->second.get()); + } + + /** + * @brief Attach or remove a new annotation of type T + * + * @note annot(nullptr) removes an attached annotation if it exists + */ + template void annot(std::unique_ptr &&p) + { + // TODO: Insert static_assert(T derives Annotation); + + if (p == nullptr) + { + _attrs.erase(typeid(T)); + } + else + { + // TODO: assert(_attribs.find(typeid(T)) == _attribs.end()); + _attrs[typeid(T)] = std::move(p); + } + } + +private: + std::map> _attrs; +}; + +} // namespace loco + +#endif // __LOCO_ADT_ANNOTATED_ITEM_H__ diff --git a/compiler/loco/include/loco/ADT/ObjectPool.h b/compiler/loco/include/loco/ADT/ObjectPool.h new file mode 100644 index 000000000..3f3a25c16 --- /dev/null +++ b/compiler/loco/include/loco/ADT/ObjectPool.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_ADT_OBJECT_POOL_H__ +#define __LOCO_ADT_OBJECT_POOL_H__ + +#include +#include +#include + +namespace loco +{ + +/** + * @brief Object Pool + * @note ObjectPool owns registered objects. + */ +template class ObjectPool +{ +public: + virtual ~ObjectPool() = default; + +public: + /// @brief Return the number of objects + uint32_t size(void) const { return _pool.size(); } + + /// @brief Access N-th object + T *at(uint32_t n) const { return _pool.at(n).get(); } + +protected: + /// @brief Take the ownership of a given object and returns its raw pointer + template U *take(std::unique_ptr &&o) + { + auto res = o.get(); + _pool.emplace_back(std::move(o)); + return res; + } + + /** + * @brief Erase an object from the pool + * + * erase(p) returns false if p does not belong to this object pool. + */ + bool erase(T *ptr) + { + auto pred = [ptr](const std::unique_ptr &o) { return o.get() == ptr; }; + auto it = std::find_if(_pool.begin(), _pool.end(), pred); + + if (it == _pool.end()) + { + return false; + } + + _pool.erase(it); + return true; + } + +private: + std::vector> _pool; +}; + +} // namespace loco + +#endif // __LOCO_ADT_OBJECT_POOL_H__ diff --git a/compiler/loco/include/loco/IR/Algorithm.h b/compiler/loco/include/loco/IR/Algorithm.h new file mode 100644 index 000000000..f7812e85d --- /dev/null +++ b/compiler/loco/include/loco/IR/Algorithm.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_ALGORITHM_H__ +#define __LOCO_IR_ALGORITHM_H__ + +#include "loco/IR/Node.h" + +#include +#include + +namespace loco +{ + +/** + * @brief Generate postorder traversal sequence starting from "roots" + * + * HOW TO USE + * + * for (auto node : postorder_traversal(...)) + * { + * ... node->do_something() ... + * } + * + */ +std::vector postorder_traversal(const std::vector &roots); + +/** + * @brief Enumerate all the nodes required to compute "roots" + */ +std::set active_nodes(const std::vector &roots); + +} // namespace loco + +#endif // __LOCO_IR_ALGORITHM_H__ diff --git a/compiler/loco/include/loco/IR/BiasShape.h b/compiler/loco/include/loco/IR/BiasShape.h new file mode 100644 index 000000000..037b0873e --- /dev/null +++ b/compiler/loco/include/loco/IR/BiasShape.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_BIAS_SHAPE_H__ +#define __LOCO_IR_BIAS_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Bias Shape + */ +class BiasShape final +{ +public: + BiasShape() = default; + +public: + const Dimension &length(void) const { return _length; } + Dimension &length(void) { return _length; } + +private: + Dimension _length; +}; + +} // namespace loco + +#endif // __LOCO_IR_BIAS_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalDialect.h b/compiler/loco/include/loco/IR/CanonicalDialect.h new file mode 100644 index 000000000..940d29a59 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalDialect.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_DIALECT_H__ +#define __LOCO_IR_CANONICAL_DIALECT_H__ + +#include "loco/IR/Dialect.h" + +namespace loco +{ + +/** + * @brief A singleton for Canonical Dialect + * + * CanonicalDialect serves as an in-memory unique identifier. + */ +class CanonicalDialect final : public Dialect +{ +private: + CanonicalDialect(); + +public: + CanonicalDialect(const CanonicalDialect &) = delete; + CanonicalDialect(CanonicalDialect &&) = delete; + +public: + static Dialect *get(void); +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_DIALECT_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNode.h b/compiler/loco/include/loco/IR/CanonicalNode.h new file mode 100644 index 000000000..2dcc02e5d --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_NODE_H__ +#define __LOCO_IR_CANONICAL_NODE_H__ + +#include "loco/IR/CanonicalNodeDecl.h" +#include "loco/IR/CanonicalNodeImpl.h" + +#endif // __LOCO_IR_CANONICAL_NODE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeDecl.h b/compiler/loco/include/loco/IR/CanonicalNodeDecl.h new file mode 100644 index 000000000..872edbb3e --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeDecl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_NODE_DECL_H__ +#define __LOCO_IR_CANONICAL_NODE_DECL_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/CanonicalOpcode.h" +#include "loco/IR/CanonicalNodeVisitor.forward.h" + +namespace loco +{ + +struct CanonicalNode : public Node +{ + virtual ~CanonicalNode() = default; + + const Dialect *dialect(void) const final; + virtual CanonicalOpcode opcode(void) const = 0; + + template T accept(CanonicalNodeVisitorBase *) const; + template T accept(CanonicalNodeMutableVisitorBase *); +}; + +template class... Mixins> +struct CanonicalNodeDef : public virtual CanonicalNode, public Mixins... +{ + virtual ~CanonicalNodeDef() = default; + + uint32_t opnum(void) const final { return static_cast(Code); } + CanonicalOpcode opcode(void) const final { return Code; } +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeImpl.h b/compiler/loco/include/loco/IR/CanonicalNodeImpl.h new file mode 100644 index 000000000..73aa4caa5 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeImpl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_NODE_IMPL_H__ +#define __LOCO_IR_CANONICAL_NODE_IMPL_H__ + +#include "loco/IR/Nodes.h" +#include "loco/IR/CanonicalNodeVisitor.h" + +#include + +namespace loco +{ + +template T CanonicalNode::accept(CanonicalNodeVisitorBase *v) const +{ + switch (this->opcode()) + { +#define CANONICAL_NODE(OPCODE, CLASS) \ + case CanonicalOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + default: + break; + } + + throw std::runtime_error{"NYI"}; +} + +template T CanonicalNode::accept(CanonicalNodeMutableVisitorBase *v) +{ + switch (this->opcode()) + { +#define CANONICAL_NODE(OPCODE, CLASS) \ + case CanonicalOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + default: + break; + } + + throw std::runtime_error{"NYI"}; +} + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_IMPL_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h new file mode 100644 index 000000000..425d77997 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ +#define __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ + +namespace loco +{ + +// NOTE These forward declarations SHOULD BE aligned with "CanonicalNodeVisitor.h" +template struct CanonicalNodeVisitorBase; +template struct CanonicalNodeMutableVisitorBase; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h new file mode 100644 index 000000000..b9ffd5472 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_NODE_VISITOR_H__ +#define __LOCO_IR_CANONICAL_NODE_VISITOR_H__ + +#include "loco/IR/Nodes.h" + +#include + +namespace loco +{ + +/** + * DO NOT use this class. Use CanonicalNodeVisitor instead. + */ +template struct CanonicalNodeVisitorBase +{ + virtual ~CanonicalNodeVisitorBase() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) virtual T visit(const CLASS *) = 0; +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +template struct CanonicalNodeVisitor : public CanonicalNodeVisitorBase +{ + virtual ~CanonicalNodeVisitor() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) \ + virtual T visit(const CLASS *node) { return visit(static_cast(node)); } +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + + /// @brief Default fallback + virtual T visit(const Node *) { throw std::runtime_error{"Not implemented, yet"}; } +}; + +/** + * DO NOT use this class. Use CanonicalNodeMutableVisitor instead. + */ +template struct CanonicalNodeMutableVisitorBase +{ + virtual ~CanonicalNodeMutableVisitorBase() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) virtual T visit(CLASS *) = 0; +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +template struct CanonicalNodeMutableVisitor : public CanonicalNodeMutableVisitorBase +{ + virtual ~CanonicalNodeMutableVisitor() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) \ + virtual T visit(CLASS *node) { return visit(static_cast(node)); } +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + + /// @brief Default fallback + virtual T visit(Node *) { throw std::runtime_error{"Not implemented, yet"}; } +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_VISITOR_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodes.lst b/compiler/loco/include/loco/IR/CanonicalNodes.lst new file mode 100644 index 000000000..527856fbe --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodes.lst @@ -0,0 +1,49 @@ +#ifndef CANONICAL_NODE +#error "Define CANONICAL_NODE" +#endif // CANONICAL_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// + +// CANONICAL_NODE(OPCODE, CLASS) +CANONICAL_NODE(AvgPool2D, AvgPool2D) +CANONICAL_NODE(BiasDecode, BiasDecode) +CANONICAL_NODE(BiasEncode, BiasEncode) +CANONICAL_NODE(ConstGen, ConstGen) +CANONICAL_NODE(Conv2D, Conv2D) +CANONICAL_NODE(DepthwiseConv2D, DepthwiseConv2D) +CANONICAL_NODE(DepthwiseFilterDecode, DepthwiseFilterDecode) +CANONICAL_NODE(DepthwiseFilterEncode, DepthwiseFilterEncode) +CANONICAL_NODE(EltwiseAdd, EltwiseAdd) +CANONICAL_NODE(EltwiseDiv, EltwiseDiv) +CANONICAL_NODE(EltwiseMax, EltwiseMax) +CANONICAL_NODE(EltwiseMul, EltwiseMul) +CANONICAL_NODE(EltwiseSqrt, EltwiseSqrt) +CANONICAL_NODE(EltwiseSub, EltwiseSub) +CANONICAL_NODE(FeatureBiasAdd, BiasAdd) +CANONICAL_NODE(FeatureDecode, FeatureDecode) +CANONICAL_NODE(FeatureEncode, FeatureEncode) +CANONICAL_NODE(FilterDecode, FilterDecode) +CANONICAL_NODE(FilterEncode, FilterEncode) +CANONICAL_NODE(FixedReshape, Reshape) +CANONICAL_NODE(Forward, Forward) +CANONICAL_NODE(MaxPool2D, MaxPool2D) +// WARN Push may be excluded from canoncial dialect in the future +CANONICAL_NODE(Push, Push) +// WARN Pull may be excluded from canoncial dialect in the future +CANONICAL_NODE(Pull, Pull) +CANONICAL_NODE(ReLU, ReLU) +CANONICAL_NODE(ReLU6, ReLU6) +CANONICAL_NODE(Tanh, Tanh) +CANONICAL_NODE(TensorConcat, TensorConcat) +CANONICAL_NODE(TensorConstantPad, TensorConstantPad) +CANONICAL_NODE(TensorBiasAdd, BiasAdd) +CANONICAL_NODE(TensorBroadcast, TensorBroadcast) +CANONICAL_NODE(TensorReduce, TensorReduce) +CANONICAL_NODE(TensorTranspose, TensorTranspose) +CANONICAL_NODE(TensorSoftmax, Softmax) +CANONICAL_NODE(TransposedConv2D, TransposedConv2D) +CANONICAL_NODE(MatrixEncode, MatrixEncode) +CANONICAL_NODE(MatrixDecode, MatrixDecode) +CANONICAL_NODE(MatMul, MatMul) diff --git a/compiler/loco/include/loco/IR/CanonicalOpcode.h b/compiler/loco/include/loco/IR/CanonicalOpcode.h new file mode 100644 index 000000000..58aa7de6d --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalOpcode.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_CANONICAL_OPCODE_H__ +#define __LOCO_IR_CANONICAL_OPCODE_H__ + +namespace loco +{ + +/** + * @brief Canonical Node Opcode + * + * WARNING The order is subject to change. DO NOT serialize this value. + */ +enum class CanonicalOpcode +{ +#define CANONICAL_NODE(OPCODE, CLASS) OPCODE, +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_OPCODE_H__ diff --git a/compiler/loco/include/loco/IR/DataType.h b/compiler/loco/include/loco/IR/DataType.h new file mode 100644 index 000000000..b07022bf5 --- /dev/null +++ b/compiler/loco/include/loco/IR/DataType.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DATA_TYPE_H__ +#define __LOCO_IR_DATA_TYPE_H__ + +namespace loco +{ + +/** + * @brief "scalar" value type + */ +enum class DataType +{ + Unknown, // Unknown type (serves as a default value) + + U8, // 8-bit unsigned integer + U16, // 16-bit unsigned integer + U32, // 32-bit unsigned integer + U64, // 64-bit unsigned integer + + S8, // 8-bit signed integer + S16, // 16-bit signed integer + S32, // 32-bit signed integer + S64, // 64-bit signed integer + + FLOAT16, // IEEE 16-bit floating-point + FLOAT32, // IEEE 32-bit floating-point + FLOAT64, // IEEE 64-bit floating-point + + // WARNING the size of Bool may vary for NN frameworks + // TODO we need to find a way to resolve this issue + BOOL, // Boolean +}; + +} // namespace loco + +#endif // __LOCO_IR_DATA_TYPE_H__ diff --git a/compiler/loco/include/loco/IR/DataTypeTraits.h b/compiler/loco/include/loco/IR/DataTypeTraits.h new file mode 100644 index 000000000..c4479e545 --- /dev/null +++ b/compiler/loco/include/loco/IR/DataTypeTraits.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DATA_TYPE_TRAITS_H__ +#define __LOCO_IR_DATA_TYPE_TRAITS_H__ + +#include "loco/IR/DataType.h" + +#include +#include + +namespace loco +{ + +/** + * @brief C++ scalar type corresponding to each DataType + */ +template struct DataTypeImpl +{ + // using Type = ... +}; + +// TODO Support other enum values +template <> struct DataTypeImpl +{ + // Use C++ int8_t type for 8bit integer + using Type = int8_t; +}; + +template <> struct DataTypeImpl +{ + // Use C++ uint8_t type for unsigned 8bit integer + using Type = uint8_t; +}; + +template <> struct DataTypeImpl +{ + // Use C++ int32_t type for 32bit integer + using Type = int32_t; +}; + +template <> struct DataTypeImpl +{ + // Use C++ float type for IEEE 32-bit floating-point numbers + using Type = float; +}; + +/** + * @brief Returns the size of the data type. + * @note If you need the size at compile time, use `sizeof(typename DataTypeImpl
::Type)`. + */ +inline uint32_t size(DataType data_type) +{ + switch (data_type) + { + case DataType::S8: + return sizeof(DataTypeImpl::Type); + case DataType::U8: + return sizeof(DataTypeImpl::Type); + case DataType::S32: + return sizeof(DataTypeImpl::Type); + case DataType::FLOAT32: + return sizeof(DataTypeImpl::Type); + default: + // TODO Support remaining data types. + assert(false); + return UINT32_MAX; // Avoid compiler warning. + } +} + +} // namespace loco + +#endif // __LOCO_IR_DATA_TYPE_TRAITS_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h b/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h new file mode 100644 index 000000000..eb4650ec9 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ + +namespace loco +{ + +enum class DepthwiseFilterAxis +{ + Depth, + Multiplier, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h new file mode 100644 index 000000000..0d9286b46 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ + +#include "loco/IR/DepthwiseFilterShape.h" +#include "loco/IR/DepthwiseFilterIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +namespace loco +{ + +/** + * @brief Describe how to build a depthwise convolution filter from a tensor + * + * Let us assume that "enc" is a depthwise filter encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a depthwise filter + * "out" as follows: + * + * for each valid filter_index for enc.shape(inp.shape) + * out.at(filter_index) = inp.at(enc.value(filter_index)) + */ +struct DepthwiseFilterEncoder +{ + virtual ~DepthwiseFilterEncoder() = default; + + virtual DepthwiseFilterShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const DepthwiseFilterIndex &index) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a depthwise convolution filter + * + * Let us assume that "dec" is a depthwise filter decoder. + * + * Given a depthwise filter "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor_index for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + */ +struct DepthwiseFilterDecoder +{ + virtual ~DepthwiseFilterDecoder() = default; + + virtual TensorShape shape(const DepthwiseFilterShape &shape) const = 0; + virtual DepthwiseFilterIndex value(const TensorIndex &index) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h b/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h new file mode 100644 index 000000000..884e50a56 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ + +#include + +namespace loco +{ + +/** + * @brief DepthwiseFilter Index + * + * DepthwiseFilter Index indicates an "element" in a given Depthwise convolution filter. + * + * Assume there is a filter K where KS denotes its shape (of DepthwiseFilterShape type). + * + * Then, any valid filter index I satisfies the following invariants: + * - 0 <= I.channel() < KS.depth() + * - 0 <= I.nth() < KS.multiplier() + * - 0 <= I.row() < KS.height() + * - 0 <= I.column() < KS.width() + */ +class DepthwiseFilterIndex final +{ +public: + DepthwiseFilterIndex() = default; + +public: + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &nth(void) const { return _nth; } + uint32_t &nth(void) { return _nth; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _channel = 0; + uint32_t _nth = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterShape.h b/compiler/loco/include/loco/IR/DepthwiseFilterShape.h new file mode 100644 index 000000000..eb1a1e335 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterShape.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * @brief DepthwiseFilter Shape + * + * This class describes the shape of depthwise filter, which is an input of depthwise 2D + * convolutional operation. + * + * depth() refers to expected channel depth of matching input + * multiplier() refers to number of traverse for one input + * height() refers to the height of 2D weights + * width() refers to the width of 2D weights + */ +class DepthwiseFilterShape final +{ +public: + DepthwiseFilterShape() = default; + +public: + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &multiplier(void) const { return _multiplier; } + Dimension &multiplier(void) { return _multiplier; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _depth; + Dimension _multiplier; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Dialect.h b/compiler/loco/include/loco/IR/Dialect.h new file mode 100644 index 000000000..b8942bfb4 --- /dev/null +++ b/compiler/loco/include/loco/IR/Dialect.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DIALECT_H__ +#define __LOCO_IR_DIALECT_H__ + +#include "loco/IR/DialectService.h" + +#include +#include +#include +#include + +namespace loco +{ + +/** + * @brief Dialect interface + * + * Each dialect implementation is expected to have static "get" method + * which returns "const Dialect *" value. + */ +class Dialect +{ +public: + virtual ~Dialect() = default; + +protected: + template void service(std::unique_ptr &&s) + { + _services[typeid(ConcreteService)] = std::move(s); + } + +public: + template ConcreteService *service(void) const + { + auto it = _services.find(typeid(ConcreteService)); + + if (it == _services.end()) + { + return nullptr; + } + + return dynamic_cast(it->second.get()); + } + +private: + std::map> _services; +}; + +} // namespace loco + +#endif // __LOCO_IR_DIALECT_H__ diff --git a/compiler/loco/include/loco/IR/DialectService.h b/compiler/loco/include/loco/IR/DialectService.h new file mode 100644 index 000000000..54a3fac74 --- /dev/null +++ b/compiler/loco/include/loco/IR/DialectService.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DIALECT_SERVICE_H__ +#define __LOCO_IR_DIALECT_SERVICE_H__ + +namespace loco +{ + +/** + * @brief Dialect Service interface + * + * Every service that each dialect exposes should inherit this interface. + */ +struct DialectService +{ + virtual ~DialectService() = default; +}; + +} // namespace loco + +#endif // __LOCO_IR_DIALECT_SERVICE_H__ diff --git a/compiler/loco/include/loco/IR/Dimension.h b/compiler/loco/include/loco/IR/Dimension.h new file mode 100644 index 000000000..7b5d5943f --- /dev/null +++ b/compiler/loco/include/loco/IR/Dimension.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DIMENSION_H__ +#define __LOCO_IR_DIMENSION_H__ + +#include + +namespace loco +{ + +/** + * @brief The value of one dimension in a tensor shape + * @note The value may be unknown + */ +class Dimension final +{ +private: + enum class Kind + { + Known, + Unknown + }; + +public: + // @brief Construct an "unknown" dimension + Dimension() = default; + + // @brief Construct a "known" dimension + Dimension(uint32_t value) { set(value); } + +public: + // @brief Return whether the value is known (or not) + bool known(void) const { return _kind == Kind::Known; } + + // @brief Return the value + // @note This value is meaningful only for known dimension + uint32_t value(void) const { return _value; } + + void set(uint32_t value) + { + _kind = Kind::Known; + _value = value; + } + + void unset(void) + { + _kind = Kind::Unknown; + _value = 0; + } + +private: + Kind _kind{Kind::Unknown}; + uint32_t _value{0}; +}; + +/** + * @brief Equality operator between two Dimensions + * + * @note Refer to the definition of equality of dimemsion at + * https://www.tensorflow.org/api_docs/python/tf/Dimension#__eq__ + */ +bool operator==(const Dimension &, const Dimension &); +bool operator==(const Dimension &, uint32_t); +bool operator==(uint32_t, const Dimension &); + +// @brief Make an "unknown" dimension +Dimension make_dimension(void); + +} // namespace loco + +#endif // __LOCO_IR_DIMENSION_H__ diff --git a/compiler/loco/include/loco/IR/Domain.h b/compiler/loco/include/loco/IR/Domain.h new file mode 100644 index 000000000..823bc1833 --- /dev/null +++ b/compiler/loco/include/loco/IR/Domain.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_DOMAIN_H__ +#define __LOCO_IR_DOMAIN_H__ + +namespace loco +{ + +/** + * @brief Describe the kind of (N-dimensional) loco values + * + * loco is an intermediate representation for neural network compiler, which mainly focuses on + * N-dimensional values (usually referred to as Tensor). + * + * There are several special cases for N-dimensional values according to its usage. For example, + * vision community often refers to 4D array as "FeatureMap". + * + * It is definitely possible to represent all of these special cases using Tensor, but that scheme + * may introduces some confusion (e.g. NCHW vs NHWC issue). + * + * loco distinguishes these special cases from Tensor in order to reduce such confusion. + * + * This "Domain" enum class enumerates all of these special cases that loco supports. + */ +enum class Domain +{ + Unknown, + Tensor, + Feature, + Filter, /* 2D Convolution Filter */ + DepthwiseFilter, /* Depthwise 2D Convolution Filter */ + Bias, + Matrix, + /* ... */ +}; + +} // namespace loco + +#endif // __LOCO_IR_DOMAIN_H__ diff --git a/compiler/loco/include/loco/IR/FeatureAxis.h b/compiler/loco/include/loco/IR/FeatureAxis.h new file mode 100644 index 000000000..cf020edd2 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FEATURE_AXIS_H__ +#define __LOCO_IR_FEATURE_AXIS_H__ + +namespace loco +{ + +enum class FeatureAxis +{ + Count, + Depth, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/FeatureCodec.h b/compiler/loco/include/loco/IR/FeatureCodec.h new file mode 100644 index 000000000..93094e13a --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureCodec.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FEATURE_CODEC_H__ +#define __LOCO_IR_FEATURE_CODEC_H__ + +#include "loco/IR/FeatureShape.h" +#include "loco/IR/FeatureIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +#include + +namespace loco +{ + +/** + * @brief Decribe how to build a (convolution) feature map from a tensor + * + * Let us assume that "enc" is a feature encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a feature map + * "out" as follows: + * + * for each valid feature index (referred to as feature_idx below) for enc.shape(inp.shape) + * out.at(feature_index) = inp.at(enc.value(feature_index)) + */ +struct FeatureEncoder +{ + virtual ~FeatureEncoder() = default; + + virtual FeatureShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const FeatureIndex &index) const = 0; + + virtual std::unique_ptr clone(void) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a (convolution) feature map + * + * Let us assume that "dec" is a feature decoder. + * + * Given a feature map "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor index (referred to as tensor_index below) for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + * + * NOTE "inp" is a feature value and "out" is a tensor value in this example. + */ +struct FeatureDecoder +{ + virtual ~FeatureDecoder() = default; + + virtual TensorShape shape(const FeatureShape &) const = 0; + virtual FeatureIndex value(const TensorIndex &) const = 0; + + virtual std::unique_ptr clone(void) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/FeatureIndex.h b/compiler/loco/include/loco/IR/FeatureIndex.h new file mode 100644 index 000000000..007f94491 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FEATURE_INDEX_H__ +#define __LOCO_IR_FEATURE_INDEX_H__ + +#include + +namespace loco +{ + +/** + * \brief Feature Index + * + * Feature Index indicates an "element" in a given feature map. + * + * Let us assume that there is a feature map F and S denotes its shape (of FeatureShape type). + * + * Then, any valid feature index I satisfies the following invariants: + * - 0 <= I.batch() < S.count() + * - 0 <= I.channel() < S.depth() + * - 0 <= I.row() < S.height() + * - 0 <= I.column() < S.width() + */ +class FeatureIndex final +{ +public: + FeatureIndex() = default; + +public: + const uint32_t &batch(void) const { return _batch; } + uint32_t &batch(void) { return _batch; } + + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _batch = 0; + uint32_t _channel = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/FeatureShape.h b/compiler/loco/include/loco/IR/FeatureShape.h new file mode 100644 index 000000000..d09a2b2b8 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureShape.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FEATURE_SHAPE_H__ +#define __LOCO_IR_FEATURE_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Feature Map Shape + * + * This class describes the shape of feature maps, which serves as the input/output of 2D + * convolutional operations (e.g. Convolution). + * + * Each feature map is a collection of 3D features conceptually. + * Each feature has depth, height, width. + * + * count() refers to the number of features in a feature map + * depth() refers to the depth of features in a given feature map + * height() refers to the height of features in a given feature map + * width() refers to the width of features in a given feature map + */ +class FeatureShape final +{ +public: + FeatureShape() = default; + +public: + const Dimension &count(void) const { return _count; } + Dimension &count(void) { return _count; } + + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _count; + Dimension _depth; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/FilterAxis.h b/compiler/loco/include/loco/IR/FilterAxis.h new file mode 100644 index 000000000..269e2aecc --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FILTER_AXIS_H__ +#define __LOCO_IR_FILTER_AXIS_H__ + +namespace loco +{ + +enum class FilterAxis +{ + Count, + Depth, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/FilterCodec.h b/compiler/loco/include/loco/IR/FilterCodec.h new file mode 100644 index 000000000..3ff548d6d --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterCodec.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FILTER_CODEC_H__ +#define __LOCO_IR_FILTER_CODEC_H__ + +#include "loco/IR/FilterShape.h" +#include "loco/IR/FilterIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +namespace loco +{ + +/** + * @brief Decribe how to build a (convolution) filter from a tensor + * + * Let us assume that "enc" is a filter encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a filter + * "out" as follows: + * + * for each valid filter index (referred to as filter_index below) for enc.shape(inp.shape) + * out.at(filter_index) = inp.at(enc.value(filter_index)) + */ +struct FilterEncoder +{ + virtual ~FilterEncoder() = default; + + virtual FilterShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const FilterIndex &index) const = 0; +}; + +/** + * @brief Decribe how to build a a tensor from a filter + */ +struct FilterDecoder +{ + virtual ~FilterDecoder() = default; + + virtual TensorShape shape(const FilterShape &shape) const = 0; + virtual FilterIndex value(const TensorIndex &index) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/FilterIndex.h b/compiler/loco/include/loco/IR/FilterIndex.h new file mode 100644 index 000000000..5765ea764 --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FILTER_INDEX_H__ +#define __LOCO_IR_FILTER_INDEX_H__ + +#include + +namespace loco +{ + +/** + * \brief Filter Index + * + * Filter Index indicates an "element" in a given (convolutional) filter. + * + * Let us assume that there is a filter K where KS denotes its shape (of FilterShape type). + * + * Then, any valid filter index I satisfies the following invariants: + * - 0 <= I.nth() < KS.count() + * - 0 <= I.channel() < KS.depth() + * - 0 <= I.row() < KS.height() + * - 0 <= I.column() < KS.width() + */ +class FilterIndex final +{ +public: + FilterIndex() = default; + +public: + const uint32_t &nth(void) const { return _nth; } + uint32_t &nth(void) { return _nth; } + + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _nth = 0; + uint32_t _channel = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/FilterShape.h b/compiler/loco/include/loco/IR/FilterShape.h new file mode 100644 index 000000000..00e44892a --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterShape.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_FILTER_SHAPE_H__ +#define __LOCO_IR_FILTER_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Filter Shape + * + * This class describes the shape of filter, which is an input of 2D + * convolutional operations (e.g. Convolution). + * + * count() refers to the number of 3D weight in a filter + * depth() refers to the depth of 3D weights + * height() refers to the height of 3D weights + * width() refers to the width of 3D weights + * + * NOTE + * + * The definition of FilterShape is almost same as that of FeatureShape, but loco + * distinguishes FeatureShape and FilterShape in class-level in order to prevent + * potential errors by type check. + */ +class FilterShape final +{ +public: + FilterShape() = default; + +public: + const Dimension &count(void) const { return _count; } + Dimension &count(void) { return _count; } + + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _count; + Dimension _depth; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Graph.forward.h b/compiler/loco/include/loco/IR/Graph.forward.h new file mode 100644 index 000000000..2a43be93a --- /dev/null +++ b/compiler/loco/include/loco/IR/Graph.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_GRAPH_FORWARD_H__ +#define __LOCO_IR_GRAPH_FORWARD_H__ + +namespace loco +{ + +// This forward declaration SHOULD BE aligned with the actual declaration in "Graph.h". +class Graph; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/Graph.h b/compiler/loco/include/loco/IR/Graph.h new file mode 100644 index 000000000..a820aba91 --- /dev/null +++ b/compiler/loco/include/loco/IR/Graph.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_GRAPH_H__ +#define __LOCO_IR_GRAPH_H__ + +#include "loco/IR/DataType.h" +// TODO Include "Node.h" instead +#include "loco/IR/Nodes.h" +#include "loco/IR/NodePool.h" +#include "loco/IR/GraphInputIndex.h" +#include "loco/IR/GraphOutputIndex.h" + +#include "loco/ADT/ObjectPool.h" + +#include +#include +#include +#include +#include + +namespace loco +{ + +// TODO Introduce Named trait +enum class Trait +{ + // Any "DataTyped" class has the following methods + // - DataType dtype(void) const; + // - void dtype(const DataType &value); + DataTyped, + // Any "TensorShaped" class has the following methods + // - const TensorShape *shape(void) const; + // - void shape(std::unique_ptr &&); + // - void shape(std::initializer_list &&); + // + // TODO Rename NodeMixin::TensorShape as NodeMixin::NDShape + TensorShaped, +}; + +template class Mixin; + +// TODO Re-implement NodeMixin using this mixin +template <> class Mixin +{ +public: + Mixin() = default; + +public: + const DataType &dtype(void) const { return _dtype; } + void dtype(const DataType &value) { _dtype = value; } + +private: + DataType _dtype = DataType::Unknown; +}; + +template <> class Mixin +{ +public: + Mixin() = default; + +public: + const TensorShape *shape(void) const { return _shape.get(); } + void shape(std::unique_ptr &&shape) { _shape = std::move(shape); } + void shape(std::initializer_list dims); + +private: + std::unique_ptr _shape = nullptr; +}; + +/** + * @brief Trait for elements with name + */ +class NamedEntity +{ +public: + const std::string &name(void) const { return _name; } + void name(const std::string &name) { _name = name; } + +/// If new interface methods are added to this class they also will need to +/// be added in `using` of this macro to get them visible from inherited classes +#define LOCO_NAMED_ENTITY_EXPOSE using NamedEntity::name + +private: + std::string _name; +}; + +/** + * @brief Graph-level Input Metadata + */ +class GraphInput final : private NamedEntity, + public Mixin, + public Mixin +{ +public: + LOCO_NAMED_ENTITY_EXPOSE; + + // TODO Use GraphInputIndex (instead of uint32_t) + GraphInput(uint32_t index) : _index{index} + { + // DO NOTHING + } + + GraphInput(const GraphInput &) = delete; + GraphInput(GraphInput &&) = delete; + + ~GraphInput() = default; + +public: + GraphInputIndex index(void) const { return _index; } + +private: + uint32_t _index; +}; + +/** + * @brief Graph-level Output Metadata + */ +class GraphOutput final : private NamedEntity, + public Mixin, + public Mixin +{ +public: + LOCO_NAMED_ENTITY_EXPOSE; + + // TODO Use GraphOutputIndex (instead of uint32_t) + GraphOutput(uint32_t index) : _index{index} + { + // DO NOTHING + } + + GraphOutput(const GraphOutput &) = delete; + GraphOutput(GraphOutput &&) = delete; + + ~GraphOutput() = default; + +public: + GraphOutputIndex index(void) const { return _index; } + +private: + uint32_t _index; +}; + +/** + * @brief A neural network graph + */ +class Graph final : public NamedEntity +{ +public: + /** + * @brief Node Pool + * + * This alias confines the impact of changes to loco internals. + * + * TODO Remove this alias + */ + using NodeContext = NodePool; + + /** + * @brief Object Pool with Simple Factory Method + * + * TODO Remove this unused class + */ + template struct SimpleFactoryObjectPool : public ObjectPool + { + virtual ~SimpleFactoryObjectPool() = default; + + T *create(void) + { + std::unique_ptr ptr{new T}; + return ObjectPool::take(std::move(ptr)); + } + }; + + /** + * @brief GraphInput Pool + */ + struct InputContext final : public ObjectPool + { + GraphInput *create(void); + }; + + /** + * @brief GraphOutput Pool + */ + struct OutputContext final : public ObjectPool + { + GraphOutput *create(void); + }; + +public: + Graph() + { + // Associate "NodeContext" and the current "Graph" + _node_ctx.graph(this); + } + + // Copy/Move is not allowed for Graph + Graph(const Graph &) = delete; + Graph(Graph &&) = delete; + + ~Graph() = default; + +public: + NodeContext *nodes(void) { return &_node_ctx; } + const NodeContext *nodes(void) const { return &_node_ctx; } + InputContext *inputs(void) { return &_input_ctx; } + const InputContext *inputs(void) const { return &_input_ctx; } + OutputContext *outputs(void) { return &_output_ctx; } + const OutputContext *outputs(void) const { return &_output_ctx; } + +private: + NodeContext _node_ctx; + InputContext _input_ctx; + OutputContext _output_ctx; +}; + +struct GraphInputIndexQueryService : public DialectService +{ + virtual ~GraphInputIndexQueryService() = default; + + /** + * @brief Check whether a given node is associated with any Graph-level input + */ + virtual bool associated(const Node *node) const = 0; + + /** + * Exceptions + * - index SHOULD throw std::invalid_argument exception if a given node is not associated with + * any input (i.e. assocaited above returns false). + */ + virtual GraphInputIndex index(const Node *node) const = 0; +}; + +std::vector input_nodes(const Graph *); + +struct GraphOutputIndexQueryService : public DialectService +{ + virtual ~GraphOutputIndexQueryService() = default; + + /** + * @brief Check whether a given node is associated with any Graph-level output + */ + virtual bool associated(const Node *node) const = 0; + + /** + * Exceptions + * - index SHOULD throw std::invalid_argument exception if a given node is not associated with + * any output (i.e. assocaited above returns false). + */ + virtual GraphOutputIndex index(const Node *node) const = 0; +}; + +// TODO Use "const Graph *" +std::vector output_nodes(Graph *); + +/** + * @brief Enumerate all the nodes in a given graph + * + * NOTE This method returns std::set unlike input_nodes and output_nodes. + * + * Please use traverse algorithms that "Algorithm.h" provides (such as postorder_traversal) + * if order is relevant for implementation. + */ +std::set all_nodes(Graph *); + +std::unique_ptr make_graph(void); + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_H__ diff --git a/compiler/loco/include/loco/IR/GraphInputIndex.h b/compiler/loco/include/loco/IR/GraphInputIndex.h new file mode 100644 index 000000000..3c7ae98ef --- /dev/null +++ b/compiler/loco/include/loco/IR/GraphInputIndex.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_GRAPH_INPUT_INDEX_H__ +#define __LOCO_IR_GRAPH_INPUT_INDEX_H__ + +#include + +namespace loco +{ + +using GraphInputIndex = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_INPUT_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/GraphOutputIndex.h b/compiler/loco/include/loco/IR/GraphOutputIndex.h new file mode 100644 index 000000000..3231cbd95 --- /dev/null +++ b/compiler/loco/include/loco/IR/GraphOutputIndex.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ +#define __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ + +#include + +namespace loco +{ + +using GraphOutputIndex = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/MatrixAxis.h b/compiler/loco/include/loco/IR/MatrixAxis.h new file mode 100644 index 000000000..8a1689bb3 --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixAxis.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_MATRIX_AXIS_H__ +#define __LOCO_IR_MATRIX_AXIS_H__ + +namespace loco +{ + +enum class MatrixAxis +{ + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/MatrixCodec.h b/compiler/loco/include/loco/IR/MatrixCodec.h new file mode 100644 index 000000000..40312641a --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixCodec.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_MATRIX_CODEC_H__ +#define __LOCO_IR_MATRIX_CODEC_H__ + +#include "loco/IR/MatrixShape.h" +#include "loco/IR/MatrixIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +#include + +namespace loco +{ + +/** + * @brief Decribe how to build a matrix from a tensor + * + * Let us assume that "enc" is a matrix encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a matrix + * "out" as follows: + * + * for each valid matrix index (referred to as matrix_idx below) for enc.shape(inp.shape) + * out.at(matrix_index) = inp.at(enc.value(matrix_index)) + */ +struct MatrixEncoder +{ + virtual ~MatrixEncoder() = default; + + virtual MatrixShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const MatrixIndex &index) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a matrix + * + * Let us assume that "dec" is a matrix decoder. + * + * Given a matrix "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor index (referred to as tensor_index below) for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + * + * NOTE "inp" is a matrix value and "out" is a tensor value in this example. + */ +struct MatrixDecoder +{ + virtual ~MatrixDecoder() = default; + + virtual TensorShape shape(const MatrixShape &) const = 0; + virtual MatrixIndex value(const TensorIndex &) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/MatrixIndex.h b/compiler/loco/include/loco/IR/MatrixIndex.h new file mode 100644 index 000000000..eb6d65580 --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixIndex.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_MATRIX_INDEX_H__ +#define __LOCO_IR_MATRIX_INDEX_H__ + +#include + +namespace loco +{ + +/** + * @brief Matrix Index + * + * Matrix Index indicates an "element" in a given Matrix + * + * Let us assume that there is a Matrix F and S denotes its shape (of MatrixShape type). + * + * Then, any valid Matrix index I satisfies the following invariants: + * - 0 <= I.row() < S.height() + * - 0 <= I.column() < S.width() + */ +class MatrixIndex final +{ +public: + MatrixIndex() = default; + +public: + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/MatrixShape.h b/compiler/loco/include/loco/IR/MatrixShape.h new file mode 100644 index 000000000..512691beb --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixShape.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_MATRIX_SHAPE_H__ +#define __LOCO_IR_MATRIX_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * @brief Matrix Shape + * + * This class describes the shape of matrix, which serves as the input/output of + * matrix operations (e.g. Matrix Multiplication). + * + * Each matrix is a collection of 2D features conceptually. + * Each matrix has height, width. + * + * height() refers to the height of matrix in a given matrix + * width() refers to the width of matrix in a given matrix + */ +class MatrixShape final +{ +public: + MatrixShape() = default; + +public: + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Node.forward.h b/compiler/loco/include/loco/IR/Node.forward.h new file mode 100644 index 000000000..425b28aff --- /dev/null +++ b/compiler/loco/include/loco/IR/Node.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_FORWARD_H__ +#define __LOCO_IR_NODE_FORWARD_H__ + +namespace loco +{ + +// NOTE This forward declaration SHOULD BE aligned with Node delcaration in "Node.h" +class Node; + +} // namespace loco + +#endif // __LOCO_IR_NODE_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/Node.h b/compiler/loco/include/loco/IR/Node.h new file mode 100644 index 000000000..ef0bf238d --- /dev/null +++ b/compiler/loco/include/loco/IR/Node.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_H__ +#define __LOCO_IR_NODE_H__ + +#include "loco/ADT/AnnotatedItem.h" + +#include "loco/IR/Use.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/NodePool.forward.h" +#include "loco/IR/Graph.forward.h" + +#include +#include +#include + +namespace loco +{ + +/** + * @brief Extensible Node Metadata + */ +struct NodeAnnotation +{ + virtual ~NodeAnnotation() = default; +}; + +enum class SubstQualifier +{ + Default, // Replace all the occurrences as "Use" (by default) +}; + +template class Subst; + +/** + * @brief Logical unit of computation + */ +class Node : public AnnotatedItem +{ +public: + friend class Use; + friend class Subst; + friend class NodePool; + friend std::set succs(const Node *node); + +public: + Node() = default; + + Node(const Node &) = delete; + Node(Node &&) = delete; + + virtual ~Node(); + +public: + Graph *graph(void) { return _graph; } + const Graph *graph(void) const { return _graph; } + +private: + /** + * @brief Set associated "Graph" + * + * @note Only "NodePool" class is permitted to invoke this private method. + */ + void graph(Graph *g) { _graph = g; } + +public: + /** + * @brief Return "Dialect" identifier that this node belongs to + * + * dialect() SHOULD return a valid pointer. + */ + virtual const Dialect *dialect(void) const = 0; + + virtual uint32_t opnum(void) const = 0; + +public: + /// @brief Return the number of arguments + virtual uint32_t arity(void) const = 0; + + /// @brief Access N-th argument node + virtual Node *arg(uint32_t N) const = 0; + + /** + * @brief Drop all the reference of arguments + * + * arg(n) SHOULD return nullptr for every valid n after drop() call. + */ + virtual void drop(void) = 0; + +private: + /** + * @brief Associated Graph + * + * May be nullptr if no associated Graph exists. + */ + Graph *_graph = nullptr; + + /** + * @brief The edges to a node that uses this node as its argument + * + * @note "succs" function below accesses this private field. + */ + std::set _uses; +}; + +/// @brief Enumerate all the predecessors of a given node +std::set preds(const Node *node); +/// @brief Enumerate all the successors of a given node +std::set succs(const Node *node); + +/** + * @brief A helper for below "replace" helper + */ +template <> class Subst +{ +public: + friend Subst replace(Node *node); + +private: + explicit Subst(Node *from); + +public: + void with(Node *into) const; + +private: + Node *_from; +}; + +Subst replace(Node *node); + +} // namespace loco + +#endif // __LOCO_IR_NODE_H__ diff --git a/compiler/loco/include/loco/IR/NodeMixins.h b/compiler/loco/include/loco/IR/NodeMixins.h new file mode 100644 index 000000000..f0e34b0ba --- /dev/null +++ b/compiler/loco/include/loco/IR/NodeMixins.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_MIXINS_H__ +#define __LOCO_IR_NODE_MIXINS_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/DataType.h" +#include "loco/IR/Dimension.h" + +#include +#include + +namespace loco +{ + +enum class NodeTrait +{ + DataType, + // Nodes with TensorShape trait will provide the following methods: + // - rank() + // - rank(value) + // - dim() + // - dim(value) + // - shape({...}) + TensorShape, +}; + +template class NodeMixin; + +template <> class NodeMixin +{ +public: + NodeMixin() = default; + +public: + const DataType &dtype(void) const { return _dtype; } + void dtype(const DataType &dtype) { _dtype = dtype; } + +private: + /// @brief Data type + DataType _dtype{DataType::Unknown}; +}; + +template <> class NodeMixin +{ +public: + NodeMixin() = default; + +public: + uint32_t rank(void) const { return _dims.size(); } + void rank(uint32_t value) { _dims.resize(value); } + + const Dimension &dim(uint32_t axis) const { return _dims.at(axis); } + Dimension &dim(uint32_t axis) { return _dims.at(axis); } + + void shape(std::initializer_list dims) + { + rank(dims.size()); + + uint32_t axis = 0; + for (auto d : dims) + { + dim(axis++) = d; + } + } + +private: + /// @brief Data shape (as tensor) + std::vector _dims; +}; + +template struct FixedArity +{ + template class Mixin : public virtual Base + { + public: + Mixin() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::unique_ptr{new Use{this}}; + } + } + + virtual ~Mixin() = default; + + public: + unsigned arity(void) const final { return N; } + + 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. + Use *at(unsigned n) const { return _args.at(n).get(); } + + private: + std::array, N> _args{}; + }; +}; + +template struct With +{ + template struct Mixin : public virtual Base, public NodeMixin + { + // DO NOTHING + }; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODE_MIXINS_H__ diff --git a/compiler/loco/include/loco/IR/NodePool.forward.h b/compiler/loco/include/loco/IR/NodePool.forward.h new file mode 100644 index 000000000..87bf01311 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodePool.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_POOL_FORWARD_H__ +#define __LOCO_IR_NODE_POOL_FORWARD_H__ + +namespace loco +{ + +// This forward declaration SHOULD BE aligned with the actual declaration in "NodePool.h". +class NodePool; + +} // namespace loco + +#endif // __LOCO_IR_NODE_POOL_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/NodePool.h b/compiler/loco/include/loco/IR/NodePool.h new file mode 100644 index 000000000..4db4caae3 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodePool.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_POOL_H__ +#define __LOCO_IR_NODE_POOL_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Graph.forward.h" + +#include "loco/ADT/ObjectPool.h" + +namespace loco +{ + +class NodePool final : public ObjectPool +{ +public: + friend class Graph; + +public: + ~NodePool(); + +public: + template Derived *create(Args &&... args) + { + std::unique_ptr ptr{new Derived(std::forward(args)...)}; + ptr->graph(_graph); + return ObjectPool::take(std::move(ptr)); + } + + void destroy(Node *node) + { + if (!ObjectPool::erase(node)) + { + throw std::invalid_argument{"node"}; + } + } + +private: + /// Only "Graph" is permitted to invoke this private method. + void graph(Graph *g) { _graph = g; } + +private: + Graph *_graph = nullptr; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODE_POOL_H__ diff --git a/compiler/loco/include/loco/IR/NodeShape.h b/compiler/loco/include/loco/IR/NodeShape.h new file mode 100644 index 000000000..5eefd3c19 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodeShape.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODE_SHAPE_H__ +#define __LOCO_IR_NODE_SHAPE_H__ + +#include "loco/IR/Domain.h" + +#include "loco/IR/BiasShape.h" +#include "loco/IR/DepthwiseFilterShape.h" +#include "loco/IR/FeatureShape.h" +#include "loco/IR/FilterShape.h" +#include "loco/IR/MatrixShape.h" +#include "loco/IR/TensorShape.h" + +#include + +namespace loco +{ + +class NodeShape final +{ +public: + NodeShape() = default; + +public: + NodeShape(const BiasShape &shape) { set(shape); } + NodeShape(const DepthwiseFilterShape &shape) { set(shape); } + NodeShape(const FeatureShape &shape) { set(shape); } + NodeShape(const FilterShape &shape) { set(shape); } + NodeShape(const MatrixShape &shape) { set(shape); } + NodeShape(const TensorShape &shape) { set(shape); } + +public: + const Domain &domain(void) const { return _domain; } + +public: + void set(const BiasShape &); + void set(const DepthwiseFilterShape &); + void set(const FeatureShape &); + void set(const FilterShape &); + void set(const MatrixShape &); + void set(const TensorShape &); + +public: + template ShapeType as(void) const; + +private: + Domain _domain = Domain::Unknown; + std::vector _dims; +}; + +bool operator==(const NodeShape &lhs, const NodeShape &rhs); + +} // namespace loco + +#endif // __LOCO_IR_NODE_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Nodes.h b/compiler/loco/include/loco/IR/Nodes.h new file mode 100644 index 000000000..9aac48b6e --- /dev/null +++ b/compiler/loco/include/loco/IR/Nodes.h @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_NODES_H__ +#define __LOCO_IR_NODES_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Use.h" +#include "loco/IR/Domain.h" +#include "loco/IR/DataType.h" +#include "loco/IR/DataTypeTraits.h" +#include "loco/IR/Dimension.h" +#include "loco/IR/Window.h" +#include "loco/IR/Stride.h" +#include "loco/IR/Padding2D.h" +#include "loco/IR/PaddingND.h" +#include "loco/IR/TensorAxis.h" +#include "loco/IR/TensorAxisSet.h" +#include "loco/IR/FeatureCodec.h" +#include "loco/IR/FilterCodec.h" +#include "loco/IR/DepthwiseFilterCodec.h" +#include "loco/IR/MatrixCodec.h" +#include "loco/IR/NodeMixins.h" +#include "loco/IR/CanonicalNodeDecl.h" +#include "loco/IR/GraphInputIndex.h" +#include "loco/IR/GraphOutputIndex.h" + +namespace loco +{ + +class Graph; +class GraphInput; +class GraphOutput; + +/** + * @brief Make a value visible to user + */ +class Push /* to user */ final + : public CanonicalNodeDef::Mixin> +{ +public: + Push() = default; + +public: + Node *from(void) const { return at(0)->node(); } + void from(Node *node) { at(0)->node(node); } + +public: + void index(const GraphOutputIndex &index); + + /** + * @brief Get associated output index + * + * The behavior of this method is undefined when "index" is not set before. + * + * NOTE This method intentionally returns "GraphOutputIndex" instead of "const GraphOutputIndex &" + * not to expose the internal implementation details. + */ + GraphOutputIndex index(void) const; + + /** + * @brief Check whether index is initialized + * + * NOTE "indexed" method does not validate whether index is in a valid range + */ + bool indexed(void) const { return _index != -1; } + +private: + int64_t _index = -1; // Uninitialized +}; + +void link(GraphOutput *, Push *push); + +/// @brief Find a Push node with a given output index +Push *push_node(Graph *g, const GraphOutputIndex &index); + +/** + * @brief Create a value from user data + */ +class Pull /* from user */ final + : public CanonicalNodeDef::Mixin, + With::Mixin> +{ +public: + Pull() = default; + +public: + void index(const GraphInputIndex &index); + + /** + * @brief Get associated input index + * + * The behavior of this method is undefined when "index" is not set before. + * + * NOTE This method intentionally returns "GraphInputIndex" instead of "const GraphInputIndex &" + * not to expose the internal implementation details. + */ + GraphInputIndex index(void) const; + + /** + * @brief Check whether index is initialized + * + * NOTE "indexed" method does not validate whether index is in a valid range + */ + bool indexed(void) const { return _index != -1; } + +public: + void dtype(const DataType &d); + DataType dtype(void) const; + +private: + int64_t _index = -1; // Uninitialized + + /** + * @brief Locally cached data type attribute + * + * TODO Remove this cache once all the clients are updated + */ + DataType _dtype = DataType::Unknown; +}; + +void link(GraphInput *, Pull *pull); + +/// @brief Find a Pull node with a given input index +Pull *pull_node(Graph *g, const GraphInputIndex &index); + +/** + * @brief Create a new value identical to its input + * + * This node may encode memory transfer (such as CPU -> GPU or GPU -> CPU) + */ +class Forward final : public CanonicalNodeDef::Mixin> +{ +public: + Forward() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input + */ +class ReLU final : public CanonicalNodeDef::Mixin> +{ +public: + ReLU() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input capping the units at 6. + */ +class ReLU6 final : public CanonicalNodeDef::Mixin> +{ +public: + ReLU6() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input by tanh + */ +class Tanh final : public CanonicalNodeDef::Mixin> +{ +public: + Tanh() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a value from constant byte array + * + * @note ConstGen assumes "lexical memory layout". + * + * Let us assume that a 'ConstGen' generates a constant tensor of shape "S". + * for each valid index I, the corresponding value comes from offset(S, I) + * where the implementation of "offset" is given as follows: + * + * uint32_t stride(TensorShape shape, uint32_t axis) { + * uint32_t res = 1; + * for (uint32_t n = rank(shape) - 1; n > axis; --n) { res *= shape.dim(n); } + * return res; + * } + * + * uint32_t offset(TensorShape shape, TensorIndex index) { + * uint32_t res = 0; + * for (uint32_t n = 0; n < rank(shape); ++n) { res += index.at(n) * stride(shape, n); } + * return res; + * } + */ +class ConstGen final + : public CanonicalNodeDef::Mixin, + With::Mixin, With::Mixin> +{ +public: + ConstGen() = default; + +public: + /** + * @brief Return the number of reserved elements + * @note This method returns the number of ELEMENT (not BYTE). + */ + template uint32_t size(void) const; + + /** + * @brief Adjust the number of reserved elements + */ + template void size(uint32_t size); + + /** + * @brief Get the element at a given position + * @require at(n) is valid only when n < size() + */ + template const typename DataTypeImpl
::Type &at(uint32_t n) const; + + /** + * @brief Update the element at a given position + * @require at(n) is valid only when n < size() + */ + template typename DataTypeImpl
::Type &at(uint32_t n); + +private: + /// @brief Data + std::vector _data; +}; + +/** + * @brief 2D Max Pooling + * + * MaxPool2D takes as input a feature map, and produces another feature map + * + * --- + * Any valid MaxPool2D nodes SHOULD satisfy the following conditions. + * + * Let us define several helper functions that takes a MaxPool2D nodes first: + * - IFM_DOMAIN returns the domain of its input + * - IFM_H returns the height of its input. + * - IFM_W returns the width of its input. + * - PAD_T returns the top padding required over its input + * - PAD_B returns the bottom padding required over its input + * - PAD_L returns the left padding required over its input + * - PAD_R returns the right padding required over its input + * - WIN_H returns the height of its receptive field. + * - WIN_W returns the width of its receptive field. + * - STRIDE_H returns the vertical(= on height) stride. + * - STRIDE_W returns the horizontal(= on width) stride. + * + * Condition 1 + * Statement + * + * A valid MaxPool2D node M SHOULD satisfy the following condition: + * - IFM_DOMAIN(M) == Feature + * + * Motivation + * + * There are many possible ways to encode a feature map as a tensor. + * - e.g. NCHW/NHWC/... + * + * In order to give some freedom on memory layout to backend, loco requires a feature map + * value to be explicitly encoded via FeatureEncode. + * + * Condition 2: + * Statement + * + * A valid MaxPool2D node M SHOULD satisfy the following conditions: + * - (IFM_H(M) + PAD_T(M) + PAD_B(M) - WIN_H(M)) % STRIDE_H(M) == 0 + * - (IFM_W(M) + PAD_L(M) + PAD_R(M) - WIN_W(M)) % STRIDE_W(M) == 0 + * + * Motivation + * + * The output shape may differ for each NN framework when these conditions do not hold. + * + * In order to mitigate such a difference among NN frameworks, loco requires these conditions + * for MaxPool2D nodes. + * + * This means that each frontend implementation SHOULD insert appropriate padding/trimming node + * before/after MaxPool2D node according to the semantics of the corresponding NN framework. + * --- + */ +class MaxPool2D final : public CanonicalNodeDef::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Window<2> *window(void) const { return &_window; } + Window<2> *window(void) { return &_window; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + // Pad + Padding2D _pad; + // Window + Window<2> _window; + // Stride + Stride<2> _stride; +}; + +/** + * @brief 2D Average Pooling + * + * @note Follows MaxPool2D (TODO: describe difference) + */ +class AvgPool2D final : public CanonicalNodeDef::Mixin> +{ +public: + enum class Convention + { + Unknown, + // Use the number of elements in each receptive field as a divisor + Full, + // Use the number of valid (non-padding) elements in each receptive field as a divisor + Valid + }; + +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + +public: + Convention convention(void) const { return _convention; } + void convention(const Convention &convention) { _convention = convention; } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Window<2> *window(void) const { return &_window; } + Window<2> *window(void) { return &_window; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Convention _convention = Convention::Unknown; + Padding2D _pad; + Window<2> _window; + Stride<2> _stride; +}; + +/** + * @brief Create a feature map from a tensor + */ +class FeatureEncode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FeatureEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a feature map + */ +class FeatureDecode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FeatureDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr &&dec) { _dec = std::move(dec); } + +private: + /// @NOTE "decoder" is mandatory + std::unique_ptr _dec{nullptr}; +}; + +/** + * @brief Create a filter from a tensor + */ +class FilterEncode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FilterEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a filter + */ +class FilterDecode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FilterDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr _dec{nullptr}; +}; + +/** + * @brief Create a depthwise filter from a tensor + */ +class DepthwiseFilterEncode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + DepthwiseFilterEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a depthwise filter + */ +class DepthwiseFilterDecode final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + DepthwiseFilterDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr _dec{nullptr}; +}; + +enum class ReshapeType +{ + Fixed, // shape is known at compile time + // Add another type for a case when shape is not known at compile time +}; + +template class Reshape; + +/** + * @brief Reshape a tensor to another tensor whose shape is known at compile time + * + * @note This class reshapes the shape of an input tensor to _shape. + * Each dimension of _shape should be known at compile time. + * Any dimension of _shape should be greater than 0. + * + * Interpreter or runtime should lexicographically copy an input tensor into an output tensor. + * For example, values of an input tesor of shape [2, 2, 2, 2] will be copied into an output + * tensor of new shape [4, 4] like the following: + * input[0, 0, 0, 0] => output [0, 0] + * input[0, 0, 0, 1] => output [0, 1] + * input[0, 0, 1, 0] => output [0, 2] + * ... + * input[1, 1, 1, 1] => output [3, 3] + */ +template <> +class Reshape final + : public CanonicalNodeDef::Mixin, + With::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +using FixedReshape = Reshape; + +/** + * @brief Concatenate two tensors + * + * Given an axis, TensorConcat takes as input two tensors and produces a tensor + * concatenated along the given axis. + */ +class TensorConcat final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { at(1)->node(node); } + +public: + uint32_t axis(void) const { return _axis; } + void axis(uint32_t val) { _axis = val; } + +private: + // Axis + uint32_t _axis{0}; +}; + +/** + * @brief 2D Spatial Convolution + */ +class Conv2D final : public CanonicalNodeDef::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Depthwise 2D Convolution + */ +class DepthwiseConv2D final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Reduce type functions + */ +enum class ReduceFunc +{ + Mean, // ReduceMean + // TODO Support other reduce operations +}; + +/** + * @brief Computes ReduceFunc operations for Tensor domain + * @note All the reduce functions always keep dimensions + */ +class TensorReduce final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + const TensorAxisSet *axes(void) const { return &_axes; } + TensorAxisSet *axes(void) { return &_axes; } + +public: + ReduceFunc func(void) const { return _func; } + void func(ReduceFunc func) { _func = func; } + +private: + TensorAxisSet _axes; + ReduceFunc _func; +}; + +/** + * @brief 2D Transposed Convolution + * + * @note TransposedConv2D have a few important conventions that IR users should + * understand and follow, so please check below notice carefully. + * + * + * 1. What is 'input' and 'output' + * + * For loco canonical TransposedConv2D, 'input' and 'output' mean actual input + * and output node of TransposedConv2D node. Be careful that some other + * frameworks may use opposite sense, especially TensorFlow which is inspired by + * backpropagation of convolution. + * For example, loco::TransposedConv2D::ifm() means actual input feature map + * node that is sourced into TransposedConv2D. + * + * 2. How to read kernel representation + * + * TransposedConv2D::ker() should be a node of Filter domain. Following is what + * each FilterAxis means as a kernel of TransposedConv2D: + * - FilterAxis::Height : kernel's height + * - FilterAxis::Width : kernel's width + * - FilterAxis::Depth : IFM's channel depth + * - FilterAxis::Count : OFM's channel depth + * TODO We may refactor FilterAxis as follow to reduce ambiguity: + * - FilterAxis::Height -> FilterAxis::H + * - FilterAxis::Width -> FilterAxis::W + * - FilterAxis::Depth -> FilterAxis::I + * - FilterAxis::Count -> FilterAxis::O + * + * + * 3. Tight fit rule + * + * TransposedConv2D have no information about its output shape. Instead, it + * always satisfy following 'tight fit' rule for horizontal and vertical + * dimension: + * + * O = S * ( I - 1 ) + F - P + * + * where + * O: output size + * S: stride + * I: input size + * F: effective kernal(filter) size + * P: whole pad size (= front + rear pad) + * + * With this, output shape is uniquely determined by all inputs and attributes. + */ +class TransposedConv2D final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Computes softmax activations + */ +template class Softmax; + +/** +* @brief Computes softmax activations for Tensor domain +*/ +template <> +class Softmax final + : public CanonicalNodeDef::Mixin> +{ +public: + Softmax() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { return at(0)->node(node); } + + uint32_t axis(void) const { return _axis; } + void axis(uint32_t axis) { _axis = axis; } + +private: + uint32_t _axis = 0; +}; + +using TensorSoftmax = Softmax; + +/** + * @brief Create a "Tensor" from a "Bias" + */ +class BiasDecode final : public CanonicalNodeDef::Mixin> +{ +public: + BiasDecode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a "Bias" from a "Tensor" + * + * BiasEncode currently requires a rank-1 tensor as its input. + */ +class BiasEncode final : public CanonicalNodeDef::Mixin> +{ +public: + BiasEncode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Produce a value of domain D from an input value (of domain D) and a bias + */ +template class BiasAdd; + +/** + * @brief Add Tensor and Bias + * + * for each valid tensor index I + * out(I) = value(I) + bias(I.at(axis)) + */ +template <> +class BiasAdd final + : public CanonicalNodeDef::Mixin> +{ +public: + BiasAdd() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + + Node *bias(void) const { return at(1)->node(); } + void bias(Node *node) { return at(1)->node(node); } + + uint32_t axis(void) const { return _axis; } + void axis(uint32_t axis) { _axis = axis; } + +private: + uint32_t _axis = 0; +}; + +// +// Alias for external users +// +// loco::TensorBiasAdd +// vs. +// loco::BiasAdd +// +using TensorBiasAdd = BiasAdd; + +/** + * @brief Add Feature and Bias along "depth" axis + * + * for each valid feature index (b, ch, row, col) + * out(b, ch, row, col) = value(b, ch, row, col) + bias(ch) + */ +template <> +class BiasAdd final + : public CanonicalNodeDef::Mixin> +{ +public: + BiasAdd() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + + Node *bias(void) const { return at(1)->node(); } + void bias(Node *node) { return at(1)->node(node); } +}; + +using FeatureBiasAdd = BiasAdd; + +/** + * @brief Pads a tensor with constant value + * + * Pads a input tensor according to the padding with constant value. + * + * The dimension of each axis n of the output is + * output.dim(n) = padding.front(n) + input.dim(n) + padding.back(n) + * + * For example, input tensor of shape [1, 2] with + * + * padding.front(0) = 1; + * padding.back(0) = 2; + * + * padding.front(1) = 3; + * padding.back(1) = 4; + * + * will be a output tensor of shape + * [padding.front(0) + 1 + padding.back(0), padding.front(1) + 2 + padding.back(1)] = [4,9]. + */ +class TensorConstantPad final + : public CanonicalNodeDef::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + Node *constant(void) const { return at(1)->node(); } + void constant(Node *node) { at(1)->node(node); } + +public: + const PaddingND *padding(void) const { return &_padding; } + PaddingND *padding(void) { return &_padding; } + +private: + PaddingND _padding; +}; + +/** + * @brief Elementwise Add lhs and rhs + */ +class EltwiseAdd final : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseAdd() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Maximum of lhs and rhs + * + * o = (l > r) ? l : r (element-wise) + */ +class EltwiseMax final : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseMax() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Mul lhs and rhs + */ +class EltwiseMul final : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseMul() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Sub lhs and rhs + */ +class EltwiseSub final : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseSub() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Div lhs and rhs + */ +class EltwiseDiv final : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseDiv() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Sqrt of input + */ +class EltwiseSqrt final + : public CanonicalNodeDef::Mixin> +{ +public: + EltwiseSqrt() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Duplicate elements along specified axes + * + * TensorBroadcast takes a tensor and produces another tensor with the same rank but HIGHER + * dimensionality. + * + * To create such a tensor. TensorBroadcast duplicates the element along the specified axes. + * + * It is possible to control the degree of duplication with a partial map from TensorAxis to + * Dimension. + * + * TODO Explain the constraints (The dimension of inputs for specified axes SHOULD BE 1). + * TODO Explain the operation semantics + */ +class TensorBroadcast final + : public CanonicalNodeDef::Mixin> +{ +public: + TensorBroadcast() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + class Mapping final + { + public: + Mapping() = default; + + public: + bool defined(const TensorAxis &axis) const; + + const Dimension &dim(const TensorAxis &axis) const; + Dimension &dim(const TensorAxis &axis); + + private: + std::map _content; + }; + + Mapping *mapping(void) { return &_mapping; } + const Mapping *mapping(void) const { return &_mapping; } + +private: + Mapping _mapping; +}; + +/** + * @brief Create Matrix from Tensor + * + * MatrixEncode currently requires a rank-2 Tensor as its input. + */ +class MatrixEncode final + : public CanonicalNodeDef::Mixin> +{ +public: + MatrixEncode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + MatrixEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr _enc{nullptr}; +}; + +/** + * @brief Create Tensor from Matrix + * + * MatrixDecode currently requires a Matrix as its input. + */ +class MatrixDecode final + : public CanonicalNodeDef::Mixin> +{ +public: + MatrixDecode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + MatrixDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr _dec{nullptr}; +}; + +/** + * @brief Matrix Multiplication lhs and rhs + * + * LHS and RHS must be on Matrix domain + */ +class MatMul final : public CanonicalNodeDef::Mixin> +{ +public: + MatMul() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Permute an input + * + * In the following case, + * + * output = loco::TensorTranspose(input) + * + * perm()->axis(output's axis) = input's axis + * + * Input and output belong to tensor domain. + */ +class TensorTranspose final + : public CanonicalNodeDef::Mixin> +{ +public: + TensorTranspose() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { return at(0)->node(node); } + + class Perm final + { + public: + Perm() = default; + + public: + uint32_t size() const { return _vals.size(); } + void size(uint32_t size) { _vals.resize(size); } + + const TensorAxis &axis(TensorAxis n) const { return _vals[n]; } + TensorAxis &axis(TensorAxis n) { return _vals[n]; } + + private: + std::vector _vals; + }; + + Perm *perm(void) { return &_perm; } + const Perm *perm(void) const { return &_perm; } + +private: + Perm _perm; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODES_H__ diff --git a/compiler/loco/include/loco/IR/Padding2D.h b/compiler/loco/include/loco/IR/Padding2D.h new file mode 100644 index 000000000..30557a891 --- /dev/null +++ b/compiler/loco/include/loco/IR/Padding2D.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_PADDING2D_H__ +#define __LOCO_IR_PADDING2D_H__ + +#include + +namespace loco +{ + +class Padding2D final +{ +public: + Padding2D() : _top{0}, _bottom{0}, _left{0}, _right{0} + { + // DO NOTHING + } + +public: + Padding2D(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) + : _top{top}, _bottom{bottom}, _left{left}, _right{right} + { + // DO NOTHING + } + +public: + uint32_t top(void) const { return _top; } + void top(uint32_t value) { _top = value; } + +public: + uint32_t bottom(void) const { return _bottom; } + void bottom(uint32_t value) { _bottom = value; } + +public: + uint32_t left(void) const { return _left; } + void left(uint32_t value) { _left = value; } + +public: + uint32_t right(void) const { return _right; } + void right(uint32_t value) { _right = value; } + +private: + uint32_t _top; + uint32_t _bottom; + uint32_t _left; + uint32_t _right; +}; + +} // namespace loco + +#endif // __LOCO_IR_PADDING2D_H__ diff --git a/compiler/loco/include/loco/IR/PaddingND.h b/compiler/loco/include/loco/IR/PaddingND.h new file mode 100644 index 000000000..59be73943 --- /dev/null +++ b/compiler/loco/include/loco/IR/PaddingND.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_PADDINGND_H__ +#define __LOCO_IR_PADDINGND_H__ + +#include +#include + +namespace loco +{ + +/** + * This class indicates how many pads to add before(front) and after(back) the contents of + * tensor in that dimension. + */ +class PaddingND final +{ + +public: + const uint32_t &front(uint32_t dim) const { return _front.at(dim); } + uint32_t &front(uint32_t dim) { return _front.at(dim); } + +public: + const uint32_t &back(uint32_t dim) const { return _back.at(dim); } + uint32_t &back(uint32_t dim) { return _back.at(dim); } + +public: + uint32_t rank(void) const { return _front.size(); } + void rank(uint32_t s) + { + _front.resize(s); + _back.resize(s); + } + +private: + std::vector _front; + std::vector _back; +}; + +} // namespace loco + +#endif // __LOCO_IR_PADDINGND_H__ diff --git a/compiler/loco/include/loco/IR/PermutingCodec.h b/compiler/loco/include/loco/IR/PermutingCodec.h new file mode 100644 index 000000000..60b05dcbb --- /dev/null +++ b/compiler/loco/include/loco/IR/PermutingCodec.h @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_PERMUTING_CODEC_H__ +#define __LOCO_IR_PERMUTING_CODEC_H__ + +#include "loco/IR/Domain.h" + +#include "loco/IR/FeatureAxis.h" +#include "loco/IR/FeatureCodec.h" +#include "loco/IR/FilterAxis.h" +#include "loco/IR/FilterCodec.h" +#include "loco/IR/DepthwiseFilterAxis.h" +#include "loco/IR/DepthwiseFilterCodec.h" +#include "loco/IR/MatrixAxis.h" +#include "loco/IR/MatrixCodec.h" +#include "loco/IR/TensorAxis.h" + +#include + +namespace loco +{ + +template class Permutation; +template class PermutingEncoder; +template class PermutingDecoder; + +/** + * @brief Mapping between Feature/Tensor Axis + */ +template <> class Permutation +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a tensor axis is specified for a given feature axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const FeatureAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given feature axis + * + * This method works correclty only when feature axis is mapped before. + */ + TensorAxis axis(const FeatureAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given feature axis + */ + TensorAxis &axis(const FeatureAxis &axis_f); + + TensorAxis operator[](const FeatureAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const FeatureAxis &axis_f) { return axis(axis_f); } + +private: + std::map _map; +}; + +template <> class PermutingEncoder final : public FeatureEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + FeatureShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const FeatureIndex &index) const override; + + std::unique_ptr clone(void) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +template <> class PermutingDecoder final : public FeatureDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const FeatureShape &tensor_shape) const override; + FeatureIndex value(const TensorIndex &index) const override; + + std::unique_ptr clone(void) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Mapping between Filter/Tensor Axis + */ +template <> class Permutation +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given filter axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const FilterAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given filter axis + * + * This method works correctly only for mapped filter axes. + */ + const TensorAxis &axis(const FilterAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given filter axis + */ + TensorAxis &axis(const FilterAxis &axis_f); + + TensorAxis operator[](const FilterAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const FilterAxis &axis_f) { return axis(axis_f); } + +private: + std::map _map; +}; + +/** + * @brief Permutation-based Tensor-to-Filter converter + */ +template <> class PermutingEncoder final : public FilterEncoder +{ +public: + PermutingEncoder() = default; + +public: + explicit PermutingEncoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + FilterShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const FilterIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Permutation-based Filter-to-Tensor converter + */ +template <> class PermutingDecoder final : public FilterDecoder +{ +public: + PermutingDecoder() = default; + +public: + explicit PermutingDecoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const FilterShape &tensor_shape) const override; + FilterIndex value(const TensorIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Mapping between DepthwiseFilter/Tensor Axis + */ +template <> class Permutation +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given depthwise filter axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const DepthwiseFilterAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given depthwise filter axis + * + * This method works correctly only for mapped depthwise filter axes. + */ + const TensorAxis &axis(const DepthwiseFilterAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given depthwise filter axis + */ + TensorAxis &axis(const DepthwiseFilterAxis &axis_f); + + TensorAxis operator[](const DepthwiseFilterAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const DepthwiseFilterAxis &axis_f) { return axis(axis_f); } + +private: + std::map _map; +}; + +/** + * @brief Permutation-based Tensor-to-DepthwiseFilter converter + */ +template <> class PermutingEncoder final : public DepthwiseFilterEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + DepthwiseFilterShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const DepthwiseFilterIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Permutation-based DepthwiseFilter-to-Tensor converter + */ +template <> class PermutingDecoder final : public DepthwiseFilterDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const DepthwiseFilterShape &shape) const override; + DepthwiseFilterIndex value(const TensorIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Mapping between Matrix/Tensor Axis + */ +template <> class Permutation +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given matrix axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const MatrixAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given matrix axis + * + * This method works correctly only for mapped matrix axes. + */ + TensorAxis axis(const MatrixAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given matrix axis + */ + TensorAxis &axis(const MatrixAxis &axis_f); + + TensorAxis operator[](const MatrixAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const MatrixAxis &axis_f) { return axis(axis_f); } + +private: + std::map _map; +}; + +/** + * @brief Permutation-based Tensor-to-Matrix converter + */ +template <> class PermutingEncoder final : public MatrixEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + MatrixShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const MatrixIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +/** + * @brief Permutation-based Matrix-to-Tensor converter + */ +template <> class PermutingDecoder final : public MatrixDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const MatrixShape &tensor_shape) const override; + MatrixIndex value(const TensorIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + +} // namespace loco + +#endif // __LOCO_IR_PERMUTING_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/Stride.h b/compiler/loco/include/loco/IR/Stride.h new file mode 100644 index 000000000..eb9d47115 --- /dev/null +++ b/compiler/loco/include/loco/IR/Stride.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_STRIDE_H__ +#define __LOCO_IR_STRIDE_H__ + +#include + +namespace loco +{ + +/** + * @brief Stride configuration for N-dimensional spatial operations + */ +template class Stride; + +/** + * @brief Stride configuration for 2D spatial operations + */ +template <> class Stride<2> final +{ +public: + uint32_t vertical(void) const { return _vertical; } + void vertical(uint32_t value) { _vertical = value; } + +public: + uint32_t horizontal(void) const { return _horizontal; } + void horizontal(uint32_t value) { _horizontal = value; } + +private: + uint32_t _vertical = 1; + uint32_t _horizontal = 1; +}; + +} // namespace loco + +#endif // __LOCO_IR_STRIDE_H__ diff --git a/compiler/loco/include/loco/IR/TensorAxis.h b/compiler/loco/include/loco/IR/TensorAxis.h new file mode 100644 index 000000000..c41da512e --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorAxis.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_TENSOR_AXIS_H__ +#define __LOCO_IR_TENSOR_AXIS_H__ + +#include + +namespace loco +{ + +using TensorAxis = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/TensorAxisSet.h b/compiler/loco/include/loco/IR/TensorAxisSet.h new file mode 100644 index 000000000..240dcc556 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorAxisSet.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_TENSOR_AXIS_SET_H__ +#define __LOCO_IR_TENSOR_AXIS_SET_H__ + +#include "loco/IR/TensorAxis.h" + +#include + +namespace loco +{ + +class TensorAxisSet final +{ +public: + TensorAxisSet() = default; + +public: + bool defined(const TensorAxis &axis) const { return _axes.find(axis) != _axes.end(); } + void insert(const TensorAxis &axis) { _axes.insert(axis); } + +private: + std::set _axes; +}; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_AXIS_SET_H__ diff --git a/compiler/loco/include/loco/IR/TensorIndex.h b/compiler/loco/include/loco/IR/TensorIndex.h new file mode 100644 index 000000000..8f2385104 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorIndex.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_TENSOR_INDEX_H__ +#define __LOCO_IR_TENSOR_INDEX_H__ + +#include + +namespace loco +{ + +// TODO Remove dependencies on angkor +using TensorIndex = nncc::core::ADT::tensor::Index; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/TensorShape.h b/compiler/loco/include/loco/IR/TensorShape.h new file mode 100644 index 000000000..af1066d52 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorShape.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_TENSOR_SHAPE_H__ +#define __LOCO_IR_TENSOR_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +#include +#include + +namespace loco +{ + +class TensorShape +{ +public: + TensorShape() = default; + TensorShape(std::initializer_list dims) : _dims(dims.begin(), dims.end()) {} + +public: + uint32_t rank(void) const { return _dims.size(); } + void rank(uint32_t r) { _dims.resize(r); } + + const Dimension &dim(uint32_t axis) const { return _dims.at(axis); } + Dimension &dim(uint32_t axis) { return _dims.at(axis); } + +private: + std::vector _dims; +}; + +/** + * @brief Return the number of elements in a tensor of given shape + * + * NOTE 1. + * + * "volume" returns 1 if the rank is 0. + * + * NOTE 2. + * + * "caller" SHOULD pass a valid shape that has no unknown dimension. + * - The behavior of "volume" on invalid is undefined. + * + */ +uint32_t element_count(const loco::TensorShape *tensor_shape); + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Use.h b/compiler/loco/include/loco/IR/Use.h new file mode 100644 index 000000000..a4db924e4 --- /dev/null +++ b/compiler/loco/include/loco/IR/Use.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_USE_H__ +#define __LOCO_IR_USE_H__ + +#include "loco/IR/Node.forward.h" + +namespace loco +{ + +/** + * @brief The edge between a node definition and its user. + * + * Note that this "Use" denotes **one** edge between a node and its users, + * and thus there are unique node and user for each Use. + * + * There will be multiple "Use" edges for the same node if there are multiple + * users. + * + * This class design is heavily inspired from "Use" class in LLVM. + */ +class Use final +{ +public: + /** + * @brief Construct Use with its user + * @note user SHOULD BE set on construction. + */ + Use(Node *user) : _user{user} + { + // DO NOTHING + } + + Use(const Use &) = delete; + Use(Use &&) = delete; + + ~Use() + { + // Unlink itself from the node + node(nullptr); + } + +public: + Node *node(void) const { return _node; } + void node(Node *node); + +public: + Node *user(void) const { return _user; } + +private: + Node *_node{nullptr}; + Node *_user{nullptr}; +}; + +} // namespace loco + +#endif // __LOCO_IR_USE_H__ diff --git a/compiler/loco/include/loco/IR/Verifier.h b/compiler/loco/include/loco/IR/Verifier.h new file mode 100644 index 000000000..8ff85e16f --- /dev/null +++ b/compiler/loco/include/loco/IR/Verifier.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_VERIFIER_H__ +#define __LOCO_IR_VERIFIER_H__ + +#include "loco/IR/Graph.h" + +#include + +namespace loco +{ + +/** + * @brief Possible error categories + * + * This enum class enumerates all the possible validation failure reasons. + * + * WARN DO NOT serialize this code. The tag value is subject to change. + */ +enum class ErrorCategory +{ + MissingArgument, + /* TO BE ADDED */ +}; + +/** + * @brief The details of each error + */ +template class ErrorDetail; + +/** + * @brief The details of MissingArgument error + */ +template <> class ErrorDetail +{ +public: + ErrorDetail(loco::Node *node, uint32_t index) : _node{node}, _index{index} + { + // DO NOTHING + } + +public: + /// @brief The node with missing arguments + loco::Node *node(void) const { return _node; } + /// @brief The missing argument index + uint32_t index(void) const { return _index; } + +private: + loco::Node *_node; + uint32_t _index; +}; + +/** + * @brief Error listener interface + * + * DOo NOT inherit this interface. Use DefaultErrorListener instead. + */ +struct IErrorListener +{ + virtual ~IErrorListener() = default; + + virtual void notify(const ErrorDetail &) = 0; +}; + +/** + * @brief Error listener (with default implementation) + */ +struct ErrorListener : public IErrorListener +{ + virtual ~ErrorListener() = default; + + void notify(const ErrorDetail &) override { return; } +}; + +/** + * @brief Validate a loco graph + * + * "valid" returns true if a given graph has no error. + * + * NOTE Given a valid(non-null) listener, "valid" notifies error details to the listener. + */ +bool valid(Graph *g, std::unique_ptr &&l = nullptr); + +} // namespace loco + +#endif // __LOCO_IR_VERIFIER_H__ diff --git a/compiler/loco/include/loco/IR/Window.h b/compiler/loco/include/loco/IR/Window.h new file mode 100644 index 000000000..604fea868 --- /dev/null +++ b/compiler/loco/include/loco/IR/Window.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_WINDOW_H__ +#define __LOCO_IR_WINDOW_H__ + +#include + +namespace loco +{ + +/** + * @brief ND Receptive Field Shape + * + * Window describes the shape of N-dimensional receptive field. + */ +template class Window; + +/** + * @brief 2D Receptive Field Shape + */ +template <> class Window<2> final +{ +public: + uint32_t vertical(void) const { return _vertical; } + void vertical(uint32_t value) { _vertical = value; } + +public: + uint32_t horizontal(void) const { return _horizontal; } + void horizontal(uint32_t value) { _horizontal = value; } + +private: + uint32_t _vertical = 1; + uint32_t _horizontal = 1; +}; + +} // namespace loco + +#endif // __LOCO_IR_WINDOW_H__ diff --git a/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h b/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h new file mode 100644 index 000000000..cd3bed405 --- /dev/null +++ b/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ + +#include "loco/Service/ShapeInferenceRule.h" + +namespace loco +{ + +/** + * @brief Shape inference rule for canonical dialect + */ +struct CanonicalShapeInferenceRule final : public ShapeInferenceRule +{ + bool support(const API &ver) const final; + bool recognize(const Dialect *) const final; + bool infer(const Node *, NodeShape &) const final; + void infer(const Context *, const Node *, Sink *) const final; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h b/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h new file mode 100644 index 000000000..1a6c85b42 --- /dev/null +++ b/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ + +#include "loco/Service/ShapeInferenceRule.h" + +#include + +namespace loco +{ + +/** + * @brief Shape inference rule for multiple dialects + */ +class MultiDialectShapeInferenceRule final : public ShapeInferenceRule +{ +public: + bool recognize(const Dialect *) const final; + bool infer(const Node *, NodeShape &) const final; + + /// @brief Bind a specific rule to a Dialect + MultiDialectShapeInferenceRule &bind(const Dialect *d, const ShapeInferenceRule *rule); + +private: + std::map _rules; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/ShapeInference.h b/compiler/loco/include/loco/Service/ShapeInference.h new file mode 100644 index 000000000..f7bc5d4d6 --- /dev/null +++ b/compiler/loco/include/loco/Service/ShapeInference.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_SERVICE_SHAPE_INFERENCE_H__ +#define __LOCO_SERVICE_SHAPE_INFERENCE_H__ + +#include "loco/Service/ShapeInferenceRule.h" +#include "loco/IR/Graph.h" + +/** + * @file This file implements dialect-agnostic shape inference framework + * + * HOW TO USE: + * + * loco::Graph *g = ...; + * loco::ShapeInferenceRule *rule = ...; + * loco::apply(rule).to(g); + * + */ +namespace loco +{ + +class ShapeInferenceSession +{ +public: + ShapeInferenceSession(const ShapeInferenceRule *rule) : _rule{rule} + { + // DO NOTHING + } + +public: + bool to(Graph *g) const; + +private: + const ShapeInferenceRule *_rule; +}; + +inline ShapeInferenceSession apply(ShapeInferenceRule *r) { return ShapeInferenceSession{r}; } + +struct ShapeInference +{ + static bool known(const Node *); + static NodeShape get(const Node *); + static void erase(Node *); +}; + +inline bool shape_known(const Node *node) { return ShapeInference::known(node); } +inline NodeShape shape_get(const Node *node) { return ShapeInference::get(node); } +inline void shape_erase(Node *node) { ShapeInference::erase(node); } + +} // namespace loco + +#endif // __LOCO_SERVICE_SHAPE_INFERENCE_H__ diff --git a/compiler/loco/include/loco/Service/ShapeInferenceRule.h b/compiler/loco/include/loco/Service/ShapeInferenceRule.h new file mode 100644 index 000000000..889f0b6b2 --- /dev/null +++ b/compiler/loco/include/loco/Service/ShapeInferenceRule.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ + +#include "loco/IR/Domain.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/Node.h" +#include "loco/IR/NodeShape.h" + +namespace loco +{ + +struct ShapeInferenceRule +{ + virtual ~ShapeInferenceRule() = default; + + enum class API + { + /** + * API v1 + * + * This API uses "shape_get" method to query the shape of other nodes. + */ + V1, + + /** + * API v2 + * + * This API uses a given context (defined below) to query the shape of other nodes. + */ + V2, + }; + + /// @brief Check whether a given API is available or not + virtual bool support(const API &api) const + { + // To be backward compatible + return api == API::V1; + } + + /// @brief Return true if this rule recognizes a given dialect + virtual bool recognize(const Dialect *) const = 0; + + /** + * @brief Infer node's shape + * + * WARNING!! + * + * Implementation SHOULD return true only when it succeeds in inference! + * + */ + virtual bool infer(const Node *, NodeShape &) const = 0; + + // + // API v2 + // + struct Context + { + virtual ~Context() = default; + + virtual bool known(const Node *node) const = 0; + virtual NodeShape get(const Node *node) const = 0; + }; + + struct Sink + { + virtual ~Sink() = default; + + // TODO Add methods for error reporting + + // Each ShapeInferenceRule SHOULD invoke one of okay and fail before it returns + virtual void okay(const NodeShape &) = 0; + virtual void fail(void) = 0; + }; + + // WARNING! Invoke this method only when API v2 is supported + virtual void infer(const Context *, const Node *, Sink *) const; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/TypeInference.h b/compiler/loco/include/loco/Service/TypeInference.h new file mode 100644 index 000000000..c2ce1a4c7 --- /dev/null +++ b/compiler/loco/include/loco/Service/TypeInference.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_SERVICE_TYPE_INFERENCE_H__ +#define __LOCO_SERVICE_TYPE_INFERENCE_H__ + +#include "loco/IR/DataType.h" + +#include "loco/IR/Node.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/Graph.h" + +#include + +/** + * @file This file implements dialect-agnostic type inference framework. + * + * HOW TO USE: + * + * loco::Graph *g = ...; + * loco::TypeInferenceRule *rule = ...; + * loco::apply(rule).to(g); + * + */ +namespace loco +{ + +struct TypeInferenceRule +{ + virtual ~TypeInferenceRule() = default; + + /// @brief Return true if this rule recognizes a given dialect + virtual bool recognize(const Dialect *) const = 0; + + /** + * Framework guarantees the followings: + * + * 1. Framework tries to infer the data type of each node only after the data type of all of + * its valid (= non-nullptr) argument nodes is inferred. + * 2. The result of preceding "infer" is accessible through below dtype_get method. + * - This holds only when preceding "infer" returns true. + */ + virtual bool infer(const Node *, DataType &) const = 0; +}; + +/** + * @brief Type Inference Rule for Canonical Dialect + */ +struct CanonicalTypeInferenceRule final : public TypeInferenceRule +{ + bool recognize(const Dialect *) const final; + bool infer(const Node *, DataType &) const final; +}; + +/** + * @brief Type Inference Rule for multiple dialects + */ +class MultiDialectTypeInferenceRule final : public TypeInferenceRule +{ +public: + bool recognize(const Dialect *) const final; + bool infer(const Node *, DataType &) const final; + + /// @brief Bind a specific rule to a Dialect + MultiDialectTypeInferenceRule &bind(const Dialect *d, const TypeInferenceRule *rule); + +private: + std::map _rules; +}; + +class TypeInferenceSession +{ +public: + TypeInferenceSession(const TypeInferenceRule *rule) : _rule{rule} + { + // DO NOTHING + } + +public: + bool to(Graph *g) const; + +private: + const TypeInferenceRule *_rule; +}; + +inline TypeInferenceSession apply(TypeInferenceRule *r) { return TypeInferenceSession{r}; } + +struct TypeInference +{ + static bool known(const Node *); + static DataType get(const Node *); + static void erase(Node *); +}; + +inline bool dtype_known(const Node *node) { return TypeInference::known(node); } +inline DataType dtype_get(const Node *node) { return TypeInference::get(node); } +inline void dtype_erase(Node *node) { TypeInference::erase(node); } + +} // namespace loco + +#endif // __LOCO_SERVICE_TYPE_INFERENCE_H__ diff --git a/compiler/loco/src/ADT/AnnotatedItem.test.cpp b/compiler/loco/src/ADT/AnnotatedItem.test.cpp new file mode 100644 index 000000000..42113ff7b --- /dev/null +++ b/compiler/loco/src/ADT/AnnotatedItem.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/ADT/AnnotatedItem.h" + +#include +#include + +namespace +{ + +struct Annotation +{ + virtual ~Annotation() = default; +}; + +template struct DerivedAnnotation final : public Annotation +{ + static std::unique_ptr> make(void) + { + return stdex::make_unique>(); + } +}; + +} // namespace + +TEST(AnnotatedItemTest, annotation) +{ + loco::AnnotatedItem<::Annotation> item; + + ASSERT_EQ(item.annot>(), nullptr); + + item.annot(DerivedAnnotation<0>::make()); + + ASSERT_NE(item.annot>(), nullptr); + ASSERT_EQ(item.annot>(), nullptr); + + item.annot>(nullptr); + ASSERT_EQ(item.annot>(), nullptr); + + // Below check guarantees that "annot(nullptr)" is allowed even when there is no annotation. + // This guarantee allows us to simplify code for some cases. + // + // Let us consider the following example: + // + // void f(loco::AnnotatedItem *item) + // { + // /* DO SOMETHING */ + // if (cond) { item->annot(nullptr); + // } + // + // void g(loco::AnnotatedItem *item) + // { + // f(item); + // item->annot(nullptr); + // } + // + // The implementation of "g" gets complicated if annot(nullptr) is not allowed if there is + // no annotation. + // + item.annot>(nullptr); +} diff --git a/compiler/loco/src/ADT/ObjectPool.cpp b/compiler/loco/src/ADT/ObjectPool.cpp new file mode 100644 index 000000000..d15a30a99 --- /dev/null +++ b/compiler/loco/src/ADT/ObjectPool.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/ADT/ObjectPool.h" + +// This file validates "ObjectPool.h". Pleaes DO NOT remove this file. diff --git a/compiler/loco/src/IR/Algorithm.cpp b/compiler/loco/src/IR/Algorithm.cpp new file mode 100644 index 000000000..712e29975 --- /dev/null +++ b/compiler/loco/src/IR/Algorithm.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Algorithm.h" + +#include +#include +#include + +namespace +{ + +class Frame final +{ +public: + Frame(loco::Node *ptr) : _ptr{ptr}, _pos{-1} + { + // DO NOTHING + } + +public: + loco::Node *ptr(void) const { return _ptr; } + int64_t pos(void) const { return _pos; } + + loco::Node &node(void) const { return *_ptr; } + + void advance(void) { _pos += 1; } + +private: + loco::Node *_ptr = nullptr; + int64_t _pos = -1; +}; + +} // namespace + +namespace loco +{ + +// TODO Support cyclic graphs +std::vector postorder_traversal(const std::vector &roots) +{ + std::vector res; + + std::set visited_nodes; + std::stack frames; + + auto visited = [&visited_nodes](loco::Node *node) { + return visited_nodes.find(node) != visited_nodes.end(); + }; + + // NOTE There is not much difference between "auto" and "auto &" as node is of "loco::Node *" + // type. + for (auto node : roots) + { + assert((node != nullptr) && "root is invalid"); + frames.push(Frame{node}); + } + + while (!frames.empty()) + { + auto &top_frame = frames.top(); + + if (top_frame.pos() == -1) + { + if (visited(top_frame.ptr())) + { + frames.pop(); + continue; + } + visited_nodes.insert(top_frame.ptr()); + } + + top_frame.advance(); + + assert(top_frame.pos() >= 0); + + if (top_frame.pos() < static_cast(top_frame.node().arity())) + { + // Let's visit the next argument + // + // NOTE "next" may be nullptr if a graph is under construction. + if (auto next = top_frame.node().arg(top_frame.pos())) + { + frames.push(Frame{next}); + } + } + else + { + // Let's visit the current argument (all the arguments are already visited) + auto curr = top_frame.ptr(); + res.emplace_back(curr); + frames.pop(); + } + } + + return res; +} + +std::set active_nodes(const std::vector &roots) +{ + // This implementation works but may be inefficient + // + // TODO Use efficient implementation if necessary + auto nodes = postorder_traversal(roots); + return std::set{nodes.begin(), nodes.end()}; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/Algorithm.test.cpp b/compiler/loco/src/IR/Algorithm.test.cpp new file mode 100644 index 000000000..f0a3585c0 --- /dev/null +++ b/compiler/loco/src/IR/Algorithm.test.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Algorithm.h" +#include "loco/IR/Graph.h" + +#include + +#include + +namespace +{ + +bool contains(const std::vector &vec, loco::Node *val) +{ + return std::any_of(vec.begin(), vec.end(), [val](loco::Node *node) { return node == val; }); +} + +bool contains(const std::set &s, loco::Node *val) +{ + return std::any_of(s.begin(), s.end(), [val](loco::Node *node) { return node == val; }); +} + +} // namespace + +TEST(AlgorithmTest, postorder_traversal) +{ + auto g = loco::make_graph(); + + auto pull_1 = g->nodes()->create(); + auto push = g->nodes()->create(); + + push->from(pull_1); + + // Create a dummy node unreachable from the above "push" node + g->nodes()->create(); + + auto seq = loco::postorder_traversal({push}); + + ASSERT_EQ(seq.size(), 2); + ASSERT_EQ(seq.at(0), pull_1); + ASSERT_EQ(seq.at(1), push); +} + +TEST(AlgorithmTest, postorder_traversal_visit_once) +{ + auto g = loco::make_graph(); + + // Create a network of the following form: + // + // Push1 Push2 <-- outputs + // \ / + // Pull <-- input + // + auto pull = g->nodes()->create(); + auto push_1 = g->nodes()->create(); + auto push_2 = g->nodes()->create(); + + push_1->from(pull); + push_2->from(pull); + + auto seq = loco::postorder_traversal({push_1, push_2}); + + ASSERT_EQ(seq.size(), 3); + ASSERT_TRUE(contains(seq, pull)); + ASSERT_TRUE(contains(seq, push_1)); + ASSERT_TRUE(contains(seq, push_2)); +} + +TEST(AlgorithmTest, postorder_traversal_incomplte_graph) +{ + auto g = loco::make_graph(); + + // Create a network of the following form: + // + // TensorConcat + // / \ + // Pull X + // + auto pull = g->nodes()->create(); + auto concat = g->nodes()->create(); + + concat->lhs(pull); + + auto seq = loco::postorder_traversal({concat}); + + ASSERT_EQ(seq.size(), 2); + ASSERT_EQ(seq.at(0), pull); + ASSERT_EQ(seq.at(1), concat); +} + +TEST(AlgorithmTest, active_nodes) +{ + auto g = loco::make_graph(); + + auto pull = g->nodes()->create(); + auto push = g->nodes()->create(); + + push->from(pull); + + // NOTE This new Push node is unnecessary to compute "push" + g->nodes()->create(); + + auto s = loco::active_nodes({push}); + + ASSERT_EQ(s.size(), 2); + ASSERT_TRUE(contains(s, pull)); + ASSERT_TRUE(contains(s, push)); +} diff --git a/compiler/loco/src/IR/BiasShape.test.cpp b/compiler/loco/src/IR/BiasShape.test.cpp new file mode 100644 index 000000000..7f9b8dfed --- /dev/null +++ b/compiler/loco/src/IR/BiasShape.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/BiasShape.h" + +#include + +TEST(BiasShapeTest, default_constructor) +{ + loco::BiasShape shape; + + ASSERT_FALSE(shape.length().known()); +} diff --git a/compiler/loco/src/IR/CanonicalDialect.cpp b/compiler/loco/src/IR/CanonicalDialect.cpp new file mode 100644 index 000000000..ea956b80e --- /dev/null +++ b/compiler/loco/src/IR/CanonicalDialect.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/CanonicalDialect.h" +#include "loco/IR/Graph.h" +#include "loco/IR/Nodes.h" + +#include + +#include +#include + +namespace +{ + +struct GraphOutputIndexQueryServiceImpl final : public loco::GraphOutputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto push = dynamic_cast(node)) + { + return push->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + if (auto push = dynamic_cast(node)) + { + return push->index(); + } + throw std::invalid_argument("node"); + } +}; + +} // namespace + +namespace loco +{ + +CanonicalDialect::CanonicalDialect() +{ + service(stdex::make_unique()); +} + +Dialect *CanonicalDialect::get(void) +{ + static CanonicalDialect d; + return &d; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/CanonicalDialect.test.cpp b/compiler/loco/src/IR/CanonicalDialect.test.cpp new file mode 100644 index 000000000..96b48218d --- /dev/null +++ b/compiler/loco/src/IR/CanonicalDialect.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/CanonicalDialect.h" + +#include + +TEST(CanonicalDialectTest, get) +{ + auto d = loco::CanonicalDialect::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, loco::CanonicalDialect::get()); +} diff --git a/compiler/loco/src/IR/CanonicalNode.cpp b/compiler/loco/src/IR/CanonicalNode.cpp new file mode 100644 index 000000000..d5e13a415 --- /dev/null +++ b/compiler/loco/src/IR/CanonicalNode.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/CanonicalNode.h" +#include "loco/IR/CanonicalDialect.h" + +namespace loco +{ + +const Dialect *CanonicalNode::dialect(void) const { return CanonicalDialect::get(); } + +} // namespace loco diff --git a/compiler/loco/src/IR/CanonicalNode.test.cpp b/compiler/loco/src/IR/CanonicalNode.test.cpp new file mode 100644 index 000000000..cb61b5e83 --- /dev/null +++ b/compiler/loco/src/IR/CanonicalNode.test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/CanonicalNode.h" + +#include + +TEST(CanonicalNodeTest, visitor_with_user_default_impl) +{ + struct MyVisitor final : public loco::CanonicalNodeVisitor + { + // This visitor returns 128 if it visits a Forward node. + uint32_t visit(const loco::Forward *) final { return 128; } + + // Otherwise, this visitor returns 256. + uint32_t visit(const loco::Node *) final { return 256; } + }; + + loco::Forward forward; + loco::ConstGen constgen; + + MyVisitor v; + + ASSERT_EQ(forward.accept(&v), 128); + ASSERT_EQ(constgen.accept(&v), 256); +} + +TEST(CanonicalNodeTest, visitor) +{ + struct CountingVisitor final : public loco::CanonicalNodeVisitor + { + uint32_t visit(const loco::Forward *) final { return 1; } + }; + + // Visitor can visit constant nodes + const loco::Forward node; + + CountingVisitor v; + + ASSERT_EQ(node.accept(&v), 1); +} + +TEST(CanonicalNodeTest, mutable_visitor) +{ + struct ResetForward final : public loco::CanonicalNodeMutableVisitor + { + void visit(loco::Forward *node) final { node->input(nullptr); } + }; + + loco::Pull pull_node; + loco::Forward forward_node; + + forward_node.input(&pull_node); + + ResetForward v; + forward_node.accept(&v); + + ASSERT_EQ(forward_node.input(), nullptr); +} diff --git a/compiler/loco/src/IR/CanonicalOpcode.cpp b/compiler/loco/src/IR/CanonicalOpcode.cpp new file mode 100644 index 000000000..6355ecf1f --- /dev/null +++ b/compiler/loco/src/IR/CanonicalOpcode.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/CanonicalOpcode.h" + +// NOTE This file validates "CanonicalOpcode.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/DataType.cpp b/compiler/loco/src/IR/DataType.cpp new file mode 100644 index 000000000..56794dac7 --- /dev/null +++ b/compiler/loco/src/IR/DataType.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DataType.h" + +// This file validates "DataType.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/DataTypeTraits.test.cpp b/compiler/loco/src/IR/DataTypeTraits.test.cpp new file mode 100644 index 000000000..76d2515a9 --- /dev/null +++ b/compiler/loco/src/IR/DataTypeTraits.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DataTypeTraits.h" + +#include + +#include + +TEST(DataTypeTraitsTest, FLOAT32) +{ + auto obtained = std::type_index(typeid(loco::DataTypeImpl::Type)); + auto expected = std::type_index(typeid(float)); + + ASSERT_EQ(obtained, expected); +} diff --git a/compiler/loco/src/IR/DepthwiseFilterAxis.cpp b/compiler/loco/src/IR/DepthwiseFilterAxis.cpp new file mode 100644 index 000000000..9d58795b2 --- /dev/null +++ b/compiler/loco/src/IR/DepthwiseFilterAxis.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DepthwiseFilterAxis.h" + +// NOTE This file validates "DepthwiseFilterAxis.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/DepthwiseFilterCodec.cpp b/compiler/loco/src/IR/DepthwiseFilterCodec.cpp new file mode 100644 index 000000000..05a7fd723 --- /dev/null +++ b/compiler/loco/src/IR/DepthwiseFilterCodec.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DepthwiseFilterCodec.h" + +// NOTE This file validates "DepthwiseFilterCodec.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp b/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp new file mode 100644 index 000000000..202647cfc --- /dev/null +++ b/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DepthwiseFilterIndex.h" + +#include + +TEST(DepthwiseFilterIndexTest, default_constructor) +{ + loco::DepthwiseFilterIndex index; + + // All the values are 0 at the beginning + ASSERT_EQ(index.channel(), 0); + ASSERT_EQ(index.nth(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); +} + +TEST(DepthwiseFilterIndexTest, settet_and_getter) +{ + loco::DepthwiseFilterIndex index; + + // Set depth + index.channel() = 2; + + ASSERT_EQ(index.channel(), 2); + ASSERT_EQ(index.nth(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set multiplier + index.nth() = 3; + + ASSERT_EQ(index.channel(), 2); + ASSERT_EQ(index.nth(), 3); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set height + index.row() = 4; + + ASSERT_EQ(index.channel(), 2); + ASSERT_EQ(index.nth(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 0); + + // Set width + index.column() = 5; + + ASSERT_EQ(index.channel(), 2); + ASSERT_EQ(index.nth(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 5); +} diff --git a/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp b/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp new file mode 100644 index 000000000..2b9518c1f --- /dev/null +++ b/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DepthwiseFilterShape.h" + +#include + +TEST(DepthwiseFilterShapeTest, default_constructor) +{ + loco::DepthwiseFilterShape shape; + + ASSERT_FALSE(shape.depth().known()); + ASSERT_FALSE(shape.multiplier().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); +} + +TEST(DepthwiseFilterShapeTest, settet_and_getter) +{ + loco::DepthwiseFilterShape shape; + + // Set depth + shape.depth() = 2; + + ASSERT_TRUE(shape.depth().known()); + ASSERT_FALSE(shape.multiplier().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.depth(), 2); + + // Set multiplier + shape.multiplier() = 3; + + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.multiplier().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.depth(), 2); + ASSERT_EQ(shape.multiplier(), 3); + + // Set height + shape.height() = 4; + + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.multiplier().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.depth(), 2); + ASSERT_EQ(shape.multiplier(), 3); + ASSERT_EQ(shape.height(), 4); + + // Set width + shape.width() = 5; + + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.multiplier().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_TRUE(shape.width().known()); + + ASSERT_EQ(shape.depth(), 2); + ASSERT_EQ(shape.multiplier(), 3); + ASSERT_EQ(shape.height(), 4); + ASSERT_EQ(shape.width(), 5); +} diff --git a/compiler/loco/src/IR/Dialect.cpp b/compiler/loco/src/IR/Dialect.cpp new file mode 100644 index 000000000..a381b47eb --- /dev/null +++ b/compiler/loco/src/IR/Dialect.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Dialect.h" + +// NOTE This file validates "Dialect.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/Dialect.test.cpp b/compiler/loco/src/IR/Dialect.test.cpp new file mode 100644 index 000000000..312bb52ef --- /dev/null +++ b/compiler/loco/src/IR/Dialect.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Dialect.h" + +#include + +#include + +TEST(DialectTest, service) +{ + struct S0 final : public loco::DialectService + { + }; + struct S1 final : public loco::DialectService + { + }; + + struct MockDialect final : public loco::Dialect + { + MockDialect() { service(stdex::make_unique()); } + }; + + MockDialect dialect; + + ASSERT_EQ(dialect.service(), nullptr); + ASSERT_NE(dialect.service(), nullptr); +} diff --git a/compiler/loco/src/IR/DialectService.cpp b/compiler/loco/src/IR/DialectService.cpp new file mode 100644 index 000000000..fb8041e47 --- /dev/null +++ b/compiler/loco/src/IR/DialectService.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/DialectService.h" + +// NOTE This file validates "DialectService.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/Dimension.cpp b/compiler/loco/src/IR/Dimension.cpp new file mode 100644 index 000000000..0d11c83e8 --- /dev/null +++ b/compiler/loco/src/IR/Dimension.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +bool operator==(const Dimension &lhs, const Dimension &rhs) +{ + return lhs.known() && rhs.known() && lhs.value() == rhs.value(); +} + +bool operator==(const Dimension &lhs, uint32_t rhs) { return lhs.known() && lhs.value() == rhs; } +bool operator==(uint32_t lhs, const Dimension &rhs) { return rhs.known() && lhs == rhs.value(); } + +Dimension make_dimension(void) { return Dimension{}; } + +} // namespace loco diff --git a/compiler/loco/src/IR/Dimension.test.cpp b/compiler/loco/src/IR/Dimension.test.cpp new file mode 100644 index 000000000..4faf78ac8 --- /dev/null +++ b/compiler/loco/src/IR/Dimension.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Dimension.h" + +#include + +namespace +{ + +struct DimensionTest : public ::testing::Test +{ +protected: + uint32_t value(void) const { return _value; } + +private: + uint32_t const _value{3}; +}; + +} // namespace + +TEST_F(DimensionTest, default_constructor) +{ + loco::Dimension dim; + + ASSERT_FALSE(dim.known()); +} + +TEST_F(DimensionTest, value_constructor) +{ + loco::Dimension dim{value()}; + + ASSERT_TRUE(dim.known()); + ASSERT_EQ(dim.value(), value()); +} + +TEST_F(DimensionTest, set) +{ + loco::Dimension dim; + + dim.set(value()); + + ASSERT_TRUE(dim.known()); + ASSERT_EQ(dim.value(), value()); +} + +TEST_F(DimensionTest, unset) +{ + loco::Dimension dim{value()}; + + dim.unset(); + + ASSERT_FALSE(dim.known()); +} + +TEST_F(DimensionTest, operator_eq) +{ + loco::Dimension unknown; + loco::Dimension known{3}; + + // Compare uint32_t and an unknown dimension + ASSERT_FALSE(unknown == 3); + ASSERT_FALSE(3 == unknown); + + // Compare uint32_t and a known dimension + ASSERT_TRUE(known == 3); + ASSERT_TRUE(3 == known); + + ASSERT_FALSE(known == 4); + ASSERT_FALSE(4 == known); + + // Compare two known dimensions + loco::Dimension another_known{3}; + ASSERT_TRUE(known == another_known); + + // Compare two unknown dimensions + loco::Dimension unknown_a, unknown_b; + ASSERT_TRUE(unknown_a.known() == false && unknown_b.known() == false); + ASSERT_FALSE(unknown_a == unknown_b); +} + +TEST_F(DimensionTest, make_unknown_dimension) +{ + auto dim = loco::make_dimension(); + + ASSERT_FALSE(dim.known()); +} diff --git a/compiler/loco/src/IR/Domain.cpp b/compiler/loco/src/IR/Domain.cpp new file mode 100644 index 000000000..7bad04750 --- /dev/null +++ b/compiler/loco/src/IR/Domain.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Domain.h" + +// NOTE This file validates "Domain.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/FeatureAxis.cpp b/compiler/loco/src/IR/FeatureAxis.cpp new file mode 100644 index 000000000..b0f560677 --- /dev/null +++ b/compiler/loco/src/IR/FeatureAxis.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FeatureAxis.h" + +// NOTE This file validates "FeatureAxis.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/FeatureCodec.cpp b/compiler/loco/src/IR/FeatureCodec.cpp new file mode 100644 index 000000000..99d39a489 --- /dev/null +++ b/compiler/loco/src/IR/FeatureCodec.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FeatureCodec.h" + +// NOTE This file validates "FeatureCodec.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/FeatureIndex.test.cpp b/compiler/loco/src/IR/FeatureIndex.test.cpp new file mode 100644 index 000000000..82b563986 --- /dev/null +++ b/compiler/loco/src/IR/FeatureIndex.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FeatureIndex.h" + +#include + +TEST(FeatureIndexTest, default_constructor) +{ + loco::FeatureIndex index; + + // All the values are 0 at the beginning + ASSERT_EQ(index.batch(), 0); + ASSERT_EQ(index.channel(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); +} + +TEST(FeatureIndexTest, settet_and_getter) +{ + loco::FeatureIndex index; + + // Set count + index.batch() = 2; + + ASSERT_EQ(index.batch(), 2); + ASSERT_EQ(index.channel(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set channel + index.channel() = 3; + + ASSERT_EQ(index.batch(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set height + index.row() = 4; + + ASSERT_EQ(index.batch(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 0); + + // Set width + index.column() = 5; + + ASSERT_EQ(index.batch(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 5); +} diff --git a/compiler/loco/src/IR/FeatureShape.test.cpp b/compiler/loco/src/IR/FeatureShape.test.cpp new file mode 100644 index 000000000..59e25ac23 --- /dev/null +++ b/compiler/loco/src/IR/FeatureShape.test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FeatureShape.h" + +#include + +TEST(FeatureShapeTest, default_constructor) +{ + loco::FeatureShape shape; + + ASSERT_FALSE(shape.count().known()); + ASSERT_FALSE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); +} + +TEST(FeatureShapeTest, settet_and_getter) +{ + loco::FeatureShape shape; + + // Set count + shape.count() = 2; + + ASSERT_TRUE(shape.count().known()); + ASSERT_FALSE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + + // Set depth + shape.depth() = 3; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + + // Set height + shape.height() = 4; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + ASSERT_EQ(shape.height(), 4); + + // Set width + shape.width() = 5; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_TRUE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + ASSERT_EQ(shape.height(), 4); + ASSERT_EQ(shape.width(), 5); +} diff --git a/compiler/loco/src/IR/FilterAxis.cpp b/compiler/loco/src/IR/FilterAxis.cpp new file mode 100644 index 000000000..be4234e6a --- /dev/null +++ b/compiler/loco/src/IR/FilterAxis.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FilterAxis.h" + +// NOTE This file validates "FilterAxis.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/FilterCodec.cpp b/compiler/loco/src/IR/FilterCodec.cpp new file mode 100644 index 000000000..f48cf1821 --- /dev/null +++ b/compiler/loco/src/IR/FilterCodec.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FilterCodec.h" + +// NOTE This file validates "FilterCodec.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/FilterIndex.test.cpp b/compiler/loco/src/IR/FilterIndex.test.cpp new file mode 100644 index 000000000..58f38718e --- /dev/null +++ b/compiler/loco/src/IR/FilterIndex.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FilterIndex.h" + +#include + +TEST(FilterIndexTest, default_constructor) +{ + loco::FilterIndex index; + + // All the values are 0 at the beginning + ASSERT_EQ(index.nth(), 0); + ASSERT_EQ(index.channel(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); +} + +TEST(FilterIndexTest, settet_and_getter) +{ + loco::FilterIndex index; + + // Set count + index.nth() = 2; + + ASSERT_EQ(index.nth(), 2); + ASSERT_EQ(index.channel(), 0); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set channel + index.channel() = 3; + + ASSERT_EQ(index.nth(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 0); + ASSERT_EQ(index.column(), 0); + + // Set height + index.row() = 4; + + ASSERT_EQ(index.nth(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 0); + + // Set width + index.column() = 5; + + ASSERT_EQ(index.nth(), 2); + ASSERT_EQ(index.channel(), 3); + ASSERT_EQ(index.row(), 4); + ASSERT_EQ(index.column(), 5); +} diff --git a/compiler/loco/src/IR/FilterShape.test.cpp b/compiler/loco/src/IR/FilterShape.test.cpp new file mode 100644 index 000000000..ccb60ed76 --- /dev/null +++ b/compiler/loco/src/IR/FilterShape.test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/FilterShape.h" + +#include + +TEST(FilterShapeTest, default_constructor) +{ + loco::FilterShape shape; + + ASSERT_FALSE(shape.count().known()); + ASSERT_FALSE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); +} + +TEST(FilterShapeTest, settet_and_getter) +{ + loco::FilterShape shape; + + // Set count + shape.count() = 2; + + ASSERT_TRUE(shape.count().known()); + ASSERT_FALSE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + + // Set depth + shape.depth() = 3; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_FALSE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + + // Set height + shape.height() = 4; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_FALSE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + ASSERT_EQ(shape.height(), 4); + + // Set width + shape.width() = 5; + + ASSERT_TRUE(shape.count().known()); + ASSERT_TRUE(shape.depth().known()); + ASSERT_TRUE(shape.height().known()); + ASSERT_TRUE(shape.width().known()); + + ASSERT_EQ(shape.count(), 2); + ASSERT_EQ(shape.depth(), 3); + ASSERT_EQ(shape.height(), 4); + ASSERT_EQ(shape.width(), 5); +} diff --git a/compiler/loco/src/IR/Graph.cpp b/compiler/loco/src/IR/Graph.cpp new file mode 100644 index 000000000..1d8752252 --- /dev/null +++ b/compiler/loco/src/IR/Graph.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Graph.h" + +#include + +#include + +namespace +{ + +std::unique_ptr make_tensor_shape(std::initializer_list dims) +{ + auto tensor_shape = stdex::make_unique(); + + tensor_shape->rank(dims.size()); + { + uint32_t axis = 0; + for (auto it = dims.begin(); it != dims.end(); ++it) + { + tensor_shape->dim(axis++) = *it; + } + assert(axis == dims.size()); + } + + return std::move(tensor_shape); +} + +} // namespace + +namespace loco +{ + +void Mixin::shape(std::initializer_list dims) +{ + shape(make_tensor_shape(dims)); +} + +GraphInput *Graph::InputContext::create(void) +{ + return take(stdex::make_unique(size())); +} + +GraphOutput *Graph::OutputContext::create(void) +{ + return take(stdex::make_unique(size())); +} + +std::set all_nodes(loco::Graph *g) +{ + std::set res; + + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + res.insert(g->nodes()->at(n)); + } + + return res; +} + +std::vector input_nodes(const Graph *g) +{ + std::map table; + + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + auto node = g->nodes()->at(n); + + if (auto service = node->dialect()->service()) + { + if (service->associated(node)) + { + auto input_index = service->index(node); + assert(table.find(input_index) == table.end()); + table[input_index] = node; + } + } + } + + std::vector res; + + for (uint32_t n = 0; n < g->inputs()->size(); ++n) + { + auto it = table.find(n); + res.emplace_back(it == table.end() ? nullptr : it->second); + } + + return res; +} + +std::vector output_nodes(loco::Graph *g) +{ + std::map table; + + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + auto node = g->nodes()->at(n); + + if (auto service = node->dialect()->service()) + { + if (service->associated(node)) + { + auto output_index = service->index(node); + assert(table.find(output_index) == table.end()); + table[output_index] = node; + } + } + } + + std::vector res; + + for (uint32_t n = 0; n < g->outputs()->size(); ++n) + { + auto it = table.find(n); + res.emplace_back(it == table.end() ? nullptr : it->second); + } + + return res; +} + +std::unique_ptr make_graph(void) { return std::unique_ptr{new Graph}; } + +} // namespace loco diff --git a/compiler/loco/src/IR/Graph.test.cpp b/compiler/loco/src/IR/Graph.test.cpp new file mode 100644 index 000000000..6df630b0f --- /dev/null +++ b/compiler/loco/src/IR/Graph.test.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Graph.h" + +#include + +namespace +{ + +// @brief Mockup class for loco::NamedEntity +struct NamedElement final : private loco::NamedEntity +{ + LOCO_NAMED_ENTITY_EXPOSE; +}; + +} // namespace + +TEST(NamedTest, constructor) +{ + NamedElement elem; + + ASSERT_EQ(elem.name(), ""); +} + +TEST(NamedTest, setter_and_getter) +{ + NamedElement elem; + + elem.name("name"); + ASSERT_EQ(elem.name(), "name"); +} + +TEST(DataTypedMixinTest, constructor) +{ + loco::Mixin mixin; + + ASSERT_EQ(mixin.dtype(), loco::DataType::Unknown); +} + +TEST(DataTypedMixinTest, setter_and_getter) +{ + loco::Mixin mixin; + + mixin.dtype(loco::DataType::FLOAT32); + ASSERT_EQ(mixin.dtype(), loco::DataType::FLOAT32); +} + +TEST(TensorShapedMixinTest, setter_and_getter) +{ + loco::Mixin mixin; + + mixin.shape({1, 2, 3, 4}); + ASSERT_NE(mixin.shape(), nullptr); + ASSERT_EQ(mixin.shape()->rank(), 4); + ASSERT_EQ(mixin.shape()->dim(0), 1); + ASSERT_EQ(mixin.shape()->dim(1), 2); + ASSERT_EQ(mixin.shape()->dim(2), 3); + ASSERT_EQ(mixin.shape()->dim(3), 4); +} + +TEST(GraphTest, create_and_destroy_node) +{ + auto g = loco::make_graph(); + + auto pull = g->nodes()->create(); + + ASSERT_NO_THROW(g->nodes()->destroy(pull)); + ASSERT_THROW(g->nodes()->destroy(pull), std::invalid_argument); +} + +TEST(GraphTest, create_input) +{ + auto g = loco::make_graph(); + + auto input = g->inputs()->create(); + + // TODO Add more checks + ASSERT_EQ(input->shape(), nullptr); + ASSERT_EQ(input->index(), 0); +} + +TEST(GraphTest, create_output) +{ + auto g = loco::make_graph(); + + auto output = g->outputs()->create(); + + // TODO Add more checks + ASSERT_EQ(output->shape(), nullptr); + ASSERT_EQ(output->index(), 0); +} + +namespace +{ +// temp node with multple params for ctor. loco::CanonicalOpcode::ReLU is used for simplicity +class ParamCtorNode + : public loco::CanonicalNodeDef::Mixin> +{ +public: + ParamCtorNode(int i, float f) + { + _i = i; + _f = f; + } + + int i() { return _i; } + float f() { return _f; } + +private: + int _i; + float _f; +}; +} // namespace + +TEST(GraphTest, consturctor_with_param_node) +{ + auto g = loco::make_graph(); + + auto test_node = g->nodes()->create(22, 11.11); + + ASSERT_EQ(test_node->graph(), g.get()); + ASSERT_EQ(const_cast(test_node)->graph(), g.get()); + + ASSERT_EQ(test_node->i(), 22); + ASSERT_FLOAT_EQ(test_node->f(), 11.11); + + ASSERT_NO_THROW(g->nodes()->destroy(test_node)); + ASSERT_THROW(g->nodes()->destroy(test_node), std::invalid_argument); +} + +TEST(GraphTest, getters_over_const_instance) +{ + auto g = loco::make_graph(); + + auto pull = g->nodes()->create(); + auto push = g->nodes()->create(); + + loco::link(g->inputs()->create(), pull); + loco::link(g->outputs()->create(), push); + + auto ptr = const_cast(g.get()); + + EXPECT_EQ(ptr->nodes()->size(), 2); + EXPECT_EQ(ptr->inputs()->size(), 1); +} + +TEST(GraphTest, graph_node_enumeration) +{ + auto g = loco::make_graph(); + + auto pull_1 = g->nodes()->create(); + auto push_1 = g->nodes()->create(); + + auto nodes = loco::all_nodes(g.get()); + + // Returns true if "nodes" includes a given node + auto member = [&nodes](loco::Node *node) { return nodes.find(node) != nodes.end(); }; + + ASSERT_EQ(nodes.size(), 2); + ASSERT_TRUE(member(pull_1)); + ASSERT_TRUE(member(push_1)); +} + +TEST(GraphTest, graph_inout_enumeration) +{ + auto g = loco::make_graph(); + + std::vector pull_nodes; + + auto pull_1 = g->nodes()->create(); + auto pull_2 = g->nodes()->create(); + auto pull_3 = g->nodes()->create(); + + auto push_1 = g->nodes()->create(); + auto push_2 = g->nodes()->create(); + auto push_3 = g->nodes()->create(); + + loco::link(g->inputs()->create(), pull_2); + loco::link(g->inputs()->create(), pull_1); + + loco::link(g->outputs()->create(), push_1); + loco::link(g->outputs()->create(), push_3); + + auto output_nodes = loco::output_nodes(g.get()); + + ASSERT_EQ(output_nodes.size(), 2); + ASSERT_EQ(output_nodes.at(0), push_1); + ASSERT_EQ(output_nodes.at(1), push_3); +} + +TEST(GraphTest, graph_name) +{ + auto g = loco::make_graph(); + + g->name("HelloGraph"); + ASSERT_TRUE(g->name() == "HelloGraph"); +} + +TEST(GraphTest, graph_name_nullptr_NEG) +{ + auto g = loco::make_graph(); + + EXPECT_ANY_THROW(g->name(nullptr)); +} diff --git a/compiler/loco/src/IR/GraphInputIndex.cpp b/compiler/loco/src/IR/GraphInputIndex.cpp new file mode 100644 index 000000000..0c94d704c --- /dev/null +++ b/compiler/loco/src/IR/GraphInputIndex.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/GraphInputIndex.h" + +// NOTE This file validates "GraphInputIndex.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/GraphOutputIndex.cpp b/compiler/loco/src/IR/GraphOutputIndex.cpp new file mode 100644 index 000000000..e6fdb9f94 --- /dev/null +++ b/compiler/loco/src/IR/GraphOutputIndex.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/GraphOutputIndex.h" + +// NOTE This file validates "GraphOutputIndex.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/MatrixAxis.cpp b/compiler/loco/src/IR/MatrixAxis.cpp new file mode 100644 index 000000000..d0773f758 --- /dev/null +++ b/compiler/loco/src/IR/MatrixAxis.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/MatrixAxis.h" + +// NOTE This file validates "MatrixAxis.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/MatrixCodec.cpp b/compiler/loco/src/IR/MatrixCodec.cpp new file mode 100644 index 000000000..87ae42610 --- /dev/null +++ b/compiler/loco/src/IR/MatrixCodec.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/MatrixCodec.h" + +// NOTE This file validates "MatrixCodec.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/MockupNode.h b/compiler/loco/src/IR/MockupNode.h new file mode 100644 index 000000000..ec56c90e2 --- /dev/null +++ b/compiler/loco/src/IR/MockupNode.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_IR_MOCKUP_NODE_H__ +#define __LOCO_IR_MOCKUP_NODE_H__ + +#include "loco/IR/Use.h" +#include "loco/IR/Node.h" + +namespace +{ + +struct MockDialect final : public loco::Dialect +{ + static loco::Dialect *get(void) + { + static MockDialect d; + return &d; + } +}; + +// @brief Mockup node for internal testing +class MockupNode final : public loco::Node +{ +public: + MockupNode() = default; + +public: + const loco::Dialect *dialect(void) const final { return MockDialect::get(); } + uint32_t opnum(void) const final { return 0; } + + uint32_t arity(void) const final { return 1; } + Node *arg(uint32_t N) const final { return _arg.node(); } + void drop(void) final { _arg.node(nullptr); } + + Node *in(void)const { return _arg.node(); } + void in(Node *node) { _arg.node(node); } + +private: + loco::Use _arg{this}; +}; + +} // namespace + +#endif // __LOCO_IR_MOCKUP_NODE_H__ diff --git a/compiler/loco/src/IR/Node.cpp b/compiler/loco/src/IR/Node.cpp new file mode 100644 index 000000000..90ec5c997 --- /dev/null +++ b/compiler/loco/src/IR/Node.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Node.h" +#include "loco/IR/Use.h" + +#include + +namespace loco +{ + +Node::~Node() +{ + // To detect dangling references + assert(_uses.size() == 0); +} + +std::set preds(const Node *node) +{ + std::set res; + + for (uint32_t n = 0; n < node->arity(); ++n) + { + if (auto pred = node->arg(n)) + { + res.insert(pred); + } + } + + return res; +} + +std::set succs(const Node *node) +{ + std::set res; + + for (auto use : node->_uses) + { + auto user = use->user(); + assert(user != nullptr); + res.insert(user); + } + + return res; +} + +Subst::Subst(Node *from) : _from{from} +{ + // _from SHOULD be valid + assert(_from != nullptr); +} + +void Subst::with(Node *into) const +{ + if (_from == into) + { + return; + } + + auto *uses = &(_from->_uses); + + while (!uses->empty()) + { + auto use = *(uses->begin()); + use->node(into); + } +} + +Subst replace(Node *node) +{ + // Let's create Subst! + return Subst{node}; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/Node.test.cpp b/compiler/loco/src/IR/Node.test.cpp new file mode 100644 index 000000000..00e444465 --- /dev/null +++ b/compiler/loco/src/IR/Node.test.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Node.h" + +#include "MockupNode.h" + +#include + +TEST(NodeTest, preds) +{ + ::MockupNode arg; + ::MockupNode node; + + node.in(&arg); + + auto preds = loco::preds(&node); + + ASSERT_EQ(preds.size(), 1); + ASSERT_NE(preds.find(&arg), preds.end()); +} + +TEST(NodeTest, succs) +{ + ::MockupNode node; + ::MockupNode succ_1; + ::MockupNode succ_2; + + succ_1.in(&node); + succ_2.in(&node); + + auto succs = loco::succs(&node); + + ASSERT_EQ(succs.size(), 2); + ASSERT_NE(succs.find(&succ_1), succs.end()); + ASSERT_NE(succs.find(&succ_2), succs.end()); +} + +TEST(NodeTest, replace_with) +{ + ::MockupNode node_1; + ::MockupNode node_2; + + ::MockupNode node_3; + ::MockupNode node_4; + + node_3.in(&node_1); + node_4.in(&node_2); + + // The following holds at this point + // - node_3 USE node_1 + // - node_4 USE node_2 + ASSERT_EQ(node_3.in(), &node_1); + ASSERT_EQ(node_4.in(), &node_2); + + // Replace all the usage of node_1 with node_2 + replace(&node_1).with(&node_2); + + // The following holds at this point + // - node_3 USE node_2 + // - node_4 USE node_2 + ASSERT_EQ(node_3.in(), &node_2); + ASSERT_EQ(node_4.in(), &node_2); +} + +TEST(NodeTest, constructor) +{ + MockupNode node; + + // graph() SHOULD return nullptr if node is not constructed through "Graph" + ASSERT_EQ(node.graph(), nullptr); +} + +// TODO Rewrite this as a FixedAritry mix-in test +#if 0 +TEST(FixedArityNodeTest, constructor) +{ + struct DerivedNode final : public loco::FixedArityNode<1, loco::Node> + { + loco::Dialect *dialect(void) const final { return MockDialect::get(); } + uint32_t opnum(void) const final { return 0; } + }; + + DerivedNode node; + + ASSERT_EQ(node.arity(), 1); + ASSERT_EQ(node.arg(0), nullptr); +} +#endif diff --git a/compiler/loco/src/IR/NodeMixins.cpp b/compiler/loco/src/IR/NodeMixins.cpp new file mode 100644 index 000000000..66037b17a --- /dev/null +++ b/compiler/loco/src/IR/NodeMixins.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/NodeMixins.h" + +// NOTE This file validates "NodeMixins.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/NodePool.cpp b/compiler/loco/src/IR/NodePool.cpp new file mode 100644 index 000000000..553f15eb5 --- /dev/null +++ b/compiler/loco/src/IR/NodePool.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/NodePool.h" + +namespace loco +{ + +NodePool::~NodePool() +{ + // Drop all the references before deallocation + for (uint32_t n = 0; n < size(); ++n) + { + at(n)->drop(); + } +} + +} // namespace loco diff --git a/compiler/loco/src/IR/NodeShape.cpp b/compiler/loco/src/IR/NodeShape.cpp new file mode 100644 index 000000000..0130cfbdb --- /dev/null +++ b/compiler/loco/src/IR/NodeShape.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/NodeShape.h" + +#include +#include + +// +// BiasShape Support +// +namespace loco +{ + +void NodeShape::set(const BiasShape &shape) +{ + _domain = Domain::Bias; + + _dims.resize(1); + _dims.at(0) = shape.length(); +} + +template <> BiasShape NodeShape::as(void) const +{ + assert(_domain == Domain::Bias); + + BiasShape res; + + res.length() = _dims.at(0); + + return res; +} + +} // namespace loco + +// +// DepthwiseFilterShape Support +// +namespace loco +{ + +void NodeShape::set(const DepthwiseFilterShape &shape) +{ + _domain = Domain::DepthwiseFilter; + + _dims.resize(4); + _dims.at(0) = shape.multiplier(); + _dims.at(1) = shape.depth(); + _dims.at(2) = shape.height(); + _dims.at(3) = shape.width(); +} + +template <> DepthwiseFilterShape NodeShape::as(void) const +{ + assert(_domain == Domain::DepthwiseFilter); + + DepthwiseFilterShape res; + + res.multiplier() = _dims.at(0); + res.depth() = _dims.at(1); + res.height() = _dims.at(2); + res.width() = _dims.at(3); + + return res; +} + +} // namespace loco + +// +// FeatureShape Support +// +namespace loco +{ + +void NodeShape::set(const FeatureShape &shape) +{ + _domain = Domain::Feature; + + _dims.resize(4); + _dims.at(0) = shape.count(); + _dims.at(1) = shape.depth(); + _dims.at(2) = shape.height(); + _dims.at(3) = shape.width(); +} + +template <> FeatureShape NodeShape::as(void) const +{ + assert(_domain == Domain::Feature); + + FeatureShape res; + + res.count() = _dims.at(0); + res.depth() = _dims.at(1); + res.height() = _dims.at(2); + res.width() = _dims.at(3); + + return res; +} + +} // namespace loco + +// +// FilterShape Support +// +namespace loco +{ + +void NodeShape::set(const FilterShape &shape) +{ + _domain = Domain::Filter; + + _dims.resize(4); + _dims.at(0) = shape.count(); + _dims.at(1) = shape.depth(); + _dims.at(2) = shape.height(); + _dims.at(3) = shape.width(); +} + +template <> FilterShape NodeShape::as(void) const +{ + assert(_domain == Domain::Filter); + + FilterShape res; + + res.count() = _dims.at(0); + res.depth() = _dims.at(1); + res.height() = _dims.at(2); + res.width() = _dims.at(3); + + return res; +} + +} // namespace loco + +// +// MatrixShape Support +// +namespace loco +{ + +void NodeShape::set(const MatrixShape &shape) +{ + _domain = Domain::Matrix; + + _dims.resize(2); + _dims.at(0) = shape.height(); + _dims.at(1) = shape.width(); +} + +template <> MatrixShape NodeShape::as(void) const +{ + assert(_domain == Domain::Matrix); + + MatrixShape res; + + res.height() = _dims.at(0); + res.width() = _dims.at(1); + + return res; +} + +} // namespace loco + +// +// TensorShape Support +// +namespace loco +{ + +void NodeShape::set(const TensorShape &shape) +{ + _domain = Domain::Tensor; + + _dims.resize(shape.rank()); + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + _dims.at(axis) = shape.dim(axis); + } +} + +template <> TensorShape NodeShape::as(void) const +{ + assert(_domain == Domain::Tensor); + + TensorShape res; + + res.rank(_dims.size()); + for (uint32_t axis = 0; axis < _dims.size(); ++axis) + { + res.dim(axis) = _dims.at(axis); + } + + return res; +} + +} // namespace loco + +namespace loco +{ + +bool operator==(const NodeShape &lhs, const NodeShape &rhs) +{ + if (lhs.domain() != rhs.domain()) + return false; + + switch (lhs.domain()) + { + case loco::Domain::Tensor: + { + auto lhs_t = lhs.as(); + auto rhs_t = rhs.as(); + if (lhs_t.rank() != rhs_t.rank()) + return false; + for (uint32_t axis = 0; axis < lhs_t.rank(); ++axis) + { + if (!(lhs_t.dim(axis) == rhs_t.dim(axis))) + return false; + } + return true; + } + + case loco::Domain::Feature: + { + auto lhs_f = lhs.as(); + auto rhs_f = rhs.as(); + + return (lhs_f.count() == rhs_f.count() && lhs_f.depth() == rhs_f.depth() && + lhs_f.height() == rhs_f.height() && lhs_f.width() == rhs_f.width()); + } + + case loco::Domain::Filter: + { + auto lhs_f = lhs.as(); + auto rhs_f = rhs.as(); + + return (lhs_f.count() == rhs_f.count() && lhs_f.depth() == rhs_f.depth() && + lhs_f.height() == rhs_f.height() && lhs_f.width() == rhs_f.width()); + } + + case loco::Domain::DepthwiseFilter: + { + auto lhs_f = lhs.as(); + auto rhs_f = rhs.as(); + + return (lhs_f.multiplier() == rhs_f.multiplier() && lhs_f.depth() == rhs_f.depth() && + lhs_f.height() == rhs_f.height() && lhs_f.width() == rhs_f.width()); + } + + case loco::Domain::Bias: + { + auto lhs_f = lhs.as(); + auto rhs_f = rhs.as(); + + return (lhs_f.length() == rhs_f.length()); + } + + case loco::Domain::Matrix: + { + auto lhs_f = lhs.as(); + auto rhs_f = rhs.as(); + + return (lhs_f.height() == rhs_f.height() && lhs_f.width() == rhs_f.width()); + } + + default: + throw std::runtime_error("Not supported domain for NodeShape equality"); + } + return false; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/NodeShape.test.cpp b/compiler/loco/src/IR/NodeShape.test.cpp new file mode 100644 index 000000000..4f092e024 --- /dev/null +++ b/compiler/loco/src/IR/NodeShape.test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/NodeShape.h" + +#include + +TEST(NodeShapeTest, default_constructor) +{ + loco::NodeShape node_shape; + + ASSERT_EQ(node_shape.domain(), loco::Domain::Unknown); +} + +TEST(NodeShapeTest, bias_shape_constructor) +{ + loco::BiasShape bias_shape; + + bias_shape.length() = 4; + + loco::NodeShape node_shape{bias_shape}; + + ASSERT_EQ(node_shape.domain(), loco::Domain::Bias); + ASSERT_EQ(node_shape.as().length(), 4); +} + +TEST(NodeShapeTest, dwfilter_shape_constructor) +{ + loco::DepthwiseFilterShape dwfilter_shape; + + dwfilter_shape.depth() = 2; + dwfilter_shape.multiplier() = 3; + dwfilter_shape.height() = 4; + dwfilter_shape.width() = 5; + + loco::NodeShape node_shape{dwfilter_shape}; + + ASSERT_EQ(node_shape.domain(), loco::Domain::DepthwiseFilter); + ASSERT_EQ(node_shape.as().depth(), 2); + ASSERT_EQ(node_shape.as().multiplier(), 3); + ASSERT_EQ(node_shape.as().height(), 4); + ASSERT_EQ(node_shape.as().width(), 5); +} + +TEST(NodeShapeTest, feature_shape_constructor) +{ + loco::FeatureShape feature_shape; + + feature_shape.count() = 2; + feature_shape.depth() = 3; + feature_shape.height() = 4; + feature_shape.width() = 5; + + loco::NodeShape node_shape{feature_shape}; + + ASSERT_EQ(node_shape.domain(), loco::Domain::Feature); + ASSERT_EQ(node_shape.as().count(), 2); + ASSERT_EQ(node_shape.as().depth(), 3); + ASSERT_EQ(node_shape.as().height(), 4); + ASSERT_EQ(node_shape.as().width(), 5); +} + +TEST(NodeShapeTest, filter_shape_constructor) +{ + loco::FilterShape filter_shape; + + filter_shape.count() = 2; + filter_shape.depth() = 3; + filter_shape.height() = 4; + filter_shape.width() = 5; + + loco::NodeShape node_shape{filter_shape}; + + ASSERT_EQ(node_shape.domain(), loco::Domain::Filter); + ASSERT_EQ(node_shape.as().count(), 2); + ASSERT_EQ(node_shape.as().depth(), 3); + ASSERT_EQ(node_shape.as().height(), 4); + ASSERT_EQ(node_shape.as().width(), 5); +} + +TEST(NodeShapeTest, tensor_shape_constructor) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + tensor_shape.dim(0) = 4; + tensor_shape.dim(1) = 5; + + loco::NodeShape node_shape{tensor_shape}; + + ASSERT_EQ(node_shape.domain(), loco::Domain::Tensor); + ASSERT_EQ(node_shape.as().rank(), 2); + ASSERT_EQ(node_shape.as().dim(0), 4); + ASSERT_EQ(node_shape.as().dim(1), 5); +} + +TEST(NodeShapeTest, copy_constructible) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + tensor_shape.dim(0) = 4; + tensor_shape.dim(1) = 5; + + loco::NodeShape orig{tensor_shape}; + loco::NodeShape copy{orig}; // Call Copy Constructor + + ASSERT_EQ(copy.domain(), loco::Domain::Tensor); + ASSERT_EQ(copy.as().rank(), 2); + ASSERT_EQ(copy.as().dim(0), 4); + ASSERT_EQ(copy.as().dim(1), 5); +} diff --git a/compiler/loco/src/IR/Nodes.cpp b/compiler/loco/src/IR/Nodes.cpp new file mode 100644 index 000000000..133b69430 --- /dev/null +++ b/compiler/loco/src/IR/Nodes.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Nodes.h" +#include "loco/IR/Graph.h" + +#include +#include + +// This file validates "Nodes.h". Please DO NOT remove this file. +namespace +{ + +/** + * @note This function is currently only used in assert. Compiler will + * warn/error this function as unused in Release build. + * Making inline will make compiler happy. + */ +// Is it possible to update lhs as rhs? +inline bool dtype_assignable(loco::DataType lhs, loco::DataType rhs) +{ + if (lhs == loco::DataType::Unknown) + { + return true; + } + + // lhs is already known, and thus rhs should be matched + return lhs == rhs; +} + +} // namespace + +/** + * Push + */ +namespace loco +{ + +void Push::index(const GraphOutputIndex &index) +{ + // Push internally stores "GraphOutputIndex" as int64_t + _index = static_cast(index); +} + +GraphOutputIndex Push::index(void) const +{ + assert(_index >= std::numeric_limits::min()); + assert(_index <= std::numeric_limits::max()); + return static_cast(_index); +} + +void link(GraphOutput *output, Push *push) { push->index(output->index()); } + +Push *push_node(Graph *g, const GraphOutputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto push = dynamic_cast(g->nodes()->at(n))) + { + if (push->indexed() && push->index() == index) + { + return push; + } + } + } + return nullptr; +} + +} // namespace loco + +/** + * Pull + */ +namespace loco +{ + +void Pull::index(const GraphInputIndex &index) +{ + // ASSUMPTION + // + // It is possible to update index multiple times, but only with the same value! + assert(!indexed() or _index == index); + + if (indexed()) + { + assert(_index == index); + return; + } + + // Push internally stores "GraphInputIndex" as int64_t + _index = static_cast(index); + + // ASSUMPTION: The return value of graph() never changes! + if (graph() != nullptr && _dtype != loco::DataType::Unknown) + { + // Update Graph-level input only if it is not yet specified + if (graph()->inputs()->at(_index)->dtype() == DataType::Unknown) + { + graph()->inputs()->at(_index)->dtype(_dtype); + } + assert(graph()->inputs()->at(_index)->dtype() == _dtype); + graph()->inputs()->at(_index)->dtype(_dtype); + + // Reset the locally cached data + _dtype = DataType::Unknown; + } +} + +GraphInputIndex Pull::index(void) const +{ + assert(_index >= std::numeric_limits::min()); + assert(_index <= std::numeric_limits::max()); + return static_cast(_index); +} + +void Pull::dtype(const DataType &dt) +{ + // ASSUMPTION: "dtype" is never invalidated! + assert(dt != loco::DataType::Unknown); + // ASSUMPTION + // + // It is possible to update index multiple times, but only with the same value! + if (indexed()) + { + assert(dtype_assignable(graph()->inputs()->at(_index)->dtype(), dt)); + graph()->inputs()->at(_index)->dtype(dt); + return; + } + + // Use local cache + _dtype = dt; +} + +DataType Pull::dtype(void) const +{ + if (graph() != nullptr and _index >= 0) + { + assert(_dtype == DataType::Unknown); + return graph()->inputs()->at(_index)->dtype(); + } + else + { + return _dtype; + } +} + +void link(GraphInput *input, Pull *pull) { pull->index(input->index()); } + +Pull *pull_node(Graph *g, const GraphInputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto pull = dynamic_cast(g->nodes()->at(n))) + { + if (pull->indexed() && pull->index() == index) + { + return pull; + } + } + } + return nullptr; +} + +} // namespace loco + +/** + * ConstGen + */ +namespace loco +{ + +template uint32_t ConstGen::size(void) const +{ + assert(dtype() == DT); + assert(_data.size() % sizeof(typename DataTypeImpl
::Type) == 0); + return _data.size() / sizeof(typename DataTypeImpl
::Type); +} + +template void ConstGen::size(uint32_t l) +{ + assert(dtype() == DT); + _data.resize(l * sizeof(typename DataTypeImpl
::Type)); +} + +template const typename DataTypeImpl
::Type &ConstGen::at(uint32_t n) const +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +template typename DataTypeImpl
::Type &ConstGen::at(uint32_t n) +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +#define INSTANTIATE(DT) \ + template uint32_t ConstGen::size
(void) const; \ + template void ConstGen::size
(uint32_t); \ + template const typename DataTypeImpl
::Type &ConstGen::at
(uint32_t) const; \ + template typename DataTypeImpl
::Type &ConstGen::at
(uint32_t); + +INSTANTIATE(DataType::S32); +INSTANTIATE(DataType::FLOAT32); + +#undef INSTANTIATE + +} // namespace loco + +/** + * TensorBroadcast + */ +namespace loco +{ + +bool TensorBroadcast::Mapping::defined(const TensorAxis &axis) const +{ + return _content.find(axis) != _content.end(); +} + +const Dimension &TensorBroadcast::Mapping::dim(const TensorAxis &axis) const +{ + return _content.at(axis); +} + +Dimension &TensorBroadcast::Mapping::dim(const TensorAxis &axis) { return _content[axis]; } + +} // namespace loco diff --git a/compiler/loco/src/IR/Nodes.test.cpp b/compiler/loco/src/IR/Nodes.test.cpp new file mode 100644 index 000000000..cd51f46c0 --- /dev/null +++ b/compiler/loco/src/IR/Nodes.test.cpp @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Nodes.h" +#include "loco/IR/CanonicalDialect.h" + +#include + +TEST(PushTest, constructor) +{ + loco::Push push_node; + + ASSERT_EQ(push_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(push_node.opcode(), loco::CanonicalOpcode::Push); + + ASSERT_FALSE(push_node.indexed()); +} + +TEST(PushTest, shape) +{ + const std::vector dims{1, 8, 16, 3}; + + loco::Pull push_node; + + push_node.shape({dims[0], dims[1], dims[2], dims[3]}); + + ASSERT_EQ(push_node.rank(), dims.size()); + ASSERT_EQ(push_node.dim(0), dims[0]); + ASSERT_EQ(push_node.dim(1), dims[1]); + ASSERT_EQ(push_node.dim(2), dims[2]); + ASSERT_EQ(push_node.dim(3), dims[3]); +} + +TEST(PullTest, constructor) +{ + loco::Pull pull_node; + + ASSERT_EQ(pull_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(pull_node.opcode(), loco::CanonicalOpcode::Pull); + + ASSERT_FALSE(pull_node.indexed()); + + ASSERT_EQ(pull_node.dtype(), loco::DataType::Unknown); + ASSERT_EQ(pull_node.rank(), 0); +} + +TEST(PullTest, shape) +{ + const std::vector dims{1, 8, 16, 3}; + + loco::Pull pull_node; + + pull_node.shape({dims[0], dims[1], dims[2], dims[3]}); + + ASSERT_EQ(pull_node.rank(), dims.size()); + ASSERT_EQ(pull_node.dim(0), dims[0]); + ASSERT_EQ(pull_node.dim(1), dims[1]); + ASSERT_EQ(pull_node.dim(2), dims[2]); + ASSERT_EQ(pull_node.dim(3), dims[3]); +} + +TEST(ForwardTest, constructor) +{ + loco::Forward forward_node; + + ASSERT_EQ(forward_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(forward_node.opcode(), loco::CanonicalOpcode::Forward); + + ASSERT_EQ(forward_node.input(), nullptr); +} + +TEST(ReLUTest, constructor) +{ + loco::ReLU relu_node; + + ASSERT_EQ(relu_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(relu_node.opcode(), loco::CanonicalOpcode::ReLU); + + ASSERT_EQ(relu_node.input(), nullptr); +} + +TEST(ReLU6Test, constructor) +{ + loco::ReLU6 relu6_node; + + ASSERT_EQ(relu6_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(relu6_node.opcode(), loco::CanonicalOpcode::ReLU6); + + ASSERT_EQ(relu6_node.input(), nullptr); +} + +TEST(ConstGenTest, constructor) +{ + loco::ConstGen constgen_node; + + ASSERT_EQ(constgen_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(constgen_node.opcode(), loco::CanonicalOpcode::ConstGen); + + ASSERT_EQ(constgen_node.dtype(), loco::DataType::Unknown); + ASSERT_EQ(constgen_node.rank(), 0); + + constgen_node.dtype(loco::DataType::FLOAT32); + ASSERT_EQ(constgen_node.dtype(), loco::DataType::FLOAT32); + + constgen_node.rank(2); + ASSERT_EQ(constgen_node.rank(), 2); + + constgen_node.dim(0) = 2; + constgen_node.dim(1) = 3; + + ASSERT_TRUE(constgen_node.dim(0).known()); + ASSERT_TRUE(constgen_node.dim(1).known()); + + ASSERT_EQ(constgen_node.dim(0), 2); + ASSERT_EQ(constgen_node.dim(1), 3); + + constgen_node.size(6); + + ASSERT_EQ(constgen_node.size(), 6); + + constgen_node.at(0) = 0.0f; // Set 0,0 + constgen_node.at(1) = 1.0f; // Set 0,1 + constgen_node.at(2) = 2.0f; // Set 0,2 + constgen_node.at(3) = 3.0f; // Set 1,0 + constgen_node.at(4) = 4.0f; // Set 1,1 + constgen_node.at(5) = 5.0f; // Set 1,2 + + ASSERT_EQ(constgen_node.at(0), 0.0f); + ASSERT_EQ(constgen_node.at(1), 1.0f); + ASSERT_EQ(constgen_node.at(2), 2.0f); + ASSERT_EQ(constgen_node.at(3), 3.0f); + ASSERT_EQ(constgen_node.at(4), 4.0f); + ASSERT_EQ(constgen_node.at(5), 5.0f); +} + +TEST(ConstGenTest, constructor_s32) +{ + loco::ConstGen constgen_node; + + ASSERT_EQ(constgen_node.dtype(), loco::DataType::Unknown); + ASSERT_EQ(constgen_node.rank(), 0); + + constgen_node.dtype(loco::DataType::S32); + ASSERT_EQ(constgen_node.dtype(), loco::DataType::S32); + + constgen_node.rank(2); + ASSERT_EQ(constgen_node.rank(), 2); + + constgen_node.dim(0) = 2; + constgen_node.dim(1) = 3; + + ASSERT_TRUE(constgen_node.dim(0).known()); + ASSERT_TRUE(constgen_node.dim(1).known()); + + ASSERT_EQ(constgen_node.dim(0), 2); + ASSERT_EQ(constgen_node.dim(1), 3); + + constgen_node.size(6); + + ASSERT_EQ(constgen_node.size(), 6); + + constgen_node.at(0) = 0; // Set 0,0 + constgen_node.at(1) = 1; // Set 0,1 + constgen_node.at(2) = 2; // Set 0,2 + constgen_node.at(3) = -3; // Set 1,0 + constgen_node.at(4) = -4; // Set 1,1 + constgen_node.at(5) = -5; // Set 1,2 + + ASSERT_EQ(constgen_node.at(0), 0); + ASSERT_EQ(constgen_node.at(1), 1); + ASSERT_EQ(constgen_node.at(2), 2); + ASSERT_EQ(constgen_node.at(3), -3); + ASSERT_EQ(constgen_node.at(4), -4); + ASSERT_EQ(constgen_node.at(5), -5); +} + +TEST(MaxPool2DTest, constructor) +{ + loco::MaxPool2D maxpool_node; + + ASSERT_EQ(maxpool_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(maxpool_node.opcode(), loco::CanonicalOpcode::MaxPool2D); + + ASSERT_EQ(maxpool_node.ifm(), nullptr); + + ASSERT_EQ(maxpool_node.pad()->top(), 0); + ASSERT_EQ(maxpool_node.pad()->bottom(), 0); + ASSERT_EQ(maxpool_node.pad()->left(), 0); + ASSERT_EQ(maxpool_node.pad()->right(), 0); + + ASSERT_EQ(maxpool_node.window()->vertical(), 1); + ASSERT_EQ(maxpool_node.window()->horizontal(), 1); + + ASSERT_EQ(maxpool_node.stride()->vertical(), 1); + ASSERT_EQ(maxpool_node.stride()->horizontal(), 1); +} + +TEST(MaxPool2DTest, pad) +{ + const uint32_t t = 1; + const uint32_t b = 2; + const uint32_t l = 3; + const uint32_t r = 4; + + loco::MaxPool2D maxpool_node; + + maxpool_node.pad()->top(t); + ASSERT_EQ(maxpool_node.pad()->top(), t); + + maxpool_node.pad()->bottom(b); + ASSERT_EQ(maxpool_node.pad()->bottom(), b); + + maxpool_node.pad()->left(l); + ASSERT_EQ(maxpool_node.pad()->left(), l); + + maxpool_node.pad()->right(r); + ASSERT_EQ(maxpool_node.pad()->right(), r); +} + +TEST(AvgPool2DTest, constructor) +{ + loco::AvgPool2D avgpool_node; + + ASSERT_EQ(avgpool_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(avgpool_node.opcode(), loco::CanonicalOpcode::AvgPool2D); + + ASSERT_EQ(avgpool_node.ifm(), nullptr); + + ASSERT_EQ(avgpool_node.convention(), loco::AvgPool2D::Convention::Unknown); + + ASSERT_EQ(avgpool_node.pad()->top(), 0); + ASSERT_EQ(avgpool_node.pad()->bottom(), 0); + ASSERT_EQ(avgpool_node.pad()->left(), 0); + ASSERT_EQ(avgpool_node.pad()->right(), 0); + + ASSERT_EQ(avgpool_node.window()->vertical(), 1); + ASSERT_EQ(avgpool_node.window()->horizontal(), 1); + + ASSERT_EQ(avgpool_node.stride()->vertical(), 1); + ASSERT_EQ(avgpool_node.stride()->horizontal(), 1); +} + +TEST(FeatureEncodeTest, constructor) +{ + loco::FeatureEncode feature_encode; + + ASSERT_EQ(feature_encode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(feature_encode.opcode(), loco::CanonicalOpcode::FeatureEncode); + + ASSERT_EQ(feature_encode.input(), nullptr); + ASSERT_EQ(feature_encode.encoder(), nullptr); +} + +TEST(FeatureDecodeTest, constructor) +{ + loco::FeatureDecode feature_decode; + + ASSERT_EQ(feature_decode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(feature_decode.opcode(), loco::CanonicalOpcode::FeatureDecode); + + ASSERT_EQ(feature_decode.input(), nullptr); + ASSERT_EQ(feature_decode.decoder(), nullptr); +} + +TEST(Reshape_Fixed_Test, constructor) +{ + loco::Reshape reshape; + + ASSERT_EQ(reshape.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(reshape.opcode(), loco::CanonicalOpcode::FixedReshape); + + ASSERT_EQ(reshape.rank(), 0); +} + +TEST(Reshape_Fixed_Test, shape) +{ + loco::Reshape reshape; + reshape.shape({2, 3}); + + ASSERT_EQ(reshape.rank(), 2); + ASSERT_EQ(reshape.dim(0), 2); + ASSERT_EQ(reshape.dim(1), 3); +} + +TEST(FilterEncodeTest, constructor) +{ + loco::FilterEncode filter_encode; + + ASSERT_EQ(filter_encode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(filter_encode.opcode(), loco::CanonicalOpcode::FilterEncode); + + ASSERT_EQ(filter_encode.input(), nullptr); + ASSERT_EQ(filter_encode.encoder(), nullptr); +} + +TEST(FilterDecodeTest, constructor) +{ + loco::FilterDecode filter_decode; + + ASSERT_EQ(filter_decode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(filter_decode.opcode(), loco::CanonicalOpcode::FilterDecode); + + ASSERT_EQ(filter_decode.input(), nullptr); + ASSERT_EQ(filter_decode.decoder(), nullptr); +} + +TEST(DepthwiseFilterEncodeTest, constructor) +{ + loco::DepthwiseFilterEncode dw_filter_encode; + + ASSERT_EQ(dw_filter_encode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(dw_filter_encode.opcode(), loco::CanonicalOpcode::DepthwiseFilterEncode); + + ASSERT_EQ(dw_filter_encode.input(), nullptr); + ASSERT_EQ(dw_filter_encode.encoder(), nullptr); +} + +TEST(DepthwiseFilterDecodeTest, constructor) +{ + loco::DepthwiseFilterDecode dw_filter_decode; + + ASSERT_EQ(dw_filter_decode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(dw_filter_decode.opcode(), loco::CanonicalOpcode::DepthwiseFilterDecode); + + ASSERT_EQ(dw_filter_decode.input(), nullptr); + ASSERT_EQ(dw_filter_decode.decoder(), nullptr); +} + +TEST(TensorConcatTest, constructor) +{ + loco::TensorConcat tensor_concat; + + ASSERT_EQ(tensor_concat.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(tensor_concat.opcode(), loco::CanonicalOpcode::TensorConcat); + + ASSERT_EQ(tensor_concat.lhs(), nullptr); + ASSERT_EQ(tensor_concat.rhs(), nullptr); + ASSERT_EQ(tensor_concat.axis(), 0); + + tensor_concat.axis(3); + ASSERT_EQ(tensor_concat.axis(), 3); +} + +TEST(Conv2DTest, constructor) +{ + loco::Conv2D conv2d; + + ASSERT_EQ(conv2d.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(conv2d.opcode(), loco::CanonicalOpcode::Conv2D); + + ASSERT_EQ(conv2d.ifm(), nullptr); + ASSERT_EQ(conv2d.ker(), nullptr); + + ASSERT_NE(conv2d.pad(), nullptr); + ASSERT_EQ(conv2d.pad()->top(), 0); + ASSERT_EQ(conv2d.pad()->bottom(), 0); + ASSERT_EQ(conv2d.pad()->left(), 0); + ASSERT_EQ(conv2d.pad()->right(), 0); + + ASSERT_NE(conv2d.stride(), nullptr); + ASSERT_EQ(conv2d.stride()->vertical(), 1); + ASSERT_EQ(conv2d.stride()->horizontal(), 1); +} + +TEST(DepthwiseConv2DTest, constructor) +{ + loco::DepthwiseConv2D dw_conv2d; + + ASSERT_EQ(dw_conv2d.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(dw_conv2d.opcode(), loco::CanonicalOpcode::DepthwiseConv2D); + + ASSERT_EQ(dw_conv2d.ifm(), nullptr); + ASSERT_EQ(dw_conv2d.ker(), nullptr); + + ASSERT_NE(dw_conv2d.pad(), nullptr); + ASSERT_EQ(dw_conv2d.pad()->top(), 0); + ASSERT_EQ(dw_conv2d.pad()->bottom(), 0); + ASSERT_EQ(dw_conv2d.pad()->left(), 0); + ASSERT_EQ(dw_conv2d.pad()->right(), 0); + + ASSERT_NE(dw_conv2d.stride(), nullptr); + ASSERT_EQ(dw_conv2d.stride()->vertical(), 1); + ASSERT_EQ(dw_conv2d.stride()->horizontal(), 1); +} + +TEST(TransposedConv2DTest, constructor) +{ + loco::TransposedConv2D tr_conv2d; + + ASSERT_EQ(tr_conv2d.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(tr_conv2d.opcode(), loco::CanonicalOpcode::TransposedConv2D); + + ASSERT_EQ(tr_conv2d.ifm(), nullptr); + ASSERT_EQ(tr_conv2d.ker(), nullptr); + + ASSERT_NE(tr_conv2d.pad(), nullptr); + ASSERT_EQ(tr_conv2d.pad()->top(), 0); + ASSERT_EQ(tr_conv2d.pad()->bottom(), 0); + ASSERT_EQ(tr_conv2d.pad()->left(), 0); + ASSERT_EQ(tr_conv2d.pad()->right(), 0); + + ASSERT_NE(tr_conv2d.stride(), nullptr); + ASSERT_EQ(tr_conv2d.stride()->vertical(), 1); + ASSERT_EQ(tr_conv2d.stride()->horizontal(), 1); +} + +TEST(BiasEncodeTest, constructor) +{ + loco::BiasEncode bias_encode; + + ASSERT_EQ(bias_encode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(bias_encode.opcode(), loco::CanonicalOpcode::BiasEncode); + + ASSERT_EQ(bias_encode.input(), nullptr); +} + +TEST(TensorBiasAddTest, constructor) +{ + loco::BiasAdd bias_add; + + ASSERT_EQ(bias_add.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(bias_add.opcode(), loco::CanonicalOpcode::TensorBiasAdd); + + ASSERT_EQ(bias_add.value(), nullptr); + ASSERT_EQ(bias_add.bias(), nullptr); + ASSERT_EQ(bias_add.axis(), 0); +} + +TEST(TensorBiasAddTest, alias) +{ + loco::TensorBiasAdd bias_add; + + SUCCEED(); +} + +TEST(FeatureBiasAddTest, constructor) +{ + loco::BiasAdd bias_add; + + ASSERT_EQ(bias_add.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(bias_add.opcode(), loco::CanonicalOpcode::FeatureBiasAdd); + + ASSERT_EQ(bias_add.value(), nullptr); + ASSERT_EQ(bias_add.bias(), nullptr); +} + +TEST(FeatureBiasAddTest, alias) +{ + loco::FeatureBiasAdd bias_add; + + SUCCEED(); +} + +TEST(EltwiseAddTest, constructor) +{ + loco::EltwiseAdd eltwise_add; + + SUCCEED(); +} + +TEST(EltwiseMaxTest, constructor) +{ + loco::EltwiseMax eltwise_max; + + SUCCEED(); +} + +TEST(EltwiseMulTest, constructor) +{ + loco::EltwiseMul eltwise_mul; + + SUCCEED(); +} + +TEST(EltwiseSubTest, constructor) +{ + loco::EltwiseSub eltwise_sub; + + SUCCEED(); +} + +TEST(EltwiseDivTest, constructor) +{ + loco::EltwiseDiv eltwise_div; + + SUCCEED(); +} + +TEST(EltwiseSqrtTest, constructor) +{ + loco::EltwiseSqrt sqrt_node; + + ASSERT_EQ(sqrt_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(sqrt_node.opcode(), loco::CanonicalOpcode::EltwiseSqrt); + + ASSERT_EQ(sqrt_node.input(), nullptr); +} + +TEST(TensorBroadcastTest, constructor) +{ + loco::TensorBroadcast tensor_broadcast_node; + + ASSERT_EQ(tensor_broadcast_node.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(tensor_broadcast_node.opcode(), loco::CanonicalOpcode::TensorBroadcast); + + ASSERT_EQ(tensor_broadcast_node.input(), nullptr); +} + +TEST(TensorBroadcastTest, mapping) +{ + loco::TensorBroadcast tensor_broadcast_node; + + ASSERT_EQ(tensor_broadcast_node.mapping()->defined(0), false); + + tensor_broadcast_node.mapping()->dim(0) = 3; + + ASSERT_EQ(tensor_broadcast_node.mapping()->defined(0), true); + ASSERT_EQ(tensor_broadcast_node.mapping()->dim(0), 3); +} + +TEST(MatrixEncodeTest, constructor) +{ + loco::MatrixEncode matrix_encode; + + ASSERT_EQ(matrix_encode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(matrix_encode.opcode(), loco::CanonicalOpcode::MatrixEncode); + + ASSERT_EQ(matrix_encode.input(), nullptr); +} + +TEST(MatrixDecodeTest, constructor) +{ + loco::MatrixDecode matrix_decode; + + ASSERT_EQ(matrix_decode.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(matrix_decode.opcode(), loco::CanonicalOpcode::MatrixDecode); + + ASSERT_EQ(matrix_decode.input(), nullptr); +} + +TEST(MatMulTest, constructor) +{ + loco::MatMul mat_mul; + + ASSERT_EQ(mat_mul.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(mat_mul.opcode(), loco::CanonicalOpcode::MatMul); + + ASSERT_EQ(mat_mul.lhs(), nullptr); + ASSERT_EQ(mat_mul.rhs(), nullptr); +} + +TEST(TransposeTest, constructor) +{ + loco::TensorTranspose transpose; + + ASSERT_EQ(transpose.dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(transpose.opcode(), loco::CanonicalOpcode::TensorTranspose); + + ASSERT_EQ(transpose.input(), nullptr); + ASSERT_EQ(transpose.perm()->size(), 0); +} + +TEST(TransposeTest, perm) +{ + loco::TensorTranspose transpose; + + transpose.perm()->size(3); + transpose.perm()->axis(0) = 1; + transpose.perm()->axis(1) = 2; + transpose.perm()->axis(2) = 0; + + ASSERT_EQ(transpose.perm()->axis(0), 1); + ASSERT_EQ(transpose.perm()->axis(1), 2); + ASSERT_EQ(transpose.perm()->axis(2), 0); +} diff --git a/compiler/loco/src/IR/Padding2D.test.cpp b/compiler/loco/src/IR/Padding2D.test.cpp new file mode 100644 index 000000000..2e3d4af87 --- /dev/null +++ b/compiler/loco/src/IR/Padding2D.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Padding2D.h" + +#include + +TEST(PadTest, default_constructor_2D) +{ + loco::Padding2D pad; + + ASSERT_EQ(pad.top(), 0); + ASSERT_EQ(pad.bottom(), 0); + ASSERT_EQ(pad.left(), 0); + ASSERT_EQ(pad.right(), 0); +} diff --git a/compiler/loco/src/IR/PaddingND.test.cpp b/compiler/loco/src/IR/PaddingND.test.cpp new file mode 100644 index 000000000..0e20406ff --- /dev/null +++ b/compiler/loco/src/IR/PaddingND.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/PaddingND.h" + +#include + +TEST(PaddingNDTest, default_constructor_ND) +{ + loco::PaddingND padding; + + padding.rank(1); + padding.front(0) = 1; + padding.back(0) = 2; + + ASSERT_EQ(padding.rank(), 1); + ASSERT_EQ(padding.front(0), 1); + ASSERT_EQ(padding.back(0), 2); +} diff --git a/compiler/loco/src/IR/PermutingCodec.cpp b/compiler/loco/src/IR/PermutingCodec.cpp new file mode 100644 index 000000000..2857e5e28 --- /dev/null +++ b/compiler/loco/src/IR/PermutingCodec.cpp @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/PermutingCodec.h" + +#include + +#include +#include +#include + +/** + * Feature Domain + */ +namespace +{ + +using loco::FeatureAxis; + +inline bool valid(const FeatureAxis &axis) +{ + switch (axis) + { + case FeatureAxis::Count: + return true; + case FeatureAxis::Depth: + return true; + case FeatureAxis::Height: + return true; + case FeatureAxis::Width: + return true; + default: + break; + } + + return false; +} + +inline bool valid(const loco::Permutation &perm) +{ + auto check = [&perm](FeatureAxis axis_f) { + if (!perm.mapped(axis_f)) + return false; + return perm.axis(axis_f) < 4; + }; + + if (!check(FeatureAxis::Count)) + return false; + if (!check(FeatureAxis::Depth)) + return false; + if (!check(FeatureAxis::Height)) + return false; + if (!check(FeatureAxis::Width)) + return false; + + // Check whether tensor axes are all distinct + std::set values; + + values.insert(perm[FeatureAxis::Count]); + values.insert(perm[FeatureAxis::Depth]); + values.insert(perm[FeatureAxis::Height]); + values.insert(perm[FeatureAxis::Width]); + + return values.size() == 4; +} + +} // namespace + +namespace loco +{ + +// +// Permutation +// +bool Permutation::mapped(const FeatureAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid feature axis"); + return _map.find(axis_f) != _map.end(); +} + +uint32_t Permutation::axis(const FeatureAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid feature axis"); + assert(mapped(axis_f) && "unmapped feature axis"); + return _map.at(axis_f); +} + +uint32_t &Permutation::axis(const FeatureAxis &axis_f) +{ + assert(valid(axis_f) && "invalid feature axis"); + return _map[axis_f]; +} + +// +// Permuting Encoder +// +FeatureShape PermutingEncoder::shape(const TensorShape &in) const +{ + assert(valid() && "invalid permutation"); + + FeatureShape out; + + out.count() = in.dim(_perm[FeatureAxis::Count]); + out.depth() = in.dim(_perm[FeatureAxis::Depth]); + out.height() = in.dim(_perm[FeatureAxis::Height]); + out.width() = in.dim(_perm[FeatureAxis::Width]); + + return out; +} + +TensorIndex PermutingEncoder::value(const FeatureIndex &in) const +{ + assert(valid() && "invalid permutation"); + + TensorIndex out; + + out.resize(4); + + out.at(_perm[FeatureAxis::Count]) = in.batch(); + out.at(_perm[FeatureAxis::Depth]) = in.channel(); + out.at(_perm[FeatureAxis::Height]) = in.row(); + out.at(_perm[FeatureAxis::Width]) = in.column(); + + return out; +} + +std::unique_ptr PermutingEncoder::clone(void) const +{ + return stdex::make_unique>(_perm); +} + +bool PermutingEncoder::valid(void) const { return ::valid(_perm); } + +// +// Permuting Decoder +// +TensorShape PermutingDecoder::shape(const FeatureShape &in) const +{ + assert(valid() && "invalid permuation"); + + TensorShape out; + + out.rank(4); + + out.dim(_perm[FeatureAxis::Count]) = in.count(); + out.dim(_perm[FeatureAxis::Depth]) = in.depth(); + out.dim(_perm[FeatureAxis::Height]) = in.height(); + out.dim(_perm[FeatureAxis::Width]) = in.width(); + + return out; +} + +FeatureIndex PermutingDecoder::value(const TensorIndex &in) const +{ + assert(valid() && "invalid permutation"); + + FeatureIndex out; + + out.batch() = in.at(_perm[FeatureAxis::Count]); + out.channel() = in.at(_perm[FeatureAxis::Depth]); + out.row() = in.at(_perm[FeatureAxis::Height]); + out.column() = in.at(_perm[FeatureAxis::Width]); + + return out; +} + +std::unique_ptr PermutingDecoder::clone(void) const +{ + return stdex::make_unique>(_perm); +} + +bool PermutingDecoder::valid(void) const { return ::valid(_perm); } + +} // namespace loco + +/** + * Filter Domain + */ +namespace +{ + +using loco::FilterAxis; + +inline bool valid(const FilterAxis &axis) +{ + switch (axis) + { + case FilterAxis::Count: + return true; + case FilterAxis::Depth: + return true; + case FilterAxis::Height: + return true; + case FilterAxis::Width: + return true; + default: + break; + } + + return false; +} + +inline bool valid(const loco::Permutation &perm) +{ + auto check = [&perm](FilterAxis axis_f) { + if (!perm.mapped(axis_f)) + return false; + return perm.axis(axis_f) < 4; + }; + + if (!check(FilterAxis::Count)) + return false; + if (!check(FilterAxis::Depth)) + return false; + if (!check(FilterAxis::Height)) + return false; + if (!check(FilterAxis::Width)) + return false; + + // Check whether tensor axes are all distinct + std::set values; + + values.insert(perm[FilterAxis::Count]); + values.insert(perm[FilterAxis::Depth]); + values.insert(perm[FilterAxis::Height]); + values.insert(perm[FilterAxis::Width]); + + return values.size() == 4; +} + +} // namespace + +namespace loco +{ + +// +// Permutation +// +bool Permutation::mapped(const FilterAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid filter axis"); + return _map.find(axis_f) != _map.end(); +} + +const uint32_t &Permutation::axis(const FilterAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid filter axis"); + assert(mapped(axis_f) && "unmapped filter axis"); + return _map.at(axis_f); +} + +uint32_t &Permutation::axis(const FilterAxis &axis_f) +{ + assert(valid(axis_f) && "invalid filter axis"); + return _map[axis_f]; +} + +// +// Permuting Encoder +// +FilterShape PermutingEncoder::shape(const TensorShape &in) const +{ + assert(valid() && "invalid permutation"); + + FilterShape out; + + out.count() = in.dim(_perm[FilterAxis::Count]); + out.depth() = in.dim(_perm[FilterAxis::Depth]); + out.height() = in.dim(_perm[FilterAxis::Height]); + out.width() = in.dim(_perm[FilterAxis::Width]); + + return out; +} + +TensorIndex PermutingEncoder::value(const FilterIndex &in) const +{ + assert(valid() && "invalid permutation"); + + TensorIndex out; + + out.resize(4); + + out.at(_perm[FilterAxis::Count]) = in.nth(); + out.at(_perm[FilterAxis::Depth]) = in.channel(); + out.at(_perm[FilterAxis::Height]) = in.row(); + out.at(_perm[FilterAxis::Width]) = in.column(); + + return out; +} + +bool PermutingEncoder::valid(void) const { return ::valid(_perm); } + +// +// Permuting Decoder +// +TensorShape PermutingDecoder::shape(const FilterShape &in) const +{ + assert(valid() && "invalid permutation"); + + TensorShape out; + + out.rank(4); + out.dim(_perm[FilterAxis::Count]) = in.count(); + out.dim(_perm[FilterAxis::Depth]) = in.depth(); + out.dim(_perm[FilterAxis::Height]) = in.height(); + out.dim(_perm[FilterAxis::Width]) = in.width(); + + return out; +} + +FilterIndex PermutingDecoder::value(const TensorIndex &in) const +{ + assert(valid() && "invalid permutation"); + + FilterIndex out; + + out.nth() = in.at(_perm[FilterAxis::Count]); + out.channel() = in.at(_perm[FilterAxis::Depth]); + out.row() = in.at(_perm[FilterAxis::Height]); + out.column() = in.at(_perm[FilterAxis::Width]); + + return out; +} + +bool PermutingDecoder::valid(void) const { return ::valid(_perm); } + +} // namespace loco + +/** + * DepthwiseFilter Domain + */ +namespace +{ + +using loco::DepthwiseFilterAxis; + +inline bool valid(const DepthwiseFilterAxis &axis) +{ + switch (axis) + { + case DepthwiseFilterAxis::Depth: + return true; + case DepthwiseFilterAxis::Multiplier: + return true; + case DepthwiseFilterAxis::Height: + return true; + case DepthwiseFilterAxis::Width: + return true; + default: + break; + } + + return false; +} + +inline bool valid(const loco::Permutation &perm) +{ + auto check = [&perm](DepthwiseFilterAxis axis_f) { + if (!perm.mapped(axis_f)) + return false; + return perm.axis(axis_f) < 4; + }; + + if (!check(DepthwiseFilterAxis::Depth)) + return false; + if (!check(DepthwiseFilterAxis::Multiplier)) + return false; + if (!check(DepthwiseFilterAxis::Height)) + return false; + if (!check(DepthwiseFilterAxis::Width)) + return false; + + // Check whether tensor axes are all distinct + std::set values; + + values.insert(perm[DepthwiseFilterAxis::Depth]); + values.insert(perm[DepthwiseFilterAxis::Multiplier]); + values.insert(perm[DepthwiseFilterAxis::Height]); + values.insert(perm[DepthwiseFilterAxis::Width]); + + return values.size() == 4; +} + +} // namespace + +namespace loco +{ + +// +// Permutation +// +bool Permutation::mapped(const DepthwiseFilterAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid depthwise filter axis"); + return _map.find(axis_f) != _map.end(); +} + +const uint32_t &Permutation::axis(const DepthwiseFilterAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid depthwise filter axis"); + assert(mapped(axis_f) && "unmapped depthwise filter axis"); + return _map.at(axis_f); +} + +uint32_t &Permutation::axis(const DepthwiseFilterAxis &axis_f) +{ + assert(valid(axis_f) && "invalid depthwise filter axis"); + return _map[axis_f]; +} + +// +// Permuting Encoder +// +DepthwiseFilterShape PermutingEncoder::shape(const TensorShape &in) const +{ + assert(valid() && "invalid permutation"); + + DepthwiseFilterShape out; + + out.depth() = in.dim(_perm[DepthwiseFilterAxis::Depth]); + out.multiplier() = in.dim(_perm[DepthwiseFilterAxis::Multiplier]); + out.height() = in.dim(_perm[DepthwiseFilterAxis::Height]); + out.width() = in.dim(_perm[DepthwiseFilterAxis::Width]); + + return out; +} + +TensorIndex PermutingEncoder::value(const DepthwiseFilterIndex &in) const +{ + assert(valid() && "invalid permutation"); + + TensorIndex out; + + out.resize(4); + + out.at(_perm[DepthwiseFilterAxis::Depth]) = in.channel(); + out.at(_perm[DepthwiseFilterAxis::Multiplier]) = in.nth(); + out.at(_perm[DepthwiseFilterAxis::Height]) = in.row(); + out.at(_perm[DepthwiseFilterAxis::Width]) = in.column(); + + return out; +} + +bool PermutingEncoder::valid(void) const { return ::valid(_perm); } + +// +// Permuting Decoder +// +TensorShape PermutingDecoder::shape(const DepthwiseFilterShape &in) const +{ + assert(valid() && "invalid permutation"); + + TensorShape out; + out.rank(4); + + out.dim(_perm[DepthwiseFilterAxis::Depth]) = in.depth(); + out.dim(_perm[DepthwiseFilterAxis::Multiplier]) = in.multiplier(); + out.dim(_perm[DepthwiseFilterAxis::Height]) = in.height(); + out.dim(_perm[DepthwiseFilterAxis::Width]) = in.width(); + + return out; +} + +DepthwiseFilterIndex PermutingDecoder::value(const TensorIndex &in) const +{ + assert(valid() && "invalid permutation"); + assert(in.rank() == 4); + + DepthwiseFilterIndex out; + + out.channel() = in.at(_perm[DepthwiseFilterAxis::Depth]); + out.nth() = in.at(_perm[DepthwiseFilterAxis::Multiplier]); + out.row() = in.at(_perm[DepthwiseFilterAxis::Height]); + out.column() = in.at(_perm[DepthwiseFilterAxis::Width]); + + return out; +} + +bool PermutingDecoder::valid(void) const { return ::valid(_perm); } + +} // namespace loco + +/** + * Matrix Domain + */ +namespace +{ + +using loco::MatrixAxis; + +inline bool valid(const MatrixAxis &axis) +{ + switch (axis) + { + case MatrixAxis::Height: + return true; + case MatrixAxis::Width: + return true; + default: + break; + } + + return false; +} + +inline bool valid(const loco::Permutation &perm) +{ + auto check = [&perm](MatrixAxis axis_f) { + if (!perm.mapped(axis_f)) + return false; + return perm.axis(axis_f) < 2; + }; + + if (!check(MatrixAxis::Height)) + return false; + if (!check(MatrixAxis::Width)) + return false; + + // Check whether tensor axes are all distinct + std::set values; + + values.insert(perm[MatrixAxis::Height]); + values.insert(perm[MatrixAxis::Width]); + + return values.size() == 2; +} + +} // namespace + +namespace loco +{ + +// +// Permutation +// +bool Permutation::mapped(const MatrixAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid matrix axis"); + return _map.find(axis_f) != _map.end(); +} + +uint32_t Permutation::axis(const MatrixAxis &axis_f) const +{ + assert(valid(axis_f) && "invalid matrix axis"); + assert(mapped(axis_f) && "unmapped matrix axis"); + return _map.at(axis_f); +} + +uint32_t &Permutation::axis(const MatrixAxis &axis_f) +{ + assert(valid(axis_f) && "invalid matrix axis"); + return _map[axis_f]; +} + +// +// Permuting Encoder +// +MatrixShape PermutingEncoder::shape(const TensorShape &in) const +{ + assert(valid() && "invalid permutation"); + + MatrixShape out; + + out.height() = in.dim(_perm[MatrixAxis::Height]); + out.width() = in.dim(_perm[MatrixAxis::Width]); + + return out; +} + +TensorIndex PermutingEncoder::value(const MatrixIndex &in) const +{ + assert(valid() && "invalid permutation"); + + TensorIndex out; + + out.resize(2); + + out.at(_perm[MatrixAxis::Height]) = in.row(); + out.at(_perm[MatrixAxis::Width]) = in.column(); + + return out; +} + +bool PermutingEncoder::valid(void) const { return ::valid(_perm); } + +// +// Permuting Decoder +// +TensorShape PermutingDecoder::shape(const MatrixShape &in) const +{ + assert(valid() && "invalid permuation"); + + TensorShape out; + + out.rank(2); + + out.dim(_perm[MatrixAxis::Height]) = in.height(); + out.dim(_perm[MatrixAxis::Width]) = in.width(); + + return out; +} + +MatrixIndex PermutingDecoder::value(const TensorIndex &in) const +{ + assert(valid() && "invalid permutation"); + + MatrixIndex out; + + out.row() = in.at(_perm[MatrixAxis::Height]); + out.column() = in.at(_perm[MatrixAxis::Width]); + + return out; +} + +bool PermutingDecoder::valid(void) const { return ::valid(_perm); } + +} // namespace loco diff --git a/compiler/loco/src/IR/PermutingCodec.test.cpp b/compiler/loco/src/IR/PermutingCodec.test.cpp new file mode 100644 index 000000000..2eff286d0 --- /dev/null +++ b/compiler/loco/src/IR/PermutingCodec.test.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/PermutingCodec.h" + +#include + +using namespace loco; + +TEST(PemutationTest, feature) +{ + Permutation perm; + + // All values are invalid at the beginning + ASSERT_FALSE(perm.mapped(FeatureAxis::Count)); + ASSERT_FALSE(perm.mapped(FeatureAxis::Depth)); + ASSERT_FALSE(perm.mapped(FeatureAxis::Height)); + ASSERT_FALSE(perm.mapped(FeatureAxis::Width)); + + // Update mapping + perm[FeatureAxis::Count] = 5; + perm[FeatureAxis::Depth] = 6; + perm[FeatureAxis::Height] = 7; + perm[FeatureAxis::Width] = 8; + + // Now perm has a mapping for all the axes + ASSERT_TRUE(perm.mapped(FeatureAxis::Count)); + ASSERT_TRUE(perm.mapped(FeatureAxis::Depth)); + ASSERT_TRUE(perm.mapped(FeatureAxis::Height)); + ASSERT_TRUE(perm.mapped(FeatureAxis::Width)); + + // Check the value + ASSERT_EQ(perm[FeatureAxis::Count], 5); + ASSERT_EQ(perm[FeatureAxis::Depth], 6); + ASSERT_EQ(perm[FeatureAxis::Height], 7); + ASSERT_EQ(perm[FeatureAxis::Width], 8); +} + +TEST(PemutationTest, filter) +{ + Permutation perm; + + // All values are invalid at the beginning + ASSERT_FALSE(perm.mapped(FilterAxis::Count)); + ASSERT_FALSE(perm.mapped(FilterAxis::Depth)); + ASSERT_FALSE(perm.mapped(FilterAxis::Height)); + ASSERT_FALSE(perm.mapped(FilterAxis::Width)); + + // Update mapping + perm[FilterAxis::Count] = 5; + perm[FilterAxis::Depth] = 6; + perm[FilterAxis::Height] = 7; + perm[FilterAxis::Width] = 8; + + // Now perm has a mapping for all the axes + ASSERT_TRUE(perm.mapped(FilterAxis::Count)); + ASSERT_TRUE(perm.mapped(FilterAxis::Depth)); + ASSERT_TRUE(perm.mapped(FilterAxis::Height)); + ASSERT_TRUE(perm.mapped(FilterAxis::Width)); + + // Check the value + ASSERT_EQ(perm[FilterAxis::Count], 5); + ASSERT_EQ(perm[FilterAxis::Depth], 6); + ASSERT_EQ(perm[FilterAxis::Height], 7); + ASSERT_EQ(perm[FilterAxis::Width], 8); +} + +TEST(PemutationTest, depthwise_filter) +{ + Permutation perm; + + // All values are invalid at the beginning + ASSERT_FALSE(perm.mapped(DepthwiseFilterAxis::Depth)); + ASSERT_FALSE(perm.mapped(DepthwiseFilterAxis::Multiplier)); + ASSERT_FALSE(perm.mapped(DepthwiseFilterAxis::Height)); + ASSERT_FALSE(perm.mapped(DepthwiseFilterAxis::Width)); + + // Update mapping + perm[DepthwiseFilterAxis::Depth] = 5; + perm[DepthwiseFilterAxis::Multiplier] = 6; + perm[DepthwiseFilterAxis::Height] = 7; + perm[DepthwiseFilterAxis::Width] = 8; + + // Now perm has a mapping for all the axes + ASSERT_TRUE(perm.mapped(DepthwiseFilterAxis::Depth)); + ASSERT_TRUE(perm.mapped(DepthwiseFilterAxis::Multiplier)); + ASSERT_TRUE(perm.mapped(DepthwiseFilterAxis::Height)); + ASSERT_TRUE(perm.mapped(DepthwiseFilterAxis::Width)); + + // Check the value + ASSERT_EQ(perm[DepthwiseFilterAxis::Depth], 5); + ASSERT_EQ(perm[DepthwiseFilterAxis::Multiplier], 6); + ASSERT_EQ(perm[DepthwiseFilterAxis::Height], 7); + ASSERT_EQ(perm[DepthwiseFilterAxis::Width], 8); +} + +TEST(PermutingEncoderTest, feature) +{ + PermutingEncoder enc; + + // Encoder is invalid at the beginning + ASSERT_FALSE(enc.valid()); + + // Set "invalid" mapping + enc.perm()->axis(FeatureAxis::Count) = 0; + enc.perm()->axis(FeatureAxis::Depth) = 6; + enc.perm()->axis(FeatureAxis::Height) = 1; + enc.perm()->axis(FeatureAxis::Width) = 2; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set another "invalid" mapping + enc.perm()->axis(FeatureAxis::Depth) = 1; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set "valid" mapping + enc.perm()->axis(FeatureAxis::Depth) = 3; + + // Encoder is now valid + ASSERT_TRUE(enc.valid()); + + // Let's test with a HD (1280x720) RGB image + TensorShape tensor_shape; + + tensor_shape.rank(4); + tensor_shape.dim(0) = 1; // COUNT + tensor_shape.dim(1) = 720; // HEIGHT + tensor_shape.dim(2) = 1280; // WIDTH + tensor_shape.dim(3) = 3; // DEPTH + + // Get the feature shape corresponding to a given image + auto feature_shape = enc.shape(tensor_shape); + + ASSERT_EQ(feature_shape.count(), 1); + ASSERT_EQ(feature_shape.depth(), 3); + ASSERT_EQ(feature_shape.height(), 720); + ASSERT_EQ(feature_shape.width(), 1280); + + // Let's find a source tensor index! + FeatureIndex feature_index; + + feature_index.batch() = 0; + feature_index.channel() = 1; + feature_index.row() = 2; + feature_index.column() = 3; + + auto tensor_index = enc.value(feature_index); + + ASSERT_EQ(tensor_index.at(0), 0); // BATCH(COUNT) + ASSERT_EQ(tensor_index.at(1), 2); // ROW(HEIGHT) + ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH) + ASSERT_EQ(tensor_index.at(3), 1); // CHANNEL(DEPTH) +} + +TEST(PermutingEncoderTest, feature_clone) +{ + PermutingEncoder src_enc; + + auto src_perm = src_enc.perm(); + + src_perm->axis(FeatureAxis::Count) = 0; + src_perm->axis(FeatureAxis::Depth) = 3; + src_perm->axis(FeatureAxis::Height) = 1; + src_perm->axis(FeatureAxis::Width) = 2; + + auto dst_enc = src_enc.clone(); + auto dst_perm = dynamic_cast *>(dst_enc.get())->perm(); + + EXPECT_EQ(dst_perm->axis(FeatureAxis::Count), src_perm->axis(FeatureAxis::Count)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Depth), src_perm->axis(FeatureAxis::Depth)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Height), src_perm->axis(FeatureAxis::Height)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Width), src_perm->axis(FeatureAxis::Width)); + + // Update on cloned encoder SHOULD NOT affect the original encoder + dst_perm->axis(FeatureAxis::Height) += 1; + + EXPECT_EQ(src_perm->axis(FeatureAxis::Height), 1); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Height), 2); +} + +TEST(PermutingEncoderTest, filter) +{ + PermutingEncoder enc; + + // Encoder is invalid at the beginning + ASSERT_FALSE(enc.valid()); + + // Set "invalid" mapping + enc.perm()->axis(FilterAxis::Count) = 0; + enc.perm()->axis(FilterAxis::Depth) = 6; + enc.perm()->axis(FilterAxis::Height) = 1; + enc.perm()->axis(FilterAxis::Width) = 2; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set another "invalid" mapping + enc.perm()->axis(FilterAxis::Depth) = 1; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set "valid" mapping + enc.perm()->axis(FilterAxis::Depth) = 3; + + // Encoder is now valid + ASSERT_TRUE(enc.valid()); + + TensorShape tensor_shape; + + tensor_shape.rank(4); + tensor_shape.dim(0) = 8; // COUNT + tensor_shape.dim(1) = 1; // HEIGHT + tensor_shape.dim(2) = 7; // WIDTH + tensor_shape.dim(3) = 4; // DEPTH + + // Get the corresponding filter shape + auto filter_shape = enc.shape(tensor_shape); + + ASSERT_EQ(filter_shape.count(), 8); + ASSERT_EQ(filter_shape.depth(), 4); + ASSERT_EQ(filter_shape.height(), 1); + ASSERT_EQ(filter_shape.width(), 7); + + // Let's find a source tensor index! + FilterIndex filter_index; + + filter_index.nth() = 1; + filter_index.channel() = 2; + filter_index.row() = 0; + filter_index.column() = 3; + + auto tensor_index = enc.value(filter_index); + + ASSERT_EQ(tensor_index.at(0), 1); // NTH(COUNT) + ASSERT_EQ(tensor_index.at(1), 0); // ROW(HEIGHT) + ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH) + ASSERT_EQ(tensor_index.at(3), 2); // CHANNEL(DEPTH) +} + +TEST(PermutingEncoderTest, depthwise_filter) +{ + PermutingEncoder enc; + + // Encoder is invalid at the beginning + ASSERT_FALSE(enc.valid()); + + // Set "invalid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Depth) = 0; + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 6; + enc.perm()->axis(DepthwiseFilterAxis::Height) = 1; + enc.perm()->axis(DepthwiseFilterAxis::Width) = 2; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set another "invalid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 1; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set "valid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 3; + + // Encoder is now valid + ASSERT_TRUE(enc.valid()); + + TensorShape tensor_shape; + + tensor_shape.rank(4); + tensor_shape.dim(0) = 8; // DEPTH + tensor_shape.dim(1) = 1; // HEIGHT + tensor_shape.dim(2) = 7; // WIDTH + tensor_shape.dim(3) = 4; // MULTIPLIER + + // Get the corresponding depthwise filter shape + auto filter_shape = enc.shape(tensor_shape); + + ASSERT_EQ(filter_shape.depth(), 8); + ASSERT_EQ(filter_shape.multiplier(), 4); + ASSERT_EQ(filter_shape.height(), 1); + ASSERT_EQ(filter_shape.width(), 7); + + // Let's find a source tensor index! + DepthwiseFilterIndex filter_index; + + filter_index.channel() = 1; + filter_index.nth() = 2; + filter_index.row() = 0; + filter_index.column() = 3; + + auto tensor_index = enc.value(filter_index); + + ASSERT_EQ(tensor_index.at(0), 1); // CHANNEL(DEPTH) + ASSERT_EQ(tensor_index.at(1), 0); // ROW(HEIGHT) + ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH) + ASSERT_EQ(tensor_index.at(3), 2); // NTH(MULTIPLIER) +} + +TEST(PermutingEncoderTest, depthwisefilter_init) +{ + Permutation src_perm; + + src_perm.axis(DepthwiseFilterAxis::Multiplier) = 0; + src_perm.axis(DepthwiseFilterAxis::Depth) = 3; + src_perm.axis(DepthwiseFilterAxis::Height) = 1; + src_perm.axis(DepthwiseFilterAxis::Width) = 2; + + PermutingEncoder dst_enc{src_perm}; + auto dst_perm = dst_enc.perm(); + + EXPECT_EQ(dst_perm->axis(DepthwiseFilterAxis::Multiplier), + src_perm.axis(DepthwiseFilterAxis::Multiplier)); + EXPECT_EQ(dst_perm->axis(DepthwiseFilterAxis::Depth), src_perm.axis(DepthwiseFilterAxis::Depth)); + EXPECT_EQ(dst_perm->axis(DepthwiseFilterAxis::Height), + src_perm.axis(DepthwiseFilterAxis::Height)); + EXPECT_EQ(dst_perm->axis(DepthwiseFilterAxis::Width), src_perm.axis(DepthwiseFilterAxis::Width)); + + // Update on dst perm SHOULD NOT affect the src perm + dst_perm->axis(DepthwiseFilterAxis::Height) += 1; + + EXPECT_EQ(src_perm.axis(DepthwiseFilterAxis::Height), 1); + EXPECT_EQ(dst_perm->axis(DepthwiseFilterAxis::Height), 2); +} + +TEST(PermutingDecoderTest, feature) +{ + PermutingDecoder dec; + + // Decoder is invalid at the beginning + ASSERT_FALSE(dec.valid()); + + // Set "invalid" mapping + dec.perm()->axis(FeatureAxis::Count) = 0; + dec.perm()->axis(FeatureAxis::Depth) = 6; + dec.perm()->axis(FeatureAxis::Height) = 1; + dec.perm()->axis(FeatureAxis::Width) = 2; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set another "invalid" mapping + dec.perm()->axis(FeatureAxis::Depth) = 1; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set "valid" mapping + dec.perm()->axis(FeatureAxis::Depth) = 3; + + // Decoder is now valid + ASSERT_TRUE(dec.valid()); + + // Let's test with a HD (1280x720) RGB image + FeatureShape feature_shape; + + feature_shape.count() = 1; + feature_shape.depth() = 3; + feature_shape.height() = 720; + feature_shape.width() = 1280; + + // Get the tensor shape corresponding to a given image + auto tensor_shape = dec.shape(feature_shape); + + ASSERT_EQ(tensor_shape.rank(), 4); + ASSERT_EQ(tensor_shape.dim(0), 1); // COUNT + ASSERT_EQ(tensor_shape.dim(1), 720); // HEIGHT + ASSERT_EQ(tensor_shape.dim(2), 1280); // WIDTH + ASSERT_EQ(tensor_shape.dim(3), 3); // DEPTH + + // Let's find a source feature index! + TensorIndex tensor_index; + + tensor_index.resize(4); + + tensor_index.at(0) = 0; // BATCH(COUNT) + tensor_index.at(3) = 1; // CHANNEL(DEPTH) + tensor_index.at(1) = 2; // ROW(HEIGHT) + tensor_index.at(2) = 3; // COLUMN(WIDTH) + + auto feature_index = dec.value(tensor_index); + + ASSERT_EQ(feature_index.batch(), 0); + ASSERT_EQ(feature_index.channel(), 1); + ASSERT_EQ(feature_index.row(), 2); + ASSERT_EQ(feature_index.column(), 3); +} + +TEST(PermutingDecoderTest, feature_clone) +{ + PermutingDecoder src_enc; + + auto src_perm = src_enc.perm(); + + src_perm->axis(FeatureAxis::Count) = 0; + src_perm->axis(FeatureAxis::Depth) = 3; + src_perm->axis(FeatureAxis::Height) = 1; + src_perm->axis(FeatureAxis::Width) = 2; + + auto dst_enc = src_enc.clone(); + auto dst_perm = dynamic_cast *>(dst_enc.get())->perm(); + + EXPECT_EQ(dst_perm->axis(FeatureAxis::Count), src_perm->axis(FeatureAxis::Count)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Depth), src_perm->axis(FeatureAxis::Depth)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Height), src_perm->axis(FeatureAxis::Height)); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Width), src_perm->axis(FeatureAxis::Width)); + + // Update on cloned decoder SHOULD NOT affect the original decoder + dst_perm->axis(FeatureAxis::Height) += 1; + + EXPECT_EQ(src_perm->axis(FeatureAxis::Height), 1); + EXPECT_EQ(dst_perm->axis(FeatureAxis::Height), 2); +} + +TEST(PermutingDecoderTest, filter) +{ + PermutingDecoder dec; + + // Decoder is invalid at the beginning + ASSERT_FALSE(dec.valid()); + + // Set "invalid" mapping + dec.perm()->axis(FilterAxis::Count) = 0; + dec.perm()->axis(FilterAxis::Depth) = 6; + dec.perm()->axis(FilterAxis::Height) = 1; + dec.perm()->axis(FilterAxis::Width) = 2; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set another "invalid" mapping + dec.perm()->axis(FilterAxis::Depth) = 1; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set "valid" mapping + dec.perm()->axis(FilterAxis::Depth) = 3; + + // Decoder is now valid + ASSERT_TRUE(dec.valid()); + + // Let's test with a small filter + FilterShape filter_shape; + + filter_shape.count() = 10; + filter_shape.depth() = 3; + filter_shape.height() = 6; + filter_shape.width() = 8; + + // Get the tensor shape corresponding to a given image + auto tensor_shape = dec.shape(filter_shape); + + ASSERT_EQ(tensor_shape.rank(), 4); + ASSERT_EQ(tensor_shape.dim(0), 10); // COUNT + ASSERT_EQ(tensor_shape.dim(1), 6); // HEIGHT + ASSERT_EQ(tensor_shape.dim(2), 8); // WIDTH + ASSERT_EQ(tensor_shape.dim(3), 3); // DEPTH + + // Let's find a source filter index! + TensorIndex tensor_index; + + tensor_index.resize(4); + + tensor_index.at(0) = 0; // BATCH(COUNT) + tensor_index.at(3) = 1; // CHANNEL(DEPTH) + tensor_index.at(1) = 2; // ROW(HEIGHT) + tensor_index.at(2) = 3; // COLUMN(WIDTH) + + auto filter_index = dec.value(tensor_index); + + ASSERT_EQ(filter_index.nth(), 0); + ASSERT_EQ(filter_index.channel(), 1); + ASSERT_EQ(filter_index.row(), 2); + ASSERT_EQ(filter_index.column(), 3); +} + +TEST(PermutingDecoderTest, depthwise_filter) +{ + PermutingDecoder dec; + + // Decoder is invalid at the beginning + ASSERT_FALSE(dec.valid()); + + // Set "invalid" mapping + dec.perm()->axis(DepthwiseFilterAxis::Depth) = 0; + dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 6; + dec.perm()->axis(DepthwiseFilterAxis::Height) = 1; + dec.perm()->axis(DepthwiseFilterAxis::Width) = 2; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set another "invalid" mapping + dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 1; + + // Decoder is still invalid + ASSERT_FALSE(dec.valid()); + + // Set "valid" mapping + dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 3; + + // Decoder is now valid + ASSERT_TRUE(dec.valid()); + + DepthwiseFilterShape dw_filter_shape; + + dw_filter_shape.depth() = 8; + dw_filter_shape.multiplier() = 1; + dw_filter_shape.height() = 7; + dw_filter_shape.width() = 4; + + // Get the corresponding depthwise filter shape + auto tensor_shape = dec.shape(dw_filter_shape); + + ASSERT_EQ(tensor_shape.dim(0).value(), 8); + ASSERT_EQ(tensor_shape.dim(1).value(), 7); + ASSERT_EQ(tensor_shape.dim(2).value(), 4); + ASSERT_EQ(tensor_shape.dim(3).value(), 1); + + // Let's find a source tensor index! + TensorIndex tensor_index; + tensor_index.resize(4); + + tensor_index.at(0) = 4; + tensor_index.at(1) = 2; + tensor_index.at(2) = 1; + tensor_index.at(3) = 0; + + auto dw_filter_index = dec.value(tensor_index); + + ASSERT_EQ(dw_filter_index.channel(), 4); + ASSERT_EQ(dw_filter_index.nth(), 0); + ASSERT_EQ(dw_filter_index.row(), 2); + ASSERT_EQ(dw_filter_index.column(), 1); +} diff --git a/compiler/loco/src/IR/Stride.test.cpp b/compiler/loco/src/IR/Stride.test.cpp new file mode 100644 index 000000000..60deb5c6f --- /dev/null +++ b/compiler/loco/src/IR/Stride.test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Stride.h" + +#include + +TEST(StrideTest, default_constructor_2D) +{ + loco::Stride<2> stride; + + ASSERT_EQ(stride.vertical(), 1); + ASSERT_EQ(stride.horizontal(), 1); +} + +TEST(StrideTest, setter_and_getter_2D) +{ + loco::Stride<2> stride; + + stride.vertical(2); + + ASSERT_EQ(stride.vertical(), 2); + ASSERT_EQ(stride.horizontal(), 1); + + stride.horizontal(3); + + ASSERT_EQ(stride.vertical(), 2); + ASSERT_EQ(stride.horizontal(), 3); +} diff --git a/compiler/loco/src/IR/TensorAxis.cpp b/compiler/loco/src/IR/TensorAxis.cpp new file mode 100644 index 000000000..b083847fc --- /dev/null +++ b/compiler/loco/src/IR/TensorAxis.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/TensorAxis.h" + +// NOTE This file validates "TensorAxis.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/TensorAxisSet.cpp b/compiler/loco/src/IR/TensorAxisSet.cpp new file mode 100644 index 000000000..c58237bf7 --- /dev/null +++ b/compiler/loco/src/IR/TensorAxisSet.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/TensorAxisSet.h" + +// NOTE This file validates "TensorAxisSet.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/TensorIndex.cpp b/compiler/loco/src/IR/TensorIndex.cpp new file mode 100644 index 000000000..cbd3698eb --- /dev/null +++ b/compiler/loco/src/IR/TensorIndex.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/TensorIndex.h" + +// NOTE This file validates "TensorIndex.h". Please DO NOT remove this file. diff --git a/compiler/loco/src/IR/TensorShape.cpp b/compiler/loco/src/IR/TensorShape.cpp new file mode 100644 index 000000000..ad30dcbc0 --- /dev/null +++ b/compiler/loco/src/IR/TensorShape.cpp @@ -0,0 +1,39 @@ +/* + * 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 "loco/IR/TensorShape.h" + +#include + +namespace loco +{ + +uint32_t element_count(const loco::TensorShape *tensor_shape) +{ + uint32_t res = 1; + + for (uint32_t axis = 0; axis < tensor_shape->rank(); ++axis) + { + // Let's use "assert" here as "caller" is responsible for this check. + // Please refer to the header for details. + assert(tensor_shape->dim(axis).known()); + res *= tensor_shape->dim(axis).value(); + } + + return res; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/TensorShape.test.cpp b/compiler/loco/src/IR/TensorShape.test.cpp new file mode 100644 index 000000000..ce03ccbd4 --- /dev/null +++ b/compiler/loco/src/IR/TensorShape.test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/TensorShape.h" + +#include + +TEST(TensorShapeTest, default_constructor) +{ + loco::TensorShape tensor_shape; + + ASSERT_EQ(tensor_shape.rank(), 0); +} + +TEST(TensorShapeTest, initializer_list_constructor) +{ + loco::TensorShape tensor_shape{3, 5}; + + ASSERT_EQ(tensor_shape.rank(), 2); + + ASSERT_TRUE(tensor_shape.dim(0).known()); + ASSERT_TRUE(tensor_shape.dim(1).known()); + + ASSERT_EQ(tensor_shape.dim(0).value(), 3); + ASSERT_EQ(tensor_shape.dim(1).value(), 5); +} + +TEST(TensorShapeTest, rank) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + + ASSERT_EQ(tensor_shape.rank(), 2); + ASSERT_FALSE(tensor_shape.dim(0).known()); + ASSERT_FALSE(tensor_shape.dim(1).known()); +} + +TEST(TensorShapeTest, dim) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + + tensor_shape.dim(0) = 3; + + ASSERT_TRUE(tensor_shape.dim(0).known()); + ASSERT_FALSE(tensor_shape.dim(1).known()); + + ASSERT_EQ(tensor_shape.dim(0), 3); +} + +TEST(TensorShapeTest, rank_update) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + + tensor_shape.dim(1) = 3; + + tensor_shape.rank(4); + + ASSERT_FALSE(tensor_shape.dim(0).known()); + ASSERT_TRUE(tensor_shape.dim(1).known()); + ASSERT_FALSE(tensor_shape.dim(2).known()); + ASSERT_FALSE(tensor_shape.dim(3).known()); + + ASSERT_EQ(tensor_shape.dim(1), 3); +} + +TEST(TensorShapeTest, copy) +{ + loco::TensorShape src; + + src.rank(2); + src.dim(1) = 3; + + loco::TensorShape dst; + + dst = src; + + ASSERT_EQ(dst.rank(), 2); + + ASSERT_FALSE(dst.dim(0).known()); + ASSERT_TRUE(dst.dim(1).known()); + + ASSERT_EQ(dst.dim(1), 3); +} + +TEST(TensorShapeTest, element_count) +{ + // Check Rank-0 case + loco::TensorShape src; + + ASSERT_EQ(loco::element_count(&src), 1); +} diff --git a/compiler/loco/src/IR/Use.cpp b/compiler/loco/src/IR/Use.cpp new file mode 100644 index 000000000..fed562c65 --- /dev/null +++ b/compiler/loco/src/IR/Use.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Use.h" +#include "loco/IR/Node.h" + +#include + +namespace loco +{ + +void Use::node(Node *node) +{ + if (_node != nullptr) + { + assert(_node->_uses.find(this) != _node->_uses.end()); + _node->_uses.erase(this); + _node = nullptr; + } + + assert(_node == nullptr); + + if (node != nullptr) + { + _node = node; + _node->_uses.insert(this); + } + + assert(_node == node); +} + +} // namespace loco diff --git a/compiler/loco/src/IR/Use.test.cpp b/compiler/loco/src/IR/Use.test.cpp new file mode 100644 index 000000000..4a2f1cc25 --- /dev/null +++ b/compiler/loco/src/IR/Use.test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Use.h" + +#include "MockupNode.h" + +#include + +TEST(UseTest, constructor) +{ + MockupNode user; + loco::Use use{&user}; + + ASSERT_EQ(use.user(), &user); + ASSERT_EQ(use.node(), nullptr); +} + +TEST(UseTest, link_node) +{ + MockupNode def; + MockupNode user; + loco::Use use{&user}; + + use.node(&def); + + ASSERT_EQ(use.user(), &user); + ASSERT_EQ(use.node(), &def); +} diff --git a/compiler/loco/src/IR/Verifier.cpp b/compiler/loco/src/IR/Verifier.cpp new file mode 100644 index 000000000..42735a327 --- /dev/null +++ b/compiler/loco/src/IR/Verifier.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Verifier.h" + +#include +#include + +namespace +{ + +using namespace loco; + +struct GraphVerifier final +{ +public: + GraphVerifier(loco::Graph *graph) : _graph{graph} + { + // graph SHOULD NOT BE null + assert(_graph != nullptr); + } + +public: + // ErrorListener SHOULD outlive GraphVerifier + GraphVerifier &enroll(ErrorListener *l) + { + if (l != nullptr) + { + _listeners.insert(l); + } + return (*this); + } + + GraphVerifier &enroll(std::unique_ptr &&l) + { + if (l != nullptr) + { + _listeners.insert(l.get()); + // Take the ownership of a given listener + _owned_listeners.insert(std::move(l)); + } + return (*this); + } + +public: + void run(void) const + { + for (auto node : loco::all_nodes(_graph)) + { + // Verify nodes + for (uint32_t n = 0; n < node->arity(); ++n) + { + if (node->arg(n) == nullptr) + { + notify(ErrorDetail{node, n}); + } + } + } + } + +private: + template void notify(const Error &error) const + { + for (const auto &listener : _listeners) + { + listener->notify(error); + } + } + +private: + loco::Graph *_graph = nullptr; + + // All active error listeners + std::set _listeners; + + // Owned error listeners + std::set> _owned_listeners; +}; + +inline GraphVerifier graph_verifier(loco::Graph *graph) { return GraphVerifier{graph}; } + +} // namespace + +namespace loco +{ + +bool valid(Graph *g, std::unique_ptr &&l) +{ + class ErrorCounter final : public ErrorListener + { + public: + uint32_t count(void) const { return _count; } + + public: + void notify(const ErrorDetail &) { _count += 1; } + + private: + uint32_t _count = 0; + }; + + ErrorCounter counter; + graph_verifier(g).enroll(&counter).enroll(std::move(l)).run(); + return counter.count() == 0; +} + +} // namespace loco diff --git a/compiler/loco/src/IR/Verifier.test.cpp b/compiler/loco/src/IR/Verifier.test.cpp new file mode 100644 index 000000000..247a59390 --- /dev/null +++ b/compiler/loco/src/IR/Verifier.test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Verifier.h" + +#include + +#include +#include + +using stdex::make_unique; + +TEST(VerifierTest, valid_minimal) +{ + auto g = loco::make_graph(); + auto push = g->nodes()->create(); + + ASSERT_FALSE(loco::valid(g.get())); +} + +TEST(VerifierTest, valid_error_reporter) +{ + using namespace loco; + + auto g = loco::make_graph(); + auto push = g->nodes()->create(); + + class Collector final : public loco::ErrorListener + { + public: + Collector(std::vector> *out) : _out{out} + { + // DO NOTHING + } + + public: + void notify(const ErrorDetail &d) override + { + _out->emplace_back(d); + } + + private: + std::vector> *_out; + }; + + std::vector> errors; + ASSERT_FALSE(loco::valid(g.get(), make_unique(&errors))); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(errors.at(0).node(), push); + ASSERT_EQ(errors.at(0).index(), 0); +} diff --git a/compiler/loco/src/IR/Window.test.cpp b/compiler/loco/src/IR/Window.test.cpp new file mode 100644 index 000000000..c112e0f96 --- /dev/null +++ b/compiler/loco/src/IR/Window.test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/IR/Window.h" + +#include + +TEST(WindowTest, default_constructor_2D) +{ + loco::Window<2> window; + + ASSERT_EQ(window.vertical(), 1); + ASSERT_EQ(window.horizontal(), 1); +} + +TEST(WindowTest, setter_and_getter_2D) +{ + loco::Window<2> window; + + window.vertical(2); + + ASSERT_EQ(window.vertical(), 2); + ASSERT_EQ(window.horizontal(), 1); + + window.horizontal(3); + + ASSERT_EQ(window.vertical(), 2); + ASSERT_EQ(window.horizontal(), 3); +} diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp new file mode 100644 index 000000000..d30a8279a --- /dev/null +++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp @@ -0,0 +1,774 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/CanonicalShapeInferenceRule.h" +#include "loco/Service/ShapeInference.h" + +#include +#include +#include + +#include + +namespace +{ + +struct PlaneShape +{ + loco::Dimension height; + loco::Dimension width; +}; + +PlaneShape make_plane_shape(const loco::FeatureShape &feature_shape) +{ + PlaneShape plane_shape; + + plane_shape.height = feature_shape.height(); + plane_shape.width = feature_shape.width(); + + return plane_shape; +} + +class FeatureShapeUpdater final +{ +public: + FeatureShapeUpdater(loco::FeatureShape *ptr) : _feature_shape_ptr{ptr} + { + // DO NOTHING + } + +public: + void with(const PlaneShape &plane_shape) const + { + _feature_shape_ptr->height() = plane_shape.height; + _feature_shape_ptr->width() = plane_shape.width; + } + +private: + loco::FeatureShape *_feature_shape_ptr; +}; + +/** + * HOW TO USE + * + * loco::FeatureShape feature_shape = ...; + * + * update(feature_shape).with(...) + */ +FeatureShapeUpdater update(loco::FeatureShape &feature_shape) +{ + return FeatureShapeUpdater{&feature_shape}; +} + +loco::Window<2> window_of(const loco::FilterShape &filter_shape) +{ + loco::Window<2> window; + + window.vertical(filter_shape.height().value()); + window.horizontal(filter_shape.width().value()); + + return window; +} + +loco::Window<2> window_of(const loco::DepthwiseFilterShape &depthwise_filter_shape) +{ + loco::Window<2> window; + + window.vertical(depthwise_filter_shape.height().value()); + window.horizontal(depthwise_filter_shape.width().value()); + + return window; +} + +enum class Direction +{ + Forward, + Backward, +}; + +template class PlaneInference; + +template <> class PlaneInference final +{ +public: + PlaneShape operator()(const PlaneShape &in) const + { + assert(_pad != nullptr); + assert(_window != nullptr); + assert(_stride != nullptr); + + uint32_t const raw_input_height = in.height.value(); + uint32_t const raw_input_width = in.width.value(); + + uint32_t const raw_window_height = _window->vertical(); + uint32_t const raw_window_width = _window->horizontal(); + + uint32_t const vertical_padding = _pad->top() + _pad->bottom(); + uint32_t const horizontal_padding = _pad->left() + _pad->right(); + + uint32_t const effective_input_height = raw_input_height + vertical_padding; + uint32_t const effective_input_width = raw_input_width + horizontal_padding; + + // NOTE To support "dilation" later + uint32_t const effective_window_height = raw_window_height; + uint32_t const effective_window_width = raw_window_width; + + uint32_t const vertical_stride = _stride->vertical(); + uint32_t const horizontal_stride = _stride->horizontal(); + + assert((effective_input_height - effective_window_height) % vertical_stride == 0); + assert((effective_input_width - effective_window_width) % horizontal_stride == 0); + + PlaneShape res; + + res.height = (effective_input_height - effective_window_height) / vertical_stride + 1; + res.width = (effective_input_width - effective_window_width) / horizontal_stride + 1; + + return res; + } + +public: + void pad(const loco::Padding2D *value) { _pad = value; } + void window(const loco::Window<2> *value) { _window = value; } + void stride(const loco::Stride<2> *value) { _stride = value; } + +private: + const loco::Padding2D *_pad = nullptr; + const loco::Window<2> *_window = nullptr; + const loco::Stride<2> *_stride = nullptr; +}; + +template <> class PlaneInference final +{ +public: + PlaneShape operator()(const PlaneShape &in) const + { + assert(_pad != nullptr); + assert(_window != nullptr); + assert(_stride != nullptr); + + uint32_t const input_height = in.height.value(); + uint32_t const input_width = in.width.value(); + + uint32_t const vertical_padding = _pad->top() + _pad->bottom(); + uint32_t const horizontal_padding = _pad->left() + _pad->right(); + + uint32_t const raw_window_height = _window->vertical(); + uint32_t const raw_window_width = _window->horizontal(); + + // TODO Support "dilation" + uint32_t const effective_window_height = raw_window_height; + uint32_t const effective_window_width = raw_window_width; + + uint32_t const vertical_stride = _stride->vertical(); + uint32_t const horizontal_stride = _stride->horizontal(); + + PlaneShape res; + + res.height = vertical_stride * (input_height - 1) + effective_window_height - vertical_padding; + res.width = horizontal_stride * (input_width - 1) + effective_window_width - horizontal_padding; + + return res; + } + +public: + void pad(const loco::Padding2D *value) { _pad = value; } + void window(const loco::Window<2> *value) { _window = value; } + void stride(const loco::Stride<2> *value) { _stride = value; } + +private: + const loco::Padding2D *_pad = nullptr; + const loco::Window<2> *_window = nullptr; + const loco::Stride<2> *_stride = nullptr; +}; + +/** + * There are two possible maintenance policies. + * - Introduce a new canonical node first, and then extend this algorithm later + * - Introduce a new canonical node and extend this algorithm at the same time + * + * The current implementation assumes the former one (for historical reason). + * + * TODO Evaluate the impact of the latter one + * + * NOTE "Forward" means that this algorithm computes the ouput shape from inputs shapes + */ +class ForwardShapeInferenceAlgorithm final : public loco::CanonicalNodeVisitor +{ +public: + ForwardShapeInferenceAlgorithm(const loco::ShapeInferenceRule::Context *ctx) : _ctx{ctx} + { + // DO NOTHING + } + +private: + const loco::ShapeInferenceRule::Context *_ctx; + +private: + bool shape_known(const loco::Node *node) const { return _ctx->known(node); } + loco::NodeShape node_shape(const loco::Node *node) const { return _ctx->get(node); } + +private: + loco::NodeShape eltwise_binary_node_shape(const loco::Node *node) + { + // This helper works only for binary node. + assert(node->arity() == 2); + + auto lhs_shape = node_shape(node->arg(0)); + auto rhs_shape = node_shape(node->arg(1)); + + // ASSERT: lhs_shape == rhs_shape + + return lhs_shape; + } + +public: + // CASE: AvgPool2D + loco::NodeShape visit(const loco::AvgPool2D *node) final + { + PlaneInference infer_plane_shape; + + infer_plane_shape.pad(node->pad()); + infer_plane_shape.window(node->window()); + infer_plane_shape.stride(node->stride()); + + auto input_feature_shape = node_shape(node->ifm()).as(); + auto input_plane_shape = make_plane_shape(input_feature_shape); + auto output_plane_shape = infer_plane_shape(input_plane_shape); + auto output_feature_shape = input_feature_shape; // AvgPool2D does not change count/depth + + // Update the height/width of output_feature_shape with that of output_plane_shape + update(output_feature_shape).with(output_plane_shape); + + return loco::NodeShape{output_feature_shape}; + } + + // CASE: BiasDecode + loco::NodeShape visit(const loco::BiasDecode *node) final + { + // The input of BiasDecode SHOULD BE a bias! + assert(node_shape(node->input()).domain() == loco::Domain::Bias); + auto input_bias_shape = node_shape(node->input()).as(); + + loco::TensorShape output_tensor_shape; + + output_tensor_shape.rank(1); + output_tensor_shape.dim(0) = input_bias_shape.length(); + + return loco::NodeShape{output_tensor_shape}; + } + + // CASE: BiasEncode + loco::NodeShape visit(const loco::BiasEncode *node) final + { + // The input of BiasEncode SHOULD BE a tensor! + assert(node_shape(node->input()).domain() == loco::Domain::Tensor); + auto input_tensor_shape = node_shape(node->input()).as(); + + loco::BiasShape output_bias_shape; + + output_bias_shape.length() = input_tensor_shape.dim(0); + + return loco::NodeShape{output_bias_shape}; + } + + // CASE: ConstGen + loco::NodeShape visit(const loco::ConstGen *node) final + { + loco::TensorShape tensor_shape; + + tensor_shape.rank(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + tensor_shape.dim(axis) = node->dim(axis); + } + + return loco::NodeShape{tensor_shape}; + } + + // CASE: Conv2D + loco::NodeShape visit(const loco::Conv2D *node) final + { + auto filter_shape = node_shape(node->ker()).as(); + auto filter_window = window_of(filter_shape); + + PlaneInference infer_plane_shape; + + infer_plane_shape.pad(node->pad()); + infer_plane_shape.window(&filter_window); + infer_plane_shape.stride(node->stride()); + + auto input_feature_shape = node_shape(node->ifm()).as(); + auto input_plane_shape = make_plane_shape(input_feature_shape); + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + loco::FeatureShape output_feature_shape; + + // "COUNT" does not change + output_feature_shape.count() = input_feature_shape.count(); + // "DEPTH" depends on # of filters + output_feature_shape.depth() = filter_shape.count(); + // Update the height/width of output_feature_shape with that of output_plane_shape + update(output_feature_shape).with(output_plane_shape); + + return loco::NodeShape{output_feature_shape}; + } + + // CASE: DepthwiseConv2D + loco::NodeShape visit(const loco::DepthwiseConv2D *node) final + { + auto depthwise_filter_shape = node_shape(node->ker()).as(); + auto dpethwise_filter_window = window_of(depthwise_filter_shape); + + PlaneInference infer_plane_shape; + + infer_plane_shape.pad(node->pad()); + infer_plane_shape.window(&dpethwise_filter_window); + infer_plane_shape.stride(node->stride()); + + auto input_feature_shape = node_shape(node->ifm()).as(); + auto input_plane_shape = make_plane_shape(input_feature_shape); + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + loco::FeatureShape output_feature_shape; + + // "COUNT" does not change + output_feature_shape.count() = input_feature_shape.count(); + // "DEPTH" depends on [in_channels * channel_multiplier] of filters + output_feature_shape.depth() = loco::Dimension(depthwise_filter_shape.depth().value() * + depthwise_filter_shape.multiplier().value()); + // Update the height/width of output_feature_shape with that of output_plane_shape + update(output_feature_shape).with(output_plane_shape); + + return loco::NodeShape{output_feature_shape}; + } + + // CASE: DepthwiseFilterEncode + loco::NodeShape visit(const loco::DepthwiseFilterEncode *node) final + { + auto input_tensor_shape = node_shape(node->input()).as(); + return loco::NodeShape{node->encoder()->shape(input_tensor_shape)}; + } + + // CASE: DepthwiseFilterDecode + loco::NodeShape visit(const loco::DepthwiseFilterDecode *node) final + { + auto input_dw_filter_shape = node_shape(node->input()).as(); + return loco::NodeShape{node->decoder()->shape(input_dw_filter_shape)}; + } + + // CASE: EltwiseAdd + loco::NodeShape visit(const loco::EltwiseAdd *node) final + { + return eltwise_binary_node_shape(node); + } + + // CASE: EltwiseDiv + loco::NodeShape visit(const loco::EltwiseDiv *node) final + { + return eltwise_binary_node_shape(node); + } + + // CASE: EltwiseMax + loco::NodeShape visit(const loco::EltwiseMax *node) final + { + return eltwise_binary_node_shape(node); + } + + // CASE: EltwiseMul + loco::NodeShape visit(const loco::EltwiseMul *node) final + { + return eltwise_binary_node_shape(node); + } + + // CASE: EltwiseSqrt + loco::NodeShape visit(const loco::EltwiseSqrt *node) final { return node_shape(node->input()); } + + // CASE: EltwiseSub + loco::NodeShape visit(const loco::EltwiseSub *node) final + { + return eltwise_binary_node_shape(node); + } + + // CASE: Forward + loco::NodeShape visit(const loco::Forward *node) final { return node_shape(node->input()); } + + // CASE: FeatureBiasAdd + loco::NodeShape visit(const loco::FeatureBiasAdd *node) final + { + assert(node_shape(node->value()).domain() == loco::Domain::Feature); + assert(node_shape(node->bias()).domain() == loco::Domain::Bias); + + // Q. What to do when there is a mismatch between value's depth and bias's length? + + return node_shape(node->value()); + } + + // CASE: FeatureDecode + loco::NodeShape visit(const loco::FeatureDecode *node) final + { + auto input_node_shape = node_shape(node->input()); + return loco::NodeShape{node->decoder()->shape(input_node_shape.as())}; + } + + // CASE: FeatureEncode + loco::NodeShape visit(const loco::FeatureEncode *node) final + { + auto input_node_shape = node_shape(node->input()); + return loco::NodeShape{node->encoder()->shape(input_node_shape.as())}; + } + + // CASE: FilterDecode + loco::NodeShape visit(const loco::FilterDecode *node) final + { + auto input_filter_shape = node_shape(node->input()).as(); + return loco::NodeShape{node->decoder()->shape(input_filter_shape)}; + } + + // CASE: FilterEncode + loco::NodeShape visit(const loco::FilterEncode *node) final + { + auto input_tensor_shape = node_shape(node->input()).as(); + return loco::NodeShape{node->encoder()->shape(input_tensor_shape)}; + } + + // CASE: FixedReshape + loco::NodeShape visit(const loco::FixedReshape *node) final + { + loco::TensorShape tensor_shape; + + tensor_shape.rank(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + tensor_shape.dim(axis) = node->dim(axis); + } + + return loco::NodeShape{tensor_shape}; + } + + // CASE: MatMul + loco::NodeShape visit(const loco::MatMul *node) final + { + assert(shape_known(node->lhs())); + assert(shape_known(node->rhs())); + auto const lhs_shape = node_shape(node->lhs()).as(); + auto const rhs_shape = node_shape(node->rhs()).as(); + + loco::MatrixShape out_shape; + + // Checking shape capability for multiplication + assert(lhs_shape.width() == rhs_shape.height()); + + out_shape.height() = lhs_shape.height(); + out_shape.width() = rhs_shape.width(); + + return out_shape; + } + + // CASE: MatrixDecode + loco::NodeShape visit(const loco::MatrixDecode *node) final + { + auto input_node_shape = node_shape(node->input()); + return loco::NodeShape{node->decoder()->shape(input_node_shape.as())}; + } + + // CASE: MatrixEncode + loco::NodeShape visit(const loco::MatrixEncode *node) final + { + auto input_node_shape = node_shape(node->input()); + return loco::NodeShape{node->encoder()->shape(input_node_shape.as())}; + } + + // CASE: MaxPool2D + loco::NodeShape visit(const loco::MaxPool2D *node) final + { + PlaneInference infer_plane_shape; + + infer_plane_shape.pad(node->pad()); + infer_plane_shape.window(node->window()); + infer_plane_shape.stride(node->stride()); + + auto input_feature_shape = node_shape(node->ifm()).as(); + auto input_plane_shape = make_plane_shape(input_feature_shape); + auto output_plane_shape = infer_plane_shape(input_plane_shape); + auto output_feature_shape = input_feature_shape; // MaxPool2D does not change count/depth + + // Update the height/width of output_feature_shape with that of output_plane_shape + update(output_feature_shape).with(output_plane_shape); + + return loco::NodeShape{output_feature_shape}; + } + + // CASE: Push + loco::NodeShape visit(const loco::Push *node) final + { + assert(shape_known(node->from())); + return node_shape(node->from()); + } + + // CASE: Pull + loco::NodeShape visit(const loco::Pull *node) final + { + // Build a tensor shape from "Pull" node + loco::TensorShape tensor_shape; + + tensor_shape.rank(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + tensor_shape.dim(axis) = node->dim(axis); + } + + return loco::NodeShape{tensor_shape}; + } + + // CASE: ReLU + loco::NodeShape visit(const loco::ReLU *node) final { return node_shape(node->input()); } + + // CASE: ReLU6 + loco::NodeShape visit(const loco::ReLU6 *node) final { return node_shape(node->input()); } + + // CASE: Tanh + loco::NodeShape visit(const loco::Tanh *node) final { return node_shape(node->input()); } + + // CASE: TensorBiasAdd + loco::NodeShape visit(const loco::TensorBiasAdd *node) final + { + assert(node_shape(node->value()).domain() == loco::Domain::Tensor); + assert(node_shape(node->bias()).domain() == loco::Domain::Bias); + + // Q. What to do when there is a mismatch between value's dim and bias's length? + + return node_shape(node->value()); + } + + // CASE: TensorConcat + loco::NodeShape visit(const loco::TensorConcat *node) + { + auto const lhs_shape = node_shape(node->lhs()).as(); + auto const rhs_shape = node_shape(node->rhs()).as(); + + assert(lhs_shape.rank() == rhs_shape.rank()); + uint32_t const out_rank = lhs_shape.rank(); + + loco::TensorShape out_shape; + + out_shape.rank(out_rank); + + for (uint32_t axis = 0; axis < out_rank; ++axis) + { + if (axis == node->axis()) + { + out_shape.dim(axis) = lhs_shape.dim(axis).value() + rhs_shape.dim(axis).value(); + } + else + { + assert(lhs_shape.dim(axis) == rhs_shape.dim(axis)); + out_shape.dim(axis) = lhs_shape.dim(axis); + } + } + + return loco::NodeShape{out_shape}; + } + + // CASE: TensorBroadcast + loco::NodeShape visit(const loco::TensorBroadcast *node) final + { + auto tensor_shape = node_shape(node->input()).as(); + auto const tensor_rank = tensor_shape.rank(); + + for (uint32_t axis = 0; axis < tensor_rank; ++axis) + { + if (node->mapping()->defined(axis)) + { + tensor_shape.dim(axis) = node->mapping()->dim(axis); + } + } + + return loco::NodeShape{tensor_shape}; + } + + // CASE: TensorReduce + loco::NodeShape visit(const loco::TensorReduce *node) final + { + auto tensor_shape = node_shape(node->input()).as(); + auto const tensor_rank = tensor_shape.rank(); + + for (uint32_t d = 0; d < tensor_rank; ++d) + if (node->axes()->defined(d)) + tensor_shape.dim(d) = 1; + + return loco::NodeShape{tensor_shape}; + } + + // CASE: TensorSoftmax + loco::NodeShape visit(const loco::TensorSoftmax *node) final { return node_shape(node->input()); } + + // CASE: TensorTranspose + loco::NodeShape visit(const loco::TensorTranspose *node) final + { + loco::TensorShape output_shape; + + auto input_shape = node_shape(node->input()).as(); + assert(input_shape.rank() == node->perm()->size()); + + output_shape.rank(input_shape.rank()); + + for (uint32_t output_axis = 0; output_axis < output_shape.rank(); output_axis++) + { + auto new_dim = input_shape.dim(node->perm()->axis(output_axis)); + output_shape.dim(output_axis) = new_dim; + } + + return loco::NodeShape(output_shape); + } + + // CASE: TransposedConv2D + loco::NodeShape visit(const loco::TransposedConv2D *node) final + { + auto filter_shape = node_shape(node->ker()).as(); + auto filter_window = window_of(filter_shape); + + PlaneInference infer_plane_shape; + + infer_plane_shape.pad(node->pad()); + infer_plane_shape.window(&filter_window); + infer_plane_shape.stride(node->stride()); + + auto input_feature_shape = node_shape(node->ifm()).as(); + auto input_plane_shape = make_plane_shape(input_feature_shape); + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + loco::FeatureShape output_feature_shape; + + // "COUNT" does not change + output_feature_shape.count() = input_feature_shape.count(); + // Output "DEPTH" depends on count of filters + output_feature_shape.depth() = filter_shape.count(); + // Update the height/width of output_feature_shape with that of output_plane_shape + update(output_feature_shape).with(output_plane_shape); + + return loco::NodeShape{output_feature_shape}; + } + + // CASE: TensorConstantPad + loco::NodeShape visit(const loco::TensorConstantPad *node) final + { + auto const tensor_shape = loco::shape_get(node->input()).as(); + auto padding = node->padding(); + + loco::TensorShape out_shape; + out_shape.rank(tensor_shape.rank()); + for (uint32_t axis = 0; axis < out_shape.rank(); ++axis) + { + out_shape.dim(axis) = + tensor_shape.dim(axis).value() + padding->front(axis) + padding->back(axis); + } + + return loco::NodeShape{out_shape}; + } +}; + +} // namespace + +namespace +{ +namespace compat +{ + +struct Context final : public loco::ShapeInferenceRule::Context +{ + bool known(const loco::Node *node) const final { return loco::shape_known(node); } + loco::NodeShape get(const loco::Node *node) const final { return loco::shape_get(node); } +}; + +class Sink final : public loco::ShapeInferenceRule::Sink +{ +public: + enum Status + { + Unknown, + Okay, + Fail, + }; + +public: + const Status &status(void) const { return _status; } + const loco::NodeShape &shape(void) const { return _shape; } + +public: + void okay(const loco::NodeShape &shape) final + { + _status = Okay; + _shape = shape; + } + + void fail(void) final + { + // Notify failure + _status = Fail; + } + +private: + Status _status = Unknown; + loco::NodeShape _shape; +}; + +} // namespace compat +} // namespace + +namespace loco +{ + +bool CanonicalShapeInferenceRule::support(const API &api) const +{ + return api == API::V1 or api == API::V2; +} + +bool CanonicalShapeInferenceRule::recognize(const Dialect *d) const +{ + return CanonicalDialect::get() == d; +} + +bool CanonicalShapeInferenceRule::infer(const Node *node, NodeShape &shape) const +{ + ::compat::Context ctx; + ::compat::Sink sink; + + infer(&ctx, node, &sink); + + assert(sink.status() == ::compat::Sink::Okay or sink.status() == ::compat::Sink::Fail); + + if (sink.status() == ::compat::Sink::Fail) + { + return false; + } + + shape = sink.shape(); + return true; +} + +void CanonicalShapeInferenceRule::infer(const Context *ctx, const Node *node, Sink *sink) const +{ + assert(node->dialect() == loco::CanonicalDialect::get()); + assert(dynamic_cast(node) != nullptr); + + ForwardShapeInferenceAlgorithm alg{ctx}; + auto shape = dynamic_cast(node)->accept(&alg); + + sink->okay(shape); +} + +} // namespace loco diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp new file mode 100644 index 000000000..5cc8c3808 --- /dev/null +++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/CanonicalShapeInferenceRule.h" +#include "loco/Service/ShapeInference.h" + +#include "GraphTestcase.h" + +#include + +#include + +TEST(CanonicalShapeInferenceRuleTest, minimal) +{ + // Create a simple identity network, which takes Tensor<1x2x3x4> as input. + GraphTestcase testcase{1, 2, 3, 4}; + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.push_node)); + ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().rank(), 4); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(0), 1); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(1), 2); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(2), 3); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(3), 4); +} + +TEST(CanonicalShapeInferenceRuleTest, const_gen) +{ + // Create a sample network + GraphTestcase testcase; + + testcase.const_node->dtype(loco::DataType::FLOAT32); + testcase.const_node->shape({1, 2}); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.push_node)); + ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().rank(), 2); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(0), 1); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(1), 2); +} + +TEST(CanonicalShapeInferenceRuleTest, relu) +{ + // Create a sample network + GraphTestcase testcase; + + testcase.pull_node->shape({1, 2, 3, 4}); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.push_node)); + ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().rank(), 4); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(0), 1); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(1), 2); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(2), 3); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(3), 4); +} + +TEST(CanonicalShapeInferenceRuleTest, feature_codec) +{ + // Create a sample network + GraphTestcase testcase; + + testcase.pull_node->shape({1, 2, 3, 4}); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.encode_node)); + ASSERT_EQ(loco::shape_get(testcase.encode_node).domain(), loco::Domain::Feature); + + ASSERT_TRUE(loco::shape_known(testcase.decode_node)); + ASSERT_EQ(loco::shape_get(testcase.decode_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.decode_node).as().rank(), 4); + ASSERT_EQ(loco::shape_get(testcase.decode_node).as().dim(0), 1); + ASSERT_EQ(loco::shape_get(testcase.decode_node).as().dim(1), 2); + ASSERT_EQ(loco::shape_get(testcase.decode_node).as().dim(2), 3); + ASSERT_EQ(loco::shape_get(testcase.decode_node).as().dim(3), 4); +} + +TEST(CanonicalShapeInferenceRuleTest, avgpool2d) +{ + using namespace loco; + + // Create a sample network + GraphTestcase testcase; + + auto perm = make_NHWC_perm(); + + testcase.pull_node->shape({1, 8, 4, 3}); + + testcase.encode_node->encoder(stdex::make_unique>(perm)); + + testcase.avgpool2d_node->window()->vertical(2); + testcase.avgpool2d_node->window()->horizontal(2); + + testcase.avgpool2d_node->stride()->vertical(2); + testcase.avgpool2d_node->stride()->horizontal(2); + + testcase.decode_node->decoder(stdex::make_unique>(perm)); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + // + // NOTE AvgPool2D testcase assumes NHWC layout + ASSERT_TRUE(loco::shape_known(testcase.avgpool2d_node)); + ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).domain(), loco::Domain::Feature); + ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as().count(), 1); + ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as().depth(), 3); + ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as().height(), 4); + ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as().width(), 2); +} + +TEST(CanonicalShapeInferenceRuleTest, depthwiseconv2d) +{ + using namespace loco; + + // Create a sample network + GraphTestcase testcase; + + testcase.pull_node->shape({1, 4, 4, 3}); + + testcase.const_node->dtype(loco::DataType::FLOAT32); + testcase.const_node->shape({2, 2, 3, 2}); + + testcase.depthwiseconv2d_node->stride()->vertical(1); + testcase.depthwiseconv2d_node->stride()->horizontal(1); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + // + // NOTE DepthwiseConv2D testcase assumes NHWC layout + ASSERT_TRUE(loco::shape_known(testcase.depthwiseconv2d_node)); + ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).domain(), loco::Domain::Feature); + ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as().count(), 1); + ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as().depth(), 6); + ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as().height(), 3); + ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as().width(), 3); +} + +TEST(CanonicalShapeInferenceRuleTest, transposedconv2d) +{ + using namespace loco; + + // Create a sample network + GraphTestcase testcase; + + testcase.pull_node->shape({1, 270, 480, 24}); // NHWC + + testcase.const_node->dtype(loco::DataType::FLOAT32); + testcase.const_node->shape({3, 3, 24, 12}); // HWCN (or HWIO) + + testcase.tr_conv2d_node->stride()->vertical(2); + testcase.tr_conv2d_node->stride()->horizontal(2); + + testcase.tr_conv2d_node->pad()->top(0); + testcase.tr_conv2d_node->pad()->bottom(1); + testcase.tr_conv2d_node->pad()->left(0); + testcase.tr_conv2d_node->pad()->right(1); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.tr_conv2d_node)); + ASSERT_EQ(loco::shape_get(testcase.tr_conv2d_node).domain(), loco::Domain::Feature); + ASSERT_EQ(loco::shape_get(testcase.tr_conv2d_node).as().count(), 1); + ASSERT_EQ(loco::shape_get(testcase.tr_conv2d_node).as().height(), 540); + ASSERT_EQ(loco::shape_get(testcase.tr_conv2d_node).as().width(), 960); + ASSERT_EQ(loco::shape_get(testcase.tr_conv2d_node).as().depth(), 12); +} + +TEST(CanonicalShapeInferenceRuleTest, maxpool2d) +{ + using namespace loco; + + // Create a sample network + GraphTestcase testcase; + + auto perm = make_NHWC_perm(); + + testcase.pull_node->shape({1, 8, 4, 3}); + + testcase.encode_node->encoder(stdex::make_unique>(perm)); + + testcase.maxpool2d_node->window()->vertical(2); + testcase.maxpool2d_node->window()->horizontal(2); + + testcase.maxpool2d_node->stride()->vertical(2); + testcase.maxpool2d_node->stride()->horizontal(2); + + testcase.decode_node->decoder(stdex::make_unique>(perm)); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + // + // NOTE MaxPool2D testcase assumes NHWC layout + ASSERT_TRUE(loco::shape_known(testcase.maxpool2d_node)); + ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).domain(), loco::Domain::Feature); + ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as().count(), 1); + ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as().depth(), 3); + ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as().height(), 4); + ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as().width(), 2); +} + +TEST(CanonicalShapeInferenceRuleTest, tensor_concat) +{ + using namespace loco; + + // Create a sample network + GraphTestcase testcase; + + testcase.lhs_node->shape({1, 2, 3}); + testcase.rhs_node->shape({1, 4, 3}); + testcase.concat_node->axis(1); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.concat_node)); + ASSERT_EQ(loco::shape_get(testcase.concat_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.concat_node).as().rank(), 3); + ASSERT_EQ(loco::shape_get(testcase.concat_node).as().dim(0), 1); + ASSERT_EQ(loco::shape_get(testcase.concat_node).as().dim(1), 6); + ASSERT_EQ(loco::shape_get(testcase.concat_node).as().dim(2), 3); +} + +TEST(CanonicalShapeInferenceRuleTest, fixed_reshape) +{ + // Create a sample network + GraphTestcase testcase; + + testcase.pull_node->shape({6, 6}); + testcase.reshape_node->shape({4, 9}); + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.push_node)); + ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().rank(), 2); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(0), 4); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(1), 9); +} + +TEST(CanonicalShapeInferenceRuleTest, tensor_broadcast) +{ + // Create a sample network + GraphTestcase testcase{1, 2}; + + testcase.broadcast_node->mapping()->dim(0) = 4; + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(testcase.push_node)); + ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().rank(), 2); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(0), 4); + ASSERT_EQ(loco::shape_get(testcase.push_node).as().dim(1), 2); +} + +TEST(CanonicalShapeInferenceRuleTest, tensor_transpose) +{ + // Create a sample network + GraphTestcase tc; + + tc.pull_node->shape({10, 20, 30, 40}); + + tc.transpose_node->perm()->size(4); + tc.transpose_node->perm()->axis(0) = 2; + tc.transpose_node->perm()->axis(1) = 3; + tc.transpose_node->perm()->axis(2) = 0; + tc.transpose_node->perm()->axis(3) = 1; + + // Run Inference + loco::CanonicalShapeInferenceRule rule; + + loco::apply(&rule).to(tc.graph()); + + // Verify! + ASSERT_TRUE(loco::shape_known(tc.push_node)); + ASSERT_EQ(loco::shape_get(tc.push_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(tc.push_node).as().rank(), 4); + ASSERT_EQ(loco::shape_get(tc.push_node).as().dim(0), 30); + ASSERT_EQ(loco::shape_get(tc.push_node).as().dim(1), 40); + ASSERT_EQ(loco::shape_get(tc.push_node).as().dim(2), 10); + ASSERT_EQ(loco::shape_get(tc.push_node).as().dim(3), 20); +} + +namespace +{ + +struct MockContext final : public loco::ShapeInferenceRule::Context +{ + bool known(const loco::Node *node) const final { return _content.find(node) != _content.end(); } + loco::NodeShape get(const loco::Node *node) const final { return _content.at(node); } + + std::map _content; +}; + +struct MockSink final : public loco::ShapeInferenceRule::Sink +{ + void okay(const loco::NodeShape &res) final { shape = res; } + void fail(void) final { return; } + + loco::NodeShape shape; +}; + +} // namespace + +TEST(CanonicalShapeInferenceRuleTest, infer_v2) +{ + auto g = loco::make_graph(); + + // Create an incomplete graph + auto relu_1 = g->nodes()->create(); + auto relu_2 = g->nodes()->create(); + + relu_2->input(relu_1); + + // Set up Context + MockContext ctx; + + loco::TensorShape tensor_shape; + + tensor_shape.rank(2); + tensor_shape.dim(0) = 4; + tensor_shape.dim(1) = 5; + + ctx._content[relu_1] = tensor_shape; + + // Create a Sink + MockSink sink; + + loco::CanonicalShapeInferenceRule rule; + + rule.infer(&ctx, relu_2, &sink); + + ASSERT_EQ(sink.shape.domain(), loco::Domain::Tensor); + ASSERT_EQ(sink.shape.as().rank(), 2); + ASSERT_EQ(sink.shape.as().dim(0), 4); + ASSERT_EQ(sink.shape.as().dim(1), 5); +} diff --git a/compiler/loco/src/Service/GraphBuilder.h b/compiler/loco/src/Service/GraphBuilder.h new file mode 100644 index 000000000..71084673c --- /dev/null +++ b/compiler/loco/src/Service/GraphBuilder.h @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_H__ +#define __GRAPH_BUILDER_H__ + +// loco-internal headers +#include "loco/IR/Graph.h" + +// repo-internal headers +#include + +// C++ standard headers +#include + +// +// This file includes a stack-based loco graph builder +// +// HOW TO USE +// +// loco::Graph *g = ... +// auto builder = make_graph_builder(g); +// +// builder->push(...); +// + +class GraphBuilder final +{ +public: + class Stack final + { + public: + Stack() = default; + + public: + loco::Node *top(void) const { return _content.top(); } + + public: + loco::Node *pop(void) + { + auto ret = top(); + _content.pop(); + return ret; + } + + public: + void push(loco::Node *node) { _content.push(node); } + + private: + std::stack _content; + }; + + class Context final + { + public: + Context(loco::Graph *graph) : _graph{graph} + { + // DO NOTHING + } + + public: + loco::Graph *graph(void) { return _graph; } + Stack *stack(void) { return &_stack; } + + private: + loco::Graph *_graph = nullptr; + Stack _stack; + }; + +public: + GraphBuilder(loco::Graph *graph) : _context{graph} + { + // DO NOTHING + } + +public: + // "Layer" is in theory a subgraph builder. + template + auto push(Args &&... args) + -> decltype(static_cast(nullptr)->operator()(static_cast(nullptr))) + { + Layer layer{std::forward(args)...}; + return layer(ctx()); + } + +public: + loco::Node *pop(void) { return ctx()->stack()->pop(); } + +private: + Context *ctx(void) { return &_context; } + +private: + Context _context; +}; + +static inline std::unique_ptr make_graph_builder(loco::Graph *g) +{ + return stdex::make_unique(g); +} + +// "InputLayer" creates both GraphInput and Pull node at once +struct InputLayer final +{ + class Return + { + public: + Return(loco::GraphInput *input, loco::Pull *node) : _input{input}, _node{node} + { + // DO NOTHING + } + + public: + loco::Pull *node(void) { return _node; } + + public: + Return *name(const std::string &value) + { + _input->name(value); + return this; + } + + public: + Return *shape(std::initializer_list dims) + { + // TODO Uncomment this line when GraphInput is ready + // _graph_input->shape(dims) + _node->shape(dims); + return this; + } + + private: + loco::GraphInput *_input = nullptr; + loco::Pull *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto input_index = ctx->graph()->inputs()->size(); + auto graph_input = ctx->graph()->inputs()->create(); + + auto pull_node = ctx->graph()->nodes()->create(); + + pull_node->index(input_index); + + loco::link(graph_input, pull_node); + + ctx->stack()->push(pull_node); + + return stdex::make_unique(graph_input, pull_node); + } +}; + +// "OutputLayer" creates both GraphOutput and Push node at once. +struct OutputLayer final +{ + class Return + { + public: + Return(loco::GraphOutput *output, loco::Push *node) : _output{output}, _node{node} + { + // DO NOTHING + } + + public: + loco::Push *node(void) { return _node; } + + public: + Return *name(const std::string &value) + { + // TODO Uncomment this line when GraphOutput is ready + // _graph_output->shape(dims) + _output->name(value); + return this; + } + + private: + loco::GraphOutput *_output = nullptr; + loco::Push *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto output_index = ctx->graph()->outputs()->size(); + auto graph_output = ctx->graph()->outputs()->create(); + + auto push_node = ctx->graph()->nodes()->create(); + + push_node->from(ctx->stack()->pop()); + push_node->index(output_index); + + loco::link(graph_output, push_node); + + ctx->stack()->push(push_node); + + return stdex::make_unique(graph_output, push_node); + } +}; + +struct ReLULayer final +{ + // This "Return" is unnecessary for ReLU as ReLU has no attributes), but + // introduced for consistency. + class Return + { + public: + Return(loco::ReLU *node) : _node{node} + { + // DO NOTHING + } + + public: + loco::ReLU *node(void) { return _node; } + + private: + loco::ReLU *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto relu_node = ctx->graph()->nodes()->create(); + + relu_node->input(ctx->stack()->pop()); + + ctx->stack()->push(relu_node); + + return stdex::make_unique(relu_node); + } +}; + +struct ConstGenLayer final +{ + class Return + { + public: + Return(loco::ConstGen *node) : _node{node} + { + // DO NOTHING + } + + public: + loco::ConstGen *node(void) { return _node; } + + private: + loco::ConstGen *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto const_node = ctx->graph()->nodes()->create(); + + ctx->stack()->push(const_node); + + return stdex::make_unique(const_node); + } +}; + +#include "loco/IR/PermutingCodec.h" + +struct FeatureEncodeLayer final +{ + class Return + { + public: + Return(loco::FeatureEncode *node) : _node{node} + { + // DO NOTHING + } + + public: + Return *perm(const loco::Permutation &perm) + { + using namespace loco; + _node->encoder(stdex::make_unique>(perm)); + return this; + } + + public: + loco::FeatureEncode *node(void) { return _node; } + + private: + loco::FeatureEncode *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto encode_node = ctx->graph()->nodes()->create(); + + encode_node->input(ctx->stack()->pop()); + + ctx->stack()->push(encode_node); + + return stdex::make_unique(encode_node); + } +}; + +struct FeatureDecodeLayer final +{ + class Return + { + public: + Return(loco::FeatureDecode *node) : _node{node} + { + // DO NOTHING + } + + public: + Return *perm(const loco::Permutation &perm) + { + using namespace loco; + _node->decoder(stdex::make_unique>(perm)); + return this; + } + + public: + loco::FeatureDecode *node(void) { return _node; } + + private: + loco::FeatureDecode *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + using namespace loco; + + auto decode_node = ctx->graph()->nodes()->create(); + + decode_node->input(ctx->stack()->pop()); + + ctx->stack()->push(decode_node); + + return stdex::make_unique(decode_node); + } +}; + +struct FilterEncodeLayer final +{ + class Return + { + public: + Return(loco::FilterEncode *node) : _node{node} + { + // DO NOTHING + } + + public: + Return *perm(const loco::Permutation &perm) + { + auto encoder = stdex::make_unique>(); + encoder->perm(perm); + _node->encoder(std::move(encoder)); + return this; + } + + public: + loco::FilterEncode *node(void) { return _node; } + + private: + loco::FilterEncode *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto encode_node = ctx->graph()->nodes()->create(); + + encode_node->input(ctx->stack()->pop()); + + ctx->stack()->push(encode_node); + + return stdex::make_unique(encode_node); + } +}; + +struct DepthwiseFilterEncodeLayer final +{ + class Return + { + public: + Return(loco::DepthwiseFilterEncode *node) : _node{node} + { + // DO NOTHING + } + + public: + Return *perm(const loco::Permutation &perm) + { + using namespace loco; + _node->encoder(stdex::make_unique>(perm)); + return this; + } + + public: + loco::DepthwiseFilterEncode *node(void) { return _node; } + + private: + loco::DepthwiseFilterEncode *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto encode_node = ctx->graph()->nodes()->create(); + + encode_node->input(ctx->stack()->pop()); + + ctx->stack()->push(encode_node); + + return stdex::make_unique(encode_node); + } +}; + +struct DepthwiseConv2DLayer final +{ + class Return + { + public: + Return(loco::DepthwiseConv2D *node) : _node{node} + { + // DO NOTHING + } + + public: + loco::DepthwiseConv2D *node(void) { return _node; } + + private: + loco::DepthwiseConv2D *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto depthwiseconv2d_node = ctx->graph()->nodes()->create(); + + depthwiseconv2d_node->ker(ctx->stack()->pop()); + depthwiseconv2d_node->ifm(ctx->stack()->pop()); + + ctx->stack()->push(depthwiseconv2d_node); + + return stdex::make_unique(depthwiseconv2d_node); + } +}; + +struct TransposedConv2DLayer final +{ + class Return + { + public: + Return(loco::TransposedConv2D *node) : _node{node} + { + // DO NOTHING + } + + public: + loco::TransposedConv2D *node(void) { return _node; } + + private: + loco::TransposedConv2D *_node; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto tr_conv2d_node = ctx->graph()->nodes()->create(); + + tr_conv2d_node->ker(ctx->stack()->pop()); + tr_conv2d_node->ifm(ctx->stack()->pop()); + + ctx->stack()->push(tr_conv2d_node); + + return stdex::make_unique(tr_conv2d_node); + } +}; + +struct FixedReshapeLayer final +{ + class Return + { + public: + Return(loco::FixedReshape *node) : _node{node} + { + // DO NOTHING + } + + public: + Return *shape(std::initializer_list dims) + { + _node->shape(dims); + return this; + } + + public: + loco::FixedReshape *node(void) { return _node; } + + private: + loco::FixedReshape *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto reshape_node = ctx->graph()->nodes()->create(); + + reshape_node->input(ctx->stack()->pop()); + + ctx->stack()->push(reshape_node); + + return stdex::make_unique(reshape_node); + } +}; + +struct TensorBroadcastLayer final +{ + class Return + { + public: + Return(loco::TensorBroadcast *node) : _node{node} + { + // DO NOTHING + } + + public: + loco::TensorBroadcast *node(void) { return _node; } + + private: + loco::TensorBroadcast *_node = nullptr; + }; + + std::unique_ptr operator()(GraphBuilder::Context *ctx) + { + auto broadcast_node = ctx->graph()->nodes()->create(); + + broadcast_node->input(ctx->stack()->pop()); + ctx->stack()->push(broadcast_node); + + return stdex::make_unique(broadcast_node); + } +}; + +#endif // __GRAPH_BUILDER_H__ diff --git a/compiler/loco/src/Service/GraphBuilder.test.cpp b/compiler/loco/src/Service/GraphBuilder.test.cpp new file mode 100644 index 000000000..7b2ea5198 --- /dev/null +++ b/compiler/loco/src/Service/GraphBuilder.test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GraphBuilder.h" + +#include "loco/IR/Nodes.h" +#include "loco/IR/CanonicalDialect.h" +#include "loco/IR/CanonicalOpcode.h" + +#include + +TEST(GraphBuilderTest, Usecase_000) +{ + struct SampleLayer final + { + loco::Node *operator()(GraphBuilder::Context *ctx) + { + auto node = ctx->graph()->nodes()->create(); + ctx->stack()->push(node); + return node; + } + }; + + auto g = loco::make_graph(); + auto gbuilder = make_graph_builder(g.get()); + + gbuilder->push(); + + auto node = gbuilder->pop(); + + ASSERT_EQ(g->nodes()->size(), 1); + ASSERT_EQ(node->dialect(), loco::CanonicalDialect::get()); + ASSERT_EQ(node->opnum(), static_cast(loco::CanonicalOpcode::ConstGen)); +} diff --git a/compiler/loco/src/Service/GraphTestcase.h b/compiler/loco/src/Service/GraphTestcase.h new file mode 100644 index 000000000..6743b9a14 --- /dev/null +++ b/compiler/loco/src/Service/GraphTestcase.h @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_TESTCASE_H__ +#define __GRAPH_TESTCASE_H__ + +#include "loco/IR/Graph.h" +#include "loco/IR/PermutingCodec.h" + +#include "GraphBuilder.h" + +#include + +enum class GraphCode +{ + Identity, + ConstGen, + Relu, + FeatureCodec, + AvgPool2D, + DepthwiseConv2D, + TransposedConv2D, + MaxPool2D, + TensorBroadcast, + TensorConcat, + TensorTranspose, + FixedReshape, +}; + +namespace +{ + +template loco::Permutation make_NHWC_perm(void); + +template <> loco::Permutation make_NHWC_perm(void) +{ + loco::Permutation perm; + + perm[loco::FeatureAxis::Count] = 0; + perm[loco::FeatureAxis::Height] = 1; + perm[loco::FeatureAxis::Width] = 2; + perm[loco::FeatureAxis::Depth] = 3; + + return perm; +} + +template loco::Permutation make_HWCN_perm(void); + +// @note Also known as HWIO permutation +template <> loco::Permutation make_HWCN_perm(void) +{ + loco::Permutation perm; + + perm[loco::FilterAxis::Height] = 0; + perm[loco::FilterAxis::Width] = 1; + perm[loco::FilterAxis::Depth] = 2; + perm[loco::FilterAxis::Count] = 3; + + return perm; +} + +template loco::Permutation make_HWCM_perm(void); + +template <> loco::Permutation make_HWCM_perm(void) +{ + loco::Permutation perm; + + perm[loco::DepthwiseFilterAxis::Height] = 0; + perm[loco::DepthwiseFilterAxis::Width] = 1; + perm[loco::DepthwiseFilterAxis::Depth] = 2; + perm[loco::DepthwiseFilterAxis::Multiplier] = 3; + + return perm; +} + +} // namespace + +template class GraphTestcase; + +template <> class GraphTestcase final +{ +private: + void init(std::initializer_list dims) + { + // Create a sample network + _graph = loco::make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->shape(dims)->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + // NOTE This default constructor guarantees backward compatbility. + GraphTestcase() { init({1, 4, 8, 3}); } + GraphTestcase(std::initializer_list dims) { init(dims); } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + _graph = loco::make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + const_node = graph_builder->push()->node(); + + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::ConstGen *const_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + // Create a sample network + _graph = loco::make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->node(); + relu_node = graph_builder->push()->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::ReLU *relu_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + Permutation perm; + + perm[FeatureAxis::Count] = 0; + perm[FeatureAxis::Height] = 1; + perm[FeatureAxis::Width] = 2; + perm[FeatureAxis::Depth] = 3; + + // Create a sample network + _graph = make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->node(); + encode_node = graph_builder->push()->perm(perm)->node(); + decode_node = graph_builder->push()->perm(perm)->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FeatureEncode *encode_node = nullptr; + loco::FeatureDecode *decode_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + // Create a sample network + _graph = make_graph(); + + // Create Graph Input/Output + auto graph_input = _graph->inputs()->create(); + auto graph_output = _graph->outputs()->create(); + + graph_input->name("input"); + graph_output->name("output"); + + // Create and connect nodes + pull_node = _graph->nodes()->create(); + pull_node->index(0); + + encode_node = _graph->nodes()->create(); + encode_node->input(pull_node); + + avgpool2d_node = _graph->nodes()->create(); + avgpool2d_node->ifm(encode_node); + + decode_node = _graph->nodes()->create(); + decode_node->input(avgpool2d_node); + + push_node = _graph->nodes()->create(); + push_node->index(0); + push_node->from(decode_node); + + // Create a link between input/output and corresponding nodes + loco::link(graph_input, pull_node); + loco::link(graph_output, push_node); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FeatureEncode *encode_node = nullptr; + loco::AvgPool2D *avgpool2d_node = nullptr; + loco::FeatureDecode *decode_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + _graph = make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + Permutation perm = make_NHWC_perm(); + Permutation filter_perm = make_HWCM_perm(); + + pull_node = graph_builder->push()->name("input")->node(); + encode_node = graph_builder->push()->perm(perm)->node(); + + const_node = graph_builder->push()->node(); + + filter_encode_node = + graph_builder->push()->perm(filter_perm)->node(); + + depthwiseconv2d_node = graph_builder->push()->node(); + + decode_node = graph_builder->push()->perm(perm)->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FeatureEncode *encode_node = nullptr; + loco::ConstGen *const_node = nullptr; + loco::DepthwiseFilterEncode *filter_encode_node = nullptr; + loco::DepthwiseConv2D *depthwiseconv2d_node = nullptr; + loco::FeatureDecode *decode_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + // Prepare permutations + Permutation feature_perm = make_NHWC_perm(); + Permutation filter_perm = make_HWCN_perm(); + + // Build graph + _graph = make_graph(); + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->node(); + encode_node = graph_builder->push()->perm(feature_perm)->node(); + const_node = graph_builder->push()->node(); + filter_encode_node = graph_builder->push()->perm(filter_perm)->node(); + tr_conv2d_node = graph_builder->push()->node(); + decode_node = graph_builder->push()->perm(feature_perm)->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FeatureEncode *encode_node = nullptr; + loco::ConstGen *const_node = nullptr; + loco::FilterEncode *filter_encode_node = nullptr; + loco::TransposedConv2D *tr_conv2d_node = nullptr; + loco::FeatureDecode *decode_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + // Create a sample network + _graph = make_graph(); + + // Create Graph Input/Output + auto graph_input = _graph->inputs()->create(); + auto graph_output = _graph->outputs()->create(); + + graph_input->name("input"); + graph_output->name("output"); + + // Create and connect nodes + pull_node = _graph->nodes()->create(); + pull_node->index(0); + + encode_node = _graph->nodes()->create(); + encode_node->input(pull_node); + + maxpool2d_node = _graph->nodes()->create(); + maxpool2d_node->ifm(encode_node); + + decode_node = _graph->nodes()->create(); + decode_node->input(maxpool2d_node); + + push_node = _graph->nodes()->create(); + push_node->index(0); + push_node->from(decode_node); + + // Create a link between input/output and corresponding nodes + loco::link(graph_input, pull_node); + loco::link(graph_output, push_node); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FeatureEncode *encode_node = nullptr; + loco::MaxPool2D *maxpool2d_node = nullptr; + loco::FeatureDecode *decode_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + // Create a sample network + _graph = make_graph(); + + // Create Graph Input/Output + auto graph_lhs = _graph->inputs()->create(); + auto graph_rhs = _graph->inputs()->create(); + auto graph_out = _graph->outputs()->create(); + + graph_lhs->name("lhs"); + graph_rhs->name("rhs"); + graph_out->name("output"); + + // Create and connect nodes + lhs_node = _graph->nodes()->create(); + lhs_node->index(0); + + rhs_node = _graph->nodes()->create(); + rhs_node->index(1); + + concat_node = _graph->nodes()->create(); + concat_node->lhs(lhs_node); + concat_node->rhs(rhs_node); + + push_node = _graph->nodes()->create(); + push_node->index(0); + push_node->from(concat_node); + + // Create a link between input/output and corresponding nodes + loco::link(graph_lhs, lhs_node); + loco::link(graph_rhs, rhs_node); + loco::link(graph_out, push_node); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *lhs_node = nullptr; + loco::Pull *rhs_node = nullptr; + loco::TensorConcat *concat_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + _graph = loco::make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->node(); + reshape_node = graph_builder->push()->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::FixedReshape *reshape_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase(std::initializer_list dims) + { + _graph = loco::make_graph(); + + auto graph_builder = make_graph_builder(_graph.get()); + + pull_node = graph_builder->push()->name("input")->shape(dims)->node(); + broadcast_node = graph_builder->push()->node(); + push_node = graph_builder->push()->name("output")->node(); + } + +public: + loco::Graph *graph(void) { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::TensorBroadcast *broadcast_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +template <> class GraphTestcase final +{ +public: + GraphTestcase() + { + using namespace loco; + + // Create a sample network + _graph = make_graph(); + + // Create Graph Input/Output + auto graph_input = _graph->inputs()->create(); + auto graph_output = _graph->outputs()->create(); + + graph_input->name("input"); + graph_output->name("output"); + + // Create and connect nodes + pull_node = _graph->nodes()->create(); + pull_node->index(0); + + transpose_node = _graph->nodes()->create(); + transpose_node->input(pull_node); + + push_node = _graph->nodes()->create(); + push_node->index(0); + push_node->from(transpose_node); + + // Create a link between input/output and corresponding nodes + loco::link(graph_input, pull_node); + loco::link(graph_output, push_node); + } + +public: + loco::Graph *graph() { return _graph.get(); } + + loco::Pull *pull_node = nullptr; + loco::TensorTranspose *transpose_node = nullptr; + loco::Push *push_node = nullptr; + +private: + std::unique_ptr _graph; +}; + +#endif // __GRAPH_TESTCASE_H__ diff --git a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.cpp b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.cpp new file mode 100644 index 000000000..2178f5d05 --- /dev/null +++ b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/MultiDialectShapeInferenceRule.h" +#include "loco/Service/ShapeInferenceRule.h" + +#include +#include +#include + +#include + +namespace loco +{ + +bool MultiDialectShapeInferenceRule::recognize(const Dialect *d) const +{ + const auto found = _rules.find(d); + + if (found == _rules.cend()) + return false; + + auto rule = found->second; + auto result = rule->recognize(d); + + return result; +} + +bool MultiDialectShapeInferenceRule::infer(const Node *node, NodeShape &shape) const +{ + const auto found = _rules.find(node->dialect()); + + if (found == _rules.cend()) + return false; + + auto rule = found->second; + if (rule->infer(node, shape)) + return true; + + return false; +} + +MultiDialectShapeInferenceRule &MultiDialectShapeInferenceRule::bind(const Dialect *d, + const ShapeInferenceRule *rule) +{ + assert(_rules.find(d) == _rules.end()); + assert(rule->recognize(d)); + + _rules[d] = rule; + + return (*this); +} + +} // namespace loco diff --git a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp new file mode 100644 index 000000000..ffa9ee5ca --- /dev/null +++ b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/CanonicalShapeInferenceRule.h" +#include "loco/Service/MultiDialectShapeInferenceRule.h" +#include "loco/Service/ShapeInference.h" + +#include +#include + +#include + +#include +#include + +// mockup for MultiDialectShapeInferenceRule +// Each class is dedicated for handling shape { D1, D2 } and D1, D2 are declared as a template +namespace +{ + +template class TestDialect final : public loco::Dialect +{ +public: + static Dialect *get(void) + { + static TestDialect d; + return &d; + } +}; + +template +struct TestOpNode final : public loco::FixedArity<1>::Mixin, + public loco::NodeMixin +{ + void input(Node *node) { at(0)->node(node); } + const loco::Dialect *dialect(void) const final { return TestDialect::get(); } + uint32_t opnum(void) const final { return static_cast(D1); /* not used */ } +}; + +template +struct TestShapeInferenceRule final : public loco::ShapeInferenceRule +{ +public: + bool recognize(const loco::Dialect *d) const final { return (d == TestDialect::get()); } + + bool infer(const loco::Node *node, loco::NodeShape &node_shape) const final + { + assert(recognize(node->dialect())); + auto test_node = dynamic_cast *>(node); + assert(test_node != nullptr); + + loco::TensorShape ts; + { + ts.rank(2); + ts.dim(0) = D1; + ts.dim(1) = D2; // making shape : { D1, D2 } + } + + node_shape.set(ts); + + return true; + } +}; + +} // namespace + +TEST(MultiDialectShapeInferenceRuleTest, test1) +{ + // Create a simple network : Pull ------- t23<2,3> ------------ t45<4,5> ---------- Push + // TensorShape({2, 3}) TensorShape({4, 5}) + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + auto t23_node = g->nodes()->create>(); + auto t45_node = g->nodes()->create>(); + auto push_node = g->nodes()->create(); + + t23_node->input(pull_node); + t45_node->input(t23_node); + push_node->from(t45_node); + + auto graph_input = g->inputs()->create(); + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + graph_output->name("output"); + loco::link(graph_output, push_node); + + // initially they don't have shape info + ASSERT_FALSE(loco::shape_known(t23_node)); + ASSERT_FALSE(loco::shape_known(t45_node)); + + // Run Type Inference + loco::CanonicalShapeInferenceRule canonical_rule; + TestShapeInferenceRule<2, 3> t23_rule; + TestShapeInferenceRule<4, 5> t45_rule; + + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(TestDialect<2, 3>::get(), &t23_rule) + .bind(TestDialect<4, 5>::get(), &t45_rule); + + loco::apply(&rules).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::shape_known(t23_node)); + auto t23_shape = loco::shape_get(t23_node); + ASSERT_EQ(t23_shape.domain(), loco::Domain::Tensor); + ASSERT_EQ(t23_shape.as().rank(), 2); + ASSERT_EQ(t23_shape.as().dim(0), 2); + ASSERT_EQ(t23_shape.as().dim(1), 3); + + ASSERT_TRUE(loco::shape_known(t45_node)); + auto t45_shape = loco::shape_get(t45_node); + ASSERT_EQ(t45_shape.domain(), loco::Domain::Tensor); + ASSERT_EQ(t45_shape.as().rank(), 2); + ASSERT_EQ(t45_shape.as().dim(0), 4); + ASSERT_EQ(t45_shape.as().dim(1), 5); +} diff --git a/compiler/loco/src/Service/ShapeInference.cpp b/compiler/loco/src/Service/ShapeInference.cpp new file mode 100644 index 000000000..84eb10963 --- /dev/null +++ b/compiler/loco/src/Service/ShapeInference.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/ShapeInference.h" +#include "loco/IR/Algorithm.h" + +#include + +#include + +namespace +{ + +bool inputs_shape_ready(loco::Node *node) +{ + assert(node != nullptr); + + for (uint32_t arity = 0; arity < node->arity(); ++arity) + { + if (!loco::ShapeInference::known(node->arg(arity))) + { + return false; + } + } + return true; +} + +} // namespace + +// +// Infrastructure +// +namespace +{ + +struct ShapeAnnotation : public loco::NodeAnnotation +{ +public: + ShapeAnnotation(const loco::NodeShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + const loco::NodeShape &shape(void) const { return _shape; } + +private: + loco::NodeShape _shape; +}; + +} // namespace + +namespace loco +{ + +bool ShapeInferenceSession::to(Graph *g) const +{ + assert(_rule->support(ShapeInferenceRule::API::V1) && "API v1 is unavailable"); + + bool changed = false; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + if (_rule->recognize(node->dialect())) + { + loco::NodeShape shape; + + if (!shape_known(node) && inputs_shape_ready(node)) + { + if (_rule->infer(node, shape)) + { + node->annot(stdex::make_unique(shape)); + changed = true; + } + } + } + } + + return changed; +} + +bool ShapeInference::known(const Node *node) { return node->annot() != nullptr; } + +NodeShape ShapeInference::get(const Node *node) +{ + assert(known(node)); + return node->annot()->shape(); +} + +void ShapeInference::erase(Node *node) { node->annot(nullptr); } + +} // namespace loco diff --git a/compiler/loco/src/Service/ShapeInference.test.cpp b/compiler/loco/src/Service/ShapeInference.test.cpp new file mode 100644 index 000000000..e10b98844 --- /dev/null +++ b/compiler/loco/src/Service/ShapeInference.test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/ShapeInference.h" +#include "GraphTestcase.h" + +#include + +#include + +// This test validates whether framework works as expected. +TEST(ShapeInferenceTest, framework) +{ + // Mock-up Shape Inference Rule + struct SampleShapeInferenceRule final : public loco::ShapeInferenceRule + { + public: + SampleShapeInferenceRule(std::vector *nodes) : _nodes{nodes} + { + // DO NOTHING + } + + public: + // Accept all the dialects + bool recognize(const loco::Dialect *) const final { return true; } + + bool infer(const loco::Node *node, loco::NodeShape &shape) const final + { + // Record the order of inference + _nodes->emplace_back(node); + + if (_nodes->size() != 1) + { + return false; + } + + // Set the first node as Tensor<1> + loco::TensorShape tensor_shape; + + tensor_shape.rank(1); + tensor_shape.dim(0) = 4; + + shape.set(tensor_shape); + + return true; + } + + private: + std::vector *_nodes; + }; + + GraphTestcase testcase; + + std::vector nodes; + + SampleShapeInferenceRule rule{&nodes}; + + loco::apply(&rule).to(testcase.graph()); + + // Framework SHOULD visit all the nodes + ASSERT_EQ(nodes.size(), 2); + // Framework SHOULD visit "pull" before "push" + ASSERT_EQ(nodes.at(0), testcase.pull_node); + ASSERT_EQ(nodes.at(1), testcase.push_node); + + // Framework SHOULD make an annotation if "rule" returns TRUE + ASSERT_TRUE(loco::shape_known(testcase.pull_node)); + ASSERT_EQ(loco::shape_get(testcase.pull_node).domain(), loco::Domain::Tensor); + ASSERT_EQ(loco::shape_get(testcase.pull_node).as().rank(), 1); + ASSERT_EQ(loco::shape_get(testcase.pull_node).as().dim(0), 4); + + // Framework SHOULD NOT make any annotation if "rule" returns FALSE + ASSERT_FALSE(loco::shape_known(testcase.push_node)); +} diff --git a/compiler/loco/src/Service/ShapeInferenceRule.cpp b/compiler/loco/src/Service/ShapeInferenceRule.cpp new file mode 100644 index 000000000..bed841260 --- /dev/null +++ b/compiler/loco/src/Service/ShapeInferenceRule.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/ShapeInferenceRule.h" + +#include + +// This file validates "ShapeInferenceRule.h". Please DO NOT remove this file. + +namespace loco +{ + +void ShapeInferenceRule::infer(const Context *, const Node *, Sink *) const +{ + throw std::runtime_error{"API v2 is not supported"}; +} + +} // namespace loco diff --git a/compiler/loco/src/Service/TypeInference.cpp b/compiler/loco/src/Service/TypeInference.cpp new file mode 100644 index 000000000..fbf0033ee --- /dev/null +++ b/compiler/loco/src/Service/TypeInference.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/TypeInference.h" + +#include "loco/IR/Algorithm.h" + +#include + +#include + +namespace +{ + +struct DataTypeAnnotation : public loco::NodeAnnotation +{ +public: + DataTypeAnnotation(const loco::DataType &dtype) : _dtype{dtype} + { + // DO NOTHING + } + +public: + const loco::DataType &dtype(void) const { return _dtype; } + +private: + loco::DataType _dtype; +}; + +bool inputs_dtype_ready(loco::Node *node) +{ + assert(node != nullptr); + + for (uint32_t arity = 0; arity < node->arity(); ++arity) + { + if (!loco::TypeInference::known(node->arg(arity))) + { + return false; + } + } + return true; +} + +} // namespace + +namespace loco +{ + +bool TypeInferenceSession::to(Graph *g) const +{ + bool changed = false; + + for (auto node : postorder_traversal(output_nodes(g))) + { + if (_rule->recognize(node->dialect())) + { + DataType dtype = DataType::Unknown; + + if (!dtype_known(node) && inputs_dtype_ready(node)) + { + if (_rule->infer(node, dtype)) + { + node->annot(stdex::make_unique(dtype)); + changed = true; + } + } + } + } + + return changed; +} + +bool TypeInference::known(const Node *node) { return node->annot() != nullptr; } + +DataType TypeInference::get(const Node *node) +{ + assert(known(node)); + return node->annot()->dtype(); +} + +void TypeInference::erase(Node *node) { return node->annot(nullptr); } + +} // namespace loco + +// +// Canonical (Data) Type Inference Rule +// +#include +#include +#include + +namespace +{ + +/** + * There are two possible maintenance policies. + * - Introduce a new canonical node first, and then extend this algorithm later + * - Introduce a new canonical node and extend this algorithm at the same time + * + * The current implementation assumes the former one (for historical reason). + * + * TODO Evaluate the impact of the latter one + */ +struct CanonicalTypeForwardAlgorithm final : public loco::CanonicalNodeVisitor +{ + loco::DataType visit(const loco::AvgPool2D *node) { return loco::dtype_get(node->ifm()); } + loco::DataType visit(const loco::BiasDecode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::BiasEncode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::ConstGen *node) { return node->dtype(); } + loco::DataType visit(const loco::Conv2D *node) { return loco::dtype_get(node->ifm()); } + loco::DataType visit(const loco::DepthwiseConv2D *node) { return loco::dtype_get(node->ifm()); } + loco::DataType visit(const loco::DepthwiseFilterEncode *node) + { + return loco::dtype_get(node->input()); + } + loco::DataType visit(const loco::DepthwiseFilterDecode *node) + { + return loco::dtype_get(node->input()); + } + loco::DataType visit(const loco::EltwiseAdd *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::EltwiseDiv *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::EltwiseMax *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::EltwiseMul *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::EltwiseSqrt *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::EltwiseSub *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::Forward *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::FeatureBiasAdd *node) { return loco::dtype_get(node->value()); } + loco::DataType visit(const loco::FeatureDecode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::FeatureEncode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::FilterDecode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::FilterEncode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::FixedReshape *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::MatrixDecode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::MatrixEncode *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::MatMul *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::MaxPool2D *node) { return loco::dtype_get(node->ifm()); } + loco::DataType visit(const loco::Push *node) { return loco::dtype_get(node->from()); } + loco::DataType visit(const loco::Pull *node) { return node->dtype(); } + loco::DataType visit(const loco::ReLU *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::ReLU6 *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::Tanh *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::TensorConcat *node) { return loco::dtype_get(node->lhs()); } + loco::DataType visit(const loco::TensorConstantPad *node) + { + return loco::dtype_get(node->input()); + } + loco::DataType visit(const loco::TensorBiasAdd *node) { return loco::dtype_get(node->value()); } + loco::DataType visit(const loco::TensorBroadcast *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::TensorReduce *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::TensorSoftmax *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::TensorTranspose *node) { return loco::dtype_get(node->input()); } + loco::DataType visit(const loco::TransposedConv2D *node) { return loco::dtype_get(node->ifm()); } +}; + +} // namespace + +namespace loco +{ + +bool CanonicalTypeInferenceRule::recognize(const Dialect *d) const +{ + // This rule recognizes only "loco.canonical" dialect! + return CanonicalDialect::get() == d; +} + +bool CanonicalTypeInferenceRule::infer(const Node *node, DataType &dtype) const +{ + assert(node->dialect() == loco::CanonicalDialect::get()); + assert(dynamic_cast(node) != nullptr); + + CanonicalTypeForwardAlgorithm alg; + dtype = dynamic_cast(node)->accept(&alg); + + return true; +} + +bool MultiDialectTypeInferenceRule::recognize(const Dialect *d) const +{ + const auto found = _rules.find(d); + + if (found == _rules.cend()) + return false; + + auto rule = found->second; + auto result = rule->recognize(d); + + return result; +} + +bool MultiDialectTypeInferenceRule::infer(const Node *node, DataType &dtype) const +{ + const auto found = _rules.find(node->dialect()); + + if (found == _rules.cend()) + return false; + + auto rule = found->second; + if (rule->infer(node, dtype)) + return true; + + return false; +} + +MultiDialectTypeInferenceRule &MultiDialectTypeInferenceRule::bind(const Dialect *d, + const TypeInferenceRule *rule) +{ + assert(_rules.find(d) == _rules.end()); + assert(rule->recognize(d)); + + _rules[d] = rule; + + return (*this); +} + +} // namespace loco diff --git a/compiler/loco/src/Service/TypeInference.test.cpp b/compiler/loco/src/Service/TypeInference.test.cpp new file mode 100644 index 000000000..4660401db --- /dev/null +++ b/compiler/loco/src/Service/TypeInference.test.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco/Service/TypeInference.h" + +#include "GraphTestcase.h" + +#include +#include + +#include + +#include + +// This test validates whether framework works as expected. +TEST(TypeInferenceTest, framework) +{ + // Create a sample network + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + auto push_node = g->nodes()->create(); + + push_node->from(pull_node); + + // Create Graph Input & Output + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + loco::link(graph_output, push_node); + + // Mock-up Type Inference Rule + struct SampleTypeInferenceRule final : public loco::TypeInferenceRule + { + public: + SampleTypeInferenceRule(std::vector *nodes) : _nodes{nodes} + { + // DO NOTHING + } + + public: + bool recognize(const loco::Dialect *) const final + { + // Accept all the dialects + return true; + } + + bool infer(const loco::Node *node, loco::DataType &dtype) const final + { + // Record the order of inference + _nodes->emplace_back(node); + + if (_nodes->size() != 1) + { + return false; + } + + // Annotate the first node as "U8" + dtype = loco::DataType::U8; + return true; + } + + private: + std::vector *_nodes; + }; + + std::vector nodes; + + SampleTypeInferenceRule rule{&nodes}; + + loco::apply(&rule).to(g.get()); + + ASSERT_EQ(nodes.size(), 2); // Framework SHOULD visit all the nodes + ASSERT_EQ(nodes.at(0), pull_node); // Framework SHOULD visit "pull" before "push" + ASSERT_EQ(nodes.at(1), push_node); + + // Framework SHOULD NOT make any annotation if "rule" returns FALSE + ASSERT_TRUE(loco::dtype_known(pull_node)); + // Framework SHOULD make an annotation if "rule" returns TRUE + ASSERT_EQ(loco::dtype_get(pull_node), loco::DataType::U8); + ASSERT_FALSE(loco::dtype_known(push_node)); +} + +TEST(CanonicalTypeInferenceRuleTest, minimal) +{ + // Create a simple network + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + + pull_node->dtype(loco::DataType::U8); + + auto push_node = g->nodes()->create(); + + push_node->from(pull_node); + + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + loco::link(graph_output, push_node); + + // Run Type Inference + loco::CanonicalTypeInferenceRule rule; + + loco::apply(&rule).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::dtype_known(push_node)); + ASSERT_EQ(loco::dtype_get(push_node), loco::DataType::U8); +} + +TEST(CanonicalTypeInferenceRuleTest, relu6) +{ + // Create a simple Relu6 network + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + + pull_node->dtype(loco::DataType::FLOAT32); + + auto relu6_node = g->nodes()->create(); + + relu6_node->input(pull_node); + + auto push_node = g->nodes()->create(); + + push_node->from(relu6_node); + + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + loco::link(graph_output, push_node); + + // Run Type Inference + loco::CanonicalTypeInferenceRule rule; + + loco::apply(&rule).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::dtype_known(relu6_node)); + ASSERT_EQ(loco::dtype_get(relu6_node), loco::DataType::FLOAT32); +} + +TEST(CanonicalTypeInferenceRuleTest, tensor_broadcast) +{ + // Create a sample network + GraphTestcase testcase{1, 2}; + + testcase.graph()->inputs()->at(0)->dtype(loco::DataType::U8); + + // Run Type Inference + loco::CanonicalTypeInferenceRule rule; + + loco::apply(&rule).to(testcase.graph()); + + // Verify! + ASSERT_TRUE(loco::dtype_known(testcase.push_node)); + ASSERT_EQ(loco::dtype_get(testcase.push_node), loco::DataType::U8); +} + +// mockup for MultiDialectTypeInferenceRule +// OpNode of a specific loco datatype (defined in template) will be used. +// And a Dialect for the OpNode and its inference rules are created. +#include + +namespace +{ + +template class TestDialect final : public loco::Dialect +{ +public: + static Dialect *get(void) + { + static TestDialect d; + return &d; + } +}; + +template +struct TestOpNode final : public loco::FixedArity<1>::Mixin, + public loco::NodeMixin +{ + void input(Node *node) { at(0)->node(node); } + const loco::Dialect *dialect(void) const final { return TestDialect::get(); } + uint32_t opnum(void) const final { return static_cast(N); } +}; + +template struct TestTypeInferenceRule final : public loco::TypeInferenceRule +{ +public: + bool recognize(const loco::Dialect *d) const final { return (d == TestDialect::get()); } + + bool infer(const loco::Node *node, loco::DataType &dtype) const final + { + assert(node->dialect() == TestDialect::get()); + auto test_node = dynamic_cast *>(node); + assert(test_node != nullptr); + + dtype = N; + return true; + } +}; + +} // namespace + +TEST(MultiDialectTypeInferenceRuleTest, test1) +{ + // Create a simple network : Pull - S8 - U8 - Push + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + pull_node->dtype(loco::DataType::FLOAT32); + + auto s8_node = g->nodes()->create>(); + s8_node->input(pull_node); + + auto u8_node = g->nodes()->create>(); + u8_node->input(s8_node); + + auto push_node = g->nodes()->create(); + push_node->from(u8_node); + + auto graph_input = g->inputs()->create(); + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + graph_output->name("output"); + loco::link(graph_output, push_node); + + // initially they don't have type info + ASSERT_FALSE(loco::dtype_known(s8_node)); + ASSERT_FALSE(loco::dtype_known(u8_node)); + + // Run Type Inference + TestTypeInferenceRule u8_rule; + TestTypeInferenceRule s8_rule; + loco::CanonicalTypeInferenceRule canon_rule; + + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(TestDialect::get(), &s8_rule) + .bind(TestDialect::get(), &u8_rule) + .bind(loco::CanonicalDialect::get(), &canon_rule); + + loco::apply(&rules).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::dtype_known(s8_node)); + ASSERT_EQ(loco::dtype_get(s8_node), loco::DataType::S8); + + ASSERT_TRUE(loco::dtype_known(u8_node)); + ASSERT_EQ(loco::dtype_get(u8_node), loco::DataType::U8); +} diff --git a/compiler/loco/src/loco.test.cpp b/compiler/loco/src/loco.test.cpp new file mode 100644 index 000000000..4c4f51aa5 --- /dev/null +++ b/compiler/loco/src/loco.test.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loco.h" + +#include + +// This test shows how to create an "identity" network with loco. +// +// What is "identity" network? +// - A network simply passes its input as its output +// +// TODO Create "Ouput" first and then create "Push" later +TEST(LOCO, identity_network) +{ + auto g = loco::make_graph(); + + // Create a "pull" node as an input + auto pull_node = g->nodes()->create(); + + // Set "data type" + pull_node->dtype(loco::DataType::FLOAT32); + + // Set "data shape" + pull_node->rank(2); + pull_node->dim(0) = 3; + pull_node->dim(1) = 4; + + // Create a "push" node as an output + auto push_node = g->nodes()->create(); + + // Set "source" + push_node->from(pull_node); + + // Create Graph Input & Output + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + loco::link(graph_input, pull_node); + graph_input->dtype(loco::DataType::FLOAT32); + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + loco::link(graph_output, push_node); + + // loco::link SHOULD update "index" + ASSERT_EQ(pull_node->index(), 0); + ASSERT_EQ(graph_input->dtype(), loco::DataType::FLOAT32); + + // loco::link SHOULD update "index" + ASSERT_EQ(push_node->index(), 0); +} + +#if 0 +"identity_network_V2" test shows how to use loco when loco.core and loco.canonical are decoupled. + +NOTE "identity_network" test is left for backward compatiblity check +TODO Remove "identity_network" test once all the clients are migrated. +#endif +TEST(LOCO, identity_network_V2) +{ + auto g = loco::make_graph(); + + // Create Graph Input & Output + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + graph_input->dtype(loco::DataType::FLOAT32); + // TODO Set Shape + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + // TODO Set Shape + + // Create a "pull" node as an input + auto pull_node = g->nodes()->create(); + + pull_node->index(0); + + // Create a "push" node as an output + auto push_node = g->nodes()->create(); + + push_node->index(0); + push_node->from(pull_node); + + ASSERT_EQ(pull_node->dtype(), loco::DataType::FLOAT32); + // TODO Check Shape of pull_node + // TODO Check Shape of push_node + + ASSERT_EQ(loco::pull_node(g.get(), 0), pull_node); + ASSERT_EQ(loco::push_node(g.get(), 0), push_node); +} diff --git a/compiler/loco/src/tensorflow.test.cpp b/compiler/loco/src/tensorflow.test.cpp new file mode 100644 index 000000000..f534aee7b --- /dev/null +++ b/compiler/loco/src/tensorflow.test.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief This file includes various tests that shows how to encode TensorFlow models using loco. + * + * @note All the python examples below assume TensorFlow v1.13 + */ +#include "loco.h" + +#include + +#include + +using stdex::make_unique; + +namespace +{ + +loco::Permutation make_NHWC_permutation(void) +{ + loco::Permutation 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; +} + +/** + * @brief Create a HxWxIxO (or HxWxCxN) permutation which tf.nn.conv2d uses + * + * Reference: [tf.nn.conv2d](https://www.tensorflow.org/api_docs/python/tf/nn/conv2d) + * > Given an input tensor of shape [batch, in_height, in_width, in_channels] and a filter / + * > kernel tensor of shape [filter_height, filter_width, in_channels, out_channels], ... + * + * NOTE "HWIO" is borrowed from TensorFlow Lite Converter + * + * https://github.com/tensorflow/tensorflow/blob/v1.13.1/tensorflow/lite/toco/model.h#L169 + */ +loco::Permutation make_HWIO_permutation(void) +{ + loco::Permutation HWIO; + + HWIO.axis(loco::FilterAxis::Height) = 0; // H + HWIO.axis(loco::FilterAxis::Width) = 1; // W + HWIO.axis(loco::FilterAxis::Depth) = 2; // I, a.k.a. C + HWIO.axis(loco::FilterAxis::Count) = 3; // O, a.k.a. N + + return HWIO; +} + +} // nemaspace + +#if 0 +>>> MaxPool_Float_000 testcase + +MaxPool_Float_000 test guarantees that loco is expressive enough to encode the following example. + +Python: +``` +import tensorflow as tf +value = tf.placeholder(dtype=tf.float32, shape=[1, 16, 16, 2], name="value") +maxpool = tf.nn.max_pool(value, [1, 3, 3, 1], [1, 1, 1, 1], 'VALID', name="maxpool") +tf.get_default_graph().as_graph_def() +``` + +The above code produces the following TensorFlow GraphDef: + +node { + name: "value" + op: "Placeholder" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 1 } + dim { size: 16 } + dim { size: 16 } + dim { size: 2 } + } + } + } +} +node { + name: "maxpool" + op: "MaxPool" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NHWC" } + } + attr { + key: "ksize" + value { list { i: 1 i: 3 i: 3 i: 1 } } + } + attr { + key: "padding" + value { s: "VALID" } + } + attr { + key: "strides" + value { list { i: 1 i: 1 i: 1 i: 1 } } + } +} + +Below test guarantees that loco is expressive enough to encode this example. +#endif +TEST(TensorFlowTest, MaxPool_Float_000) +{ + auto g = loco::make_graph(); + + // The first "value" node corresponds to the following "Pull" node. + // + // %value = Pull(dtype: FLOAT32, shape: [1, 16, 16, 2]) + auto value = g->nodes()->create(); + + value->dtype(loco::DataType::FLOAT32); + value->shape({1, 16, 16, 2}); + + // The next "maxpool" node corresponds to a sequence of the following loco nodes: + // - "FeatureEncode" + // - "MaxPool2D + // - "FeatureDecode" + // + // "maxpool.data_format" is 'NHWC' which corresponds to the following permutation + // Count <-> 0 + // Height <-> 1 + // Width <-> 2 + // Depth <-> 3 + loco::Permutation 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; + + auto encoder = make_unique>(); + + encoder->perm(NHWC); + + auto decoder = make_unique>(); + + decoder->perm(NHWC); + + // %node_0 = FeatureEncode(%value, perm { Count = 0, Height = 1, Width = 2, Depth = 3 }) + auto node_0 = g->nodes()->create(); + + node_0->input(value); + node_0->encoder(std::move(encoder)); + + // %node_1 = MaxPool(%node_0, window.H: 3, window.W: 3, stride.H: 1, stride.W : 1) + auto node_1 = g->nodes()->create(); + + node_1->ifm(node_0); + + // From "ksize" attributes + node_1->window()->horizontal(3); + node_1->window()->vertical(3); + + // From "strides" attributes + node_1->stride()->horizontal(1); + node_1->stride()->vertical(1); + + // %output = FeatureDecode(%node_1, perm { Count = 0, Height = 1, Width = 2, Depth = 3 }) + auto output = g->nodes()->create(); + + output->input(node_1); + output->decoder(std::move(decoder)); + + // %push = Push(%output) + auto push = g->nodes()->create(); + + push->from(output); + + // + // Mark network-level input/output + // + auto input_0 = g->inputs()->create(); + loco::link(input_0, value); + + auto output_0 = g->outputs()->create(); + loco::link(output_0, push); + + // NOTE This example SHOULD BE valid. + ASSERT_TRUE(loco::valid(g.get())); +} + +#if 0 +>>> Conv2D_Float_000 testcase + +Conv2D_Float_000 test guarantees that loco is expressive enough to encode the following example. + +Python: +``` +import tensorflow as tf +inp = tf.placeholder(dtype=tf.float32, shape=[1, 16, 16, 2], name="inp") +ker = tf.constant(value=[1.0], dtype=tf.float32, shape=[7, 1, 2, 4], name="ker") +conv2d = tf.nn.conv2d(input=inp, filter=ker, strides=[1, 1, 1, 1], padding='VALID', name="conv2d") +tf.get_default_graph().as_graph_def() +``` + +TensorFlow GraphDef: +``` +node { + name: "inp" + op: "Placeholder" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 1 } + dim { size: 16 } + dim { size: 16 } + dim { size: 2 } + } + } + } +} +node { + name: "ker" + op: "Const" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { size: 7 } + dim { size: 1 } + dim { size: 2 } + dim { size: 4 } + } + float_val: 1.0 + } + } + } +} +node { + name: "conv2d" + op: "Conv2D" + input: "inp" + input: "ker" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NHWC" } + } + attr { + key: "dilations" + value { list { i: 1 i: 1 i: 1 i: 1 } } + } + attr { + key: "padding" + value { s: "VALID" } + } + attr { + key: "strides" + value { list { i: 1 i: 1 i: 1 i: 1 } } + } +} +``` +#endif +TEST(TensorFlowTest, Conv2D_Float_000) +{ + auto g = loco::make_graph(); + + // The first "inp" node corresponds to "Pull" + auto inp = g->nodes()->create(); + { + inp->dtype(loco::DataType::FLOAT32); + inp->shape({1, 16, 16, 2}); + } + + // The seoncd "ker" node corresponds to "ConstGen" + auto ker = g->nodes()->create(); + { + ker->dtype(loco::DataType::FLOAT32); + // 'I' denotes IFM DEPTH, and 'O' denotes OFM DEPTH + ker->shape({7 /*H*/, 1 /*W*/, 2 /*I*/, 3 /*O*/}); + ker->size(7 * 1 * 2 * 3); + for (uint32_t n = 0; n < 7 * 1 * 2 * 3; ++n) + { + // NOTE TensorFlow uses the last value to fill unspecified region + ker->at(n) = 1.0f; + } + } + + // The next "conv2d" node is decomposed into the following loco nodes + // - "FeatureEncode" + // - "FilterEncode" + // - "Conv2D" + // - "FeatureDecode" + auto encoded_ifm = g->nodes()->create(); + { + // From "conv2d.data_format" attribute + auto encoder = make_unique>(); + encoder->perm(make_NHWC_permutation()); + + encoded_ifm->input(inp); + encoded_ifm->encoder(std::move(encoder)); + } + + auto encoded_ker = g->nodes()->create(); + { + // From "tf.nn.conv2d" specification + auto encoder = make_unique>(); + encoder->perm(make_HWIO_permutation()); + + encoded_ker->input(ker); + encoded_ker->encoder(std::move(encoder)); + } + + auto conv2d = g->nodes()->create(); + { + conv2d->ifm(encoded_ifm); + conv2d->ker(encoded_ker); + + // From "stride" attribute + conv2d->stride()->horizontal(1); + conv2d->stride()->vertical(1); + } + + // "decoded_ofm" corresponds to the output of "conv2d" node. + auto decoded_ofm = g->nodes()->create(); + { + // From "conv2d.data_format" attribute + auto decoder = make_unique>(); + decoder->perm(make_NHWC_permutation()); + + decoded_ofm->input(conv2d); + decoded_ofm->decoder(std::move(decoder)); + } + + // Makr "conv2d" as a network-level output with Push + auto push = g->nodes()->create(); + { + push->from(decoded_ofm); + } + + // + // Mark network-level input/output + // + auto input_0 = g->inputs()->create(); + loco::link(input_0, inp); + + auto output_0 = g->outputs()->create(); + loco::link(output_0, push); + + // NOTE This example SHOULD BE valid. + ASSERT_TRUE(loco::valid(g.get())); +} diff --git a/compiler/locoex-customop/CMakeLists.txt b/compiler/locoex-customop/CMakeLists.txt new file mode 100644 index 000000000..df1e01526 --- /dev/null +++ b/compiler/locoex-customop/CMakeLists.txt @@ -0,0 +1,18 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(locoex_customop SHARED ${SOURCES}) +target_include_directories(locoex_customop PUBLIC include) +target_link_libraries(locoex_customop PUBLIC loco) +target_link_libraries(locoex_customop PRIVATE stdex locop pepper_str) +install(TARGETS locoex_customop DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(locoex_customop_test ${TESTS}) +target_link_libraries(locoex_customop_test loco locoex_customop stdex) diff --git a/compiler/locoex-customop/README.md b/compiler/locoex-customop/README.md new file mode 100644 index 000000000..3f71140f9 --- /dev/null +++ b/compiler/locoex-customop/README.md @@ -0,0 +1,9 @@ +# locoex + +_locoex_ is an extention of loco. Classes with `COp` prefix enables *Custom Operation*. +In this version, a *custom operation* means one of the following: + +1. an op that is supported by Tensorflow but not supported both by the moco and the onert +1. an op that is not supported by Tensorflow, moco, and the onert + +`COpCall` node will represent IR entity that calls custom operations and kernels. diff --git a/compiler/locoex-customop/include/locoex/COpAttrTypes.h b/compiler/locoex-customop/include/locoex/COpAttrTypes.h new file mode 100644 index 000000000..9fbd125d9 --- /dev/null +++ b/compiler/locoex-customop/include/locoex/COpAttrTypes.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_COPATTRTYPES_H__ +#define __LOCOEX_COPATTRTYPES_H__ + +#include + +namespace locoex +{ + +/** + * @brief Tensorflow attribute type + * Refer to https://www.tensorflow.org/guide/extend/op#attr_types + */ +enum class COpAttrType +{ + Int, + Float, + // TODO Support more attr types such as String, Bool, DataType, Tensor, Shape, List +}; + +/** + * @brief Struct that holds attr type + */ +struct COpAttrData +{ +protected: + COpAttrData(COpAttrType attr_type) : _type(attr_type) {} + +public: + virtual ~COpAttrData() = default; + +public: + COpAttrType type() const { return _type; } + void type(COpAttrType attr_type) { _type = attr_type; } + +private: + COpAttrType _type; +}; + +/** + * @brief Struct that holds attr data of int type + */ +struct COpAttrInt final : public COpAttrData +{ +public: + COpAttrInt(int tf_val) : COpAttrData(COpAttrType::Int) { _val = tf_val; } + + int val() const { return _val; } + void val(int val) { _val = val; } + +private: + int _val; +}; + +/** + * @brief Struct that holds attr data of float type + */ +struct COpAttrFloat final : public COpAttrData +{ +public: + COpAttrFloat(float tf_val) : COpAttrData(COpAttrType::Float) { _val = tf_val; } + + float val() const { return _val; } + void val(float val) { _val = val; } + +private: + float _val; +}; + +template struct AttrTypeTrait; + +template <> struct AttrTypeTrait +{ + using Type = COpAttrFloat; +}; + +template <> struct AttrTypeTrait +{ + using Type = COpAttrInt; +}; + +// TODO support more attr types + +} // namespace locoex + +#endif // __LOCOEX_COPATTRTYPES_H__ diff --git a/compiler/locoex-customop/include/locoex/COpCall.h b/compiler/locoex-customop/include/locoex/COpCall.h new file mode 100644 index 000000000..197fd8d0c --- /dev/null +++ b/compiler/locoex-customop/include/locoex/COpCall.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_COPCALL_H__ +#define __LOCOEX_COPCALL_H__ + +#include "VariadicArityNode.h" +#include "locoex/COpAttrTypes.h" +#include "locoex/COpNode.h" + +#include + +#include +#include + +namespace locoex +{ + +/** + * @brief Class to calls custom operation + */ +class COpCall final : public VariadicArityNode, + public loco::NodeMixin, + public loco::NodeMixin +{ +public: + COpCall(unsigned arity) : VariadicArityNode(arity) {} + +public: + void op(const std::string &op) { _op.assign(op); } + const std::string &op() { return _op; } + + void name(const std::string &name) { _name.assign(name); } + const std::string &name() { return _name; } + + void input(uint32_t nth, loco::Node *node) { at(nth)->node(node); } + loco::Node *input(uint32_t nth) const { return at(nth)->node(); } + + /// @brief Store [attr_name, attr_data] + void attr(const std::string &attr_name, std::unique_ptr &&attr_data); + + /// @brief Retrieve attr_data stored with attr_name + template + const typename AttrTypeTrait::Type *attr(const std::string &attr_name) const; + + /// @brief get all the names of attr + std::vector attr_names() const; + +private: + std::string _op; + std::string _name; + + std::map> _attrs; +}; + +} // namespace locoex + +#endif // __LOCOEX_COPCALL_H__ diff --git a/compiler/locoex-customop/include/locoex/COpDialect.h b/compiler/locoex-customop/include/locoex/COpDialect.h new file mode 100644 index 000000000..86ca5a7a1 --- /dev/null +++ b/compiler/locoex-customop/include/locoex/COpDialect.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_COPDIALECT_H__ +#define __LOCOEX_COPDIALECT_H__ + +#include + +namespace locoex +{ + +/** + * @brief A singleton for locoex custom op Dialect + */ +class COpDialect final : public loco::Dialect +{ +private: + COpDialect() = default; + +public: + COpDialect(const Dialect &) = delete; + COpDialect(Dialect &&) = delete; + +public: + static loco::Dialect *get(void); +}; + +} // namespace locoex + +#endif // __LOCOEX_COPDIALECT_H__ diff --git a/compiler/locoex-customop/include/locoex/COpNode.h b/compiler/locoex-customop/include/locoex/COpNode.h new file mode 100644 index 000000000..fce99d2d9 --- /dev/null +++ b/compiler/locoex-customop/include/locoex/COpNode.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_COPNODE_DECL_H__ +#define __LOCOEX_COPNODE_DECL_H__ + +#include +#include + +namespace locoex +{ + +struct COpNode : public loco::Node +{ + virtual ~COpNode() = default; + + const loco::Dialect *dialect(void) const final; + + uint32_t opnum(void) const final { return 0; /* opnum for custom op */ } +}; + +} // namespace locoex + +#endif // __LOCOEX_COPNODE_DECL_H__ diff --git a/compiler/locoex-customop/include/locoex/Service/COpFormattedGraph.h b/compiler/locoex-customop/include/locoex/Service/COpFormattedGraph.h new file mode 100644 index 000000000..5decf4ecc --- /dev/null +++ b/compiler/locoex-customop/include/locoex/Service/COpFormattedGraph.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_FORMATTED_GRAPH_H__ +#define __LOCOEX_SERVICE_FORMATTED_GRAPH_H__ + +#include + +#include + +namespace locoex +{ + +class COpNodeSummaryBuilder final : public locop::NodeSummaryBuilder +{ +public: + COpNodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *node, locop::NodeSummary &s) const final; + +private: + bool summary(const locoex::COpCall *, locop::NodeSummary &) const; + +private: + const locop::SymbolTable *_tbl; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_FORMATTED_GRAPH_H__ diff --git a/compiler/locoex-customop/include/locoex/Service/COpShapeInferenceRule.h b/compiler/locoex-customop/include/locoex/Service/COpShapeInferenceRule.h new file mode 100644 index 000000000..d2a332da4 --- /dev/null +++ b/compiler/locoex-customop/include/locoex/Service/COpShapeInferenceRule.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_COP_SHAPE_INFERENCE_RULE_H__ +#define __LOCOEX_SERVICE_COP_SHAPE_INFERENCE_RULE_H__ + +#include +#include +#include +#include + +namespace locoex +{ + +/** + * @brief Shape inference rule for COpDialect + * + * @note the shape of inputs and output of CopCall must belong to loco::Domain::Tensor + */ +struct COpShapeInferenceRule final : public loco::ShapeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::NodeShape &) const final; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_COP_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/locoex-customop/include/locoex/Service/COpTypeInference.h b/compiler/locoex-customop/include/locoex/Service/COpTypeInference.h new file mode 100644 index 000000000..13163a5de --- /dev/null +++ b/compiler/locoex-customop/include/locoex/Service/COpTypeInference.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_SERVICE_TYPE_INFERENCE_H__ +#define __LOCOEX_SERVICE_TYPE_INFERENCE_H__ + +#include + +namespace locoex +{ + +/** + * @brief Type Inference Rule for COpDialect + */ +struct COpTypeInferenceRule final : public loco::TypeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::DataType &) const final; +}; + +} // namespace locoex + +#endif // __LOCOEX_SERVICE_TYPE_INFERENCE_H__ diff --git a/compiler/locoex-customop/include/locoex/VariadicArityNode.h b/compiler/locoex-customop/include/locoex/VariadicArityNode.h new file mode 100644 index 000000000..fce754cde --- /dev/null +++ b/compiler/locoex-customop/include/locoex/VariadicArityNode.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOEX_VARIADICARITYNODES_OP_H__ +#define __LOCOEX_VARIADICARITYNODES_OP_H__ + +#include +#include + +#include +#include +#include + +namespace locoex +{ + +/** + * @brief Nodes with the variadic inputs + */ +template class VariadicArityNode : public Base +{ +public: + VariadicArityNode(uint32_t arity) + { + for (uint32_t n = 0; n < arity; ++n) + { + _args.emplace_back(std::move(std::unique_ptr{new 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> _args; +}; + +} // namespace locoex + +#endif // __LOCOEX_VARIADICARITYNODES_OP_H__ diff --git a/compiler/locoex-customop/requires.cmake b/compiler/locoex-customop/requires.cmake new file mode 100644 index 000000000..9127144f2 --- /dev/null +++ b/compiler/locoex-customop/requires.cmake @@ -0,0 +1,4 @@ +require("loco") +require("stdex") +require("locop") +require("pepper-str") diff --git a/compiler/locoex-customop/src/COpCall.cpp b/compiler/locoex-customop/src/COpCall.cpp new file mode 100644 index 000000000..029914758 --- /dev/null +++ b/compiler/locoex-customop/src/COpCall.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/COpCall.h" + +#include "locoex/COpAttrTypes.h" + +namespace locoex +{ + +template +const typename AttrTypeTrait::Type *COpCall::attr(const std::string &attr_name) const +{ + COpAttrData *attr_data; + auto found = _attrs.find(attr_name); + if (found != _attrs.end()) + { + attr_data = found->second.get(); + return dynamic_cast::Type *>(attr_data); + } + else + throw std::runtime_error("Cannot find requested attr"); +} + +void COpCall::attr(const std::string &attr_name, std::unique_ptr &&attr_data) +{ + if (_attrs.find(attr_name) == _attrs.end()) + _attrs[attr_name] = std::move(attr_data); + else + throw std::runtime_error("Attr already inserted"); +} + +std::vector COpCall::attr_names() const +{ + std::vector attr_names; + + for (auto it = _attrs.cbegin(); it != _attrs.cend(); ++it) + { + attr_names.emplace_back(it->first); + } + + return attr_names; +} + +#define INSTANTIATE(AT) \ + template const typename AttrTypeTrait::Type *COpCall::attr(const std::string &attr_name) \ + const; + +INSTANTIATE(COpAttrType::Float) +INSTANTIATE(COpAttrType::Int) + +#undef INSTANTIATE + +} // namespace locoex diff --git a/compiler/locoex-customop/src/COpCall.test.cpp b/compiler/locoex-customop/src/COpCall.test.cpp new file mode 100644 index 000000000..d5f01d22d --- /dev/null +++ b/compiler/locoex-customop/src/COpCall.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/COpCall.h" +#include "locoex/COpAttrTypes.h" + +#include +#include + +#include + +#include + +TEST(CallTest, Test_01) +{ + using namespace locoex; + + // attr name + std::string int_attr = "my_int"; + std::string float_attr = "my_float"; + + int int_val = 100; + float float_val = 3.14; + + // building loco test graph + auto g = loco::make_graph(); + + // generating input + auto inp = g->nodes()->create(); + { + inp->dtype(loco::DataType::FLOAT32); + inp->shape({1, 2}); + } + + // generating custom op + auto custom = g->nodes()->create(2U); + { + custom->input(0, inp); + custom->input(1, inp); + + custom->attr(int_attr, stdex::make_unique(int_val)); + custom->attr(float_attr, stdex::make_unique(float_val)); + } + + // access custom op input + loco::Node *input0 = custom->input(0); + loco::Node *input1 = custom->input(1); + + ASSERT_EQ(custom->arity(), 2); + ASSERT_EQ(dynamic_cast(input0), inp); + ASSERT_EQ(dynamic_cast(input1), inp); + + // access custom op attrs + auto names = custom->attr_names(); + + bool int_cheched = false, float_cheched = false; + + for (const auto &name : names) + { + if (auto int_attr = custom->attr(name)) + { + ASSERT_EQ(int_attr->val(), int_val); + int_cheched = true; + } + else if (auto float_attr = custom->attr(name)) + { + ASSERT_FLOAT_EQ(float_attr->val(), float_val); + float_cheched = true; + } + else + { + FAIL(); + } + } + + ASSERT_TRUE(int_cheched && float_cheched); +} diff --git a/compiler/locoex-customop/src/COpDialect.cpp b/compiler/locoex-customop/src/COpDialect.cpp new file mode 100644 index 000000000..46b7f8dd8 --- /dev/null +++ b/compiler/locoex-customop/src/COpDialect.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/COpDialect.h" + +namespace locoex +{ + +loco::Dialect *COpDialect::get(void) +{ + static COpDialect d; + return &d; +} + +} // namespace locoex diff --git a/compiler/locoex-customop/src/COpDialect.test.cpp b/compiler/locoex-customop/src/COpDialect.test.cpp new file mode 100644 index 000000000..b00bf21a9 --- /dev/null +++ b/compiler/locoex-customop/src/COpDialect.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/COpDialect.h" + +#include + +TEST(COpDialectTest, get) +{ + auto d = locoex::COpDialect::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, locoex::COpDialect::get()); +} diff --git a/compiler/locoex-customop/src/COpNode.cpp b/compiler/locoex-customop/src/COpNode.cpp new file mode 100644 index 000000000..c489eedbc --- /dev/null +++ b/compiler/locoex-customop/src/COpNode.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/COpNode.h" +#include "locoex/COpDialect.h" + +namespace locoex +{ + +const loco::Dialect *COpNode::dialect(void) const { return COpDialect::get(); } + +} // namespace locoex diff --git a/compiler/locoex-customop/src/Service/COpFormattedGraph.cpp b/compiler/locoex-customop/src/Service/COpFormattedGraph.cpp new file mode 100644 index 000000000..916663ec0 --- /dev/null +++ b/compiler/locoex-customop/src/Service/COpFormattedGraph.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/Service/COpFormattedGraph.h" + +#include +#include +#include + +#include + +#include +#include + +namespace locoex +{ + +bool COpNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (node->dialect() != locoex::COpDialect::get()) + return false; + + if (auto call_node = dynamic_cast(node)) + { + return summary(call_node, s); + } + + return false; +} + +bool COpNodeSummaryBuilder::summary(const locoex::COpCall *node, locop::NodeSummary &s) const +{ + assert(node != nullptr); + + s.opname("COp.Call"); + for (uint32_t i = 0; i < node->arity(); i++) + s.args().append(pepper::str("input_", i), _tbl->lookup(node->arg(i))); + + for (auto name : node->attr_names()) + { + if (auto int_attr = node->attr(name)) + s.args().append(name, pepper::str(int_attr->val())); + else if (auto float_attr = node->attr(name)) + s.args().append(name, pepper::str(float_attr->val())); + else + throw std::runtime_error("Not yet supported Attr Type"); + } + + s.state(locop::NodeSummary::State::Complete); + return true; +} + +} // namespace locoex diff --git a/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp new file mode 100644 index 000000000..4dc8f461f --- /dev/null +++ b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/Service/COpShapeInferenceRule.h" + +#include "locoex/COpDialect.h" +#include "locoex/COpNode.h" +#include "locoex/COpCall.h" + +#include + +#include + +namespace locoex +{ + +bool COpShapeInferenceRule::recognize(const loco::Dialect *d) const +{ + return COpDialect::get() == d; +} + +bool COpShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const +{ + assert(node->dialect() == COpDialect::get()); + assert(dynamic_cast(node) != nullptr); + + auto cop_call = dynamic_cast(node); + + // Note that the shape of custom op is considered as TensorShape + // TODO Decide how to deal with this shape error cases + for (uint32_t n = 0; n < cop_call->arity(); n++) + if (loco::shape_get(cop_call->input(n)).domain() != loco::Domain::Tensor) + throw std::runtime_error("Input of custom op must belong to Tensor domain."); + + loco::TensorShape out_shape; + + out_shape.rank(cop_call->rank()); + for (uint32_t d = 0; d < cop_call->rank(); d++) + out_shape.dim(d) = cop_call->dim(d); + + shape.set(out_shape); + + return true; +} + +} // namespace locoex diff --git a/compiler/locoex-customop/src/Service/COpShapeInferenceRule.test.cpp b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.test.cpp new file mode 100644 index 000000000..c86931ba7 --- /dev/null +++ b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/Service/COpShapeInferenceRule.h" +#include "locoex/COpCall.h" +#include + +#include + +TEST(COpShapeInferenceRuleTest, minimal) +{ + // Create a simple network + auto g = loco::make_graph(); + + auto call_node = g->nodes()->create(0); + call_node->shape({1, 3}); + + auto push_node = g->nodes()->create(); + push_node->from(call_node); + + auto graph_output = g->outputs()->create(); + graph_output->name("output"); + loco::link(graph_output, push_node); + + // pre-check + ASSERT_FALSE(loco::shape_known(call_node)); + + // Run Shape Inference + locoex::COpShapeInferenceRule rule; + + loco::apply(&rule).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::shape_known(call_node)); + ASSERT_EQ(loco::shape_get(call_node).domain(), loco::Domain::Tensor); + + auto shape = loco::shape_get(call_node).as(); + ASSERT_EQ(shape.rank(), 2); + ASSERT_EQ(shape.dim(0), 1); + ASSERT_EQ(shape.dim(1), 3); +} diff --git a/compiler/locoex-customop/src/Service/COpTypeInference.cpp b/compiler/locoex-customop/src/Service/COpTypeInference.cpp new file mode 100644 index 000000000..b41454eb2 --- /dev/null +++ b/compiler/locoex-customop/src/Service/COpTypeInference.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/Service/COpTypeInference.h" + +#include "locoex/COpDialect.h" +#include "locoex/COpCall.h" + +#include + +namespace locoex +{ + +bool COpTypeInferenceRule::recognize(const loco::Dialect *d) const +{ + // This rule recognizes only "COpDialect" dialect! + return COpDialect::get() == d; +} + +bool COpTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const +{ + assert(node->dialect() == COpDialect::get()); + + auto customop = dynamic_cast(node); + + assert(customop != nullptr); + assert(customop->dtype() != loco::DataType::Unknown); + + dtype = customop->dtype(); + + return true; +} + +} // namespace locoex diff --git a/compiler/locoex-customop/src/Service/COpTypeInference.test.cpp b/compiler/locoex-customop/src/Service/COpTypeInference.test.cpp new file mode 100644 index 000000000..97ddd8618 --- /dev/null +++ b/compiler/locoex-customop/src/Service/COpTypeInference.test.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include + +TEST(TypeInferenceRuleTest, COpTypeInference) +{ + // Create a simple Relu6 network + auto g = loco::make_graph(); + + auto pull_node = g->nodes()->create(); + pull_node->dtype(loco::DataType::FLOAT32); + + auto call_node = g->nodes()->create(1); + call_node->input(0, pull_node); + call_node->dtype(loco::DataType::FLOAT32); + + auto push_node = g->nodes()->create(); + push_node->from(call_node); + + auto graph_input = g->inputs()->create(); + + graph_input->name("input"); + loco::link(graph_input, pull_node); + + auto graph_output = g->outputs()->create(); + + graph_output->name("output"); + loco::link(graph_output, push_node); + + // Run Type Inference + locoex::COpTypeInferenceRule cop_rule; + loco::CanonicalTypeInferenceRule canon_rule; + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(locoex::COpDialect::get(), &cop_rule).bind(loco::CanonicalDialect::get(), &canon_rule); + + loco::apply(&rules).to(g.get()); + + // Verify! + ASSERT_TRUE(loco::dtype_known(call_node)); + ASSERT_EQ(loco::dtype_get(call_node), loco::DataType::FLOAT32); +} diff --git a/compiler/locoex-customop/src/VariadicArityNode.test.cpp b/compiler/locoex-customop/src/VariadicArityNode.test.cpp new file mode 100644 index 000000000..a618824e5 --- /dev/null +++ b/compiler/locoex-customop/src/VariadicArityNode.test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locoex/VariadicArityNode.h" + +#include + +#include + +namespace +{ +using namespace locoex; + +class TestNode : public VariadicArityNode +{ +public: + TestNode(uint32_t arity) : VariadicArityNode(arity) {} + + void input(uint32_t idx, loco::Node *node) { at(idx)->node(node); } + loco::Node *input(uint32_t idx) const { return at(idx)->node(); } + + const loco::Dialect *dialect(void) const { return nullptr; } // this won't be called for testing + uint32_t opnum(void) const { return -1; } // this won't be called for testing +}; + +class ZeroInputNode : public TestNode +{ +public: + ZeroInputNode() : TestNode(0) {} +}; + +class BinaryInputNode : public TestNode +{ +public: + BinaryInputNode() : TestNode(2) {} +}; +} + +TEST(CustomOpTest, VariadicArityNode_arity_0) +{ + loco::Pull pull; + + ZeroInputNode z_node; + + ASSERT_EQ(z_node.arity(), 0); +} + +TEST(CustomOpTest, VariadicArityNode_arity_2) +{ + loco::Pull pull_00, pull_01; + + BinaryInputNode b_node; + b_node.input(0, &pull_00); + b_node.input(1, &pull_01); + + ASSERT_EQ(b_node.arity(), 2); + ASSERT_EQ(b_node.input(0), &pull_00); + ASSERT_EQ(b_node.input(1), &pull_01); +} diff --git a/compiler/locomotiv/CMakeLists.txt b/compiler/locomotiv/CMakeLists.txt new file mode 100644 index 000000000..5c0156b78 --- /dev/null +++ b/compiler/locomotiv/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(locomotiv STATIC ${SOURCES}) +set_target_properties(locomotiv PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(locomotiv PUBLIC include) +target_include_directories(locomotiv PRIVATE src) +target_link_libraries(locomotiv PUBLIC loco) +target_link_libraries(locomotiv PUBLIC angkor) +target_link_libraries(locomotiv PRIVATE stdex) +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(locomotiv PRIVATE nncc_common) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(locomotiv_test ${TESTS}) +target_include_directories(locomotiv_test PRIVATE src) +target_link_libraries(locomotiv_test locomotiv) + +add_test(locomotiv_test locomotiv_test) diff --git a/compiler/locomotiv/README.md b/compiler/locomotiv/README.md new file mode 100644 index 000000000..9569f6ea3 --- /dev/null +++ b/compiler/locomotiv/README.md @@ -0,0 +1,90 @@ +# locomotiv +_locomotiv_ is a reference interpreter for _loco_ IR. + +# Purpose +- _locomotiv_ would serve as code level specification and reference implementation for loco IR. +- _locomotiv_ is required for loco-related tools to be tested. + +# Sample code to use locomotiv library +This sample code shows how to use locomotiv. Please refer to `src/Session.test.cpp` as well for actual usage. +```cpp +template using Buffer = nncc::core::ADT::tensor::Buffer + +loco::Graph *graph; +// ... building graph ... + +// Open interpreter session +locomotiv::Session sess(graph); + +for (uint32_t i = 0; i < s.input_size(); ++i) +{ + Buffer buffer; + // ... building buffer ... + + locomotiv::NodeData input_data = locomotiv::make_data(buffer); + + sess.set_input(i, input_data); +} + +// Run inference +sess.infer(); + +// Query inferred output +locomotiv::NodeData *output_data = sess.get_output(query_index); + +// Get buffer according to data type +switch(output_data->dtype()) +{ +case loco::DataType::S32: +{ + Buffer output_buffer = output_data->as_s32_bufptr(); + // Do something + break; +} +case loco::DataType::FLOAT32: +{ + Buffer output_buffer = output_data->as_f32_bufptr(); + // Do something + break; +} +// ... +} +``` + +# How to support new loco node execution: recommended guide + +## Steps to support new loco node +1. First of all, understand semantics of the node to newly support, especially on calculation spec and valid use cases. +2. Add the node to `locomotiv/src/Node.lst`. Please keep alphabetical order. This automatically declares `NodeExecution::execute(TheNode *)` and updates `NodeExecution::run()` to deal with the node. +3. Define `execute(loco::TheNode *)` at `locomotiv/src/Node/TheNode.cpp`. +4. Test new node execution at `locomotiv/src/Node/TheNode.test.cpp` if possible. + +### Note on internal data layout rule +For each domain(see `loco::Domain`), `locomotiv` has fixed layout rule on how to store its data in memory. +- Feature is represented as NHWC layout + - That is number of batch(N), height(H), width(W) and channel depth(C) +- Filter is represented as NHWC layout + - That is number of filter(N), height(H), width(W) and input channel depth(C) +- DepthwiseFilter is represented as HWCM layout + - That is height(H), width(W), input channel depth(C) and depth multiplier(M) +- Matrix is represented as HW layout + - That is height(H), width(W) + +### Notes on step 3 +- Mocking Tensorflow lite `reference_op.h` might be a good place to start. +- `execute()` can be called multiple time. It just recalculates and updates annotated data. So it should `erase_annot_data()` before newly `annot_data()`. +- Most node execution behaviour would be implemented for each data type. +- `execute()` should throw runtime error on invalid cases. Some of these cases are explained: + - Invalid argument node + - e.g.) Pull -> MaxPool2D is invalid as MaxPool2D requires feature map as its argument. + - Lack of argument data + - e.g.) Given 'Pull -> Push' graph. On execution of Push, if no NodeData annotated to Pull, it is invalid. + - Mismatch of argument shapes + - e.g.) Addition between 2x2 and 3x3 tensor is invalid + - e.g.) MaxPool2D expects its ifm to be 4D feature, otherwise invalid. + - Mismatch between node's own information and inferred information + - Some node already have attributes like shape or data type. If inferred information is different with existing node's, it is invalid. + +### Recommendation on step 4 (test) +- If the node has no arguments, create a node object and `NodeExecution::run()` on it. Check whether it operates correctly. +- If the node has N(>= 1) arguments, make N pull node inputs, source them to the node to be tested. FeatureEncode or FilterEncode node may be required inbetween depending on the node's argument type. Then annotate N pull nodes with its data, `NodeExecution::run()` on the node to test, and check whether it operates correctly. diff --git a/compiler/locomotiv/include/locomotiv/NodeData.h b/compiler/locomotiv/include/locomotiv/NodeData.h new file mode 100644 index 000000000..c9960db46 --- /dev/null +++ b/compiler/locomotiv/include/locomotiv/NodeData.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_NODEDATA_H_ +#define _LOCOMOTIV_NODEDATA_H_ + +#include +#include +#include + +#include + +namespace locomotiv +{ + +/** + * @brief Read-only no-template wrapper for 'Buffer'. Serves interface for input + * and output of 'Session'. + * + * @note Once NodeData is created, it is not modifiable. + */ +struct NodeData +{ + template using Buffer = nncc::core::ADT::tensor::Buffer; + using Shape = nncc::core::ADT::tensor::Shape; + + virtual ~NodeData() = default; + + virtual const loco::DataType &dtype() const = 0; + + virtual const Shape *shape() const = 0; + + // TODO Support more data types + virtual const Buffer *as_s32_bufptr() const = 0; + virtual const Buffer *as_f32_bufptr() const = 0; +}; + +/** + * @brief Copy buffer to make NodeData + * + * @note NodeData is read-only. You may prepare buffer with ALL data, then call + * this function to make data. + */ +template std::unique_ptr make_data(const NodeData::Buffer
&buffer); + +} // namespace locomotiv + +#endif // _LOCOMOTIV_NODEDATA_H_ diff --git a/compiler/locomotiv/include/locomotiv/Session.h b/compiler/locomotiv/include/locomotiv/Session.h new file mode 100644 index 000000000..3268d60b3 --- /dev/null +++ b/compiler/locomotiv/include/locomotiv/Session.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_SESSION_H_ +#define _LOCOMOTIV_SESSION_H_ + +#include "locomotiv/NodeData.h" + +#include + +#include +#include + +namespace locomotiv +{ + +/** + * @brief Session for loco graph inference + */ +class Session final +{ +public: + Session() = delete; + + /// @brief Make Session for graph with graph outputs themselves + Session(loco::Graph *g) : Session(g, loco::output_nodes(g)) + { + // DO NOTHING + } + + /** + * @brief Make Session for graph with selective custom outputs. Only + * subgraph to calculate given outputs would be executed. + * + * @note Set required inputs for given outputs, or inference may fail. + * @note custom_outputs don't need to be graph output, but can be any nodes + * in the middle of the graph. + * @warn This approach may fail in case of graph with control flow + */ + Session(loco::Graph *g, const std::vector &custom_outputs) + : _graph(g), _outputs(custom_outputs) + { + // DO NOTHING + } + + /// @brief Make Session by range + template + Session(loco::Graph *g, InputIt begin, InputIt end) : _graph(g), _outputs(begin, end) + { + // DO NOTHING + } + + /// @brief Free all node annotations of the graph assigned by this Session + ~Session(); + + /// @brief Get number of graph inputs held by this Session + uint32_t input_size() const { return _graph->inputs()->size(); } + + /** + * @brief Set graph input at specific index by NodeData. + * + * @throw runtime_error In case when another NodeData already annotated for the + * input, and when given data type or shape are not + * congruent with loco node information. + */ + void set_input(uint32_t index, std::unique_ptr &&data); + + /** + * @brief Do inference for this session and graph + * + * @note Multiple run is possible. Abort program when inputs are not fully set + * or invalid calculation found in the middle. + */ + void infer(); + + /// @brief Get number of graph outputs held by this Session + uint32_t output_size() const { return _outputs.size(); } + + /** + * @brief Get output of graph as NodeData + * + * @note May return nullptr, for example, when graph output not yet calculated + */ + const NodeData *get_output(uint32_t index); + + const loco::Node *get_output_node(uint32_t index) { return _outputs.at(index); } + +private: + loco::Graph *_graph; + std::vector _outputs; +}; + +} // namespace locomotiv + +#endif // _LOCOMOTIV_SESSION_H_ diff --git a/compiler/locomotiv/requires.cmake b/compiler/locomotiv/requires.cmake new file mode 100644 index 000000000..1c09aa13d --- /dev/null +++ b/compiler/locomotiv/requires.cmake @@ -0,0 +1,2 @@ +require("angkor") +require("stdex") diff --git a/compiler/locomotiv/src/Node.lst b/compiler/locomotiv/src/Node.lst new file mode 100644 index 000000000..be3b10520 --- /dev/null +++ b/compiler/locomotiv/src/Node.lst @@ -0,0 +1,40 @@ +#ifndef NODE +#error Define NODE first +#endif // NODE + +// NODE(Name) : alphabetic order please + +NODE(AvgPool2D) +NODE(BiasAdd) +NODE(BiasAdd) +NODE(BiasEncode) +NODE(ConstGen) +NODE(Conv2D) +NODE(DepthwiseConv2D) +NODE(DepthwiseFilterEncode) +NODE(EltwiseAdd) +NODE(EltwiseDiv) +NODE(EltwiseMax) +NODE(EltwiseMul) +NODE(EltwiseSqrt) +NODE(EltwiseSub) +NODE(FeatureDecode) +NODE(FeatureEncode) +NODE(FilterEncode) +NODE(Forward) +NODE(MatrixDecode) +NODE(MatrixEncode) +NODE(MatMul) +NODE(MaxPool2D) +NODE(Pull) +NODE(Push) +NODE(ReLU) +NODE(ReLU6) +NODE(Reshape) +NODE(Tanh) +NODE(TensorBroadcast) +NODE(TensorConcat) +NODE(TensorConstantPad) +NODE(TensorReduce) +NODE(TensorSoftmax) +NODE(TransposedConv2D) diff --git a/compiler/locomotiv/src/Node/AvgPool2D.cpp b/compiler/locomotiv/src/Node/AvgPool2D.cpp new file mode 100644 index 000000000..ad603badf --- /dev/null +++ b/compiler/locomotiv/src/Node/AvgPool2D.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +/** + * @brief Compute 1D output size based on given 1D arguments. + * + * @param whole_pad Sum of front and back pad + */ +inline uint32_t compute_out_size(uint32_t image_size, uint32_t whole_pad, uint32_t filter_size, + uint32_t stride) +{ + assert((image_size + whole_pad - filter_size) % stride == 0); + return (image_size + whole_pad - filter_size) / stride + 1; +} + +template +nncc::core::ADT::tensor::Buffer avgPool2D(const loco::AvgPool2D *avgpool2d, + const Buffer *ifm_buf) +{ + assert(avgpool2d->convention() == loco::AvgPool2D::Convention::Valid || + avgpool2d->convention() == loco::AvgPool2D::Convention::Full); + + auto ifm_shape = ifm_buf->shape(); + + const uint32_t batches = ifm_shape.dim(0); + const uint32_t depth = ifm_shape.dim(3); + + const uint32_t ifm_height = ifm_shape.dim(1); + const uint32_t ifm_width = ifm_shape.dim(2); + + const uint32_t window_height = avgpool2d->window()->vertical(); + const uint32_t window_width = avgpool2d->window()->horizontal(); + + const uint32_t stride_height = avgpool2d->stride()->vertical(); + const uint32_t stride_width = avgpool2d->stride()->horizontal(); + + const uint32_t pad_top = avgpool2d->pad()->top(); + const uint32_t pad_bottom = avgpool2d->pad()->bottom(); + + const uint32_t pad_left = avgpool2d->pad()->left(); + const uint32_t pad_right = avgpool2d->pad()->right(); + + const uint32_t output_height = + compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); + const uint32_t output_width = + compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); + + // prepare output buffer + Shape output_shape{batches, output_height, output_width, depth}; + auto output_buf = make_buffer(output_shape); + + for (uint32_t batch = 0; batch < batches; ++batch) + { + for (uint32_t out_y = 0; out_y < output_height; ++out_y) + { + for (uint32_t out_x = 0; out_x < output_width; ++out_x) + { + for (uint32_t channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - pad_left; + const int in_y_origin = (out_y * stride_height) - pad_top; + + uint32_t f_x0, f_x1, f_y0, f_y1; + if (avgpool2d->convention() == loco::AvgPool2D::Convention::Valid) + { + f_x0 = std::max(0, -in_x_origin); + f_x1 = std::min(window_width, ifm_width - in_x_origin); + f_y0 = std::max(0, -in_y_origin); + f_y1 = std::min(window_height, ifm_height - in_y_origin); + } + else + { + throw std::runtime_error("TODO support AvgPool2D::Convention::Full"); + } + const uint32_t filter_x_start = f_x0; + const uint32_t filter_x_end = f_x1; + + const uint32_t filter_y_start = f_y0; + const uint32_t filter_y_end = f_y1; + + T total = 0; + uint32_t filter_ele_count = 0; + + for (uint32_t filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (uint32_t filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const uint32_t in_x = in_x_origin + filter_x; + const uint32_t in_y = in_y_origin + filter_y; + total += ifm_buf->at(Index({batch, in_y, in_x, channel})); + filter_ele_count++; + } + } + + assert(filter_ele_count > 0); + output_buf.at(Index({batch, out_y, out_x, channel})) = total / filter_ele_count; + } + } + } + } + + return output_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::AvgPool2D *avgpool2d) +{ + auto ifm_data = annot_data(avgpool2d->ifm()); + + validate(ifm_data, "Can't find input data of AvgPool2D"); + validate(ifm_data->shape()->rank() == 4, "IFM rank should be 4"); + validate(annot_domain(avgpool2d->ifm()) == loco::Domain::Feature, + "ifm of AvgPool2D is not Feature"); + + std::unique_ptr avgpool2d_data = nullptr; + + switch (ifm_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto ifm_buf = ifm_data->as_f32_bufptr(); + + auto avgpool2d_buf = avgPool2D(avgpool2d, ifm_buf); + + avgpool2d_data = make_data(avgpool2d_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(avgpool2d_data != nullptr); + + annot_data(avgpool2d, std::move(avgpool2d_data)); + annot_domain(avgpool2d, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/AvgPool2D.test.cpp b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp new file mode 100644 index 000000000..89e10a35e --- /dev/null +++ b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shape, + const Shape &ofm_shape, const uint32_t window_v, const uint32_t window_h, + const uint32_t stride_v, const uint32_t stride_h, const uint32_t pad_top, + const uint32_t pad_bottom, const uint32_t pad_left, const uint32_t pad_right) +{ + // Let's make FeatureEncode-AvgPool2D graph + auto g = loco::make_graph(); + auto enc = g->nodes()->create(); + + // Fill output data of FeatureEncode from ifm + auto enc_buf = make_buffer(ifm_shape); + + auto ifm_overlay = make_overlay(ifm_shape, const_cast(ifm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ifm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + enc_buf.at(ind) = ifm_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(enc_buf); + locomotiv::annot_data(enc, std::move(enc_data)); + locomotiv::annot_domain(enc, loco::Domain::Feature); + + // build TF AvgPool2D + auto avgpool2d = g->nodes()->create(); + avgpool2d->ifm(enc); + avgpool2d->convention(loco::AvgPool2D::Convention::Valid); + avgpool2d->window()->vertical(window_v); + avgpool2d->window()->horizontal(window_h); + avgpool2d->stride()->vertical(stride_v); + avgpool2d->stride()->horizontal(stride_h); + avgpool2d->pad()->top(pad_top); + avgpool2d->pad()->bottom(pad_bottom); + avgpool2d->pad()->left(pad_left); + avgpool2d->pad()->right(pad_right); + + // run interpreter + locomotiv::NodeExecution::get().run(avgpool2d); + + // get result of calculation + auto avgpool2d_data = locomotiv::annot_data(avgpool2d); + + // check the result + ASSERT_NE(avgpool2d_data, nullptr); + ASSERT_TRUE(avgpool2d_data->dtype() == loco::DataType::FLOAT32); + ASSERT_TRUE(*(avgpool2d_data->shape()) == ofm_shape); + + auto ofm_overlay = + make_overlay(ofm_shape, const_cast(expected_ofm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ASSERT_FLOAT_EQ(avgpool2d_data->as_f32_bufptr()->at(ind), ofm_overlay.at(ind)); + } + + ASSERT_EQ(locomotiv::annot_domain(avgpool2d), loco::Domain::Feature); +} + +} // namespace + +// clang-format off +/* ifm and ofm are from the code below: +import tensorflow as tf + +value = tf.constant([[[[-0.281157], [-1.0601869], [-0.622261], [-1.1777412]], + [[1.4411974], [0.01408334], [0.06958964], [-0.08663343]], + [[1.3424183], [-0.89015573], [0.2520576], [0.04843695]], + [[-1.6668711], [-0.02187406], [1.9362065], [1.3341236]]]]) +avgpool = tf.nn.avg_pool(value, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding= 'VALID', + data_format="NHWC") +with tf.Session() as sess: + print(sess.run(avgpool)) +*/ +TEST(NodeExecution_AvgPool2D, f32_1x4x4x1_calculation) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + -0.281157, -1.0601869, -0.622261, -1.1777412, + 1.4411974, 0.01408334, 0.06958964, -0.08663343, + 1.3424183, -0.89015573, 0.2520576, 0.04843695, + -1.6668711, -0.02187406, 1.9362065, 1.3341236 + }; + + const float ofm[] = + { + 0.02848421, -0.45426148, + -0.30912063, 0.89270616 + }; + + run_test(ifm, ofm, + Shape{1, 4, 4, 1}, Shape{1, 2, 2, 1}, // input shape , output shape + 2, 2, // kernel + 2, 2, // stride + 0, 0, 0, 0 // padding + ); +} +// clang-format on + +// clang-format off +/* ifm and ofm are from the code below: +import tensorflow as tf + +value = tf.constant([[[[-0.281157], [-1.0601869], [-0.622261]], + [[1.4411974], [0.01408334], [0.06958964]], + [[1.3424183], [-0.89015573], [0.2520576]]]]) +avgpool = tf.nn.avg_pool(value, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1], padding= 'SAME', + data_format="NHWC") +with tf.Session() as sess: + print(sess.run(avgpool)) +*/ +TEST(NodeExecution_AvgPool2D, f32_1x3x3x1_calculation) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + -0.281157, -1.0601869, -0.622261, + 1.4411974, 0.01408334, 0.06958964, + 1.3424183, -0.89015573, 0.2520576 + }; + + const float ofm[] = + { + 0.02848421, -0.39969373, -0.2763357, + 0.4768858, -0.13860628, 0.16082363, + 0.22613129, -0.31904906, 0.2520576 + }; + + run_test(ifm, ofm, + Shape{1, 3, 3, 1}, Shape{1, 3, 3, 1}, // input shape , output shape + 2, 2, // kernel + 1, 1, // stride + 0, 1, 0, 1 // padding + ); +} +// clang-format on diff --git a/compiler/locomotiv/src/Node/BiasAdd.cpp b/compiler/locomotiv/src/Node/BiasAdd.cpp new file mode 100644 index 000000000..0724fb728 --- /dev/null +++ b/compiler/locomotiv/src/Node/BiasAdd.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include + +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +#include +#include + +namespace +{ +using locomotiv::NodeData; + +std::unique_ptr calc(const NodeData *input_data, const NodeData *bias_data, + uint32_t axis); + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::BiasAdd *bias_add) +{ + auto input_data = locomotiv::annot_data(bias_add->value()); + auto bias_data = locomotiv::annot_data(bias_add->bias()); + + validate(input_data && bias_data, "Input not ready"); + validate(locomotiv::annot_domain(bias_add->value()) == loco::Domain::Tensor && + locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, + "Wrong input domain"); + + std::unique_ptr bias_add_data = calc(input_data, bias_data, bias_add->axis()); + + assert(bias_add_data != nullptr); + annot_data(bias_add, std::move(bias_add_data)); + annot_domain(bias_add, annot_domain(bias_add->value())); +} + +void NodeExecution::execute(loco::BiasAdd *bias_add) +{ + auto input_data = locomotiv::annot_data(bias_add->value()); + auto bias_data = locomotiv::annot_data(bias_add->bias()); + + validate(input_data && bias_data, "Input not ready"); + validate(locomotiv::annot_domain(bias_add->value()) == loco::Domain::Feature && + locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, + "Wrong input domain"); + + std::unique_ptr bias_add_data = calc(input_data, bias_data, 3); + + assert(bias_add_data != nullptr); + annot_data(bias_add, std::move(bias_add_data)); + annot_domain(bias_add, loco::Domain::Feature); +} + +} // namespace locomotiv + +namespace +{ +using locomotiv::NodeData; +using locomotiv::validate; +using locomotiv::make_data; + +std::unique_ptr calc(const NodeData *input_data, const NodeData *bias_data, uint32_t axis) +{ + validate(input_data->shape()->dim(axis) == bias_data->shape()->dim(0), "Bias size mismatch"); + + std::unique_ptr bias_add_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto bias_bufptr = bias_data->as_f32_bufptr(); + auto bias_add_buf = make_buffer(*input_data->shape()); + + auto *shape = input_data->shape(); + + for (IndexEnumerator e{*shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + nncc::core::ADT::tensor::Index bias_index({index.at(axis)}); + bias_add_buf.at(index) = input_bufptr->at(index) + bias_bufptr->at(bias_index); + } + + bias_add_data = make_data(bias_add_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + return bias_add_data; +} + +} // namespace diff --git a/compiler/locomotiv/src/Node/BiasAdd.test.cpp b/compiler/locomotiv/src/Node/BiasAdd.test.cpp new file mode 100644 index 000000000..0ca826673 --- /dev/null +++ b/compiler/locomotiv/src/Node/BiasAdd.test.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + + inp = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) + bias = tf.constant([1.1, 2.1], shape=[2], dtype=tf.float32) + out = tf.nn.bias_add(inp, bias) + + with tf.Session() as sess: + print(sess.run(out)) + */ + +TEST(NodeExecution_TensorBiasAdd, f32) +{ + float in_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float bias_val[] = {1.1, 2.1}; + float out_val[] = {2.1, 4.1, 4.1, 6.1, 6.1, 8.1, 8.1, 10.1, 10.1, + 12.1, 12.1, 14.1, 14.1, 16.1, 16.1, 18.1, 18.1, 20.1}; + + // make BiasAdd(Pull, Const) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp = g->nodes()->create(); + { + inp->dtype(loco::DataType::FLOAT32); + inp->shape({1, 3, 3, 2}); + } + + auto bias = g->nodes()->create(); + { + // nothing to do + } + + auto bias_add = g->nodes()->create>(); + { + bias_add->value(inp); + bias_add->bias(bias); + bias_add->axis(3); // axis(3) means C in NHWC + } + + // Make and assign data to pull node + auto inp_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_buf.shape()}; e.valid(); e.advance()) + { + inp_buf.at(e.current()) = in_val[n++]; + } + } + + auto bias_buf = make_buffer(Shape{2}); + { + int n = 0; + for (IndexEnumerator e{bias_buf.shape()}; e.valid(); e.advance()) + { + bias_buf.at(e.current()) = bias_val[n++]; + } + } + + auto inp_data = locomotiv::make_data(inp_buf); + locomotiv::annot_data(inp, std::move(inp_data)); + locomotiv::annot_domain(inp, loco::Domain::Tensor); + + auto bias_data = locomotiv::make_data(bias_buf); + locomotiv::annot_data(bias, std::move(bias_data)); + locomotiv::annot_domain(bias, loco::Domain::Bias); + + locomotiv::NodeExecution::get().run(bias_add); + + auto bias_add_data = locomotiv::annot_data(bias_add); + + // comparing the result + ASSERT_NE(bias_add_data, nullptr); + ASSERT_EQ(bias_add_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(bias_add_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(bias_add_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(bias_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(bias_add), loco::Domain::Tensor); +} + +/* +test case generated from the following: + + inp = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) + bias = tf.constant([1.1, 2.1], shape=[2], dtype=tf.float32) + out = tf.nn.bias_add(inp, bias) + + with tf.Session() as sess: + print(sess.run(out)) + */ + +TEST(NodeExecution_FeatureBiasAdd, f32) +{ + float in_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float bias_val[] = {1.1, 2.1}; + float out_val[] = {2.1, 4.1, 4.1, 6.1, 6.1, 8.1, 8.1, 10.1, 10.1, + 12.1, 12.1, 14.1, 14.1, 16.1, 16.1, 18.1, 18.1, 20.1}; + + // make FeatureBiasAdd(FeatureEncode, BiasEncode) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto feature_encode = g->nodes()->create(); + { + // setting values is ignored for testing + } + + auto bias = g->nodes()->create(); + { + // nothing to do + } + + auto feature_bias_add = g->nodes()->create>(); + { + feature_bias_add->value(feature_encode); + feature_bias_add->bias(bias); + } + + // Make and assign data to pull node + auto inp_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_buf.shape()}; e.valid(); e.advance()) + { + inp_buf.at(e.current()) = in_val[n++]; + } + } + + auto bias_buf = make_buffer(Shape{2}); + { + int n = 0; + for (IndexEnumerator e{bias_buf.shape()}; e.valid(); e.advance()) + { + bias_buf.at(e.current()) = bias_val[n++]; + } + } + + auto inp_data = locomotiv::make_data(inp_buf); + locomotiv::annot_data(feature_encode, std::move(inp_data)); + locomotiv::annot_domain(feature_encode, loco::Domain::Feature); + + auto bias_data = locomotiv::make_data(bias_buf); + locomotiv::annot_data(bias, std::move(bias_data)); + locomotiv::annot_domain(bias, loco::Domain::Bias); + + locomotiv::NodeExecution::get().run(feature_bias_add); + + auto bias_add_data = locomotiv::annot_data(feature_bias_add); + + // comparing the result + ASSERT_NE(bias_add_data, nullptr); + ASSERT_EQ(bias_add_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(bias_add_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(bias_add_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(bias_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(feature_bias_add), loco::Domain::Feature); +} diff --git a/compiler/locomotiv/src/Node/BiasEncode.cpp b/compiler/locomotiv/src/Node/BiasEncode.cpp new file mode 100644 index 000000000..c2f2b44c0 --- /dev/null +++ b/compiler/locomotiv/src/Node/BiasEncode.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::BiasEncode *bias_enc) +{ + auto input_data = annot_data(bias_enc->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(bias_enc->input()) == loco::Domain::Tensor, + "Input domain should be Tensor"); + validate(input_data->shape()->rank() == 1, "Input data rank must be 1"); + + std::unique_ptr bias_enc_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_bufptr = input_data->as_s32_bufptr(); + bias_enc_data = make_data(*input_bufptr); + break; + } + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + bias_enc_data = make_data(*input_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(bias_enc_data != nullptr); + annot_data(bias_enc, std::move(bias_enc_data)); + annot_domain(bias_enc, loco::Domain::Bias); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/BiasEncode.test.cpp b/compiler/locomotiv/src/Node/BiasEncode.test.cpp new file mode 100644 index 000000000..73e2af8a8 --- /dev/null +++ b/compiler/locomotiv/src/Node/BiasEncode.test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Buffer; + +namespace +{ +template loco::DataType loco_dtype() { throw std::runtime_error("Not supported yet"); } +template <> loco::DataType loco_dtype() { return loco::DataType::S32; } +template <> loco::DataType loco_dtype() { return loco::DataType::FLOAT32; } + +template const Buffer *as_bufptr(const locomotiv::NodeData *data) +{ + throw std::runtime_error("Not supported yet"); +} +template <> const Buffer *as_bufptr(const locomotiv::NodeData *data) +{ + return data->as_s32_bufptr(); +} +template <> const Buffer *as_bufptr(const locomotiv::NodeData *data) +{ + return data->as_f32_bufptr(); +} + +template void test() +{ + // Make pull-BiasEncode graph + auto g = loco::make_graph(); + + auto pull = g->nodes()->create(); + { + pull->dtype(loco_dtype()); + pull->shape({1}); + } + + auto bias_enc = g->nodes()->create(); + { + bias_enc->input(pull); + } + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + { + pull_buf.at(Index{0}) = static_cast(100); + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + } + + locomotiv::NodeExecution::get().run(bias_enc); + + // check + auto bias_enc_data = locomotiv::annot_data(bias_enc); + + ASSERT_NE(bias_enc_data, nullptr); + ASSERT_EQ(bias_enc_data->dtype(), loco_dtype()); + ASSERT_EQ(*(bias_enc_data->shape()), Shape{1}); + ASSERT_EQ(as_bufptr(bias_enc_data)->at(Index{0}), pull_buf.at(Index{0})); + + ASSERT_EQ(locomotiv::annot_domain(bias_enc), loco::Domain::Bias); +} +} // namespace + +TEST(NodeExecution_BiasEncode, s32) { test(); } + +TEST(NodeExecution_BiasEncode, f32) { test(); } diff --git a/compiler/locomotiv/src/Node/ConstGen.cpp b/compiler/locomotiv/src/Node/ConstGen.cpp new file mode 100644 index 000000000..0360b9fef --- /dev/null +++ b/compiler/locomotiv/src/Node/ConstGen.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include + +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +namespace +{ + +/** + * @brief Get offset based on given shape and index. Assume lexical layout. + * + * examples) + * For shape = {3, 4} and index = {1, 2}, + * offset would be 6 ( = 1 * (4) + 2 ) + * For shape = {2, 3, 4} and index = {1, 0, 2}, + * offset would be 14 ( = 1 * (3*4) + 0 *(4) + 2 ) + */ +inline uint32_t offset_by_index(const Shape &shape, const Index &index) +{ + static const nncc::core::ADT::tensor::LexicalLayout l; + return l.offset(shape, index); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::ConstGen *constgen) +{ + uint32_t volume = 1; + + Shape shape; + shape.resize(constgen->rank()); + for (uint32_t i = 0; i < shape.rank(); ++i) + { + shape.dim(i) = constgen->dim(i).value(); + volume *= shape.dim(i); + } + + std::unique_ptr data = nullptr; + + switch (constgen->dtype()) + { + case loco::DataType::S32: + { + assert(volume == constgen->size()); + + auto buf = make_buffer(shape); + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + uint32_t offset = ::offset_by_index(shape, index); + buf.at(index) = constgen->at(offset); + } + + data = locomotiv::make_data(buf); + break; + } + case loco::DataType::FLOAT32: + { + assert(volume == constgen->size()); + + auto buf = make_buffer(shape); + + for (IndexEnumerator e{shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + uint32_t offset = ::offset_by_index(shape, index); + buf.at(index) = constgen->at(offset); + } + + data = locomotiv::make_data(buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(data != nullptr); + annot_data(constgen, std::move(data)); + annot_domain(constgen, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/ConstGen.test.cpp b/compiler/locomotiv/src/Node/ConstGen.test.cpp new file mode 100644 index 000000000..838f4c11d --- /dev/null +++ b/compiler/locomotiv/src/Node/ConstGen.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_ConstGen, s32) +{ + // Make ConstGen node + loco::ConstGen constgen; + + constgen.dtype(loco::DataType::S32); + constgen.shape({2, 3}); + constgen.size(6); + + constgen.at(0) = 0; // Set 0,0 + constgen.at(1) = 1; // Set 0,1 + constgen.at(2) = 2; // Set 0,2 + constgen.at(3) = -3; // Set 1,0 + constgen.at(4) = -4; // Set 1,1 + constgen.at(5) = -5; // Set 1,2 + + // run execution + locomotiv::NodeExecution::get().run(&constgen); + + // test + auto data = locomotiv::annot_data(&constgen); + ASSERT_NE(data, nullptr); + ASSERT_EQ(data->dtype(), loco::DataType::S32); + ASSERT_EQ(*data->shape(), Shape({2, 3})); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 0}), 0); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 1}), 1); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 2}), 2); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 0}), -3); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 1}), -4); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 2}), -5); + + ASSERT_EQ(locomotiv::annot_domain(&constgen), loco::Domain::Tensor); +} + +TEST(NodeExecution_ConstGen, f32) +{ + // Make ConstGen node + loco::ConstGen constgen; + + constgen.dtype(loco::DataType::FLOAT32); + constgen.shape({2, 3}); + constgen.size(6); + + constgen.at(0) = 0.0f; // Set 0,0 + constgen.at(1) = 1.0f; // Set 0,1 + constgen.at(2) = 2.0f; // Set 0,2 + constgen.at(3) = 3.0f; // Set 1,0 + constgen.at(4) = 4.0f; // Set 1,1 + constgen.at(5) = 5.0f; // Set 1,2 + + // run execution + locomotiv::NodeExecution::get().run(&constgen); + + // test + auto data = locomotiv::annot_data(&constgen); + ASSERT_NE(data, nullptr); + ASSERT_EQ(data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*data->shape(), Shape({2, 3})); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 0}), 0.0f); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 1}), 1.0f); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 2}), 2.0f); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 0}), 3.0f); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 1}), 4.0f); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 2}), 5.0f); + + ASSERT_EQ(locomotiv::annot_domain(&constgen), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/Conv2D.cpp b/compiler/locomotiv/src/Node/Conv2D.cpp new file mode 100644 index 000000000..2e4185574 --- /dev/null +++ b/compiler/locomotiv/src/Node/Conv2D.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ +// image size includes padding. +inline uint32_t compute_out_size(uint32_t image_size, uint32_t filter_size, uint32_t stride) +{ + assert((image_size + stride - filter_size) % stride == 0); + return (image_size + stride - filter_size) / stride; +} + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +/** + * @brief Calculates Conv2D + * @note Both input_buf and filter_buf have NHWC format + */ +template +Buffer calc_conv2D(const loco::Conv2D *conv2d, const Buffer *input_buf, + const Buffer *filter_buf) +{ + auto input_shape = input_buf->shape(); + auto filter_shape = filter_buf->shape(); + + locomotiv::validate(input_shape.rank() == 4, "ifm rank must be 4"); + locomotiv::validate(filter_shape.rank() == 4, "filter rank must be 4"); + locomotiv::validate(input_shape.dim(3) == filter_shape.dim(3), + "channel value mismatch"); // should have same channel values + + const uint32_t input_height = input_shape.dim(1); + const uint32_t input_width = input_shape.dim(2); + + const uint32_t filter_height = filter_shape.dim(1); + const uint32_t filter_width = filter_shape.dim(2); + + const uint32_t stride_width = conv2d->stride()->horizontal(); + const uint32_t stride_height = conv2d->stride()->vertical(); + + // TODO Enable dilations. Let's set these to 1 for now. + const uint32_t dilation_width_factor = 1; + const uint32_t dilation_height_factor = 1; + + const uint32_t pad_top = conv2d->pad()->top(); + const uint32_t pad_bottom = conv2d->pad()->bottom(); + + const uint32_t pad_left = conv2d->pad()->left(); + const uint32_t pad_right = conv2d->pad()->right(); + + const uint32_t output_height = + compute_out_size(input_height + pad_top + pad_bottom, filter_height, stride_height); + const uint32_t output_width = + compute_out_size(input_width + pad_left + pad_right, filter_width, stride_width); + + const uint32_t batches = input_shape.dim(0); + const uint32_t input_depth = input_shape.dim(3); + const uint32_t output_depth = filter_shape.dim(0); + + Shape output_shape{batches, output_height, output_width, output_depth}; + auto output_buf = make_buffer(output_shape); + + for (uint32_t batch = 0; batch < batches; ++batch) + { + for (uint32_t out_y = 0; out_y < output_height; ++out_y) + { + for (uint32_t out_x = 0; out_x < output_width; ++out_x) + { + for (uint32_t out_channel = 0; out_channel < output_depth; ++out_channel) + { + const int in_x_origin = (out_x * stride_width) - pad_left; + const int in_y_origin = (out_y * stride_height) - pad_top; + + RET_T total = static_cast(0); + + for (uint32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (uint32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + for (uint32_t in_channel = 0; in_channel < input_depth; ++in_channel) + { + const int32_t in_x = in_x_origin + dilation_width_factor * filter_x; + const int32_t in_y = in_y_origin + dilation_height_factor * filter_y; + + // If the location is outside the bounds of the input image, + // use zero as a default value. + if ((in_x >= 0) && ((unsigned)in_x < input_width) && (in_y >= 0) && + ((unsigned)in_y < input_height)) + { + auto input_value = + input_buf->at(Index({batch, (unsigned)in_y, (unsigned)in_x, in_channel})); + auto filter_value = + filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); + total += (input_value * filter_value); + } + } + } + } + output_buf.at(Index({batch, out_y, out_x, out_channel})) = total; + } + } + } + } + return output_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Conv2D *conv2d) +{ + auto ifm_data = annot_data(conv2d->ifm()); + auto ker_data = annot_data(conv2d->ker()); + + validate(ifm_data, "Can't find input data of Conv2D"); + validate(ifm_data->shape()->rank() == 4, "ifm rank must be 4"); + + validate(ker_data, "Can't find kernel data of Conv2D"); + validate(ker_data->shape()->rank() == 4, "Kernel rank must be 4"); + + validate(annot_domain(conv2d->ifm()) == loco::Domain::Feature, "IFM of Conv2D is not feature"); + validate(annot_domain(conv2d->ker()) == loco::Domain::Filter, "Kernel of Conv2D is not filter"); + + std::unique_ptr conv2d_result = nullptr; + + if (ifm_data->dtype() == loco::DataType::FLOAT32 && ker_data->dtype() == loco::DataType::FLOAT32) + { + auto ifm_buf = ifm_data->as_f32_bufptr(); + auto ker_buf = ker_data->as_f32_bufptr(); + + auto conv2d_buf = calc_conv2D(conv2d, ifm_buf, ker_buf); + + conv2d_result = make_data(conv2d_buf); + } + else + throw std::runtime_error("NYI for these DataTypes"); + + assert(conv2d_result != nullptr); + + annot_data(conv2d, std::move(conv2d_result)); + annot_domain(conv2d, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Conv2D.test.cpp b/compiler/locomotiv/src/Node/Conv2D.test.cpp new file mode 100644 index 000000000..83d7fc268 --- /dev/null +++ b/compiler/locomotiv/src/Node/Conv2D.test.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +void run_test(const float *ifm, const float *ker, const float *expected_ofm, const Shape &ifm_shape, + const Shape ker_shape, const Shape ofm_shape, const uint32_t stride_v, + const uint32_t stride_h, const uint32_t pad_top = 0, const uint32_t pad_bottom = 0, + const uint32_t pad_left = 0, const uint32_t pad_right = 0) +{ + auto g = loco::make_graph(); + + // Fill output data of FeatureEncode from ifm + auto ifm_enc = g->nodes()->create(); + { + auto ifm_enc_buf = make_buffer(ifm_shape); + auto ifm_overlay = make_overlay(ifm_shape, const_cast(ifm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ifm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ifm_enc_buf.at(ind) = ifm_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ifm_enc_buf); + locomotiv::annot_data(ifm_enc, std::move(enc_data)); + locomotiv::annot_domain(ifm_enc, loco::Domain::Feature); + } + + // Fill output data of FilterEncode from ker + auto ker_enc = g->nodes()->create(); + { + auto ker_enc_buf = make_buffer(ker_shape); + auto ker_overlay = make_overlay(ker_shape, const_cast(ker)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ker_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ker_enc_buf.at(ind) = ker_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ker_enc_buf); + locomotiv::annot_data(ker_enc, std::move(enc_data)); + locomotiv::annot_domain(ker_enc, loco::Domain::Filter); + } + + // build Conv2D + auto conv2d = g->nodes()->create(); + conv2d->ifm(ifm_enc); + conv2d->ker(ker_enc); + conv2d->stride()->vertical(stride_v); + conv2d->stride()->horizontal(stride_h); + conv2d->pad()->top(pad_top); + conv2d->pad()->bottom(pad_bottom); + conv2d->pad()->left(pad_left); + conv2d->pad()->right(pad_right); + + // run interpreter + locomotiv::NodeExecution::get().run(conv2d); + + // get result of calculation + auto conv2d_result = locomotiv::annot_data(conv2d); + + // check the result + ASSERT_NE(conv2d_result, nullptr); + ASSERT_TRUE(conv2d_result->dtype() == loco::DataType::FLOAT32); + ASSERT_TRUE(*(conv2d_result->shape()) == ofm_shape); + + auto ofm_overlay = + make_overlay(ofm_shape, const_cast(expected_ofm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ASSERT_FLOAT_EQ(conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind)); + } + + ASSERT_EQ(locomotiv::annot_domain(conv2d), loco::Domain::Feature); +} + +} // namespace + +// clang-format off +/* ifm and ofm are from the code below: + +ifm = tf.random_normal([1, 5, 5, 1], stddev=1) +ker = tf.random_normal([3, 3, 1, 1], stddev=1) +out = tf.nn.conv2d(ifm, ker, strides = [1, 2, 2, 1], padding= 'VALID') + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_Conv2D, f32_1x5x5x1_calculation) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + -0.48850584, 1.4292705, -1.3424522, -0.7441476, -1.8964586, + 1.7021934, -0.39246717, 0.6248314, 0.12724274, 1.3915083, + 0.382255, 0.7725081, 0.9171561, -1.1847119, 0.61858755, + 1.1530193, -0.476239, -0.9038663, -0.48764458, 0.339963, + 2.2817912, -0.8464133, -1.0598192, 0.8361126, 1.2344601 + }; + + const float ker[] = + { + -0.0830195, 0.21088193, -0.11781317, + 0.07755677, 1.6337638, 1.0792778, + -1.6922939, -1.5437212, 0.96667504 + }; + + const float ofm[] = + { + -0.28752697, 2.8108592, + -5.220376 , 0.7973861 + }; + + run_test(ifm, ker, ofm, + Shape{1, 5, 5, 1}, Shape{1, 3, 3, 1}, Shape{1, 2, 2, 1}, // shapes of input, ker, output + 2, 2 // stride + ); +} + +TEST(NodeExecution_Conv2D, f32_multiple_channel) +{ + // testing channel != 1, stride = [1,1] + using nncc::core::ADT::tensor::Shape; + + float ifm[1*5*5*3]; + for (int n = 0; n < 5*5*3; n++) ifm[n] = 2.2; + + float ker[2*2*2*3]; // nhwc + for (int n = 0; n < 2*2*2*3; n++) ker[n] = 1.1; + + float ofm[1*4*4*2]; + for (int n = 0; n < 1*4*4*2; n++) ofm[n] = 29.04; + + run_test(ifm, ker, ofm, + Shape{1, 5, 5, 3}, Shape{2, 2, 2, 3}, Shape{1, 4, 4, 2}, // shapes of input, ker, output + 1, 1 // stride + ); +} + +/* ifm and ofm are from the code below: +tensorflow version : 1.12.0 + +import tensorflow as tf + +ifm = tf.constant([-1.3653529, 0.4160791, 0.5059157, 0.7649683, 0.39364856, + -1.0164733, 1.506766, -1.1413091, 1.2766701, -0.9253511, + 1.3570246, 0.32089928, -0.9898171, 1.983792, -0.3423274, + -1.1901658, 1.2288222, -0.47401968, -0.01369802, 0.4136331, + 0.06960588, -0.16537654, -0.65015996, -0.555224, 0.7140603 +], shape=[1, 5, 5, 1]) + +ker = tf.constant([2.3490515, -0.4572366, 0.05790535, + 0.3672005, 0.52679914, 0.74607974, + -1.7211207, 1.1174419, -0.59663385 +], shape=[3, 3, 1, 1]) + +ofm = tf.nn.conv2d(ifm, ker, strides=[1, 1, 1, 1], padding='SAME') + +with tf.Session() as sess: + print(sess.run(ofm)) +*/ +TEST(NodeExecution_Conv2D, with_padding) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + -1.3653529, 0.4160791, 0.5059157, 0.7649683, 0.39364856, + -1.0164733, 1.506766, -1.1413091, 1.2766701, -0.9253511, + 1.3570246, 0.32089928, -0.9898171, 1.983792, -0.3423274, + -1.1901658, 1.2288222, -0.47401968, -0.01369802, 0.4136331, + 0.06960588, -0.16537654, -0.65015996, -0.555224, 0.7140603 + }; + + const float ker[] = + { + 2.3490515, -0.4572366, 0.05790535, + 0.3672005, 0.52679914, 0.74607974, + -1.7211207, 1.1174419, -0.59663385 + }; + + const float ofm[] = + { + -2.443676, 4.2094254, -3.6403496, 4.8254814, -2.743059, + 2.5620093, -5.185688, -1.1470609, 4.54913, -2.1985974, + -0.5567835, 0.49045527, 2.5752437, -2.3383713, 4.455967, + -0.13562866, 2.9236434, 1.4019353, -3.0521483, 6.782954, + 0.5286269, -3.9317036, 2.285041, -1.0817666, -0.04901773 + }; + + run_test(ifm, ker, ofm, + Shape{1, 5, 5, 1}, Shape{1, 3, 3, 1}, Shape{1, 5, 5, 1}, // shapes of input, ker, output + 1, 1, // stride + 1, 1, 1, 1 // padding + ); +} +// clang-format on diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp new file mode 100644 index 000000000..92d5aa161 --- /dev/null +++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ + +/** + * @brief Compute 1D output size based on given 1D arguments. + * + * @param whole_pad Sum of front and back pad + */ +inline uint32_t compute_out_size(uint32_t image_size, uint32_t whole_pad, uint32_t filter_size, + uint32_t stride) +{ + assert((image_size + whole_pad - filter_size) % stride == 0); + return (image_size + whole_pad - filter_size) / stride + 1; +} + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +/** + * @brief Calculates DepthwiseConv2D + * @note ifm_buf has NHWC and ker_buf HWCM format + * (Please check locomotiv README for further information) + */ +template +Buffer calc_dw_conv2d(const loco::DepthwiseConv2D *dw_conv2d, const Buffer *ifm_buf, + const Buffer *ker_buf) +{ + auto ifm_shape = ifm_buf->shape(); + auto ker_shape = ker_buf->shape(); + + locomotiv::validate(ifm_shape.rank() == 4, "ifm rank must be 4"); + locomotiv::validate(ker_shape.rank() == 4, "depthwise filter rank must be 4"); + locomotiv::validate(ifm_shape.dim(3 /* of NHWC */) == ker_shape.dim(2 /* of HWCM */), + "channel value mismatch"); // should have same channel values + + const uint32_t ifm_height = ifm_shape.dim(1); + const uint32_t ifm_width = ifm_shape.dim(2); + + const uint32_t ker_height = ker_shape.dim(0); + const uint32_t ker_width = ker_shape.dim(1); + + const uint32_t stride_width = dw_conv2d->stride()->horizontal(); + const uint32_t stride_height = dw_conv2d->stride()->vertical(); + + // TODO Enable dilations. Let's set these to 1 for now. + const uint32_t dilation_width_factor = 1; + const uint32_t dilation_height_factor = 1; + + const uint32_t pad_top = dw_conv2d->pad()->top(); + const uint32_t pad_bottom = dw_conv2d->pad()->bottom(); + + const uint32_t pad_left = dw_conv2d->pad()->left(); + const uint32_t pad_right = dw_conv2d->pad()->right(); + + const uint32_t ofm_height = + compute_out_size(ifm_height, pad_top + pad_bottom, ker_height, stride_height); + const uint32_t ofm_width = + compute_out_size(ifm_width, pad_left + pad_right, ker_width, stride_width); + + const uint32_t batches = ifm_shape.dim(0); + const uint32_t ifm_depth = ifm_shape.dim(3); + const uint32_t multiplier = ker_shape.dim(3); + const uint32_t ofm_depth = ifm_depth * multiplier; + + Shape ofm_shape{batches, ofm_height, ofm_width, ofm_depth}; + auto ofm_buf = make_buffer(ofm_shape); + + for (uint32_t batch = 0; batch < batches; ++batch) + { + for (uint32_t ofm_y = 0; ofm_y < ofm_height; ++ofm_y) + { + for (uint32_t ofm_x = 0; ofm_x < ofm_width; ++ofm_x) + { + for (uint32_t ch = 0; ch < ifm_depth; ++ch) + { + for (uint32_t nth = 0; nth < multiplier; nth++) + { + const int in_x_origin = (ofm_x * stride_width) - pad_left; + const int in_y_origin = (ofm_y * stride_height) - pad_top; + float total = 0.f; + for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y) + { + for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x) + { + const int in_x = in_x_origin + dilation_width_factor * ker_x; + const int in_y = in_y_origin + dilation_height_factor * ker_y; + // If the location is outside the bounds of the input image, + // use zero as a default value. + if ((in_x >= 0) && ((unsigned)in_x < ifm_width) && (in_y >= 0) && + ((unsigned)in_y < ifm_height)) + { + auto ifm_value = ifm_buf->at(Index({batch, (unsigned)in_y, (unsigned)in_x, ch})); + auto ker_value = ker_buf->at(Index({ker_y, ker_x, ch, nth})); + total += (ifm_value * ker_value); + } + } + } + uint32_t ofm_channel = ch * multiplier + nth; + ofm_buf.at(Index({batch, ofm_y, ofm_x, ofm_channel})) = total; + } + } + } + } + } + return ofm_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::DepthwiseConv2D *dw_conv2d) +{ + auto ifm_data = annot_data(dw_conv2d->ifm()); + auto ker_data = annot_data(dw_conv2d->ker()); + + validate(ifm_data, "Can't find input data of DepthwiseConv2D"); + validate(ifm_data->shape()->rank() == 4, "ifm rank must be 4"); + + validate(ker_data, "Can't find kernel data of DepthwiseConv2D"); + validate(ker_data->shape()->rank() == 4, "Kernel rank must be 4"); + + validate(annot_domain(dw_conv2d->ifm()) == loco::Domain::Feature, + "IFM of DepthwiseConv2D is not feature"); + validate(annot_domain(dw_conv2d->ker()) == loco::Domain::DepthwiseFilter, + "Kernel of DepthwiseConv2D is not depthwise filter"); + + std::unique_ptr dw_conv2d_result = nullptr; + + if (ifm_data->dtype() == loco::DataType::FLOAT32 && ker_data->dtype() == loco::DataType::FLOAT32) + { + auto ifm_buf = ifm_data->as_f32_bufptr(); + auto ker_buf = ker_data->as_f32_bufptr(); + + auto dw_conv2d_buf = calc_dw_conv2d(dw_conv2d, ifm_buf, ker_buf); + + dw_conv2d_result = make_data(dw_conv2d_buf); + } + else + throw std::runtime_error("NYI for these DataTypes"); + + assert(dw_conv2d_result != nullptr); + + annot_data(dw_conv2d, std::move(dw_conv2d_result)); + annot_domain(dw_conv2d, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp new file mode 100644 index 000000000..48824c2e0 --- /dev/null +++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +void run_test(const float *ifm, const float *ker, const float *expected_ofm, const Shape &ifm_shape, + const Shape ker_shape, const Shape ofm_shape, const uint32_t stride_v, + const uint32_t stride_h, const uint32_t pad_top = 0, const uint32_t pad_bottom = 0, + const uint32_t pad_left = 0, const uint32_t pad_right = 0) +{ + auto g = loco::make_graph(); + + // Fill output data of FeatureEncode from ifm + auto ifm_enc = g->nodes()->create(); + { + auto ifm_enc_buf = make_buffer(ifm_shape); + auto ifm_overlay = make_overlay(ifm_shape, const_cast(ifm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ifm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ifm_enc_buf.at(ind) = ifm_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ifm_enc_buf); + locomotiv::annot_data(ifm_enc, std::move(enc_data)); + locomotiv::annot_domain(ifm_enc, loco::Domain::Feature); + } + + // Fill output data of DepthwiseFilterEncode from ker + auto ker_enc = g->nodes()->create(); + { + auto ker_enc_buf = make_buffer(ker_shape); + auto ker_overlay = make_overlay(ker_shape, const_cast(ker)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ker_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ker_enc_buf.at(ind) = ker_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ker_enc_buf); + locomotiv::annot_data(ker_enc, std::move(enc_data)); + locomotiv::annot_domain(ker_enc, loco::Domain::DepthwiseFilter); + } + + // build DepthwiseConv2D + auto dw_conv2d = g->nodes()->create(); + dw_conv2d->ifm(ifm_enc); + dw_conv2d->ker(ker_enc); + dw_conv2d->stride()->vertical(stride_v); + dw_conv2d->stride()->horizontal(stride_h); + dw_conv2d->pad()->top(pad_top); + dw_conv2d->pad()->bottom(pad_bottom); + dw_conv2d->pad()->left(pad_left); + dw_conv2d->pad()->right(pad_right); + + // run interpreter + locomotiv::NodeExecution::get().run(dw_conv2d); + + // get result of calculation + auto dw_conv2d_result = locomotiv::annot_data(dw_conv2d); + + // check the result + ASSERT_NE(dw_conv2d_result, nullptr); + ASSERT_TRUE(dw_conv2d_result->dtype() == loco::DataType::FLOAT32); + ASSERT_TRUE(*(dw_conv2d_result->shape()) == ofm_shape); + + auto ofm_overlay = + make_overlay(ofm_shape, const_cast(expected_ofm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ASSERT_FLOAT_EQ(dw_conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind)); + } + + ASSERT_EQ(locomotiv::annot_domain(dw_conv2d), loco::Domain::Feature); +} + +} // namespace + +// clang-format off + +/* ifm, ker and ofm are from the code below: + +ifm = tf.random_normal([1, 5, 5, 2], stddev=1.1) +ker = tf.random_normal([4, 4, 2, 3], stddev=1.1) +out = tf.nn.depthwise_conv2d(ifm, ker, strides = [1, 1, 1, 1], padding= 'VALID') + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_DepthwiseConv2D, f32_random_valid) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = {0.8122538, 1.209147, 0.6903842, -0.26646265, 1.516799, -1.8540707, + -0.74240327, 1.7811562, -0.03699546, -0.44468504, -1.4982721, -1.1858582, + -0.21140318, -0.974522, 1.0000849, -1.294535, -0.6108882, 0.25827602, + 1.3631831, -0.5180266, 0.20870179, 0.18333802, -0.42263857, -1.6694735, + 0.0415236, -0.3903758, 2.0933757, -0.29660916, 2.1218338, -1.1599928, + 0.57163256, 0.48865932, -1.3622656, 0.35924262, 1.2951899, -0.1769997, + 0.74513537, -0.31920406, -1.2902768, -0.7095059, 1.9157801, -0.41028237, + 1.2502829, 0.3354887, 1.4199319, -0.20366786, -0.8828556, 0.5173567, + 1.7708117, -0.30096334}; + const float ker[] = { + -0.19805557, 0.58464956, -0.7804337, 0.06974592, 0.45790604, 0.24833807, 0.43393376, + 0.2541043, -0.04406675, -0.32167575, 1.0546446, -1.4978354, 0.20829494, 1.1659569, + 0.37908667, -0.94137955, 0.293349, -1.1023049, 0.76133233, 0.55595005, 1.4458209, + 1.6128604, 1.5655615, -2.183877, -0.90535915, -0.49858555, 1.7168728, -1.1590382, + 0.6706056, 1.2215618, -0.06603386, 0.16559464, 0.541991, -0.44488335, 0.766181, + 1.0227629, -0.6352362, -1.670828, -0.63334507, 0.0313305, -0.6721083, 0.50112915, + -0.15218066, 0.67222077, -0.3613627, -0.08516614, -0.5024078, -0.9503976, -2.1892295, + 1.8308185, -0.15187284, 1.5761136, 0.24869336, -1.7378871, -0.22518761, 1.0175673, + 0.7084485, -0.74157554, -1.8185995, -1.3330095, -0.04427439, 1.0556892, -0.68243974, + 0.32001218, 2.0901792, -1.1612813, 0.7294674, 0.05740008, -0.00832882, 1.0446658, + 0.4477195, -0.09174404, -1.0176039, 1.5066665, -2.148343, 0.29421416, 0.93011874, + -0.15737922, -1.6444012, 0.25780794, -0.6545867, -0.3488956, 0.26167992, -0.154414, + 0.2798124, -0.8590068, 2.0494444, 0.48268002, 0.81941164, -0.4848027, 0.76870304, + 0.7102261, 0.45778143, 0.23214905, -0.17742023, -0.75016516}; + const float ofm[] = {4.474646, 0.6792067, -1.9799856, 7.484751, 4.3087378, -1.905938, + 1.4887369, 0.4361322, 0.79539883, -3.8583446, -4.502204, 4.356392, + -5.3030324, 3.493003, -4.349277, 2.3069482, -3.8881323, -0.73901534, + -0.6629516, 2.1247253, -4.9229584, 1.6716996, -3.0208125, 1.0597891}; + + run_test(ifm, ker, ofm, + Shape{1, 5, 5, 2}, Shape{4, 4, 2, 3}, Shape{1, 2, 2, 6}, // shapes of input, ker, output + 1, 1 // stride + ); +} + +// TODO Add same padding test + +// clang-format on diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp new file mode 100644 index 000000000..17004901f --- /dev/null +++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +/** + * @brief Encode input tensor into depthwise filter represented in "HWCM" layout + * + * (Please check locomotiv README for further information) + */ +template +std::unique_ptr dw_filter_encode(const loco::DepthwiseFilterEncode *node, + const Buffer *input_buf) +{ + auto encoder = node->encoder(); + + // Make TensorShape from input + loco::TensorShape input_shape; + input_shape.rank(input_buf->shape().rank()); + assert(input_shape.rank() == 4); + for (uint32_t i = 0; i < input_shape.rank(); ++i) + { + input_shape.dim(i) = input_buf->shape().dim(i); + } + + loco::DepthwiseFilterShape node_shape = encoder->shape(input_shape); + + // Make HWCM (i.e. height, width, depth, multiplier) buffer from DepthwiseFilterShape + Buffer node_buf = make_buffer( + Shape{node_shape.height().value(), node_shape.width().value(), node_shape.depth().value(), + node_shape.multiplier().value()}); + + // Copy buffer in an order arranged by encoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::DepthwiseFilterIndex index; + index.row() = e.current().at(0); + index.column() = e.current().at(1); + index.channel() = e.current().at(2); + index.nth() = e.current().at(3); + + node_buf.at(e.current()) = input_buf->at(encoder->value(index)); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::DepthwiseFilterEncode *enc) +{ + auto input_data = annot_data(enc->input()); + + validate(input_data, "Input of DepthwiseFilterEncode not ready"); + validate(annot_domain(enc->input()) == loco::Domain::Tensor, + "Input of DepthwiseFilterEncode is not Tensor"); + validate(input_data->shape()->rank() == 4, "Input shape mismatch"); + + std::unique_ptr enc_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + enc_data = dw_filter_encode(enc, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(enc_data != nullptr); + annot_data(enc, std::move(enc_data)); + annot_domain(enc, loco::Domain::DepthwiseFilter); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp new file mode 100644 index 000000000..db828c08b --- /dev/null +++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include + +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +TEST(NodeExecution_DepthwiseFilterEncode, f32) +{ + const uint32_t H = 2; + const uint32_t W = 3; + const uint32_t C = 4; + const uint32_t M = 5; + + auto g = loco::make_graph(); + + // Pull + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + + // Make and assign "MHWC" data to pull node + auto pull_buf = make_buffer(Shape{M, H, W, C}); + float f = 1; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = f; + f += 0.1f; // Doesn't matter what it is + } + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + // Encoder to correctly read input tensor as MHWC + auto encoder = std::unique_ptr>( + new loco::PermutingEncoder); + encoder->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 0; + encoder->perm()->axis(loco::DepthwiseFilterAxis::Height) = 1; + encoder->perm()->axis(loco::DepthwiseFilterAxis::Width) = 2; + encoder->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 3; + + // DepthwiseFilterEncode + auto enc = g->nodes()->create(); + enc->input(pull); + enc->encoder(std::move(encoder)); + + locomotiv::NodeExecution::get().run(enc); + + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(enc_data->shape()), (Shape{H, W, C, M})); // locomotiv depthwise filter is HWCM + auto enc_buf = enc_data->as_f32_bufptr(); + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + for (uint32_t m = 0; m < M; ++m) + ASSERT_FLOAT_EQ(pull_buf.at(Index{m, h, w, c}), enc_buf->at(Index{h, w, c, m})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::DepthwiseFilter); +} diff --git a/compiler/locomotiv/src/Node/EltwiseAdd.cpp b/compiler/locomotiv/src/Node/EltwiseAdd.cpp new file mode 100644 index 000000000..e5e2d67c7 --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseAdd.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseAdd *eltwise_add) +{ + struct Func final : public BinaryFunc + { + float apply(float lhs, float rhs) const { return lhs + rhs; } + }; + + Func f; + + eltwise_binary(eltwise_add, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp b/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp new file mode 100644 index 000000000..2899dccdd --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + +x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +y = tf.constant([-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18], + shape=[1, 3, 3, 2], dtype=tf.float32) +out = tf.math.add(x, y) + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_EltwiseAdd, f32) +{ + float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float y_val[] = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18}; + float out_val[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // make EltwiseAdd(Pull, Pull) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp_lhs = g->nodes()->create(); + { + inp_lhs->dtype(loco::DataType::FLOAT32); + inp_lhs->shape({1, 3, 3, 2}); + } + + auto inp_rhs = g->nodes()->create(); + { + inp_rhs->dtype(loco::DataType::FLOAT32); + inp_rhs->shape({1, 3, 3, 2}); + } + + auto eltwise_add = g->nodes()->create(); + { + eltwise_add->lhs(inp_lhs); + eltwise_add->rhs(inp_rhs); + } + + // Make and assign data to two pull nodes + auto inp_lhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance()) + { + inp_lhs_buf.at(e.current()) = x_val[n++]; + } + } + + auto inp_rhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance()) + { + inp_rhs_buf.at(e.current()) = y_val[n++]; + } + } + + auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf); + locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data)); + locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor); + + auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf); + locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data)); + locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor); + + // run the network + locomotiv::NodeExecution::get().run(eltwise_add); + + // get result + auto eltwise_add_data = locomotiv::annot_data(eltwise_add); + + // comparing the result + ASSERT_NE(eltwise_add_data, nullptr); + ASSERT_EQ(eltwise_add_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(eltwise_add_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(eltwise_add_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(eltwise_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(eltwise_add), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/EltwiseDiv.cpp b/compiler/locomotiv/src/Node/EltwiseDiv.cpp new file mode 100644 index 000000000..a054d9a97 --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseDiv.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseDiv *eltwise_div) +{ + struct Func final : public BinaryFunc + { + float apply(float lhs, float rhs) const { return lhs / rhs; } + }; + + Func f; + + eltwise_binary(eltwise_div, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp b/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp new file mode 100644 index 000000000..60950c15b --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + +x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +y = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +out = tf.div(x, y) + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_EltwiseDiv, f32) +{ + float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float y_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float out_val[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + + // make EltwiseDiv(Pull, Pull) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp_lhs = g->nodes()->create(); + { + inp_lhs->dtype(loco::DataType::FLOAT32); + inp_lhs->shape({1, 3, 3, 2}); + } + + auto inp_rhs = g->nodes()->create(); + { + inp_rhs->dtype(loco::DataType::FLOAT32); + inp_rhs->shape({1, 3, 3, 2}); + } + + auto eltwise_div = g->nodes()->create(); + { + eltwise_div->lhs(inp_lhs); + eltwise_div->rhs(inp_rhs); + } + + // Make and assign data to two pull nodes + auto inp_lhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance()) + { + inp_lhs_buf.at(e.current()) = x_val[n++]; + } + } + + auto inp_rhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance()) + { + inp_rhs_buf.at(e.current()) = y_val[n++]; + } + } + + auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf); + locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data)); + locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor); + + auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf); + locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data)); + locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor); + + // run the network + locomotiv::NodeExecution::get().run(eltwise_div); + + // get result + auto eltwise_div_data = locomotiv::annot_data(eltwise_div); + + // comparing the result + ASSERT_NE(eltwise_div_data, nullptr); + ASSERT_EQ(eltwise_div_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(eltwise_div_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(eltwise_div_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(eltwise_div_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(eltwise_div), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/EltwiseMax.cpp b/compiler/locomotiv/src/Node/EltwiseMax.cpp new file mode 100644 index 000000000..ec44fd6fa --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseMax.cpp @@ -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. + */ + +#include "NodeExecution.h" + +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseMax *eltwise_max) +{ + struct Func final : public BinaryFunc + { + float apply(float lhs, float rhs) const { return std::max(lhs, rhs); } + }; + + Func f; + + eltwise_binary(eltwise_max, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseMax.test.cpp b/compiler/locomotiv/src/Node/EltwiseMax.test.cpp new file mode 100644 index 000000000..c64db8994 --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseMax.test.cpp @@ -0,0 +1,121 @@ +/* + * 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 "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + +x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +y = tf.constant([18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + shape=[1, 3, 3, 2], dtype=tf.float32) +out = tf.math.maximum(x, y) + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_EltwiseMax, f32) +{ + float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float y_val[] = {18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + float out_val[] = {18, 17, 16, 15, 14, 13, 12, 11, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + + // make EltwiseMax(Pull, Pull) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp_lhs = g->nodes()->create(); + { + inp_lhs->dtype(loco::DataType::FLOAT32); + inp_lhs->shape({1, 3, 3, 2}); + } + + auto inp_rhs = g->nodes()->create(); + { + inp_rhs->dtype(loco::DataType::FLOAT32); + inp_rhs->shape({1, 3, 3, 2}); + } + + auto eltwise_max = g->nodes()->create(); + { + eltwise_max->lhs(inp_lhs); + eltwise_max->rhs(inp_rhs); + } + + // Make and assign data to two pull nodes + auto inp_lhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance()) + { + inp_lhs_buf.at(e.current()) = x_val[n++]; + } + } + + auto inp_rhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance()) + { + inp_rhs_buf.at(e.current()) = y_val[n++]; + } + } + + auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf); + locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data)); + locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor); + + auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf); + locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data)); + locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor); + + // run the network + locomotiv::NodeExecution::get().run(eltwise_max); + + // get result + auto eltwise_max_data = locomotiv::annot_data(eltwise_max); + + // comparing the result + ASSERT_NE(eltwise_max_data, nullptr); + ASSERT_EQ(eltwise_max_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(eltwise_max_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(eltwise_max_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(eltwise_max_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(eltwise_max), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/EltwiseMul.cpp b/compiler/locomotiv/src/Node/EltwiseMul.cpp new file mode 100644 index 000000000..6720ab92f --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseMul.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseMul *eltwise_mul) +{ + struct Func final : public BinaryFunc + { + float apply(float lhs, float rhs) const { return lhs * rhs; } + }; + + Func f; + + eltwise_binary(eltwise_mul, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseMul.test.cpp b/compiler/locomotiv/src/Node/EltwiseMul.test.cpp new file mode 100644 index 000000000..b76888300 --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseMul.test.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + +x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +y = tf.constant([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], shape=[1, 3, 3, 2], + dtype=tf.float32) +out = tf.math.multiply(x, y) + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_EltwiseMul, f32) +{ + float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float y_val[] = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + float out_val[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, + 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8}; + + // make EltwiseMul(Pull, Pull) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp_lhs = g->nodes()->create(); + { + inp_lhs->dtype(loco::DataType::FLOAT32); + inp_lhs->shape({1, 3, 3, 2}); + } + + auto inp_rhs = g->nodes()->create(); + { + inp_rhs->dtype(loco::DataType::FLOAT32); + inp_rhs->shape({1, 3, 3, 2}); + } + + auto eltwise_mul = g->nodes()->create(); + { + eltwise_mul->lhs(inp_lhs); + eltwise_mul->rhs(inp_rhs); + } + + // Make and assign data to two pull nodes + auto inp_lhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance()) + { + inp_lhs_buf.at(e.current()) = x_val[n++]; + } + } + + auto inp_rhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance()) + { + inp_rhs_buf.at(e.current()) = y_val[n++]; + } + } + + auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf); + locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data)); + locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor); + + auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf); + locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data)); + locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor); + + // run the network + locomotiv::NodeExecution::get().run(eltwise_mul); + + // get result + auto eltwise_mul_data = locomotiv::annot_data(eltwise_mul); + + // comparing the result + ASSERT_NE(eltwise_mul_data, nullptr); + ASSERT_EQ(eltwise_mul_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(eltwise_mul_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(eltwise_mul_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(eltwise_mul_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(eltwise_mul), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/EltwiseSqrt.cpp b/compiler/locomotiv/src/Node/EltwiseSqrt.cpp new file mode 100644 index 000000000..b4625a757 --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseSqrt.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include + +namespace +{ + +inline float sqrt_ew(float val) { return sqrt(val); } + +struct Func final : public locomotiv::UnaryFunc +{ + float apply(float v) const final { return sqrt_ew(v); } +}; + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseSqrt *sqrt_node) +{ + Func f; + + eltwise_unary(sqrt_node, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp b/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp new file mode 100644 index 000000000..adb1b853e --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_EltwiseSqrt, f32) +{ + // Make Pull-EltwiseSqrt graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({4}); + auto sqrt = g->nodes()->create(); + sqrt->input(pull); + + // Make and assign data to Pull node + auto pull_buf = make_buffer(Shape{4}); + pull_buf.at(Index{0}) = 4.0f; + pull_buf.at(Index{1}) = 9.0f; + pull_buf.at(Index{2}) = 0.0f; + pull_buf.at(Index{3}) = -1.0f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(sqrt); + + auto sqrt_data = locomotiv::annot_data(sqrt); + ASSERT_NE(sqrt_data, nullptr); + ASSERT_EQ(sqrt_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(sqrt_data->shape()), Shape{4}); + ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{0}), 2.0f); + ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{1}), 3.0f); + ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{2}), 0.0f); + ASSERT_TRUE(std::isnan(sqrt_data->as_f32_bufptr()->at(Index{3}))); + + ASSERT_EQ(locomotiv::annot_domain(sqrt), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/EltwiseSub.cpp b/compiler/locomotiv/src/Node/EltwiseSub.cpp new file mode 100644 index 000000000..7943f950b --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseSub.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +namespace locomotiv +{ + +void NodeExecution::execute(loco::EltwiseSub *eltwise_sub) +{ + struct Func final : public BinaryFunc + { + float apply(float lhs, float rhs) const { return lhs - rhs; } + }; + + Func f; + + eltwise_binary(eltwise_sub, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/EltwiseSub.test.cpp b/compiler/locomotiv/src/Node/EltwiseSub.test.cpp new file mode 100644 index 000000000..7eff90f9e --- /dev/null +++ b/compiler/locomotiv/src/Node/EltwiseSub.test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +/* +test case generated from the following: + +x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +y = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + shape=[1, 3, 3, 2], dtype=tf.float32) +out = tf.math.subtract(x, y) + +with tf.Session() as sess: + print(sess.run(out)) +*/ +TEST(NodeExecution_EltwiseSub, f32) +{ + float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float y_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + float out_val[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // make EltwiseSub(Pull, Pull) + auto g = loco::make_graph(); + Shape input_shape{1, 3, 3, 2}; // NHWC + + auto inp_lhs = g->nodes()->create(); + { + inp_lhs->dtype(loco::DataType::FLOAT32); + inp_lhs->shape({1, 3, 3, 2}); + } + + auto inp_rhs = g->nodes()->create(); + { + inp_rhs->dtype(loco::DataType::FLOAT32); + inp_rhs->shape({1, 3, 3, 2}); + } + + auto eltwise_sub = g->nodes()->create(); + { + eltwise_sub->lhs(inp_lhs); + eltwise_sub->rhs(inp_rhs); + } + + // Make and assign data to two pull nodes + auto inp_lhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance()) + { + inp_lhs_buf.at(e.current()) = x_val[n++]; + } + } + + auto inp_rhs_buf = make_buffer(input_shape); + { + int n = 0; + for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance()) + { + inp_rhs_buf.at(e.current()) = y_val[n++]; + } + } + + auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf); + locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data)); + locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor); + + auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf); + locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data)); + locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor); + + // run the network + locomotiv::NodeExecution::get().run(eltwise_sub); + + // get result + auto eltwise_sub_data = locomotiv::annot_data(eltwise_sub); + + // comparing the result + ASSERT_NE(eltwise_sub_data, nullptr); + ASSERT_EQ(eltwise_sub_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(eltwise_sub_data->shape()), Shape({1, 3, 3, 2})); + + uint32_t n = 0; + for (IndexEnumerator e{*(eltwise_sub_data->shape())}; e.valid(); e.advance()) + { + ASSERT_FLOAT_EQ(eltwise_sub_data->as_f32_bufptr()->at(e.current()), out_val[n++]); + } + + ASSERT_EQ(locomotiv::annot_domain(eltwise_sub), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/FeatureCodec.test.cpp b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp new file mode 100644 index 000000000..c35f0e69a --- /dev/null +++ b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include + +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::Buffer; + +// This file is intended to test FeatureEncode and FeatureDecode at once +namespace +{ + +class NodeExecution_FeatureCodec : public ::testing::Test +{ +private: + loco::Graph g; + +protected: + /// @brief Make Pull node and set data by given buffer and data type + template loco::Pull *pull_layer(Buffer
&pull_buf, loco::DataType dtype) + { + auto pull = g.nodes()->create(); + pull->dtype(dtype); + + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + return pull; + } + + /// @brief Make FeatureEncode node with given input and encoding permutation + loco::FeatureEncode *feature_encode_layer(loco::Node *input, + const loco::Permutation &perm) + { + auto encoder = std::unique_ptr>( + new loco::PermutingEncoder); + + encoder->perm(perm); + + auto enc = g.nodes()->create(); + enc->input(input); + enc->encoder(std::move(encoder)); + + return enc; + } + + /// @brief Make FeatureDecode node with given input and decoding permutation + loco::FeatureDecode *feature_decode_layer(loco::Node *input, + const loco::Permutation &perm) + { + auto decoder = std::unique_ptr>( + new loco::PermutingDecoder); + + decoder->perm(perm); + + auto dec = g.nodes()->create(); + dec->input(input); + dec->decoder(std::move(decoder)); + + return dec; + } +}; + +} // namespace + +TEST_F(NodeExecution_FeatureCodec, s32) +{ + const uint32_t N = 2; + const uint32_t H = 3; + const uint32_t W = 4; + const uint32_t C = 5; + + // Make "NCHW" data for pull node + auto pull_buf = make_buffer(Shape{N, C, H, W}); + int32_t i = 0; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = i; + ++i; // Doesn't matter what it is + } + + // Make NCHW permutation for encoder and decoder + loco::Permutation NCHW; + + NCHW.axis(loco::FeatureAxis::Count) = 0; + NCHW.axis(loco::FeatureAxis::Depth) = 1; + NCHW.axis(loco::FeatureAxis::Height) = 2; + NCHW.axis(loco::FeatureAxis::Width) = 3; + + // Pull + auto pull = pull_layer(pull_buf, loco::DataType::S32); + + // FeatureEncode + auto enc = feature_encode_layer(pull, NCHW); + locomotiv::NodeExecution::get().run(enc); + + // Test FeatureEncode + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv feature is NHWC + auto enc_buf = enc_data->as_s32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), enc_buf->at(Index{n, h, w, c})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Feature); + + // FeatureDecode + auto dec = feature_decode_layer(enc, NCHW); + locomotiv::NodeExecution::get().run(dec); + + // Test FeatureDecode: Encode -> Decode == identity + auto dec_data = locomotiv::annot_data(dec); + ASSERT_NE(dec_data, nullptr); + ASSERT_EQ(dec_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(dec_data->shape()), (Shape{N, C, H, W})); + auto dec_buf = dec_data->as_s32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), dec_buf->at(Index{n, c, h, w})); + + ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor); +} + +TEST_F(NodeExecution_FeatureCodec, f32) +{ + const uint32_t N = 2; + const uint32_t H = 3; + const uint32_t W = 4; + const uint32_t C = 5; + + // Make crazy "CHNW" data for pull node + auto pull_buf = make_buffer(Shape{C, H, N, W}); + float f = 0.0f; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = f; + f += 0.1f; // Doesn't matter what it is + } + + // Make CHNW permutation for encoder and decoder + loco::Permutation CHNW; + + CHNW.axis(loco::FeatureAxis::Depth) = 0; + CHNW.axis(loco::FeatureAxis::Height) = 1; + CHNW.axis(loco::FeatureAxis::Count) = 2; + CHNW.axis(loco::FeatureAxis::Width) = 3; + + // Pull + auto pull = pull_layer(pull_buf, loco::DataType::FLOAT32); + + // FeatureEncode + auto enc = feature_encode_layer(pull, CHNW); + locomotiv::NodeExecution::get().run(enc); + + // Test FeatureEncode + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv feature is NHWC + auto enc_buf = enc_data->as_f32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), enc_buf->at(Index{n, h, w, c})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Feature); + + // FeatureDecode + auto dec = feature_decode_layer(enc, CHNW); + locomotiv::NodeExecution::get().run(dec); + + // Test FeatureDecode: Encode -> Decode == identity + auto dec_data = locomotiv::annot_data(dec); + ASSERT_NE(dec_data, nullptr); + ASSERT_EQ(dec_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(dec_data->shape()), (Shape{C, H, N, W})); + auto dec_buf = dec_data->as_f32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), dec_buf->at(Index{c, h, n, w})); + + ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/FeatureDecode.cpp b/compiler/locomotiv/src/Node/FeatureDecode.cpp new file mode 100644 index 000000000..8a56a56b2 --- /dev/null +++ b/compiler/locomotiv/src/Node/FeatureDecode.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::Index; + +template +std::unique_ptr feature_decode(const loco::FeatureDecode *node, + const Buffer *input_buf) +{ + auto decoder = node->decoder(); + + // Make FeatureShape from input. Note that feature in locomotiv represented as NHWC + loco::FeatureShape input_shape; + assert(input_buf->shape().rank() == 4); + input_shape.count() = input_buf->shape().dim(0); + input_shape.height() = input_buf->shape().dim(1); + input_shape.width() = input_buf->shape().dim(2); + input_shape.depth() = input_buf->shape().dim(3); + + loco::TensorShape node_shape = decoder->shape(input_shape); + + // Make tensor buffer from TensorShape + Buffer node_buf = + make_buffer(Shape{node_shape.dim(0).value(), node_shape.dim(1).value(), + node_shape.dim(2).value(), node_shape.dim(3).value()}); + + // Copy buffer in an order arranged by decoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::FeatureIndex feature_index = decoder->value(e.current()); + Index buf_index({feature_index.batch(), feature_index.row(), feature_index.column(), + feature_index.channel()}); + + node_buf.at(e.current()) = input_buf->at(buf_index); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::FeatureDecode *dec) +{ + auto input_data = annot_data(dec->input()); + + validate(input_data, "Input of FeatureDecode not ready"); + validate(annot_domain(dec->input()) == loco::Domain::Feature, + "Input of FeatureDecode is not Feature"); + validate(input_data->shape()->rank() == 4, "Input shape mismatch"); + + std::unique_ptr dec_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_buf = input_data->as_s32_bufptr(); + dec_data = feature_decode(dec, input_buf); + break; + } + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + dec_data = feature_decode(dec, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(dec_data != nullptr); + annot_data(dec, std::move(dec_data)); + annot_domain(dec, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/FeatureEncode.cpp b/compiler/locomotiv/src/Node/FeatureEncode.cpp new file mode 100644 index 000000000..406de76ff --- /dev/null +++ b/compiler/locomotiv/src/Node/FeatureEncode.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +template +std::unique_ptr feature_encode(const loco::FeatureEncode *node, + const Buffer *input_buf) +{ + auto encoder = node->encoder(); + + // Make TensorShape from input + loco::TensorShape input_shape; + input_shape.rank(input_buf->shape().rank()); + assert(input_shape.rank() == 4); + for (uint32_t i = 0; i < input_shape.rank(); ++i) + { + input_shape.dim(i) = input_buf->shape().dim(i); + } + + loco::FeatureShape node_shape = encoder->shape(input_shape); + + // Make NHWC buffer from FeatureShape + Buffer node_buf = + make_buffer(Shape{node_shape.count().value(), node_shape.height().value(), + node_shape.width().value(), node_shape.depth().value()}); + + // Copy buffer in an order arranged by encoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::FeatureIndex index; + index.batch() = e.current().at(0); + index.row() = e.current().at(1); + index.column() = e.current().at(2); + index.channel() = e.current().at(3); + + node_buf.at(e.current()) = input_buf->at(encoder->value(index)); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::FeatureEncode *enc) +{ + auto input_data = annot_data(enc->input()); + + validate(input_data, "Input of FeatureEncode not ready"); + validate(annot_domain(enc->input()) == loco::Domain::Tensor, + "Input of FeatureEncode is not Tensor"); + validate(input_data->shape()->rank() == 4, "Input shape mismatch"); + + std::unique_ptr enc_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_buf = input_data->as_s32_bufptr(); + enc_data = feature_encode(enc, input_buf); + break; + } + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + enc_data = feature_encode(enc, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(enc_data != nullptr); + annot_data(enc, std::move(enc_data)); + annot_domain(enc, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/FilterEncode.cpp b/compiler/locomotiv/src/Node/FilterEncode.cpp new file mode 100644 index 000000000..cd9d708dc --- /dev/null +++ b/compiler/locomotiv/src/Node/FilterEncode.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +template +std::unique_ptr filter_encode(const loco::FilterEncode *node, + const Buffer *input_buf) +{ + auto encoder = node->encoder(); + + // Make TensorShape from input + loco::TensorShape input_shape; + input_shape.rank(input_buf->shape().rank()); + assert(input_shape.rank() == 4); + for (uint32_t i = 0; i < input_shape.rank(); ++i) + { + input_shape.dim(i) = input_buf->shape().dim(i); + } + + loco::FilterShape node_shape = encoder->shape(input_shape); + + // Make NHWC buffer from FilterShape + Buffer node_buf = + make_buffer(Shape{node_shape.count().value(), node_shape.height().value(), + node_shape.width().value(), node_shape.depth().value()}); + + // Copy buffer in an order arranged by encoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::FilterIndex index; + index.nth() = e.current().at(0); + index.row() = e.current().at(1); + index.column() = e.current().at(2); + index.channel() = e.current().at(3); + + node_buf.at(e.current()) = input_buf->at(encoder->value(index)); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::FilterEncode *enc) +{ + auto input_data = annot_data(enc->input()); + + validate(input_data, "Input of FilterEncode not ready"); + validate(annot_domain(enc->input()) == loco::Domain::Tensor, + "Input of FilterEncode is not Tensor"); + validate(input_data->shape()->rank() == 4, "Input shape mismatch"); + + std::unique_ptr enc_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_buf = input_data->as_s32_bufptr(); + enc_data = filter_encode(enc, input_buf); + break; + } + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + enc_data = filter_encode(enc, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(enc_data != nullptr); + annot_data(enc, std::move(enc_data)); + annot_domain(enc, loco::Domain::Filter); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/FilterEncode.test.cpp b/compiler/locomotiv/src/Node/FilterEncode.test.cpp new file mode 100644 index 000000000..79b8308e2 --- /dev/null +++ b/compiler/locomotiv/src/Node/FilterEncode.test.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include + +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; + +TEST(NodeExecution_FilterEncode, s32) +{ + const uint32_t N = 2; + const uint32_t H = 3; + const uint32_t W = 4; + const uint32_t C = 5; + + auto g = loco::make_graph(); + + // Pull + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::S32); + + // Make and assign "NCHW" data to pull node + auto pull_buf = make_buffer(Shape{N, C, H, W}); + int32_t i = 1; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = i; + ++i; // Doesn't matter what it is + } + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + // Encoder to correctly read input tensor as NCHW + auto encoder = std::unique_ptr>( + new loco::PermutingEncoder); + encoder->perm()->axis(loco::FilterAxis::Count) = 0; + encoder->perm()->axis(loco::FilterAxis::Depth) = 1; + encoder->perm()->axis(loco::FilterAxis::Height) = 2; + encoder->perm()->axis(loco::FilterAxis::Width) = 3; + + // FilterEncode + auto enc = g->nodes()->create(); + enc->input(pull); + enc->encoder(std::move(encoder)); + + locomotiv::NodeExecution::get().run(enc); + + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv filter is NHWC + auto enc_buf = enc_data->as_s32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), enc_buf->at(Index{n, h, w, c})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Filter); +} + +TEST(NodeExecution_FilterEncode, f32) +{ + const uint32_t N = 2; + const uint32_t H = 3; + const uint32_t W = 4; + const uint32_t C = 5; + + auto g = loco::make_graph(); + + // Pull + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + + // Make and assign crazy "CHNW" data to pull node + auto pull_buf = make_buffer(Shape{C, H, N, W}); + float f = 1; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = f; + f += 0.1f; // Doesn't matter what it is + } + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + // Encoder to correctly read input tensor as CHNW + auto encoder = std::unique_ptr>( + new loco::PermutingEncoder); + encoder->perm()->axis(loco::FilterAxis::Depth) = 0; + encoder->perm()->axis(loco::FilterAxis::Height) = 1; + encoder->perm()->axis(loco::FilterAxis::Count) = 2; + encoder->perm()->axis(loco::FilterAxis::Width) = 3; + + // FilterEncode + auto enc = g->nodes()->create(); + enc->input(pull); + enc->encoder(std::move(encoder)); + + locomotiv::NodeExecution::get().run(enc); + + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv filter is NHWC + auto enc_buf = enc_data->as_f32_bufptr(); + for (uint32_t n = 0; n < N; ++n) + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + for (uint32_t c = 0; c < C; ++c) + ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), enc_buf->at(Index{n, h, w, c})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Filter); +} diff --git a/compiler/locomotiv/src/Node/Forward.cpp b/compiler/locomotiv/src/Node/Forward.cpp new file mode 100644 index 000000000..eb7d44a59 --- /dev/null +++ b/compiler/locomotiv/src/Node/Forward.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Forward *forward) +{ + auto input_data = annot_data(forward->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(forward->input()) != loco::Domain::Unknown, + "Input domain must not Unknown"); + + std::unique_ptr forward_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_bufptr = input_data->as_s32_bufptr(); + forward_data = make_data(*input_bufptr); + break; + } + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + forward_data = make_data(*input_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(forward_data != nullptr); + annot_data(forward, std::move(forward_data)); + annot_domain(forward, annot_domain(forward->input())); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Forward.test.cpp b/compiler/locomotiv/src/Node/Forward.test.cpp new file mode 100644 index 000000000..73d37139a --- /dev/null +++ b/compiler/locomotiv/src/Node/Forward.test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Forward, s32) +{ + // Make pull-forward graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::S32); + pull->shape({1}); + auto forward = g->nodes()->create(); + forward->input(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + pull_buf.at(Index{0}) = 42; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(forward); + + auto forward_data = locomotiv::annot_data(forward); + ASSERT_NE(forward_data, nullptr); + ASSERT_EQ(forward_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(forward_data->shape()), Shape{1}); + ASSERT_EQ(forward_data->as_s32_bufptr()->at(Index{0}), pull_buf.at(Index{0})); + + ASSERT_EQ(locomotiv::annot_domain(forward), loco::Domain::Tensor); +} + +TEST(NodeExecution_Forward, f32) +{ + // Make pull-forward graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1}); + auto forward = g->nodes()->create(); + forward->input(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + pull_buf.at(Index{0}) = 3.14f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(forward); + + auto forward_data = locomotiv::annot_data(forward); + ASSERT_NE(forward_data, nullptr); + ASSERT_EQ(forward_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(forward_data->shape()), Shape{1}); + ASSERT_FLOAT_EQ(forward_data->as_f32_bufptr()->at(Index{0}), pull_buf.at(Index{0})); + + ASSERT_EQ(locomotiv::annot_domain(forward), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/MatMul.cpp b/compiler/locomotiv/src/Node/MatMul.cpp new file mode 100644 index 000000000..77b7315a9 --- /dev/null +++ b/compiler/locomotiv/src/Node/MatMul.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +/** + * @brief Calculate Matrix Multiplication + */ +template Buffer calc_mat_mul(const Buffer *lhs_buf, const Buffer *rhs_buf) +{ + const auto lhs_shape = lhs_buf->shape(); + const auto rhs_shape = rhs_buf->shape(); + + assert(lhs_shape.rank() == 2 && "lhs rank must be 2"); + assert(rhs_shape.rank() == 2 && "rhs rank must be 2"); + // lhs width should be the same as rhs height + assert(lhs_shape.dim(1) == rhs_shape.dim(0) && "height/width mismatch"); + + const uint32_t lhs_height = lhs_shape.dim(0); + const uint32_t lhs_width = lhs_shape.dim(1); + + const uint32_t rhs_width = rhs_shape.dim(1); + + const uint32_t output_height = lhs_height; + const uint32_t output_width = rhs_width; + + Shape output_shape{output_height, output_width}; + auto output_buf = make_buffer(output_shape); + + for (uint32_t out_y = 0; out_y < output_height; ++out_y) + { + for (uint32_t out_x = 0; out_x < output_width; ++out_x) + { + T total = static_cast(0); // accumulator + // Accumulate through axis + for (uint32_t axis = 0; axis < lhs_width; ++axis) + { + total += lhs_buf->at(Index({out_y, axis})) * rhs_buf->at(Index({axis, out_x})); + } + // Set output value + output_buf.at(Index({out_y, out_x})) = total; + } + } + + return output_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::MatMul *mat_mul) +{ + auto lhs_data = annot_data(mat_mul->lhs()); + auto rhs_data = annot_data(mat_mul->rhs()); + + validate(lhs_data, "Can't find left matrix data of MatMul"); + validate(lhs_data->shape()->rank() == 2, "lhs rank must be 2"); + + validate(rhs_data, "Can't find right matrix data of MatMul"); + validate(rhs_data->shape()->rank() == 2, "rhs rank must be 2"); + + validate(annot_domain(mat_mul->lhs()) == loco::Domain::Matrix, + "Left matrix of MatMul is not a Matrix"); + validate(annot_domain(mat_mul->rhs()) == loco::Domain::Matrix, + "Right matrix of MatMul is not a Matrix"); + + std::unique_ptr mat_mul_result = nullptr; + + if (lhs_data->dtype() == loco::DataType::FLOAT32 && rhs_data->dtype() == loco::DataType::FLOAT32) + { + const auto lhs_buf = lhs_data->as_f32_bufptr(); + const auto rhs_buf = rhs_data->as_f32_bufptr(); + + auto mat_mul_buf = calc_mat_mul(lhs_buf, rhs_buf); + + mat_mul_result = make_data(mat_mul_buf); + } + else if (lhs_data->dtype() == loco::DataType::S32 && rhs_data->dtype() == loco::DataType::S32) + { + const auto lhs_buf = lhs_data->as_s32_bufptr(); + const auto rhs_buf = rhs_data->as_s32_bufptr(); + + auto mat_mul_buf = calc_mat_mul(lhs_buf, rhs_buf); + + mat_mul_result = make_data(mat_mul_buf); + } + else + throw std::runtime_error("NYI for these DataTypes"); + + assert(mat_mul_result != nullptr); + + annot_data(mat_mul, std::move(mat_mul_result)); + annot_domain(mat_mul, loco::Domain::Matrix); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/MatMul.test.cpp b/compiler/locomotiv/src/Node/MatMul.test.cpp new file mode 100644 index 000000000..bd480f7c7 --- /dev/null +++ b/compiler/locomotiv/src/Node/MatMul.test.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +template +void run_test(const T *lhs, const T *rhs, const T *expected_output, const Shape &lhs_shape, + const Shape &rhs_shape, const Shape &out_shape, loco::DataType expected_datatype) +{ + auto g = loco::make_graph(); + // Fill lhs MatrixEncode + auto lhs_enc = g->nodes()->create(); + { + auto lhs_enc_buf = make_buffer(lhs_shape); + auto lhs_overlay = make_overlay(lhs_shape, const_cast(lhs)); + for (nncc::core::ADT::tensor::IndexEnumerator e{lhs_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + lhs_enc_buf.at(ind) = lhs_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(lhs_enc_buf); + locomotiv::annot_data(lhs_enc, std::move(enc_data)); + locomotiv::annot_domain(lhs_enc, loco::Domain::Matrix); + } + // Fill rhs MatrixEncode + auto rhs_enc = g->nodes()->create(); + { + auto rhs_enc_buf = make_buffer(rhs_shape); + auto rhs_overlay = make_overlay(rhs_shape, const_cast(rhs)); + for (nncc::core::ADT::tensor::IndexEnumerator e{rhs_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + rhs_enc_buf.at(ind) = rhs_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(rhs_enc_buf); + locomotiv::annot_data(rhs_enc, std::move(enc_data)); + locomotiv::annot_domain(rhs_enc, loco::Domain::Matrix); + } + + // build MatMul + auto mat_mul = g->nodes()->create(); + mat_mul->lhs(lhs_enc); + mat_mul->rhs(rhs_enc); + + // run interpreter + locomotiv::NodeExecution::get().run(mat_mul); + + // get result of calculation + auto mat_mul_result = locomotiv::annot_data(mat_mul); + + // check the result + ASSERT_NE(mat_mul_result, nullptr); + ASSERT_TRUE(mat_mul_result->dtype() == expected_datatype); + ASSERT_TRUE(*(mat_mul_result->shape()) == out_shape); + + auto out_overlay = make_overlay(out_shape, const_cast(expected_output)); + for (nncc::core::ADT::tensor::IndexEnumerator e{out_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + if (expected_datatype == loco::DataType::FLOAT32) + ASSERT_FLOAT_EQ(mat_mul_result->as_f32_bufptr()->at(ind), out_overlay.at(ind)); + else if (expected_datatype == loco::DataType::S32) + ASSERT_EQ(mat_mul_result->as_s32_bufptr()->at(ind), out_overlay.at(ind)); + else + throw std::runtime_error("NYI for these DataTypes"); + } + + ASSERT_EQ(locomotiv::annot_domain(mat_mul), loco::Domain::Matrix); +} + +} // namespace + +// clang-format off +/* from the code below: + +import numpy as np + +a = [[-0.48850584, 1.4292705, -1.3424522], + [1.7021934, -0.39246717, 0.6248314]] + +b = [[-0.0830195, 0.21088193, -0.11781317], + [0.07755677, 1.6337638, 1.0792778], + [-1.6922939, -1.5437212, 0.96667504]] + +print(np.array(a) @ np.array(b)) +*/ +TEST(NodeExecution_MatMul, f32_2x3_3x3) +{ + using nncc::core::ADT::tensor::Shape; + + const float lhs[] = + { + -0.48850584, 1.4292705, -1.3424522, + 1.7021934, -0.39246717, 0.6248314 + }; + + const float rhs[] = + { + -0.0830195, 0.21088193, -0.11781317, + 0.07755677, 1.6337638, 1.0792778, + -1.6922939, -1.5437212, 0.96667504 + }; + + const float out[] = + { + 2.42322878, 4.30444527, 0.30241731, + -1.2291521, -1.2468023, -0.02011299 + }; + + run_test(lhs, rhs, out, Shape{2, 3}, Shape{3, 3}, Shape{2, 3}, loco::DataType::FLOAT32); +} + +/* from the code below: + +import numpy as np + +a = np.random.randint(10000, size=(4, 2)) + +b = np.random.randint(10000, size=(2, 6)) + +print(a) +print(b) +print(np.array(a) @ np.array(b)) +*/ +TEST(NodeExecution_MatMul, s32_4x2_2x6) +{ + using nncc::core::ADT::tensor::Shape; + + const int32_t lhs[] = + { + 6392, 4993, + 54, 9037, + 3947, 5820, + 5800, 4181 + }; + + const int32_t rhs[] = + { + 2694, 8376, 8090, 1285, 7492, 1652, + 5427, 8798, 7634, 2229, 5439, 6999 + }; + + const int32_t out[] = + { + 44317059, 97467806, 89827842, 19343117, 75045791, 45505591, + 49189275, 79959830, 69425318, 20212863, 49556811, 63339171, + 42218358, 84264432, 76361110, 18044675, 61225904, 47254624, + 38315487, 85365238, 78839754, 16772449, 66194059, 38844419 + }; + + run_test(lhs, rhs, out, Shape{4, 2}, Shape{2, 6}, Shape{4, 6}, loco::DataType::S32); +} + +// clang-format on diff --git a/compiler/locomotiv/src/Node/MatrixCodec.test.cpp b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp new file mode 100644 index 000000000..8fc5d593b --- /dev/null +++ b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include + +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::Buffer; + +// This file is intended to test MatrixEncode and MatrixDecode at once +namespace +{ + +class NodeExecution_MatrixCodec : public ::testing::Test +{ +private: + loco::Graph g; + +protected: + /// @brief Make Pull node and set data by given buffer and data type + template loco::Pull *pull_layer(Buffer
&pull_buf, loco::DataType dtype) + { + auto pull = g.nodes()->create(); + pull->dtype(dtype); + + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + return pull; + } + + /// @brief Make MatrixEncode node with given input and encoding permutation + loco::MatrixEncode *matrix_encode_layer(loco::Node *input, + const loco::Permutation &perm) + { + auto encoder = std::unique_ptr>( + new loco::PermutingEncoder); + + encoder->perm(perm); + + auto enc = g.nodes()->create(); + enc->input(input); + enc->encoder(std::move(encoder)); + + return enc; + } + + /// @brief Make MatrixDecode node with given input and decoding permutation + loco::MatrixDecode *matrix_decode_layer(loco::Node *input, + const loco::Permutation &perm) + { + auto decoder = std::unique_ptr>( + new loco::PermutingDecoder); + + decoder->perm(perm); + + auto dec = g.nodes()->create(); + dec->input(input); + dec->decoder(std::move(decoder)); + + return dec; + } +}; + +} // namespace + +TEST_F(NodeExecution_MatrixCodec, HW_s32) +{ + const uint32_t H = 3; + const uint32_t W = 4; + + // Make HW data for pull node + auto pull_buf = make_buffer(Shape{H, W}); + int32_t i = 0; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = i; + ++i; // Doesn't matter what it is + } + + // Make HW permutation for encoder and decoder + loco::Permutation HW; + + HW.axis(loco::MatrixAxis::Height) = 0; + HW.axis(loco::MatrixAxis::Width) = 1; + + // Pull + auto pull = pull_layer(pull_buf, loco::DataType::S32); + + // MatrixEncode + auto enc = matrix_encode_layer(pull, HW); + locomotiv::NodeExecution::get().run(enc); + + // Test MatrixEncode + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(enc_data->shape()), (Shape{H, W})); // locomotiv matrix is HW + auto enc_buf = enc_data->as_s32_bufptr(); + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + ASSERT_EQ(pull_buf.at(Index{h, w}), enc_buf->at(Index{h, w})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Matrix); + + // MatrixDecode + auto dec = matrix_decode_layer(enc, HW); + locomotiv::NodeExecution::get().run(dec); + + // Test MatrixDecode: Encode -> Decode == identity + auto dec_data = locomotiv::annot_data(dec); + ASSERT_NE(dec_data, nullptr); + ASSERT_EQ(dec_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(dec_data->shape()), (Shape{H, W})); + auto dec_buf = dec_data->as_s32_bufptr(); + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + ASSERT_EQ(pull_buf.at(Index{h, w}), dec_buf->at(Index{h, w})); + + ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor); +} + +TEST_F(NodeExecution_MatrixCodec, WH_f32) +{ + const uint32_t W = 6; + const uint32_t H = 5; + + // Make crazy WH data for pull node + auto pull_buf = make_buffer(Shape{W, H}); + float f = 0.0f; + for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance()) + { + pull_buf.at(e.current()) = f; + f += 0.1f; // Doesn't matter what it is + } + + // Make WH permutation for encoder and decoder + loco::Permutation WH; + + WH.axis(loco::MatrixAxis::Width) = 0; + WH.axis(loco::MatrixAxis::Height) = 1; + + // Pull + auto pull = pull_layer(pull_buf, loco::DataType::FLOAT32); + + // MatrixEncode + auto enc = matrix_encode_layer(pull, WH); + locomotiv::NodeExecution::get().run(enc); + + // Test MatrixEncode + auto enc_data = locomotiv::annot_data(enc); + ASSERT_NE(enc_data, nullptr); + ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(enc_data->shape()), (Shape{H, W})); // locomotiv matrix is HW + auto enc_buf = enc_data->as_f32_bufptr(); + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + ASSERT_FLOAT_EQ(pull_buf.at(Index{w, h}), enc_buf->at(Index{h, w})); + + ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Matrix); + + // MatrixDecode + auto dec = matrix_decode_layer(enc, WH); + locomotiv::NodeExecution::get().run(dec); + + // Test MatrixDecode: Encode -> Decode == identity + auto dec_data = locomotiv::annot_data(dec); + ASSERT_NE(dec_data, nullptr); + ASSERT_EQ(dec_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(dec_data->shape()), (Shape{W, H})); + auto dec_buf = dec_data->as_f32_bufptr(); + for (uint32_t h = 0; h < H; ++h) + for (uint32_t w = 0; w < W; ++w) + ASSERT_FLOAT_EQ(pull_buf.at(Index{w, h}), dec_buf->at(Index{w, h})); + + ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/MatrixDecode.cpp b/compiler/locomotiv/src/Node/MatrixDecode.cpp new file mode 100644 index 000000000..c591676ae --- /dev/null +++ b/compiler/locomotiv/src/Node/MatrixDecode.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::Index; + +template +std::unique_ptr matrix_decode(const loco::MatrixDecode *node, + const Buffer *input_buf) +{ + auto decoder = node->decoder(); + + // Make MatrixShape from input. Note that matrix in locomotiv represented as HW + loco::MatrixShape input_shape; + assert(input_buf->shape().rank() == 2); + input_shape.height() = input_buf->shape().dim(0); + input_shape.width() = input_buf->shape().dim(1); + + loco::TensorShape node_shape = decoder->shape(input_shape); + + // Make tensor buffer from TensorShape + Buffer node_buf = + make_buffer(Shape{node_shape.dim(0).value(), node_shape.dim(1).value()}); + + // Copy buffer in an order arranged by decoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::MatrixIndex matrix_index = decoder->value(e.current()); + Index buf_index({matrix_index.row(), matrix_index.column()}); + + node_buf.at(e.current()) = input_buf->at(buf_index); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::MatrixDecode *matrix_dec) +{ + auto input_data = annot_data(matrix_dec->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(matrix_dec->input()) == loco::Domain::Matrix, + "Input domain should be Matrix"); + validate(input_data->shape()->rank() == 2, "Input data rank must be 2"); + + std::unique_ptr matrix_dec_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_buf = input_data->as_s32_bufptr(); + matrix_dec_data = matrix_decode(matrix_dec, input_buf); + break; + } + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + matrix_dec_data = matrix_decode(matrix_dec, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(matrix_dec_data != nullptr); + + annot_data(matrix_dec, std::move(matrix_dec_data)); + annot_domain(matrix_dec, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/MatrixEncode.cpp b/compiler/locomotiv/src/Node/MatrixEncode.cpp new file mode 100644 index 000000000..e3554e15a --- /dev/null +++ b/compiler/locomotiv/src/Node/MatrixEncode.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::IndexEnumerator; + +template +std::unique_ptr matrix_encode(const loco::MatrixEncode *node, + const Buffer *input_buf) +{ + auto encoder = node->encoder(); + + // Make TensorShape from input + loco::TensorShape input_shape; + input_shape.rank(input_buf->shape().rank()); + assert(input_shape.rank() == 2); + for (uint32_t i = 0; i < input_shape.rank(); ++i) + { + input_shape.dim(i) = input_buf->shape().dim(i); + } + + loco::MatrixShape node_shape = encoder->shape(input_shape); + + // Make HW buffer from MatrixShape + Buffer node_buf = + make_buffer(Shape{node_shape.height().value(), node_shape.width().value()}); + + // Copy buffer in an order arranged by encoder + for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) + { + loco::MatrixIndex index; + index.row() = e.current().at(0); + index.column() = e.current().at(1); + + node_buf.at(e.current()) = input_buf->at(encoder->value(index)); + } + + return locomotiv::make_data(node_buf); +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::MatrixEncode *matrix_enc) +{ + auto input_data = annot_data(matrix_enc->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(matrix_enc->input()) == loco::Domain::Tensor, + "Input domain should be Tensor"); + validate(input_data->shape()->rank() == 2, "Input data rank must be 2"); + + std::unique_ptr matrix_enc_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_buf = input_data->as_s32_bufptr(); + matrix_enc_data = matrix_encode(matrix_enc, input_buf); + break; + } + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + matrix_enc_data = matrix_encode(matrix_enc, input_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(matrix_enc_data != nullptr); + + annot_data(matrix_enc, std::move(matrix_enc_data)); + annot_domain(matrix_enc, loco::Domain::Matrix); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/MaxPool2D.cpp b/compiler/locomotiv/src/Node/MaxPool2D.cpp new file mode 100644 index 000000000..5d92f89f5 --- /dev/null +++ b/compiler/locomotiv/src/Node/MaxPool2D.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ + +/** + * @brief Compute 1D output size based on given 1D arguments. + * + * @param whole_pad Sum of front and back pad + */ +inline uint32_t compute_out_size(uint32_t image_size, uint32_t whole_pad, uint32_t filter_size, + uint32_t stride) +{ + assert((image_size + whole_pad - filter_size) % stride == 0); + return (image_size + whole_pad - filter_size) / stride + 1; +} + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +template +nncc::core::ADT::tensor::Buffer maxPool2D(const loco::MaxPool2D *maxpool2d, + const Buffer *ifm_buf) +{ + auto ifm_shape = ifm_buf->shape(); + + const uint32_t batches = ifm_shape.dim(0); + const uint32_t depth = ifm_shape.dim(3); + + const uint32_t ifm_height = ifm_shape.dim(1); + const uint32_t ifm_width = ifm_shape.dim(2); + + const uint32_t window_height = maxpool2d->window()->vertical(); + const uint32_t window_width = maxpool2d->window()->horizontal(); + + const uint32_t stride_height = maxpool2d->stride()->vertical(); + const uint32_t stride_width = maxpool2d->stride()->horizontal(); + + const uint32_t pad_top = maxpool2d->pad()->top(); + const uint32_t pad_bottom = maxpool2d->pad()->bottom(); + + const uint32_t pad_left = maxpool2d->pad()->left(); + const uint32_t pad_right = maxpool2d->pad()->right(); + + const uint32_t output_height = + compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); + const uint32_t output_width = + compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); + + // prepare output buffer + Shape output_shape{batches, output_height, output_width, depth}; + auto output_buf = make_buffer(output_shape); + + for (uint32_t batch = 0; batch < batches; ++batch) + { + for (uint32_t out_y = 0; out_y < output_height; ++out_y) + { + for (uint32_t out_x = 0; out_x < output_width; ++out_x) + { + for (uint32_t channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - pad_left; + const int in_y_origin = (out_y * stride_height) - pad_top; + + // Compute the boundaries of the filter region clamped so as to + // ensure that the filter window fits in the input array. + const uint32_t filter_x_start = std::max(0, -in_x_origin); + const uint32_t filter_x_end = std::min(window_width, ifm_width - in_x_origin); + + const uint32_t filter_y_start = std::max(0, -in_y_origin); + const uint32_t filter_y_end = std::min(window_height, ifm_height - in_y_origin); + + T max = std::numeric_limits::lowest(); + + for (uint32_t filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (uint32_t filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const uint32_t in_x = in_x_origin + filter_x; + const uint32_t in_y = in_y_origin + filter_y; + max = std::max(max, ifm_buf->at(Index({batch, in_y, in_x, channel}))); + } + } + + output_buf.at(Index({batch, out_y, out_x, channel})) = max; + } + } + } + } + + return output_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::MaxPool2D *maxpool2d) +{ + auto ifm_data = annot_data(maxpool2d->ifm()); + + validate(ifm_data, "Can't find input data of MaxPool2D"); + validate(ifm_data->shape()->rank() == 4, "IFM rank should be 4"); + validate(annot_domain(maxpool2d->ifm()) == loco::Domain::Feature, + "ifm of MaxPool2D is not Feature"); + + std::unique_ptr maxpool2d_data = nullptr; + + switch (ifm_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto ifm_buf = ifm_data->as_f32_bufptr(); + + auto maxpool2d_buf = maxPool2D(maxpool2d, ifm_buf); + + maxpool2d_data = make_data(maxpool2d_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(maxpool2d_data != nullptr); + + annot_data(maxpool2d, std::move(maxpool2d_data)); + annot_domain(maxpool2d, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/MaxPool2D.test.cpp b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp new file mode 100644 index 000000000..9d877a96b --- /dev/null +++ b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shape, + const Shape &ofm_shape, const uint32_t window_v, const uint32_t window_h, + const uint32_t stride_v, const uint32_t stride_h, const uint32_t pad_top, + const uint32_t pad_bottom, const uint32_t pad_left, const uint32_t pad_right) +{ + // Let's make FeatureEncode-MaxPool2D graph + auto g = loco::make_graph(); + auto enc = g->nodes()->create(); + + // Fill output data of FeatureEncode from ifm + auto enc_buf = make_buffer(ifm_shape); + + auto ifm_overlay = make_overlay(ifm_shape, const_cast(ifm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ifm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + enc_buf.at(ind) = ifm_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(enc_buf); + locomotiv::annot_data(enc, std::move(enc_data)); + locomotiv::annot_domain(enc, loco::Domain::Feature); + + // build MaxPool2D + auto maxpool2d = g->nodes()->create(); + maxpool2d->ifm(enc); + maxpool2d->window()->vertical(window_v); + maxpool2d->window()->horizontal(window_h); + maxpool2d->stride()->vertical(stride_v); + maxpool2d->stride()->horizontal(stride_h); + maxpool2d->pad()->top(pad_top); + maxpool2d->pad()->bottom(pad_bottom); + maxpool2d->pad()->left(pad_left); + maxpool2d->pad()->right(pad_right); + + // run interpreter + locomotiv::NodeExecution::get().run(maxpool2d); + + // get result of calculation + auto maxpool2d_data = locomotiv::annot_data(maxpool2d); + + // check the result + ASSERT_NE(maxpool2d_data, nullptr); + ASSERT_TRUE(maxpool2d_data->dtype() == loco::DataType::FLOAT32); + ASSERT_TRUE(*(maxpool2d_data->shape()) == ofm_shape); + + auto ofm_overlay = + make_overlay(ofm_shape, const_cast(expected_ofm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ASSERT_FLOAT_EQ(maxpool2d_data->as_f32_bufptr()->at(ind), ofm_overlay.at(ind)); + } + + ASSERT_EQ(locomotiv::annot_domain(maxpool2d), loco::Domain::Feature); +} + +} // namespace + +// clang-format off +/* ifm and ofm are from the code below: + + value = tf.random_normal([1, 3, 3, 1], stddev=1) + maxpool = tf.nn.max_pool(value, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1], padding= 'VALID', + data_format="NHWC") + with tf.Session() as sess: + print(sess.run(maxpool)) +*/ + +TEST(NodeExecution_MaxPool2D, f32_1x3x3x1_calculation) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + -1.5510627, 0.3653609, 1.9002001, + -0.15861237, -0.32944828, 1.2053918, + 0.50054574, -0.8533826, 0.131492, + }; + + const float ofm[] = + { + 0.3653609, 1.9002001, + 0.50054574, 1.2053918 + }; + + run_test(ifm, ofm, + Shape{1, 3, 3, 1}, Shape{1, 2, 2, 1}, // input shape , output shape + 2, 2, // kernel + 1, 1, // stride + 0, 0, 0, 0 // padding + ); +} + +TEST(NodeExecution_MaxPool2D, with_padding) +{ + using nncc::core::ADT::tensor::Shape; + + const float ifm[] = + { + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25 + }; + + const float ofm[] = + { + 7, 9, 10, + 17, 19, 20, + 22, 24, 25 + }; + + run_test(ifm, ofm, + Shape{1, 5, 5, 1}, Shape{1, 3, 3, 1}, // input shape , output shape + 3, 3, // kernel + 2, 2, // stride + 1, 1, 1, 1 // padding - this mimics SAME padding + ); +} +// clang-format on diff --git a/compiler/locomotiv/src/Node/Pull.cpp b/compiler/locomotiv/src/Node/Pull.cpp new file mode 100644 index 000000000..c482d8b04 --- /dev/null +++ b/compiler/locomotiv/src/Node/Pull.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "UserData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Pull *pull) +{ +// TODO Remove deprecated code +#if 0 + validate(annot_data(pull), "Data for Pull is not ready"); + + validate(annot_domain(pull) == loco::Domain::Tensor, "Domain for Pull is not Tensor"); + + // DO NOTHING +#endif + + auto input_data = user_data(pull); + + validate(input_data, "Input not ready"); + // User always passes a "Tensor" + + std::unique_ptr pull_data = nullptr; + + // Q. Is it possible to use generic one? + switch (input_data->dtype()) + { + case loco::DataType::S32: + { + auto input_bufptr = input_data->as_s32_bufptr(); + pull_data = make_data(*input_bufptr); + break; + } + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + pull_data = make_data(*input_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(pull_data != nullptr); + annot_data(pull, std::move(pull_data)); + annot_domain(pull, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Pull.test.cpp b/compiler/locomotiv/src/Node/Pull.test.cpp new file mode 100644 index 000000000..53e78776b --- /dev/null +++ b/compiler/locomotiv/src/Node/Pull.test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "UserData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Pull, check_data_ready) +{ + // Make graph with Pull node only + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + + // Data not ready yet + ASSERT_ANY_THROW(locomotiv::NodeExecution::get().run(pull)); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::user_data(pull, std::move(pull_data)); + +// The behavior of Pull is now consistent with that of other nodes. +// - annot_data and annot_domain is available after evaluating that "pull" node. +// TODO Remove this +#if 0 + // Domain not ready yet + ASSERT_ANY_THROW(locomotiv::NodeExecution::get().run(pull)); + + // Set Domain + locomotiv::annot_domain(pull, loco::Domain::Tensor); +#endif + + // Valid run + ASSERT_NO_THROW(locomotiv::NodeExecution::get().run(pull)); +} diff --git a/compiler/locomotiv/src/Node/Push.cpp b/compiler/locomotiv/src/Node/Push.cpp new file mode 100644 index 000000000..fc5808b15 --- /dev/null +++ b/compiler/locomotiv/src/Node/Push.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Push *push) +{ + auto from_data = annot_data(push->from()); + + validate(from_data, "Ingredient not ready"); + validate(annot_domain(push->from()) == loco::Domain::Tensor, "Ingredient of Push is not tensor"); + + std::unique_ptr push_data = nullptr; + + switch (from_data->dtype()) + { + case loco::DataType::S32: + { + auto from_bufptr = from_data->as_s32_bufptr(); + push_data = make_data(*from_bufptr); + break; + } + case loco::DataType::FLOAT32: + { + auto from_bufptr = from_data->as_f32_bufptr(); + push_data = make_data(*from_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(push_data != nullptr); + annot_data(push, std::move(push_data)); + annot_domain(push, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Push.test.cpp b/compiler/locomotiv/src/Node/Push.test.cpp new file mode 100644 index 000000000..be8f1e4e9 --- /dev/null +++ b/compiler/locomotiv/src/Node/Push.test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Push, s32) +{ + // Make pull-push graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::S32); + pull->shape({1}); + auto push = g->nodes()->create(); + push->from(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + pull_buf.at(Index{0}) = 42; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(push); + + auto push_data = locomotiv::annot_data(push); + ASSERT_NE(push_data, nullptr); + ASSERT_EQ(push_data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(push_data->shape()), Shape{1}); + ASSERT_EQ(push_data->as_s32_bufptr()->at(Index{0}), pull_buf.at(Index{0})); + + ASSERT_EQ(locomotiv::annot_domain(push), loco::Domain::Tensor); +} + +TEST(NodeExecution_Push, f32) +{ + // Make pull-push graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1}); + auto push = g->nodes()->create(); + push->from(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1}); + pull_buf.at(Index{0}) = 3.14f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(push); + + auto push_data = locomotiv::annot_data(push); + ASSERT_NE(push_data, nullptr); + ASSERT_EQ(push_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(push_data->shape()), Shape{1}); + ASSERT_FLOAT_EQ(push_data->as_f32_bufptr()->at(Index{0}), pull_buf.at(Index{0})); + + ASSERT_EQ(locomotiv::annot_domain(push), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/ReLU.cpp b/compiler/locomotiv/src/Node/ReLU.cpp new file mode 100644 index 000000000..c0f8620e7 --- /dev/null +++ b/compiler/locomotiv/src/Node/ReLU.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +namespace +{ + +inline float relu_ew(float val) { return val > 0.0f ? val : 0.0f; } + +struct Func final : public locomotiv::UnaryFunc +{ + float apply(float v) const final { return relu_ew(v); } +}; + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::ReLU *relu) +{ + Func f; + + eltwise_unary(relu, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/ReLU.test.cpp b/compiler/locomotiv/src/Node/ReLU.test.cpp new file mode 100644 index 000000000..0ddd01d0f --- /dev/null +++ b/compiler/locomotiv/src/Node/ReLU.test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_ReLU, f32) +{ + // Make pull-relu graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2}); + auto relu = g->nodes()->create(); + relu->input(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{2}); + pull_buf.at(Index{0}) = -10.0f; + pull_buf.at(Index{1}) = 10.0f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(relu); + + auto relu_data = locomotiv::annot_data(relu); + ASSERT_NE(relu_data, nullptr); + ASSERT_EQ(relu_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(relu_data->shape()), Shape{2}); + ASSERT_FLOAT_EQ(relu_data->as_f32_bufptr()->at(Index{0}), 0.0f); + ASSERT_FLOAT_EQ(relu_data->as_f32_bufptr()->at(Index{1}), 10.0f); + + ASSERT_EQ(locomotiv::annot_domain(relu), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/ReLU6.cpp b/compiler/locomotiv/src/Node/ReLU6.cpp new file mode 100644 index 000000000..586c015fc --- /dev/null +++ b/compiler/locomotiv/src/Node/ReLU6.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +// TODO Remove deprecated code +#if 0 +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include + +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +#include +#include +#endif + +namespace +{ + +inline float relu6_ew(float val) { return val < 0.0f ? 0.0f : (val < 6.0f ? val : 6.0f); } + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::ReLU6 *relu6) +{ +// TODO Remove deprecated code +#if 0 + auto input_data = annot_data(relu6->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(relu6->input()) != loco::Domain::Unknown, + "Input domain of ReLU is Unknown"); + + std::unique_ptr relu6_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto *shape = input_data->shape(); + auto relu6_buf = make_buffer(*shape); + + for (IndexEnumerator e{*shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + relu6_buf.at(index) = relu6_ew(input_bufptr->at(index)); + } + + relu6_data = make_data(relu6_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(relu6_data != nullptr); + annot_data(relu6, std::move(relu6_data)); + annot_domain(relu6, annot_domain(relu6->input())); +#endif + + struct Func final : public UnaryFunc + { + float apply(float v) const final { return relu6_ew(v); } + }; + + Func f; + + eltwise_unary(relu6, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/ReLU6.test.cpp b/compiler/locomotiv/src/Node/ReLU6.test.cpp new file mode 100644 index 000000000..07f6af23f --- /dev/null +++ b/compiler/locomotiv/src/Node/ReLU6.test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_ReLU6, f32) +{ + // Make pull-relu6 graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2, 2}); + auto relu6 = g->nodes()->create(); + relu6->input(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{2, 2}); + pull_buf.at(Index{0, 0}) = -5.0f; + pull_buf.at(Index{0, 1}) = 6.0f; + pull_buf.at(Index{1, 0}) = 7.0f; + pull_buf.at(Index{1, 1}) = -8.0f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(relu6); + + auto relu6_data = locomotiv::annot_data(relu6); + ASSERT_NE(relu6_data, nullptr); + ASSERT_EQ(relu6_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(relu6_data->shape()), Shape({2, 2})); + ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{0, 0}), 0.0f); + ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{0, 1}), 6.0f); + ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{1, 0}), 6.0f); + ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{1, 1}), 0.0f); + + ASSERT_EQ(locomotiv::annot_domain(relu6), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/Reshape.cpp b/compiler/locomotiv/src/Node/Reshape.cpp new file mode 100644 index 000000000..ac1672024 --- /dev/null +++ b/compiler/locomotiv/src/Node/Reshape.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::num_elements; + +#include +#include +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Reshape *reshape) +{ + auto input_data = annot_data(reshape->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(reshape->input()) == loco::Domain::Tensor, + "Input domain of Reshape is not Tensor"); + + std::unique_ptr reshape_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto *input_shape = input_data->shape(); + + using Shape = nncc::core::ADT::tensor::Shape; + std::unique_ptr output_shape(new Shape()); + + output_shape->resize(reshape->rank()); + for (uint32_t axis = 0; axis < output_shape->rank(); ++axis) + { + output_shape->dim(axis) = reshape->dim(axis).value(); + } + + auto reshape_bufptr = make_buffer(*output_shape); + + float *input_ptr = const_cast(input_bufptr->base()); + uint64_t input_len = num_elements(*input_shape) * sizeof(float); + + float *output_ptr = reshape_bufptr.base(); + + assert(input_len == num_elements(*output_shape) * sizeof(float)); + memcpy(output_ptr, input_ptr, input_len); + + reshape_data = make_data(reshape_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(reshape_data != nullptr); + annot_data(reshape, std::move(reshape_data)); + annot_domain(reshape, annot_domain(reshape->input())); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Reshape.test.cpp b/compiler/locomotiv/src/Node/Reshape.test.cpp new file mode 100644 index 000000000..8e54a16df --- /dev/null +++ b/compiler/locomotiv/src/Node/Reshape.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Reshape, f32) +{ + // Make pull-reshape graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({4}); + auto reshape = g->nodes()->create>(); + reshape->input(pull); + reshape->shape({2, 2}); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{4}); + pull_buf.at(Index{0}) = 0.0f; + pull_buf.at(Index{1}) = 1.1f; + pull_buf.at(Index{2}) = 2.2f; + pull_buf.at(Index{3}) = 3.3f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(reshape); + + auto reshape_data = locomotiv::annot_data(reshape); + ASSERT_NE(reshape_data, nullptr); + ASSERT_EQ(reshape_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(reshape_data->shape()), (Shape{2, 2})); + ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{0, 0}), 0.0f); + ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{0, 1}), 1.1f); + ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{1, 0}), 2.2f); + ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{1, 1}), 3.3f); + + ASSERT_EQ(locomotiv::annot_domain(reshape), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/Softmax.cpp b/compiler/locomotiv/src/Node/Softmax.cpp new file mode 100644 index 000000000..352598b27 --- /dev/null +++ b/compiler/locomotiv/src/Node/Softmax.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Shape; + +#include +#include +#include + +namespace +{ + +Index reduce_index(const Index &index, uint32_t axis) +{ + Index r_index; + + r_index.resize(index.rank()); + for (uint32_t i = 0; i < index.rank(); ++i) + r_index.at(i) = index.at(i); + r_index.at(axis) = 0; + + return r_index; +} + +Shape reduce_shape(const Shape &shape, uint32_t axis) +{ + Shape r_shape; + + r_shape.resize(shape.rank()); + for (uint32_t i = 0; i < shape.rank(); ++i) + r_shape.dim(i) = shape.dim(i); + r_shape.dim(axis) = 1; + + return r_shape; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TensorSoftmax *softmax) +{ + auto input_data = annot_data(softmax->input()); + + validate(input_data, "Input not ready"); + validate(annot_domain(softmax->input()) == loco::Domain::Tensor, + "Input domain of TensorSoftmax is not Tensor"); + + std::unique_ptr softmax_data = nullptr; + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto axis = softmax->axis(); + + auto *input_shape = input_data->shape(); + auto input_bufptr = input_data->as_f32_bufptr(); + auto softmax_buf = make_buffer(*input_data->shape()); + + auto reduce_sum_shape = reduce_shape(*input_shape, axis); + auto reduce_sum_bufptr = make_buffer(reduce_sum_shape); + + for (IndexEnumerator e{*input_shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + const auto r_index = reduce_index(index, axis); + + reduce_sum_bufptr.at(r_index) += exp(input_bufptr->at(index)); + } + + for (IndexEnumerator e{*input_shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + const auto r_index = reduce_index(index, axis); + + softmax_buf.at(index) = exp(input_bufptr->at(index)) / reduce_sum_bufptr.at(r_index); + } + + softmax_data = make_data(softmax_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(softmax_data != nullptr); + annot_data(softmax, std::move(softmax_data)); + annot_domain(softmax, annot_domain(softmax->input())); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Softmax.test.cpp b/compiler/locomotiv/src/Node/Softmax.test.cpp new file mode 100644 index 000000000..21d240275 --- /dev/null +++ b/compiler/locomotiv/src/Node/Softmax.test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Softmax, f32) +{ + // Make pull-softmax graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({2, 2}); + auto softmax = g->nodes()->create(); + softmax->input(pull); + softmax->axis(1); + + // Make and assign data to pull node + auto pull_buf = make_buffer({2, 2}); + pull_buf.at(Index{0, 0}) = 1.1f; + pull_buf.at(Index{0, 1}) = 1.1f; + pull_buf.at(Index{1, 0}) = 3.3f; + pull_buf.at(Index{1, 1}) = 3.3f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(softmax); + + auto kShape = Shape{2, 2}; + auto softmax_data = locomotiv::annot_data(softmax); + ASSERT_NE(softmax_data, nullptr); + ASSERT_EQ(softmax_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(softmax_data->shape()), kShape); + ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{0, 0}), 0.5f); + ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{0, 1}), 0.5f); + ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{1, 0}), 0.5f); + ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{1, 1}), 0.5f); + + ASSERT_EQ(locomotiv::annot_domain(softmax), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/Tanh.cpp b/compiler/locomotiv/src/Node/Tanh.cpp new file mode 100644 index 000000000..78d329e7c --- /dev/null +++ b/compiler/locomotiv/src/Node/Tanh.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include + +namespace +{ + +struct Func final : public locomotiv::UnaryFunc +{ + float apply(float v) const final { return std::tanh(v); } +}; + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::Tanh *tanh) +{ + Func f; + + eltwise_unary(tanh, f); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/Tanh.test.cpp b/compiler/locomotiv/src/Node/Tanh.test.cpp new file mode 100644 index 000000000..78c3a13ba --- /dev/null +++ b/compiler/locomotiv/src/Node/Tanh.test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Tanh, f32) +{ + // Make pull-Tanh graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({3}); + auto tanh = g->nodes()->create(); + tanh->input(pull); + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{3}); + pull_buf.at(Index{0}) = 0.0f; + pull_buf.at(Index{1}) = 1.0f; + pull_buf.at(Index{2}) = -1.0f; + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(tanh); + + auto tanh_data = locomotiv::annot_data(tanh); + ASSERT_NE(tanh_data, nullptr); + ASSERT_EQ(tanh_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(tanh_data->shape()), Shape{3}); + ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{0}), 0.0f); + ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{1}), 0.761594f); + ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{2}), -0.761594f); + + ASSERT_EQ(locomotiv::annot_domain(tanh), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/TensorBroadcast.cpp b/compiler/locomotiv/src/Node/TensorBroadcast.cpp new file mode 100644 index 000000000..010ca6821 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorBroadcast.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Shape; + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TensorBroadcast *tensor_broadcast) +{ + auto input_data = annot_data(tensor_broadcast->input()); + + // Calculate output shape + Shape input_shape = *(input_data->shape()); + + // TODO Reuse "ShapeInferenceService" + Shape output_shape; + + output_shape.resize(input_shape.rank()); + for (uint32_t axis = 0; axis < input_shape.rank(); ++axis) + { + if (tensor_broadcast->mapping()->defined(axis)) + { + assert(input_shape.dim(axis) == 1); // Required by TensorBroadcast definition + output_shape.dim(axis) = tensor_broadcast->mapping()->dim(axis).value(); + } + else + { + output_shape.dim(axis) = input_shape.dim(axis); + } + } + + assert(input_shape.rank() == output_shape.rank()); + + uint32_t const rank = input_shape.rank(); + + std::unique_ptr output_data = nullptr; + + switch (input_data->dtype()) + { + // TODO Use type-generic implementation! + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto output_buf = make_buffer(output_shape); + + for (IndexEnumerator e{output_shape}; e.valid(); e.advance()) + { + auto input_index = e.current(); + const auto &output_index = e.current(); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + if (tensor_broadcast->mapping()->defined(axis)) + { + input_index.at(axis) = 0; + } + } + + output_buf.at(output_index) = input_bufptr->at(input_index); + } + + output_data = make_data(output_buf); + break; + } + default: + throw std::runtime_error("Not yet supported"); + } + + assert(output_data != nullptr); + annot_data(tensor_broadcast, std::move(output_data)); + annot_domain(tensor_broadcast, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp b/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp new file mode 100644 index 000000000..e8347d737 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_TensorBroadcast, f32) +{ + // Create a sample graph w/ TensorBroadcast + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->shape({1, 1}); + auto broadcast = g->nodes()->create(); + broadcast->input(pull); + broadcast->mapping()->dim(0) = 2; + + // Make and assign data to pull node + auto pull_buf = make_buffer(Shape{1, 1}); + pull_buf.at(Index{0, 0}) = -1.0f; + + auto pull_data = locomotiv::make_data(pull_buf); + locomotiv::annot_data(pull, std::move(pull_data)); + locomotiv::annot_domain(pull, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(broadcast); + + auto broadcast_data = locomotiv::annot_data(broadcast); + ASSERT_NE(broadcast_data, nullptr); + ASSERT_EQ(broadcast_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ((*(broadcast_data->shape())), (Shape{2, 1})); + ASSERT_FLOAT_EQ(broadcast_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f); + ASSERT_FLOAT_EQ(broadcast_data->as_f32_bufptr()->at(Index{1, 0}), -1.0f); + + ASSERT_EQ(locomotiv::annot_domain(broadcast), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/TensorConcat.cpp b/compiler/locomotiv/src/Node/TensorConcat.cpp new file mode 100644 index 000000000..5097e55c6 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorConcat.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Shape; + +#include +#include + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TensorConcat *tensor_concat) +{ + auto lhs_data = annot_data(tensor_concat->lhs()); + auto rhs_data = annot_data(tensor_concat->rhs()); + auto axis = tensor_concat->axis(); + + validate(lhs_data && rhs_data, "Ingredient not ready"); + validate(lhs_data->dtype() == rhs_data->dtype(), "lhs and rhs of Concat should have same dtype"); + + validate(annot_domain(tensor_concat->lhs()) == loco::Domain::Tensor && + annot_domain(tensor_concat->rhs()) == loco::Domain::Tensor, + "Some ingredients of TensorConcat is not Tensor"); + + // Calculate output shape + Shape lhs_shape = *lhs_data->shape(); + Shape rhs_shape = *rhs_data->shape(); + Shape concat_shape; + + assert(lhs_shape.rank() == rhs_shape.rank()); + concat_shape.resize(lhs_shape.rank()); + for (uint32_t index = 0; index < lhs_shape.rank(); ++index) + { + if (index == axis) + concat_shape.dim(index) = lhs_shape.dim(index) + rhs_shape.dim(index); + else + { + assert(lhs_shape.dim(index) == rhs_shape.dim(index)); + concat_shape.dim(index) = lhs_shape.dim(index); + } + } + auto left_dim_size = lhs_shape.dim(axis); + + // Copy data from two inputs LHS and RHS to Concat + std::unique_ptr concat_data = nullptr; + switch (lhs_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto lhs_bufptr = lhs_data->as_f32_bufptr(); + auto rhs_bufptr = rhs_data->as_f32_bufptr(); + auto concat_buf = make_buffer(concat_shape); + + for (IndexEnumerator e{concat_shape}; e.valid(); e.advance()) + { + const auto &e_index = e.current(); + + if (e_index.at(axis) < left_dim_size) + { + // Left index is same as output index + concat_buf.at(e_index) = lhs_bufptr->at(e_index); + } + else + { + // Adjust right index to valid range + Index r_index = e_index; + r_index.at(axis) -= left_dim_size; + concat_buf.at(e_index) = rhs_bufptr->at(r_index); + } + } + + concat_data = make_data(concat_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(concat_data != nullptr); + annot_data(tensor_concat, std::move(concat_data)); + annot_domain(tensor_concat, loco::Domain::Tensor); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/TensorConcat.test.cpp b/compiler/locomotiv/src/Node/TensorConcat.test.cpp new file mode 100644 index 000000000..d71b51524 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorConcat.test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_TensorConcat, f32) +{ + // Make (pull, pull)-concat graph + auto g = loco::make_graph(); + auto pull_l = g->nodes()->create(); + pull_l->dtype(loco::DataType::FLOAT32); + pull_l->shape({1, 2}); + auto pull_r = g->nodes()->create(); + pull_r->dtype(loco::DataType::FLOAT32); + pull_r->shape({1, 2}); + auto tconcat = g->nodes()->create(); + tconcat->lhs(pull_l); + tconcat->rhs(pull_r); + tconcat->axis(0); + + // Make and assign data to pull node + auto pull_l_buf = make_buffer(Shape{1, 2}); + pull_l_buf.at(Index{0, 0}) = -1.0f; + pull_l_buf.at(Index{0, 1}) = -2.0f; + auto pull_r_buf = make_buffer(Shape{1, 2}); + pull_r_buf.at(Index{0, 0}) = 3.0f; + pull_r_buf.at(Index{0, 1}) = 4.0f; + + auto pull_l_data = locomotiv::make_data(pull_l_buf); + locomotiv::annot_data(pull_l, std::move(pull_l_data)); + locomotiv::annot_domain(pull_l, loco::Domain::Tensor); + auto pull_r_data = locomotiv::make_data(pull_r_buf); + locomotiv::annot_data(pull_r, std::move(pull_r_data)); + locomotiv::annot_domain(pull_r, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(tconcat); + + auto concat_data = locomotiv::annot_data(tconcat); + ASSERT_NE(concat_data, nullptr); + ASSERT_EQ(concat_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ((*(concat_data->shape())), (Shape{2, 2})); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 1}), -2.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 0}), 3.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 1}), 4.0f); + + ASSERT_EQ(locomotiv::annot_domain(tconcat), loco::Domain::Tensor); +} + +TEST(NodeExecution_TensorConcat, f32_2) +{ + // Make (pull, pull)-concat graph + auto g = loco::make_graph(); + auto pull_l = g->nodes()->create(); + pull_l->dtype(loco::DataType::FLOAT32); + pull_l->shape({1, 2}); + auto pull_r = g->nodes()->create(); + pull_r->dtype(loco::DataType::FLOAT32); + pull_r->shape({3, 2}); + auto tconcat = g->nodes()->create(); + tconcat->lhs(pull_l); + tconcat->rhs(pull_r); + tconcat->axis(0); + + // Make and assign data to pull node + auto pull_l_buf = make_buffer(Shape{1, 2}); + pull_l_buf.at(Index{0, 0}) = -1.0f; + pull_l_buf.at(Index{0, 1}) = -2.0f; + auto pull_r_buf = make_buffer(Shape{3, 2}); + pull_r_buf.at(Index{0, 0}) = 3.0f; + pull_r_buf.at(Index{0, 1}) = 4.0f; + pull_r_buf.at(Index{1, 0}) = -3.0f; + pull_r_buf.at(Index{1, 1}) = -4.0f; + pull_r_buf.at(Index{2, 0}) = 5.0f; + pull_r_buf.at(Index{2, 1}) = 6.0f; + + auto pull_l_data = locomotiv::make_data(pull_l_buf); + locomotiv::annot_data(pull_l, std::move(pull_l_data)); + locomotiv::annot_domain(pull_l, loco::Domain::Tensor); + auto pull_r_data = locomotiv::make_data(pull_r_buf); + locomotiv::annot_data(pull_r, std::move(pull_r_data)); + locomotiv::annot_domain(pull_r, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(tconcat); + + auto concat_data = locomotiv::annot_data(tconcat); + ASSERT_NE(concat_data, nullptr); + ASSERT_EQ(concat_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ((*(concat_data->shape())), (Shape{4, 2})); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 1}), -2.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 0}), 3.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 1}), 4.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{2, 0}), -3.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{2, 1}), -4.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{3, 0}), 5.0f); + ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{3, 1}), 6.0f); + + ASSERT_EQ(locomotiv::annot_domain(tconcat), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/TensorConstantPad.cpp b/compiler/locomotiv/src/Node/TensorConstantPad.cpp new file mode 100644 index 000000000..989afaf94 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorConstantPad.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include + +#include + +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TensorConstantPad *pad) +{ + auto input_data = annot_data(pad->input()); + auto input_domain = annot_domain(pad->input()); + validate(input_data, "Input not ready"); + validate(input_domain == loco::Domain::Tensor, "Input domain of TensorConstantPad is not Tensor"); + + auto input_shape = input_data->shape(); + const uint32_t input_rank = input_shape->rank(); + + auto padding = pad->padding(); + validate(input_rank == padding->rank(), "input and padding should have same rank"); + + auto constant_node = pad->constant(); + auto constant_data = annot_data(constant_node); + validate(constant_data->dtype() == input_data->dtype(), "constant and input have same data type"); + validate(constant_data->shape()->rank() == 1 && constant_data->shape()->dim(0) == 1, + "constant should have one rank with one dimension at zero axis"); + + std::unique_ptr pad_data = nullptr; + Index base_index; + base_index.resize(input_rank); + + // Tensor is padded by relocating its base. + // padded output index = input index + base index + for (uint32_t axis = 0; axis < padding->rank(); axis++) + { + base_index.at(axis) = padding->front(axis); + } + + // calculate output shape + Shape output_shape; + output_shape.resize(input_rank); + for (uint32_t i = 0; i < input_rank; i++) + { + output_shape.dim(i) = input_shape->dim(i) + padding->front(i) + padding->back(i); + } + + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_buf = input_data->as_f32_bufptr(); + auto constant_data_buf = constant_data->as_f32_bufptr(); + const auto constant_value = constant_data_buf->at(Index{0}); + + auto output_buf = make_buffer(output_shape); + + for (IndexEnumerator ie{*input_shape}, oe{output_shape}; oe.valid(); oe.advance()) + { + auto input_index = ie.current(); + auto output_index = oe.current(); + + if ((input_index + base_index) == output_index) + { + output_buf.at(output_index) = input_buf->at(input_index); + ie.advance(); + } + else + { + output_buf.at(output_index) = constant_value; + } + } + + pad_data = make_data(output_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(pad_data != nullptr); + annot_data(pad, std::move(pad_data)); + annot_domain(pad, annot_domain(pad->input())); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp b/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp new file mode 100644 index 000000000..0f60c5f85 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Shape; + +TEST(NodeExecution_Pad, tensor_constant_pad_4_dim) +{ + auto g = loco::make_graph(); + + auto inputTensor = g->nodes()->create(); + inputTensor->dtype(loco::DataType::FLOAT32); + inputTensor->shape({1, 2, 2, 1}); + auto inputTensor_buf = make_buffer(Shape{1, 2, 2, 1}); + inputTensor_buf.at(Index{0, 0, 0, 0}) = 1.0f; + inputTensor_buf.at(Index{0, 0, 1, 0}) = 2.0f; + inputTensor_buf.at(Index{0, 1, 0, 0}) = 3.0f; + inputTensor_buf.at(Index{0, 1, 1, 0}) = 4.0f; + auto inputTensor_data = locomotiv::make_data(inputTensor_buf); + locomotiv::annot_data(inputTensor, std::move(inputTensor_data)); + locomotiv::annot_domain(inputTensor, loco::Domain::Tensor); + + auto constant = g->nodes()->create(); + constant->dtype(loco::DataType::FLOAT32); + constant->shape({1}); + auto constant_buf = make_buffer(Shape{1}); + constant_buf.at(Index{0}) = 0.0f; + auto constant_data = locomotiv::make_data(constant_buf); + locomotiv::annot_data(constant, std::move(constant_data)); + locomotiv::annot_domain(constant, loco::Domain::Tensor); + + auto pad = g->nodes()->create(); + pad->input(inputTensor); + pad->constant(constant); + + auto padding = pad->padding(); + padding->rank(4); + padding->front(0) = 0; + padding->back(0) = 0; + padding->front(1) = 3; + padding->back(1) = 1; + padding->front(2) = 1; + padding->back(2) = 1; + padding->front(3) = 0; + padding->back(3) = 0; + + locomotiv::NodeExecution::get().run(pad); + + auto pad_data = locomotiv::annot_data(pad); + ASSERT_NE(pad_data, nullptr); + ASSERT_EQ(pad_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(pad_data->shape()), Shape({1, 6, 4, 1})); + + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0, 3, 1, 0}), 1.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0, 3, 2, 0}), 2.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0, 4, 1, 0}), 3.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0, 4, 2, 0}), 4.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0, 0, 0, 0}), 0.0f); + + ASSERT_EQ(locomotiv::annot_domain(pad), loco::Domain::Tensor); +} + +TEST(NodeExecution_Pad, tensor_constant_pad_1_dim) +{ + auto g = loco::make_graph(); + + auto inputTensor = g->nodes()->create(); + inputTensor->dtype(loco::DataType::FLOAT32); + inputTensor->shape({3}); + auto inputTensor_buf = make_buffer(Shape{3}); + inputTensor_buf.at(Index{0}) = 1.0f; + inputTensor_buf.at(Index{1}) = 5.0f; + inputTensor_buf.at(Index{2}) = 3.0f; + auto inputTensor_data = locomotiv::make_data(inputTensor_buf); + locomotiv::annot_data(inputTensor, std::move(inputTensor_data)); + locomotiv::annot_domain(inputTensor, loco::Domain::Tensor); + + auto constant = g->nodes()->create(); + constant->dtype(loco::DataType::FLOAT32); + constant->shape({1}); + auto constant_buf = make_buffer(Shape{1}); + constant_buf.at(Index{0}) = 0.0f; + auto constant_data = locomotiv::make_data(constant_buf); + locomotiv::annot_data(constant, std::move(constant_data)); + locomotiv::annot_domain(constant, loco::Domain::Tensor); + + auto pad = g->nodes()->create(); + pad->input(inputTensor); + pad->constant(constant); + auto padding = pad->padding(); + padding->rank(1); + padding->front(0) = 2; + padding->back(0) = 1; + + locomotiv::NodeExecution::get().run(pad); + + auto pad_data = locomotiv::annot_data(pad); + ASSERT_NE(pad_data, nullptr); + ASSERT_EQ(pad_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(pad_data->shape()), Shape({6})); + + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{0}), 0.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1}), 0.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{2}), 1.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{3}), 5.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{4}), 3.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{5}), 0.0f); + + ASSERT_EQ(locomotiv::annot_domain(pad), loco::Domain::Tensor); +} + +TEST(NodeExecution_Pad, tensor_constant_pad_6_dim) +{ + auto g = loco::make_graph(); + + auto inputTensor = g->nodes()->create(); + inputTensor->dtype(loco::DataType::FLOAT32); + inputTensor->shape({2, 1, 3, 2, 1, 2}); + auto inputTensor_buf = make_buffer(Shape{2, 1, 3, 2, 1, 2}); + int a, b, c, d, e, f; + float dummy = 1.0f; + for (uint32_t a = 0; a < 2; a++) + { + for (uint32_t b = 0; b < 1; b++) + { + for (uint32_t c = 0; c < 3; c++) + { + for (uint32_t d = 0; d < 2; d++) + { + for (uint32_t e = 0; e < 1; e++) + { + for (uint32_t f = 0; f < 2; f++) + { + inputTensor_buf.at(Index{a, b, c, d, e, f}) = dummy++; + } + } + } + } + } + } + auto inputTensor_data = locomotiv::make_data(inputTensor_buf); + locomotiv::annot_data(inputTensor, std::move(inputTensor_data)); + locomotiv::annot_domain(inputTensor, loco::Domain::Tensor); + + auto constant = g->nodes()->create(); + constant->dtype(loco::DataType::FLOAT32); + constant->shape({1}); + auto constant_buf = make_buffer(Shape{1}); + constant_buf.at(Index{0}) = 0.0f; + auto constant_data = locomotiv::make_data(constant_buf); + locomotiv::annot_data(constant, std::move(constant_data)); + locomotiv::annot_domain(constant, loco::Domain::Tensor); + + auto pad = g->nodes()->create(); + pad->input(inputTensor); + pad->constant(constant); + auto padding = pad->padding(); + + padding->rank(6); + padding->front(0) = 1; + padding->back(0) = 1; + padding->front(1) = 0; + padding->back(1) = 0; + padding->front(2) = 1; + padding->back(2) = 2; + padding->front(3) = 2; + padding->back(3) = 1; + padding->front(4) = 0; + padding->back(4) = 0; + padding->front(5) = 1; + padding->back(5) = 2; + + locomotiv::NodeExecution::get().run(pad); + + auto pad_data = locomotiv::annot_data(pad); + ASSERT_NE(pad_data, nullptr); + ASSERT_EQ(pad_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(pad_data->shape()), Shape({4, 1, 6, 5, 1, 5})); + + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 2, 0, 1}), 1.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 2, 0, 2}), 2.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 3, 0, 1}), 3.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 3, 0, 2}), 4.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 2, 0, 1}), 5.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 2, 0, 2}), 6.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 3, 0, 1}), 7.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 3, 0, 2}), 8.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 3, 2, 0, 1}), 9.0f); + ASSERT_FLOAT_EQ(pad_data->as_f32_bufptr()->at(Index{1, 0, 3, 2, 0, 2}), 10.0f); + + ASSERT_EQ(locomotiv::annot_domain(pad), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/TensorReduce.cpp b/compiler/locomotiv/src/Node/TensorReduce.cpp new file mode 100644 index 000000000..fae7a75c5 --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorReduce.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Buffer; + +#include +#include + +namespace +{ + +Index reduced_index(const Index &index, const loco::TensorAxisSet &axes) +{ + Index r_index; + + r_index.resize(index.rank()); + for (uint32_t i = 0; i < index.rank(); ++i) + r_index.at(i) = (axes.defined(i)) ? 0 : index.at(i); + + return r_index; +} + +Shape reduced_shape(const Shape &shape, const loco::TensorAxisSet &axes) +{ + Shape r_shape; + + r_shape.resize(shape.rank()); + for (uint32_t i = 0; i < shape.rank(); ++i) + r_shape.dim(i) = (axes.defined(i)) ? 1 : shape.dim(i); + + return r_shape; +} + +} // namespace + +namespace +{ + +template struct ReduceFunction +{ + static void apply(Buffer &lhs, const Buffer &rhs, const loco::TensorAxisSet &axes) + { + throw std::runtime_error("Not supported ReduceFunc type"); + } +}; + +template struct ReduceFunction +{ + static void apply(Buffer &lhs, const Buffer &rhs, const loco::TensorAxisSet &axes) + { + for (IndexEnumerator e{rhs.shape()}; e.valid(); e.advance()) + { + const auto &index = e.current(); + const auto r_index = reduced_index(index, axes); + + lhs.at(r_index) += rhs.at(index); + } + + uint32_t r_cnt = 1; + for (uint32_t i = 0; i < rhs.shape().rank(); ++i) + if (axes.defined(i)) + r_cnt *= rhs.shape().dim(i); + + for (IndexEnumerator e{lhs.shape()}; e.valid(); e.advance()) + { + const auto &index = e.current(); + lhs.at(index) /= static_cast(r_cnt); + } + } +}; + +template +void apply(Buffer &lhs, const Buffer &rhs, const loco::TensorReduce &node) +{ + switch (node.func()) + { + case loco::ReduceFunc::Mean: + ReduceFunction::apply(lhs, rhs, *node.axes()); + break; + + // TODO Support more ReduceFunc type + default: + break; + } +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TensorReduce *node) +{ + auto input_data = annot_data(node->input()); + auto input_shape = input_data->shape(); + + validate(input_data, "Input not ready"); + validate(annot_domain(node->input()) == loco::Domain::Tensor, + "Input domain of TensorReduce is not Tensor"); + + std::unique_ptr reduce_data = nullptr; + Shape r_shape = reduced_shape(*input_shape, *node->axes()); + switch (input_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto reduce_buf = make_buffer(r_shape); + + apply(reduce_buf, *input_bufptr, *node); + + reduce_data = make_data(reduce_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(reduce_data != nullptr); + annot_data(node, std::move(reduce_data)); + annot_domain(node, annot_domain(node->input())); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/TensorReduce.test.cpp b/compiler/locomotiv/src/Node/TensorReduce.test.cpp new file mode 100644 index 000000000..68398cacd --- /dev/null +++ b/compiler/locomotiv/src/Node/TensorReduce.test.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeExecution_Fixed_Reduce_Mean, f32_0) +{ + // Make pull-TensorReduce(Mean) graph + auto g = loco::make_graph(); + auto pull_input = g->nodes()->create(); + pull_input->dtype(loco::DataType::FLOAT32); + pull_input->shape({1, 2, 2}); + auto reduce_node = g->nodes()->create(); + reduce_node->input(pull_input); + reduce_node->axes()->insert(0); + reduce_node->axes()->insert(1); + reduce_node->func(loco::ReduceFunc::Mean); + + // Make and assign data to pull node + auto pull_input_buf = make_buffer({1, 2, 2}); + pull_input_buf.at(Index{0, 0, 0}) = 1.1f; + pull_input_buf.at(Index{0, 0, 1}) = 2.2f; + pull_input_buf.at(Index{0, 1, 0}) = 5.5f; + pull_input_buf.at(Index{0, 1, 1}) = 6.6f; + auto pull_input_data = locomotiv::make_data(pull_input_buf); + locomotiv::annot_data(pull_input, std::move(pull_input_data)); + locomotiv::annot_domain(pull_input, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(reduce_node); + + auto kShape = Shape{1, 1, 2}; + auto reduce_data = locomotiv::annot_data(reduce_node); + ASSERT_NE(reduce_data, nullptr); + ASSERT_EQ(reduce_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(reduce_data->shape()), kShape); + ASSERT_FLOAT_EQ(reduce_data->as_f32_bufptr()->at(Index{0, 0, 0}), 3.3f); + ASSERT_FLOAT_EQ(reduce_data->as_f32_bufptr()->at(Index{0, 0, 1}), 4.4f); + + ASSERT_EQ(locomotiv::annot_domain(reduce_node), loco::Domain::Tensor); +} + +TEST(NodeExecution_Fixed_Reduce_Mean, f32_1) +{ + // Make pull-TensorReduce(Mean) graph + auto g = loco::make_graph(); + auto pull_input = g->nodes()->create(); + pull_input->dtype(loco::DataType::FLOAT32); + pull_input->shape({1, 2, 2}); + auto reduce_node = g->nodes()->create(); + reduce_node->input(pull_input); + reduce_node->axes()->insert(1); + reduce_node->axes()->insert(2); + reduce_node->func(loco::ReduceFunc::Mean); + + // Make and assign data to pull node + auto pull_input_buf = make_buffer({1, 2, 2}); + pull_input_buf.at(Index{0, 0, 0}) = 1.1f; + pull_input_buf.at(Index{0, 0, 1}) = 2.2f; + pull_input_buf.at(Index{0, 1, 0}) = 5.5f; + pull_input_buf.at(Index{0, 1, 1}) = 6.6f; + auto pull_input_data = locomotiv::make_data(pull_input_buf); + locomotiv::annot_data(pull_input, std::move(pull_input_data)); + locomotiv::annot_domain(pull_input, loco::Domain::Tensor); + + locomotiv::NodeExecution::get().run(reduce_node); + + auto kShape = Shape{1, 1, 1}; + auto reduce_data = locomotiv::annot_data(reduce_node); + ASSERT_NE(reduce_data, nullptr); + ASSERT_EQ(reduce_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(reduce_data->shape()), kShape); + ASSERT_FLOAT_EQ(reduce_data->as_f32_bufptr()->at(Index{0, 0, 0}), 3.85f); + + ASSERT_EQ(locomotiv::annot_domain(reduce_node), loco::Domain::Tensor); +} diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.cpp new file mode 100644 index 000000000..3ea4f071d --- /dev/null +++ b/compiler/locomotiv/src/Node/TransposedConv2D.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDataImpl.h" +#include "NodeDomain.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ + +using nncc::core::ADT::tensor::Buffer; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +/** + * @brief Compute 1D output size for transposed convolution based on given 1D arguments. + * + * @param whole_pad Sum of front and rear pad + */ +inline uint32_t compute_transposed_out_size(uint32_t input_size, uint32_t whole_pad, + uint32_t filter_size, uint32_t stride) +{ + return stride * (input_size - 1) + filter_size - whole_pad; +} + +/** + * @brief Calculates TransposedConv2D + * @note Both input_buf and filter_buf have NHWC format + */ +template +Buffer calc_tr_conv2D(const loco::TransposedConv2D *tr_conv2d, + const Buffer *input_buf, const Buffer *filter_buf) +{ + auto input_shape = input_buf->shape(); + auto filter_shape = filter_buf->shape(); + + locomotiv::validate(input_shape.rank() == 4, "ifm rank must be 4"); + locomotiv::validate(filter_shape.rank() == 4, "filter rank must be 4"); + locomotiv::validate(input_shape.dim(3) /* depth of input */ == + filter_shape.dim(3) /* depth of filter */, + "channel value mismatch"); + + const uint32_t input_height = input_shape.dim(1); + const uint32_t input_width = input_shape.dim(2); + + const uint32_t filter_height = filter_shape.dim(1); + const uint32_t filter_width = filter_shape.dim(2); + + const uint32_t stride_width = tr_conv2d->stride()->horizontal(); + const uint32_t stride_height = tr_conv2d->stride()->vertical(); + + const uint32_t pad_top = tr_conv2d->pad()->top(); + const uint32_t pad_bottom = tr_conv2d->pad()->bottom(); + + const uint32_t pad_left = tr_conv2d->pad()->left(); + const uint32_t pad_right = tr_conv2d->pad()->right(); + + // TODO Support dilations + + const uint32_t output_height = + compute_transposed_out_size(input_height, pad_top + pad_bottom, filter_height, stride_height); + const uint32_t output_width = + compute_transposed_out_size(input_width, pad_left + pad_right, filter_width, stride_width); + + const uint32_t batches = input_shape.dim(0); + const uint32_t input_depth = input_shape.dim(3); + const uint32_t output_depth = filter_shape.dim(0); // count of filter + + Shape output_shape{batches, output_height, output_width, output_depth}; + auto output_buf = make_buffer(output_shape); + + // initialize output + for (IndexEnumerator e{output_shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + output_buf.at(index) = static_cast(0); + } + + // Loop through input elements one at a time. + for (uint32_t batch = 0; batch < batches; ++batch) + { + for (uint32_t in_y = 0; in_y < input_height; ++in_y) + { + for (uint32_t in_x = 0; in_x < input_width; ++in_x) + { + for (uint32_t in_channel = 0; in_channel < input_depth; ++in_channel) + { + // Loop through the output elements it will influence + const int out_x_origin = (in_x * stride_width) - pad_left; + const int out_y_origin = (in_y * stride_height) - pad_top; + for (uint32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (uint32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + for (uint32_t out_channel = 0; out_channel < output_depth; ++out_channel) + { + // Compute output element location + const int out_x = out_x_origin + filter_x; + const int out_y = out_y_origin + filter_y; + // We cannot accumulate out of bounds + if ((out_x >= 0) && ((unsigned)out_x < output_width) && (out_y >= 0) && + ((unsigned)out_y < output_height)) + { + auto input_value = input_buf->at(Index({batch, in_y, in_x, in_channel})); + auto filter_value = + filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); + output_buf.at(Index({batch, (unsigned)out_y, (unsigned)out_x, out_channel})) += + input_value * filter_value; + } + } + } + } + } + } + } + } + return output_buf; +} + +} // namespace + +namespace locomotiv +{ + +void NodeExecution::execute(loco::TransposedConv2D *tr_conv2d) +{ + auto ifm_data = annot_data(tr_conv2d->ifm()); + auto ker_data = annot_data(tr_conv2d->ker()); + + validate(ifm_data, "Can't find input data of TransposedConv2D"); + validate(ifm_data->shape()->rank() == 4, "ifm rank must be 4"); + + validate(ker_data, "Can't find kernel data of TransposedConv2D"); + validate(ker_data->shape()->rank() == 4, "Kernel rank must be 4"); + + validate(annot_domain(tr_conv2d->ifm()) == loco::Domain::Feature, + "IFM of TransposedConv2D is not feature"); + validate(annot_domain(tr_conv2d->ker()) == loco::Domain::Filter, + "Kernel of TransposedConv2D is not filter"); + + std::unique_ptr tr_conv2d_result = nullptr; + + if (ifm_data->dtype() == loco::DataType::FLOAT32 && ker_data->dtype() == loco::DataType::FLOAT32) + { + auto ifm_buf = ifm_data->as_f32_bufptr(); + auto ker_buf = ker_data->as_f32_bufptr(); + + auto tr_conv2d_buf = calc_tr_conv2D(tr_conv2d, ifm_buf, ker_buf); + + tr_conv2d_result = make_data(tr_conv2d_buf); + } + else + throw std::runtime_error("NYI for these DataTypes"); + + assert(tr_conv2d_result != nullptr); + + annot_data(tr_conv2d, std::move(tr_conv2d_result)); + annot_domain(tr_conv2d, loco::Domain::Feature); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp new file mode 100644 index 000000000..bd955a06b --- /dev/null +++ b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +#include +#include +#include +#include +#include "nncc/core/ADT/tensor/IndexEnumerator.h" + +#include + +namespace +{ +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; +using nncc::core::ADT::tensor::make_overlay; + +void run_test(const float *ifm, const float *ker, const float *expected_ofm, const Shape &ifm_shape, + const Shape ker_shape, const Shape ofm_shape, const uint32_t stride_v, + const uint32_t stride_h, const uint32_t pad_top = 0, const uint32_t pad_bottom = 0, + const uint32_t pad_left = 0, const uint32_t pad_right = 0) +{ + auto g = loco::make_graph(); + + // Fill output data of FeatureEncode from ifm + auto ifm_enc = g->nodes()->create(); + { + auto ifm_enc_buf = make_buffer(ifm_shape); + auto ifm_overlay = make_overlay(ifm_shape, const_cast(ifm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ifm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ifm_enc_buf.at(ind) = ifm_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ifm_enc_buf); + locomotiv::annot_data(ifm_enc, std::move(enc_data)); + locomotiv::annot_domain(ifm_enc, loco::Domain::Feature); + } + + // Fill output data of FilterEncode from ker + auto ker_enc = g->nodes()->create(); + { + auto ker_enc_buf = make_buffer(ker_shape); + auto ker_overlay = make_overlay(ker_shape, const_cast(ker)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ker_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ker_enc_buf.at(ind) = ker_overlay.at(ind); + } + + auto enc_data = locomotiv::make_data(ker_enc_buf); + locomotiv::annot_data(ker_enc, std::move(enc_data)); + locomotiv::annot_domain(ker_enc, loco::Domain::Filter); + } + + // build TransposedConv2D + auto tr_conv2d = g->nodes()->create(); + tr_conv2d->ifm(ifm_enc); + tr_conv2d->ker(ker_enc); + tr_conv2d->stride()->vertical(stride_v); + tr_conv2d->stride()->horizontal(stride_h); + tr_conv2d->pad()->top(pad_top); + tr_conv2d->pad()->bottom(pad_bottom); + tr_conv2d->pad()->left(pad_left); + tr_conv2d->pad()->right(pad_right); + + // run interpreter + locomotiv::NodeExecution::get().run(tr_conv2d); + + // get result of calculation + auto conv2d_result = locomotiv::annot_data(tr_conv2d); + + // check the result + ASSERT_NE(conv2d_result, nullptr); + ASSERT_TRUE(conv2d_result->dtype() == loco::DataType::FLOAT32); + ASSERT_TRUE(*(conv2d_result->shape()) == ofm_shape); + + auto ofm_overlay = + make_overlay(ofm_shape, const_cast(expected_ofm)); + for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) + { + const auto &ind = e.current(); + ASSERT_FLOAT_EQ(conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind)); + } + + ASSERT_EQ(locomotiv::annot_domain(tr_conv2d), loco::Domain::Feature); +} + +} // namespace + +// clang-format off +/* +ifm = tf.constant(1.1, shape = [1, 2, 2, 4]) +ker = tf.constant(2.2, shape = [3, 3, 2, 4]) +tr_conv = tf.nn.conv2d_transpose(ifm, ker, output_shape = (1, 5, 5, 2), strides = [1, 2, 2, 1], padding = "VALID") + +with tf.Session() as session: + tr_conv_data = session.run(tr_conv) + */ +TEST(NodeExecution_TransposedConv2D, f32) +{ + using nncc::core::ADT::tensor::Shape; + + float ifm[1 * 2 * 2 * 4]; + for (int n = 0; n < 1 * 2 * 2 * 4; n++) + ifm[n] = 1.1; + + float ker[2 * 3 * 3 * 4]; // NHWC + for (int n = 0; n < 2 * 3 * 3 * 4; n++) + ker[n] = 2.2; + + float ofm[1 * 5 * 5 * 2] = {9.68, 9.68, 9.68, 9.68, 19.36, 19.36, 9.68, 9.68, 9.68, 9.68, + 9.68, 9.68, 9.68, 9.68, 19.36, 19.36, 9.68, 9.68, 9.68, 9.68, + 19.36, 19.36, 19.36, 19.36, 38.72, 38.72, 19.36, 19.36, 19.36, 19.36, + 9.68, 9.68, 9.68, 9.68, 19.36, 19.36, 9.68, 9.68, 9.68, 9.68, + 9.68, 9.68, 9.68, 9.68, 19.36, 19.36, 9.68, 9.68, 9.68, 9.68}; + + run_test(ifm, ker, ofm, + Shape{1, 2, 2, 4}, Shape{2, 3, 3, 4}, Shape{1, 5, 5, 2}, // shapes of ifm, ker, ofm + 2, 2 // stride + ); +} +// clang-format on diff --git a/compiler/locomotiv/src/NodeData.cpp b/compiler/locomotiv/src/NodeData.cpp new file mode 100644 index 000000000..69ba4a1c2 --- /dev/null +++ b/compiler/locomotiv/src/NodeData.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" + +namespace locomotiv +{ + +template <> std::unique_ptr make_data(const NodeData::Buffer &buf) +{ + return std::unique_ptr(new NodeDataImpl(buf)); +} + +template <> std::unique_ptr make_data(const NodeData::Buffer &buf) +{ + return std::unique_ptr(new NodeDataImpl(buf)); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/NodeData.test.cpp b/compiler/locomotiv/src/NodeData.test.cpp new file mode 100644 index 000000000..b1c9832d5 --- /dev/null +++ b/compiler/locomotiv/src/NodeData.test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locomotiv/NodeData.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeData, as_s32_buffer_wrapper) +{ + const Shape shape{1}; + auto buf = make_buffer(shape); + buf.at(Index{0}) = 42; + + auto data = locomotiv::make_data(buf); + + ASSERT_EQ(data->dtype(), loco::DataType::S32); + ASSERT_EQ(*(data->shape()), shape); + ASSERT_EQ(data->as_s32_bufptr()->at(Index{0}), 42); +} + +TEST(NodeData, as_f32_buffer_wrapper) +{ + const Shape shape{1}; + auto buf = make_buffer(shape); + buf.at(Index{0}) = 3.14f; + + auto data = locomotiv::make_data(buf); + + ASSERT_EQ(data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(data->shape()), shape); + ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0}), 3.14f); +} diff --git a/compiler/locomotiv/src/NodeDataImpl.cpp b/compiler/locomotiv/src/NodeDataImpl.cpp new file mode 100644 index 000000000..2efebe5a9 --- /dev/null +++ b/compiler/locomotiv/src/NodeDataImpl.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeDataImpl.h" + +#include + +#include + +namespace +{ + +class NodeDataAnnotation final : public loco::NodeAnnotation +{ +public: + NodeDataAnnotation(std::unique_ptr &&data) : _data{std::move(data)} + { + // DO NOTHING + } + +public: + const locomotiv::NodeData *data(void) const { return _data.get(); } + +private: + std::unique_ptr _data; +}; + +} // namespace + +namespace locomotiv +{ + +template <> NodeDataImpl::NodeDataImpl(const Buffer &buf) +{ + _dtype = loco::DataType::S32; + _s32.reset(new Buffer(buf)); + _shape = const_cast(&(_s32->shape())); +} + +template <> NodeDataImpl::NodeDataImpl(const Buffer &buf) +{ + _dtype = loco::DataType::FLOAT32; + _f32.reset(new Buffer(buf)); + _shape = const_cast(&(_f32->shape())); +} + +void annot_data(loco::Node *node, std::unique_ptr &&data) +{ + node->annot(stdex::make_unique(std::move(data))); +} + +const NodeData *annot_data(const loco::Node *node) +{ + if (auto annot = node->annot()) + { + return annot->data(); + } + + return nullptr; +} + +void erase_annot_data(loco::Node *node) { node->annot(nullptr); } + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/NodeDataImpl.h b/compiler/locomotiv/src/NodeDataImpl.h new file mode 100644 index 000000000..bdd9db386 --- /dev/null +++ b/compiler/locomotiv/src/NodeDataImpl.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_NODEDATAIMPL_H_ +#define _LOCOMOTIV_NODEDATAIMPL_H_ + +#include "locomotiv/NodeData.h" + +namespace locomotiv +{ + +/** + * @brief An implementation of NodeData interface + */ +class NodeDataImpl final : public NodeData +{ +public: + template using Buffer = nncc::core::ADT::tensor::Buffer; + using Shape = nncc::core::ADT::tensor::Shape; + + template NodeDataImpl(const Buffer
&buf); + + const loco::DataType &dtype() const override { return _dtype; } + + const Shape *shape() const override { return _shape; } + + const Buffer *as_s32_bufptr() const override { return _s32.get(); } + + const Buffer *as_f32_bufptr() const override { return _f32.get(); } + +private: + loco::DataType _dtype = loco::DataType::Unknown; + Shape *_shape = nullptr; + std::unique_ptr> _s32 = nullptr; + std::unique_ptr> _f32 = nullptr; +}; + +/// @brief Bind "NodeData" to "Node" +void annot_data(loco::Node *node, std::unique_ptr &&data); + +/** + * @brief Get "NodeData" for a given node + * + * NOTE Returns nullptr if "NodeData" is not binded yet + */ +const NodeData *annot_data(const loco::Node *node); + +/// @brief Release "NodeData" bound to a given node +void erase_annot_data(loco::Node *node); + +} // namespace locomotiv + +#endif // _LOCOMOTIV_NODEDATAIMPL_H_ diff --git a/compiler/locomotiv/src/NodeDataImpl.test.cpp b/compiler/locomotiv/src/NodeDataImpl.test.cpp new file mode 100644 index 000000000..b85956063 --- /dev/null +++ b/compiler/locomotiv/src/NodeDataImpl.test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locomotiv/NodeData.h" +#include "NodeDataImpl.h" + +#include +#include +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(NodeDataImpl, as_annotation) +{ + const Shape shape{1}; + auto buf = make_buffer(shape); + buf.at(Index{0}) = 3.14f; + + std::unique_ptr data = locomotiv::make_data(buf); + + auto g = loco::make_graph(); + auto node = g->nodes()->create(); + + ASSERT_EQ(locomotiv::annot_data(node), nullptr); + + // Set annotation + locomotiv::annot_data(node, std::move(data)); + + // Get annotation + const locomotiv::NodeData *obtained = locomotiv::annot_data(node); + ASSERT_NE(obtained, nullptr); + + ASSERT_EQ(obtained->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(obtained->shape()), shape); + ASSERT_FLOAT_EQ(obtained->as_f32_bufptr()->at(Index{0}), 3.14f); + + // Erase annotation + locomotiv::erase_annot_data(node); + ASSERT_EQ(locomotiv::annot_data(node), nullptr); +} diff --git a/compiler/locomotiv/src/NodeDomain.cpp b/compiler/locomotiv/src/NodeDomain.cpp new file mode 100644 index 000000000..709b9fe34 --- /dev/null +++ b/compiler/locomotiv/src/NodeDomain.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeDomain.h" + +#include + +namespace locomotiv +{ + +struct NodeDomain final : public loco::NodeAnnotation +{ + NodeDomain(const loco::Domain &domain) : value(domain) + { + // DO NOTHING + } + + loco::Domain value = loco::Domain::Unknown; +}; + +void annot_domain(loco::Node *node, const loco::Domain &domain) +{ + assert(domain != loco::Domain::Unknown); + auto node_domain = std::unique_ptr(new NodeDomain(domain)); + assert(node_domain); + node->annot(std::move(node_domain)); +} + +loco::Domain annot_domain(const loco::Node *node) +{ + auto node_domain = node->annot(); + if (node_domain) + return node_domain->value; + else + return loco::Domain::Unknown; +} + +void erase_annot_domain(loco::Node *node) { node->annot(nullptr); } + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/NodeDomain.h b/compiler/locomotiv/src/NodeDomain.h new file mode 100644 index 000000000..fc93f77f7 --- /dev/null +++ b/compiler/locomotiv/src/NodeDomain.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_NODEDOMAIN_H_ +#define _LOCOMOTIV_NODEDOMAIN_H_ + +#include +#include + +namespace locomotiv +{ + +/// @brief Wrapper to annotate domain to node. Cannot annotate unknown domain. +void annot_domain(loco::Node *node, const loco::Domain &domain); + +/// @brief Wrapper to get domain annotation of node +loco::Domain annot_domain(const loco::Node *node); + +/// @brief Erase already annotated node domain +void erase_annot_domain(loco::Node *node); + +} // namespace locomotiv + +#endif // _LOCOMOTIV_NODEDOMAIN_H_ diff --git a/compiler/locomotiv/src/NodeDomain.test.cpp b/compiler/locomotiv/src/NodeDomain.test.cpp new file mode 100644 index 000000000..9cfcf2eb8 --- /dev/null +++ b/compiler/locomotiv/src/NodeDomain.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeDomain.h" + +#include + +TEST(NodeDomain, as_annotation) +{ + loco::Pull node; + + ASSERT_EQ(locomotiv::annot_domain(&node), loco::Domain::Unknown); + + // Set annotation + locomotiv::annot_domain(&node, loco::Domain::Tensor); + + // Get annotation + const loco::Domain obtained = locomotiv::annot_domain(&node); + ASSERT_EQ(obtained, loco::Domain::Tensor); + + // Erase annotation + locomotiv::erase_annot_domain(&node); + ASSERT_EQ(locomotiv::annot_domain(&node), loco::Domain::Unknown); +} diff --git a/compiler/locomotiv/src/NodeExecution.cpp b/compiler/locomotiv/src/NodeExecution.cpp new file mode 100644 index 000000000..e532b5af6 --- /dev/null +++ b/compiler/locomotiv/src/NodeExecution.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NodeExecution.h" + +#include "NodeDomain.h" +#include "NodeDataImpl.h" +#include "Validation.h" + +#include +#include +#include +#include +#include + +#include +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::IndexEnumerator; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +namespace locomotiv +{ + +float UnaryFunc::apply(float) const { throw std::runtime_error{"F32 is not supported yet"}; } +int32_t UnaryFunc::apply(int32_t) const { throw std::runtime_error{"S32 is not supported yet"}; } + +float BinaryFunc::apply(float, float) const +{ + throw std::runtime_error{"F32 is not supported yet"}; +} + +int32_t BinaryFunc::apply(int32_t, int32_t) const +{ + throw std::runtime_error{"S32 is not supported yet"}; +} + +// TODO Use visitor pattern of loco when available +void NodeExecution::run(loco::Node *node) +{ + erase_annot_data(node); + +#define NODE(Name) \ + if (as(node)) \ + { \ + execute(as(node)); \ + return; \ + } +#include "Node.lst" +#undef NODE + + throw std::runtime_error("Not supported loco::Node type"); +} + +void NodeExecution::eltwise_unary(loco::Node *node, const UnaryFunc &f) +{ + auto input_node = node->arg(0); + auto input_domain = annot_domain(input_node); + auto input_data = annot_data(input_node); + auto input_dtype = input_data->dtype(); + + validate(input_data, "Input is not ready"); + validate(input_domain != loco::Domain::Unknown, "Input domain is unknown"); + + auto output_node = node; + // Element-wise Unary Operation does not affect Domain + auto output_domain = input_domain; + // Eltwise-wise Unary Operation does not affet Data Type (ASSUMPTION) + // + // TODO Check this assumption + auto output_dtype = input_dtype; + std::unique_ptr output_data = nullptr; + + switch (output_dtype) + { + case loco::DataType::FLOAT32: + { + auto input_bufptr = input_data->as_f32_bufptr(); + auto output_buf = make_buffer(*input_data->shape()); + auto *shape = input_data->shape(); + + for (IndexEnumerator e{*shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + output_buf.at(index) = f.apply(input_bufptr->at(index)); + } + + output_data = make_data(output_buf); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(output_data != nullptr); + annot_data(output_node, std::move(output_data)); + annot_domain(output_node, output_domain); +} + +void NodeExecution::eltwise_binary(loco::Node *node, const BinaryFunc &f) +{ + auto lhs_node = node->arg(0); + auto rhs_node = node->arg(1); + auto lhs_data = annot_data(lhs_node); + auto rhs_data = annot_data(rhs_node); + + validate(lhs_data && rhs_data, "Input not ready"); + validate(annot_domain(lhs_node) == annot_domain(rhs_node), "Wrong input domain"); + validate(lhs_data->dtype() == rhs_data->dtype(), "Wrong input type"); + validate(*lhs_data->shape() == *rhs_data->shape(), "Wrong input shape"); + + auto out_node = node; + std::unique_ptr out_data = nullptr; + + switch (lhs_data->dtype()) + { + case loco::DataType::FLOAT32: + { + auto lhs_bufptr = lhs_data->as_f32_bufptr(); + auto rhs_bufptr = rhs_data->as_f32_bufptr(); + auto out_bufptr = make_buffer(*lhs_data->shape()); + + auto *shape = lhs_data->shape(); + + for (IndexEnumerator e{*shape}; e.valid(); e.advance()) + { + const auto &index = e.current(); + out_bufptr.at(index) = f.apply(lhs_bufptr->at(index), rhs_bufptr->at(index)); + } + + out_data = make_data(out_bufptr); + break; + } + default: + throw std::runtime_error("NYI for this DataType"); + } + + assert(out_data != nullptr); + annot_data(out_node, std::move(out_data)); + annot_domain(out_node, annot_domain(lhs_node)); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/NodeExecution.h b/compiler/locomotiv/src/NodeExecution.h new file mode 100644 index 000000000..363188d38 --- /dev/null +++ b/compiler/locomotiv/src/NodeExecution.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_NODEEXECUTION_H_ +#define _LOCOMOTIV_NODEEXECUTION_H_ + +#include + +namespace locomotiv +{ + +struct UnaryFunc +{ + virtual ~UnaryFunc() = default; + + virtual float apply(float) const; + virtual int32_t apply(int32_t) const; +}; + +// Q. How to support mixed precision binary operators? +struct BinaryFunc +{ + virtual ~BinaryFunc() = default; + + virtual float apply(float, float) const; + virtual int32_t apply(int32_t, int32_t) const; +}; + +/** + * @brief Helper class for Session, responsible to process one node calculation. + */ +class NodeExecution +{ +public: + /// @brief Run calculation for one unspecified Node + void run(loco::Node *node); + + static NodeExecution &get() + { + static NodeExecution me; + return me; + } + +private: + NodeExecution() {} + + template Derived *as(loco::Node *node) + { + return dynamic_cast(node); + } + +// clang-format off + /** + * @brief Calculate for one specified node and update its result as NodeData. + * Abort program when its ingredients are not ready or not supported. + * + * @note Definitions of overloaded execute() are in 'Node/' directory + */ +// clang-format on +#define NODE(Name) void execute(loco::Name *); +#include "Node.lst" +#undef NODE + + void eltwise_unary(loco::Node *node, const UnaryFunc &f); + void eltwise_binary(loco::Node *node, const BinaryFunc &f); +}; + +} // namespace locomotiv + +#endif // _LOCOMOTIV_NODEEXECUTION_H_ diff --git a/compiler/locomotiv/src/Session.cpp b/compiler/locomotiv/src/Session.cpp new file mode 100644 index 000000000..841a14a5c --- /dev/null +++ b/compiler/locomotiv/src/Session.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locomotiv/Session.h" +#include "locomotiv/NodeData.h" + +#include "UserData.h" +#include "NodeDataImpl.h" +#include "NodeExecution.h" +#include "NodeDomain.h" + +#include + +namespace locomotiv +{ + +Session::~Session() +{ + for (uint32_t i = 0; i < _graph->nodes()->size(); ++i) + { + auto node = _graph->nodes()->at(i); + erase_user_data(node); + erase_annot_data(node); + erase_annot_domain(node); + } +} + +void Session::set_input(uint32_t index, std::unique_ptr &&data) +{ + assert(index < input_size()); + + // Check whether already annotated + auto pull = loco::pull_node(_graph, index); + if (user_data(pull)) + { + throw std::runtime_error("Graph input already has NodeData"); + } + + // Check data type match + if (pull->dtype() != data->dtype()) + { + throw std::runtime_error("Data type mismatch"); + } + + // Check shape match + auto shape = data->shape(); + if (pull->rank() != shape->rank()) + { + throw std::runtime_error("Shape rank mismatch"); + } + for (uint32_t i = 0; i < pull->rank(); ++i) + { + if (pull->dim(i).known() && pull->dim(i).value() != shape->dim(i)) + { + throw std::runtime_error("Shape dimension mismatch"); + } + } + + user_data(pull, std::move(data)); +} + +void Session::infer() +{ + auto schedules = loco::postorder_traversal(_outputs); + + for (auto node : schedules) + { + NodeExecution::get().run(node); + } +} + +const NodeData *Session::get_output(uint32_t index) +{ + assert(index < output_size()); + + auto output_node = _outputs.at(index); + return annot_data(output_node); +} + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/Session.test.cpp b/compiler/locomotiv/src/Session.test.cpp new file mode 100644 index 000000000..6d4a2414f --- /dev/null +++ b/compiler/locomotiv/src/Session.test.cpp @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locomotiv/Session.h" +#include "locomotiv/NodeData.h" + +#include "UserData.h" + +#include +#include +#include +#include + +#include + +#include + +using nncc::core::ADT::tensor::Index; +using nncc::core::ADT::tensor::Shape; +using nncc::core::ADT::tensor::LexicalLayout; +using nncc::core::ADT::tensor::make_buffer; + +TEST(Session, graph_IO_size) +{ + // Make graph + auto g = loco::make_graph(); + + // inputs + const uint32_t inputs = 2; + for (uint32_t i = 0; i < inputs; ++i) + { + auto pull = g->nodes()->create(); + loco::link(g->inputs()->create(), pull); + } + + // outputs + const uint32_t outputs = 3; + for (uint32_t o = 0; o < outputs; ++o) + { + auto push = g->nodes()->create(); + loco::link(g->outputs()->create(), push); + } + + // Make session + locomotiv::Session s(g.get()); + + ASSERT_EQ(s.input_size(), inputs); + ASSERT_EQ(s.output_size(), outputs); +} + +TEST(Session, set_input) +{ + // Make graph + auto g = loco::make_graph(); + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->rank(1); + pull->dim(0) = 1; + loco::link(g->inputs()->create(), pull); + + // Make good data + auto buf = make_buffer(Shape{1}); + auto data = locomotiv::make_data(buf); + + // Make data with different data type + auto buf_not_dtype = make_buffer(Shape{1}); + auto data_not_dtype = locomotiv::make_data(buf_not_dtype); + + // Make data with different rank + auto buf_not_rank = make_buffer(Shape{1, 1}); + auto data_not_rank = locomotiv::make_data(buf_not_rank); + + // Make data with different dimension + auto buf_not_dim = make_buffer(Shape{2}); + auto data_not_dim = locomotiv::make_data(buf_not_dim); + + // Make session + locomotiv::Session s(g.get()); + + ASSERT_ANY_THROW(s.set_input(0, std::move(data_not_dtype))); + ASSERT_ANY_THROW(s.set_input(0, std::move(data_not_rank))); + ASSERT_ANY_THROW(s.set_input(0, std::move(data_not_dim))); + ASSERT_NO_THROW(s.set_input(0, std::move(data))); + ASSERT_ANY_THROW(s.set_input(0, std::move(data))); +} + +TEST(Session, inference_identity) +{ + std::vector> graphs; + + // pull-push / f32 / known shape + { + auto g = loco::make_graph(); + + // Pull node + auto pull_node = g->nodes()->create(); + pull_node->dtype(loco::DataType::FLOAT32); + pull_node->rank(1); + pull_node->dim(0) = 1; + + // Push node + auto push_node = g->nodes()->create(); + push_node->from(pull_node); + + // Input + auto graph_input = g->inputs()->create(); + loco::link(graph_input, pull_node); + + // Output + auto graph_output = g->outputs()->create(); + loco::link(graph_output, push_node); + + graphs.push_back(std::move(g)); + } + + // pull-push / f32 / unknown shape + { + auto g = loco::make_graph(); + + // Pull node + auto pull_node = g->nodes()->create(); + pull_node->dtype(loco::DataType::FLOAT32); + pull_node->rank(1); + pull_node->dim(0) = loco::make_dimension(); + + // Push node + auto push_node = g->nodes()->create(); + push_node->from(pull_node); + + // Input + auto graph_input = g->inputs()->create(); + loco::link(graph_input, pull_node); + + // Output + auto graph_output = g->outputs()->create(); + loco::link(graph_output, push_node); + + graphs.push_back(std::move(g)); + } + + for (auto it = graphs.begin(); it != graphs.end(); ++it) + { + auto g = it->get(); + locomotiv::Session s(g); + + const Shape shape{1}; + auto buf = make_buffer(shape); + buf.at(Index{0}) = 3.14f; + auto data = locomotiv::make_data(buf); + + // Input not ready + ASSERT_ANY_THROW(s.infer()); + + s.set_input(0, std::move(data)); + + // Valid run + ASSERT_NO_THROW(s.infer()); + // Multiple run is possible + ASSERT_NO_THROW(s.infer()); + + auto output_data = s.get_output(0); + ASSERT_NE(output_data, nullptr); + ASSERT_EQ(output_data->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(*(output_data->shape()), Shape{1}); + ASSERT_EQ(output_data->as_f32_bufptr()->at(Index{0}), 3.14f); + } +} + +TEST(Session, session_for_subgraph) +{ + /* + * Make following graph: + * ConstGen_1 -- + * \ + * ConstGen_2 --- TensorConcat_1 --- TensorConcat_3 --- Push + * / + * ConstGen_3 --- TensorConcat_2 -- + * / + * ConstGen_4 -- + */ + auto g = loco::make_graph(); + + auto c1 = g->nodes()->create(); + auto c2 = g->nodes()->create(); + auto c3 = g->nodes()->create(); + auto c4 = g->nodes()->create(); + + c1->dtype(loco::DataType::FLOAT32); + c2->dtype(loco::DataType::FLOAT32); + c3->dtype(loco::DataType::FLOAT32); + c4->dtype(loco::DataType::FLOAT32); + c1->shape({1}); + c2->shape({1}); + c3->shape({1}); + c4->shape({1}); + c1->size(1); + c2->size(1); + c3->size(1); + c4->size(1); + + c1->at(0) = 0.1f; + c2->at(0) = 0.2f; + c3->at(0) = 0.3f; + c4->at(0) = 0.4f; + + auto t1 = g->nodes()->create(); + auto t2 = g->nodes()->create(); + auto t3 = g->nodes()->create(); + + // Note: default concat axis is 0 + t1->lhs(c1); + t1->rhs(c2); + t2->lhs(c3); + t2->rhs(c4); + t3->lhs(t1); + t3->rhs(t2); + + auto push = g->nodes()->create(); + push->from(t3); + + { + // Session to get t1 only + locomotiv::Session s(g.get(), {t1}); + ASSERT_EQ(s.output_size(), 1); + ASSERT_EQ(s.get_output_node(0), dynamic_cast(t1)); + + s.infer(); + + auto t1_data = s.get_output(0); + ASSERT_NE(t1_data, nullptr); + ASSERT_EQ(*(t1_data->shape()), Shape{2}); + + auto t1_buf = t1_data->as_f32_bufptr(); + ASSERT_EQ(t1_buf->at({0}), 0.1f); + ASSERT_EQ(t1_buf->at({1}), 0.2f); + } + + { + // Session to get t2 only + locomotiv::Session s(g.get(), {t2}); + ASSERT_EQ(s.output_size(), 1); + ASSERT_EQ(s.get_output_node(0), dynamic_cast(t2)); + + s.infer(); + + auto t2_data = s.get_output(0); + ASSERT_NE(t2_data, nullptr); + ASSERT_EQ(*(t2_data->shape()), Shape{2}); + + auto t2_buf = t2_data->as_f32_bufptr(); + ASSERT_EQ(t2_buf->at({0}), 0.3f); + ASSERT_EQ(t2_buf->at({1}), 0.4f); + } + + { + // Session to get t2 and push + locomotiv::Session s(g.get(), {t2, push}); + ASSERT_EQ(s.output_size(), 2); + ASSERT_EQ(s.get_output_node(0), dynamic_cast(t2)); + ASSERT_EQ(s.get_output_node(1), dynamic_cast(push)); + + s.infer(); + + auto t2_data = s.get_output(0); + ASSERT_NE(t2_data, nullptr); + ASSERT_EQ(*(t2_data->shape()), Shape{2}); + + auto t2_buf = t2_data->as_f32_bufptr(); + ASSERT_EQ(t2_buf->at({0}), 0.3f); + ASSERT_EQ(t2_buf->at({1}), 0.4f); + + auto push_data = s.get_output(1); + ASSERT_NE(push_data, nullptr); + ASSERT_EQ(*(push_data->shape()), Shape{4}); + + auto push_buf = push_data->as_f32_bufptr(); + ASSERT_EQ(push_buf->at({0}), 0.1f); + ASSERT_EQ(push_buf->at({1}), 0.2f); + ASSERT_EQ(push_buf->at({2}), 0.3f); + ASSERT_EQ(push_buf->at({3}), 0.4f); + } +} + +TEST(Session, ctor_by_range) +{ + // Make graph + auto g = loco::make_graph(); + + auto constgen = g->nodes()->create(); + auto relu = g->nodes()->create(); + auto push = g->nodes()->create(); + + constgen->dtype(loco::DataType::FLOAT32); + constgen->shape({2}); + constgen->size(2); + constgen->at(0) = 0.1f; + constgen->at(1) = -0.1f; + + relu->input(constgen); + push->from(relu); + + std::array custom_outputs = {constgen, push}; + + // Make Session by range + locomotiv::Session s(g.get(), custom_outputs.begin(), custom_outputs.end()); + + s.infer(); + + auto constgen_data = s.get_output(0); + ASSERT_NE(constgen_data, nullptr); + ASSERT_EQ(*(constgen_data->shape()), Shape{2}); + + auto constgen_buf = constgen_data->as_f32_bufptr(); + ASSERT_EQ(constgen_buf->at({0}), 0.1f); + ASSERT_EQ(constgen_buf->at({1}), -0.1f); + + auto push_data = s.get_output(1); + ASSERT_NE(push_data, nullptr); + ASSERT_EQ(*(push_data->shape()), Shape{2}); + + auto push_buf = push_data->as_f32_bufptr(); + ASSERT_EQ(push_buf->at({0}), 0.1f); + ASSERT_EQ(push_buf->at({1}), 0.0f); +} + +// Below here is internal test for locomotiv, i.e. not public usage of locomotiv +#include "NodeDataImpl.h" +#include "NodeDomain.h" + +TEST(Session, dtor) +{ + auto g = loco::make_graph(); + + // Pull node + auto pull = g->nodes()->create(); + pull->dtype(loco::DataType::FLOAT32); + pull->rank(1); + pull->dim(0) = 1; + + // Input + auto input = g->inputs()->create(); + loco::link(input, pull); + + { + locomotiv::Session s(g.get()); + + auto buf = make_buffer(Shape{1}); + auto data = locomotiv::make_data(buf); + + s.set_input(0, std::move(data)); + + auto data_annotated = locomotiv::annot_data(pull); + ASSERT_EQ(data_annotated, nullptr); + auto user_data_annotated = locomotiv::user_data(pull); + ASSERT_NE(user_data_annotated, nullptr); + auto domain_annotated = locomotiv::annot_domain(pull); + ASSERT_EQ(domain_annotated, loco::Domain::Unknown); + } + + auto data_annotated = locomotiv::annot_data(pull); + ASSERT_EQ(data_annotated, nullptr); + auto user_data_annotated = locomotiv::user_data(pull); + ASSERT_EQ(user_data_annotated, nullptr); + auto domain_annotated = locomotiv::annot_domain(pull); + ASSERT_EQ(domain_annotated, loco::Domain::Unknown); +} diff --git a/compiler/locomotiv/src/UserData.cpp b/compiler/locomotiv/src/UserData.cpp new file mode 100644 index 000000000..b658ada9b --- /dev/null +++ b/compiler/locomotiv/src/UserData.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UserData.h" + +#include + +#include + +namespace +{ + +class UserDataAnnotation final : public loco::NodeAnnotation +{ +public: + UserDataAnnotation(std::unique_ptr &&data) : _data{std::move(data)} + { + // DO NOTHING + } + +public: + const locomotiv::NodeData *data(void) const { return _data.get(); } + +private: + std::unique_ptr _data; +}; + +} // namespace + +namespace locomotiv +{ + +const NodeData *user_data(const loco::Node *node) +{ + if (auto annot = node->annot()) + { + return annot->data(); + } + + return nullptr; +} + +void user_data(loco::Node *node, std::unique_ptr &&data) +{ + node->annot(stdex::make_unique(std::move(data))); +} + +void erase_user_data(loco::Node *node) { node->annot(nullptr); } + +} // namespace locomotiv diff --git a/compiler/locomotiv/src/UserData.h b/compiler/locomotiv/src/UserData.h new file mode 100644 index 000000000..661d02140 --- /dev/null +++ b/compiler/locomotiv/src/UserData.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_USERDATA_H_ +#define _LOCOMOTIV_USERDATA_H_ + +#include "locomotiv/NodeData.h" + +namespace locomotiv +{ + +const NodeData *user_data(const loco::Node *node); +void user_data(loco::Node *node, std::unique_ptr &&data); +void erase_user_data(loco::Node *node); + +} // namespace locomotiv + +#endif // _LOCOMOTIV_USERDATA_H_ diff --git a/compiler/locomotiv/src/Validation.h b/compiler/locomotiv/src/Validation.h new file mode 100644 index 000000000..59b8c40c7 --- /dev/null +++ b/compiler/locomotiv/src/Validation.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOCOMOTIV_VALIDATION_H_ +#define _LOCOMOTIV_VALIDATION_H_ + +#include +#include + +namespace locomotiv +{ + +inline void validate(bool true_cond, const std::string &&exception_msg) +{ + if (!true_cond) + throw std::runtime_error(exception_msg); +} + +} // namespace locomotiv + +#endif // _LOCOMOTIV_VALIDATION_H_ diff --git a/compiler/locop/CMakeLists.txt b/compiler/locop/CMakeLists.txt new file mode 100644 index 000000000..107ee8be8 --- /dev/null +++ b/compiler/locop/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(locop STATIC ${SOURCES}) +set_target_properties(locop PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(locop PUBLIC include) +target_link_libraries(locop PUBLIC loco) +# Let's apply nncc common compile options +# +# NOTE This will enable strict compilation (warnings as error). +# Please refer to the top-level CMakeLists.txt for details +target_link_libraries(locop PRIVATE nncc_common) +target_link_libraries(locop PUBLIC nncc_coverage) +target_link_libraries(locop PRIVATE pp) +target_link_libraries(locop PRIVATE stdex) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Google Test is mandatory for internal testing +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(locop_test ${TESTS}) +target_link_libraries(locop_test stdex) +target_link_libraries(locop_test locop) diff --git a/compiler/locop/README.md b/compiler/locop/README.md new file mode 100644 index 000000000..81ef41ca2 --- /dev/null +++ b/compiler/locop/README.md @@ -0,0 +1,3 @@ +# locop + +_locop_ is a collection of _loco_ pretty printers. diff --git a/compiler/locop/include/locop/CanonicalNodeSummaryBuilder.h b/compiler/locop/include/locop/CanonicalNodeSummaryBuilder.h new file mode 100644 index 000000000..e9ced3f17 --- /dev/null +++ b/compiler/locop/include/locop/CanonicalNodeSummaryBuilder.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_CANONICAL_NODE_SUMMARY_BUILDER_H__ +#define __LOCOP_CANONICAL_NODE_SUMMARY_BUILDER_H__ + +#include "locop/NodeSummaryBuilder.h" + +namespace locop +{ + +/** + * @brief Built-in Node Summary Builder for Canonical Dialect + */ +class CanonicalNodeSummaryBuilder final : public NodeSummaryBuilder +{ +public: + CanonicalNodeSummaryBuilder(const SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *node, locop::NodeSummary &out) const final; + +private: + const SymbolTable *_tbl; +}; + +} // namespace locop + +#endif // __LOCOP_CANONICAL_NODE_SUMMARY_BUILDER_H__ diff --git a/compiler/locop/include/locop/FormattedGraph.h b/compiler/locop/include/locop/FormattedGraph.h new file mode 100644 index 000000000..0805c0e39 --- /dev/null +++ b/compiler/locop/include/locop/FormattedGraph.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_FORMATTED_GRAPH_H__ +#define __LOCOP_FORMATTED_GRAPH_H__ + +#include "locop/SymbolTable.h" +#include "locop/NodeSummary.h" +#include "locop/NodeSummaryBuilder.h" +// TODO Remove this redundant include +#include "locop/CanonicalNodeSummaryBuilder.h" + +#include + +#include +#include +#include +#include + +namespace locop +{ + +struct FormattedGraph +{ + virtual ~FormattedGraph() = default; + + virtual void dump(std::ostream &os) const = 0; +}; + +std::ostream &operator<<(std::ostream &, const FormattedGraph &); + +enum Formatter +{ + LinearV1, + // TO BE ADDED +}; + +template class FormattedGraphImpl; + +template <> class FormattedGraphImpl final : public FormattedGraph +{ +public: + FormattedGraphImpl(loco::Graph *graph) : _graph{graph} {} + +public: + void dump(std::ostream &os) const final; + +public: + FormattedGraphImpl &with(std::unique_ptr &&f) + { + _factory = std::move(f); + return (*this); + } + +private: + loco::Graph *_graph; + + /** + * @brief User-provided NodeSummaryBuilderFactory + */ + std::unique_ptr _factory = nullptr; +}; + +template FormattedGraphImpl fmt(loco::Graph *g) +{ + return FormattedGraphImpl{g}; +} + +template FormattedGraphImpl fmt(const std::unique_ptr &g) +{ + return fmt(g.get()); +} + +} // namespace locop + +#endif // __LOCOP_FORMATTED_GRAPH_H__ diff --git a/compiler/locop/include/locop/FormattedTensorShape.h b/compiler/locop/include/locop/FormattedTensorShape.h new file mode 100644 index 000000000..25621d6c3 --- /dev/null +++ b/compiler/locop/include/locop/FormattedTensorShape.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_FORMATTED_TENSOR_SHAPE_H__ +#define __LOCOP_FORMATTED_TENSOR_SHAPE_H__ + +#include "locop/Interfaces.h" + +#include + +namespace locop +{ + +enum class TensorShapeFormat +{ + // D_0 x D_1 x ... D_N + Plain, + // [ D_0 x D_1 x D_2 x ... ] + Bracket, +}; + +template class FormattedTensorShape; + +template <> +class FormattedTensorShape final : public Spec +{ +public: + FormattedTensorShape(const loco::TensorShape *ptr) : _ptr{ptr} + { + // DO NOTHING + } + +public: + void dump(std::ostream &os) const final; + +private: + const loco::TensorShape *_ptr = nullptr; +}; + +template <> +class FormattedTensorShape final : public Spec +{ +public: + FormattedTensorShape(const loco::TensorShape *ptr) : _ptr{ptr} + { + // DO NOTHING + } + +public: + void dump(std::ostream &os) const final; + +private: + const loco::TensorShape *_ptr = nullptr; +}; + +template FormattedTensorShape fmt(loco::TensorShape *ptr) +{ + return FormattedTensorShape{ptr}; +} + +} // namespace locop + +#endif // __LOCOP_FORMATTED_TENSOR_SHAPE_H__ diff --git a/compiler/locop/include/locop/GenericNodeSummaryBuilder.h b/compiler/locop/include/locop/GenericNodeSummaryBuilder.h new file mode 100644 index 000000000..cdfe45a2b --- /dev/null +++ b/compiler/locop/include/locop/GenericNodeSummaryBuilder.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_GENERIC_NODE_SUMMARY_BUILDER_H__ +#define __LOCOP_GENERIC_NODE_SUMMARY_BUILDER_H__ + +#include "locop/NodeSummaryBuilder.h" + +namespace locop +{ + +/** + * @brief Dialect-agnostic Node Summary Builder + */ +class GenericNodeSummaryBuilder final : public NodeSummaryBuilder +{ +public: + GenericNodeSummaryBuilder(const SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *node, locop::NodeSummary &out) const final; + +private: + const SymbolTable *_tbl; +}; + +} // namespace locop + +#endif // __LOCOP_GENERIC_NODE_SUMMARY_BUILDER_H__ diff --git a/compiler/locop/include/locop/Interfaces.h b/compiler/locop/include/locop/Interfaces.h new file mode 100644 index 000000000..0b4974d0f --- /dev/null +++ b/compiler/locop/include/locop/Interfaces.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_INTERFACES_H__ +#define __LOCOP_INTERFACES_H__ + +#include + +namespace locop +{ + +enum class Interface +{ + Formatted, +}; + +template struct Spec; + +template <> struct Spec +{ + virtual ~Spec() = default; + + virtual void dump(std::ostream &os) const = 0; +}; + +std::ostream &operator<<(std::ostream &, const Spec &); + +} // namespace locop + +#endif // __LOCOP_INTERFACES_H__ diff --git a/compiler/locop/include/locop/NodeSummary.h b/compiler/locop/include/locop/NodeSummary.h new file mode 100644 index 000000000..59fe66357 --- /dev/null +++ b/compiler/locop/include/locop/NodeSummary.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCO_NODE_SUMMARY_H__ +#define __LOCO_NODE_SUMMARY_H__ + +#include +#include +#include +#include + +namespace locop +{ + +using OpName = std::string; +using ArgName = std::string; +using ArgValue = std::string; +using ArgElem = std::pair; + +class ArgDesc +{ +public: + ArgDesc() = default; + +public: + /// @brief The number of presented arguments + uint32_t count(void) const { return _args.size(); } + + const ArgElem &at(uint32_t n) const { return _args.at(n); } + void append(const ArgName &name, const ArgValue &value) { _args.emplace_back(name, value); } + +private: + std::vector _args; +}; + +struct NodeDesc +{ +public: + /** + * @brief Multi-line comments + */ + class Comments final + { + public: + Comments() = default; + + public: + uint32_t count(void) const { return _lines.size(); } + const std::string &at(uint32_t n) const { return _lines.at(n); } + void append(const std::string &s); + + private: + std::vector _lines; + }; + +public: + enum class State + { + // All the node descriptions are "Invalid" at the beginning. + // + // Any valid node description SHOULD NOT be at this state. + Invalid, + // This state means that the producer is **NOT** confident about the information that + // it generates. + // + // There may be some missing information. + PartiallyKnown, + // This state means that the producer is confident about the information that it + // generates. + Complete, + }; + +public: + NodeDesc() = default; + NodeDesc(const OpName &opname) { this->opname(opname); } + +public: + const OpName &opname(void) const; + void opname(const OpName &value); + + const ArgDesc &args(void) const { return _args; } + ArgDesc &args(void) { return _args; } + + const Comments &comments(void) const { return _comments; } + Comments &comments(void) { return _comments; } + + const State &state(void) const { return _state; } + void state(const State &s) { _state = s; } + +private: + std::unique_ptr _name = nullptr; + ArgDesc _args; + Comments _comments; + State _state = State::Invalid; +}; + +using NodeSummary = NodeDesc; + +} // namespace locop + +#endif // __LOCO_NODE_SUMMARY_H__ diff --git a/compiler/locop/include/locop/NodeSummaryBuilder.h b/compiler/locop/include/locop/NodeSummaryBuilder.h new file mode 100644 index 000000000..b84bc71cd --- /dev/null +++ b/compiler/locop/include/locop/NodeSummaryBuilder.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_NODE_SUMMARY_BUILDER_H__ +#define __LOCOP_NODE_SUMMARY_BUILDER_H__ + +#include "locop/SymbolTable.h" +#include "locop/NodeSummary.h" + +#include + +namespace locop +{ + +/** + * @brief Build a summary from loco Node + */ +struct NodeSummaryBuilder +{ + virtual ~NodeSummaryBuilder() = default; + + virtual bool build(const loco::Node *, NodeSummary &) const = 0; +}; + +struct NodeSummaryBuilderFactory +{ + virtual ~NodeSummaryBuilderFactory() = default; + + virtual std::unique_ptr create(const SymbolTable *) const = 0; +}; + +} // namespace locop + +#endif // __LOCOP_NODE_SUMMARY_BUILDER_H__ diff --git a/compiler/locop/include/locop/SymbolTable.h b/compiler/locop/include/locop/SymbolTable.h new file mode 100644 index 000000000..ee9fc78e2 --- /dev/null +++ b/compiler/locop/include/locop/SymbolTable.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOCOP_SYMBOL_TABLE_H__ +#define __LOCOP_SYMBOL_TABLE_H__ + +#include + +#include + +namespace locop +{ + +/** + * @brief Symbol Table Interface + * + * Symbol Table gives a name for each node. + */ +struct SymbolTable +{ + virtual ~SymbolTable() = default; + + virtual std::string lookup(const loco::Node *) const = 0; +}; + +} // namespace locop + +#endif // __LOCOP_SYMBOL_TABLE_H__ diff --git a/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp new file mode 100644 index 000000000..b962f490b --- /dev/null +++ b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/CanonicalNodeSummaryBuilder.h" + +#include "locop/FormattedTensorShape.h" + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +using locop::SymbolTable; + +namespace +{ + +// TODO Move this into loco +loco::TensorShape tensor_shape(const loco::NodeMixin *m) +{ + loco::TensorShape res; + + res.rank(m->rank()); + + for (uint32_t axis = 0; axis < m->rank(); ++axis) + { + res.dim(axis) = m->dim(axis); + } + + return res; +} + +using PrettyTensorShape = locop::FormattedTensorShape; + +inline PrettyTensorShape pretty(const loco::TensorShape &shape) +{ + return PrettyTensorShape{&shape}; +} + +} // namespace + +namespace +{ + +/** + * @brief Return the opname as "." + */ +std::string opname(const loco::Node *node) +{ + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto canonical_node = dynamic_cast(node); + + assert(canonical_node != nullptr); + + switch (canonical_node->opcode()) + { +#define CANONICAL_NODE(OPCODE, CLASS) \ + case loco::CanonicalOpcode::OPCODE: \ + return "canonical." #OPCODE; +#include "loco/IR/CanonicalNodes.lst" +#undef CANONICAL_NODE + default: + break; + }; + + return "canonical." + "Invalid"; + } + + return "unknown." + "Unknown"; +} + +struct NodeDesc : public locop::NodeDesc +{ +public: + NodeDesc() = default; + NodeDesc(const locop::OpName &opname) : locop::NodeDesc{opname} + { + // DO NOTHING + } + +public: + // DEPRECATED + const locop::OpName &name(void) const { return opname(); } + + // DEPRECATED + uint32_t arg_size(void) const { return args().count(); } + // DEPRECATED + const locop::ArgElem &arg(uint32_t n) const { return args().at(n); } + // DEPRECATED + void arg(const locop::ArgName &name, const locop::ArgValue &value) { args().append(name, value); } +}; + +NodeDesc default_node_desc(const SymbolTable &tbl, const loco::Node *node) +{ + NodeDesc res{opname(node)}; + + for (uint32_t n = 0; n < node->arity(); ++n) + { + res.arg(std::string{"arg"} + std::to_string(n), tbl.lookup(node->arg(n))); + } + res.state(NodeDesc::State::PartiallyKnown); + + return res; +} + +class CanonicalNodeDescBuilder final : public loco::CanonicalNodeVisitor +{ +public: + CanonicalNodeDescBuilder(const SymbolTable *symtbl) : _symtbl{symtbl} + { + // DO NOTHING + } + +private: + std::string nodename(const loco::Node *node) const { return _symtbl->lookup(node); } + +public: + // TODO Build a node description for each canonical node + NodeDesc visit(const loco::Push *node) final + { + NodeDesc res{opname(node)}; + + res.arg("index", node->indexed() ? pp::fmt(node->index()) : pp::fmt('?')); + res.arg("from", nodename(node->from())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::Pull *node) final + { + NodeDesc res{opname(node)}; + + res.arg("index", node->indexed() ? pp::fmt(node->index()) : pp::fmt('?')); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::Forward *node) final + { + NodeDesc res{opname(node)}; + + res.arg("input", nodename(node->input())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::ConstGen *node) final + { + NodeDesc res{opname(node)}; + + // TODO Print data type + res.arg("shape", pp::fmt(pretty(tensor_shape(node)))); + res.state(NodeDesc::State::PartiallyKnown); + + return res; + } + + NodeDesc visit(const loco::TensorConcat *node) final + { + NodeDesc res{opname(node)}; + + res.arg("lhs", nodename(node->lhs())); + res.arg("rhs", nodename(node->rhs())); + res.arg("axis", pp::fmt(node->axis())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::EltwiseAdd *node) final + { + NodeDesc res{opname(node)}; + + res.arg("lhs", nodename(node->lhs())); + res.arg("rhs", nodename(node->rhs())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::EltwiseMul *node) final + { + NodeDesc res{opname(node)}; + + res.arg("lhs", nodename(node->lhs())); + res.arg("rhs", nodename(node->rhs())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::TensorReduce *node) final + { + NodeDesc res{opname(node)}; + + // TODO Print TensorAxisSet + res.arg("input", nodename(node->input())); + res.arg("func", pp::fmt((int32_t)node->func())); + + res.state(NodeDesc::State::PartiallyKnown); + + return res; + } + + NodeDesc visit(const loco::Reshape *node) final + { + NodeDesc res{opname(node)}; + + res.arg("input", nodename(node->input())); + res.arg("shape", pp::fmt(pretty(tensor_shape(node)))); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::Tanh *node) final + { + NodeDesc res{opname(node)}; + + res.arg("input", nodename(node->input())); + res.state(NodeDesc::State::Complete); + + return res; + } + + NodeDesc visit(const loco::TensorSoftmax *node) final + { + NodeDesc res{opname(node)}; + + res.arg("input", nodename(node->input())); + res.arg("axis", pp::fmt(node->axis())); + res.state(NodeDesc::State::Complete); + + return res; + } + +public: + NodeDesc visit(const loco::Node *node) final { return default_node_desc(*_symtbl, node); } + +private: + const SymbolTable *_symtbl; +}; + +NodeDesc canonical_node_desc(const SymbolTable &tbl, const loco::CanonicalNode *canonical_node) +{ + CanonicalNodeDescBuilder builder{&tbl}; + return canonical_node->accept(&builder); +} + +} // namespace + +namespace locop +{ + +bool CanonicalNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &out) const +{ + // Skip if a given node does not belong to loco.canonical + if (node->dialect() != loco::CanonicalDialect::get()) + { + return false; + } + + auto canonical_node = dynamic_cast(node); + assert(canonical_node != nullptr); + out = canonical_node_desc(*_tbl, canonical_node); + return true; +} + +} // namespace locop diff --git a/compiler/locop/src/ExampleGraph.h b/compiler/locop/src/ExampleGraph.h new file mode 100644 index 000000000..76813bcd8 --- /dev/null +++ b/compiler/locop/src/ExampleGraph.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __EXAMPLE_GRAPH_H__ +#define __EXAMPLE_GRAPH_H__ + +#include + +#include + +namespace +{ + +enum GraphCode +{ + PullPush, /* Pull - Push network */ +}; + +template struct Bundle; +template std::unique_ptr> make_bundle(void); + +template <> struct Bundle +{ + std::unique_ptr g; + loco::Pull *pull; + loco::Push *push; + + loco::Graph *graph(void) { return g.get(); } +}; + +template <> std::unique_ptr> make_bundle(void) +{ + auto g = loco::make_graph(); + + auto pull = g->nodes()->create(); + + pull->rank(2); + pull->dim(0) = loco::make_dimension(); // Mark dim 0 as unknown + pull->dim(1) = 4; + + auto push = g->nodes()->create(); + + push->from(pull); + + auto res = stdex::make_unique>(); + + res->g = std::move(g); + res->pull = pull; + res->push = push; + + return std::move(res); +} + +} // namespace + +#endif // __EXAMPLE_GRAPH_H__ diff --git a/compiler/locop/src/FormattedGraph.cpp b/compiler/locop/src/FormattedGraph.cpp new file mode 100644 index 000000000..84de1e888 --- /dev/null +++ b/compiler/locop/src/FormattedGraph.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/FormattedGraph.h" +#include "locop/FormattedTensorShape.h" +#include "locop/GenericNodeSummaryBuilder.h" + +#include +#include + +#include + +#include + +#include +#include + +#include + +using locop::SymbolTable; + +namespace +{ + +std::string str(const loco::DataType &dtype) +{ + switch (dtype) + { + case loco::DataType::Unknown: + return "Unknown"; + + case loco::DataType::U8: + return "U8"; + case loco::DataType::U16: + return "U16"; + case loco::DataType::U32: + return "U32"; + case loco::DataType::U64: + return "U64"; + + case loco::DataType::S8: + return "S8"; + case loco::DataType::S16: + return "S16"; + case loco::DataType::S32: + return "S32"; + case loco::DataType::S64: + return "S64"; + + case loco::DataType::FLOAT16: + return "FLOAT16"; + case loco::DataType::FLOAT32: + return "FLOAT32"; + case loco::DataType::FLOAT64: + return "FLOAT64"; + + default: + break; + }; + + throw std::invalid_argument{"dtype"}; +} + +std::string str(const loco::Domain &domain) +{ + // TODO Generate! + switch (domain) + { + case loco::Domain::Unknown: + return "Unknown"; + case loco::Domain::Tensor: + return "Tensor"; + case loco::Domain::Feature: + return "Feature"; + case loco::Domain::Filter: + return "Filter"; + case loco::Domain::DepthwiseFilter: + return "DWFilter"; + case loco::Domain::Bias: + return "Bias"; + default: + break; + } + + throw std::invalid_argument{"domain"}; +} + +std::string str(const loco::NodeShape &node_shape) +{ + using namespace locop; + + switch (node_shape.domain()) + { + case loco::Domain::Tensor: + { + auto tensor_shape = node_shape.as(); + return pp::fmt(locop::fmt(&tensor_shape)); + } + // TODO Show details + case loco::Domain::Feature: + case loco::Domain::Filter: + case loco::Domain::DepthwiseFilter: + case loco::Domain::Bias: + return "..."; + + default: + break; + } + + throw std::invalid_argument{"domain"}; +} + +// TODO Use locop::fmt +locop::FormattedTensorShape +formatted_tensor_shape(const loco::TensorShape *ptr) +{ + return locop::FormattedTensorShape{ptr}; +} + +} // namespace + +namespace +{ + +struct NodeDesc : public locop::NodeDesc +{ +public: + NodeDesc() = default; + NodeDesc(const locop::OpName &opname) : locop::NodeDesc{opname} + { + // DO NOTHING + } + +public: + // DEPRECATED + const locop::OpName &name(void) const { return opname(); } + + // DEPRECATED + uint32_t arg_size(void) const { return args().count(); } + // DEPRECATED + const locop::ArgElem &arg(uint32_t n) const { return args().at(n); } + // DEPRECATED + void arg(const locop::ArgName &name, const locop::ArgValue &value) { args().append(name, value); } +}; + +} // namespace + +// TODO Remove this workaround +namespace locop +{ + +std::ostream &operator<<(std::ostream &os, const NodeDesc &d) +{ + assert(d.state() != NodeDesc::State::Invalid); + + std::vector values; + + for (uint32_t n = 0; n < d.args().count(); ++n) + { + values.emplace_back(d.args().at(n).first + ": " + d.args().at(n).second); + } + + if (d.state() == NodeDesc::State::PartiallyKnown) + { + values.emplace_back("..."); + } + + os << d.opname(); + os << "("; + if (values.size() > 0) + { + os << values.at(0); + for (uint32_t n = 1; n < values.size(); ++n) + { + os << ", " << values.at(n); + } + } + os << ")"; + + return os; +} + +} // namespace locop + +namespace locop +{ + +std::ostream &operator<<(std::ostream &os, const FormattedGraph &fmt) +{ + fmt.dump(os); + return os; +} + +} // namespace locop + +namespace locop +{ + +void FormattedGraphImpl::dump(std::ostream &os) const +{ + struct SymbolTableImpl final : public SymbolTable + { + std::string lookup(const loco::Node *node) const final + { + if (node == nullptr) + { + return "(null)"; + } + + return _content.at(node); + } + + std::map _content; + }; + + SymbolTableImpl symbols; + + auto symbol = [&symbols](const loco::Node *node) { return symbols.lookup(node); }; + + for (uint32_t n = 0; n < _graph->nodes()->size(); ++n) + { + symbols._content[_graph->nodes()->at(n)] = pp::fmt("%", n); + } + + // Find the disjoint node clusters + // + // TODO Move this implementation into loco Algorithms.h + std::map parents; + + for (auto node : loco::all_nodes(_graph)) + { + parents[node] = nullptr; + } + + for (auto node : loco::all_nodes(_graph)) + { + for (uint32_t n = 0; n < node->arity(); ++n) + { + if (auto arg = node->arg(n)) + { + parents[arg] = node; + } + } + } + + auto find = [&parents](loco::Node *node) { + loco::Node *cur = node; + + while (parents.at(cur) != nullptr) + { + cur = parents.at(cur); + } + + return cur; + }; + + std::set roots; + + for (auto node : loco::all_nodes(_graph)) + { + roots.insert(find(node)); + } + + std::map> clusters; + + // Create clusters + for (auto root : roots) + { + clusters[root] = std::set{}; + } + + for (auto node : loco::all_nodes(_graph)) + { + clusters.at(find(node)).insert(node); + } + + std::unique_ptr node_summary_builder; + + if (_factory) + { + // Use User-defined NodeSummaryBuilder if NodeSummaryBuilderFactory is present + node_summary_builder = _factory->create(&symbols); + } + else + { + // Use Built-in NodeSummaryBuilder otherwise + node_summary_builder = stdex::make_unique(&symbols); + } + + // Print Graph Input(s) + for (uint32_t n = 0; n < _graph->inputs()->size(); ++n) + { + auto input = _graph->inputs()->at(n); + + std::string name = input->name(); + + std::string shape = "?"; + if (input->shape() != nullptr) + { + shape = pp::fmt(formatted_tensor_shape(input->shape())); + } + + // TODO Print dtype + os << pp::fmt("In #", n, " { name: ", name, ", shape: ", shape, " }") << std::endl; + } + + // Print Graph Output(s) + for (uint32_t n = 0; n < _graph->outputs()->size(); ++n) + { + auto output = _graph->outputs()->at(n); + + std::string name = output->name(); + + std::string shape = "?"; + if (output->shape() != nullptr) + { + shape = pp::fmt(formatted_tensor_shape(output->shape())); + } + + // TODO Print dtype + os << pp::fmt("Out #", n, " { name: ", name, ", shape: ", shape, " }") << std::endl; + } + + if (_graph->inputs()->size() + _graph->outputs()->size() != 0) + { + os << std::endl; + } + + for (auto it = clusters.begin(); it != clusters.end(); ++it) + { + std::vector cluster_outputs; + + for (auto node : it->second) + { + // NOTE This is inefficient but anyway working :) + if (loco::succs(node).empty()) + { + cluster_outputs.emplace_back(node); + } + } + + for (auto node : loco::postorder_traversal(cluster_outputs)) + { + locop::NodeSummary node_summary; + + // Build a node summary + if (!node_summary_builder->build(node, node_summary)) + { + throw std::runtime_error{"Fail to build a node summary"}; + } + + for (uint32_t n = 0; n < node_summary.comments().count(); ++n) + { + os << "; " << node_summary.comments().at(n) << std::endl; + } + + os << symbol(node); + + if (loco::shape_known(node)) + { + auto node_shape = loco::shape_get(node); + os << " : " << str(node_shape.domain()); + os << "<"; + os << str(node_shape); + os << ", "; + // Show DataType + os << (loco::dtype_known(node) ? str(loco::dtype_get(node)) : std::string{"?"}); + os << ">"; + } + + os << " = " << node_summary << std::endl; + } + os << std::endl; + } +} + +} // namespace locop diff --git a/compiler/locop/src/FormattedGraph.test.cpp b/compiler/locop/src/FormattedGraph.test.cpp new file mode 100644 index 000000000..c9808d3a2 --- /dev/null +++ b/compiler/locop/src/FormattedGraph.test.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/FormattedGraph.h" +#include "ExampleGraph.h" + +#include + +#include + +TEST(LinearV1FormatterTest, simple) +{ + auto bundle = make_bundle(); + auto g = bundle->graph(); + + // TODO Validate the output (when the implementation becomes stable) + std::cout << locop::fmt(g) << std::endl; +} + +TEST(LinearV1FormatterTest, user_defined_node_summary_builder) +{ + struct MyAnnotation final : public loco::NodeAnnotation + { + // DO NOTHING + }; + + auto bundle = make_bundle(); + auto g = bundle->graph(); + { + bundle->push->annot(stdex::make_unique()); + } + + struct MyBuilder final : public locop::NodeSummaryBuilder + { + bool build(const loco::Node *node, locop::NodeSummary &s) const final + { + s.opname("my.op"); + if (node->annot()) + { + s.comments().append("annotated"); + } + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; + } + }; + + struct MyFactory final : public locop::NodeSummaryBuilderFactory + { + std::unique_ptr create(const locop::SymbolTable *) const final + { + return stdex::make_unique(); + } + }; + + std::cout << locop::fmt(g).with(stdex::make_unique()) << std::endl; + + // TODO Check whether MyBuilder actually sees all the nodes in a graph + SUCCEED(); +} + +// This test shows how to compose two node summary builders. +TEST(LinearV1FormatterTest, node_summary_builder_composition) +{ + struct MyNode : public loco::FixedArity<0>::Mixin + { + uint32_t opnum(void) const final { return 0; } + const loco::Dialect *dialect(void) const final { return nullptr; }; + }; + + auto g = loco::make_graph(); + { + auto user = g->nodes()->create(); + + auto push = g->nodes()->create(); + + push->from(user); + } + + // TODO Reuse MyBuilder above + struct MyBuilder final : public locop::NodeSummaryBuilder + { + bool build(const loco::Node *node, locop::NodeSummary &s) const final + { + s.opname("my.op"); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; + } + }; + + class CompositeBuilder final : public locop::NodeSummaryBuilder + { + public: + CompositeBuilder(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + + public: + bool build(const loco::Node *node, locop::NodeSummary &s) const final + { + if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (MyBuilder().build(node, s)) + { + return true; + } + + return false; + } + + private: + const locop::SymbolTable *_tbl; + }; + + struct MyFactory final : public locop::NodeSummaryBuilderFactory + { + std::unique_ptr create(const locop::SymbolTable *tbl) const final + { + return stdex::make_unique(tbl); + } + }; + + std::cout << locop::fmt(g).with(stdex::make_unique()) << std::endl; + + // TODO Check whether MyBuilder actually sees all the nodes in a graph + SUCCEED(); +} diff --git a/compiler/locop/src/FormattedTensorShape.cpp b/compiler/locop/src/FormattedTensorShape.cpp new file mode 100644 index 000000000..b2b6ea074 --- /dev/null +++ b/compiler/locop/src/FormattedTensorShape.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/FormattedTensorShape.h" + +namespace loco +{ + +std::ostream &operator<<(std::ostream &os, const loco::Dimension &d) +{ + os << (d.known() ? std::to_string(d.value()) : std::string{"?"}); + return os; +} + +} // namespace + +namespace locop +{ + +void FormattedTensorShape::dump(std::ostream &os) const +{ + if (_ptr->rank() > 0) + { + os << _ptr->dim(0); + + for (uint32_t axis = 1; axis < _ptr->rank(); ++axis) + { + os << " x " << _ptr->dim(axis); + } + } +} + +} // namespace locop + +namespace locop +{ + +void FormattedTensorShape::dump(std::ostream &os) const +{ + os << "["; + + if (_ptr->rank() > 0) + { + os << " " << _ptr->dim(0); + + for (uint32_t axis = 1; axis < _ptr->rank(); ++axis) + { + os << " x " << _ptr->dim(axis); + } + } + + os << " ]"; +} + +} // namespace locop diff --git a/compiler/locop/src/FormattedTensorShape.test.cpp b/compiler/locop/src/FormattedTensorShape.test.cpp new file mode 100644 index 000000000..0f0017ab4 --- /dev/null +++ b/compiler/locop/src/FormattedTensorShape.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/FormattedTensorShape.h" + +#include + +#include + +using namespace locop; + +TEST(FormattedTensorShapeTest, BracketFormat) +{ + auto tensor_shape = stdex::make_unique(); + + tensor_shape->rank(2); + tensor_shape->dim(0) = 4; + + std::cout << fmt(tensor_shape.get()) << std::endl; +} diff --git a/compiler/locop/src/GenericNodeSummaryBuilder.cpp b/compiler/locop/src/GenericNodeSummaryBuilder.cpp new file mode 100644 index 000000000..e3bbe5aad --- /dev/null +++ b/compiler/locop/src/GenericNodeSummaryBuilder.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/GenericNodeSummaryBuilder.h" + +#include + +namespace locop +{ + +bool GenericNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &out) const +{ + out.opname(pp::fmt(node->dialect(), ".op_", node->opnum())); + + for (uint32_t n = 0; n < node->arity(); ++n) + { + out.args().append(pp::fmt("arg", n), _tbl->lookup(node->arg(n))); + } + + out.state(NodeDesc::State::PartiallyKnown); + + return true; +} + +} // namespace locop diff --git a/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp b/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp new file mode 100644 index 000000000..d688b5490 --- /dev/null +++ b/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/GenericNodeSummaryBuilder.h" +#include "locop/FormattedGraph.h" + +#include + +#include + +#include + +TEST(GenericNodeSummaryBuilderTest, simple) +{ + struct MockDialect final : public loco::Dialect + { + static Dialect *get(void) + { + static MockDialect d; + return &d; + } + }; + + struct MockNode : public loco::FixedArity<0>::Mixin + { + const loco::Dialect *dialect(void) const final { return MockDialect::get(); }; + uint32_t opnum(void) const final { return 0; } + }; + + struct MockFactory final : public locop::NodeSummaryBuilderFactory + { + std::unique_ptr create(const locop::SymbolTable *tbl) const final + { + return stdex::make_unique(tbl); + } + }; + + auto g = loco::make_graph(); + + g->nodes()->create(); + + std::cout << locop::fmt(g).with(stdex::make_unique()) << std::endl; + + SUCCEED(); +} diff --git a/compiler/locop/src/Interfaces.cpp b/compiler/locop/src/Interfaces.cpp new file mode 100644 index 000000000..14e0211ba --- /dev/null +++ b/compiler/locop/src/Interfaces.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/Interfaces.h" + +namespace locop +{ + +std::ostream &operator<<(std::ostream &os, const Spec &formatted) +{ + formatted.dump(os); + return os; +} + +} // namespace locop diff --git a/compiler/locop/src/NodeSummary.cpp b/compiler/locop/src/NodeSummary.cpp new file mode 100644 index 000000000..3f8856997 --- /dev/null +++ b/compiler/locop/src/NodeSummary.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/NodeSummary.h" + +#include + +#include + +namespace locop +{ + +void NodeDesc::Comments::append(const std::string &s) +{ + // TODO Check whether s contains any newline character + _lines.emplace_back(s); +} + +const std::string &NodeDesc::opname(void) const +{ + // _name SHOULD BE set before use + assert(_name != nullptr); + return *_name; +} + +void NodeDesc::opname(const std::string &v) { _name = stdex::make_unique(v); } + +} // namespace loco diff --git a/compiler/locop/src/NodeSummaryBuilder.cpp b/compiler/locop/src/NodeSummaryBuilder.cpp new file mode 100644 index 000000000..6610bf71f --- /dev/null +++ b/compiler/locop/src/NodeSummaryBuilder.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locop/NodeSummaryBuilder.h" + +// This file checks whether "NodeSummaryBuilder.h" is self-complete or not. +// +// WARNING!! Do NOT remove this file. diff --git a/compiler/logo-core/CMakeLists.txt b/compiler/logo-core/CMakeLists.txt new file mode 100644 index 000000000..3bc71dbd0 --- /dev/null +++ b/compiler/logo-core/CMakeLists.txt @@ -0,0 +1,19 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(logo_core STATIC ${SOURCES}) +set_target_properties(logo_core PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(logo_core PRIVATE src) +target_include_directories(logo_core PUBLIC include) +target_link_libraries(logo_core PUBLIC loco) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(logo_core_test ${TESTS}) +target_include_directories(logo_core_test PRIVATE src) +target_link_libraries(logo_core_test logo_core) diff --git a/compiler/logo-core/README.md b/compiler/logo-core/README.md new file mode 100644 index 000000000..0dee3954b --- /dev/null +++ b/compiler/logo-core/README.md @@ -0,0 +1,3 @@ +# logo-core + +_logo-core_ provides _loco_ General Graph Pass Core for Transformation and Optimization diff --git a/compiler/logo-core/include/logo/Pass.h b/compiler/logo-core/include/logo/Pass.h new file mode 100644 index 000000000..4f667f156 --- /dev/null +++ b/compiler/logo-core/include/logo/Pass.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_PASS_H__ +#define __LOGO_PASS_H__ + +#include + +#include + +namespace logo +{ + +class Pass +{ +public: + virtual ~Pass() = default; + +public: + virtual const char *name(void) const { return nullptr; } + +public: + /** + * @brief Run the pass + * + * @return false if there was nothing changed + */ + virtual bool run(loco::Graph *graph) = 0; +}; + +std::string pass_name(const Pass *); + +} // namespace logo + +#endif // __LOGO_PASS_H__ diff --git a/compiler/logo-core/include/logo/Phase.h b/compiler/logo-core/include/logo/Phase.h new file mode 100644 index 000000000..d1b7ccd5f --- /dev/null +++ b/compiler/logo-core/include/logo/Phase.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_PHASE_H__ +#define __LOGO_PHASE_H__ + +#include + +#include + +#include +#include + +namespace logo +{ + +// Phase is a collection of Pass(es) +using Phase = std::vector>; + +enum class PhaseEvent +{ + PhaseBegin, + PhaseEnd, + + PassBegin, + PassEnd, +}; + +template struct PhaseEventInfo; + +template <> class PhaseEventInfo +{ + // Empty +}; + +template <> class PhaseEventInfo +{ + // Empty +}; + +template <> class PhaseEventInfo +{ +public: + void pass(const Pass *pass) { _pass = pass; } + const Pass *pass(void) const { return _pass; } + +private: + const Pass *_pass; +}; + +template <> class PhaseEventInfo +{ +public: + void pass(const Pass *pass) { _pass = pass; } + const Pass *pass(void) const { return _pass; } + + void changed(bool changed) { _changed = changed; } + bool changed(void) const { return _changed; } + +private: + const Pass *_pass; + bool _changed; +}; + +struct PhaseEventListener +{ + virtual ~PhaseEventListener() = default; + + virtual void notify(const PhaseEventInfo *) { return; }; + virtual void notify(const PhaseEventInfo *) { return; }; + virtual void notify(const PhaseEventInfo *) { return; }; + virtual void notify(const PhaseEventInfo *) { return; }; +}; + +// TODO Will be other mix-ins for Phase Runners? +class PhaseRunnerMixinObservable +{ +public: + PhaseRunnerMixinObservable() = default; + +public: + virtual ~PhaseRunnerMixinObservable() = default; + +public: + void attach(PhaseEventListener *listener) { _listener = listener; } + +public: + void notifyPhaseBegin(void) const + { + if (_listener) + { + PhaseEventInfo info; + + _listener->notify(&info); + } + } + + void notifyPhaseEnd(void) const + { + if (_listener) + { + PhaseEventInfo info; + + _listener->notify(&info); + } + } + + void notifyPassBegin(Pass *pass) const + { + if (_listener) + { + PhaseEventInfo info; + + info.pass(pass); + + _listener->notify(&info); + } + } + + void notifyPassEnd(Pass *pass, bool changed) const + { + if (_listener) + { + PhaseEventInfo info; + + info.pass(pass); + info.changed(changed); + + _listener->notify(&info); + } + } + +private: + PhaseEventListener *_listener = nullptr; +}; + +enum class PhaseStrategy +{ + // Run all the passes until there is no pass that makes a change + Saturate, + // Same as Saturate but will restart from the first when there is a change + Restart, +}; + +template class PhaseRunner; + +template <> class PhaseRunner final : public PhaseRunnerMixinObservable +{ +public: + PhaseRunner(loco::Graph *graph) : _graph{graph} + { + // DO NOTHING + } + +public: + void run(const Phase &) const; + +private: + loco::Graph *_graph; +}; + +template <> class PhaseRunner final : public PhaseRunnerMixinObservable +{ +public: + PhaseRunner(loco::Graph *graph) : _graph{graph} + { + // DO NOTHING + } + +public: + void run(const Phase &) const; + +private: + loco::Graph *_graph; +}; + +} // namespace logo + +#endif // __LOGO_PHASE_H__ diff --git a/compiler/logo-core/requires.cmake b/compiler/logo-core/requires.cmake new file mode 100644 index 000000000..44f6870da --- /dev/null +++ b/compiler/logo-core/requires.cmake @@ -0,0 +1 @@ +require("loco") diff --git a/compiler/logo-core/src/Pass.cpp b/compiler/logo-core/src/Pass.cpp new file mode 100644 index 000000000..a44010760 --- /dev/null +++ b/compiler/logo-core/src/Pass.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace logo +{ + +std::string pass_name(const Pass *t) +{ + if (t->name() == nullptr) + { + return "(unknown)"; + } + + return t->name(); +} + +} // namespace logo diff --git a/compiler/logo-core/src/Pass.test.cpp b/compiler/logo-core/src/Pass.test.cpp new file mode 100644 index 000000000..b6bebff62 --- /dev/null +++ b/compiler/logo-core/src/Pass.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +TEST(LogoPassTests, pass_name_over_unnamed_pass) +{ + struct Bumblebee final : public logo::Pass + { + bool run(loco::Graph *) final { return false; } + }; + + Bumblebee bumblebee; + + ASSERT_EQ(logo::pass_name(&bumblebee), "(unknown)"); +} + +TEST(LogoPassTests, pass_name_over_named_pass) +{ + struct Bumblebee final : public logo::Pass + { + const char *name(void) const final { return "Bee"; } + bool run(loco::Graph *) final { return false; } + }; + + Bumblebee bumblebee; + + ASSERT_EQ(logo::pass_name(&bumblebee), "Bee"); +} diff --git a/compiler/logo-core/src/Phase.cpp b/compiler/logo-core/src/Phase.cpp new file mode 100644 index 000000000..b929a31ba --- /dev/null +++ b/compiler/logo-core/src/Phase.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace logo +{ + +void PhaseRunner::run(const Phase &phase) const +{ + notifyPhaseBegin(); + + for (bool changed = true; changed;) + { + changed = false; + + for (auto &pass : phase) + { + notifyPassBegin(pass.get()); + + bool pass_changed = pass->run(_graph); + changed = changed || pass_changed; + + notifyPassEnd(pass.get(), pass_changed); + } + } + + notifyPhaseEnd(); +} + +void PhaseRunner::run(const Phase &phase) const +{ + notifyPhaseBegin(); + + for (bool changed = true; changed;) + { + changed = false; + + for (auto &pass : phase) + { + notifyPassBegin(pass.get()); + + bool pass_changed = pass->run(_graph); + changed = changed || pass_changed; + + notifyPassEnd(pass.get(), pass_changed); + + if (changed) + { + break; + } + } + } + + notifyPhaseEnd(); +} + +} // namespace logo diff --git a/compiler/logo/CMakeLists.txt b/compiler/logo/CMakeLists.txt new file mode 100644 index 000000000..399cb7586 --- /dev/null +++ b/compiler/logo/CMakeLists.txt @@ -0,0 +1,23 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(logo STATIC ${SOURCES}) +set_target_properties(logo PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(logo PRIVATE src) +target_include_directories(logo PUBLIC include) +target_link_libraries(logo PUBLIC loco) +target_link_libraries(logo PUBLIC logo_core) +target_link_libraries(logo PRIVATE locomotiv) +target_link_libraries(logo PRIVATE stdex) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(logo_test ${TESTS}) +target_include_directories(logo_test PRIVATE src) +target_link_libraries(logo_test logo) +target_link_libraries(logo_test stdex) diff --git a/compiler/logo/README.md b/compiler/logo/README.md new file mode 100644 index 000000000..0cf1ba313 --- /dev/null +++ b/compiler/logo/README.md @@ -0,0 +1,3 @@ +# logo + +_logo_ provides _loco_ General Graph Passes for Transformation and Optimization diff --git a/compiler/logo/include/logo/ConstantFoldingPass.h b/compiler/logo/include/logo/ConstantFoldingPass.h new file mode 100644 index 000000000..99ccdc315 --- /dev/null +++ b/compiler/logo/include/logo/ConstantFoldingPass.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_CONSTANT_FOLDING_PASS_H__ +#define __LOGO_CONSTANT_FOLDING_PASS_H__ + +#include + +#include + +namespace logo +{ + +/** + * @brief Performs constant folding optimization + */ +class ConstantFoldingPass : public Pass +{ +public: + const char *name(void) const final { return "ConstantFoldingPass"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace logo + +#endif // __LOGO_CONSTANT_FOLDING_PASS_H__ diff --git a/compiler/logo/include/logo/Passes.h b/compiler/logo/include/logo/Passes.h new file mode 100644 index 000000000..636251e45 --- /dev/null +++ b/compiler/logo/include/logo/Passes.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_PASSES_H__ +#define __LOGO_PASSES_H__ + +// Please keep this in alphabetical order + +#include +#include +#include +#include +#include +#include +#include + +#endif // __LOGO_PASSES_H__ diff --git a/compiler/logo/include/logo/RemoveDeadNodePass.h b/compiler/logo/include/logo/RemoveDeadNodePass.h new file mode 100644 index 000000000..ae1c67feb --- /dev/null +++ b/compiler/logo/include/logo/RemoveDeadNodePass.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_REMOVE_DEAD_NODE_PASS_H__ +#define __LOGO_REMOVE_DEAD_NODE_PASS_H__ + +#include + +namespace logo +{ + +struct RemoveDeadNodePass final : public Pass +{ + const char *name(void) const final { return "RemoveDeadNodePass"; } + + bool run(loco::Graph *g); +}; + +} // namespace logo + +#endif // __LOGO_REMOVE_DEAD_NODE_PASS_H__ diff --git a/compiler/logo/include/logo/RemoveForwardNodePass.h b/compiler/logo/include/logo/RemoveForwardNodePass.h new file mode 100644 index 000000000..12437c43f --- /dev/null +++ b/compiler/logo/include/logo/RemoveForwardNodePass.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_REMOVE_FORWARD_NODE_PASS_H__ +#define __LOGO_REMOVE_FORWARD_NODE_PASS_H__ + +#include + +namespace logo +{ + +/** + * @brief Use the input of "Forward" node instead + * + * BEFORE: + * [X] -> [Forward] -> [Y] + * + * AFTER: + * [X] -> [Y] + * [Forward] + * + * NOTE This transform does not remove "Forward" node + */ +struct RemoveForwardNodePass final : public Pass +{ + const char *name(void) const final { return "RemoveForwardNodePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace logo + +#endif // __LOGO_REMOVE_FORWARD_NODE_PASS_H__ diff --git a/compiler/logo/include/logo/ReorderDecodePass.h b/compiler/logo/include/logo/ReorderDecodePass.h new file mode 100644 index 000000000..2f74c6afa --- /dev/null +++ b/compiler/logo/include/logo/ReorderDecodePass.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_REORDER_DECODE_PASS_H__ +#define __LOGO_REORDER_DECODE_PASS_H__ + +#include + +#include +#include + +namespace logo +{ + +/** + * @brief Reorder XXXDecode -> ? as ? -> XXXDecode if possible + * + * This transformation increases the chance of domain conversion simplification. + */ +template struct ReorderDecodePass; + +template <> struct ReorderDecodePass final : public Pass +{ + const char *name(void) const final { return "ReorderDecodePass "; } + + bool run(loco::Graph *g); +}; + +template <> struct ReorderDecodePass final : public Pass +{ + const char *name(void) const final { return "ReorderDecodePass "; } + + bool run(loco::Graph *g); +}; + +} // namespace logo + +#endif // __LOGO_REORDER_DECODE_PASS_H__ diff --git a/compiler/logo/include/logo/ResolveDuplicateReshapePass.h b/compiler/logo/include/logo/ResolveDuplicateReshapePass.h new file mode 100644 index 000000000..7e6c67fcd --- /dev/null +++ b/compiler/logo/include/logo/ResolveDuplicateReshapePass.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_RESOLVE_DUPLICATE_RESHAPE_PASS_H__ +#define __LOGO_RESOLVE_DUPLICATE_RESHAPE_PASS_H__ + +#include + +#include + +namespace logo +{ + +/** + * @brief Resolve duplicated Reshape nodes in a row + */ +class ResolveDuplicateReshapePass final : public Pass +{ +public: + const char *name(void) const final { return "ResolveDuplicateReshapePass"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace logo + +#endif // __LOGO_RESOLVE_DUPLICATE_RESHAPE_PASS_H__ diff --git a/compiler/logo/include/logo/ResolveRedundantReshapePass.h b/compiler/logo/include/logo/ResolveRedundantReshapePass.h new file mode 100644 index 000000000..3a2dc4f3d --- /dev/null +++ b/compiler/logo/include/logo/ResolveRedundantReshapePass.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_RESOLVE_REDUNDANT_RESHAPE_PASS_H__ +#define __LOGO_RESOLVE_REDUNDANT_RESHAPE_PASS_H__ + +#include + +#include + +namespace logo +{ + +/** + * @brief Remove redundant canonical FixedReshape + * + * @note To effectively run this transform, canonical shape inference should be + * done ahead + */ +class ResolveRedundantReshapePass final : public Pass +{ +public: + const char *name(void) const final { return "ResolveRedundantReshapePass"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace logo + +#endif // __LOGO_RESOLVE_REDUNDANT_RESHAPE_PASS_H__ diff --git a/compiler/logo/include/logo/SimplifyDomainConversionPass.h b/compiler/logo/include/logo/SimplifyDomainConversionPass.h new file mode 100644 index 000000000..551806f60 --- /dev/null +++ b/compiler/logo/include/logo/SimplifyDomainConversionPass.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOGO_SIMPLIFY_DOMAIN_CONVERSION_H__ +#define __LOGO_SIMPLIFY_DOMAIN_CONVERSION_H__ + +#include + +namespace logo +{ + +/** + * @brief Simplify redundant domain conversion + * + * SimplifyDomainConversionPass recognizes the following patterns: + * - FeatureDecode followed by FeatureEncode (Feature -> Tensor -> Feature) + * - FeatureEncode followed by FeatureDecode (Tensor -> Feature -> Tensor) + * - FilterEncode followed by FilterDecode (Tensor -> Filter -> Tensor) + * - BiasEncode followed by BiasDecode (Tensor -> Bias -> Tensor) + * - DepthwiseFilterEncode followed by DepthwiseFilterDecode (Tensor -> DepthwiseFilter -> Tensor) + * - MatrixDecode followed by MatrixEncode (Matrix -> Tensor -> Matrix) + * - MatrixEncode followed by MatrixDecode (Tensor -> Matrix -> Tensor) + * - (TO BE ADDED) + */ +struct SimplifyDomainConversionPass final : public Pass +{ + const char *name(void) const final { return "SimplifyDomainConversionPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace logo + +#endif // __LOGO_SIMPLIFY_DOMAIN_CONVERSION_H__ diff --git a/compiler/logo/requires.cmake b/compiler/logo/requires.cmake new file mode 100644 index 000000000..9a7d14788 --- /dev/null +++ b/compiler/logo/requires.cmake @@ -0,0 +1,4 @@ +require("loco") +require("logo-core") +require("locomotiv") +require("stdex") diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.cpp new file mode 100644 index 000000000..e038e7140 --- /dev/null +++ b/compiler/logo/src/Passes/ConstantFoldingPass.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include + +#include +#include + +namespace +{ + +uint64_t num_elements(const loco::NodeMixin &shape) +{ + if (shape.rank() == 0) + { + return 0; + } + + uint64_t res = 1; + + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + assert(shape.dim(axis).known()); + res *= shape.dim(axis).value(); + } + + return res; +} + +/// @brief For some op, constant folding should not be performed. This returns true if node is such +/// op. +bool skip(const loco::Node *node) +{ + static std::set skip_op = { + // TODO Current implementation works for 'Tensor' domain only. Support other domains such as + // `Feature`, `Filter`, `Bias`, etc. + static_cast(loco::CanonicalOpcode::FilterEncode), + static_cast(loco::CanonicalOpcode::FeatureEncode), + static_cast(loco::CanonicalOpcode::BiasEncode), + static_cast(loco::CanonicalOpcode::DepthwiseFilterEncode), + + // We don't perform constant folding for Push + static_cast(loco::CanonicalOpcode::Push), + + // TensorBroadcast is a good hint for optimization + // TODO Let this option be controlled by driver using logo + static_cast(loco::CanonicalOpcode::TensorBroadcast), + }; + + if (node->dialect() == loco::CanonicalDialect::get()) + { + if (skip_op.find(node->opnum()) != skip_op.end()) + return true; + } + + return false; +} + +/// @brief Checks if a node is a target of constant folding transform +bool foldable(const loco::Node *node) +{ + if (node->dialect() == loco::CanonicalDialect::get()) + { + if (skip(node)) + return false; + + if (node->arity() == 0) // e.g., when a node is e.g, ConstGen or Pull + return false; + + // When all args are ConstGen, let's do Constant Folding Transforms + for (int i = 0; i < node->arity(); i++) + { + if (node->arg(i)->opnum() != static_cast(loco::CanonicalOpcode::ConstGen)) + return false; + } + + return true; + } + else + { + return false; + } +} + +void fold(loco::Graph *graph, loco::Node *node) +{ + assert(foldable(node)); // sanity check to find a mistake when this function is reused later + + // calcluate foldable node + locomotiv::Session sess(graph, std::vector{node}); + sess.infer(); + auto data = sess.get_output(0); + + assert(data != nullptr); + + auto shape = data->shape(); + auto dtype = data->dtype(); + + // build ConstGen + auto new_const = graph->nodes()->create(); + { + new_const->dtype(dtype); + + new_const->rank(shape->rank()); + for (int d = 0; d < shape->rank(); d++) + new_const->dim(d) = shape->dim(d); + + auto count = num_elements(*new_const); + + if (dtype == loco::DataType::FLOAT32) + { + new_const->size(count); + + auto const_buf = data->as_f32_bufptr()->base(); + for (int x = 0; x < count; x++) + new_const->at(x) = const_buf[x]; + } + else if (dtype == loco::DataType::S32) + { + new_const->size(count); + + auto const_buf = data->as_s32_bufptr()->base(); + for (int x = 0; x < count; x++) + new_const->at(x) = const_buf[x]; + } + } + + // replace node with new_const + loco::replace(node).with(new_const); +} + +} // namespace + +namespace logo +{ + +bool ConstantFoldingPass::run(loco::Graph *graph) +{ + auto outputs = loco::output_nodes(graph); + + bool changed = false; + for (auto node : loco::postorder_traversal(outputs)) + { + if (foldable(node)) + { + fold(graph, node); + changed = true; + } + } + + return changed; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp new file mode 100644 index 000000000..824027762 --- /dev/null +++ b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "TestHelper.h" + +#include + +#include + +using namespace logo::test; + +namespace +{ + +/* + test case: + ConstGen ---- Relu ---- Push + (-3.14, 3.14) (0, 3.14) + + after constant folding: + ConstGen ------Push + (0, 3.14) +*/ +void create_net_const_relu(loco::Graph *graph) +{ + assert(graph); + + auto const_node = graph->nodes()->create(); + { + const_node->dtype(loco::DataType::FLOAT32); + const_node->rank(1); + const_node->dim(0) = 2; + const_node->size(2); + const_node->at(0) = -3.14f; + const_node->at(1) = 3.14f; + } + + auto relu_node = graph->nodes()->create(); + { + relu_node->input(const_node); + } + + auto push_node = graph->nodes()->create(); + { + push_node->from(relu_node); + } + + auto graph_output = graph->outputs()->create(); + { + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); + } +} + +} // namespace + +TEST(ConstantFolding, const_relu_to_const) +{ + auto graph = loco::make_graph(); + create_net_const_relu(graph.get()); + + logo::ConstantFoldingPass pass; + while (pass.run(graph.get()) == true) + { + ; + } + + auto push = logo::test::find_first_node_by_type(graph.get()); + auto const_gen = dynamic_cast(push->from()); + ASSERT_NE(const_gen, nullptr); + + ASSERT_EQ(const_gen->size(), 2); + ASSERT_EQ(const_gen->at(0), 0); // result of relu(-3.14) + ASSERT_EQ(const_gen->at(1), 3.14f); +} + +namespace +{ + +/* + test case: + ConstGen ---- Relu ---+ + (-1, 1) (0, 1) | + ConstGen ---+-- ConcatV2 ----- Push + (2, 3) | (0, 1, 2, 3) + axis(0) ---+ + + after constant folding: + ConstGen ----- Push + (0, 1, 2, 3) +*/ +void create_net_const_relu_concat(loco::Graph *graph) +{ + assert(graph); + + auto const_1_node = graph->nodes()->create(); + { + const_1_node->dtype(loco::DataType::FLOAT32); + const_1_node->rank(1); + const_1_node->dim(0) = 2; + const_1_node->size(2); + const_1_node->at(0) = -1.0f; + const_1_node->at(1) = 1.0f; + } + + auto relu_node = graph->nodes()->create(); + { + relu_node->input(const_1_node); + } + + auto const_2_node = graph->nodes()->create(); + { + const_2_node->dtype(loco::DataType::FLOAT32); + const_2_node->rank(1); + const_2_node->dim(0) = 2; + const_2_node->size(2); + const_2_node->at(0) = 2.0f; + const_2_node->at(1) = 3.0f; + } + + auto concat_node = graph->nodes()->create(); + { + concat_node->lhs(relu_node); + concat_node->rhs(const_2_node); + concat_node->axis(0); + } + + auto push_node = graph->nodes()->create(); + { + push_node->from(concat_node); + } + + auto graph_output = graph->outputs()->create(); + { + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); + } +} + +} // namespace + +TEST(ConstantFolding, const_relu_to_concat) +{ + auto graph = loco::make_graph(); + create_net_const_relu_concat(graph.get()); + + logo::ConstantFoldingPass pass; + while (pass.run(graph.get()) == true) + { + ; + } + + auto push = logo::test::find_first_node_by_type(graph.get()); + auto const_gen = dynamic_cast(push->from()); + ASSERT_NE(const_gen, nullptr); + + ASSERT_EQ(const_gen->size(), 4); + ASSERT_EQ(const_gen->at(0), 0); + ASSERT_EQ(const_gen->at(1), 1); + ASSERT_EQ(const_gen->at(2), 2); + ASSERT_EQ(const_gen->at(3), 3); +} diff --git a/compiler/logo/src/Passes/RemoveDeadNodePass.cpp b/compiler/logo/src/Passes/RemoveDeadNodePass.cpp new file mode 100644 index 000000000..9b6ed6ab0 --- /dev/null +++ b/compiler/logo/src/Passes/RemoveDeadNodePass.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +namespace logo +{ + +bool RemoveDeadNodePass::run(loco::Graph *g) +{ + // Let's enumerate nodes required to compute output nodes + auto active_nodes = loco::active_nodes(loco::output_nodes(g)); + + // Find dead(= non-active) nodes + std::set candidates; + + for (auto node : loco::all_nodes(g)) + { + if (active_nodes.find(node) == active_nodes.end()) + { + candidates.insert(node); + } + } + + // Let's drop the references from each dead node first and then remove these dead nodes + // + // Why? + // + // Let us consider the following example: + // %0 = Pull(...) + // %1 = ConstGen(...) + // %2 = Forward(input: %1) + // %3 = Push(from: %0) <- OUTPUT + // + // Forward (%2) is dead as it does not contribute to the final result (%3). However, it + // refers to another dead node (%1). + // + // This example indicates that naive implementation results in dangling references. + // + // There are two possible solutions: + // 1. Destroy nodes in topological order + // 2. Drop the reference first and then destroy them + // + // The current implementation takes the latter approach for the simplicity of implementation. + for (auto node : candidates) + { + node->drop(); + } + + for (auto node : candidates) + { + g->nodes()->destroy(node); + } + + return candidates.size() > 0; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/RemoveForwardNodePass.cpp b/compiler/logo/src/Passes/RemoveForwardNodePass.cpp new file mode 100644 index 000000000..c951cfac4 --- /dev/null +++ b/compiler/logo/src/Passes/RemoveForwardNodePass.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +namespace logo +{ + +bool RemoveForwardNodePass::run(loco::Graph *g) +{ + struct Collector final : public loco::CanonicalNodeMutableVisitor + { + void visit(loco::Forward *node) final + { + if (node->input() != nullptr) + { + candidates.insert(node); + } + } + + void visit(loco::Node *) final { return; } + + std::set candidates; + }; + + Collector collector; + + for (auto node : loco::all_nodes(g)) + { + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto canonical_node = dynamic_cast(node); + canonical_node->accept(&collector); + } + } + + for (auto node : collector.candidates) + { + replace(node).with(node->input()); + node->input(nullptr); + } + + return collector.candidates.size() > 0; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/ReorderDecodePass.cpp b/compiler/logo/src/Passes/ReorderDecodePass.cpp new file mode 100644 index 000000000..724db5780 --- /dev/null +++ b/compiler/logo/src/Passes/ReorderDecodePass.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include +#include + +namespace +{ + +bool isTensorBiasAdd(const loco::Node *node) +{ + return node->opnum() == static_cast(loco::CanonicalOpcode::TensorBiasAdd); +} + +bool isReLU(const loco::Node *node) +{ + return node->opnum() == static_cast(loco::CanonicalOpcode::ReLU); +} + +} // namespace + +namespace logo +{ + +bool ReorderDecodePass::run(loco::Graph *g) +{ + std::queue q; + + // Update queue + class Collector final : public loco::CanonicalNodeMutableVisitor + { + public: + Collector(std::queue *out) : _out{out} + { + // DO NOTHING + } + + void visit(loco::FeatureDecode *node) final + { + if (node->input() != nullptr) + { + _out->push(node); + } + } + + void visit(loco::Node *) final { return; } + + private: + // TODO This definition should be revised to support other decode operations + std::queue *_out; + }; + + Collector collector{&q}; + + for (auto node : loco::all_nodes(g)) + { + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto canonical_node = dynamic_cast(node); + canonical_node->accept(&collector); + } + } + + bool changed = false; + + while (!q.empty()) + { + auto cur_decode = q.front(); + q.pop(); + + // Collector IS EXPECTED TO guarantee this property + assert(cur_decode->input() != nullptr); + + for (auto u : loco::succs(cur_decode)) + { + /** + * Let us consider the following graph: + * + * A ---> FeatureDecode(1) ---> ReLU(2) + * + * ReorderDecodeTransform rewrites this graph as follows: + * + * A -+-> FeatureDecode(1) ---> ReLU(2) + * | + * +-> ReLU(2') ---> FeatureDecode(1') + * + * Let us feed this updates graph to ReorderDecodeTransform. + * + * The naive implementation will create a new ReLU->FeatureDecode + * chain again, and results in unbounded graph blow-up. + * + * A -+-> FeatureDeocde(1) ---> ReLU(2) + * | + * +-> ReLU(2') ---> FeatureDecode(1') + * | + * +-> ReLU(2'') ---> FeatureDecode(1'') + * + * This check prevents such unbounded graph blow-up. + */ + if (loco::succs(u).empty()) + { + continue; + } + + // Q. Is it better to create an independent transform for this rewriting rule? + if (isTensorBiasAdd(u)) + { + auto old_badd = dynamic_cast(u); + + assert(old_badd != nullptr); + + /** + * Let us consider the following example: + * + * A -=-> FeatureDecode(1) -+-> TensorBiasAdd(2) -+-> B1 + * | | + * | +-> B2 + * | | + * | +-> ... + * | + * +-> ... + * + * At this point, "cur_decode" points to (1) and "u" points to (2). + * + * First rewrite the graph as follows: + * + * A -+-> FeatureBiasAdd(2') ---> FeatureDecode(1') -+-> B1 + * | | + * | +-> B2 + * | | + * | +-> ... + * | + * +-> FeatureDecode(1) -+-> TensorBiasAdd(2) ; NO USE + * | + * +-> ... + * + * Q. Is it safe to apply this transform without "decoder" check? + */ + auto new_badd = g->nodes()->create(); + auto new_decode = g->nodes()->create(); + + new_badd->value(cur_decode->input()); + new_badd->bias(old_badd->bias()); + + new_decode->input(new_badd); + new_decode->decoder(cur_decode->decoder()->clone()); + + loco::replace(u).with(new_decode); + + // Enque FeatureDeocde(1') for the further optimization. + q.push(new_decode); + + changed = true; + } + } + } + + return changed; +} + +bool ReorderDecodePass::run(loco::Graph *g) +{ + std::queue q; + + // Update queue + class Collector final : public loco::CanonicalNodeMutableVisitor + { + public: + Collector(std::queue *out) : _out{out} + { + // DO NOTHING + } + + void visit(loco::FeatureDecode *node) final + { + if (node->input() != nullptr) + { + _out->push(node); + } + } + + void visit(loco::Node *) final { return; } + + private: + // TODO This definition should be revised to support other decode operations + std::queue *_out; + }; + + Collector collector{&q}; + + for (auto node : loco::all_nodes(g)) + { + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto canonical_node = dynamic_cast(node); + canonical_node->accept(&collector); + } + } + + bool changed = false; + + while (!q.empty()) + { + auto cur_decode = q.front(); + q.pop(); + + // Collector IS EXPECTED TO guarantee this property + assert(cur_decode->input() != nullptr); + + for (auto u : loco::succs(cur_decode)) + { + /** + * Let us consider the following graph: + * + * A ---> FeatureDecode(1) ---> ReLU(2) + * + * ReorderDecodeTransform rewrites this graph as follows: + * + * A -+-> FeatureDecode(1) ---> ReLU(2) + * | + * +-> ReLU(2') ---> FeatureDecode(1') + * + * Let us feed this updates graph to ReorderDecodeTransform. + * + * The naive implementation will create a new ReLU->FeatureDecode + * chain again, and results in unbounded graph blow-up. + * + * A -+-> FeatureDeocde(1) ---> ReLU(2) + * | + * +-> ReLU(2') ---> FeatureDecode(1') + * | + * +-> ReLU(2'') ---> FeatureDecode(1'') + * + * This check prevents such unbounded graph blow-up. + */ + if (loco::succs(u).empty()) + { + continue; + } + + if (isReLU(u)) + { + /** + * Let us consider the following example: + * + * A -=-> FeatureDecode(1) -+-> ReLU(2) -+-> B1 + * | | + * | +-> B2 + * | | + * | +-> ... + * | + * +-> ... + * + * At this point, "cur_decode" points to FeatureDecode(1) and "u" points to ReLU(2). + * + * First rewrite the graph as follows: + * + * A -+-> ReLU(2') ---> FeatureDecode(1') -+-> B1 + * | | + * | +-> B2 + * | | + * | +-> ... + * | + * +-> FeatureDecode -+-> ReLU(2) ; NO USE + * | + * +-> ... + */ + auto new_relu = g->nodes()->create(); + auto new_decode = g->nodes()->create(); + + new_relu->input(cur_decode->input()); + + new_decode->input(new_relu); + new_decode->decoder(cur_decode->decoder()->clone()); + + loco::replace(u).with(new_decode); + + /** + * Enque FeatureDeocde(1') for the further optimization. + */ + q.push(new_decode); + + changed = true; + } + } + } + + return changed; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp new file mode 100644 index 000000000..d3c74cb77 --- /dev/null +++ b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +namespace +{ + +/// @return true when 'node' and its input node are both FixedReshapes +bool is_duplicate_reshape(loco::Node *node) +{ + auto node_as_reshape = dynamic_cast(node); + + if (!node_as_reshape) + return false; + + auto input_as_reshape = dynamic_cast(node_as_reshape->input()); + + if (!input_as_reshape) + return false; + + return true; +} + +/** + * @brief Remap reshape's input to its input's input, i.e. bypass input reshape + * + * Before: + * + * In ----- FixedReshape_1 ----- [Out_1]* + * \ + * ------- FixedReshape_2 --- [Out_2]* + * ('reshape' arg) + * + * After: + * + * In ----- FixedReshape_1 ----- [Out_1]* + * \ + * --------------------------- FixedReshape_2 --- [Out_2]* + * + * Note: In case of no Out_1, FixedReshape_1 becomes dead node. + * Out_1 can be another FixedReshape as well, which would be resolved in + * another occurance of this transform pass. + */ +void remap_input(loco::FixedReshape *reshape) +{ + auto input_reshape = dynamic_cast(reshape->input()); + + auto volume = [](loco::FixedReshape *node) { + uint32_t vol = 1; + for (uint32_t axis = 0; axis < node->rank(); ++axis) + { + assert(node->dim(axis).known()); + vol *= node->dim(axis).value(); + } + return vol; + }; + + // Volume mismatch between duplicate reshapes is pointless + assert(volume(reshape) == volume(input_reshape)); + + // Set node's input as input's input, i.e. bypass + reshape->input(input_reshape->input()); +} + +} // namespace + +namespace logo +{ + +bool ResolveDuplicateReshapePass::run(loco::Graph *graph) +{ + auto outputs = loco::output_nodes(graph); + + bool changed = false; + for (auto node : loco::postorder_traversal(outputs)) + { + if (is_duplicate_reshape(node)) + { + auto node_as_reshape = dynamic_cast(node); + + remap_input(node_as_reshape); + + changed = true; + } + } + + return changed; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/ResolveRedundantReshapePass.cpp b/compiler/logo/src/Passes/ResolveRedundantReshapePass.cpp new file mode 100644 index 000000000..da4af15c1 --- /dev/null +++ b/compiler/logo/src/Passes/ResolveRedundantReshapePass.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#include + +namespace +{ + +bool shape_inference_done(loco::FixedReshape *reshape) +{ + return loco::shape_known(reshape) && loco::shape_known(reshape->input()); +} + +bool are_same_tensor_shapes(const loco::NodeShape &lhs, const loco::NodeShape &rhs) +{ + assert(lhs.domain() == loco::Domain::Tensor); + assert(rhs.domain() == loco::Domain::Tensor); + + auto lts = lhs.as(); + auto rts = rhs.as(); + + if (lts.rank() != rts.rank()) + return false; + + for (uint32_t axis = 0; axis < lts.rank(); ++axis) + { + assert(lts.dim(axis).known()); + assert(rts.dim(axis).known()); + if (lts.dim(axis).value() != rts.dim(axis).value()) + return false; + } + return true; +} + +/// @return true when 'reshape' has same input and output shape +bool is_redundant_reshape(loco::FixedReshape *reshape) +{ + auto input_shape = loco::shape_get(reshape->input()); + auto output_shape = loco::shape_get(reshape); + + // Note that FixedReshape's input and output are always tensor + return are_same_tensor_shapes(input_shape, output_shape); +} + +} // namespace + +namespace logo +{ + +/** + * @brief Bypass redundant FixedReshape + * + * Before: + * + * In ----- FixedReshape ----- [Out]* + * + * After: + * + * In ------------------------ [Out]* + * \ + * ------ FixedReshape + */ +bool ResolveRedundantReshapePass::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::postorder_traversal(loco::output_nodes(graph))) + { + if (auto reshape = dynamic_cast(node)) + { + if (shape_inference_done(reshape)) + { + if (is_redundant_reshape(reshape)) + { + replace(reshape).with(reshape->input()); + changed = true; + } + } + } + } + + return changed; +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp new file mode 100644 index 000000000..9b7a8d1c7 --- /dev/null +++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace +{ + +using namespace loco; + +// TODO Move this helper into loco +bool equal(const Permutation *lhs, const Permutation *rhs) +{ + for (const auto &axis : + {FeatureAxis::Count, FeatureAxis::Depth, FeatureAxis::Height, FeatureAxis::Width}) + { + if (lhs->axis(axis) != rhs->axis(axis)) + { + return false; + } + } + return true; +} + +bool equal(const Permutation *lhs, const Permutation *rhs) +{ + for (const auto &axis : + {FilterAxis::Count, FilterAxis::Depth, FilterAxis::Height, FilterAxis::Width}) + { + if (lhs->axis(axis) != rhs->axis(axis)) + { + return false; + } + } + return true; +} + +bool equal(const Permutation *lhs, + const Permutation *rhs) +{ + for (const auto &axis : {DepthwiseFilterAxis::Depth, DepthwiseFilterAxis::Multiplier, + DepthwiseFilterAxis::Height, DepthwiseFilterAxis::Width}) + { + if (lhs->axis(axis) != rhs->axis(axis)) + { + return false; + } + } + return true; +} + +bool equal(const Permutation *lhs, const Permutation *rhs) +{ + for (const auto &axis : {MatrixAxis::Height, MatrixAxis::Width}) + { + if (lhs->axis(axis) != rhs->axis(axis)) + { + return false; + } + } + return true; +} + +void set_input_null(loco::Node *node) +{ + if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else if (auto casted = dynamic_cast(node)) + casted->input(nullptr); + else + assert(false && "not supported node type"); +} + +} // namespace + +namespace logo +{ + +bool SimplifyDomainConversionPass::run(loco::Graph *g) +{ + // TODO Introduce and Use "Pattern Match" + struct Collector final : public loco::CanonicalNodeMutableVisitor + { + // Let's find FeatureDecode followed by FeatureEncode + void visit(loco::FeatureEncode *encode_node) final + { + using namespace loco; + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decode_node = dynamic_cast(encode_node->input()); + if (decode_node == nullptr) + { + return; + } + assert(decode_node->input() != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({encode_node, decode_node->input()}); + } + } + + // Let's find `FeatureEncode -- FeatureDecode` pattern + void visit(loco::FeatureDecode *decode_node) final + { + using namespace loco; + + auto encode_node = dynamic_cast(decode_node->input()); + if (encode_node == nullptr) + { + return; + } + assert(encode_node->input() != nullptr); + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({decode_node, encode_node->input()}); + } + } + + // Let's find `FilterEncode -- FilterDecode` pattern + void visit(loco::FilterDecode *decode_node) final + { + using namespace loco; + + auto encode_node = dynamic_cast(decode_node->input()); + if (encode_node == nullptr) + { + return; + } + assert(encode_node->input() != nullptr); + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({decode_node, encode_node->input()}); + } + else + { + std::vector perm_vec; + perm_vec.resize(4); + + auto enc_perm = perm_encoder->perm(); + auto dec_perm = perm_decoder->perm(); + + for (const auto &axis : + {FilterAxis::Count, FilterAxis::Height, FilterAxis::Width, FilterAxis::Depth}) + { + auto from = enc_perm->axis(axis); + auto to = dec_perm->axis(axis); + perm_vec[to] = from; + } + + transposeCandidates.insert(stdex::make_unique( + encode_node, decode_node, encode_node->input(), perm_vec)); + } + } + + // Let's find `BiasEncode -- BiasDecode` pattern + void visit(loco::BiasDecode *decode_node) final + { + if (auto encode_node = dynamic_cast(decode_node->input())) + { + assert(encode_node->input() != nullptr); + forwardCandidates.insert({decode_node, encode_node->input()}); + } + } + + // Let's find `DepthwiseFilterEncode -- DepthwiseFilterDecode` pattern + void visit(loco::DepthwiseFilterDecode *decode_node) final + { + using namespace loco; + + auto encode_node = dynamic_cast(decode_node->input()); + if (encode_node == nullptr) + { + return; + } + assert(encode_node->input() != nullptr); + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({decode_node, encode_node->input()}); + } + else + { + std::vector perm_vec; + perm_vec.resize(4); + + auto enc_perm = perm_encoder->perm(); + auto dec_perm = perm_decoder->perm(); + + for (const auto &axis : {DepthwiseFilterAxis::Depth, DepthwiseFilterAxis::Height, + DepthwiseFilterAxis::Width, DepthwiseFilterAxis::Multiplier}) + { + auto from = enc_perm->axis(axis); + auto to = dec_perm->axis(axis); + perm_vec[to] = from; + } + + transposeCandidates.insert(stdex::make_unique( + encode_node, decode_node, encode_node->input(), perm_vec)); + } + } + + // Let's find MatrixDecode followed by MatrixEncode + void visit(loco::MatrixEncode *encode_node) final + { + using namespace loco; + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decode_node = dynamic_cast(encode_node->input()); + if (decode_node == nullptr) + { + return; + } + assert(decode_node->input() != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({encode_node, decode_node->input()}); + } + } + + // Let's find MatrixEncode followed by MatrixDecode + void visit(loco::MatrixDecode *decode_node) final + { + using namespace loco; + + auto encode_node = dynamic_cast(decode_node->input()); + if (encode_node == nullptr) + { + return; + } + assert(encode_node->input() != nullptr); + + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + + auto decoder = decode_node->decoder(); + assert(decoder != nullptr); + + // NOTE Work only for permuting codec + auto perm_decoder = dynamic_cast *>(decoder); + auto perm_encoder = dynamic_cast *>(encoder); + + if (perm_encoder == nullptr || perm_decoder == nullptr) + { + return; + } + + if (equal(perm_encoder->perm(), perm_decoder->perm())) + { + forwardCandidates.insert({decode_node, encode_node->input()}); + } + else + { + std::vector perm_vec; + perm_vec.resize(2); + + auto enc_perm = perm_encoder->perm(); + auto dec_perm = perm_decoder->perm(); + + for (const auto &axis : {MatrixAxis::Height, MatrixAxis::Width}) + { + auto from = enc_perm->axis(axis); + auto to = dec_perm->axis(axis); + perm_vec[to] = from; + } + + transposeCandidates.insert(stdex::make_unique( + encode_node, decode_node, encode_node->input(), perm_vec)); + } + } + + void visit(loco::Node *) final { return; } + + using SimplifyingInfo = std::pair; + std::set forwardCandidates; + + struct TransposeCtx + { + loco::Node *first_node; // starting node of subgraph that will be replaced + loco::Node *last_node; // end node of subgraph that will be replaced + loco::Node *input_node; // input of subgraph + std::vector perm_vec; // perm vector for transpose + + TransposeCtx(loco::Node *first, loco::Node *last, loco::Node *input, + std::vector perm) + : first_node(first), last_node(last), input_node(input), perm_vec(perm) + { /* empty */ + } + }; + + std::set> transposeCandidates; + }; + + Collector collector; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (node->dialect() == loco::CanonicalDialect::get()) + { + auto canonical_node = dynamic_cast(node); + canonical_node->accept(&collector); + } + } + + for (auto p : collector.forwardCandidates) + { + auto forward_node = g->nodes()->create(); + forward_node->input(p.second); + replace(p.first).with(forward_node); + set_input_null(p.first); + } + + for (auto &ctx : collector.transposeCandidates) + { + auto transpose_node = g->nodes()->create(); + { + transpose_node->perm()->size(ctx->perm_vec.size()); + + for (loco::TensorAxis axis = 0; axis < ctx->perm_vec.size(); axis++) + transpose_node->perm()->axis(axis) = ctx->perm_vec[axis]; + } + + transpose_node->input(ctx->input_node); + replace(ctx->last_node).with(transpose_node); + set_input_null(ctx->first_node); + } + + return (collector.forwardCandidates.size() > 0 or collector.transposeCandidates.size() > 0); +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp new file mode 100644 index 000000000..6bd93c1b2 --- /dev/null +++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "TestHelper.h" + +#include +#include + +#include + +namespace +{ + +// code borrowed from GraphBlock.h/cpp in exo-tflite +enum class FilterLayout +{ + OHWI, // a.k.a., NHWC, Tensorflow Lite uses this layout + HWIO, // Tensorflow format +}; + +template loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + // Make NHWC permutation for encoder and decoder + loco::Permutation 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 <> loco::Permutation perm() +{ + // Make NHWC permutation for encoder and decoder + loco::Permutation HWIO; + + 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::FilterDecode *make_filter_decode(loco::Node *input_for_decode) +{ + loco::Graph *g = input_for_decode->graph(); + + auto decoder = stdex::make_unique>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode) +{ + loco::Graph *g = input_for_encode->graph(); + + auto encoder = stdex::make_unique>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +/* + test case: + ConstGen (2x3x4x5) ---- FeatureEncode ---- FeatureDecode --- Push + 0 H O 0 + 1 W H 1 + 2 I(depth) W 2 + 3 O(count) I 3 + + axis 0 ---------------------> H --------------> H -----------> 1 + axis 1 ---------------------> W --------------> W -----------> 2 + axis 2 ---------------------> I --------------> I -----------> 3 + axis 3 ---------------------> O --------------> O -----------> 0 + + so perm vector of Tranpose = [3, 0, 1, 2] +*/ +void create_net_FilterEncode_FilterDecode_different_perms(loco::Graph *graph) +{ + assert(graph); + + auto const_node = graph->nodes()->create(); + { + const_node->dtype(loco::DataType::FLOAT32); + const_node->rank(4); + int count = 1; + for (int i = 0; i < 4; ++i) + { + const_node->dim(i) = i + 2; + count *= i + 2; + } + const_node->size(count); + for (uint32_t i = 0; i < count; i++) + const_node->at(i) = 3.14f; // any number + } + + auto encoder = make_filter_encode(const_node); + auto decoder = make_filter_decode(encoder); + + auto push_node = graph->nodes()->create(); + { + push_node->from(decoder); + } + + auto graph_output = graph->outputs()->create(); + { + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); + } +} + +/* + test case: + ConstGen (2x3x4x5) ---- FeatureEncode ---- FeatureDecode --- Push + 0 H H 0 + 1 W W 1 + 2 I(depth) I 2 + 3 O(count) O 3 + + axis 0 ---------------------> H --------------> H -----------> 0 + axis 1 ---------------------> W --------------> W -----------> 1 + axis 2 ---------------------> I --------------> I -----------> 2 + axis 3 ---------------------> O --------------> O -----------> 3 + + so perm vector of Tranpose = [0, 1, 2, 3] and transposes should be eliminated +*/ +void create_net_FilterEncode_FilterDecode_equal_perms(loco::Graph *graph) +{ + assert(graph); + + auto const_node = graph->nodes()->create(); + { + const_node->dtype(loco::DataType::FLOAT32); + const_node->rank(4); + int count = 1; + for (int i = 0; i < 4; ++i) + { + const_node->dim(i) = i + 2; + count *= i + 2; + } + const_node->size(count); + for (uint32_t i = 0; i < count; i++) + const_node->at(i) = 3.14f; // any number + } + + auto encoder = make_filter_encode(const_node); + auto decoder = make_filter_decode(encoder); + + auto push_node = graph->nodes()->create(); + { + push_node->from(decoder); + } + + auto graph_output = graph->outputs()->create(); + { + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); + } +} + +} // namespace + +TEST(SimplifyDomainConversionPass, FilterEncode_FilterDecode_different_perms) +{ + auto graph = loco::make_graph(); + create_net_FilterEncode_FilterDecode_different_perms(graph.get()); + + logo::SimplifyDomainConversionPass pass; + while (pass.run(graph.get()) == true) + ; + + auto tr = logo::test::find_first_node_by_type(graph.get()); + { + ASSERT_EQ(tr->perm()->size(), 4); + ASSERT_EQ(tr->perm()->axis(0), 3); + ASSERT_EQ(tr->perm()->axis(1), 0); + ASSERT_EQ(tr->perm()->axis(2), 1); + ASSERT_EQ(tr->perm()->axis(3), 2); + } + + auto const_gen = dynamic_cast(tr->input()); + ASSERT_NE(const_gen, nullptr); +} + +TEST(SimplifyDomainConversionPass, FilterEncode_FilterDecode_equal_perms) +{ + auto graph = loco::make_graph(); + create_net_FilterEncode_FilterDecode_equal_perms(graph.get()); + + logo::SimplifyDomainConversionPass pass; + while (pass.run(graph.get()) == true) + ; + + ASSERT_EQ(loco::output_nodes(graph.get()).size(), 1); + loco::Node *output_node = loco::output_nodes(graph.get())[0]; + + auto forward = dynamic_cast(output_node->arg(0)); + ASSERT_NE(forward, nullptr); + auto const_gen = dynamic_cast(forward->arg(0)); + ASSERT_NE(const_gen, nullptr); +} diff --git a/compiler/logo/src/TestHelper.h b/compiler/logo/src/TestHelper.h new file mode 100644 index 000000000..43631efa9 --- /dev/null +++ b/compiler/logo/src/TestHelper.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_HELPER_H__ +#define __TEST_HELPER_H__ + +#include + +namespace logo +{ +namespace test +{ + +template T *find_first_node_by_type(loco::Graph *g) +{ + T *first_node = nullptr; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + first_node = dynamic_cast(node); + if (first_node != nullptr) + break; + } + + return first_node; +} + +} // namespace test +} // namespace logo + +#endif // __TEST_HELPER_H__ 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 + +#include + +#include + +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 +#include +#include + +// 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 + +#include +#include + +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 +#include +#include + +#include +#include +#include +#include + +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(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(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>> +encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map &opcodes, + std::unordered_map &custom_opcodes) +{ + std::vector> 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 = exportSubgraph(gd); + auto subgraphs = _builder.CreateVector(std::vector>{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 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> 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 = exportSubgraph(gd); + subgraph_vec.push_back(subgraph); + } + + auto subgraphs = _builder.CreateVector(std::vector>{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 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(_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 + +#include + +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 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 + +#include +#include + +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(_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(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast(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() == nullptr); + node->annot(std::make_unique(tensor_id)); +} + +CircleTensorIndex get_tensor_index(loco::Node *node) +{ + assert(node->annot() != nullptr); + return node->annot()->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 +#include + +#include + +#include + +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 +#include +#include +#include + +#include +#include + +#include + +using namespace flatbuffers; +using namespace circle; + +namespace +{ + +using namespace luci; + +class OperationExporter final : public luci::CircleNodeMutableVisitor, + public loco::CanonicalNodeMutableVisitor +{ +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 + void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op); + +private: + FlatBufferBuilder &builder; + SerializedModelData &md; + SerializedGraphData &gd; +}; + +template +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 inputs_vec{get_tensor_index(node->value())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->dimension())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(node, circle::BuiltinOperator_AVERAGE_POOL_2D); +} + +void OperationExporter::visit(luci::CircleConcatenation *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + std::vector inputs_vec; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->block_shape()), + get_tensor_index(node->crops())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->weights()), + get_tensor_index(node->bias())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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(node, circle::BuiltinOperator_MAX_POOL_2D); +} + +void OperationExporter::visit(luci::CircleMean *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MEAN); + std::vector inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->reduction_indices())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->paddings())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->features())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->tensor()), + get_tensor_index(node->shape())}; + std::vector 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( + 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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->logits())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))}; + std::vector 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 inputs_vec{get_tensor_index(node->inputSizes()), + get_tensor_index(node->filter()), + get_tensor_index(node->outBackprop())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()), + get_tensor_index(node->beta())}; + std::vector outputs_vec{get_tensor_index(static_cast(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 inputs{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector 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(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 + +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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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; + +struct NoOpDetector final : public luci::CircleNodeMutableVisitor +{ + // 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(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(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(node)); + tensor_info.quantparam(node->quantparam()); + + set_tensor_index(node, tensor_index); + + ctx.emplace_back(tensor_info); +} + +} // namespace + +namespace +{ + +flatbuffers::Offset> encodeShape(FlatBufferBuilder &builder, + const ShapeDescription &shape) +{ + assert(shape._rank_known && "unknown number of dimensions is not supported"); + return builder.CreateVector(shape._dims); +} + +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, NodeT *) +{ + return CreateBuffer(builder); +} + +template +flatbuffers::Offset encodeOpBufferByDType(FlatBufferBuilder &builder, + luci::CircleConst *c) +{ + using NativeType = typename loco::DataTypeImpl
::Type; + + std::vector raw_data; + const uint32_t size = c->size
(); + raw_data.reserve(size); + for (uint32_t i = 0; i < size; ++i) + { + raw_data.push_back(c->at
(i)); + } + const size_t raw_size = size * sizeof(NativeType); + auto array_offset = builder.CreateVector(reinterpret_cast(raw_data.data()), raw_size); + return CreateBuffer(builder, array_offset); +} + +template <> +flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c) +{ + // TODO use switch + if (c->dtype() == loco::DataType::FLOAT32) + { + return encodeOpBufferByDType(builder, c); + } + else if (c->dtype() == loco::DataType::S32) + { + return encodeOpBufferByDType(builder, c); + } + else if (c->dtype() == loco::DataType::U8) + { + return encodeOpBufferByDType(builder, c); + } + + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); +} + +flatbuffers::Offset +encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam) +{ + if (quantparam == nullptr) + return 0; + + flatbuffers::Offset> min; + flatbuffers::Offset> max; + flatbuffers::Offset> scale; + flatbuffers::Offset> 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(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(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 + +#include + +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 +#include + +#include + +#include + +namespace luci +{ + +void optimize(loco::Graph *g) +{ + logo::Phase phase; + { + // prepare type and shape before optimization + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + + // TODO add more optimization passes (with a knob) + } + + logo::PhaseRunner 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 + +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 +#include + +#include + +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 *) +{ + 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 *) +{ + LOGGER(prime); + + INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *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 + +#include + +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 *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) 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 + +#include + +#include + +namespace luci +{ + +struct OpCode +{ + circle::BuiltinOperator opcode; + + bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; } +}; + +} // namespace luci + +namespace std +{ + +template <> struct hash +{ + size_t operator()(const luci::OpCode &x) const { return hash()(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 _inputs; + /// @brief SubGraph output tensor id + std::vector _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 _operator_codes; + std::unordered_map _custom_operator_codes; + std::vector> _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> _operators; + std::vector> _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 + +#include +#include +#include + +#include + +#include +#include +#include +#include + +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 +luci_quantparam(const circle::QuantizationParametersT *quantization); + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class CircleReader +{ +private: + using CircleBuffers_t = std::vector>; + using CircleTensors_t = std::vector>; + using CircleOperators_t = std::vector>; + using CircleOperatorCodes_t = std::vector>; + +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 &inputs() const { return _current_subgraph->inputs; } + const std::vector &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 _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 + +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 &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 + +#include + +#include + +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; + + 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 + +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 &&builder) + { + _builder_map[op] = std::move(builder); + } + +private: + const GraphBuilderSource *_parent = nullptr; + +private: + std::map> _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 &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 &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 &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 &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 &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 &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 + +/* + * @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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 + +#include + +#include + +namespace luci +{ + +class Importer final +{ +public: + Importer(); + +public: + explicit Importer(const GraphBuilderSource *source) : _source{source} + { + // DO NOTHING + } + +public: + std::unique_ptr import(const circle::Model *model) const; + std::unique_ptr 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 +#include +#include + +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 +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(); + + 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 &inputs = op.inputs; + const std::vector &outputs = op.outputs; + const auto &tensors = context->reader()->tensors(); + + std::vector 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 + +#include + +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 + +namespace luci +{ + +GraphBuilderRegistry::GraphBuilderRegistry() +{ +#define CIRCLE_NODE(OPCODE, CLASS) add(circle::BuiltinOperator_##OPCODE, std::make_unique()); + + 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 +#include +#include +#include + +#include + +#include + +namespace +{ + +void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &reader, + loco::Graph *graph) +{ + LOGGER(l); + + auto nodefinder = std::make_unique(); + + 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(); + 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 &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 &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(); + 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(); + const std::vector &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 &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 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())); + + return std::move(graph); +} + +std::unique_ptr 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())); + + 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 + +#include + +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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(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 +#include + +#include +#include + +#include + +namespace luci +{ + +template +static void copy_data(const std::vector &raw_data, uint32_t num_elements, + CircleConst *const_node) +{ + using T = typename loco::DataTypeImpl
::Type; + + assert(raw_data.size() == num_elements * sizeof(T)); + const auto *data = reinterpret_cast(raw_data.data()); + + const_node->size
(num_elements); + for (uint32_t i = 0; i < num_elements; ++i) + { + const_node->at
(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(); + 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 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 &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(buffer, num_elements, const_node); + break; + + case loco::DataType::U8: + copy_data(buffer, num_elements, const_node); + break; + + case loco::DataType::S32: + copy_data(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 + +#include + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 &inputs, + loco::Graph *graph) const +{ + auto node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include +#include + +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(options->values_count)) + return false; + + if (outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CirclePackGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 +#include + +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 &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 &shape, loco::Graph *graph) +{ + auto *shape_node = graph->nodes()->create(); + shape_node->dtype(loco::DataType::S32); + shape_node->rank(1); + shape_node->dim(0) = shape.size(); + shape_node->size(shape.size()); + for (uint32_t i = 0; i < shape.size(); ++i) + { + shape_node->at(i) = shape[i]; + } + return shape_node; +} + +CircleNode *CircleReshapeGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector &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(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +#include + +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 &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + 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 + +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 + +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 + +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 +#include + +#include "CircleOpcode.h" +#include "CircleNodeVisitor.forward.h" +#include "CircleQuantParam.h" + +#include + +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 T accept(CircleNodeVisitorBase *) const; + template T accept(CircleNodeMutableVisitorBase *); + + 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 &&quantparam) + { + _quantparam = std::move(quantparam); + } + +private: + NodeName _name; + std::unique_ptr _quantparam; +}; + +template struct CircleNodeImpl : public CircleNode +{ + virtual ~CircleNodeImpl() = default; + + uint32_t opnum(void) const final { return static_cast(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 + +#include + +namespace luci +{ + +template T CircleNode::accept(CircleNodeVisitorBase *v) const +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast(this)); + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + default: + break; + } + + INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled"); +} + +template T CircleNode::accept(CircleNodeMutableVisitorBase *v) +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast(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 struct CircleNodeVisitorBase; +template 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 + +namespace luci +{ + +/** + * DO NOT use this class. Use CircleNodeVisitor instead. + */ +template 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 struct CircleNodeVisitor : public CircleNodeVisitorBase +{ + virtual ~CircleNodeVisitor() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + virtual T visit(const CIRCLE_CLASS *node) { return visit(static_cast(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 struct CircleNodeMutableVisitorBase +{ + virtual ~CircleNodeMutableVisitorBase() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(CIRCLE_CLASS *) = 0; + +#include "CircleNodes.lst" + +#undef CIRCLE_NODE +}; + +template struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase +{ + virtual ~CircleNodeMutableVisitor() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + virtual T visit(CIRCLE_CLASS *node) { return visit(static_cast(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 +#include + +namespace luci +{ + +struct CircleQuantParam +{ + std::vector min; + std::vector max; + std::vector scale; + std::vector 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 +#include + +namespace luci +{ + +/// @brief enumeration of mixin class +enum class LuciNodeTrait +{ + FusedActFunc, + Bias +}; + +template class LuciNodeMixin; + +template <> class LuciNodeMixin +{ +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 +{ +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 class FixedArityNode : public Base +{ +public: + FixedArityNode() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::make_unique(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, 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 + +#include +#include + +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 &&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> _graphs; +}; + +std::unique_ptr 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> +{ +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>, + public LuciNodeMixin +{ +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> +{ +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>, + public LuciNodeMixin +{ +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> +{ +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 + +namespace luci +{ + +/** + * @brief CONCATENATION in Circle + */ +class CircleConcatenation final + : public VariadicArityNode>, + public LuciNodeMixin +{ +public: + CircleConcatenation(uint32_t arity) + : VariadicArityNode>(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 + +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>, + public loco::NodeMixin, + public loco::NodeMixin +{ +public: + CircleConst() = default; + +public: + template uint32_t size(void) const; + template void size(uint32_t size); + template const typename loco::DataTypeImpl
::Type &at(uint32_t n) const; + template typename loco::DataTypeImpl
::Type &at(uint32_t n); + + template const typename loco::DataTypeImpl
::Type &scalar(void) const; + template typename loco::DataTypeImpl
::Type &scalar(void); + +private: + std::vector _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>, + public LuciNodeMixin, + public LuciNodeMixin +{ +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> +{ +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>, + public LuciNodeMixin, + public LuciNodeMixin +{ +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>, + public LuciNodeMixin +{ +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> +{ +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> +{ +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>, + public LuciNodeMixin, + public LuciNodeMixin +{ +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> +{ +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 +#include + +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>, + public loco::NodeMixin, + public loco::NodeMixin +{ +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>, + public LuciNodeMixin +{ +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> +{ +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> +{ +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>, + public LuciNodeMixin +{ +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> +{ +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> +{ +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>, + public LuciNodeMixin +{ +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 + +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> +{ +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 + +namespace luci +{ + +/** + * @brief PACK in Circle + */ +class CirclePack final : public VariadicArityNode> +{ +public: + CirclePack(uint32_t arity) : VariadicArityNode>(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> +{ +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> +{ +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> +{ +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> +{ +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 _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> +{ +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> +{ +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> +{ +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> +{ +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>, + public LuciNodeMixin +{ +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> +{ +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> +{ +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 +#include + +#include +#include +#include + +namespace luci +{ + +/** + * @brief Nodes with the variadic inputs + */ +template class VariadicArityNode : public Base +{ +public: + VariadicArityNode(uint32_t arity) + { + for (uint32_t n = 0; n < arity; ++n) + { + _args.push_back(std::make_unique(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> _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 +#include +#include + +// 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 +#include +#include + +#include +#include + +namespace +{ + +struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto circleinput = dynamic_cast(node)) + { + return circleinput->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + auto circleinput = dynamic_cast(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(node)) + { + return circleoutput->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + auto circleoutput = dynamic_cast(node); + assert(circleoutput != nullptr); + return circleoutput->index(); + } +}; + +} // namespace + +namespace luci +{ + +CircleDialect::CircleDialect() +{ + service(std::make_unique()); + service(std::make_unique()); +} + +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 + +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 + +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(); + const_shape_node->rank(1); + const_shape_node->dim(0) = size; + const_shape_node->dtype(S32); + const_shape_node->size(size); + for (uint32_t axis = 0; axis < size; ++axis) + const_shape_node->at(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 + +namespace luci +{ + +void Module::add(std::unique_ptr &&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 make_module(void) { return std::make_unique(); } + +} // 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 + +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 + +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 + +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 + +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 + +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 + +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 + +namespace luci +{ + +template uint32_t CircleConst::size(void) const +{ + assert(dtype() == DT); + assert(_data.size() % sizeof(typename loco::DataTypeImpl
::Type) == 0); + return _data.size() / sizeof(typename loco::DataTypeImpl
::Type); +} + +template void CircleConst::size(uint32_t l) +{ + assert(dtype() == DT); + _data.resize(l * sizeof(typename loco::DataTypeImpl
::Type)); +} + +template +const typename loco::DataTypeImpl
::Type &CircleConst::at(uint32_t n) const +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +template typename loco::DataTypeImpl
::Type &CircleConst::at(uint32_t n) +{ + assert(dtype() == DT); + assert(n < size
()); + return *(reinterpret_cast::Type *>(_data.data()) + n); +} + +template +const typename loco::DataTypeImpl
::Type &CircleConst::scalar(void) const +{ + assert(dtype() == DT); + return *(reinterpret_cast::Type *>(_data.data())); +} + +template typename loco::DataTypeImpl
::Type &CircleConst::scalar(void) +{ + assert(dtype() == DT); + return *(reinterpret_cast::Type *>(_data.data())); +} + +#define INSTANTIATE(DT) \ + template uint32_t CircleConst::size
(void) const; \ + template void CircleConst::size
(uint32_t); \ + template const typename loco::DataTypeImpl
::Type &CircleConst::at
(uint32_t) const; \ + template typename loco::DataTypeImpl
::Type &CircleConst::at
(uint32_t); \ + template const typename loco::DataTypeImpl
::Type &CircleConst::scalar
(void) const; \ + template typename loco::DataTypeImpl
::Type &CircleConst::scalar
(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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 +#include + +namespace luci +{ + +void CircleInput::index(const loco::GraphInputIndex &index) +{ + // CircleInput internally stores "GraphInputIndex" as int64_t + _index = static_cast(index); +} + +loco::GraphInputIndex CircleInput::index(void) const +{ + assert(_index >= std::numeric_limits::min()); + assert(_index <= std::numeric_limits::max()); + return static_cast(_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 + +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 + +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 + +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 + +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 + +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 + +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 +#include + +namespace luci +{ + +void CircleOutput::index(const loco::GraphOutputIndex &index) +{ + // CircleOutput internally stores "GraphOutputIndex" as int64_t + _index = static_cast(index); +} + +loco::GraphOutputIndex CircleOutput::index(void) const +{ + assert(_index >= std::numeric_limits::min()); + assert(_index <= std::numeric_limits::max()); + return static_cast(_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 + +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 + +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 + +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 + +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 + +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(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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 +#include +#include + +// 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 T safecast(const char *, const T &); + +template <> bool safecast(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(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(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 + +#include + +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()); + ctx->config(std::make_unique()); + } + + 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 + +#include + +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 create(const locop::SymbolTable *tlb) const final + { + return std::make_unique(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 +#include + +#include + +namespace luci +{ + +using FormattedGraph = locop::FormattedGraphImpl; + +FormattedGraph fmt(loco::Graph *g); + +static inline FormattedGraph fmt(const std::unique_ptr &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 +#include + +#include + +#include +#include +#include + +/** + * @brief dump std::vector values to stream + */ +std::ostream &operator<<(std::ostream &os, const std::vector &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(opnum)) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + case luci::CircleOpcode::OPCODE: \ + return prefix + #OPCODE; +#include +#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 +#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(node)) \ + { \ + s.opname(circle_opname(node->opnum())); \ + return summary(dynamic_cast(node), s); \ + } +#include +#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(); + return std::move(locop::fmt(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 + +#include +#include + +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; +}; + +} // 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 + +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 + +#include + +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 + +#include + +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 + +#include "ProgressReporter.h" + +#include + +#include + +namespace +{ + +using namespace luci; + +class OptimizeOptionsImpl : public luci::CircleOptimizer::Options +{ +public: + void enable(Algorithm) final; + bool query(Algorithm) final; + +private: + std::vector _algorithms; +}; + +void OptimizeOptionsImpl::enable(Algorithm algo) { _algorithms.push_back(algo); } + +bool OptimizeOptionsImpl::query(Algorithm algo) +{ + std::vector::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(); + } + + 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()); + } + // Shape inference is needed for added nodes doing above transformations + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + /* TRANSFORM DECLARATION END */ + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner 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 + +#include + +#include +#include + +// 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 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 bool with_commutative_args_of(const COMM_NODE *node); + +private: + ARG_TYPE_1 **_arg_1; + ARG_TYPE_2 **_arg_2; +}; + +template +inline NodeFiller fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) +{ + return NodeFiller{arg_1, arg_2}; +} + +template +template +bool NodeFiller::with_commutative_args_of(const COMM_NODE *node) +{ + // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2 + { + auto x = dynamic_cast(node->x()); + auto y = dynamic_cast(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(node->x()); + auto y = dynamic_cast(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(); + 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(mean->reduction_indices()); + if (not red_indices) + return false; + if (red_indices->rank() != 1) + return false; + std::set 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(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(); + 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(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() == 1); + + CHECK_OR_FALSE(is_instance_mean(mean_as_variance)); + sqdiff = dynamic_cast(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(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(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(); + auto reshape_beta = graph->nodes()->create(); + { + auto ifm_shape = loco::shape_get(p.ifm).as(); + uint32_t ifm_channel_depth = ifm_shape.dim(3).value(); + + int32_t new_shape[1] = {static_cast(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(); + instance_norm->input(p.ifm); + instance_norm->gamma(reshape_gamma); + instance_norm->beta(reshape_beta); + float epsilon = p.const_as_epsilon->at(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(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 +#include + +#include +#include + +#include + +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 *) +{ + 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 *) +{ + LOGGER(prime); + + INFO(prime) << "PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo *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 + +#include + +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 *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) override; + void notify(const logo::PhaseEventInfo *) 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 +#include + +#include +#include +#include +#include +#include + +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 +#include + +#include +#include +#include + +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 + +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 + +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 + +#include + +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 + +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 +#include + +#include +#include + +namespace luci +{ + +struct ShapeDescription +{ + std::vector _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 inline bool isNHWC(Permutation *perm); + +template <> inline bool isNHWC(loco::Permutation *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 *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 + +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 +#include +#include + +// 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 +#include + +#include + +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 +#include +#include +#include + +#include + +#include +#include +#include + +namespace +{ + +// Call this for CircleAvgPool2D and CircleMaxPool2D only +template 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(); + 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 +{ +public: + loco::NodeShape visit(const luci::CircleAbs *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleAdd *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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(); + auto dimension_shape = loco::shape_get(node->dimension()).as(); + + 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(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(); + } + 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(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(); + // 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(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(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(); + auto const_crops_shape = loco::shape_get(const_crops).as(); + 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(dim); + dim_size -= const_crops->at(dim * 2); + dim_size -= const_crops->at(dim * 2 + 1); + shape_output.dim(dim + 1) = dim_size; + + assert(output_batch_size % const_block_shape->at(dim) == 0); + output_batch_size = output_batch_size / const_block_shape->at(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(); + auto axis = node->axis(); + if (axis < 0) + axis += first_shape.rank(); + + assert(0 <= axis); + assert(first_shape.rank() > static_cast(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(); + + for (uint32_t j = 0; j < output_shape.rank(); ++j) + { + if (j == static_cast(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(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as(); // 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(); + + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleDepthwiseConv2D *node) final + { + auto ifm_shape = loco::shape_get(node->input()).as(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as(); // 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(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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(); + const auto y_shape = loco::shape_get(node->y()).as(); + 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(); + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleFullyConnected *node) final + { + auto input_shape = loco::shape_get(node->input()).as(); + auto weights_shape = loco::shape_get(node->weights()).as(); + + // 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(); + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleLogicalOr *node) final + { + const auto input_shape = loco::shape_get(node->x()).as(); + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleMaximum *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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(); + auto reduction_indices = dynamic_cast(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 reduction_values; + + for (uint32_t i = 0; i < reduction_indices->size(); ++i) + { + int32_t axis = reduction_indices->at(i); + if (axis < 0) + axis += input_shape.rank(); + if (not(0 <= axis and axis < static_cast(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 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(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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(); + // 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(); + 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(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(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(); + auto paddings = dynamic_cast(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(idx + 0); // left + value += paddings->at(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(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleRelu6 *node) final + { + auto input_shape = loco::shape_get(node->features()).as(); + + 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(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(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(); + 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(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(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSoftmax *node) final + { + auto input_shape = loco::shape_get(node->logits()).as(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSqrt *node) final + { + auto input_shape = loco::shape_get(node->x()).as(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSquaredDifference *node) final + { + auto x_shape = loco::shape_get(node->x()).as(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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(); + auto y_shape = loco::shape_get(node->y()).as(); + + 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 + 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()); + + for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++) + { + auto in_axis = perm_node->template at(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(); + + auto canon_perm = dynamic_cast(node->perm()); + auto circle_perm = dynamic_cast(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(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(axis); + + return loco::NodeShape{shape}; + } + + // Circle Only + loco::NodeShape visit(const luci::CircleInstanceNorm *node) final + { + auto input_shape = loco::shape_get(node->input()).as(); + + 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(); + + 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(node) != nullptr); + + ShapeInferenceAlgorithm alg; + shape = dynamic_cast(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 +#include + +#include +#include +#include +#include +#include + +#include + +#include + +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(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(); + 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(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(); + 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(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(); + 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(); + { + 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(); + { + y_node->rank(2); + y_node->dim(0) = 3; + y_node->dim(1) = 5; + } + auto tfl_node = g->nodes()->create(); + { + tfl_node->x(x_node); + tfl_node->y(y_node); + } + auto push_node = g->nodes()->create(); + { + 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(); + 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 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(3); + g.const_perm->at(0) = 1; + g.const_perm->at(1) = 2; + g.const_perm->at(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(); + 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 +#include +#include + +#include + +namespace +{ + +struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor +{ + // 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(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 + +#include +#include + +#include +#include +#include + +#include + +#include + +TEST(CircleTypeInferenceRuleTest, minimal_with_CircleRelu) +{ + // Create a simple network + luci::test::TestGraph graph; + auto tfl_node = graph.append(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 +#include + +#include + +#include + +// 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 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 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 loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::FilterDecode of given layout +template loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode); + +enum class DepthwiseFilterLayout +{ + HWCM, +}; + +/// @brief Create a loco::DepthwiseFilterDecode of given layout +template +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 loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::MatrixDecode of given layout +template 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 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 getInputsToConvert(CanonicalT *origin) = 0; + + /// @brief Set the inputs of replacer to new_inputs + virtual void set(TFLT *replacer, std::vector &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 DomainConverter +{ +public: + template + TFLT *convert(CanonicalT *origin, InputHandler &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 +template +TFLT *DomainConverter::convert(CanonicalT *origin, + InputHandler &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(); + + // 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 feature_decodes; + + for (auto input : input_handler.getInputsToConvert(origin)) + { + auto dec = make_feature_decode(input); + feature_decodes.emplace_back(dec); + } + + input_handler.set(tfl_node, feature_decodes); + + auto enc = make_feature_encode(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 + +#include + +// TODO Change all Canonical nodes to Circle nodes + +namespace +{ + +template loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + // Make NHWC permutation for encoder and decoder + loco::Permutation 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 loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + loco::Permutation 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 perm() +{ + + // Make NHWC permutation for encoder and decoder + loco::Permutation 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 loco::Permutation perm(); + +template <> +loco::Permutation perm() +{ + loco::Permutation 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 loco::Permutation perm(); + +template <> loco::Permutation perm() +{ + loco::Permutation HW; + + HW.axis(loco::MatrixAxis::Height) = 0; + HW.axis(loco::MatrixAxis::Width) = 1; + + return HW; +} + +template <> loco::Permutation perm() +{ + loco::Permutation WH; + + WH.axis(loco::MatrixAxis::Height) = 1; + WH.axis(loco::MatrixAxis::Width) = 0; + + return WH; +} + +} // namespace + +namespace luci +{ + +template 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>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template 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>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template 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>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template 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>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template +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>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template 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>(); + + encoder->perm(perm()); + + auto enc = g->nodes()->create(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template 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>(); + + decoder->perm(perm()); + + auto dec = g->nodes()->create(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +// template instantiation +template loco::FeatureEncode * +make_feature_encode(loco::Node *input_for_encode); + +template loco::FeatureDecode * +make_feature_decode(loco::Node *input_for_encode); + +template loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode); +template loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode); + +template loco::DepthwiseFilterDecode * +make_dw_filter_decode(loco::Node *input_for_decode); + +template loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); +template loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); +template loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); +template loco::MatrixDecode *make_matrix_decode(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 + +#include + +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()); + case loco::Domain::Feature: + return to_shape_description(shape.as()); + case loco::Domain::Filter: + return to_shape_description(shape.as()); + case loco::Domain::DepthwiseFilter: + return to_shape_description(shape.as()); + case loco::Domain::Bias: + return to_shape_description(shape.as()); + case loco::Domain::Matrix: + return to_shape_description(shape.as()); + 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 +#include "GraphBlock.h" + +#include + +#include +#include + +// TODO Change all Canonical nodes to Circle nodes + +namespace luci +{ +namespace test +{ + +class TestGraph +{ +public: + std::unique_ptr g; + loco::Pull *pull; + loco::Push *push; + + TestGraph() // creates Pull and Push + { + g = loco::make_graph(); + + pull = g->nodes()->create(); + + push = g->nodes()->create(); + + 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 T *append() + { + auto node = g->nodes()->create(); + _next_input = node; + + return node; + } + + /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph + template T *append(loco::Node *arg1) + { + auto node = g->nodes()->create(); + 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 T *append(loco::Node *arg1, loco::Node *arg2) + { + auto node = g->nodes()->create(); + 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 T *append(loco::Node *arg1, loco::Node *arg2, loco::Node *arg3) + { + auto node = g->nodes()->create(); + 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 class ExampleGraph; + +/** + * @brief Class to create the following: + * + * Pull - FeatureEncoder - FeatureBiasAdd - FeatureDecode - Push + * | + * ConstGen - BiasEncode --+ + */ +template <> class ExampleGraph : 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(pull); + constgen = append(); + bias_enc = append(constgen); + fea_bias_add = append(fea_enc, bias_enc); + fea_dec = luci::make_feature_decode(fea_bias_add); + complete(fea_dec); + } +}; + +/** + * @brief Class to creates the following: + * + * ConstGen -- ReLU -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::ConstGen *constgen = nullptr; + loco::ReLU *relu = nullptr; + +public: + ExampleGraph() + { + constgen = append(); + relu = append(constgen); + complete(relu); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- Transpose -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::TensorTranspose *transpose = nullptr; + +public: + ExampleGraph() + { + transpose = append(pull); + complete(transpose); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- FilterEncode -- FilterDecode -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::FilterEncode *filterEncode = nullptr; + loco::FilterDecode *filterDecode = nullptr; + +public: + ExampleGraph() + { + filterEncode = luci::make_filter_encode(pull); // from Tensorflow + filterDecode = + luci::make_filter_decode(filterEncode); // to Tensorflow Lite + complete(filterDecode); + } +}; + +/** + * @brief Class to create the following: + * + * Pull -- CircleTranspose -- Push + */ +template <> class ExampleGraph : public TestGraph +{ +public: + loco::ConstGen *const_perm = nullptr; + luci::CircleTranspose *transpose_node = nullptr; + +public: + ExampleGraph() + { + const_perm = append(); + transpose_node = append(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 +#include + +#include +#include +#include + +#include +#include + +namespace +{ + +/** + * @brief returns a node that is CircleOutput with index is out_index in nodes + */ +luci::CircleOutput *find_node(std::vector nodes, loco::GraphOutputIndex out_index) +{ + for (auto node : nodes) + { + auto circle_output = dynamic_cast(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(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 +#include + +#include +#include +#include + +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 _data; +}; + +} // namespace + +namespace luci +{ + +std::unique_ptr load_model(const std::string &path) +{ + return std::make_unique(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 + +#include + +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 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 +#include +#include +#include + +#include +#include +#include + +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 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 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}" + "$" + ${DAILY_READ_TESTS} +) + +add_test(NAME luci_unit_writetest + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/writeverify.sh" + "${CMAKE_CURRENT_BINARY_DIR}" + "$" + ${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 ... +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 ... +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 diff --git a/compiler/mio-circle/CMakeLists.txt b/compiler/mio-circle/CMakeLists.txt new file mode 100644 index 000000000..f97ec2b99 --- /dev/null +++ b/compiler/mio-circle/CMakeLists.txt @@ -0,0 +1,28 @@ +nnas_find_package(FlatBuffers QUIET) + +if(NOT FlatBuffers_FOUND) + return() +endif(NOT FlatBuffers_FOUND) + +message(STATUS "Build mio-circle: TRUE") + +# TODO Find a better way +set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema/circle_schema.fbs") + +# NOTE Copy circle_schema.fbs as schema.fbs to generate "schema_generated.fbs" instead of "circle_schema_generated.fbs" +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema/circle_schema.fbs" +) + +FlatBuffers_Target(mio_circle + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/circle" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +# This example shows how to use "mio-circle" library +add_executable(mio_circle_example example.cpp) +target_link_libraries(mio_circle_example mio_circle) diff --git a/compiler/mio-circle/README.md b/compiler/mio-circle/README.md new file mode 100644 index 000000000..e90ec513f --- /dev/null +++ b/compiler/mio-circle/README.md @@ -0,0 +1,3 @@ +# mio-circle + +Let's make it easy to read and write Circle models. diff --git a/compiler/mio-circle/example.cpp b/compiler/mio-circle/example.cpp new file mode 100644 index 000000000..6418e0411 --- /dev/null +++ b/compiler/mio-circle/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// This example shows how to include and use "mio-circle" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!circle::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mio-tf/CMakeLists.txt b/compiler/mio-tf/CMakeLists.txt new file mode 100644 index 000000000..d670f6bab --- /dev/null +++ b/compiler/mio-tf/CMakeLists.txt @@ -0,0 +1,48 @@ +nnas_find_package(Protobuf QUIET) +# TensorFlowSource package is used to use ~.proto files +nnas_find_package(TensorFlowSource EXACT 1.12 QUIET) + +if(NOT Protobuf_FOUND) + return() +endif(NOT Protobuf_FOUND) + +if(NOT TensorFlowSource_FOUND) + return() +endif(NOT TensorFlowSource_FOUND) + +message(STATUS "Build mio-tf: TRUE") + +# Minimal Protocol Buffer specification for GraphDef file (.pb) encoding/decoding +unset(PROTO_FILES) +list(APPEND PROTO_FILES tensorflow/core/framework/versions.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/resource_handle.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/types.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/tensor.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/tensor_shape.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/attr_value.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/op_def.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/node_def.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/function.proto) +list(APPEND PROTO_FILES tensorflow/core/framework/graph.proto) + +Protobuf_Generate(GRAPHDEF_PROTO + "${CMAKE_CURRENT_BINARY_DIR}/generated" + "${TensorFlowSource_DIR}" + ${PROTO_FILES}) + +add_library(mio_tf STATIC ${GRAPHDEF_PROTO_SOURCES}) +set_target_properties(mio_tf PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(mio_tf PUBLIC ${GRAPHDEF_PROTO_INCLUDE_DIRS}) +target_link_libraries(mio_tf PUBLIC ${GRAPHDEF_PROTO_LIBRARIES}) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +file(GLOB_RECURSE TESTS "src/*.test.cpp") + +GTest_AddTest(mio_tf_test ${TESTS}) +target_include_directories(mio_tf_test PRIVATE src) +target_link_libraries(mio_tf_test mio_tf) diff --git a/compiler/mio-tf/README.md b/compiler/mio-tf/README.md new file mode 100644 index 000000000..18f475a85 --- /dev/null +++ b/compiler/mio-tf/README.md @@ -0,0 +1,3 @@ +# mio-tf + +_mio-tf_ provides a library to access TensorFlow model files diff --git a/compiler/mio-tf/src/mio_tf.test.cpp b/compiler/mio-tf/src/mio_tf.test.cpp new file mode 100644 index 000000000..013dc2d54 --- /dev/null +++ b/compiler/mio-tf/src/mio_tf.test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +TEST(MIO_TF_Test, instance) +{ + tensorflow::GraphDef gd; + tensorflow::NodeDef nd; + + SUCCEED(); +} diff --git a/compiler/mio-tflite/CMakeLists.txt b/compiler/mio-tflite/CMakeLists.txt new file mode 100644 index 000000000..cb0795a08 --- /dev/null +++ b/compiler/mio-tflite/CMakeLists.txt @@ -0,0 +1,37 @@ +nnas_find_package(FlatBuffers QUIET) + +if(NOT FlatBuffers_FOUND) + return() +endif(NOT FlatBuffers_FOUND) + +nnas_find_package(TensorFlowSource EXACT 2.1.0 QUIET) + +if(NOT TensorFlowSource_FOUND) + return() +endif(NOT TensorFlowSource_FOUND) + +message(STATUS "Build mio-tflite: TRUE") + +set(SCHEMA_FILE "${TensorFlowSource_DIR}/tensorflow/lite/schema/schema.fbs") + +# NOTE Use copy of schema.fbs as to provide unified way for circle also +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" +) + +FlatBuffers_Target(mio_tflite + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/tflite" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +add_executable(mio_tflite_example example.cpp) +target_link_libraries(mio_tflite_example mio_tflite) + +# Temporay tflite validation tool to replace nnkit-tflite +# TODO provide full tflite validation with runtime/interpreter +add_executable(mio_tflite_validate example.cpp) +target_link_libraries(mio_tflite_validate mio_tflite) diff --git a/compiler/mio-tflite/README.md b/compiler/mio-tflite/README.md new file mode 100644 index 000000000..187b1a5c6 --- /dev/null +++ b/compiler/mio-tflite/README.md @@ -0,0 +1,3 @@ +# mio-tflite + +_mio-tflite_ provides a library to access TensorFlow lite model files diff --git a/compiler/mio-tflite/example.cpp b/compiler/mio-tflite/example.cpp new file mode 100644 index 000000000..54d15103c --- /dev/null +++ b/compiler/mio-tflite/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// This example shows how to include and use "mio-tflite" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!tflite::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mir-caffe-importer/CMakeLists.txt b/compiler/mir-caffe-importer/CMakeLists.txt new file mode 100644 index 000000000..83176510e --- /dev/null +++ b/compiler/mir-caffe-importer/CMakeLists.txt @@ -0,0 +1,17 @@ +nnas_find_package(CaffeProto QUIET) + +if (NOT CaffeProto_FOUND) + return() +endif () + +set(MIR_CAFFE_IMPORTER_SOURCES + caffe_importer.cpp + caffe_importer.h + caffe_op_creator.cpp + caffe_op_creator.h + caffe_op_types.h) + +add_library(mir_caffe_importer STATIC ${MIR_CAFFE_IMPORTER_SOURCES}) +set_target_properties(mir_caffe_importer PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(mir_caffe_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(mir_caffe_importer PUBLIC mir caffeproto PRIVATE stdex) diff --git a/compiler/mir-caffe-importer/caffe_importer.cpp b/compiler/mir-caffe-importer/caffe_importer.cpp new file mode 100644 index 000000000..8e5ebda15 --- /dev/null +++ b/compiler/mir-caffe-importer/caffe_importer.cpp @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caffe_importer.h" +#include "caffe/proto/caffe.pb.h" +#include "caffe_op_creator.h" +#include "caffe_op_types.h" + +#include "mir/ops/OutputOp.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mir_caffe +{ + +namespace +{ + +class CaffeImporter +{ +public: + /// @brief Load the model and convert it into a MIR Graph. + std::unique_ptr importModelFromBinaryFile(const std::string &filename); + std::unique_ptr importModelFromTextFile(const std::string &filename); + +private: + std::unique_ptr importModel(); + + std::unique_ptr _net; + std::unique_ptr _opCreator; + + // Maps Caffe blob names to corresponding MIR operation outputs. + std::map _blobNameToOpOutput; + + static const std::map _operatorTypes; + + /** + * @brief Mark output MIR nodes + */ + void setGraphOutputs(mir::Graph *graph); + + /** + * @brief Pass through caffe graph and collect unsupported by NNC layers + * @throw PassException with message, containing detected problems + */ + void collectUnsupportedLayers(); + + /** + * @brief Create MIR node from single caffe layer + */ + void createMIRNodesFromLayer(const caffe::LayerParameter &layer); + + mir::Operation::Output *getOutputForBlob(const std::string &blob_name) const; + void setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output); + + /** + * @brief Collect unsupported parts of caffe layer + */ + void collectUnsupportedOp(const caffe::LayerParameter &layer, std::set &problems); + + /** + * @brief Returns MIR operation outputs corresponding to the inputs of the given layer. + */ + std::vector getMIRInputsForLayer(const caffe::LayerParameter &layer); + + void processDeprecatedInput(); +}; + +void loadModelFromBinaryFile(const std::string &filename, caffe::NetParameter *net) +{ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + int file_handle = open(filename.c_str(), O_RDONLY); + + if (file_handle == -1) + throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) + + "."); + + google::protobuf::io::FileInputStream file_stream(file_handle); + file_stream.SetCloseOnDelete(true); + + google::protobuf::io::CodedInputStream coded_stream(&file_stream); + coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX); + + if (!net->ParseFromCodedStream(&coded_stream)) + throw std::runtime_error("Couldn't parse file \"" + filename + "\"."); + + // If the file has not been consumed entirely, assume that the file is in the wrong format. + if (!coded_stream.ConsumedEntireMessage()) + throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely."); +} + +void loadModelFromTextFile(const std::string &filename, caffe::NetParameter *net) +{ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + int file_handle = open(filename.c_str(), O_RDONLY); + + if (file_handle == -1) + throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) + + "."); + + google::protobuf::io::FileInputStream file_stream(file_handle); + file_stream.SetCloseOnDelete(true); + + if (!google::protobuf::TextFormat::Parse(&file_stream, net)) + throw std::runtime_error("Couldn't parse file \"" + filename + "\"."); +} + +std::unique_ptr CaffeImporter::importModel() +{ + auto graph = stdex::make_unique(); + _opCreator = stdex::make_unique(graph.get()); + + collectUnsupportedLayers(); + + for (int i = 0; i < _net->layer_size(); ++i) + createMIRNodesFromLayer(_net->layer(i)); + + setGraphOutputs(graph.get()); + + return std::move(graph); +} + +std::unique_ptr CaffeImporter::importModelFromBinaryFile(const std::string &filename) +{ + _net = stdex::make_unique(); + loadModelFromBinaryFile(filename, _net.get()); + + return importModel(); +} + +std::unique_ptr CaffeImporter::importModelFromTextFile(const std::string &filename) +{ + _net = stdex::make_unique(); + loadModelFromTextFile(filename, _net.get()); + + return importModel(); +} + +void CaffeImporter::collectUnsupportedLayers() +{ + processDeprecatedInput(); + + std::set problems; + + for (const caffe::LayerParameter &layer : _net->layer()) + collectUnsupportedOp(layer, problems); + + if (!problems.empty()) + { + std::string msg("NNC can't load model. Detected problems:"); + for (const auto &problemStr : problems) + msg.append("\n * " + problemStr); + throw std::runtime_error(msg); + } +} + +void CaffeImporter::createMIRNodesFromLayer(const caffe::LayerParameter &layer) +{ + std::vector inputs = getMIRInputsForLayer(layer); + std::vector outputs; + + switch (_operatorTypes.at(layer.type())) + { + case CaffeOpType::input: + outputs = _opCreator->convertInput(layer); + break; + case CaffeOpType::convolution: + outputs = _opCreator->convertConvolution(layer, inputs); + break; + case CaffeOpType::innerProduct: + outputs = _opCreator->convertInnerProduct(layer, inputs); + break; + case CaffeOpType::pooling: + outputs = _opCreator->convertPooling(layer, inputs); + break; + case CaffeOpType::concat: + outputs = _opCreator->convertConcat(layer, inputs); + break; + case CaffeOpType::reshape: + outputs = _opCreator->convertReshape(layer, inputs); + break; + case CaffeOpType::ReLU: + outputs = _opCreator->convertReLU(layer, inputs); + break; + case CaffeOpType::softmax: + outputs = _opCreator->convertSoftmax(layer, inputs); + break; + case CaffeOpType::scale: + outputs = _opCreator->convertScale(layer, inputs); + break; + case CaffeOpType::batchNorm: + outputs = _opCreator->convertBatchNorm(layer, inputs); + break; + case CaffeOpType::dropout: + outputs = _opCreator->convertDropout(layer, inputs); + break; + case CaffeOpType::tanh: + outputs = _opCreator->convertTanH(layer, inputs); + break; + case CaffeOpType::ELU: + outputs = _opCreator->convertELU(layer, inputs); + break; + case CaffeOpType::eltwise: + outputs = _opCreator->convertEltwise(layer, inputs); + break; + case CaffeOpType::embed: + outputs = _opCreator->convertEmbed(layer, inputs); + break; + case CaffeOpType::deconvolution: + outputs = _opCreator->convertDeconvolution(layer, inputs); + break; + case CaffeOpType::split: + outputs = _opCreator->convertSplit(layer, inputs); + break; + case CaffeOpType::sigmoid: + outputs = _opCreator->convertSigmoid(layer, inputs); + break; + case CaffeOpType::LSTM: + outputs = _opCreator->convertLSTM(layer, inputs); + break; + default: + assert(false && "All unsupported types should have been found before this pass."); + } + + assert(static_cast(outputs.size()) == layer.top_size() && "Number of outputs differs."); + for (int i = 0; i < layer.top_size(); ++i) + setOutputForBlob(layer.top(i), outputs[i]); +} + +void CaffeImporter::collectUnsupportedOp(const caffe::LayerParameter &layer, + std::set &problems) +{ + auto it = _operatorTypes.find(layer.type()); + if (it == _operatorTypes.end()) + { + problems.insert(layer.type() + ": unknown layer"); + return; + } + + CaffeOpType op_type = it->second; + + switch (op_type) + { + case CaffeOpType::concat: + case CaffeOpType::input: + case CaffeOpType::softmax: + case CaffeOpType::scale: + case CaffeOpType::dropout: + case CaffeOpType::split: + case CaffeOpType::eltwise: + case CaffeOpType::ELU: + case CaffeOpType::ReLU: + case CaffeOpType::embed: + case CaffeOpType::sigmoid: + case CaffeOpType::tanh: + case CaffeOpType::innerProduct: + // No checks + break; + case CaffeOpType::deconvolution: + case CaffeOpType::convolution: + _opCreator->checkConvolution(layer, problems); + break; + case CaffeOpType::pooling: + _opCreator->checkPooling(layer, problems); + break; + case CaffeOpType::reshape: + _opCreator->checkReshape(layer, problems); + break; + case CaffeOpType::batchNorm: + _opCreator->checkBatchNorm(layer, problems); + break; + case CaffeOpType::LSTM: + _opCreator->checkLSTM(layer, problems); + break; + default: + problems.insert(layer.type() + ": unsupported layer"); + break; + } +} + +void CaffeImporter::processDeprecatedInput() +{ + if (_net->input_dim_size() != 0 || _net->input_shape_size() != 0) + throw std::runtime_error("Deprecated Caffe input types are not supported"); +} + +std::vector +CaffeImporter::getMIRInputsForLayer(const caffe::LayerParameter &layer) +{ + std::vector inputs; + + for (const auto &input_name : layer.bottom()) + inputs.push_back(getOutputForBlob(input_name)); + + return inputs; +} + +mir::Operation::Output *CaffeImporter::getOutputForBlob(const std::string &blob_name) const +{ + return _blobNameToOpOutput.at(blob_name); +} + +void CaffeImporter::setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output) +{ + const auto it = _blobNameToOpOutput.find(blob_name); + if (it != _blobNameToOpOutput.cend()) + { + // caffe input blob name could be same as output blob name, and next line will overwrite + // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem + it->second->setName(""); + } + + // Do not overwrite the name in case of fall-through layers (ex. Dropout, Split). + // TODO Find a way to handle it properly. + if (output->getName().empty()) + output->setName(blob_name); + + _blobNameToOpOutput[blob_name] = output; +} + +void CaffeImporter::setGraphOutputs(mir::Graph *graph) +{ + // TODO For now, we assume that: + // - there is exactly one output; + // - the output is from the last layer. + const auto &last_layer = *_net->layer().rbegin(); + auto output = getOutputForBlob(last_layer.top(0)); + graph->create(output); +} + +const std::map CaffeImporter::_operatorTypes = { + {"AbsVal", CaffeOpType::absVal}, + {"Accuracy", CaffeOpType::accuracy}, + {"ArgMax", CaffeOpType::argMax}, + {"BatchNorm", CaffeOpType::batchNorm}, + {"BatchReindex", CaffeOpType::batchReindex}, + {"Bias", CaffeOpType::bias}, + {"BNLL", CaffeOpType::BNLL}, + {"Clip", CaffeOpType::clip}, + {"Concat", CaffeOpType::concat}, + {"ContrastiveLoss", CaffeOpType::contrastiveLoss}, + {"Convolution", CaffeOpType::convolution}, + {"Crop", CaffeOpType::crop}, + {"Data", CaffeOpType::data}, + {"Deconvolution", CaffeOpType::deconvolution}, + {"Dropout", CaffeOpType::dropout}, + {"DummyData", CaffeOpType::dummyData}, + {"Eltwise", CaffeOpType::eltwise}, + {"ELU", CaffeOpType::ELU}, + {"Embed", CaffeOpType::embed}, + {"EuclidianLoss", CaffeOpType::euclidianLoss}, + {"Exp", CaffeOpType::exp}, + {"Filter", CaffeOpType::filter}, + {"Flatten", CaffeOpType::flatten}, + {"HDF5Data", CaffeOpType::HDF5Data}, + {"HDF5Output", CaffeOpType::HDF5Output}, + {"HingeLoss", CaffeOpType::hingeLoss}, + {"Im2Col", CaffeOpType::im2Col}, + {"ImageData", CaffeOpType::imageData}, + {"InfogainLoss", CaffeOpType::infogainLoss}, + {"InnerProduct", CaffeOpType::innerProduct}, + {"Input", CaffeOpType::input}, + {"Log", CaffeOpType::log}, + {"LRN", CaffeOpType::LRN}, + {"LSTM", CaffeOpType::LSTM}, + {"MemoryData", CaffeOpType::memoryData}, + {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss}, + {"MVN", CaffeOpType::MVN}, + {"Parameter", CaffeOpType::parameter}, + {"Pooling", CaffeOpType::pooling}, + {"Power", CaffeOpType::power}, + {"PReLU", CaffeOpType::PReLU}, + {"Python", CaffeOpType::python}, + {"Recurrent", CaffeOpType::recurrent}, + {"Reduction", CaffeOpType::reduction}, + {"ReLU", CaffeOpType::ReLU}, + {"Reshape", CaffeOpType::reshape}, + {"RNN", CaffeOpType::RNN}, + {"Scale", CaffeOpType::scale}, + {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss}, + {"Sigmoid", CaffeOpType::sigmoid}, + {"Silence", CaffeOpType::silence}, + {"Softmax", CaffeOpType::softmax}, + {"SoftmaxWithLoss", CaffeOpType::softmaxWithLoss}, + {"SPP", CaffeOpType::SPP}, + {"Split", CaffeOpType::split}, + {"Slice", CaffeOpType::slice}, + {"TanH", CaffeOpType::tanh}, + {"Threshold", CaffeOpType::threshold}, + {"Tile", CaffeOpType::tile}, + {"WindowData", CaffeOpType::windowData}}; +} // namespace + +std::unique_ptr importModelFromBinaryFile(const std::string &filename) +{ + CaffeImporter importer; + return importer.importModelFromBinaryFile(filename); +} + +std::unique_ptr importModelFromTextFile(const std::string &filename) +{ + CaffeImporter importer; + return importer.importModelFromTextFile(filename); +} + +std::unique_ptr loadModel(const std::string &filename) +{ + return importModelFromBinaryFile(filename); +} + +} // namespace mir_caffe diff --git a/compiler/mir-caffe-importer/caffe_importer.h b/compiler/mir-caffe-importer/caffe_importer.h new file mode 100644 index 000000000..cf2c055bc --- /dev/null +++ b/compiler/mir-caffe-importer/caffe_importer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE_IMPORTER_H +#define MIR_CAFFE_IMPORTER_H + +#include +#include + +#include "mir/Graph.h" + +namespace mir_caffe +{ + +std::unique_ptr importModelFromBinaryFile(const std::string &filename); +std::unique_ptr importModelFromTextFile(const std::string &filename); +// TODO Remove after changing all uses. +std::unique_ptr loadModel(const std::string &filename); + +} // namespace mir_caffe + +#endif // MIR_CAFFE_IMPORTER_H diff --git a/compiler/mir-caffe-importer/caffe_op_creator.cpp b/compiler/mir-caffe-importer/caffe_op_creator.cpp new file mode 100644 index 000000000..5d43d248e --- /dev/null +++ b/compiler/mir-caffe-importer/caffe_op_creator.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caffe_op_creator.h" + +#include "mir/ops/AddOp.h" +#include "mir/ops/AvgPool2DOp.h" +#include "mir/ops/ConcatOp.h" +#include "mir/ops/ConstantOp.h" +#include "mir/ops/Conv2DOp.h" +#include "mir/ops/Deconv2DOp.h" +#include "mir/ops/EluOp.h" +#include "mir/ops/FullyConnectedOp.h" +#include "mir/ops/GatherOp.h" +#include "mir/ops/LeakyReluOp.h" +#include "mir/ops/MaxOp.h" +#include "mir/ops/MaxPool2DOp.h" +#include "mir/ops/MulOp.h" +#include "mir/ops/ReluOp.h" +#include "mir/ops/ReshapeOp.h" +#include "mir/ops/SigmoidOp.h" +#include "mir/ops/SliceOp.h" +#include "mir/ops/SoftmaxOp.h" +#include "mir/ops/TanhOp.h" +#include "mir/ops/TransposeOp.h" +#include "mir/Index.h" +#include "mir/ShapeRange.h" +#include "mir/Tensor.h" + +#include +#include +#include + +namespace mir_caffe +{ + +static mir::Shape convertBlobShape(const caffe::BlobShape &shape) +{ + mir::Shape mir_shape(shape.dim_size()); + + for (int i = 0; i < shape.dim_size(); ++i) + { + mir_shape.dim(i) = shape.dim(i); + } + + return mir_shape; +} + +using namespace mir; + +/// @brief Split arg into @p num_parts equal parts along @p axis axis. +std::vector CaffeOpCreator::createSplit(mir::Operation::Output *arg, + int32_t num_parts, int32_t axis) +{ + const auto &arg_shape = arg->getShape(); + + assert(axis >= 0 && axis < arg_shape.rank()); + int32_t part_size = arg_shape.dim(axis) / num_parts; + assert(part_size * num_parts == arg_shape.dim(axis)); + + Shape starts(arg_shape.rank()); + Shape sizes(arg_shape); + sizes.dim(axis) = part_size; + + std::vector outputs(num_parts); + for (int32_t i = 0; i < num_parts; ++i) + { + outputs[i] = createOp(arg, starts, sizes)->getOutput(0); + starts.dim(axis) += part_size; + } + + return outputs; +} + +/// @brief Helper function for creating FullyConnected operation with non-square input. +mir::Operation::Output *CaffeOpCreator::createFullyConnected(mir::Operation::Output *input, + mir::Operation::Output *weights, + int32_t axis) +{ + const auto &input_shape = input->getShape(); + const auto &weights_shape = weights->getShape(); + + assert(axis >= 0 && axis < input_shape.rank()); + assert(weights_shape.rank() == 2); + + // Result shape is: input.shape[0:axis] + weights.shape[1]. + Shape result_shape = input_shape; + result_shape.resize(axis + 1); + result_shape.dim(axis) = weights_shape.dim(1); + + // Flatten input to 2-D shape. + int32_t outer_size = 1; + for (int32_t i = 0; i < axis; ++i) + outer_size *= input_shape.dim(i); + int32_t inner_size = 1; + for (int32_t i = axis; i < input_shape.rank(); ++i) + inner_size *= input_shape.dim(i); + + auto flatten = createOp(input, Shape{outer_size, inner_size})->getOutput(0); + auto fc = createOp(flatten, weights)->getOutput(0); + return createOp(fc, result_shape)->getOutput(0); +} + +TensorVariant CaffeOpCreator::convertBlob(const caffe::BlobProto &blob) +{ + const void *src_data; + + mir::DataType dtype; + if (blob.data_size() != 0) + { + assert(blob.double_data_size() == 0); + dtype = mir::DataType::FLOAT32; + src_data = blob.data().data(); + } + else if (blob.double_data_size() != 0) + { + dtype = mir::DataType::FLOAT64; + src_data = blob.double_data().data(); + } + else + { + throw std::runtime_error("No data in Caffe BlobProto, investigate"); + } + + const mir::Shape shape = convertBlobShape(blob.shape()); + return TensorVariant({dtype, shape}, src_data); +} + +std::vector +CaffeOpCreator::convertInput(const caffe::LayerParameter &layer) +{ + const auto ¶ms = layer.input_param(); + const auto num_inputs = layer.top_size(); + const auto num_shapes = params.shape_size(); + std::vector outputs; + + assert((num_shapes == 1 || num_shapes == num_inputs) && "Unsupported number of shapes."); + + for (int i = 0; i < num_inputs; ++i) + { + const auto &blob_shape = params.shape(num_shapes == 1 ? 0 : i); + mir::TensorType input_type(DataType::FLOAT32, convertBlobShape(blob_shape)); + auto input = createOp(input_type)->getOutput(0); + outputs.push_back(input); + } + + return outputs; +} + +template +static void convertConvolutionParam(const caffe::ConvolutionParameter &conv_param, + OperationAttributes &attributes) +{ + std::int32_t stride_h, stride_w; + if (conv_param.has_stride_h() || conv_param.has_stride_w()) + { + // If stride_h or stride_w are set, they take precedence. + stride_h = conv_param.stride_h(); + stride_w = conv_param.stride_w(); + } + else if (conv_param.stride_size() == 0) + { + // If no strides specified, they defaults to 1. + stride_h = stride_w = 1; + } + else if (conv_param.stride_size() == 1) + { + // If only one stride specified, all strides take the same value. + stride_h = stride_w = conv_param.stride(0); + } + else + { + // Otherwise, there must be a stride for each dimension. + assert(conv_param.stride_size() == 2); + stride_h = conv_param.stride(0); + stride_w = conv_param.stride(1); + } + attributes.strides = {stride_h, stride_w}; + + std::int32_t pad_h, pad_w; + if (conv_param.has_pad_h() || conv_param.has_pad_w()) + { + // If pad_h or pad_w are set, they take precedence. + pad_h = conv_param.pad_h(); + pad_w = conv_param.pad_w(); + } + else if (conv_param.pad_size() == 0) + { + // If no pads specified, they defaults to 0. + pad_h = pad_w = 0; + } + else if (conv_param.pad_size() == 1) + { + // If only one pad specified, all pads take the same value. + pad_h = pad_w = conv_param.pad(0); + } + else + { + // Otherwise, there must be a pad for each dimension. + assert(conv_param.pad_size() == 2); + pad_h = conv_param.pad(0); + pad_w = conv_param.pad(1); + } + attributes.padding_after = attributes.padding_before = {pad_h, pad_w}; +} + +void CaffeOpCreator::checkConvolution(const caffe::LayerParameter &layer, + std::set &problems_ops_set) +{ + const caffe::ConvolutionParameter ¶ms = layer.convolution_param(); + + assert(params.stride_size() <= 2); + + if (params.axis() != 1) + problems_ops_set.insert("Conv2D: Unsupported axis"); + + if (params.pad_size() != 0 && (params.has_pad_h() || params.has_pad_w())) + problems_ops_set.insert("Conv2D: Conflicting padding properties"); + + if (params.pad_size() > 2) + problems_ops_set.insert("Conv2D: Unsupported number of pads"); +} + +std::vector +CaffeOpCreator::convertConvolution(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.convolution_param(); + Conv2DOpAttributes attributes; + + convertConvolutionParam(params, attributes); + attributes.num_groups = params.group(); + attributes.data_format = DataFormat::NCHW; + + assert(layer.blobs(0).shape().dim_size() == 4); + auto kernel = createOp(convertBlob(layer.blobs(0)))->getOutput(0); + std::vector perm{0, 2, 3, 1}; // OIHW -> OHWI + kernel = createOp(kernel, perm)->getOutput(0); + auto result = createOp(inputs[0], kernel, attributes)->getOutput(0); + + // Add the bias, if any. + if (params.bias_term()) + { + auto bias = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + bias = createOp(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +std::vector +CaffeOpCreator::convertDeconvolution(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const caffe::ConvolutionParameter ¶ms = layer.convolution_param(); + Deconv2DOpAttributes attributes; + + convertConvolutionParam(params, attributes); + attributes.data_format = DataFormat::NCHW; + + if (params.group() != 1) + { + throw std::runtime_error("Deconvolution: 'group' != 1 is not supported."); + } + + auto kernel = createOp(convertBlob(layer.blobs(0)))->getOutput(0); + std::vector perm{2, 3, 1, 0}; // IOHW -> HWOI + kernel = createOp(kernel, perm)->getOutput(0); + auto result = createOp(inputs[0], kernel, attributes)->getOutput(0); + + // bias_term is optional (so might not be present) and defaults to true + if (params.bias_term()) + { + auto bias = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + bias = createOp(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +std::vector +CaffeOpCreator::convertInnerProduct(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.inner_product_param(); + auto weights = createOp(convertBlob(layer.blobs(0)))->getOutput(0); + + if (!params.transpose()) + weights = createOp(weights, std::vector{1, 0})->getOutput(0); + + auto result = createFullyConnected(inputs[0], weights, params.axis()); + + // Add the bias, if any. + if (params.bias_term()) + { + auto bias = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +std::vector +CaffeOpCreator::convertConcat(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.concat_param(); + auto concat = createOp(inputs, params.axis()); + return {concat->getOutput(0)}; +} + +template +static void convertPoolingParam(const caffe::PoolingParameter ¶ms, + const mir::Shape &input_shape, PoolingAttributes &attributes) +{ + std::int32_t kernel_h, kernel_w; + assert(!params.global_pooling()); + if (params.has_kernel_size()) + { + kernel_h = kernel_w = params.kernel_size(); + } + else + { + kernel_h = params.kernel_h(); + kernel_w = params.kernel_w(); + } + attributes.window = {kernel_h, kernel_w}; + + std::int32_t stride_h, stride_w; + if (params.has_stride_h() || params.has_stride_w()) + { + stride_h = params.stride_h(); + stride_w = params.stride_w(); + } + else + { + stride_h = stride_w = params.stride(); + } + attributes.strides = {stride_h, stride_w}; + + std::int32_t pad_h, pad_w; + if (params.has_pad_h() || params.has_pad_w()) + { + pad_h = params.pad_h(); + pad_w = params.pad_w(); + } + else + { + pad_h = pad_w = params.pad(); + } + + attributes.padding_before = attributes.padding_after = {pad_h, pad_w}; + + // Caffe uses different formula for computing output shape than MIR. Adjust padding so that + // the output shape stays the same. + constexpr int num_spatial_dims = 2; + for (int i = 0; i < num_spatial_dims; ++i) + { + // Assuming NCHW format. + const std::int32_t padded_input = + input_shape.dim(2 + i) + attributes.padding_before[i] + attributes.padding_after[i]; + if ((padded_input - attributes.window[i]) % attributes.strides[i] != 0) + ++attributes.padding_after[i]; + } +} + +void CaffeOpCreator::checkPooling(const caffe::LayerParameter &layer, + std::set &problems_ops_set) +{ + const caffe::PoolingParameter ¶ms = layer.pooling_param(); + + if (params.has_global_pooling() && params.global_pooling()) + problems_ops_set.insert("Pooling: pooling layer global_pooling param is not supported yet"); + + if (params.pool() != caffe::PoolingParameter::AVE && + params.pool() != caffe::PoolingParameter::MAX) + problems_ops_set.insert("Pooling: unsupported pooling type"); + + if (params.has_pad() && (params.has_pad_h() || params.has_pad_w())) + problems_ops_set.insert("Pooling: conflicting padding properties in pooling"); +} + +std::vector +CaffeOpCreator::convertPooling(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.pooling_param(); + + assert(inputs.size() == 1); + auto input = inputs[0]; + + mir::Operation::Output *result; + + switch (params.pool()) + { + case caffe::PoolingParameter::AVE: + { + AvgPool2DOpAttributes attributes_avg; + attributes_avg.data_format = DataFormat::NCHW; + convertPoolingParam(params, input->getShape(), attributes_avg); + result = createOp(input, attributes_avg)->getOutput(0); + break; + } + case caffe::PoolingParameter::MAX: + { + MaxPool2DOpAttributes attributes_max; + attributes_max.data_format = DataFormat::NCHW; + convertPoolingParam(params, input->getShape(), attributes_max); + result = createOp(input, attributes_max)->getOutput(0); + break; + } + default: + assert(false); + } + + return {result}; +} + +std::vector +CaffeOpCreator::convertSoftmax(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.softmax_param(); + + // CPP and ACL backends are able to perform Softmax only along the last axis. + // FIXME Do it in backends. + if (inputs[0]->getShape().rank() == 4) + { + // For now, we only account for the most common case. + if (params.axis() != 1) + throw std::runtime_error("Softmax: unsupported axis"); + int32_t axis = 3; + auto input = createOp(inputs[0], std::vector{0, 2, 3, 1}); + auto softmax = createOp(input->getOutput(0), axis); + auto result = + createOp(softmax->getOutput(0), std::vector{0, 3, 1, 2}); + return {result->getOutput(0)}; + } + + auto softmax = createOp(inputs[0], params.axis()); + return {softmax->getOutput(0)}; +} + +void CaffeOpCreator::checkReshape(const caffe::LayerParameter &layer, + std::set &problems_ops_set) +{ + const caffe::ReshapeParameter ¶ms = layer.reshape_param(); + + if (params.has_axis() || params.has_num_axes()) + problems_ops_set.insert("Reshape layer axis and num_axes params are not supported yet"); + + if (!params.has_shape()) + problems_ops_set.insert("Reshape layer doesn't have shape parameter"); + + const mir::Shape newShape = convertBlobShape(params.shape()); + + for (int32_t i = 0; i < newShape.rank(); ++i) + if (newShape.dim(i) == 0) + problems_ops_set.insert("Reshape layer zero shape values are not supported yet"); +} + +/** + * @brief Converts Caffe Reshape layer to Model IR Reshape operation. + * @todo Support "axis" and "num_axes" parameters as needed. + * @todo Decide how to react to the absence of "shape" parameter. + * @todo Support zero values in "shape" parameter. + */ +std::vector +CaffeOpCreator::convertReshape(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const caffe::ReshapeParameter ¶ms = layer.reshape_param(); + + const mir::Shape new_shape = convertBlobShape(params.shape()); + auto reshape = createOp(inputs[0], new_shape); + return {reshape->getOutput(0)}; +} + +std::vector +CaffeOpCreator::convertReLU(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + mir::Operation *relu; + if (layer.relu_param().has_negative_slope()) + { + float alpha = layer.relu_param().negative_slope(); + relu = createOp(inputs[0], alpha); + } + else + { + relu = createOp(inputs[0]); + } + + return {relu->getOutput(0)}; +} + +std::vector +CaffeOpCreator::convertScale(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.scale_param(); + auto scale = createOp(convertBlob(layer.blobs(0)))->getOutput(0); + scale = createOp(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0); + auto result = createOp(inputs[0], scale)->getOutput(0); + + // Add the bias, if any. + if (params.bias_term()) + { + auto bias = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + bias = createOp(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +void CaffeOpCreator::checkBatchNorm(const caffe::LayerParameter &layer, + std::set &problems_ops_set) +{ + const auto &scale_shape = layer.blobs(2).shape(); + + // Check that last blob(with scaleFactor) containing only one number + if (scale_shape.dim_size() != 1 || scale_shape.dim(0) != 1) + problems_ops_set.insert("Unexpected shape of scale parameter in batch norm"); +} + +std::vector +CaffeOpCreator::convertBatchNorm(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const caffe::BatchNormParameter ¶ms = layer.batch_norm_param(); + + auto input = inputs[0]; + auto mean_tensor = convertBlob(layer.blobs(0)); + auto var_tensor = convertBlob(layer.blobs(1)); + auto scale_tensor = convertBlob(layer.blobs(2)); + const float eps = params.eps(); + + float scale_factor = *reinterpret_cast(scale_tensor.at(mir::Index{0})); + + // See https://github.com/BVLC/caffe/blob/master/src/caffe/layers/batch_norm_layer.cpp#L100 + // Y = (X - mean / scale_factor) / sqrt(var / scale_factor + epsilon) = + // = (X + C1) * C2 + if (scale_factor != 0.0f) + scale_factor = 1.0f / scale_factor; + + // C1 = -mean / scale_factor + Tensor mean_accessor(mean_tensor); + for (const auto &idx : ShapeRange(mean_accessor.getShape())) + mean_accessor.at(idx) *= -scale_factor; + auto c1 = createOp(mean_tensor)->getOutput(0); + + // C2 = 1 / sqrt(var / scale_factor + epsilon) + Tensor var_accessor(var_tensor); + for (const auto &idx : ShapeRange(var_accessor.getShape())) + var_accessor.at(idx) = 1.0f / std::sqrt(var_accessor.at(idx) * scale_factor + eps); + auto c2 = createOp(var_tensor)->getOutput(0); + + c1 = createOp(c1, Shape{1, c1->getShape().dim(0), 1, 1})->getOutput(0); + c2 = createOp(c2, Shape{1, c2->getShape().dim(0), 1, 1})->getOutput(0); + + // Y = (X + C1) * C2 + auto result = createOp(input, c1)->getOutput(0); + result = createOp(result, c2)->getOutput(0); + + return {result}; +} + +std::vector +CaffeOpCreator::convertDropout(const caffe::LayerParameter &, + const std::vector &inputs) +{ + // This is a no-op in inference mode. + return {inputs[0]}; +} + +std::vector +CaffeOpCreator::convertELU(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const caffe::ELUParameter ¶ms = layer.elu_param(); + + auto elu = createOp(inputs[0], params.alpha()); + return {elu->getOutput(0)}; +} + +std::vector +CaffeOpCreator::convertEmbed(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.embed_param(); + auto data = createOp(convertBlob(layer.blobs(0))); + auto result = createOp(data->getOutput(0), inputs[0], 0)->getOutput(0); + + // Add the bias, if any. + if (params.bias_term()) + { + auto bias = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +std::vector +CaffeOpCreator::convertSigmoid(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + auto result = createOp(inputs[0]); + return {result->getOutput(0)}; +} + +std::vector +CaffeOpCreator::convertTanH(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + auto tanh = createOp(inputs[0]); + return {tanh->getOutput(0)}; +} + +std::vector +CaffeOpCreator::convertEltwise(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + auto ¶ms = layer.eltwise_param(); + + mir::Operation::Output *result; + switch (params.operation()) + { + case caffe::EltwiseParameter::PROD: + { + result = createOp(inputs[0], inputs[1])->getOutput(0); + for (int i = 2; i < layer.bottom_size(); ++i) + { + result = createOp(result, inputs[i])->getOutput(0); + } + break; + } + case caffe::EltwiseParameter::SUM: + { + std::vector scaled_inputs = inputs; + if (params.coeff_size() > 0) + { + assert(params.coeff_size() == layer.bottom_size()); + for (int i = 0; i < layer.bottom_size(); i++) + { + if (params.coeff(i) != 1.0f) + { + const float coeff_val = params.coeff(i); + TensorVariant coeff_tensor({DataType::FLOAT32, {}}, &coeff_val); + auto coeff_const = createOp(coeff_tensor)->getOutput(0); + scaled_inputs[i] = createOp(coeff_const, inputs[i])->getOutput(0); + } + } + } + result = createOp(scaled_inputs[0], scaled_inputs[1])->getOutput(0); + for (int i = 2; i < layer.bottom_size(); ++i) + { + result = createOp(result, scaled_inputs[i])->getOutput(0); + } + break; + } + case caffe::EltwiseParameter::MAX: + { + result = createOp(inputs[0], inputs[1])->getOutput(0); + for (int i = 2; i < layer.bottom_size(); ++i) + { + result = createOp(result, inputs[i])->getOutput(0); + } + break; + } + default: + throw std::runtime_error("Unknown element-wise operation."); + } + return {result}; +} + +std::vector +CaffeOpCreator::convertSplit(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + std::vector outputs(layer.top_size(), inputs.at(0)); + return outputs; +} + +void CaffeOpCreator::checkLSTM(const caffe::LayerParameter &layer, + std::set &problems_ops_set) +{ + const auto ¶ms = layer.recurrent_param(); + if (params.expose_hidden()) + problems_ops_set.insert("LSTM: parameter 'expose_hidden' has unsupported value: " + + std::to_string(params.expose_hidden())); +} + +static TensorVariant createZeroedTensor(const mir::Shape &shape) +{ + // TODO For now it is hardcoded float32. + auto elem_type = mir::DataType::FLOAT32; + std::vector zeros(static_cast(shape.numElements()), 0.0f); + return TensorVariant({elem_type, shape}, zeros.data()); +} + +/* See the following links for details on implementation: + * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/recurrent_layer.cpp + * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_layer.cpp + * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_unit_layer.cpp + * + * Inputs: + * x -- The time-varying input. Shape: [T, N, d0, d1, ..., dn]. + * cont -- The sequence continuation indicators. Shape: [T, N]. + * x_static -- The static (non-time-varying) input. Shape: [N, ...]. + * This parameter is optional and not currently supported. + * + * Additional inputs when parameter "expose_hidden" is true (not currently supported): + * h_0 -- The initial value of the hidden state. Shape: [1, N, D]. + * c_0 -- The initial value of the cell state. Shape: [1, N, D]. + * + * Learned parameters: + * xw -- x weights for input, output, forget and cell gates concatenated. + * Shape: [4 * D, d0 * d1 * ... * dn]. + * xb -- x biases for input, output, forget and cell gates concatenated. Shape: [4 * D]. + * hw -- h weights for input, output, forget and cell gates concatenated. Shape: [4 * D, D]. + * + * Outputs: + * h -- The time-varying output. Shape: [T, N, D]. + * + * Additional outputs when parameter "expose_hidden" is true (not currently supported): + * h_T -- The value of the hidden state at the last timestep. Shape: [1, N, D]. + * c_T -- The value of the cell state at the last timestep. Shape: [1, N, D]. + * + * Here: + * T - the number of timesteps, + * N - the number of independent streams. + * D - the number of hidden parameters. + * + * Formulas: + * c_cont = c[t-1] * cont[t] + * h_cont = h[t-1] * cont[t] + * i[t] = Sigmoid(x[t] . xw_i + xb_i + h_cont . hw_i) + * f[t] = Sigmoid(x[t] . xw_f + xb_f + h_cont . hw_f) + * o[t] = Sigmoid(x[t] . xw_o + xb_o + h_cont . hw_o) + * g[t] = Tanh(x[t] . xw_g + xb_g + h_cont . hw_g) + * c[t] = c_cont * f[t] + i[t] * g[t] + * h[t] = o[t] * Tanh(c[t]) + * + * Here: + * t -- the timestep (ranges from 1 to T), + * * -- the inner product, + * . -- the Hadamard product (elementwise product). + * + * In this implementation the inner products for all gates are performed as single inner product for + * efficiency. + */ +std::vector +CaffeOpCreator::convertLSTM(const caffe::LayerParameter &layer, + const std::vector &inputs) +{ + const auto ¶ms = layer.recurrent_param(); + + // Inputs to the layer. + auto x = inputs[0]; + auto cont = inputs[1]; + assert(inputs.size() == 2); + + const auto &x_shape = x->getShape(); + const int32_t seq_length = x_shape.dim(0); + const int32_t batch_size = x_shape.dim(1); + const int32_t hidden_size = params.num_output(); + + // Learned parameters of the layer. Tensors are transposed to match the ModelIR. + auto xw = createOp(convertBlob(layer.blobs(0)))->getOutput(0); + auto xb = createOp(convertBlob(layer.blobs(1)))->getOutput(0); + auto hw = createOp(convertBlob(layer.blobs(2)))->getOutput(0); + xw = createOp(xw, std::vector{1, 0})->getOutput(0); + hw = createOp(hw, std::vector{1, 0})->getOutput(0); + + // Add a dummy dimension so that element-wise operations perform properly. + cont = createOp(cont, Shape{seq_length, batch_size, 1})->getOutput(0); + + // Initialize cell and hidden states with zeros. + auto zero_tensor = createZeroedTensor(Shape{1, batch_size, hidden_size}); + auto c_t = createOp(zero_tensor)->getOutput(0); + auto h_t = createOp(zero_tensor)->getOutput(0); + + auto x_xw = createFullyConnected(x, xw, 2); + auto x_xw_b = createOp(x_xw, xb)->getOutput(0); + + // Split input and continuation tensors into seq_length slices. + std::vector x_xw_b_slices = createSplit(x_xw_b, seq_length, 0); + std::vector cont_slices = createSplit(cont, seq_length, 0); + std::vector h_slices(seq_length); + + for (int32_t t = 0; t < seq_length; t++) + { + auto c_cont_t = createOp(c_t, cont_slices[t])->getOutput(0); + auto h_cont_t = createOp(h_t, cont_slices[t])->getOutput(0); + + auto x_xw_b_t = x_xw_b_slices[t]; + auto h_hw_t = createFullyConnected(h_cont_t, hw, 2); + auto activation_inputs_concat = createOp(x_xw_b_t, h_hw_t)->getOutput(0); + auto activation_inputs = createSplit(activation_inputs_concat, 4, 2); + + auto i_t = createOp(activation_inputs[0])->getOutput(0); + auto f_t = createOp(activation_inputs[1])->getOutput(0); + auto o_t = createOp(activation_inputs[2])->getOutput(0); + auto g_t = createOp(activation_inputs[3])->getOutput(0); + + c_t = createOp(createOp(c_cont_t, f_t)->getOutput(0), + createOp(i_t, g_t)->getOutput(0)) + ->getOutput(0); + h_t = createOp(createOp(c_t)->getOutput(0), o_t)->getOutput(0); + + h_slices[t] = h_t; + } + + return {createOp(h_slices, 0)->getOutput(0)}; +} + +} // namespace mir_caffe diff --git a/compiler/mir-caffe-importer/caffe_op_creator.h b/compiler/mir-caffe-importer/caffe_op_creator.h new file mode 100644 index 000000000..721bb90b8 --- /dev/null +++ b/compiler/mir-caffe-importer/caffe_op_creator.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE_OP_CREATOR_H +#define MIR_CAFFE_OP_CREATOR_H + +#include +#include +#include +#include + +#include "mir/Graph.h" +#include "mir/TensorVariant.h" +#include "mir/Shape.h" + +#include "caffe/proto/caffe.pb.h" + +namespace mir_caffe +{ + +class CaffeOpCreator +{ +public: + explicit CaffeOpCreator(mir::Graph *g) : _graph(g){}; + + std::vector convertInput(const caffe::LayerParameter &layer); + + std::vector + convertConvolution(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertInnerProduct(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertConcat(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertPooling(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertSoftmax(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertReshape(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertReLU(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertScale(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertBatchNorm(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertDropout(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertDeconvolution(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertELU(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertEmbed(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertSigmoid(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertTanH(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertEltwise(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertSplit(const caffe::LayerParameter &layer, + const std::vector &inputs); + + std::vector + convertLSTM(const caffe::LayerParameter &layer, + const std::vector &inputs); + + void checkConvolution(const caffe::LayerParameter &layer, + std::set &problems_ops_set); + + void checkPooling(const caffe::LayerParameter &layer, std::set &problems_ops_set); + + void checkReshape(const caffe::LayerParameter &layer, std::set &problems_ops_set); + + void checkBatchNorm(const caffe::LayerParameter &layer, std::set &problems_ops_set); + + void checkLSTM(const caffe::LayerParameter &layer, std::set &problems_ops_set); + +private: + mir::Graph *_graph = nullptr; + + std::vector createSplit(mir::Operation::Output *arg, int32_t num_parts, + int32_t axis); + + mir::Operation::Output *createFullyConnected(mir::Operation::Output *input, + mir::Operation::Output *weights, int32_t axis); + + mir::TensorVariant convertBlob(const caffe::BlobProto &blob); + + template mir::Operation *createOp(Types &&... args); +}; + +template +mir::Operation *CaffeOpCreator::createOp(Types &&... args) +{ + return _graph->create(std::forward(args)...); +} + +} // namespace mir_caffe + +#endif // MIR_CAFFE_OP_CREATOR_H diff --git a/compiler/mir-caffe-importer/caffe_op_types.h b/compiler/mir-caffe-importer/caffe_op_types.h new file mode 100644 index 000000000..30fce7d5f --- /dev/null +++ b/compiler/mir-caffe-importer/caffe_op_types.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE_OP_TYPES_H +#define MIR_CAFFE_OP_TYPES_H + +namespace mir_caffe +{ + +enum class CaffeOpType +{ + absVal, + accuracy, + argMax, + batchNorm, + batchReindex, + bias, + BNLL, + clip, + concat, + contrastiveLoss, + convolution, + crop, + data, + deconvolution, + dropout, + dummyData, + eltwise, + ELU, + embed, + euclidianLoss, + exp, + filter, + flatten, + HDF5Data, + HDF5Output, + hingeLoss, + im2Col, + imageData, + infogainLoss, + innerProduct, + input, + log, + LRN, + LSTM, + memoryData, + multinomialLogisticLoss, + MVN, + parameter, + pooling, + power, + PReLU, + python, + recurrent, + reduction, + ReLU, + reshape, + RNN, + scale, + sigmoidCrossEntropyLoss, + sigmoid, + silence, + slice, + softmax, + softmaxWithLoss, + split, + SPP, + tanh, + threshold, + tile, + windowData +}; + +} // namespace mir_caffe + +#endif // MIR_CAFFE_OP_TYPES_H diff --git a/compiler/mir-caffe-importer/requires.cmake b/compiler/mir-caffe-importer/requires.cmake new file mode 100644 index 000000000..1059c50d3 --- /dev/null +++ b/compiler/mir-caffe-importer/requires.cmake @@ -0,0 +1 @@ +require("mir") diff --git a/compiler/mir-caffe2-importer/CMakeLists.txt b/compiler/mir-caffe2-importer/CMakeLists.txt new file mode 100644 index 000000000..da55839a7 --- /dev/null +++ b/compiler/mir-caffe2-importer/CMakeLists.txt @@ -0,0 +1,29 @@ +nnas_find_package(PytorchSource QUIET) +nnas_find_package(Protobuf QUIET) + +if (NOT PytorchSource_FOUND OR NOT Protobuf_FOUND) + return() +endif() + +Protobuf_Generate(CAFFE2_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated/caffe2" + "${PytorchSource_DIR}" "caffe2/proto/caffe2.proto") + +add_library(caffe2proto STATIC ${CAFFE2_PROTO_SOURCES}) +set_target_properties(caffe2proto PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(caffe2proto PUBLIC ${CAFFE2_PROTO_INCLUDE_DIRS}) +target_link_libraries(caffe2proto PUBLIC libprotobuf) + + +set(MIR_CAFFE2_IMPORTER_SOURCES + caffe2_importer.cpp + caffe2_importer.h + caffe2_op_creator.cpp + caffe2_op_creator.h + caffe2_op_types.h + caffe2_proto_helper.cpp + caffe2_proto_helper.h) + +add_library(mir_caffe2_importer STATIC ${MIR_CAFFE2_IMPORTER_SOURCES}) +set_target_properties(mir_caffe2_importer PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(mir_caffe2_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(mir_caffe2_importer PUBLIC mir caffe2proto PRIVATE stdex) diff --git a/compiler/mir-caffe2-importer/caffe2_importer.cpp b/compiler/mir-caffe2-importer/caffe2_importer.cpp new file mode 100644 index 000000000..5a6eef0aa --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_importer.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caffe2_importer.h" +#include "caffe2/proto/caffe2.pb.h" +#include "caffe2_op_types.h" +#include "caffe2_op_creator.h" +#include "caffe2_proto_helper.h" + +#include "mir/ops/InputOp.h" +#include "mir/ops/OutputOp.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +using namespace mir_caffe2; + +class Caffe2Importer +{ +public: + explicit Caffe2Importer(std::string predict_net, std::string init_net, + const std::vector> &input_shapes); + + /// @brief Load the model and convert it into a MIR Graph. + std::unique_ptr importModel(); + + ~Caffe2Importer(); + +private: + std::string _predictNet; + std::string _initNet; + std::unique_ptr _graph; + std::unique_ptr _predict_net; + std::unique_ptr _init_net; + std::unique_ptr _opCreator; + std::vector _inputShapes; + + static const std::map _operatorTypes; + + // Maps Caffe2 operator input names to corresponding MIR operation outputs. + std::unordered_map _blobNameToOutput; + + void import(); + std::unique_ptr createIR(); + + /** + * @brief Pass through caffe2 graph and collect ops unsupported by NNC + * @throw PassException with message, containing detected problems + */ + void collectUnsupportedOps(); + + /** + * @brief Creating MIR node from single caffe2 operator + */ + void createMIRNodesFromOp(const ::caffe2::OperatorDef &op); + + /** + * @brief Returns MIR operation outputs corresponding to the inputs of the given operator. + */ + std::vector getInputMIROps(const ::caffe2::OperatorDef &op); + + void setOutputForTensor(const std::string &tensor_name, Operation::Output *output); + mir::Operation::Output *getOutputForTensor(const std::string &name) const; + + /** + * @brief Mark output MIR nodes + */ + void setGraphOutputs(); +}; + +using namespace ::caffe2; +using mir::Shape; + +Caffe2Importer::Caffe2Importer(std::string predict_net, std::string init_net, + const std::vector> &input_shapes) + : _predictNet(std::move(predict_net)), _initNet(std::move(init_net)) +{ + for (auto &shape : input_shapes) + _inputShapes.emplace_back(shape); + + _graph = stdex::make_unique(); + _opCreator = stdex::make_unique(_graph.get()); +} + +Caffe2Importer::~Caffe2Importer() = default; + +static void loadModelFile(const std::string &filename, caffe2::NetDef *net) +{ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + int file_handle = open(filename.c_str(), O_RDONLY); + + if (file_handle == -1) + throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) + + "."); + + google::protobuf::io::FileInputStream file_stream(file_handle); + file_stream.SetCloseOnDelete(true); + + google::protobuf::io::CodedInputStream coded_stream(&file_stream); + coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX); + + if (!net->ParseFromCodedStream(&coded_stream)) + throw std::runtime_error("Couldn't parse file \"" + filename + "\"."); + + // If the file has not been consumed entirely, assume that the file is in the wrong format. + if (!coded_stream.ConsumedEntireMessage()) + throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely."); +} + +void Caffe2Importer::import() +{ + _predict_net = stdex::make_unique(); + loadModelFile(_predictNet, _predict_net.get()); + + _init_net = stdex::make_unique(); + loadModelFile(_initNet, _init_net.get()); + + collectUnsupportedOps(); +} + +std::unique_ptr Caffe2Importer::createIR() +{ + // Load initializers. + for (const auto &op : _init_net->op()) + createMIRNodesFromOp(op); + + // Create inputs. This has to be done after processing initializers, because they may contain + // fake inputs. + // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that: + // - there is exactly one input; + // - the input is for the first layer; + // - the input has 'float' element type. + const auto &input_name = _predict_net->op(0).input(0); + mir::TensorType input_type(mir::DataType::FLOAT32, _inputShapes[0]); + auto input = _graph->create(input_type)->getOutput(0); + setOutputForTensor(input_name, input); + + for (const auto &op : _predict_net->op()) + createMIRNodesFromOp(op); + + setGraphOutputs(); + + return std::move(_graph); +} + +std::unique_ptr Caffe2Importer::importModel() +{ + import(); + return createIR(); +} + +void Caffe2Importer::collectUnsupportedOps() +{ + std::set unsupportedOps; + for (const auto &op : _predict_net->op()) + { + if (_operatorTypes.find(op.type()) == _operatorTypes.end()) + unsupportedOps.insert(op.type()); + } + + if (!unsupportedOps.empty()) + { + std::string exceptionMsg("Can't load model, unsupported operators:"); + for (const auto &op : unsupportedOps) + exceptionMsg.append("\n * " + op); + throw std::runtime_error(exceptionMsg); + } +} + +void Caffe2Importer::createMIRNodesFromOp(const OperatorDef &op) +{ + std::vector outputs; + + auto inputs = getInputMIROps(op); + + SupportedCaffe2OpType opType = _operatorTypes.at(op.type()); + switch (opType) + { + case SupportedCaffe2OpType::constantFill: + case SupportedCaffe2OpType::givenTensorFill: + case SupportedCaffe2OpType::givenTensorInt64Fill: + outputs = _opCreator->convertConstant(inputs, op); + break; + case SupportedCaffe2OpType::add: + outputs = _opCreator->convertAdd(inputs, op); + break; + case SupportedCaffe2OpType::averagePool: + outputs = _opCreator->convertAveragePool(inputs, op); + break; + case SupportedCaffe2OpType::conv: + outputs = _opCreator->convertConv(inputs, op); + break; + case SupportedCaffe2OpType::concat: + outputs = _opCreator->convertConcat(inputs, op); + break; + case SupportedCaffe2OpType::dropout: + outputs = _opCreator->convertDropout(inputs, op); + break; + case SupportedCaffe2OpType::FC: + outputs = _opCreator->convertFC(inputs, op); + break; + case SupportedCaffe2OpType::maxPool: + outputs = _opCreator->convertMaxPool(inputs, op); + break; + case SupportedCaffe2OpType::mul: + outputs = _opCreator->convertMul(inputs, op); + break; + case SupportedCaffe2OpType::relu: + outputs = _opCreator->convertRelu(inputs); + break; + case SupportedCaffe2OpType::resizeNearest: + outputs = _opCreator->convertResizeNearest(inputs, op); + break; + case SupportedCaffe2OpType::sigmoid: + outputs = _opCreator->convertSigmoid(inputs); + break; + case SupportedCaffe2OpType::softmax: + outputs = _opCreator->convertSoftmax(inputs, op); + break; + case SupportedCaffe2OpType::spatialBN: + outputs = _opCreator->convertSpatialBN(inputs, op); + break; + case SupportedCaffe2OpType::sum: + outputs = _opCreator->convertSum(inputs); + break; + case SupportedCaffe2OpType::clip: + outputs = _opCreator->convertClip(inputs, op); + break; + case SupportedCaffe2OpType::reshape: + outputs = _opCreator->convertReshape(inputs, op); + break; + default: + assert(false && "All unsupported types should have been found before this pass."); + } + + for (size_t i = 0; i < outputs.size(); ++i) + { + setOutputForTensor(op.output(i), outputs[i]); + } +} + +std::vector Caffe2Importer::getInputMIROps(const OperatorDef &op) +{ + std::vector inputs; + + for (const auto &input_name : op.input()) + { + inputs.push_back(getOutputForTensor(input_name)); + } + + return inputs; +} + +void Caffe2Importer::setOutputForTensor(const std::string &tensor_name, Operation::Output *output) +{ + auto it = _blobNameToOutput.find(tensor_name); + if (it != _blobNameToOutput.cend()) + { + // caffe2 input blob name could be same as output blob name, and next line will overwrite + // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem + it->second->setName(""); + } + output->setName(tensor_name); + _blobNameToOutput[tensor_name] = output; +} + +mir::Operation::Output *Caffe2Importer::getOutputForTensor(const std::string &name) const +{ + return _blobNameToOutput.at(name); +} + +void Caffe2Importer::setGraphOutputs() +{ + // Create outputs. + // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that: + // - there is exactly one output; + // - the output is from the last layer. + const auto &output_name = _predict_net->op().rbegin()->output(0); + auto output = getOutputForTensor(output_name); + _graph->create(output); +} + +const std::map Caffe2Importer::_operatorTypes = { + {"Add", SupportedCaffe2OpType::add}, + {"AveragePool", SupportedCaffe2OpType::averagePool}, + {"Conv", SupportedCaffe2OpType::conv}, + {"Concat", SupportedCaffe2OpType::concat}, + {"ConstantFill", SupportedCaffe2OpType::constantFill}, + {"Dropout", SupportedCaffe2OpType::dropout}, + {"FC", SupportedCaffe2OpType::FC}, + {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill}, + {"MaxPool", SupportedCaffe2OpType::maxPool}, + {"Mul", SupportedCaffe2OpType::mul}, + {"Relu", SupportedCaffe2OpType::relu}, + {"ResizeNearest", SupportedCaffe2OpType::resizeNearest}, + {"Sigmoid", SupportedCaffe2OpType::sigmoid}, + {"Softmax", SupportedCaffe2OpType::softmax}, + {"SpatialBN", SupportedCaffe2OpType::spatialBN}, + {"Sum", SupportedCaffe2OpType::sum}, + {"Clip", SupportedCaffe2OpType::clip}, + {"Reshape", SupportedCaffe2OpType::reshape}, + {"GivenTensorInt64Fill", SupportedCaffe2OpType::givenTensorInt64Fill}, +}; +} + +namespace mir_caffe2 +{ + +std::unique_ptr loadModel(std::string predict_net, std::string init_net, + const std::vector> &input_shapes) +{ + Caffe2Importer importer(std::move(predict_net), std::move(init_net), input_shapes); + return importer.importModel(); +} + +} // namespace mir_caffe2 diff --git a/compiler/mir-caffe2-importer/caffe2_importer.h b/compiler/mir-caffe2-importer/caffe2_importer.h new file mode 100644 index 000000000..213fbe98d --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_importer.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE2_IMPORTER_H +#define MIR_CAFFE2_IMPORTER_H + +#include +#include +#include + +#include "mir/Graph.h" + +namespace mir_caffe2 +{ + +std::unique_ptr loadModel(std::string predict_net, std::string init_net, + const std::vector> &input_shapes); + +} // namespace mir_caffe2 + +#endif // MIR_CAFFE2_IMPORTER_H diff --git a/compiler/mir-caffe2-importer/caffe2_op_creator.cpp b/compiler/mir-caffe2-importer/caffe2_op_creator.cpp new file mode 100644 index 000000000..d279fb1ed --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_op_creator.cpp @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caffe2_op_creator.h" +#include "caffe2_proto_helper.h" + +#include "mir/ops/AddOp.h" +#include "mir/ops/AvgPool2DOp.h" +#include "mir/ops/CappedReluOp.h" +#include "mir/ops/ConcatOp.h" +#include "mir/ops/ConstantOp.h" +#include "mir/ops/Conv2DOp.h" +#include "mir/ops/FullyConnectedOp.h" +#include "mir/ops/MaxPool2DOp.h" +#include "mir/ops/MulOp.h" +#include "mir/ops/ReluOp.h" +#include "mir/ops/ReshapeOp.h" +#include "mir/ops/ResizeOp.h" +#include "mir/ops/SigmoidOp.h" +#include "mir/ops/SoftmaxOp.h" +#include "mir/ops/TransposeOp.h" + +#include "mir/Index.h" +#include "mir/Shape.h" +#include "mir/ShapeRange.h" +#include "mir/Tensor.h" +#include "mir/TensorUtil.h" + +#include +#include + +namespace mir_caffe2 +{ + +using namespace ::caffe2; +using namespace mir; + +// +// Helper functions +// + +static std::pair, std::vector> +getPadding(const ::caffe2::OperatorDef &op) +{ + + if (hasArgument(op.arg(), "pads")) + { + // pads order: t l b r + auto pads_arg = findArgumentByName(op.arg(), "pads"); + + std::vector paddings; + for (const auto &pad : pads_arg.ints()) + paddings.push_back(static_cast(pad)); + + assert(paddings.size() == 4); + + int32_t pad_t = paddings[0]; + int32_t pad_l = paddings[1]; + int32_t pad_b = paddings[2]; + int32_t pad_r = paddings[3]; + + std::vector padding_before{pad_t, pad_l}; + std::vector padding_after{pad_b, pad_r}; + return {padding_before, padding_after}; + } + + bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") || + hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b"); + + if (has_custom_pad) + { + int32_t pad_l = getSingleArgument(op, "pad_l", 0); + int32_t pad_t = getSingleArgument(op, "pad_t", 0); + int32_t pad_r = getSingleArgument(op, "pad_r", 0); + int32_t pad_b = getSingleArgument(op, "pad_b", 0); + + std::vector padding_before{pad_t, pad_l}; + std::vector padding_after{pad_b, pad_r}; + return {padding_before, padding_after}; + } + + int32_t pad = getSingleArgument(op, "pad", 0); + return {{pad, pad}, {pad, pad}}; +} + +static std::vector getStrides(const ::caffe2::OperatorDef &op) +{ + std::vector strides; + + if (hasArgument(op.arg(), "stride")) + { + std::int32_t stride = getSingleArgument(op, "stride", 1); + strides = {stride, stride}; + } + + if (hasArgument(op.arg(), "strides")) + { + // strides order: h w + auto strides_arg = findArgumentByName(op.arg(), "strides"); + for (const auto &s : strides_arg.ints()) + strides.push_back(s); + } + + assert(!strides.empty() && "Strides not found"); + + return strides; +} + +static std::vector getWindowSize(const ::caffe2::OperatorDef &op, + const std::vector &inputs) +{ + int is_global_pooling = getSingleArgument(op, "global_pooling", 0); + bool has_custom_kernel_size = + hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); + bool has_custom_kernels_size = hasArgument(op.arg(), "kernels"); + + int kernel_h(0), kernel_w(0); + if (is_global_pooling) + { + const auto &input_shape = inputs[0]->getShape(); + assert(input_shape.rank() == 4 && "getWindowSize() inputs must be of rank 4"); + kernel_h = input_shape.dim(2); + kernel_w = input_shape.dim(3); + } + else + { + if (has_custom_kernel_size) + { + kernel_h = getSingleArgument(op, "kernel_h", 0); + kernel_w = getSingleArgument(op, "kernel_w", 0); + } + else + { + if (has_custom_kernels_size) + { + // kernels order: h w + std::vector kernels; + auto kernels_arg = findArgumentByName(op.arg(), "kernels"); + for (const auto &ker : kernels_arg.ints()) + kernels.push_back(static_cast(ker)); + assert(kernels.size() == 2); + kernel_h = kernels[0]; + kernel_w = kernels[1]; + } + else + { + kernel_h = kernel_w = getSingleArgument(op, "kernel", 0); + } + } + } + return {kernel_h, kernel_w}; +} + +// +// Check functions +// + +static void checkLayout(const OperatorDef &op) +{ + if (getSingleArgument(op, "order", "NCHW") != "NCHW") + throw std::runtime_error(op.type() + ": only 'NCHW' axis order is supported"); +} + +static void checkConvLikeOp(const ::caffe2::OperatorDef &op) +{ + checkLayout(op); + + // Padding + bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") || + hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b"); + + if (has_custom_pad && hasArgument(op.arg(), "pad")) + throw std::runtime_error("Custom pad can't be combined with overall pad"); + + if (has_custom_pad && + !(hasArgument(op.arg(), "pad_l") && hasArgument(op.arg(), "pad_r") && + hasArgument(op.arg(), "pad_t") && hasArgument(op.arg(), "pad_b"))) + throw std::runtime_error("If one custom pad specified - all custom pads must be specified"); + + // Kernel size + bool has_custom_kernel_size = + hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); + + if (has_custom_kernel_size && hasArgument(op.arg(), "kernel")) + throw std::runtime_error("Custom kernel size can't be combined with overall kernel size"); + + if (has_custom_kernel_size && + !(hasArgument(op.arg(), "kernel_h") && hasArgument(op.arg(), "kernel_w"))) + throw std::runtime_error( + "If one custom kernel size specified - all custom kernel sizes must be specified"); +} + +static mir::TensorVariant createTensor(const OperatorDef &op) +{ + assert(hasArgument(op.arg(), "shape") && hasArgument(op.arg(), "values")); + + const auto &shape = findArgumentByName(op.arg(), "shape"); + const auto &values = findArgumentByName(op.arg(), "values"); + + mir::DataType element_type; + const void *src_data; + // if values on floats + if (!values.floats().empty()) + { + element_type = mir::DataType::FLOAT32; + src_data = values.floats().data(); + } + else + { + assert(!values.ints().empty()); + if (op.type() == "GivenTensorInt64Fill") + { + element_type = mir::DataType::INT64; + } + else + { + element_type = mir::DataType::INT32; + } + src_data = values.ints().data(); + } + + mir::Shape tensor_shape(shape.ints_size()); + + for (int i = 0; i < shape.ints_size(); ++i) + { + tensor_shape.dim(i) = shape.ints(i); + } + + return mir::TensorVariant({element_type, tensor_shape}, src_data); +} + +// +// Convert functions +// + +std::vector +Caffe2OpCreator::convertConstant(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + // Constant may not contain any data if it is a fake input. + if (!hasArgument(op.arg(), "values")) + return {}; + + return {createOp(createTensor(op))->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertAdd(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + assert(inputs.size() == 2); + auto lhs = inputs[0]; + auto rhs = inputs[1]; + + if (getSingleArgument(op, "broadcast", 0) != 0) + { + // FIXME This only works when 'axis' == 1 and the second input is 1-D. + rhs = createOp(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0); + auto result = createOp(lhs, rhs)->getOutput(0); + return {result}; + } + + auto result = createOp(lhs, rhs)->getOutput(0); + return {result}; +} + +std::vector +Caffe2OpCreator::convertAveragePool(const std::vector &inputs, + const OperatorDef &op) +{ + checkConvLikeOp(op); + + assert(inputs.size() == 1); + auto input = inputs[0]; + + AvgPool2DOpAttributes attributes; + std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op); + attributes.window = getWindowSize(op, inputs); + attributes.strides = getStrides(op); + attributes.include_pad = false; + attributes.data_format = DataFormat::NCHW; + auto result = createOp(input, attributes)->getOutput(0); + return {result}; +} + +std::vector +Caffe2OpCreator::convertConv(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + // dilation order: h w (not used) + mir::Conv2DOpAttributes attributes; + attributes.strides = getStrides(op); + std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op); + attributes.num_groups = getSingleArgument(op, "group", 1); + attributes.data_format = DataFormat::NCHW; + + std::vector perm{0, 2, 3, 1}; // OIHW -> OHWI + auto kernel = createOp(inputs[1], perm)->getOutput(0); + auto result = createOp(inputs[0], kernel, attributes)->getOutput(0); + + if (op.input_size() > 2) + { + auto bias = inputs[2]; + bias = createOp(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, bias)->getOutput(0); + } + + return {result}; +} + +std::vector +Caffe2OpCreator::convertConcat(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + checkLayout(op); + + // `1` corresponds to the default (channels) axis. + int axis = getSingleArgument(op, "axis", 1); + auto result = createOp(inputs, axis); + return {result->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertDropout(const std::vector &inputs, + const ::caffe2::OperatorDef &) +{ + // This is a no-op in inference mode. + return {inputs[0]}; +} + +std::vector +Caffe2OpCreator::convertFC(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + for (auto &s : {"axis", "axis_w", "float16_compute"}) + if (hasArgument(op.arg(), s)) + throw std::runtime_error(std::string("FC: only default '") + s + "' value is supported"); + + const auto &input_shape = inputs[0]->getShape(); + // Transform input into 2-D tensor by flattening axes + Shape shape{input_shape.dim(0), input_shape.numElements() / input_shape.dim(0)}; + + auto reshape = createOp(inputs[0], shape)->getOutput(0); + auto weights = + createOp(inputs[1], std::vector{1, 0})->getOutput(0); + auto result = createOp(reshape, weights)->getOutput(0); + result = createOp(result, inputs[2])->getOutput(0); + + return {result}; +} + +std::vector +Caffe2OpCreator::convertMaxPool(const std::vector &inputs, + const OperatorDef &op) +{ + checkConvLikeOp(op); + + assert(inputs.size() == 1); + auto input = inputs[0]; + + MaxPool2DOpAttributes attributes; + std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op); + attributes.window = getWindowSize(op, inputs); + attributes.strides = getStrides(op); + attributes.data_format = DataFormat::NCHW; + auto result = createOp(input, attributes)->getOutput(0); + return {result}; +} + +std::vector +Caffe2OpCreator::convertMul(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + assert(inputs.size() == 2); + auto lhs = inputs[0]; + auto rhs = inputs[1]; + + if (getSingleArgument(op, "broadcast", 0) != 0) + { + // FIXME This only works when `axis` == 1 and the second input is 1-D. + rhs = createOp(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0); + auto result = createOp(lhs, rhs)->getOutput(0); + return {result}; + } + + auto result = createOp(lhs, rhs)->getOutput(0); + return {result}; +} + +std::vector +Caffe2OpCreator::convertRelu(const std::vector &inputs) +{ + auto relu = createOp(inputs[0]); + return {relu->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertResizeNearest(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + std::vector scales(4); + assert(inputs[0]->getShape().rank() == 4 && "only 4d tensors is supported"); + // Assuming NCHW format. + scales[0] = 1.0f; + scales[1] = 1.0f; + scales[2] = getSingleArgument(op, "height_scale", 1.0f); + scales[3] = getSingleArgument(op, "width_scale", 1.0f); + auto result = + createOp(inputs[0], ops::ResizeOp::ResizeMethod::nearestNeighbor, scales) + ->getOutput(0); + return {result}; +} + +std::vector +Caffe2OpCreator::convertSigmoid(const std::vector &inputs) +{ + auto result = createOp(inputs[0]); + return {result->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertSoftmax(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + int axis = getSingleArgument(op, "axis", 1); + auto softmax = createOp(inputs[0], axis); + return {softmax->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertSpatialBN(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + checkLayout(op); + + // Sanity checks + if (op.input_size() != 5) + throw std::runtime_error( + "SpatialBN must have exactly 5 inputs ('sums' and 'sumsq' are not supported yet)"); + if (getSingleArgument(op, "is_test", 1) != 1) + throw std::runtime_error("SpatialBN: only test mode supported"); + + // overall_res = (X - mean) / sqrt(var + epsilon) * scale + bias + + auto scale_op = dynamic_cast(inputs[1]->getNode()); + auto bias_op = dynamic_cast(inputs[2]->getNode()); + auto mean_op = dynamic_cast(inputs[3]->getNode()); + auto var_op = dynamic_cast(inputs[4]->getNode()); + if (scale_op == nullptr || bias_op == nullptr || mean_op == nullptr || var_op == nullptr) + throw std::runtime_error( + "SpatialBN: non-constant 'scale', 'bias', 'mean' and 'var' inputs are not supported yet."); + + const auto &scale_tensor = scale_op->getValue(); + const auto &bias_tensor = bias_op->getValue(); + const auto &mean_tensor = mean_op->getValue(); + const auto &var_tensor = var_op->getValue(); + float eps = getSingleArgument(op, "epsilon", 1e-5f); + + // res1 = X - mean + Tensor bias_data(mean_tensor); + for (auto &idx : ShapeRange(bias_data.getShape())) + bias_data.at(idx) *= -1; + + auto mean = createOp(mean_tensor)->getOutput(0); + mean = createOp(mean, Shape{1, mean->getShape().dim(0), 1, 1})->getOutput(0); + auto result = createOp(inputs[0], mean)->getOutput(0); + + // res2 = res1 * scale / (var + epsilon) + Tensor multiplier(scale_tensor); + for (auto &idx : ShapeRange(scale_tensor.getShape())) + multiplier.at(idx) /= std::sqrt(*reinterpret_cast(var_tensor.at(idx)) + eps); + auto scale = createOp(scale_tensor)->getOutput(0); + scale = createOp(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, scale)->getOutput(0); + + // overall_res = res2 + bias + auto bias = createOp(bias_tensor)->getOutput(0); + bias = createOp(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0); + result = createOp(result, bias)->getOutput(0); + + return {result}; +} + +std::vector +Caffe2OpCreator::convertSum(const std::vector &inputs) +{ + auto result = createOp(inputs[0], inputs[1])->getOutput(0); + for (int i = 2; i < static_cast(inputs.size()); ++i) + { + result = createOp(result, inputs[i])->getOutput(0); + } + return {result}; +} + +std::vector +Caffe2OpCreator::convertClip(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + + float max = getSingleArgument(op, "max", float(0)); + float min = getSingleArgument(op, "min", float(0)); + + assert(max > 0.0 && min == 0.0 && "Support only if clip is CappedRelu"); + auto cap_relu = createOp(inputs[0], max); + + return {cap_relu->getOutput(0)}; +} + +std::vector +Caffe2OpCreator::convertReshape(const std::vector &inputs, + const ::caffe2::OperatorDef &op) +{ + auto shape_op = dynamic_cast(inputs[1]->getNode()); + if (shape_op == nullptr) + throw std::runtime_error("Reshape: non-constant shape is not supported yet."); + + const auto &shape_tensor = shape_op->getValue(); + + Tensor out_shape_tensor(shape_tensor); + + ShapeRange range(out_shape_tensor.getShape()); + std::vector shape_vec; + for (const auto &index : range) + { + shape_vec.push_back(static_cast(out_shape_tensor.at(index))); + } + Shape out_shape(shape_vec); + + auto reshape = createOp(inputs[0], out_shape); + + return {reshape->getOutput(0)}; +} + +} // namespace mir_caffe2 diff --git a/compiler/mir-caffe2-importer/caffe2_op_creator.h b/compiler/mir-caffe2-importer/caffe2_op_creator.h new file mode 100644 index 000000000..2b29378e9 --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_op_creator.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE2_OP_CREATOR_H +#define MIR_CAFFE2_OP_CREATOR_H + +#include +#include +#include +#include + +#include "mir/Graph.h" +#include "mir/Operation.h" +#include "mir/TensorVariant.h" +#include "mir/Shape.h" + +#include "caffe2/proto/caffe2.pb.h" + +namespace mir_caffe2 +{ + +using mir::Operation; +using mir::Shape; + +class Caffe2OpCreator +{ +public: + explicit Caffe2OpCreator(mir::Graph *g) : _graph(g) {} + + std::vector + convertConstant(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertAdd(const std::vector &inputs, const ::caffe2::OperatorDef &op); + + std::vector + convertAveragePool(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertConv(const std::vector &inputs, const ::caffe2::OperatorDef &op); + + std::vector + convertConcat(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertDropout(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertFC(const std::vector &inputs, const ::caffe2::OperatorDef &op); + + std::vector + convertMaxPool(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertMul(const std::vector &inputs, const ::caffe2::OperatorDef &op); + + std::vector + convertRelu(const std::vector &inputs); + + std::vector + convertResizeNearest(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertSigmoid(const std::vector &inputs); + + std::vector + convertSoftmax(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertSpatialBN(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + + std::vector + convertSum(const std::vector &inputs); + + std::vector + convertClip(const std::vector &inputs, const ::caffe2::OperatorDef &op); + + std::vector + convertReshape(const std::vector &inputs, + const ::caffe2::OperatorDef &op); + +private: + mir::Graph *_graph = nullptr; + + template mir::Operation *createOp(Types &&... args); +}; + +template +mir::Operation *Caffe2OpCreator::createOp(Types &&... args) +{ + return _graph->create(std::forward(args)...); +} + +} // namespace mir_caffe2 + +#endif // MIR_CAFFE2_OP_CREATOR_H diff --git a/compiler/mir-caffe2-importer/caffe2_op_types.h b/compiler/mir-caffe2-importer/caffe2_op_types.h new file mode 100644 index 000000000..b5e7e7631 --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_op_types.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE2_OP_TYPES_H +#define MIR_CAFFE2_OP_TYPES_H + +namespace mir_caffe2 +{ + +enum class SupportedCaffe2OpType +{ + add, + averagePool, + clip, + concat, + conv, + constantFill, + dropout, + FC, + givenTensorFill, + givenTensorInt64Fill, + maxPool, + mul, + relu, + reshape, + resizeNearest, + sigmoid, + softmax, + spatialBN, + sum, +}; + +} // namespace mir_caffe2 + +#endif // MIR_CAFFE2_OP_TYPES_H diff --git a/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp b/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp new file mode 100644 index 000000000..a7cde64cf --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caffe2_proto_helper.h" + +namespace mir_caffe2 +{ + +const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name) +{ + for (auto &arg : args) + if (arg.name() == name) + return arg; + throw std::runtime_error("Can't find argument with name: " + name); +} + +const bool hasArgument(RepArgument args, const std::string &name) +{ + for (auto &arg : args) + if (arg.name() == name) + return true; + return false; +} + +int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + const int default_value) +{ + if (hasArgument(op.arg(), argument_name)) + return static_cast(findArgumentByName(op.arg(), argument_name).i()); + return default_value; +} + +float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + const float default_value) +{ + if (hasArgument(op.arg(), argument_name)) + return findArgumentByName(op.arg(), argument_name).f(); + return default_value; +} + +std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + const std::string &default_value) +{ + if (hasArgument(op.arg(), argument_name)) + return findArgumentByName(op.arg(), argument_name).s(); + return default_value; +} + +} // namespace mir_caffe2 diff --git a/compiler/mir-caffe2-importer/caffe2_proto_helper.h b/compiler/mir-caffe2-importer/caffe2_proto_helper.h new file mode 100644 index 000000000..4c47edec8 --- /dev/null +++ b/compiler/mir-caffe2-importer/caffe2_proto_helper.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MIR_CAFFE2_PROTO_HELPER_H +#define MIR_CAFFE2_PROTO_HELPER_H + +#include "caffe2/proto/caffe2.pb.h" + +namespace mir_caffe2 +{ + +using RepArgument = const ::google::protobuf::RepeatedPtrField<::caffe2::Argument> &; + +const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name); + +const bool hasArgument(RepArgument args, const std::string &name); + +int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + int default_value); +float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + float default_value); +std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name, + const std::string &default_value); + +} // namespace mir_caffe2 + +#endif // MIR_CAFFE2_PROTO_HELPER_H diff --git a/compiler/mir-caffe2-importer/requires.cmake b/compiler/mir-caffe2-importer/requires.cmake new file mode 100644 index 000000000..1059c50d3 --- /dev/null +++ b/compiler/mir-caffe2-importer/requires.cmake @@ -0,0 +1 @@ +require("mir") diff --git a/compiler/mir-interpreter/CMakeLists.txt b/compiler/mir-interpreter/CMakeLists.txt new file mode 100644 index 000000000..814612ae9 --- /dev/null +++ b/compiler/mir-interpreter/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB_RECURSE interp_src ./*.cpp ./*.h) +add_library(mir_interpreter SHARED ${interp_src}) +target_link_libraries(mir_interpreter PUBLIC mir) +target_include_directories(mir_interpreter PUBLIC include) diff --git a/compiler/mir-interpreter/include/MirInterpreter.h b/compiler/mir-interpreter/include/MirInterpreter.h new file mode 100644 index 000000000..c3d971716 --- /dev/null +++ b/compiler/mir-interpreter/include/MirInterpreter.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MIR_INTERPRETER_ +#define _MIR_INTERPRETER_ + +#include "mir/Visitor.h" +#include "mir/Operation.h" +#include "mir/TensorVariant.h" +#include +#include + +namespace mir_interpreter +{ + +class MIRInterpreter : public mir::Visitor +{ +public: + explicit MIRInterpreter() = default; + + ~MIRInterpreter() override = default; + + /// @brief Set tensor to the interpreter environment. + void setTensor(const mir::Operation::Output *output, mir::TensorVariant tensor); + + /// @brief Get tensor from the interpreter environment. + const mir::TensorVariant &getTensor(const mir::Operation::Output *) const; + + void visit(mir::ops::AddOp &op) override; + void visit(mir::ops::AbsOp &op) override; + void visit(mir::ops::AvgPool2DOp &op) override; + void visit(mir::ops::CappedReluOp &op) override; + void visit(mir::ops::ConcatOp &op) override; + void visit(mir::ops::ConstantOp &op) override; + void visit(mir::ops::Conv2DOp &op) override; + void visit(mir::ops::DeConv2DOp &op) override; + void visit(mir::ops::DepthwiseConv2DOp &op) override; + void visit(mir::ops::DequantizeOp &op) override; + void visit(mir::ops::DivOp &op) override; + void visit(mir::ops::EluOp &op) override; + void visit(mir::ops::EqualOp &op) override; + void visit(mir::ops::FullyConnectedOp &op) override; + void visit(mir::ops::GatherOp &op) override; + void visit(mir::ops::GreaterOp &op) override; + void visit(mir::ops::HardSwishOp &op) override; + void visit(mir::ops::InputOp &op) override; + void visit(mir::ops::LeakyReluOp &op) override; + void visit(mir::ops::LessOp &op) override; + void visit(mir::ops::MaxOp &op) override; + void visit(mir::ops::MaxPool2DOp &op) override; + void visit(mir::ops::MulOp &op) override; + void visit(mir::ops::OutputOp &op) override; + void visit(mir::ops::PadOp &op) override; + void visit(mir::ops::QuantizeOp &op) override; + void visit(mir::ops::ReduceMeanOp &op) override; + void visit(mir::ops::ReluOp &op) override; + void visit(mir::ops::ReshapeOp &op) override; + void visit(mir::ops::ResizeOp &op) override; + void visit(mir::ops::SigmoidOp &op) override; + void visit(mir::ops::SliceOp &op) override; + void visit(mir::ops::SoftmaxOp &op) override; + void visit(mir::ops::SqrtOp &op) override; + void visit(mir::ops::SqueezeOp &op) override; + void visit(mir::ops::SubOp &op) override; + void visit(mir::ops::TanhOp &op) override; + void visit(mir::ops::TransposeOp &op) override; + void visit(mir::ops::BroadcastOp &op) override; + +protected: + void visit_fallback(mir::Operation &op) override; + +private: + mir::TensorVariant &allocateTensor(const mir::Operation::Output *output); + + /// @brief Gets the computed inputs for the operation. + std::vector> + getInputTensors(const mir::Operation &op); + + std::vector> + allocateOutputTensors(const mir::Operation &op); + + /// @brief Mapping of operation outputs to corresponding tensors. + std::unordered_map _tensors; +}; + +} // namespace mir_interpreter + +#endif // _MIR_INTERPRETER_ diff --git a/compiler/mir-interpreter/requires.cmake b/compiler/mir-interpreter/requires.cmake new file mode 100644 index 000000000..1059c50d3 --- /dev/null +++ b/compiler/mir-interpreter/requires.cmake @@ -0,0 +1 @@ +require("mir") diff --git a/compiler/mir-interpreter/src/MirInterpreter.cpp b/compiler/mir-interpreter/src/MirInterpreter.cpp new file mode 100644 index 000000000..245f7ddab --- /dev/null +++ b/compiler/mir-interpreter/src/MirInterpreter.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MirInterpreter.h" + +#include "ops/Add.h" +#include "ops/Abs.h" +#include "ops/AvgPool2D.h" +#include "ops/CappedReLU.h" +#include "ops/Concat.h" +#include "ops/Conv2D.h" +#include "ops/DeConv2D.h" +#include "ops/DepthwiseConv2D.h" +#include "ops/Div.h" +#include "ops/ELU.h" +#include "ops/Equal.h" +#include "ops/Fill.h" +#include "ops/FullyConnected.h" +#include "ops/Gather.h" +#include "ops/Greater.h" +#include "ops/HardSwish.h" +#include "ops/LeakyReLU.h" +#include "ops/Less.h" +#include "ops/Max.h" +#include "ops/MaxPool2D.h" +#include "ops/Mul.h" +#include "ops/Pad.h" +#include "ops/Quantization.h" +#include "ops/ReduceMean.h" +#include "ops/ReLU.h" +#include "ops/Reshape.h" +#include "ops/Sigmoid.h" +#include "ops/Slice.h" +#include "ops/Softmax.h" +#include "ops/Sqrt.h" +#include "ops/Sub.h" +#include "ops/Tanh.h" +#include "ops/Transpose.h" + +#include "ops/Common.h" + +#include "mir/OpDefs.h" + +#include +#include +#include +#include + +namespace mir_interpreter +{ + +using namespace mir; + +void MIRInterpreter::setTensor(const Operation::Output *output, TensorVariant tensor) +{ + const auto result = _tensors.emplace(output, std::move(tensor)); + if (!result.second) + { + const std::string &name = output->getName(); + throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\"."); + } +} + +TensorVariant &MIRInterpreter::allocateTensor(const Operation::Output *output) +{ + const auto result = _tensors.emplace(output, output->getType()); + if (!result.second) + { + const std::string &name = output->getName(); + throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\"."); + } + return result.first->second; +} + +const TensorVariant &MIRInterpreter::getTensor(const Operation::Output *output) const +{ + const auto it = _tensors.find(output); + if (it == _tensors.end()) + { + const std::string &name = output->getName(); + throw std::runtime_error("Can't find data for tensor \"" + name + "\"."); + } + return it->second; +} + +std::vector> +MIRInterpreter::getInputTensors(const Operation &op) +{ + std::vector> tensors; + for (const Operation::Output *input : op.getInputs()) + { + tensors.emplace_back(getTensor(input)); + } + return tensors; +} + +std::vector> +MIRInterpreter::allocateOutputTensors(const Operation &op) +{ + std::vector> tensors; + for (const Operation::Output &output : op.getOutputs()) + { + tensors.emplace_back(allocateTensor(&output)); + } + return tensors; +} + +void MIRInterpreter::visit(ops::InputOp &op) +{ + assert(_tensors.find(op.getOutput(0)) != _tensors.end()); +} + +void MIRInterpreter::visit(ops::AvgPool2DOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + AvgPool2D(op, inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(ops::ConstantOp &op) { setTensor(op.getOutput(0), op.getValue()); } + +void MIRInterpreter::visit(ops::ConcatOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Concat(inputs, op.getAxis(), outputs[0]); +} + +void MIRInterpreter::visit(ops::Conv2DOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + const mir::TensorVariant *bias = nullptr; + if (inputs.size() > 2) + { + bias = &(inputs[2].get()); + } + Conv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0], bias); +} + +void MIRInterpreter::visit(ops::MaxPool2DOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + MaxPool2D(inputs[0], op, outputs[0]); +} + +void MIRInterpreter::visit(ops::ReshapeOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Reshape(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(ops::ReluOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + ReLU(args[0], results[0]); +} + +void MIRInterpreter::visit(ops::SigmoidOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + Sigmoid(args[0], results[0]); +} + +void MIRInterpreter::visit(ops::SoftmaxOp &op) +{ + auto inputs = getInputTensors(op); + assert(inputs.size() == 1); + auto outputs = allocateOutputTensors(op); + Softmax(inputs[0], op.getAxis(), outputs[0]); +} + +void MIRInterpreter::visit(ops::FullyConnectedOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + const mir::TensorVariant *bias = nullptr; + if (inputs.size() > 2) + { + bias = &(inputs[3].get()); + } + FullyConnected(inputs[0], inputs[1], op, outputs[0], bias); +} + +void MIRInterpreter::visit(ops::CappedReluOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + CappedReLU(args[0], op.getCap(), results[0]); +} + +void MIRInterpreter::visit(ops::DepthwiseConv2DOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + const mir::TensorVariant *bias = nullptr; + if (inputs.size() > 2) + { + bias = &inputs[3].get(); + } + DepthwiseConv2D(op, inputs[0], inputs[1], outputs[0], bias); +} + +void MIRInterpreter::visit(ops::SliceOp &op) +{ + auto inputs = getInputTensors(op); + auto input = inputs[0]; + auto outputs = allocateOutputTensors(op); + Slice(input, op.getStarts(), outputs[0]); +} + +void MIRInterpreter::visit(ops::TanhOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + Tanh(args[0], results[0]); +} + +void MIRInterpreter::visit(ops::DeConv2DOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + DeConv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0]); +} + +void MIRInterpreter::visit(ops::EluOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + ELU(args[0], op.getAlpha(), results[0]); +} + +void MIRInterpreter::visit(ops::SqueezeOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + // Squeeze is just a special case of reshape. + Reshape(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(ops::PadOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Pad(inputs[0], op, outputs[0]); +} + +void MIRInterpreter::visit(ops::SqrtOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + Sqrt(args[0], results[0]); +} + +void MIRInterpreter::visit(ops::ResizeOp &op) +{ + // TODO support types other than float32 + auto inputs = getInputTensors(op); + assert(inputs[0].get().getElementType() == mir::DataType::FLOAT32); + auto outputs = allocateOutputTensors(op); + + Tensor input(inputs[0]); + assert(op.getMode() == ops::ResizeOp::ResizeMethod::nearestNeighbor); + + auto scales = op.getScales(); + Fill(outputs[0], [&scales, &input](const Index &id) { + Index in_idx; + in_idx.resize(4); + for (int i = 0; i < input.getShape().rank(); i++) + { + in_idx.at(i) = static_cast(floorf(id.at(i) / scales[i])); + } + return input.at(in_idx); + }); +} + +void MIRInterpreter::visit(ops::ReduceMeanOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + ReduceMean(inputs[0], op, outputs[0]); +} + +void MIRInterpreter::visit(ops::TransposeOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Transpose(inputs[0], op, outputs[0]); +} + +void MIRInterpreter::visit(ops::GatherOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Gather(inputs[0], inputs[1], op, outputs[0]); +} + +void MIRInterpreter::visit(ops::LeakyReluOp &op) +{ + auto args = getInputTensors(op); + auto results = allocateOutputTensors(op); + LeakyReLU(args[0], op.getAlpha(), results[0]); +} + +void MIRInterpreter::visit(ops::OutputOp &op) +{ + assert(_tensors.find(op.getInput(0)) != _tensors.end()); +} + +void MIRInterpreter::visit(ops::AddOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Add(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::DivOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Div(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::MaxOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Max(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::MulOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Mul(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::SubOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Sub(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::DequantizeOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Dequantize(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::QuantizeOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Quantize(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::HardSwishOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + HardSwish(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::GreaterOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Greater(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::LessOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Less(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::EqualOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Equal(inputs[0], inputs[1], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::AbsOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + Abs(inputs[0], outputs[0]); +} + +void MIRInterpreter::visit(mir::ops::BroadcastOp &op) +{ + auto inputs = getInputTensors(op); + auto outputs = allocateOutputTensors(op); + outputs[0].get() = TensorVariant{inputs[0], op.getOutputShape(0)}; +} + +void MIRInterpreter::visit_fallback(mir::Operation &) { throw std::runtime_error("NYI operation"); } + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/Abs.cpp b/compiler/mir-interpreter/src/ops/Abs.cpp new file mode 100644 index 000000000..547009ffd --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Abs.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 "Abs.h" +#include "Common.h" + +#include +#include + +#include + +namespace mir_interpreter +{ + +template struct AbsImpl +{ + static void run(const mir::TensorVariant &arg, mir::TensorVariant &result) + { + mir::Tensor arg_accessor(arg); + mir::Tensor res_accessor(result); + + for (const auto &index : mir::ShapeRange(result.getShape())) + { + res_accessor.at(index) = std::abs(arg_accessor.at(index)); + } + } +}; + +template <> struct AbsImpl +{ + static void run(const mir::TensorVariant &arg, mir::TensorVariant &result) + { + throw std::runtime_error{"NYI"}; + } +}; + +void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result) +{ + dispatch(arg.getElementType(), arg, result); +}; + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/Abs.h b/compiler/mir-interpreter/src/ops/Abs.h new file mode 100644 index 000000000..1ba59e647 --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Abs.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 _NNC_CORE_BACKEND_INTERPRETER_ABS_ +#define _NNC_CORE_BACKEND_INTERPRETER_ABS_ + +#include + +namespace mir_interpreter +{ + +void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result); + +} // namespace mir_interpreter + +#endif //_NNC_CORE_BACKEND_INTERPRETER_ABS_ diff --git a/compiler/mir-interpreter/src/ops/Add.cpp b/compiler/mir-interpreter/src/ops/Add.cpp new file mode 100644 index 000000000..631b854b7 --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Add.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Add.h" +#include "Common.h" + +#include "QuantizationHelpers.h" +#include "mir/Tensor.h" +#include "mir/ShapeRange.h" + +#include + +namespace mir_interpreter +{ + +using namespace mir; + +template struct AddImpl +{ + static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res); +}; + +template +void AddImpl::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res) +{ + TensorVariant broadcasted_lhs(lhs, res.getShape()); + TensorVariant broadcasted_rhs(rhs, res.getShape()); + Tensor lhs_accessor(broadcasted_lhs); + Tensor rhs_accessor(broadcasted_rhs); + Tensor res_accessor(res); + + for (const auto &index : ShapeRange(res.getShape())) + { + res_accessor.at(index) = lhs_accessor.at(index) + rhs_accessor.at(index); + } +} + +template <> struct AddImpl +{ + static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res); +}; + +void AddImpl::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res) +{ + const auto &lhs_type = lhs.getType(); + const auto &rhs_type = rhs.getType(); + const auto &res_type = res.getType(); + + assert(lhs_type.isQuantized()); + assert(rhs_type.isQuantized()); + assert(res_type.isQuantized()); + + int32_t lhs_offset = -lhs_type.getQuantization().getZeroPoint(); + int32_t rhs_offset = -rhs_type.getQuantization().getZeroPoint(); + int32_t output_offset = res_type.getQuantization().getZeroPoint(); + + double lhs_scale = lhs_type.getQuantization().getScale(); + double rhs_scale = rhs_type.getQuantization().getScale(); + double output_scale = res_type.getQuantization().getScale(); + + int left_shift = 20; + const double twice_max_input_scale = 2 * std::max(lhs_scale, rhs_scale); + const double real_lhs_multiplier = lhs_scale / twice_max_input_scale; + const double real_rhs_multiplier = rhs_scale / twice_max_input_scale; + const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); + + int32_t lhs_multiplier = 0; + int32_t rhs_multiplier = 0; + int32_t output_multiplier = 0; + int lhs_shift = 0; + int rhs_shift = 0; + int output_shift = 0; + + QuantizeMultiplierSmallerThanOneExp(real_lhs_multiplier, &lhs_multiplier, &lhs_shift); + QuantizeMultiplierSmallerThanOneExp(real_rhs_multiplier, &rhs_multiplier, &rhs_shift); + QuantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); + + TensorVariant broadcasted_lhs(lhs, res_type.getShape()); + TensorVariant broadcasted_rhs(rhs, res_type.getShape()); + + Tensor lhs_accessor(broadcasted_lhs); + Tensor rhs_accessor(broadcasted_rhs); + Tensor res_accessor(res); + + int32_t output_min = std::numeric_limits::min(); + int32_t output_max = std::numeric_limits::max(); + + for (const auto &index : ShapeRange(res_type.getShape())) + { + const int32_t lhs_val = lhs_accessor.at(index) + lhs_offset; + const int32_t rhs_val = rhs_accessor.at(index) + rhs_offset; + const int32_t shifted_lhs_val = lhs_val * (1 << left_shift); + const int32_t shifted_rhs_val = rhs_val * (1 << left_shift); + const int32_t scaled_lhs_val = + MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_lhs_val, lhs_multiplier, lhs_shift); + const int32_t scaled_rhs_val = + MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_rhs_val, rhs_multiplier, rhs_shift); + const int32_t raw_sum = scaled_lhs_val + scaled_rhs_val; + const int32_t raw_output = + MultiplyByQuantizedMultiplierSmallerThanOneExp(raw_sum, output_multiplier, output_shift) + + output_offset; + const int32_t clamped_output = std::min(output_max, std::max(output_min, raw_output)); + res_accessor.at(index) = static_cast(clamped_output); + } +} + +void Add(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res) +{ + if (lhs.getElementType() != rhs.getElementType()) + { + throw std::runtime_error{"Add with different input types is unsupported"}; + } + dispatch(res.getElementType(), lhs, rhs, res); +} + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/Add.h b/compiler/mir-interpreter/src/ops/Add.h new file mode 100644 index 000000000..48508226f --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Add.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NNC_CORE_BACKEND_INTERPRETER_ADD_ +#define _NNC_CORE_BACKEND_INTERPRETER_ADD_ + +#include "mir/TensorVariant.h" + +namespace mir_interpreter +{ + +void Add(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res); + +} // namespace mir_interpreter + +#endif //_NNC_CORE_BACKEND_INTERPRETER_ADD_ diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.cpp b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp new file mode 100644 index 000000000..3f1d65100 --- /dev/null +++ b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AvgPool2D.h" +#include "Common.h" + +#include "mir/ShapeRange.h" +#include "mir/Tensor.h" + +namespace mir_interpreter +{ + +using namespace mir; + +template class AvgPool2DImpl +{ +public: + static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input_var, + mir::TensorVariant &output); +}; + +template +void AvgPool2DImpl::run(const ops::AvgPool2DOp &op, const TensorVariant &input_var, + TensorVariant &output) +{ + const auto &input_shape = op.getInputShape(0); + const auto &output_shape = op.getOutputShape(0); + const auto &window_size = op.getWindowSize(); + const auto &strides = op.getStrides(); + const auto &padding_before = op.getPaddingBefore(); + const auto &padding_after = op.getPaddingAfter(); + (void)padding_after; + + constexpr int num_spatial_dims = 2; + assert(input_var.getShape().rank() == 4); + assert(window_size.size() == num_spatial_dims); + assert(strides.size() == num_spatial_dims); + assert(padding_before.size() == num_spatial_dims); + assert(padding_after.size() == num_spatial_dims); + + Tensor res_accessor(output); + Tensor input(input_var); + + ShapeRange in_range(input_shape); + Index in_index(input_shape.rank()); + + for (const auto &out_index : ShapeRange(output_shape)) + { + T result = 0; + size_t num_elements = 0; + + // Assuming NHWC format. + in_index.at(0) = out_index.at(0); + in_index.at(3) = out_index.at(3); + + for (const auto &window_index : ShapeRange(Shape(window_size))) + { + // Assuming NHWC format. + for (int i = 0; i < num_spatial_dims; ++i) + in_index.at(1 + i) = + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + + if (in_range.contains(in_index)) + { + num_elements++; + result += input.at(in_index); + } + else if (op.getIncludePad()) + { + num_elements++; + } + } + + result /= num_elements; + res_accessor.at(out_index) = result; + } +} + +template <> struct AvgPool2DImpl +{ + static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input, + mir::TensorVariant &output); +}; + +void AvgPool2DImpl::run(const ops::AvgPool2DOp &op, const TensorVariant &input, + TensorVariant &output) +{ + const auto &input_type = input.getType(); + const auto &output_type = op.getOutput(0)->getType(); + (void)input_type; + + assert(input_type.isQuantized()); + assert(output_type.isQuantized()); + assert(input_type.getElementType() == DataType::UINT8); + + const auto &input_shape = op.getInputShape(0); + const auto &output_shape = op.getOutputShape(0); + const auto &window_size = op.getWindowSize(); + const auto &strides = op.getStrides(); + const auto &padding_before = op.getPaddingBefore(); + const auto &padding_after = op.getPaddingAfter(); + (void)padding_after; + + constexpr int num_spatial_dims = 2; + assert(input.getShape().rank() == 4); + assert(window_size.size() == num_spatial_dims); + assert(strides.size() == num_spatial_dims); + assert(padding_before.size() == num_spatial_dims); + assert(padding_after.size() == num_spatial_dims); + + Tensor input_accessor(input); + Tensor res_accessor(output); + + ShapeRange in_range(input_shape); + Index in_index(input_shape.rank()); + + int32_t output_min = std::numeric_limits::min(); + int32_t output_max = std::numeric_limits::max(); + + for (const auto &out_index : ShapeRange(output_shape)) + { + int32_t result = 0; + size_t num_elements = 0; + + // Assuming NHWC format. + in_index.at(0) = out_index.at(0); + in_index.at(3) = out_index.at(3); + + for (const auto &window_index : ShapeRange(Shape(window_size))) + { + // Assuming NHWC format. + for (int i = 0; i < num_spatial_dims; ++i) + in_index.at(1 + i) = + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + + if (in_range.contains(in_index)) + { + num_elements++; + result += input_accessor.at(in_index); + } + else if (op.getIncludePad()) + { + num_elements++; + } + } + result = (result + num_elements / 2) / num_elements; + result = std::max(result, output_min); + result = std::min(result, output_max); + res_accessor.at(out_index) = static_cast(result); + } +} + +void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input, + mir::TensorVariant &output) +{ + dispatch(output.getElementType(), op, input, output); +} + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.h b/compiler/mir-interpreter/src/ops/AvgPool2D.h new file mode 100644 index 000000000..b30574cee --- /dev/null +++ b/compiler/mir-interpreter/src/ops/AvgPool2D.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_ +#define _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_ + +#include "mir/ops/AvgPool2DOp.h" +#include "mir/TensorVariant.h" + +namespace mir_interpreter +{ + +void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input, + mir::TensorVariant &output); + +} // namespace mir_interpreter + +#endif //_NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_ diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.cpp b/compiler/mir-interpreter/src/ops/CappedReLU.cpp new file mode 100644 index 000000000..1ac95ac16 --- /dev/null +++ b/compiler/mir-interpreter/src/ops/CappedReLU.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CappedReLU.h" + +#include "mir/ShapeRange.h" +#include "mir/Tensor.h" + +#include "Common.h" + +#include +#include + +namespace mir_interpreter +{ + +template struct CappedReLUImpl +{ + static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result); +}; + +template +void CappedReLUImpl::run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result) +{ + mir::Tensor arg_accessor(arg); + mir::Tensor res_accessor(result); + + for (const auto &index : mir::ShapeRange(result.getShape())) + { + res_accessor.at(index) = std::min(std::max(arg_accessor.at(index), T(0)), static_cast(cap)); + } +} + +static float dequantize(uint8_t x, const mir::AffineQuantization &q) +{ + return (static_cast(x) - q.getZeroPoint()) * q.getScale(); +} + +static uint8_t quantize(float x, const mir::AffineQuantization &q) +{ + return (static_cast(x) / q.getScale() + q.getZeroPoint()); +} + +template <> struct CappedReLUImpl +{ + static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result) + { + mir::Tensor arg_accessor(arg); + mir::Tensor res_accessor(result); + + auto quant_info = arg.getType().getQuantization(); + assert(!quant_info.empty()); + + for (const auto &index : mir::ShapeRange(result.getShape())) + { + auto value = dequantize(arg_accessor.at(index), quant_info); + auto out_value = + quantize(std::min(std::max(value, 0.0f), cap), result.getType().getQuantization()); + res_accessor.at(index) = out_value; + } + } +}; + +void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result) +{ + dispatch(arg.getElementType(), arg, cap, result); +} + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.h b/compiler/mir-interpreter/src/ops/CappedReLU.h new file mode 100644 index 000000000..ffb756d2a --- /dev/null +++ b/compiler/mir-interpreter/src/ops/CappedReLU.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_ +#define _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_ + +#include + +namespace mir_interpreter +{ + +void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result); + +} // namespace mir_interpreter + +#endif //_NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_ diff --git a/compiler/mir-interpreter/src/ops/Common.cpp b/compiler/mir-interpreter/src/ops/Common.cpp new file mode 100644 index 000000000..dae207f2e --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Common.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "Common.h" + +namespace mir_interpreter +{ + +using namespace mir; + +Index shift(const Index &in_index, const Shape &shift_from) +{ + Index index = in_index; + assert(index.rank() == shift_from.rank()); + for (int32_t d = 0; d < in_index.rank(); ++d) + { + index.at(d) = index.at(d) + shift_from.dim(d); + } + return index; +} + +} // namespace mir_interpreter diff --git a/compiler/mir-interpreter/src/ops/Common.h b/compiler/mir-interpreter/src/ops/Common.h new file mode 100644 index 000000000..43336216e --- /dev/null +++ b/compiler/mir-interpreter/src/ops/Common.h @@ -0,0 +1,65 @@ +/* + * 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 _NNC_CORE_BACKEND_INTERPRETER_COMMON_ +#define _NNC_CORE_BACKEND_INTERPRETER_COMMON_ + +#include "mir/Tensor.h" +#include "mir/TensorVariant.h" +#include "mir/DataType.h" +#include "mir/Shape.h" +#include "mir/Index.h" + +namespace mir_interpreter +{ + +template