2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
32 std::vector<ir::OpSequenceIndex> Linear::linearize(const compiler::LoweredGraph &lowered_graph)
34 std::vector<ir::OpSequenceIndex> order;
35 lowered_graph.iterateTopolOpSeqs(
36 [&](const ir::OpSequenceIndex &index, const ir::OpSequence &) -> void {
37 order.emplace_back(index);
42 void Linear::dump(const compiler::LoweredGraph &lowered_graph,
43 const std::vector<ir::OpSequenceIndex> &order)
46 const auto &toString = [](const onert::backend::Backend *backend) {
49 str += backend->config()->id();
50 return "{" + str + "}";
53 VERBOSE(Linear) << "Final OpSequence" << std::endl;
54 for (const auto index : order)
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;
65 void Linear::planTensors(const compiler::LoweredGraph &lowered_graph,
66 const std::vector<ir::OpSequenceIndex> &order)
68 const auto &graph = lowered_graph.graph();
69 ir::OperandIndexMap<std::shared_ptr<backend::ITensorBuilder>> tensor_builder_map;
71 ir::OperandIndexMap<uint32_t> uses_map;
72 ir::OperandIndexMap<uint32_t> def_map;
73 ir::OperandIndexSequence constants;
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))
83 VERBOSE(LINEAR) << "Operand #" << ind.value() << " will not be used. no more process."
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))
93 VERBOSE(LINEAR) << "Operand #" << ind.value() << " will not be used. no more process."
98 uses_map[ind] = obj.getUses().size();
99 def_map[ind] = obj.getDef().valid() ? 1 : 0;
101 bool is_const = obj.isConstant();
104 constants.append(ind);
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))
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);
119 tensor_builder_map[ind] = tensor_builder;
122 const auto io_tensors =
123 (graph.getInputs() + graph.getOutputs()) | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED;
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)
132 // Start scanning to do notify{First|Last}Use for each tensor
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)
141 tensor_builder_map[ind]->notifyFirstUse(ind);
144 // Allocate Model's inputs
145 VERBOSE(LINEAR) << "TENSORS as MODEL INPUT" << std::endl;
146 for (const auto &ind : graph.getInputs() | ir::Remove::DUPLICATED)
148 auto tensor_builder = tensor_builder_map[ind];
149 if (!tensor_builder) // for GeneratedTests.xxx_weights_as_inputs
151 tensor_builder->notifyFirstUse(ind);
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)
161 const auto &op_seq = lowered_graph.op_seqs().at(op_seq_ind);
162 for (const auto &op_idx : op_seq.operations())
164 for (const auto &ind : graph.operations().at(op_idx).getOutputs() | ir::Remove::DUPLICATED |
165 ir::Remove::UNDEFINED)
167 assert(def_map.find(ind) != def_map.end());
171 tensor_builder_map[ind]->notifyFirstUse(ind);
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)
181 const auto &operand = graph.operands().at(ind);
182 if (operand.info().isVariable())
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);
194 for (const auto &ind : graph.operations().at(op_idx).getInputs() | ir::Remove::DUPLICATED |
195 ir::Remove::UNDEFINED)
197 assert(uses_map.find(ind) != uses_map.end());
198 assert(uses_map[ind] > 0);
200 if (uses_map[ind] == 0)
202 // plan for deallocation of static tensornode
203 tensor_builder_map[ind]->notifyLastUse(ind);
205 // plan for deallocation of dynamic tensor
206 auto dyn_tensor_manager = tensor_builder_map[ind]->dynamicTensorManager();
207 if (dyn_tensor_manager)
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);
214 if (!io_tensors.contains(ind)) // I/O tensors cannot be deallocated
215 dyn_tensor_manager->planDealloc(op_idx, tensor);
222 // Dispose and validate
223 for (const auto &ind : io_tensors)
226 if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
228 tensor_builder_map[ind]->notifyLastUse(ind);
232 for (const auto &ind : constants)
235 if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
237 tensor_builder_map[ind]->notifyLastUse(ind);
242 std::all_of(uses_map.begin(), uses_map.end(),
243 [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
246 std::all_of(def_map.begin(), def_map.end(),
247 [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
250 } // namespace compiler