Imported Upstream version 1.12.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/KernelBuilder.h"
20
21 #include <loco/IR/Algorithm.h>
22
23 namespace luci_interpreter
24 {
25 namespace
26 {
27
28 template <typename NodeT> Shape getNodeShape(const NodeT *node)
29 {
30   Shape shape(node->rank());
31   for (uint32_t i = 0; i < node->rank(); ++i)
32   {
33     shape.dim(i) = node->dim(i).value();
34   }
35   return shape;
36 }
37
38 template <DataType DT> const void *getNodeDataImpl(const luci::CircleConst *node, size_t *data_size)
39 {
40   const size_t element_size = getDataTypeSize(DT);
41   const int32_t num_elements = node->size<DT>();
42
43   *data_size = num_elements * element_size;
44   if (*data_size > 0)
45   {
46     // FIXME There is no good way to get the pointer to the data currently.
47     return &node->at<DT>(0);
48   }
49   return nullptr;
50 }
51
52 const void *getNodeData(const luci::CircleConst *node, size_t *data_size)
53 {
54   switch (node->dtype())
55   {
56     case DataType::U8:
57       return getNodeDataImpl<DataType::U8>(node, data_size);
58     case DataType::FLOAT32:
59       return getNodeDataImpl<DataType::FLOAT32>(node, data_size);
60     case DataType::S16:
61       return getNodeDataImpl<DataType::S16>(node, data_size);
62     case DataType::S32:
63       return getNodeDataImpl<DataType::S32>(node, data_size);
64     case DataType::S64:
65       return getNodeDataImpl<DataType::S64>(node, data_size);
66     default:
67       throw std::runtime_error("Unsupported type.");
68   }
69 }
70
71 bool isExecutableNode(const luci::CircleNode *node)
72 {
73   switch (node->opcode())
74   {
75     // These nodes denote inputs / outputs of a graph.
76     case luci::CircleOpcode::CIRCLECONST:
77     case luci::CircleOpcode::CIRCLEINPUT:
78     case luci::CircleOpcode::CIRCLEOUTPUT:
79     case luci::CircleOpcode::CIRCLEOUTPUTEXCLUDE:
80     // The following nodes denote outputs of multiple-output nodes.
81     case luci::CircleOpcode::CIRCLEIFOUT:
82     case luci::CircleOpcode::CIRCLESPLITOUT:
83     case luci::CircleOpcode::CIRCLEUNPACKOUT:
84       return false;
85     default:
86       return true;
87   }
88 }
89
90 bool isTensorProducingNode(const luci::CircleNode *node)
91 {
92   switch (node->opcode())
93   {
94     // Output nodes do not produce tensors.
95     case luci::CircleOpcode::CIRCLEOUTPUT:
96     // The following nodes are multiple-output nodes. They do not produce tensors, the tensors
97     // are produced by the corresponding *Out nodes instead.
98     case luci::CircleOpcode::IF:
99     case luci::CircleOpcode::SPLIT:
100     case luci::CircleOpcode::UNPACK:
101       return false;
102     default:
103       return true;
104   }
105 }
106
107 } // namespace
108
109 GraphLoader::GraphLoader(
110     const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir,
111     const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph,
112     std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
113     : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir),
114       _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor)
115 {
116 }
117
118 void GraphLoader::loadTensors()
119 {
120   for (uint32_t i = 0; i < _graph->nodes()->size(); ++i)
121   {
122     const auto *node = loco::must_cast<const luci::CircleNode *>(_graph->nodes()->at(i));
123
124     if (!isTensorProducingNode(node))
125       continue;
126
127     // Only Input and Const nodes have shapes. Shapes of intermediate tensors will be inferred.
128     Shape shape{};
129     if (const auto *input_node = dynamic_cast<const luci::CircleInput *>(node))
130     {
131       shape = getNodeShape(input_node);
132     }
133     else if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
134     {
135       shape = getNodeShape(const_node);
136     }
137
138     AffineQuantization quantization;
139     if (node->quantparam() != nullptr)
140     {
141       const luci::CircleQuantParam *params = node->quantparam();
142       assert(params->scale.size() == params->zerop.size());
143       quantization.scale.assign(params->scale.cbegin(), params->scale.cend());
144       quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend());
145       quantization.quantized_dimension = params->quantized_dimension;
146     }
147
148     auto tensor = std::make_unique<Tensor>(node->dtype(), std::move(shape), std::move(quantization),
149                                            node->name());
150
151     if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
152     {
153       size_t data_size{};
154       const void *const_data = getNodeData(const_node, &data_size);
155       if (const_data != nullptr)
156         tensor->writeData(const_data, data_size);
157     }
158
159     _node_to_tensor.emplace(node, tensor.get());
160     _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node);
161
162     _runtime_graph->addTensor(std::move(tensor));
163   }
164 }
165
166 void GraphLoader::initInputOutputTensors() const
167 {
168   auto input_nodes = loco::input_nodes(_graph);
169   std::vector<Tensor *> input_tensors(input_nodes.size());
170   for (size_t i = 0; i < input_nodes.size(); ++i)
171   {
172     input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
173   }
174   _runtime_graph->setInputTensors(input_tensors);
175
176   auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
177   std::vector<Tensor *> output_tensors(output_nodes.size());
178   for (size_t i = 0; i < output_nodes.size(); ++i)
179   {
180     const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
181     output_tensors[i] = _node_to_tensor.at(node->from());
182   }
183   _runtime_graph->setOutputTensors(output_tensors);
184 }
185
186 void GraphLoader::loadOperators()
187 {
188   KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor);
189
190   // Create kernels for executable nodes. This has to be done in execution order.
191   for (const loco::Node *loco_node :
192        loco::postorder_traversal(loco::output_nodes(const_cast<loco::Graph *>(_graph))))
193   {
194     const auto *node = loco::must_cast<const luci::CircleNode *>(loco_node);
195
196     if (isExecutableNode(node))
197     {
198       std::unique_ptr<Kernel> kernel = node->accept(&kernel_builder);
199       _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
200       _runtime_graph->addKernel(std::move(kernel));
201     }
202   }
203 }
204
205 } // namespace luci_interpreter