summaryrefslogtreecommitdiff
path: root/runtime/neurun/core/src/compiler/Linear.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/neurun/core/src/compiler/Linear.cc')
-rw-r--r--runtime/neurun/core/src/compiler/Linear.cc317
1 files changed, 317 insertions, 0 deletions
diff --git a/runtime/neurun/core/src/compiler/Linear.cc b/runtime/neurun/core/src/compiler/Linear.cc
new file mode 100644
index 000000000..b18dcea02
--- /dev/null
+++ b/runtime/neurun/core/src/compiler/Linear.cc
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include "Linear.h"
+
+#include "backend/IShapeFixer.h"
+#include "backend/IConfig.h"
+#include "backend/IConstantInitializer.h"
+#include "backend/ITensorRegister.h"
+#include "backend/Backend.h"
+#include "compiler/SubTensorInfo.h"
+
+#include "util/logging.h"
+
+namespace neurun
+{
+namespace compiler
+{
+
+Linear::Linear(ir::Graph &graph) : _graph(graph)
+{
+ // Get SubgraphSequence by topological sorting
+ {
+ ir::Subgraphs &subgraphs = _graph.subgraphs();
+ ir::Operands &operands = _graph.operands();
+ // subgraphs can't access a op_seq by an operand so that input_to_subgs can offer it
+ std::unordered_map<ir::OperandIndex, std::list<ir::SubgraphIndex>> input_to_subgs;
+
+ // Get the relations between input/op_seq to be used for dfs-post-iter
+ //
+ // [0] # input -> _input_to_op_seqes[0] = {SUBG0}
+ // |
+ // [SUBG0]
+ // |
+ // [1]-----. # input -> _input_to_op_seqes[1] = {SUBG1, SUBG2}
+ // | |
+ // [SUBG1] [SUBG2]
+ // | |
+ // [2] [3] # input -> _input_to_op_seqes[2] = {SUBG3}
+ // \ / # input -> _input_to_op_seqes[3] = {SUBG3}
+ // [SUBG3]
+ // |
+ // [4]
+ subgraphs.iterate([&](const ir::SubgraphIndex &subg_idx, ir::OpSequence &subg) {
+ for (auto input : subg.getInputs())
+ {
+ // only valid_inputs
+ const auto &operand = operands.at(input);
+ if (operand.isConstant())
+ continue;
+
+ auto it = input_to_subgs.find(input);
+ if (it == input_to_subgs.end())
+ {
+ std::list<ir::SubgraphIndex> list{subg_idx};
+ input_to_subgs[input] = list;
+ }
+ else
+ {
+ it->second.push_back(subg_idx);
+ }
+ }
+ });
+
+ std::unordered_map<ir::SubgraphIndex, bool> visited;
+ subgraphs.iterate(
+ [&](const ir::SubgraphIndex &index, const ir::OpSequence &) { visited[index] = false; });
+
+ std::function<void(const ir::SubgraphIndex &, ir::OpSequence &)> dfs_recursive =
+ [&](const ir::SubgraphIndex &index, ir::OpSequence &subg) -> void {
+ if (visited[index])
+ return;
+ visited[index] = true;
+
+ // The outputs should be not constants
+ for (auto output : subg.getOutputs())
+ {
+ const auto it = input_to_subgs.find(output);
+ if (it != input_to_subgs.end())
+ {
+ const auto &subg_index_list = it->second;
+ for (const auto &index : subg_index_list)
+ {
+ auto &subg = subgraphs.at(index);
+ dfs_recursive(index, subg);
+ }
+ }
+ }
+
+ _elements.emplace_back(&subgraphs.at(index), _graph.getLowerInfo(index));
+ };
+
+ subgraphs.iterate(dfs_recursive);
+
+ // All of the nodes must have been visited.
+ assert(std::all_of(visited.begin(), visited.end(),
+ [](const std::pair<const ir::SubgraphIndex, bool> &v) { return v.second; }));
+
+ // NOTE. Now these op_seq are on the reverse order
+ std::reverse(_elements.begin(), _elements.end());
+ }
+
+ {
+ const auto &backendToString = [](const neurun::backend::Backend *backend) {
+ assert(backend);
+ std::string str;
+ str += backend->config()->id();
+ str += " ";
+ return "{ " + str + "}";
+ };
+
+ VERBOSE(Linear) << "Final SubgraphSequence" << std::endl;
+ for (const auto &element : _elements)
+ {
+ const auto subg = element.op_seq;
+ const auto lower_info = element.lower_info;
+ VERBOSE(Linear) << "* SUBG"
+ << " " << backendToString(lower_info->backend()) << " " << subg->getStr()
+ << std::endl;
+ }
+ }
+}
+
+void Linear::accept(ir::OperationVisitor &&visitor) const
+{
+ for (const auto &e : _elements)
+ {
+ e.op_seq->accept(visitor);
+ }
+}
+
+void Linear::planTensors()
+{
+ ir::OperandIndexMap<std::shared_ptr<backend::ITensorBuilder>> tensor_builder_map;
+
+ // NOTE
+ // While current ITensorBuilder exposes registerSubTensorInfo for subtensor,
+ // this stage uses registerSubTensorInfo() and notify{First|Last}Use()
+ // but handling subtensor should be processed on each backend. See #5726.
+ ir::OperandIndexMap<uint32_t> uses_map;
+ ir::OperandIndexMap<uint32_t> def_map;
+ ir::OperandIndexSequence constants;
+
+ iterate([&](const neurun::compiler::Linear::Element &element) {
+ const auto backend = element.lower_info->backend();
+ const auto tensor_register =
+ _graph.backend_resolver()->getBackendContext(backend)->tensor_register;
+ tensor_register->registerTensors(*element.op_seq, _graph.getLowerInfo());
+ });
+
+ // Prepare scanning
+ _graph.operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) {
+ const auto lower_info = _graph.getLowerInfo(ind);
+ // TODO Remove if neurun doesn't support anymore such as
+ // GeneratedTests.reshape_quant8_weights_as_inputs
+ if (lower_info->def_factors().size() == 0 && lower_info->use_factors().size() == 0 &&
+ !_graph.getInputs().contains(ind))
+ {
+ VERBOSE(LINEAR) << "Operand #" << ind.value() << " will be not used. no more process."
+ << std::endl;
+ return;
+ }
+
+ uses_map[ind] = obj.getUses().size();
+ def_map[ind] = obj.getDef().size(); // should be 1 or 0
+
+ bool is_const = obj.isConstant();
+ if (is_const)
+ {
+ constants.append(ind);
+ }
+
+ for (auto factor : lower_info->def_factors())
+ {
+ auto backend = factor.backend();
+ auto tensor_builder = _graph.backend_resolver()->getBackendContext(backend)->tensor_builder;
+
+ if (!tensor_builder->isRegistered(ind))
+ {
+ // These tensors do not exist in any op_seq (No use and def)
+ // These tensors cannot be a SubTensor
+ assert(obj.parent_info() == nullptr);
+
+ const auto info = obj.info();
+ const auto backend_layout = lower_info->def_factors().getOnlyElement().layout();
+ // TODO Change tensor info to have permuted shape
+ tensor_builder->registerTensorInfo(ind, info, backend_layout, is_const);
+ }
+
+ tensor_builder_map[ind] = tensor_builder;
+ }
+ });
+
+ // If a tensor is model output, increase the use of the tensor.
+ // This aim is same to above one.
+ for (const auto &ind : _graph.getOutputs())
+ {
+ uses_map[ind]++;
+ }
+
+ // Start scanning to do notify{First|Last}Use for each tensor
+
+ // If a tensor is a constant, increase the use of the tensor.
+ // It makes the tensor not be dealloced. It means these will be deallocated last.
+ // And allocate constant operands first
+ VERBOSE(LINEAR) << "TENSORS as CONSTANT" << std::endl;
+ for (const auto &ind : constants)
+ {
+ uses_map[ind]++;
+ tensor_builder_map[ind]->notifyFirstUse(ind);
+ }
+
+ // Allocate Model's inputs
+ VERBOSE(LINEAR) << "TENSORS as MODEL INPUT" << std::endl;
+ for (const auto &ind : _graph.getInputs())
+ {
+ auto tensor_builder = tensor_builder_map[ind];
+ if (!tensor_builder) // for GeneratedTests.xxx_weights_as_inputs
+ continue;
+ tensor_builder->notifyFirstUse(ind);
+ }
+
+ // At each operation,
+ // 1. Scan DEF of outputs. If the DEF, allocate it
+ // 2. Scan USE of inputs. Decrease the USE and deallocate if the USE is 0
+ VERBOSE(LINEAR) << "TENSORS" << std::endl;
+ for (const auto &e : _elements)
+ {
+ for (const auto &op : e.op_seq->operations())
+ {
+ for (const auto &ind : op.node->getOutputs())
+ {
+ assert(def_map.find(ind) != def_map.end());
+ if (def_map[ind])
+ {
+ def_map[ind] = 0;
+ tensor_builder_map[ind]->notifyFirstUse(ind);
+ }
+ }
+
+ for (const auto &ind : op.node->getInputs())
+ {
+ assert(uses_map.find(ind) != uses_map.end());
+ assert(uses_map[ind] > 0);
+ uses_map[ind]--;
+ if (uses_map[ind] == 0)
+ {
+ tensor_builder_map[ind]->notifyLastUse(ind);
+ }
+ }
+ }
+ }
+
+ // Dispose and validate
+ for (const auto &ind : _graph.getOutputs())
+ {
+ --uses_map[ind];
+ if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
+ {
+ tensor_builder_map[ind]->notifyLastUse(ind);
+ }
+ }
+
+ for (const auto &ind : constants)
+ {
+ --uses_map[ind];
+ if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice
+ {
+ tensor_builder_map[ind]->notifyLastUse(ind);
+ }
+ }
+
+ assert(
+ std::all_of(uses_map.begin(), uses_map.end(),
+ [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
+
+ assert(
+ std::all_of(def_map.begin(), def_map.end(),
+ [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; }));
+}
+
+void Linear::iterate(const std::function<void(const Element &element)> &fn) const
+{
+ for (const auto &e : _elements)
+ {
+ fn(e);
+ }
+}
+
+void Linear::generateConstantInitializers(void) const
+{
+ iterate([&](const compiler::Linear::Element &element) {
+ auto backend = element.lower_info->backend();
+
+ auto constant_initializer =
+ _graph.backend_resolver()->getBackendContext(backend)->constant_initializer;
+ constant_initializer->generate(*element.op_seq, _graph.operands());
+ });
+}
+
+} // namespace compiler
+} // namespace neurun