30c8f72a5cb55ff25252023acf614a8d703079e8
[platform/core/ml/nnfw.git] / runtime / onert / core / src / compiler / Linear.cc
1 /*
2  * Copyright (c) 2018 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 <algorithm>
18
19 #include "Linear.h"
20
21 #include "backend/IConfig.h"
22 #include "backend/IConstantInitializer.h"
23 #include "backend/ITensorRegister.h"
24 #include "backend/Backend.h"
25 #include "util/logging.h"
26
27 namespace onert
28 {
29 namespace compiler
30 {
31
32 std::vector<ir::OpSequenceIndex> Linear::linearize(const compiler::LoweredGraph &lowered_graph)
33 {
34   std::vector<ir::OpSequenceIndex> order;
35   lowered_graph.iterateTopolOpSeqs(
36       [&](const ir::OpSequenceIndex &index, const ir::OpSequence &) -> void {
37         order.emplace_back(index);
38       });
39   return order;
40 }
41
42 void Linear::dump(const compiler::LoweredGraph &lowered_graph,
43                   const std::vector<ir::OpSequenceIndex> &order)
44 {
45   {
46     const auto &toString = [](const onert::backend::Backend *backend) {
47       assert(backend);
48       std::string str;
49       str += backend->config()->id();
50       return "{" + str + "}";
51     };
52
53     VERBOSE(Linear) << "Final OpSequence" << std::endl;
54     for (const auto index : order)
55     {
56       const auto &op_seq = lowered_graph.op_seqs().at(index);
57       const auto lower_info = lowered_graph.getLowerInfo(index);
58       const auto &operations = lowered_graph.graph().operations();
59       VERBOSE(Linear) << "* OP_SEQ " << toString(lower_info->backend()) << " "
60                       << ir::getStrFromOpSeq(op_seq, operations) << std::endl;
61     }
62   }
63 }
64
65 void Linear::planTensors(const compiler::LoweredGraph &lowered_graph,
66                          const std::vector<ir::OpSequenceIndex> &order)
67 {
68   const auto &graph = lowered_graph.graph();
69   ir::OperandIndexMap<std::shared_ptr<backend::ITensorBuilder>> tensor_builder_map;
70
71   ir::OperandIndexMap<uint32_t> uses_map;
72   ir::OperandIndexMap<uint32_t> def_map;
73   ir::OperandIndexSequence constants;
74
75   // Prepare scanning
76   graph.operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) {
77     const auto lower_info = lowered_graph.getLowerInfo(ind);
78     // TODO Remove if onert doesn't support anymore such as
79     // GeneratedTests.reshape_quant8_weights_as_inputs
80     if (lower_info->def_factors().size() == 0 && lower_info->use_factors().size() == 0 &&
81         !graph.getInputs().contains(ind))
82     {
83       VERBOSE(LINEAR) << "Operand #" << ind.value() << " will not be used. no more process."
84                       << std::endl;
85       return;
86     }
87
88     // Unused input of subgraph
89     // TODO Register unused input as nullptr in tensor_builder
90     if (lower_info->def_factors().size() == 0 && lower_info->use_factors().size() == 0 &&
91         graph.getInputs().contains(ind))
92     {
93       VERBOSE(LINEAR) << "Operand #" << ind.value() << " will not be used. no more process."
94                       << std::endl;
95       return;
96     }
97
98     uses_map[ind] = obj.getUses().size();
99     def_map[ind] = obj.getDef().valid() ? 1 : 0;
100
101     bool is_const = obj.isConstant();
102     if (is_const)
103     {
104       constants.append(ind);
105     }
106
107     auto factor = lower_info->def_factors().getOnlyElement();
108     auto backend = factor.backend();
109     auto tensor_builder = lowered_graph.backend_contexts().at(backend)->tensor_builder;
110     if (!tensor_builder->isRegistered(ind))
111     {
112       // These tensors do not exist in any op_seq (No use and def)
113       const auto info = obj.info();
114       const auto backend_layout = factor.layout();
115       // TODO Change tensor info to have permuted shape
116       tensor_builder->registerTensorInfo(ind, info, backend_layout);
117     }
118
119     tensor_builder_map[ind] = tensor_builder;
120   });
121
122   const auto io_tensors =
123       (graph.getInputs() + graph.getOutputs()) | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED;
124
125   // If a tensor is model output, increase the use of the tensor.
126   // This aim is same to above one.
127   for (const auto &ind : io_tensors)
128   {
129     uses_map[ind]++;
130   }
131
132   // Start scanning to do notify{First|Last}Use for each tensor
133
134   // If a tensor is a constant, increase the use of the tensor.
135   // It makes the tensor not be dealloced. It means these will be deallocated last.
136   // And allocate constant operands first
137   VERBOSE(LINEAR) << "TENSORS as CONSTANT" << std::endl;
138   for (const auto &ind : constants)
139   {
140     uses_map[ind]++;
141     tensor_builder_map[ind]->notifyFirstUse(ind);
142   }
143
144   // Allocate Model's inputs
145   VERBOSE(LINEAR) << "TENSORS as MODEL INPUT" << std::endl;
146   for (const auto &ind : graph.getInputs() | ir::Remove::DUPLICATED)
147   {
148     auto tensor_builder = tensor_builder_map[ind];
149     if (!tensor_builder) // for GeneratedTests.xxx_weights_as_inputs
150       continue;
151     tensor_builder->notifyFirstUse(ind);
152   }
153
154   // At each operation,
155   // 1. Scan DEF of outputs. If the DEF, allocate it
156   // 2. Scan DEF of inputs. If variable tensor, allocate it
157   // 3. Scan USE of inputs. Decrease the USE and deallocate if the USE is 0
158   VERBOSE(LINEAR) << "TENSORS" << std::endl;
159   for (const auto op_seq_ind : order)
160   {
161     const auto &op_seq = lowered_graph.op_seqs().at(op_seq_ind);
162     for (const auto &op_idx : op_seq.operations())
163     {
164       for (const auto &ind : graph.operations().at(op_idx).getOutputs() | ir::Remove::DUPLICATED |
165                                  ir::Remove::UNDEFINED)
166       {
167         assert(def_map.find(ind) != def_map.end());
168         if (def_map[ind])
169         {
170           def_map[ind] = 0;
171           tensor_builder_map[ind]->notifyFirstUse(ind);
172         }
173       }
174
175       // Scan variable tensors
176       // This tensor has features like constant. But OperandInfo and LowerInfo treat them as
177       // non-constant because of less memory usage by memory planning in here
178       for (const auto &ind : graph.operations().at(op_idx).getInputs() | ir::Remove::DUPLICATED |
179                                  ir::Remove::UNDEFINED)
180       {
181         const auto &operand = graph.operands().at(ind);
182         if (operand.info().isVariable())
183         {
184           // The variable tensor with buffer is not supported yet
185           assert(operand.data() == nullptr);
186           assert(operand.getUses().size() == 1 && !operand.getDef().valid());
187           assert(lowered_graph.getLowerInfo(ind)->def_factors().size() == 1 &&
188                  lowered_graph.getLowerInfo(ind)->use_factors().size() == 1);
189           assert(uses_map[ind] == 1 && def_map[ind] == 0);
190           tensor_builder_map[ind]->notifyFirstUse(ind);
191         }
192       }
193
194       for (const auto &ind : graph.operations().at(op_idx).getInputs() | ir::Remove::DUPLICATED |
195                                  ir::Remove::UNDEFINED)
196       {
197         assert(uses_map.find(ind) != uses_map.end());
198         assert(uses_map[ind] > 0);
199         uses_map[ind]--;
200         if (uses_map[ind] == 0)
201         {
202           // plan for deallocation of static tensornode
203           tensor_builder_map[ind]->notifyLastUse(ind);
204
205           // plan for deallocation of dynamic tensor
206           auto dyn_tensor_manager = tensor_builder_map[ind]->dynamicTensorManager();
207           if (dyn_tensor_manager)
208           {
209             const auto *backend =
210                 lowered_graph.getLowerInfo(ind)->def_factors().getOnlyElement().backend();
211             auto &tensor_registry = lowered_graph.backend_contexts().at(backend)->tensor_registry;
212             auto *tensor = tensor_registry->getITensor(ind);
213             assert(tensor);
214             if (!io_tensors.contains(ind)) // I/O tensors cannot be deallocated
215               dyn_tensor_manager->planDealloc(op_idx, tensor);
216           }
217         }
218       }
219     }
220   }
221
222   // Dispose and validate
223   for (const auto &ind : io_tensors)
224   {
225     --uses_map[ind];
226     if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
227     {
228       tensor_builder_map[ind]->notifyLastUse(ind);
229     }
230   }
231
232   for (const auto &ind : constants)
233   {
234     --uses_map[ind];
235     if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
236     {
237       tensor_builder_map[ind]->notifyLastUse(ind);
238     }
239   }
240
241   assert(
242       std::all_of(uses_map.begin(), uses_map.end(),
243                   [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
244
245   assert(
246       std::all_of(def_map.begin(), def_map.end(),
247                   [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
248 }
249
250 } // namespace compiler
251 } // namespace onert