Imported Upstream version 1.7.0
[platform/core/ml/nnfw.git] / compiler / luci-interpreter / src / loader / GraphLoader.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "loader/GraphLoader.h"
18
19 #include "loader/ModuleLoader.h"
20 #include "loader/KernelBuilder.h"
21
22 #include <loco/IR/Algorithm.h>
23
24 namespace luci_interpreter
25 {
26 namespace
27 {
28
29 template <typename NodeT> Shape getNodeShape(const NodeT *node)
30 {
31   Shape shape(node->rank());
32   for (uint32_t i = 0; i < node->rank(); ++i)
33   {
34     shape.dim(i) = node->dim(i).value();
35   }
36   return shape;
37 }
38
39 template <DataType DT> const void *getNodeDataImpl(const luci::CircleConst *node, size_t *data_size)
40 {
41   const size_t element_size = getDataTypeSize(DT);
42   const int32_t num_elements = node->size<DT>();
43
44   *data_size = num_elements * element_size;
45   if (*data_size > 0)
46   {
47     // FIXME There is no good way to get the pointer to the data currently.
48     return &node->at<DT>(0);
49   }
50   return nullptr;
51 }
52
53 const void *getNodeData(const luci::CircleConst *node, size_t *data_size)
54 {
55   switch (node->dtype())
56   {
57     case DataType::U8:
58       return getNodeDataImpl<DataType::U8>(node, data_size);
59     case DataType::FLOAT32:
60       return getNodeDataImpl<DataType::FLOAT32>(node, data_size);
61     case DataType::S32:
62       return getNodeDataImpl<DataType::S32>(node, data_size);
63     default:
64       throw std::runtime_error("Unsupported type.");
65   }
66 }
67
68 bool isExecutableNode(const luci::CircleNode *node)
69 {
70   switch (node->opcode())
71   {
72     // These nodes denote inputs / outputs of a graph.
73     case luci::CircleOpcode::CONST:
74     case luci::CircleOpcode::CIRCLEINPUT:
75     case luci::CircleOpcode::CIRCLEOUTPUT:
76     // The following nodes denote outputs of multiple-output nodes.
77     case luci::CircleOpcode::CIRCLEIFOUT:
78     case luci::CircleOpcode::CIRCLESPLITOUT:
79     case luci::CircleOpcode::CIRCLEUNPACKOUT:
80       return false;
81     default:
82       return true;
83   }
84 }
85
86 bool isTensorProducingNode(const luci::CircleNode *node)
87 {
88   switch (node->opcode())
89   {
90     // Output nodes do not produce tensors.
91     case luci::CircleOpcode::CIRCLEOUTPUT:
92     // The following nodes are multiple-output nodes. They do not produce tensors, the tensors
93     // are produced by the corresponding *Out nodes instead.
94     case luci::CircleOpcode::IF:
95     case luci::CircleOpcode::SPLIT:
96     case luci::CircleOpcode::UNPACK:
97       return false;
98     default:
99       return true;
100   }
101 }
102
103 } // namespace
104
105 GraphLoader::GraphLoader(const ModuleLoader &module_loader, const loco::Graph *graph,
106                          RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir,
107                          std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
108     : _module_loader(module_loader), _graph(graph), _runtime_graph(runtime_graph),
109       _runtime_to_ir(runtime_to_ir), _node_to_tensor(node_to_tensor)
110 {
111 }
112
113 void GraphLoader::loadTensors()
114 {
115   for (uint32_t i = 0; i < _graph->nodes()->size(); ++i)
116   {
117     const auto *node = loco::must_cast<const luci::CircleNode *>(_graph->nodes()->at(i));
118
119     if (!isTensorProducingNode(node))
120       continue;
121
122     // Only Input and Const nodes have shapes. Shapes of intermediate tensors will be inferred.
123     Shape shape{};
124     if (const auto *input_node = dynamic_cast<const luci::CircleInput *>(node))
125     {
126       shape = getNodeShape(input_node);
127     }
128     else if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
129     {
130       shape = getNodeShape(const_node);
131     }
132
133     AffineQuantization quantization;
134     if (node->quantparam() != nullptr)
135     {
136       const luci::CircleQuantParam *params = node->quantparam();
137       quantization.scale.assign(params->scale.cbegin(), params->scale.cend());
138       quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend());
139     }
140
141     auto tensor = std::make_unique<Tensor>(node->dtype(), std::move(shape), std::move(quantization),
142                                            node->name());
143
144     if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
145     {
146       size_t data_size{};
147       const void *const_data = getNodeData(const_node, &data_size);
148       if (const_data != nullptr)
149         tensor->writeData(const_data, data_size);
150     }
151
152     _node_to_tensor.emplace(node, tensor.get());
153     _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node);
154
155     _runtime_graph->addTensor(std::move(tensor));
156   }
157 }
158
159 void GraphLoader::initInputOutputTensors() const
160 {
161   auto input_nodes = loco::input_nodes(_graph);
162   std::vector<Tensor *> input_tensors(input_nodes.size());
163   for (size_t i = 0; i < input_nodes.size(); ++i)
164   {
165     input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
166   }
167   _runtime_graph->setInputTensors(input_tensors);
168
169   auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
170   std::vector<Tensor *> output_tensors(output_nodes.size());
171   for (size_t i = 0; i < output_nodes.size(); ++i)
172   {
173     const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
174     output_tensors[i] = _node_to_tensor.at(node->from());
175   }
176   _runtime_graph->setOutputTensors(output_tensors);
177 }
178
179 void GraphLoader::loadOperators()
180 {
181   KernelBuilder kernel_builder(_module_loader, *this);
182
183   // Create kernels for executable nodes. This has to be done in execution order.
184   for (const loco::Node *loco_node :
185        loco::postorder_traversal(loco::output_nodes(const_cast<loco::Graph *>(_graph))))
186   {
187     const auto *node = loco::must_cast<const luci::CircleNode *>(loco_node);
188
189     if (isExecutableNode(node))
190     {
191       std::unique_ptr<Kernel> kernel = node->accept(&kernel_builder);
192       _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
193       _runtime_graph->addKernel(std::move(kernel));
194     }
195   }
196 }
197
198 void GraphLoader::load()
199 {
200   loadTensors();
201   initInputOutputTensors();
202   loadOperators();
203 }
204
205 } // namespace luci_interpreter