Imported Upstream version 1.8.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::S32:
61       return getNodeDataImpl<DataType::S32>(node, data_size);
62     default:
63       throw std::runtime_error("Unsupported type.");
64   }
65 }
66
67 bool isExecutableNode(const luci::CircleNode *node)
68 {
69   switch (node->opcode())
70   {
71     // These nodes denote inputs / outputs of a graph.
72     case luci::CircleOpcode::CIRCLECONST:
73     case luci::CircleOpcode::CIRCLEINPUT:
74     case luci::CircleOpcode::CIRCLEOUTPUT:
75     case luci::CircleOpcode::CIRCLEOUTPUTEXCLUDE:
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(
106     const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir,
107     const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph,
108     std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
109     : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir),
110       _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor)
111 {
112 }
113
114 void GraphLoader::loadTensors()
115 {
116   for (uint32_t i = 0; i < _graph->nodes()->size(); ++i)
117   {
118     const auto *node = loco::must_cast<const luci::CircleNode *>(_graph->nodes()->at(i));
119
120     if (!isTensorProducingNode(node))
121       continue;
122
123     // Only Input and Const nodes have shapes. Shapes of intermediate tensors will be inferred.
124     Shape shape{};
125     if (const auto *input_node = dynamic_cast<const luci::CircleInput *>(node))
126     {
127       shape = getNodeShape(input_node);
128     }
129     else if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
130     {
131       shape = getNodeShape(const_node);
132     }
133
134     AffineQuantization quantization;
135     if (node->quantparam() != nullptr)
136     {
137       const luci::CircleQuantParam *params = node->quantparam();
138       quantization.scale.assign(params->scale.cbegin(), params->scale.cend());
139       quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend());
140       quantization.quantized_dimension = params->quantized_dimension;
141     }
142
143     auto tensor = std::make_unique<Tensor>(node->dtype(), std::move(shape), std::move(quantization),
144                                            node->name());
145
146     if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
147     {
148       size_t data_size{};
149       const void *const_data = getNodeData(const_node, &data_size);
150       if (const_data != nullptr)
151         tensor->writeData(const_data, data_size);
152     }
153
154     _node_to_tensor.emplace(node, tensor.get());
155     _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node);
156
157     _runtime_graph->addTensor(std::move(tensor));
158   }
159 }
160
161 void GraphLoader::initInputOutputTensors() const
162 {
163   auto input_nodes = loco::input_nodes(_graph);
164   std::vector<Tensor *> input_tensors(input_nodes.size());
165   for (size_t i = 0; i < input_nodes.size(); ++i)
166   {
167     input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
168   }
169   _runtime_graph->setInputTensors(input_tensors);
170
171   auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
172   std::vector<Tensor *> output_tensors(output_nodes.size());
173   for (size_t i = 0; i < output_nodes.size(); ++i)
174   {
175     const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
176     output_tensors[i] = _node_to_tensor.at(node->from());
177   }
178   _runtime_graph->setOutputTensors(output_tensors);
179 }
180
181 void GraphLoader::loadOperators()
182 {
183   KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor);
184
185   // Create kernels for executable nodes. This has to be done in execution order.
186   for (const loco::Node *loco_node :
187        loco::postorder_traversal(loco::output_nodes(const_cast<loco::Graph *>(_graph))))
188   {
189     const auto *node = loco::must_cast<const luci::CircleNode *>(loco_node);
190
191     if (isExecutableNode(node))
192     {
193       std::unique_ptr<Kernel> kernel = node->accept(&kernel_builder);
194       _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
195       _runtime_graph->addKernel(std::move(kernel));
196     }
197   }
198 }
199
200 } // namespace luci_interpreter