diff options
Diffstat (limited to 'runtime/onert/core/src')
199 files changed, 17908 insertions, 0 deletions
diff --git a/runtime/onert/core/src/backend/BackendContext.cc b/runtime/onert/core/src/backend/BackendContext.cc new file mode 100644 index 000000000..f2173de44 --- /dev/null +++ b/runtime/onert/core/src/backend/BackendContext.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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 "backend/BackendContext.h" + +#include "ir/Operation.h" +#include "backend/IShapeFixer.h" +#include "backend/IConstantInitializer.h" + +namespace onert +{ +namespace backend +{ + +void BackendContext::initialize(const std::vector<OperationInfo> &operation_list, + const std::vector<ir::OperandIndex> &operand_list) +{ + _operation_list = operation_list; + _operand_list = operand_list; +} + +void BackendContext::fixShapes() +{ + for (auto &op : _operation_list) + { + _graph->operations().at(op.index).accept(*shape_fixer); + } +} + +void BackendContext::initConsts() +{ + for (auto &op : _operation_list) + { + constant_initializer->setLayout(op.layout); + _graph->operations().at(op.index).accept(*constant_initializer); + } + + for (auto ind : _operand_list) + { + const auto &obj = _graph->operands().at(ind); + if (obj.isConstant() && !constant_initializer->exist(ind)) + { + constant_initializer->registerPermuteInitializer(ind, obj); + } + } + + constant_initializer->run(); +} + +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/compiler/BackendManager.cc b/runtime/onert/core/src/compiler/BackendManager.cc new file mode 100644 index 000000000..bfed7ff2b --- /dev/null +++ b/runtime/onert/core/src/compiler/BackendManager.cc @@ -0,0 +1,140 @@ +/* + * 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 "compiler/BackendManager.h" + +#include <memory> +#include <dlfcn.h> + +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "util/logging.h" +#include "util/ConfigSource.h" +#include "misc/string_helpers.h" + +namespace onert +{ +namespace compiler +{ + +BackendManager &BackendManager::get() +{ + static BackendManager object; + return object; +} + +template <typename T, class... Types> +void BackendManager::loadObjectFromPlugin(std::shared_ptr<T> &object_of_plugin_class, + const std::string obj_creator_func_name, void *handle, + Types &&... args) +{ + T *(*allocate_obj)(Types && ... Args); + // load object creator function + allocate_obj = (T * (*)(Types && ... Args))dlsym(handle, obj_creator_func_name.c_str()); + if (allocate_obj == nullptr) + { + fprintf(stderr, "BackendManager: unable to open function %s: %s\n", + obj_creator_func_name.c_str(), dlerror()); + abort(); + } + + object_of_plugin_class.reset(allocate_obj(args...)); +} + +void BackendManager::loadBackend(const std::string &backend) +{ + if (get(backend) != nullptr) + { + return; + } + + const std::string backend_plugin = "libbackend_" + backend + ".so"; + void *handle = dlopen(backend_plugin.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (handle == nullptr) + { + VERBOSE(BackendManager::loadBackend) << "loadBackend failed to load plugin of " + << backend.c_str() << " backend: " << dlerror() + << std::endl; + return; + } + + VERBOSE(BackendManager::loadBackend) << "loaded " << backend_plugin << " as a plugin of " + << backend << " backend\n"; + + { + // load object creator function + auto backend_create = (backend_create_t)dlsym(handle, "onert_backend_create"); + if (backend_create == nullptr) + { + fprintf(stderr, "BackendManager: unable to open function onert_backend_create : %s\n", + dlerror()); + abort(); + } + + // load object creator function + auto backend_destroy = (backend_destroy_t)dlsym(handle, "onert_backend_destroy"); + if (backend_destroy == nullptr) + { + fprintf(stderr, "BackendManager: unable to open function onert_backend_destroy : %s\n", + dlerror()); + abort(); + } + + auto backend_object = + std::unique_ptr<backend::Backend, backend_destroy_t>(backend_create(), backend_destroy); + auto backend_object_raw = backend_object.get(); + bool initialized = backend_object->config()->initialize(); // Call initialize here? + if (!initialized) + { + VERBOSE(BackendManager::loadBackend) + << backend.c_str() << " backend initialization failed. Don't use this backend" + << std::endl; + dlclose(handle); + return; + } + _gen_map.emplace(backend_object->config()->id(), std::move(backend_object)); + _available_backends.push_back(backend_object_raw); + } + + // Save backend handle (avoid warning by handle lost without dlclose()) + auto u_handle = std::unique_ptr<void, dlhandle_destroy_t>{handle, [](void *h) { dlclose(h); }}; + _handle_map.emplace(backend, std::move(u_handle)); +} + +backend::Backend *BackendManager::get(const std::string &key) +{ + if (_gen_map.find(key) != _gen_map.end()) + { + return _gen_map.at(key).get(); + } + + return nullptr; +} + +const backend::Backend *BackendManager::get(const std::string &key) const +{ + if (_gen_map.find(key) != _gen_map.end()) + { + return _gen_map.at(key).get(); + } + + return nullptr; +} + +const backend::Backend *BackendManager::getDefault() const { return get("cpu"); } + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/BackendResolver.cc b/runtime/onert/core/src/compiler/BackendResolver.cc new file mode 100644 index 000000000..a47d8d2d5 --- /dev/null +++ b/runtime/onert/core/src/compiler/BackendResolver.cc @@ -0,0 +1,25 @@ +/* + * 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 "compiler/BackendResolver.h" + +namespace onert +{ +namespace compiler +{ + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/CachedDataDeleter.h b/runtime/onert/core/src/compiler/CachedDataDeleter.h new file mode 100644 index 000000000..73f00ced7 --- /dev/null +++ b/runtime/onert/core/src/compiler/CachedDataDeleter.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef __ONERT_COMPILER_CACHED_DATA_DELETER_H__ +#define __ONERT_COMPILER_CACHED_DATA_DELETER_H__ + +#include "ir/Index.h" +#include "ir/OperationVisitor.h" +#include "ir/OpSequences.h" +#include "ir/Operands.h" +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ + +class CachedDataDeleter : public ir::OperationVisitor +{ +public: + CachedDataDeleter(ir::Operands &operands) : _operands(operands) + { + // DO NOTHING + } + + virtual ~CachedDataDeleter() = default; + +public: + void run() + { + _operands.iterate( + [&](const ir::OperandIndex &ind, const ir::Operand &) { deleteCachedData(ind); }); + } + + void run(const ir::OpSequence &op_seq) + { + for (const auto &e : op_seq.operations()) + { + const auto &node = *(e.node); + node.accept(*this); + } + } + + // NOTE: Almost layers that have the big size constants are conv and fc. + void visit(const ir::operation::Conv2D &node) override + { + using ir::operation::Conv2D; + const auto ker_index{node.getInputs().at(Conv2D::Input::KERNEL)}; + const auto bias_index{node.getInputs().at(Conv2D::Input::BIAS)}; + deleteCachedData(ker_index); + deleteCachedData(bias_index); + } + + void visit(const ir::operation::DepthwiseConv2D &node) override + { + using ir::operation::DepthwiseConv2D; + const auto ker_index{node.getInputs().at(DepthwiseConv2D::Input::KERNEL)}; + const auto bias_index{node.getInputs().at(DepthwiseConv2D::Input::BIAS)}; + deleteCachedData(ker_index); + deleteCachedData(bias_index); + } + + void visit(const ir::operation::FullyConnected &node) override + { + using ir::operation::FullyConnected; + const auto weight_index{node.getInputs().at(FullyConnected::Input::WEIGHT)}; + const auto bias_index{node.getInputs().at(FullyConnected::Input::BIAS)}; + deleteCachedData(weight_index); + deleteCachedData(bias_index); + } + +private: + void deleteCachedData(const ir::OperandIndex &ind) + { + auto &obj = _operands.at(ind); + if (obj.isConstant()) + { + assert(obj.data() != nullptr); + obj.releaseData(); + } + } + +private: + ir::Operands &_operands; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_CACHED_DATA_DELETER_H__ diff --git a/runtime/onert/core/src/compiler/Compiler.cc b/runtime/onert/core/src/compiler/Compiler.cc new file mode 100644 index 000000000..85af843ae --- /dev/null +++ b/runtime/onert/core/src/compiler/Compiler.cc @@ -0,0 +1,209 @@ +/* + * 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 "compiler/Compiler.h" + +#include "ParamChecker.h" +#include "ExecutorFactory.h" +#include "OperationValidator.h" + +#include "compiler/BackendManager.h" +#include "compiler/IScheduler.h" +#include "compiler/ManualScheduler.h" +#include "compiler/HEScheduler.h" +#include "exec/ExecTime.h" +#include "ir/operation/LowerInfo.h" +#include "dumper/dot/DotDumper.h" +#include "compiler/Linear.h" +#include "interp/InterpExecutor.h" +#include "util/ConfigSource.h" +#include "ir/OperationDumper.h" +#include "compiler/CachedDataDeleter.h" +#include "misc/string_helpers.h" + +namespace onert +{ + +namespace compiler +{ + +CompilerOptions fetchCompilerOptionsFromGlobalConfig(const ir::Graph &graph) +{ + CompilerOptions options; + + options.backend_list = nnfw::misc::split(util::getConfigString(util::config::BACKENDS), ';'); + + options.trace_filepath = util::getConfigString(util::config::TRACE_FILEPATH); + options.graph_dump_level = util::getConfigInt(util::config::GRAPH_DOT_DUMP); + options.op_seq_max_node = util::getConfigInt(util::config::OP_SEQ_MAX_NODE); + options.executor = util::getConfigString(util::config::EXECUTOR); + options.he_scheduler = util::getConfigBool(util::config::USE_SCHEDULER); + options.he_profiling_mode = util::getConfigBool(util::config::PROFILING_MODE); + options.delete_cached_data = util::getConfigBool(util::config::DELETE_CACHED_DATA); + options.disable_compile = util::getConfigBool(util::config::DISABLE_COMPILE); + + { + // Backend for all + auto &ms_options = options.manual_scheduler_options; + + // Default value for op_backend_all is first element in the backend list + ms_options.backend_for_all = util::getConfigString(util::config::OP_BACKEND_ALLOPS); + +// Opcode to Backend +#define OP(OpName) \ + { \ + const auto &backend_str = util::getConfigString(util::config::OP_BACKEND_##OpName); \ + if (!backend_str.empty()) \ + { \ + ms_options.opcode_to_backend[ir::OpCode::OpName] = backend_str; \ + } \ + } +#include "ir/Operations.lst" +#undef OP + + // Index to Backend + auto map_str = util::getConfigString(util::config::OP_BACKEND_MAP); + auto key_val_list = nnfw::misc::split(map_str, ';'); + for (const auto &key_val_str : key_val_list) + { + if (key_val_str.empty()) + { + continue; + } + + auto key_val = nnfw::misc::split(key_val_str, '='); + const auto &key_str = key_val.at(0); + const auto &val = key_val.at(1); + auto key = static_cast<uint32_t>(std::stoi(key_str)); + + graph.operations().at(ir::OperationIndex{key}); // Check if exist, or this wil throw + ms_options.index_to_backend.emplace(ir::OperationIndex{key}, val); + } + } + return options; +} + +Compiler::Compiler(const std::shared_ptr<ir::Graph> &graph) + : _graph{graph}, _executor{nullptr}, _state{State::CREATED} +{ + + // Set default values for CompilerOptions + // All these default values should not be fetched from Env, when we stop supporting Android NN + // API. + _options = fetchCompilerOptionsFromGlobalConfig(*_graph); +} + +void Compiler::checkProfilerConditions() +{ + if (!_options.he_scheduler) + throw std::runtime_error("Heterogeneous scheduler must be enabled during profiling."); + + if (_options.executor != "Dataflow") + throw std::runtime_error("Profiling mode works only with 'Dataflow' executor"); +} + +void Compiler::compile(void) +{ + _state = State::STARTED; + + /*************************************************** + * Prepare compilation phase + ***************************************************/ + + // Operation validation check + OperationValidator{*_graph}(); + + // Compilable check + if (!checkCompilable()) + { + _executor = std::make_shared<interp::InterpExecutor>(*_graph); + return; + } + + // Mode check + if (_options.he_profiling_mode) + checkProfilerConditions(); + + /*************************************************** + * Backend independent analysis & optimization phase + ***************************************************/ + auto dump_level = static_cast<dumper::dot::DotDumper::Level>(_options.graph_dump_level); + + onert::dumper::dot::DotDumper dot_dumper(*_graph, dump_level); + dot_dumper.dump("before_lower"); + + // Lower: Assign backend + auto lowered_graph = std::make_unique<ir::LoweredGraph>(*_graph, _options); + + // NOTE. Current datas' reference of constant operands is 2 because of + // original graph and lowered graph. + // To delete cached data, this doing should be done for the original graph + // at this line and then once again for the lowered graph in ExecutorFactory + // TODO. Delete this code as code for disconnecting btw Graph and nnfw session lands + if (_options.delete_cached_data) + { + CachedDataDeleter(_graph->operands()).run(); + } + + auto indexed_ranks = lowered_graph->indexed_ranks(); + + /************************************************************* + * Backend independent analysis & optimization phase finished + *************************************************************/ + + _state = State::LOWERED; + + onert::dumper::dot::DotDumper dot_dumper_lowered(lowered_graph.get(), dump_level); + dot_dumper_lowered.dump("after_lower"); + + ir::OperationDumper dumper; + _graph->operations().iterate( + [&](const ir::OperationIndex &, const ir::Operation &op) { op.accept(dumper); }); + + _executor = std::shared_ptr<exec::IExecutor>{ + ExecutorFactory::get().create(std::move(lowered_graph), _options)}; + _executor->setIndexedRanks(indexed_ranks); + /******************************** + * Code generation phase finished + ********************************/ + _state = State::COMPILED; +} + +bool Compiler::checkCompilable() +{ + // Disable compile phase + // When ready to use interpreter backend, remove this config and use backend setting + if (_options.disable_compile) + { + return false; + } + + // TODO check unspecified operand shape + + // Check compilable parameter + ParamChecker paramChecker{_graph}; + paramChecker(); + if (paramChecker.haveNoneConstParam()) + { + return false; + } + + return true; +} + +} // namespace compiler + +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ExecutorFactory.cc b/runtime/onert/core/src/compiler/ExecutorFactory.cc new file mode 100644 index 000000000..8d06d6bbe --- /dev/null +++ b/runtime/onert/core/src/compiler/ExecutorFactory.cc @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2019 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 "ExecutorFactory.h" + +#include <functional> +#include "exec/ExecutionObservers.h" +#include "exec/LinearExecutor.h" +#include "exec/DataflowExecutor.h" +#include "exec/ParallelExecutor.h" +#include "compiler/BackendManager.h" +#include "compiler/ExecutionBuilder.h" +#include "exec/ExecTime.h" +#include "compiler/Linear.h" +#include "backend/IConstantInitializer.h" +#include "backend/IKernelGenerator.h" +#include "backend/IShapeFixer.h" +#include "backend/IOptimizer.h" +#include "backend/ITensorRegister.h" +#include <memory> +#include "compiler/CachedDataDeleter.h" +#include "util/ShapeInference.h" + +namespace onert +{ +namespace compiler +{ + +ExecutorFactory &ExecutorFactory::get() +{ + static ExecutorFactory singleton; + return singleton; +} + +ExecutorFactory::ExecutorFactory() +{ + _map["Linear"] = createLinearExecutor; + _map["Dataflow"] = + std::bind(createDataflowExecutor, std::placeholders::_1, std::placeholders::_2, false); + _map["Parallel"] = + std::bind(createDataflowExecutor, std::placeholders::_1, std::placeholders::_2, true); +} + +exec::IExecutor *ExecutorFactory::create(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options) +{ + return _map.at(options.executor)(std::move(lowered_graph), options); +} + +void ExecutorFactory::initializeBackendContext(ir::LoweredGraph *lowered_graph) +{ + struct Entry + { + std::vector<backend::BackendContext::OperationInfo> operation_list; + std::vector<ir::OperandIndex> operand_list; + }; + std::unordered_map<const backend::Backend *, Entry> backend_assets; + + // Build lists for operations + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &op_seq_index, const ir::OpSequence &op_seq) { + auto &op_seq_li = lowered_graph->getLowerInfo()->operation; + auto backend = op_seq_li.at(op_seq_index)->backend(); + for (auto &element : op_seq.operations()) + { + backend_assets[backend].operation_list.emplace_back(element.index, op_seq.getLayout()); + } + }); + + // Build lists for operands + lowered_graph->graph().operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &) { + const auto lower_info = lowered_graph->getLowerInfo(ind); + for (auto factor : lower_info->def_factors()) + { + auto backend = factor.backend(); + backend_assets[backend].operand_list.emplace_back(ind); + } + }); + + for (auto &pair : backend_assets) + { + auto backend = pair.first; + auto &arg = pair.second; + lowered_graph->backend_contexts().at(backend)->initialize(arg.operation_list, arg.operand_list); + } +} + +void ExecutorFactory::runTensorRegistration(ir::LoweredGraph *lowered_graph, + const std::vector<ir::OpSequenceIndex> &order) +{ + for (const auto index : order) + { + const auto &op_seq = lowered_graph->op_seqs().at(index); + const auto backend = lowered_graph->getLowerInfo(index)->backend(); + const auto tensor_register = lowered_graph->backend_contexts().at(backend)->tensor_register; + auto tensor_builder = lowered_graph->backend_contexts().at(backend)->tensor_builder; + if (tensor_register) + { + // Custom registration + tensor_register->registerTensors(op_seq, lowered_graph->getLowerInfo()); + } + else + { + // Default registration + for (const auto elem : op_seq) + { + const auto &op = *elem.node; + for (const auto &index : op.getInputs() + op.getOutputs()) + { + if (!tensor_builder->isRegistered(index)) + { + const auto &operand_lower_info = + lowered_graph->getLowerInfo(index)->def_factors().getOnlyElement(); + const auto &obj = lowered_graph->graph().operands().at(index); + const auto frontend_layout = op_seq.getLayout(); + const auto backend_layout = operand_lower_info.layout(); + ir::OperandInfo backend_info{permuteShape(obj.shape(), frontend_layout, backend_layout), + obj.typeInfo()}; + tensor_builder->registerTensorInfo(index, backend_info, backend_layout, + obj.isConstant()); + } + } + } + } + } +} + +exec::IExecutor * +ExecutorFactory::createLinearExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options) +{ + const auto &backend_contexts = lowered_graph->backend_contexts(); + + initializeBackendContext(lowered_graph.get()); + + // linearize + assert(!lowered_graph->graph().isBuildingPhase()); + + // Shape inference. + { + shape_inference::StaticInferer inferer(lowered_graph->graph().operands()); + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &, const ir::OpSequence &op_seq) { inferer.infer(op_seq); }); + } + + for (auto &pair : backend_contexts) + { + pair.second->fixShapes(); + } + + /************************************************* + * Backend dependent analysis & optimization phase + *************************************************/ + + for (auto &pair : backend_contexts) + { + auto &optimizer = pair.second->optimizer; + if (optimizer) + optimizer->optimize(); + } + + /********************************************************** + * Backend dependent analysis & optimization phase finished + **********************************************************/ + + /*********************** + * Code generation phase + ***********************/ + + auto order = Linear::linearize(*lowered_graph); + runTensorRegistration(lowered_graph.get(), order); + Linear::dump(*lowered_graph, order); + Linear::planTensors(*lowered_graph, order); + + backend::TensorBuilderSet tensor_builders; + for (const auto &e : lowered_graph->backend_contexts()) + { + tensor_builders.insert(e.second->tensor_builder); + } + + for (auto &tensor_builder : tensor_builders) + { + tensor_builder->prepare(); + } + + ExecutionBuilder builder; + + // Generate kernels + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &op_seq_index, const ir::OpSequence &op_seq) { + auto lower_info = lowered_graph->getLowerInfo(op_seq_index); + auto kernel_gen = lowered_graph->backend_contexts().at(lower_info->backend())->kernel_gen; + auto fn_seq = kernel_gen->generate(op_seq); + builder.append(op_seq_index, {&op_seq, lower_info, std::move(fn_seq)}); + }); + + for (auto &tensor_builder : tensor_builders) + { + tensor_builder->allocate(); + } + + for (auto &pair : backend_contexts) + { + pair.second->initConsts(); + } + + // Note. The best solution is not to use CachedDataDeleter but decreasing reference counts of data + // naturally + if (options.delete_cached_data) + { + CachedDataDeleter cached_data_deleter(lowered_graph->graph().operands()); + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &, const ir::OpSequence &op_seq) { + cached_data_deleter.run(op_seq); + }); + } + + auto code_map = builder.releaseCodeMap(); + + for (auto &it : code_map) + { + auto op_seq_index = it.first; + auto &fn_seq = it.second.fn_seq; + + fn_seq->iterate([&](exec::IFunction &ifunc) { + ifunc.prepare(); + auto backend = lowered_graph->getLowerInfo(op_seq_index)->backend(); + auto tensor_builder = lowered_graph->backend_contexts().at(backend)->tensor_builder; + tensor_builder->postFunctionPrepare(); + }); + } + + auto exec = new exec::LinearExecutor{std::move(lowered_graph), tensor_builders, + std::move(code_map), order}; + + if (!options.trace_filepath.empty()) + { + std::unique_ptr<exec::IExecutionObserver> ctp = + std::make_unique<exec::ChromeTracingObserver>(options.trace_filepath); + exec->addObserver(std::move(ctp)); + } + + return exec; +} + +exec::IExecutor * +ExecutorFactory::createDataflowExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, bool parallel) +{ + const auto &backend_contexts = lowered_graph->backend_contexts(); + + initializeBackendContext(lowered_graph.get()); + + for (auto &pair : backend_contexts) + { + pair.second->fixShapes(); + } + + auto order = Linear::linearize(*lowered_graph); + runTensorRegistration(lowered_graph.get(), order); + + backend::TensorBuilderSet tensor_builders; + for (const auto &e : lowered_graph->backend_contexts()) + { + tensor_builders.insert(e.second->tensor_builder); + } + + // To make tensors never be deallocated, this is a workaround to use static memory planner + for (auto &tensor_builder : tensor_builders) + { + lowered_graph->graph().operands().iterate( + [&](const ir::OperandIndex &ind, const ir::Operand &) { + if (tensor_builder->isRegistered(ind)) + { + tensor_builder->notifyFirstUse(ind); + } + }); + } + + for (auto &tensor_builder : tensor_builders) + { + tensor_builder->prepare(); + } + + ExecutionBuilder builder; + + // Generate kernels + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &op_seq_index, const ir::OpSequence &op_seq) { + auto lower_info = lowered_graph->getLowerInfo(op_seq_index); + auto kernel_gen = lowered_graph->backend_contexts().at(lower_info->backend())->kernel_gen; + auto fn_seq = kernel_gen->generate(op_seq); + builder.append(op_seq_index, {&op_seq, lower_info, std::move(fn_seq)}); + }); + + for (const auto &tensor_builder : tensor_builders) + { + tensor_builder->allocate(); + } + + for (auto &pair : backend_contexts) + { + pair.second->initConsts(); + } + + if (options.delete_cached_data) + { + CachedDataDeleter cached_data_deleter(lowered_graph->graph().operands()); + lowered_graph->op_seqs().iterate( + [&](const ir::OpSequenceIndex &, const ir::OpSequence &op_seq) { + cached_data_deleter.run(op_seq); + }); + } + + auto code_map = builder.releaseCodeMap(); + + for (auto &it : code_map) + { + auto op_seq_index = it.first; + auto &fn_seq = it.second.fn_seq; + + fn_seq->iterate([&](exec::IFunction &ifunc) { + ifunc.prepare(); + auto backend = lowered_graph->getLowerInfo(op_seq_index)->backend(); + auto tensor_builder = lowered_graph->backend_contexts().at(backend)->tensor_builder; + tensor_builder->postFunctionPrepare(); + }); + } + + exec::ExecutorBase *exec = nullptr; + if (parallel) + { + exec = + new exec::ParallelExecutor{std::move(lowered_graph), tensor_builders, std::move(code_map)}; + } + else + { + auto dataflow_exec = + new exec::DataflowExecutor{std::move(lowered_graph), tensor_builders, std::move(code_map)}; + if (options.he_profiling_mode) + { + std::vector<const backend::Backend *> backends; + for (const auto &pair : backend_contexts) + { + backends.push_back(pair.first); + } + auto et = std::make_shared<exec::ExecTime>(backends); + std::unique_ptr<exec::IExecutionObserver> obs = std::make_unique<exec::ProfileObserver>(et); + dataflow_exec->addObserver(std::move(obs)); + dataflow_exec->setProfilingMode(true); + } + exec = dataflow_exec; + } + + if (!options.trace_filepath.empty()) + { + std::unique_ptr<exec::IExecutionObserver> ctp = + std::make_unique<exec::ChromeTracingObserver>(options.trace_filepath); + exec->addObserver(std::move(ctp)); + } + + return exec; +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ExecutorFactory.h b/runtime/onert/core/src/compiler/ExecutorFactory.h new file mode 100644 index 000000000..deba18d5e --- /dev/null +++ b/runtime/onert/core/src/compiler/ExecutorFactory.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_COMPILER_EXECUTOR_FACTORY_H__ +#define __ONERT_COMPILER_EXECUTOR_FACTORY_H__ + +#include <unordered_map> + +#include "exec/IExecutor.h" +#include "ir/LoweredGraph.h" + +namespace onert +{ +namespace compiler +{ + +class ExecutorFactory +{ +public: + static ExecutorFactory &get(); + +public: + exec::IExecutor *create(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options); + +private: + ExecutorFactory(); + +private: + static void initializeBackendContext(ir::LoweredGraph *lowered_graph); + static void runTensorRegistration(ir::LoweredGraph *lowered_graph, + const std::vector<ir::OpSequenceIndex> &order); + static exec::IExecutor *createLinearExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options); + static exec::IExecutor *createDataflowExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + bool parallel); + +private: + std::unordered_map<std::string, + std::function<exec::IExecutor *(std::unique_ptr<ir::LoweredGraph>, + const compiler::CompilerOptions &options)>> + _map; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_EXECUTOR_FACTORY_H__ diff --git a/runtime/onert/core/src/compiler/HEScheduler.cc b/runtime/onert/core/src/compiler/HEScheduler.cc new file mode 100644 index 000000000..f9a264908 --- /dev/null +++ b/runtime/onert/core/src/compiler/HEScheduler.cc @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2019 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 "ir/Operand.h" +#include "compiler/HEScheduler.h" +#include "ir/Graph.h" +#include "util/ConfigSource.h" +#include "compiler/BackendResolver.h" +#include "backend/IShapeFixer.h" +#include "util/logging.h" +#include "util/Utils.h" +#include "exec/FunctionSequence.h" +#include <cassert> +#include <cmath> +#include <chrono> + +namespace onert +{ + +namespace compiler +{ +static uint32_t getOperationsFlattenedIOSize(const ir::Graph &graph, const ir::Operation &node) +{ + uint32_t size = 0; + for (const auto &ind : node.getInputs() + node.getOutputs()) + { + size += graph.operands().at(ind).info().total_size(); + } + return size; +} + +static bool isQuant(const ir::Graph &graph, const ir::Operation &node) +{ + for (const auto &input : node.getInputs()) + { + const auto &obj = graph.operands().at(input); + if (obj.typeInfo().type() == ir::DataType::QUANT8_ASYMM) + { + return true; + } + } + return false; +} + +static bool isWorkaroundSkip(const ir::Graph &graph, const backend::Backend *backend, + const ir::Operation &node, bool quant) +{ + /* TODO: this is workaround, come up with better solution if have. + Adding exception in stage doesn't help. Because if there is a record for add without + broadcast, scheduling will select it since it doesn't distinguish broadcast and + non-broadcast like it does for quant non-quantized*/ + if (backend->config()->id() == "cpu" && + (node.opcode() == ir::OpCode::Add || node.opcode() == ir::OpCode::Sub || + node.opcode() == ir::OpCode::Mul)) + { + const auto lhs_index{node.getInputs().at(ir::operation::Add::Input::LHS)}; + const auto rhs_index{node.getInputs().at(ir::operation::Add::Input::RHS)}; + /*Broadcasting isn't supported on CPU: no way to differ the existing exec_time record with and + * without broadcasting*/ + if (!(graph.operands().at(lhs_index).shape() == graph.operands().at(rhs_index).shape())) + { + return true; + } + } + /* TODO: this is workaround, come up with better solution if have. + Adding exception in stage doesn't help. Because if there is a record for Mul without + broadcast, scheduling will select it since it doesn't distinguish broadcast and + non-broadcast like it does for quant non-quantized*/ + else if (backend->config()->id() == "acl_neon" && node.opcode() == ir::OpCode::Mul) + { + const auto lhs_index{node.getInputs().at(ir::operation::Mul::Input::LHS)}; + const auto rhs_index{node.getInputs().at(ir::operation::Mul::Input::RHS)}; + + // Nontrivial broadcasting isn't supported yet + if (quant || + !(graph.operands().at(lhs_index).shape() == graph.operands().at(rhs_index).shape())) + { + return true; + } + } + return false; +} + +// if a node can be merged into op_seq +static bool isMergeable(const ir::Graph &graph, const ir::Operation &node) +{ + size_t prev_op_cnt = 0; + for (const auto &input : node.getInputs()) + { + // only valid_inputs + const auto &operand = graph.operands().at(input); + if (operand.isConstant()) + continue; + + // This operand is output of operation, not weight or bias + if (operand.getDef().list().size() > 0) + ++prev_op_cnt; + + // Current node has multiple inputs as concat or at the beginning of the separated branch + if (prev_op_cnt > 1 || operand.getUses().list().size() > 1) + { + return false; + } + } + return true; +} + +void HEScheduler::scheduleShufflingBackends() +{ + VERBOSE(HEScheduler::schedule) + << "Started task scheduling: uses all backends to get more metrics for data transfer" + << std::endl; + size_t backend_ind = 0; + for (const auto &rank : _rank_to_op) + { + VERBOSE(HEScheduler::schedule) << "scheduling (" << rank.second.value() << ")" << std::endl; + const auto &node = _graph->operations().at(rank.second); + const bool quant = isQuant(*_graph, node); + const auto size = getOperationsFlattenedIOSize(*_graph, node); + for (size_t i = 0;; ++i) + { + if (i == _all_backends.size()) + { + // wasn't able to find backend + assert(false); + break; + } + if (backend_ind == _all_backends.size()) + { + backend_ind = 0; + } + if (isWorkaroundSkip(*_graph, _all_backends[backend_ind], node, quant)) + { + ++backend_ind; + continue; + } + const auto exec_time = + _exec_time->getOperationExecTime(_all_backends[backend_ind], node.name(), quant, size); + // Scheduling to measure data transfer must be done after measuring all backends separately + assert(exec_time != _exec_time->NOT_FOUND); + if (exec_time == _exec_time->getMax()) + { + ++backend_ind; + continue; + } + _backend_resolver->setBackend(rank.second, _all_backends[backend_ind]); + VERBOSE(HEScheduler::schedule) << "backend for " << node.name() << " is " + << _all_backends[backend_ind]->config()->id() << std::endl; + ++backend_ind; + break; + } + } +} + +bool HEScheduler::isNodeProfiled(const ir::Operation &node) +{ + const bool quant = isQuant(*_graph, node); + const auto size = getOperationsFlattenedIOSize(*_graph, node); + for (const auto *backend : _all_backends) + { + const auto exec_time = _exec_time->getOperationExecTime(backend, node.name(), quant, size); + if (exec_time == _exec_time->NOT_FOUND) + return false; + } + return true; +} + +void HEScheduler::scheduleBranch(const ir::OperationIndex &index, + ir::OperationIndexMap<bool> &scheduled) +{ + auto loc_index = index; + const backend::Backend *parent_backend = nullptr; + while (true) + { + if (scheduled[loc_index]) + { + return; + } + if (!schedule(loc_index, parent_backend)) + { + return; + } + scheduled[loc_index] = true; + parent_backend = _backend_resolver->getBackend(loc_index); + + const auto &node = _graph->operations().at(loc_index); + /* get the only output operand, that is input of the next single operation + * and just this nodes output.*/ + if (node.getOutputs().size() != 1) + { + return; + } + const auto &only_out_operand = _graph->operands().at(*node.getOutputs().begin()); + loc_index = only_out_operand.getUses().list().front(); + /* verify, that next node is neither beginning nor ending node of a branch*/ + const auto &next_node = _graph->operations().at(loc_index); + if (!isMergeable(*_graph, next_node)) + { + return; + } + } +} + +std::unique_ptr<compiler::BackendResolver> HEScheduler::schedule(const ir::Graph &graph) +{ + _graph = &graph; + VERBOSE(HEScheduler::schedule) << "task scheduling started" << std::endl; + // Make ranks and save in descending order + makeRank(); + + for (const auto *backend : _all_backends) + { + _backends_avail_time.emplace(backend, std::map<int64_t, int64_t>{{0, 0}}); + } + + if (_is_profiling_mode) + { + // Check if profiling info about all backend/node pairs already exists + bool all_nodes_are_profiled = true; + _graph->operations().iterate([&](const ir::OperationIndex &, const ir::Operation &op) { + if (all_nodes_are_profiled) + all_nodes_are_profiled = isNodeProfiled(op); + }); + + // If all nodes are already profiled - schedule backends in such order, so more profiling + // information about between-backends data transfer could be collected + if (all_nodes_are_profiled) + { + scheduleShufflingBackends(); + VERBOSE(HEScheduler::schedule) << "task scheduling finished" << std::endl; + return std::move(_backend_resolver); + } + } + + ir::OperationIndexMap<bool> visited; + graph.operations().iterate( + [&](const ir::OperationIndex &index, const ir::Operation &) { visited[index] = false; }); + // for each task select the backend with the smallest earliest finishing time(eft) + for (const auto &rank : _rank_to_op) + { + scheduleBranch(rank.second, visited); + } + VERBOSE(HEScheduler::schedule) << "task scheduling finished" << std::endl; + return std::move(_backend_resolver); +} + +int64_t HEScheduler::getOpTime(const backend::Backend *backend, const std::string &operation, + bool quant, uint32_t size) +{ + const auto time = _exec_time->getOperationExecTime(backend, operation, quant, size); + if (time != _exec_time->NOT_FOUND) + return time; + + return _is_supported.at(backend).at(operation) ? 1 : _exec_time->getMax(); +} + +int64_t HEScheduler::getPermuteTime(const backend::Backend *src_backend, + const backend::Backend *dst_backend, bool quant, uint32_t size) +{ + const auto time = _exec_time->getPermuteTime(src_backend, dst_backend, quant, size); + if (time != _exec_time->NOT_FOUND) + return time; + + // Makes the scheduler prefer keeping computations on one backend + return size / 200; +} + +int64_t HEScheduler::tryBackend(const ir::Operation &node, const backend::Backend *backend) +{ + // if there is no profiling info don't use this backend during scheduling + if (!_is_profiling_mode) + { + VERBOSE(HEScheduler::tryBackend) + << "Trying to HE schedule while there is no profiling info for " << node.name() + << " on backend " << backend->config()->id() << ". So this backend won't be used. " + << std::endl; + _is_supported[backend][node.name()] = false; + return _exec_time->getMax(); + } + auto iter = _is_supported.find(backend); + if (iter != _is_supported.end()) + { + auto it2 = iter->second.find(node.name()); + if (it2 != iter->second.end()) + { + return _is_supported[backend][node.name()] ? 1 : _exec_time->getMax(); + } + } + try + { + node.accept(*_backend_contexts.at(backend)->shape_fixer); + + _is_supported[backend][node.name()] = true; + } + catch (std::runtime_error &e) + { + _is_supported[backend][node.name()] = false; + } + return _is_supported[backend][node.name()] ? 1 : _exec_time->getMax(); +} + +void HEScheduler::makeRank() +{ + VERBOSE(HEScheduler::makeRank) << "task prioritizing" << std::endl; + + _graph->operations().iterate( + [&](const ir::OperationIndex &index, const ir::Operation &) { DFSMaxRank(index); }); + + // Check that ranks are calculated for all operations(nodes) + _graph->operations().iterate([&](const ir::OperationIndex &index, const ir::Operation &) { + UNUSED_RELEASE(index); + assert(_op_to_rank->find(index) != _op_to_rank->end()); + }); + VERBOSE(HEScheduler::makeRank) << "task prioritizing finished" << std::endl; +} + +int64_t HEScheduler::DFSMaxRank(const ir::OperationIndex &index) +{ + auto op_to_rank_it = _op_to_rank->find(index); + if (op_to_rank_it != _op_to_rank->end()) + return op_to_rank_it->second; + + const auto &node = _graph->operations().at(index); + int64_t rank = 0; + const bool quant = isQuant(*_graph, node); + const auto size = getOperationsFlattenedIOSize(*_graph, node); + auto supported_backends_quantity = static_cast<int64_t>(_all_backends.size()); + + const auto max_child_rank = DFSChildrenMaxRank(index); + + // get average exec time of this op + for (const auto &backend : _all_backends) + { + auto exec_time = _exec_time->getOperationExecTime(backend, node.name(), quant, size); + if (exec_time == _exec_time->NOT_FOUND) + { + exec_time = tryBackend(node, backend); + } + if (exec_time < _exec_time->getMax()) + { + rank += exec_time; + } + else + { + // this operation isn't supported in this backend + --supported_backends_quantity; + } + } + if (supported_backends_quantity == 0) + { + throw std::runtime_error{"Encountered unsupported op: " + node.name()}; + } + rank /= supported_backends_quantity; + + // get standard deviation + int64_t std = 0; + for (const auto backend : _all_backends) + { + const auto exec_time = getOpTime(backend, node.name(), quant, size); + if (exec_time < _exec_time->getMax()) + { + std += (exec_time - rank) * (exec_time - rank); + } + } + std /= supported_backends_quantity; + if (std > 0) + { + std = static_cast<int>(std::sqrt(std)); + rank *= std; + } + rank += max_child_rank; + + assert(rank >= 0); + _rank_to_op.emplace(rank, index); + _op_to_rank->emplace(index, rank); + VERBOSE(HEScheduler::DFSMaxRank) << "rank of operation (" << index.value() << ")" << node.name() + << " is " << rank << std::endl; + + return rank; +} + +int64_t HEScheduler::DFSChildrenMaxRank(const ir::OperationIndex &index) +{ + const auto &node = _graph->operations().at(index); + int64_t max_child_rank = 0; + for (const auto &output : node.getOutputs()) + { + const auto &operand = _graph->operands().at(output); + const bool quant = operand.typeInfo().type() == ir::DataType::QUANT8_ASYMM; + // average data transfer cost of this operand's data + int64_t avg_transfer_cost = 1; + for (const auto *backend : _all_backends) + { + for (const auto *other_backend : _all_backends) + { + if (backend == other_backend) + { + continue; + } + auto transfer_cost = + getPermuteTime(backend, other_backend, quant, operand.info().total_size()); + avg_transfer_cost += transfer_cost; + } + } + avg_transfer_cost /= _all_backends.size(); + for (const auto &use : operand.getUses().list()) + { + const auto cur_child_rank = DFSMaxRank(use); + max_child_rank = std::max(max_child_rank, cur_child_rank + avg_transfer_cost); + } + } + return max_child_rank; +} + +int64_t HEScheduler::backendAvailableTime(const backend::Backend *backend, + const int64_t &starting_time, const int64_t &time_amount) +{ + const auto backend_times = _backends_avail_time.at(backend); + // finishing and starting times of an op, that will come after current op + auto next_op_fst = backend_times.upper_bound(starting_time); + // finishing time of an op, that will come before current op + auto prev_op_ft = starting_time; + // until reach the "hole/gap", that is enough to run this op + while (next_op_fst != backend_times.end() && next_op_fst->second - prev_op_ft <= time_amount) + { + prev_op_ft = next_op_fst->first + 1; + ++next_op_fst; + } + return prev_op_ft; +} + +bool HEScheduler::schedule(const ir::OperationIndex &index, const backend::Backend *parent_backend) +{ + VERBOSE(HEScheduler::schedule) << "scheduling (" << index.value() << ")" << std::endl; + int64_t eft = std::numeric_limits<int64_t>::max(), selected_exec_time = 0; + const auto &node = _graph->operations().at(index); + + std::multimap<int64_t, int64_t> selected_transfer_st_exec_time; + // select the backend with the smallest eft of this task + const backend::Backend *chosen_backend = nullptr; + for (const auto *backend : _all_backends) + { + std::multimap<int64_t, int64_t> transfer_st_exec_time; + const auto est_and_et = ESTAndExecTime(backend, index, transfer_st_exec_time); + + if (eft > est_and_et.first + est_and_et.second) + { + eft = est_and_et.first + est_and_et.second; + selected_exec_time = est_and_et.second; + chosen_backend = backend; + selected_transfer_st_exec_time = transfer_st_exec_time; + } + } + + if (chosen_backend == nullptr) + { + throw std::runtime_error{"Fail to choose backend on scheduler"}; + } + + // this is part of a branch and it is assigned another backend + if (parent_backend && parent_backend != chosen_backend) + { + return false; + } + for (const auto &it : selected_transfer_st_exec_time) + { + auto prev_op_ft = backendAvailableTime(_cpu_backend, it.first, it.second); + _backends_avail_time[_cpu_backend].insert({prev_op_ft + it.second, prev_op_ft}); + } + + _ops_eft[index] = eft; + _backends_avail_time[chosen_backend].emplace(eft, eft - selected_exec_time); + _backend_resolver->setBackend(index, chosen_backend); + + VERBOSE(HEScheduler::schedule) << "backend for " << node.name() << " is " + << chosen_backend->config()->id() << ". Its eft: " << eft + << std::endl; + return true; +} + +std::pair<int64_t, int64_t> +HEScheduler::ESTAndExecTime(const backend::Backend *backend, const ir::OperationIndex &index, + std::multimap<int64_t, int64_t> &transfer_st_exec_time) +{ + // Permutation will cause creating a separate op_seq that contains just this permutation node. + // This isn't needed for Linear executor since it doesn't use op_seqs + // Number 1 ms is picked experimentally + int64_t permute_fine = 1000; + // Multiply cpu operations' exec time by 2 because in parallel executor it might be busy with + // permutation on other branches or non-nnfw specific tasks and have to wait for it. + // Number 2 is picked experimentally + const int64_t CPU_DELAY = 2; + const auto &node = _graph->operations().at(index); + const bool quant = isQuant(*_graph, node); + const auto size = getOperationsFlattenedIOSize(*_graph, node); + // if this node can be part of a op_seq, then assigning different backend will cause creating + // another op_seq + if (isMergeable(*_graph, node)) + { + permute_fine *= 2; + } + if (isWorkaroundSkip(*_graph, backend, node, quant)) + { + return {_exec_time->getMax(), _exec_time->getMax()}; + } + // get average exec time of the op on this backend + auto exec_time = getOpTime(backend, node.name(), quant, size); + if (backend->config()->id() == "cpu" && _is_parallel_exec) + { + exec_time *= CPU_DELAY; + } + + // get max eft of direct (one level above) predecessors + auto max_pred_eft = predMaxEFT(backend, node, transfer_st_exec_time); + + int64_t total_transfer_cost = 0; + std::vector<std::multimap<int64_t, int64_t>::iterator> inserted_permutations; + // Find free time for data transferring and insert it into backend taskset. This is needed: + // 1. Time for multiple permutations for this node's input is found correctly + // 2. If backend==cpu, then free time for this node must come after permutations + for (auto &it : transfer_st_exec_time) + { + if (_is_parallel_exec) + { + it.second *= CPU_DELAY; + } + if (!_is_linear_exec) + { + it.second += permute_fine; + } + total_transfer_cost += it.second; + + const auto prev_op_ft = backendAvailableTime(_cpu_backend, it.first, it.second); + + max_pred_eft = std::max(max_pred_eft, prev_op_ft + it.second); + + const auto tmp = _backends_avail_time[_cpu_backend].emplace(prev_op_ft + it.second, prev_op_ft); + inserted_permutations.push_back(tmp.first); + } + // find the hole/gap, where this op can be put or the finishing time of the last assigned op + auto prev_op_ft = backendAvailableTime(backend, max_pred_eft, exec_time); + + // Remove inserted permutation from cpu's task set + for (const auto &it : inserted_permutations) + { + _backends_avail_time[_cpu_backend].erase(it); + } + + /* In case non-parallel executor measure just exec time and data transfer time + * because EFT(prev_op_ft) is the same for all backends. Since two operations + * can't be run simultaneously, finish of running operation must be waited for. + * When an operation starts, all backends are free. So, they need time just for + * data transfer.*/ + if (!_is_parallel_exec) + { + VERBOSE(HEScheduler::ESTAndExecTime) + << "exec_time of (" << index.value() << ") " << node.name() << " quant==" << quant << " on " + << backend->config()->id() << " is " << exec_time + << " microseconds. Data transfer cost: " << total_transfer_cost << std::endl; + + return {total_transfer_cost, exec_time}; + } + VERBOSE(HEScheduler::ESTAndExecTime) + << "exec_time of (" << index.value() << ") " << node.name() << " quant==" << quant << " on " + << backend->config()->id() << ": " << exec_time + << " microseconds. Backend available time: " << prev_op_ft + << " Parent's max eft: " << max_pred_eft - total_transfer_cost + << " data transfer cost: " << total_transfer_cost << std::endl; + + return {prev_op_ft, exec_time}; +} + +int64_t HEScheduler::predMaxEFT(const backend::Backend *backend, const ir::Operation &node, + std::multimap<int64_t, int64_t> &transfer_st_exec_time) +{ + int64_t max_pred_eft = 0; + for (const auto &input_operand_idx : node.getInputs()) + { + const auto &input_operand = _graph->operands().at(input_operand_idx); + const bool quant = input_operand.typeInfo().type() == ir::DataType::QUANT8_ASYMM; + + for (const auto &input_node_idx : input_operand.getDef().list()) + { + // Data transfer cost from parent's node backend to current node's backend: + auto parent_backend = _backend_resolver->getBackend(input_node_idx); + + max_pred_eft = std::max(max_pred_eft, _ops_eft.at(input_node_idx)); + if (parent_backend != backend) + { + // Multiply operand size by 2 because size must describe input+output size + int64_t transfer_cost = + getPermuteTime(parent_backend, backend, quant, input_operand.info().total_size() * 2); + transfer_st_exec_time.emplace(_ops_eft.at(input_node_idx), transfer_cost); + } + } + } + return max_pred_eft; +} + +} // namespace compiler + +} // namespace onert diff --git a/runtime/onert/core/src/compiler/HEScheduler.h b/runtime/onert/core/src/compiler/HEScheduler.h new file mode 100644 index 000000000..dbd71d4cb --- /dev/null +++ b/runtime/onert/core/src/compiler/HEScheduler.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file HEScheduler.h + * @brief This file contains HEScheduler class to define and run task Heterogeneous Execution + * Scheduler + */ + +#ifndef __ONERT_COMPILER_H_E_SCHEDULER_H_ +#define __ONERT_COMPILER_H_E_SCHEDULER_H_ + +#include "compiler/IScheduler.h" +#include "compiler/BackendManager.h" +#include "compiler/Compiler.h" +#include "ir/Graph.h" +#include "exec/ExecTime.h" +#include "backend/Backend.h" +#include <memory> +#include "ir/OperationIndexMap.h" +#include <map> +#include <memory> + +namespace onert +{ + +namespace compiler +{ +/** + * @brief Class to schedule tasks + */ +class HEScheduler : IScheduler +{ +public: + /** + * @brief Construct a new Heterogeneous Execution Scheduler object + * @param[in] model Graph model + * @param[in] backend_resolver backend resolver + */ + HEScheduler(const backend::BackendContexts &backend_contexts, const CompilerOptions &options) + : _backend_contexts{backend_contexts}, _is_supported{}, _backends_avail_time{}, _ops_eft{}, + _op_to_rank{std::make_shared<ir::OperationIndexMap<int64_t>>()}, + _is_profiling_mode{options.he_profiling_mode}, + _is_linear_exec{options.executor == "Linear"}, + _is_parallel_exec{options.executor == "Parallel"} + { + for (auto &entry : backend_contexts) + { + _all_backends.push_back(entry.first); + } + _backend_resolver = std::make_unique<compiler::BackendResolver>(); + _exec_time = std::make_unique<exec::ExecTime>(_all_backends); + + // Find cpu backend + auto cpu_backend_it = std::find_if( + _all_backends.begin(), _all_backends.end(), + [](const backend::Backend *backend) { return backend->config()->id() == "cpu"; }); + if (cpu_backend_it == _all_backends.end()) + throw std::runtime_error("HEScheduler could be used only if 'cpu' backend is available"); + _cpu_backend = *cpu_backend_it; + } + +public: + /** + * @brief Task scheduling + * + * @note The main idea is taken from HSIP algo: + * https://www.hindawi.com/journals/sp/2016/3676149/ + */ + std::unique_ptr<compiler::BackendResolver> schedule(const ir::Graph &graph) final; + std::shared_ptr<ir::OperationIndexMap<int64_t>> getIndexedRanks() { return _op_to_rank; } + +private: + bool isNodeProfiled(const ir::Operation &); + + bool schedule(const ir::OperationIndex &, const backend::Backend *parent_backend); + /** + * @brief Get earliest starting time and execution time of an operation on a backend. + * + * @note Returns a time when operation's inputs are ready and backend is available + * It also returns exec time. If this is "cpu" backend, then exec_time*CPU_DELAY + * + * @param[in] backend: backend, for which to return the time + * @param[in] index: index of an operation + * @param[out] transfer_st_exec_time: est and exec time of data transfer operation + * + * @return earliest starting time and execution time + */ + std::pair<int64_t, int64_t> + ESTAndExecTime(const backend::Backend *backend, const ir::OperationIndex &index, + std::multimap<int64_t, int64_t> &transfer_st_exec_time); + /** + * @brief Returns the latest finishing time of parents of a node. + * + * @param[in] backend: backend, for which to return the time + * @param[in] node: node to get eft of parents + * @param[out] transfer_st_exec_time: est and exec time of data transfer operation + * + * @return earliest finishing time of parent nodes + */ + int64_t predMaxEFT(const backend::Backend *backend, const ir::Operation &node, + std::multimap<int64_t, int64_t> &transfer_st_exec_time); + + void makeRank(); + + int64_t DFSMaxRank(const ir::OperationIndex &index); + + int64_t DFSChildrenMaxRank(const ir::OperationIndex &index); + /** + * @brief Returns the time, when backend is available for at least given amount of time. + * + * @note Returns either hole/gap between two performing two already scheduled operations, + * or the finishing time of the last scheduled operation + * + * @param[in] backend backend, for which to return the time + * @param[in] starting_time time, starting which to look for gap + * @param[in] time_amount amount of the time, for which to look gap + * + * @return time, when backend has at least time_amount free time + */ + int64_t backendAvailableTime(const backend::Backend *backend, const int64_t &starting_time, + const int64_t &time_amount); + + int64_t getOpTime(const backend::Backend *backend, const std::string &operation, bool quant, + uint32_t size); + + int64_t getPermuteTime(const backend::Backend *src_backend, const backend::Backend *dst_backend, + bool quant, uint32_t size); + + void scheduleShufflingBackends(); + + int64_t tryBackend(const ir::Operation &node, const backend::Backend *backend); + + /** + * @brief Schedule a node and its successor until: + * 1. there is no branching or connection of multiple branches + * 2. for subsequent nodes: other than predecessor's backend is prefered + * + * @param[in] index: index of an operation + * @param[in] scheduled: a map to check if this node has already been scheduled + * + * @return N/A + */ + void scheduleBranch(const ir::OperationIndex &index, ir::OperationIndexMap<bool> &scheduled); + +private: + // This variable stores backend/node pairs with unknown execution time, and hints scheduler + // whether it should assign these backends to these nodes: + // * It stores false for unsupported nodes + // * During rank calculation with enabled profiling mode it stores true for supported nodes + const backend::BackendContexts &_backend_contexts; + std::unordered_map<const backend::Backend *, std::unordered_map<std::string, bool>> _is_supported; + // Finishing and starting time of each backend + std::unordered_map<const backend::Backend *, std::map<int64_t, int64_t>> _backends_avail_time; + ir::OperationIndexMap<int64_t> _ops_eft; + std::multimap<int64_t, ir::OperationIndex, std::greater<int64_t>> _rank_to_op; + std::shared_ptr<ir::OperationIndexMap<int64_t>> _op_to_rank; + std::unique_ptr<compiler::BackendResolver> _backend_resolver; + std::unique_ptr<exec::ExecTime> _exec_time; + const ir::Graph *_graph{nullptr}; + std::vector<const backend::Backend *> + _all_backends; // TODO Remove this and use _backend_contexts instead + const backend::Backend *_cpu_backend{nullptr}; + bool _is_profiling_mode; + bool _is_linear_exec; + bool _is_parallel_exec; +}; + +} // namespace compiler + +} // namespace onert + +#endif // __ONERT_COMPILER_H_E_SCHEDULER_H_ diff --git a/runtime/onert/core/src/compiler/IScheduler.h b/runtime/onert/core/src/compiler/IScheduler.h new file mode 100644 index 000000000..5e9b9bd3c --- /dev/null +++ b/runtime/onert/core/src/compiler/IScheduler.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_CORE_COMPILER_I_SCHEDULER_H__ +#define __ONERT_CORE_COMPILER_I_SCHEDULER_H__ + +#include "compiler/BackendResolver.h" +#include "ir/Graph.h" + +namespace onert +{ +namespace compiler +{ + +struct IScheduler +{ + virtual ~IScheduler() = default; + + virtual std::unique_ptr<BackendResolver> schedule(const ir::Graph &graph) = 0; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_CORE_COMPILER_I_SCHEDULER_H__ diff --git a/runtime/onert/core/src/compiler/Linear.cc b/runtime/onert/core/src/compiler/Linear.cc new file mode 100644 index 000000000..7c658de95 --- /dev/null +++ b/runtime/onert/core/src/compiler/Linear.cc @@ -0,0 +1,280 @@ +/* + * 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 "util/logging.h" + +namespace onert +{ +namespace compiler +{ + +std::vector<ir::OpSequenceIndex> Linear::linearize(const ir::LoweredGraph &lowered_graph) +{ + std::vector<ir::OpSequenceIndex> order; + { + const ir::Graph &graph = lowered_graph.graph(); + const ir::OpSequences &op_seqs = lowered_graph.op_seqs(); + const ir::Operands &operands = graph.operands(); + // op_seqs can't access a op_seq by an operand so that input_to_op_seqs can offer it + std::unordered_map<ir::OperandIndex, std::list<ir::OpSequenceIndex>> input_to_op_seqs; + + // Get the relations between input/op_seq to be used for dfs-post-iter + // + // [0] # input -> _input_to_op_seqs[0] = {OP_SEQS0} + // | + // [OP_SEQS0] + // | + // [1]---------. # input -> _input_to_op_seqs[1] = {OP_SEQS1, OP_SEQS2} + // | | + // [OP_SEQS1] [OP_SEQS2] + // | | + // [2] [3] # input -> _input_to_op_seqs[2] = {OP_SEQS3} + // \ / # input -> _input_to_op_seqs[3] = {OP_SEQS3} + // [OP_SEQS3] + // | + // [4] + op_seqs.iterate([&](const ir::OpSequenceIndex &op_seq_idx, const ir::OpSequence &op_seq) { + for (auto input : op_seq.getInputs()) + { + // only valid_inputs + const auto &operand = operands.at(input); + if (operand.isConstant()) + continue; + + auto it = input_to_op_seqs.find(input); + if (it == input_to_op_seqs.end()) + { + std::list<ir::OpSequenceIndex> list{op_seq_idx}; + input_to_op_seqs[input] = list; + } + else + { + it->second.push_back(op_seq_idx); + } + } + }); + + std::unordered_map<ir::OpSequenceIndex, bool> visited; + op_seqs.iterate( + [&](const ir::OpSequenceIndex &index, const ir::OpSequence &) { visited[index] = false; }); + + std::function<void(const ir::OpSequenceIndex &, const ir::OpSequence &)> dfs_recursive = + [&](const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq) -> void { + if (visited[index]) + return; + visited[index] = true; + + // The outputs should be not constants + for (auto output : op_seq.getOutputs()) + { + const auto it = input_to_op_seqs.find(output); + if (it != input_to_op_seqs.end()) + { + const auto &op_seq_index_list = it->second; + for (const auto &index : op_seq_index_list) + { + auto &op_seq = op_seqs.at(index); + dfs_recursive(index, op_seq); + } + } + } + + order.emplace_back(index); + }; + + op_seqs.iterate(dfs_recursive); + + // All of the nodes must have been visited. + assert( + std::all_of(visited.begin(), visited.end(), + [](const std::pair<const ir::OpSequenceIndex, bool> &v) { return v.second; })); + + // NOTE. Now these op_seq are on the reverse order + std::reverse(order.begin(), order.end()); + } + return order; +} + +void Linear::dump(const ir::LoweredGraph &lowered_graph, + const std::vector<ir::OpSequenceIndex> &order) +{ + { + const auto &toString = [](const onert::backend::Backend *backend) { + assert(backend); + std::string str; + str += backend->config()->id(); + return "{" + str + "}"; + }; + + VERBOSE(Linear) << "Final OpSequence" << std::endl; + for (const auto index : order) + { + + const auto &op_seq = lowered_graph.op_seqs().at(index); + const auto lower_info = lowered_graph.getLowerInfo(index); + VERBOSE(Linear) << "* OP_SEQ " << toString(lower_info->backend()) << " " << op_seq.getStr() + << std::endl; + } + } +} + +void Linear::planTensors(const ir::LoweredGraph &lowered_graph, + const std::vector<ir::OpSequenceIndex> &order) +{ + const auto &graph = lowered_graph.graph(); + ir::OperandIndexMap<std::shared_ptr<backend::ITensorBuilder>> tensor_builder_map; + + ir::OperandIndexMap<uint32_t> uses_map; + ir::OperandIndexMap<uint32_t> def_map; + ir::OperandIndexSequence constants; + + // Prepare scanning + graph.operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) { + const auto lower_info = lowered_graph.getLowerInfo(ind); + // TODO Remove if onert 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 not be 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); + } + + auto factor = lower_info->def_factors().getOnlyElement(); + auto backend = factor.backend(); + auto tensor_builder = lowered_graph.backend_contexts().at(backend)->tensor_builder; + if (!tensor_builder->isRegistered(ind)) + { + // These tensors do not exist in any op_seq (No use and def) + const auto info = obj.info(); + const auto backend_layout = factor.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 op_seq_ind : order) + { + const auto &op_seq = lowered_graph.op_seqs().at(op_seq_ind); + for (const auto &op : 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; })); +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/Linear.h b/runtime/onert/core/src/compiler/Linear.h new file mode 100644 index 000000000..faeff77f3 --- /dev/null +++ b/runtime/onert/core/src/compiler/Linear.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef __ONERT_COMPILER_LINEAR_H__ +#define __ONERT_COMPILER_LINEAR_H__ + +#include <vector> +#include <memory> + +#include "ir/OpSequences.h" +#include "ir/Index.h" +#include "backend/ITensorBuilder.h" +#include "ir/LoweredGraph.h" + +namespace onert +{ +namespace ir +{ +struct OperationVisitor; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace compiler +{ + +class Linear +{ +public: + static std::vector<ir::OpSequenceIndex> linearize(const ir::LoweredGraph &lowered_graph); + static void dump(const ir::LoweredGraph &lowered_graph, + const std::vector<ir::OpSequenceIndex> &order); + static void planTensors(const ir::LoweredGraph &lowered_graph, + const std::vector<ir::OpSequenceIndex> &order); +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_LINEAR_H__ diff --git a/runtime/onert/core/src/compiler/ManualScheduler.cc b/runtime/onert/core/src/compiler/ManualScheduler.cc new file mode 100644 index 000000000..5a5aa8219 --- /dev/null +++ b/runtime/onert/core/src/compiler/ManualScheduler.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019 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 "ManualScheduler.h" +#include "ir/OpCode.h" +#include "ir/Operations.Include.h" +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "compiler/BackendManager.h" +#include "util/ConfigSource.h" +#include "util/logging.h" +#include "misc/string_helpers.h" + +namespace onert +{ +namespace compiler +{ + +ManualScheduler::ManualScheduler(const compiler::ManualSchedulerOptions &options) + : _options{options} +{ +} + +std::unique_ptr<BackendResolver> ManualScheduler::schedule(const ir::Graph &graph) +{ + auto backend_resolver = std::make_unique<compiler::BackendResolver>(); + + // 1. Backend for All operations + const backend::Backend *backend_all = BackendManager::get().get(_options.backend_for_all); + if (!backend_all) + { + backend_all = BackendManager::get().getAll().at(0); + } + VERBOSE(ManualScheduler) << "Default backend for all ops: " << _options.backend_for_all + << std::endl; + + graph.operations().iterate([&](const ir::OperationIndex &index, const ir::Operation &) { + backend_resolver->setBackend(index, backend_all); + }); + + // 2. Backend per operation type + std::unordered_map<ir::OpCode, backend::Backend *> op_type_map; + for (auto &pair : _options.opcode_to_backend) + { + op_type_map.emplace(pair.first, BackendManager::get().get(pair.second)); + } + // By default, Custom uses cpu backend + op_type_map[ir::OpCode::Custom] = BackendManager::get().get("cpu"); + + graph.operations().iterate([&](const ir::OperationIndex &index, const ir::Operation &operation) { + auto itr = op_type_map.find(operation.opcode()); + if (itr != op_type_map.end()) + { + backend_resolver->setBackend(index, itr->second); + } + }); + + // 3. Backend per operation + for (auto &pair : _options.index_to_backend) + { + const auto &key = pair.first; + const auto &val = pair.second; + + try + { + graph.operations().at(key); // Check if exist, or this will throw + backend_resolver->setBackend(key, BackendManager::get().get(val)); + } + catch (...) + { + VERBOSE(ManualScheduler) << "Invalid value while OperationIndex to Backend mapping : @" + << key.value() << " -> \"" << val << "\"" << std::endl; + } + } + + // 4. Operations that are specially handled + // All configuration above will be ignored(overwritten) + op_type_map[ir::OpCode::Permute] = BackendManager::get().get("cpu"); + + // Dump final assignment + backend_resolver->iterate([&](const ir::OperationIndex &index, const backend::Backend &backend) { + VERBOSE(ManualScheduler) << "backend for operation #" << index.value() << ": " + << backend.config()->id() << std::endl; + }); + + return backend_resolver; +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ManualScheduler.h b/runtime/onert/core/src/compiler/ManualScheduler.h new file mode 100644 index 000000000..961dd14b2 --- /dev/null +++ b/runtime/onert/core/src/compiler/ManualScheduler.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_CORE_COMPILER_MANUAL_SCHEDULER_H__ +#define __ONERT_CORE_COMPILER_MANUAL_SCHEDULER_H__ + +#include "IScheduler.h" +#include "compiler/Compiler.h" + +namespace onert +{ +namespace compiler +{ + +class ManualScheduler : public IScheduler +{ +public: + ManualScheduler(const compiler::ManualSchedulerOptions &options); + std::unique_ptr<BackendResolver> schedule(const ir::Graph &graph) override; + +private: + compiler::ManualSchedulerOptions _options; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_CORE_COMPILER_MANUAL_SCHEDULER_H__ diff --git a/runtime/onert/core/src/compiler/OperandContext.cc b/runtime/onert/core/src/compiler/OperandContext.cc new file mode 100644 index 000000000..cce555e33 --- /dev/null +++ b/runtime/onert/core/src/compiler/OperandContext.cc @@ -0,0 +1,45 @@ +/* + * 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 "OperandContext.h" + +#include <cassert> + +namespace onert +{ +namespace compiler +{ + +OperandContext &OperandContext::set(const ir::OperandIndex &id, + const std::shared_ptr<backend::ITensor> &tensor) +{ + // Only one tensor for an id + assert(_tensors.find(id) == _tensors.end()); + _tensors[id] = tensor; + return (*this); +} + +void OperandContext::iterate( + const std::function<void(const ir::OperandIndex &, backend::ITensor &)> &fn) +{ + for (auto &e : _tensors) + { + fn(e.first, *e.second); + } +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/OperandContext.h b/runtime/onert/core/src/compiler/OperandContext.h new file mode 100644 index 000000000..390b376fe --- /dev/null +++ b/runtime/onert/core/src/compiler/OperandContext.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef __ONERT_COMPILER_OPERAND_CONTEXT_H__ +#define __ONERT_COMPILER_OPERAND_CONTEXT_H__ + +#include "backend/ITensor.h" +#include "ir/OperandIndexMap.h" +#include <unordered_map> +#include <memory> + +namespace onert +{ +namespace compiler +{ + +class OperandContext +{ +public: + OperandContext &set(const ir::OperandIndex &ind, const std::shared_ptr<backend::ITensor> &tensor); + +public: + bool exist(const ir::OperandIndex &ind) const { return _tensors.find(ind) != _tensors.end(); } + +public: + std::shared_ptr<backend::ITensor> at(const ir::OperandIndex &ind) const + { + return _tensors.at(ind); + } + + std::shared_ptr<backend::ITensor> &at(const ir::OperandIndex &ind) { return _tensors.at(ind); } + + void iterate(const std::function<void(const ir::OperandIndex &, backend::ITensor &)> &fn); + +private: + ir::OperandIndexMap<std::shared_ptr<backend::ITensor>> _tensors; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_OPERAND_CONTEXT_H__ diff --git a/runtime/onert/core/src/compiler/OperationValidator.cc b/runtime/onert/core/src/compiler/OperationValidator.cc new file mode 100644 index 000000000..1368d11b9 --- /dev/null +++ b/runtime/onert/core/src/compiler/OperationValidator.cc @@ -0,0 +1,1079 @@ +/* + * 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 "OperationValidator.h" + +#include <typeinfo> + +#include "ir/Graph.h" +#include "ir/operation/LowerInfo.h" + +#include "util/logging.h" +#include "util/Utils.h" + +namespace onert +{ +namespace compiler +{ + +OperationValidator::OperationValidator(const ir::Graph &graph) + : _graph{graph}, _ctx{graph.operands()}, _current_op_seq_layout{ir::Layout::UNKNOWN} +{ +} + +void OperationValidator::operator()() +{ + // TODO Get frontend layout from graph + _current_op_seq_layout = ir::Layout::NHWC; + + _graph.operations().iterate( + [&](const ir::OperationIndex &, const ir::Operation &node) { node.accept(*this); }); +} + +void OperationValidator::visit(const ir::operation::Abs &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void OperationValidator::visit(const ir::operation::AvgPool2D &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::AvgPool2D::Input::INPUT)}; + + UNUSED_RELEASE(ofm_index); + UNUSED_RELEASE(ifm_index); + + assert(_ctx.at(ifm_index).shape().rank() == 4); +} + +void OperationValidator::visit(const ir::operation::BatchToSpaceND &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::BatchToSpaceND::Input::INPUT)}; + const auto block_size_index{ + node.getInputs().at(ir::operation::BatchToSpaceND::Input::BLOCK_SIZE)}; + + const auto frontend_layout = _current_op_seq_layout; + const auto input_shape = _ctx.at(ifm_index).shape().asFeature(frontend_layout); + const auto output_shape = _ctx.at(ofm_index).shape().asFeature(frontend_layout); + + UNUSED_RELEASE(input_shape); + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(block_size_index); + + // All assertions as per NNAPI specification. + assert(_ctx.at(ifm_index).shape().rank() == 4); + assert(_ctx.at(ofm_index).shape().rank() == 4); + assert(_ctx.at(block_size_index).shape().rank() == 1); + + assert(_ctx.at(block_size_index).shape().dim(0) == 2); + + assert(_ctx.at(block_size_index).isConstant()); + + assert(input_shape.C == output_shape.C); +} + +void OperationValidator::visit(const ir::operation::Cast &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void OperationValidator::visit(const ir::operation::Comparison &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(ir::operation::Comparison::Input::INPUT0)}; + const auto rhs_index{node.getInputs().at(ir::operation::Comparison::Input::INPUT1)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(lhs_index); + UNUSED_RELEASE(rhs_index); + + assert(_ctx.at(lhs_index).typeInfo().type() == _ctx.at(rhs_index).typeInfo().type()); + assert(_ctx.at(output_index).typeInfo().type() == ir::DataType::BOOL8); +} + +void OperationValidator::visit(const ir::operation::Softmax &node) +{ + VERBOSE(Softmax) << "Configure SOFTMAX operation" << std::endl; + + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape().rank() == _ctx.at(input_index).shape().rank()); +} + +void OperationValidator::visit(const ir::operation::InstanceNorm &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::InstanceNorm::Input::INPUT)}; + const auto gamma_index{node.getInputs().at(ir::operation::InstanceNorm::Input::GAMMA)}; + const auto beta_index{node.getInputs().at(ir::operation::InstanceNorm::Input::BETA)}; + + UNUSED_RELEASE(ofm_index); + UNUSED_RELEASE(ifm_index); + UNUSED_RELEASE(gamma_index); + UNUSED_RELEASE(beta_index); + + assert(_ctx.at(ifm_index).shape().rank() == 4); + assert(_ctx.at(ifm_index).shape() == _ctx.at(ofm_index).shape()); + assert(_ctx.at(gamma_index).shape().rank() == 1); + assert(_ctx.at(beta_index).shape().rank() == 1); +} + +void OperationValidator::visit(const ir::operation::Permute &node) +{ + VERBOSE(Permute) << "Configure Permute operation" << std::endl; + + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape().rank() == _ctx.at(input_index).shape().rank()); +} + +void OperationValidator::visit(const ir::operation::ReduceSum &node) +{ + VERBOSE(Permute) << "Configure ReduceSum operation" << std::endl; + + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::ReduceSum::Input::INPUT)}; + const auto &axes = node.param().axes; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + UNUSED_RELEASE(axes); + + const auto input_shape = _ctx.at(input_index).shape(); + const auto output_shape = _ctx.at(output_index).shape(); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(input_shape); + + assert(input_shape.rank() <= 4); + assert(output_shape.rank() <= input_shape.rank()); + + // NOTE For the 4-dimensions, if the rank of input and output are different, this runtime only + // supports cases reducing height and width or reducing depth. + // TODO We have to support all cases of dimensions up to 4. + // For correct permuting, we have to set output's shape to be equal in dimension position of the + // input. But the positions of the same dimensions in the input and output may be set differently. + // For example {2,3,4,5}(input's shape) can be reduced to {3,5}(output's shape). The original + // output shape should be {1,3,1,5}, but real output shape may be {3,5}. If you simply try to + // extend it in 4 dimensions, it should be {1,1,3,5}. + // Even if output shape is changed to {1,3,1,5}, there is another problem. It is that shape of + // output tensor used at next operation is changed to {1,3,1,5} after this operation even if the + // next operation is not desired. + if (input_shape.rank() == 4 && input_shape.rank() != output_shape.rank()) + { + if (output_shape.rank() == 2) + { + // Reducing HW + assert(input_shape.dim(0) == output_shape.dim(0) && + input_shape.dim(3) == output_shape.dim(1)); + } + else if (output_shape.rank() == 3) + { + // Reducing C or + // (Reducing H and C(input and output) == 1) or (Reducing W and C(input and output) == 1) + assert((input_shape.dim(0) == output_shape.dim(0) && + input_shape.dim(1) == output_shape.dim(1) && + input_shape.dim(2) == output_shape.dim(2)) || + (input_shape.dim(0) == output_shape.dim(0) && + (input_shape.dim(1) == output_shape.dim(1) || + input_shape.dim(2) == output_shape.dim(1)) && + input_shape.dim(3) == 1 && output_shape.dim(2) == 1)); + } + } +} + +void OperationValidator::visit(const ir::operation::Transpose &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::Transpose::Input::INPUT)}; + const auto &perm{node.param().perm}; + + const auto &output_shape = _ctx.at(output_index).shape(); + const auto &input_shape = _ctx.at(input_index).shape(); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(input_shape); + UNUSED_RELEASE(perm); + + assert(input_shape.rank() == static_cast<int>(perm.size())); + assert(input_shape.rank() == output_shape.rank()); +} + +void OperationValidator::visit(const ir::operation::ReduceMax &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::ReduceMax::Input::INPUT)}; + const auto &axes = node.param().axes; + + auto output_shape = _ctx.at(output_index).shape(); + auto input_shape = _ctx.at(input_index).shape(); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(input_shape); + UNUSED_RELEASE(axes); + + assert(input_shape.rank() <= 4); + assert(output_shape.rank() <= input_shape.rank()); + + // NOTE For the 4-dimensions, if the rank of input and output are different, this runtime only + // supports cases reducing height and width or reducing depth. + // TODO We have to support all cases of dimensions up to 4. + // For correct permuting, we have to set output's shape to be equal in dimension position of the + // input. But the positions of the same dimensions in the input and output may be set differently. + // For example {2,3,4,5}(input's shape) can be reduced to {3,5}(output's shape). The original + // output shape should be {1,3,1,5}, but real output shape may be {3,5}. If you simply try to + // extend it in 4 dimensions, it should be {1,1,3,5}. + // Even if output shape is changed to {1,3,1,5}, there is another problem. It is that shape of + // output tensor used at next operation is changed to {1,3,1,5} after this operation even if the + // next operation is not desired. + if (input_shape.rank() == 4 && input_shape.rank() != output_shape.rank()) + { + if (output_shape.rank() == 2) + { + // Reducing HW + assert(input_shape.dim(0) == output_shape.dim(0) && + input_shape.dim(3) == output_shape.dim(1)); + } + else if (output_shape.rank() == 3) + { + // Reducing C or + // (Reducing H and C(ifm and ofm) == 1) or (Reducing W and C(ifm and ofm) == 1) + assert((input_shape.dim(0) == output_shape.dim(0) && + input_shape.dim(1) == output_shape.dim(1) && + input_shape.dim(2) == output_shape.dim(2)) || + (input_shape.dim(0) == output_shape.dim(0) && + (input_shape.dim(1) == output_shape.dim(1) || + input_shape.dim(2) == output_shape.dim(1)) && + input_shape.dim(3) == 1 && output_shape.dim(2) == 1)); + } + } +} + +void OperationValidator::visit(const ir::operation::RNN &node) +{ + // NOTE This validation is for static rnn(non-dynamic shape), but not for dynamic rnn + // TODO Support dynamic rnn + const auto output_index{node.getOutputs().at(ir::operation::RNN::Output::OUTPUT)}; + const auto hidden_state_out_index{ + node.getOutputs().at(ir::operation::RNN::Output::HIDDEN_STATE_OUT)}; + + const auto input_index{node.getInputs().at(ir::operation::RNN::Input::INPUT)}; + const auto weights_index{node.getInputs().at(ir::operation::RNN::Input::WEIGHTS)}; + const auto recurrent_weights_index{ + node.getInputs().at(ir::operation::RNN::Input::RECURRENT_WEIGHTS)}; + const auto bias_index{node.getInputs().at(ir::operation::RNN::Input::BIAS)}; + const auto hidden_state_in_index{node.getInputs().at(ir::operation::RNN::Input::HIDDEN_STATE_IN)}; + + const auto batch_size = _ctx.at(output_index).shape().dim(0); + const auto num_units = _ctx.at(output_index).shape().dim(1); + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(hidden_state_out_index); + UNUSED_RELEASE(input_index); + UNUSED_RELEASE(weights_index); + UNUSED_RELEASE(recurrent_weights_index); + UNUSED_RELEASE(bias_index); + UNUSED_RELEASE(hidden_state_in_index); + UNUSED_RELEASE(batch_size); + UNUSED_RELEASE(num_units); + + assert(_ctx.at(output_index).shape().rank() == 2 && + _ctx.at(hidden_state_out_index).shape().rank() == 2 && + _ctx.at(input_index).shape().rank() == 2 && _ctx.at(weights_index).shape().rank() == 2 && + _ctx.at(recurrent_weights_index).shape().rank() == 2 && + _ctx.at(hidden_state_in_index).shape().rank() == 2); + assert(_ctx.at(bias_index).shape().rank() == 1); + + assert(batch_size == _ctx.at(input_index).shape().dim(0) && + batch_size == _ctx.at(hidden_state_in_index).shape().dim(0) && + batch_size == _ctx.at(hidden_state_out_index).shape().dim(0)); + assert(_ctx.at(input_index).shape().dim(1) == _ctx.at(weights_index).shape().dim(1)); + + assert(num_units == _ctx.at(weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_weights_index).shape().dim(0) && + num_units == _ctx.at(bias_index).shape().dim(0)); + assert(num_units == _ctx.at(output_index).shape().dim(1) && + num_units == _ctx.at(recurrent_weights_index).shape().dim(1) && + num_units == _ctx.at(hidden_state_in_index).shape().dim(1) && + num_units == _ctx.at(hidden_state_out_index).shape().dim(1)); +} + +void OperationValidator::visit(const ir::operation::SpaceToBatchND &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::SpaceToBatchND::Input::INPUT)}; + const auto block_size_index{ + node.getInputs().at(ir::operation::SpaceToBatchND::Input::BLOCK_SIZE)}; + const auto paddings_index{node.getInputs().at(ir::operation::SpaceToBatchND::Input::PADDINGS)}; + + const auto frontend_layout = _current_op_seq_layout; + const auto input_shape = _ctx.at(ifm_index).shape().asFeature(frontend_layout); + const auto output_shape = _ctx.at(ofm_index).shape().asFeature(frontend_layout); + + UNUSED_RELEASE(input_shape); + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(block_size_index); + UNUSED_RELEASE(paddings_index); + + // All assertions as per NNAPI specification. + assert(_ctx.at(ifm_index).shape().rank() == 4); + assert(_ctx.at(ofm_index).shape().rank() == 4); + assert(_ctx.at(block_size_index).shape().rank() == 1); + assert(_ctx.at(paddings_index).shape().rank() == 2); + + assert(_ctx.at(block_size_index).shape().dim(0) == 2); + assert(_ctx.at(paddings_index).shape().dim(0) == 2); + assert(_ctx.at(paddings_index).shape().dim(1) == 2); + + assert(_ctx.at(block_size_index).isConstant()); + assert(_ctx.at(paddings_index).isConstant()); + + assert(input_shape.C == output_shape.C); +} + +void OperationValidator::visit(const ir::operation::SpaceToDepth &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::SpaceToDepth::Input::INPUT)}; + + const auto frontend_layout = _current_op_seq_layout; + const auto input_shape = _ctx.at(ifm_index).shape().asFeature(frontend_layout); + const auto output_shape = _ctx.at(ofm_index).shape().asFeature(frontend_layout); + const auto block_size = node.param().block_size; + + UNUSED_RELEASE(input_shape); + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(block_size); + + // All assertions as per NNAPI specification. + assert(_ctx.at(ifm_index).shape().rank() == 4); + assert(_ctx.at(ofm_index).shape().rank() == 4); + assert((block_size >= 1) && (input_shape.H % block_size == 0) && + (input_shape.W % block_size == 0)); + assert(input_shape.N == output_shape.N); + assert(input_shape.C * block_size * block_size == output_shape.C); +} + +void OperationValidator::visit(const ir::operation::EmbeddingLookup &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lookups_index{node.getInputs().at(ir::operation::EmbeddingLookup::Input::LOOKUPS)}; + const auto values_index{node.getInputs().at(ir::operation::EmbeddingLookup::Input::VALUES)}; + + const auto &output_obj = _ctx.at(output_index); + const auto &lookups_obj = _ctx.at(lookups_index); + const auto &values_obj = _ctx.at(values_index); + + UNUSED_RELEASE(output_obj); + UNUSED_RELEASE(lookups_obj); + UNUSED_RELEASE(values_obj); + + // Verify operand here, not at SimpleEmbeddingLookup::configure() to avoid acl's modifying + // TensorShape sometimes(Issue: https://github.sec.samsung.net/STAR/nnfw/issues/729) + { + assert(lookups_obj.typeInfo().type() == ir::DataType::INT32); + + const auto &output_shape = output_obj.shape(); + const auto &lookups_shape = lookups_obj.shape(); + const auto &values_shape = values_obj.shape(); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(lookups_shape); + UNUSED_RELEASE(values_shape); + + assert(lookups_shape.rank() == 1); + assert(values_shape.rank() >= 2); + + // output should be a n-D tensor with the same rank and shape as the values tensor, except for + // the first dimension which has the same size as lookups' only dimension. + assert(output_shape.rank() == values_shape.rank()); + assert(output_shape.dim(0) == lookups_shape.dim(0)); + for (int n = 1; n < output_shape.rank(); ++n) + { + assert(output_shape.dim(n) == values_shape.dim(n)); + } + } +} + +void OperationValidator::visit(const ir::operation::Exp &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::Exp::Input::INPUT)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); + assert(_ctx.at(output_index).typeInfo().type() == _ctx.at(input_index).typeInfo().type()); +} + +void OperationValidator::visit(const ir::operation::Floor &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::Floor::Input::INPUT)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); + assert(_ctx.at(output_index).typeInfo().type() == _ctx.at(input_index).typeInfo().type()); +} + +void OperationValidator::visit(const ir::operation::HashtableLookup &node) +{ + const auto output_index{node.getOutputs().at(ir::operation::HashtableLookup::Output::OUTPUT)}; + const auto hits_index{node.getOutputs().at(ir::operation::HashtableLookup::Output::HITS)}; + + const auto lookups_index{node.getInputs().at(ir::operation::HashtableLookup::Input::LOOKUPS)}; + const auto keys_index{node.getInputs().at(ir::operation::HashtableLookup::Input::KEYS)}; + const auto values_index{node.getInputs().at(ir::operation::HashtableLookup::Input::VALUES)}; + + const auto &output_obj = _ctx.at(output_index); + const auto &hits_obj = _ctx.at(hits_index); + + const auto &lookups_obj = _ctx.at(lookups_index); + const auto &keys_obj = _ctx.at(keys_index); + const auto &values_obj = _ctx.at(values_index); + + assert(lookups_obj.typeInfo().type() == ir::DataType::INT32); + assert(keys_obj.typeInfo().type() == ir::DataType::INT32); + assert(hits_obj.typeInfo().type() == ir::DataType::QUANT8_ASYMM); + + const auto &output_shape = output_obj.shape(); + const auto &hits_shape = hits_obj.shape(); + + const auto &lookups_shape = lookups_obj.shape(); + const auto &keys_shape = keys_obj.shape(); + const auto &values_shape = values_obj.shape(); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(hits_shape); + UNUSED_RELEASE(lookups_shape); + UNUSED_RELEASE(keys_shape); + UNUSED_RELEASE(values_shape); + + assert(values_shape.rank() == output_shape.rank()); + assert(lookups_shape.rank() == 1); + assert(keys_shape.rank() == 1); + assert(values_shape.dim(0) == keys_shape.dim(0)); + assert(lookups_shape.dim(0) == output_shape.dim(0)); +} + +void OperationValidator::visit(const ir::operation::TransposeConv &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::TransposeConv::Input::INPUT)}; + const auto ker_index{node.getInputs().at(ir::operation::TransposeConv::Input::KERNEL)}; + + // Only 4D tensors are supported + assert(_ctx.at(ofm_index).shape().rank() == 4); + assert(_ctx.at(ofm_index).shape().rank() == _ctx.at(ifm_index).shape().rank()); + assert(_ctx.at(ofm_index).shape().rank() == _ctx.at(ker_index).shape().rank()); + + const auto frontend_layout = _current_op_seq_layout; + const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature(frontend_layout); + const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature(frontend_layout); + // The kernel has only IHWO layout on frontend + // So ker_shape is treated here below + // I -> N + // H -> H + // W -> W + // O -> C + const auto ker_shape = _ctx.at(ker_index).shape().asFeature(ir::Layout::NHWC); + + UNUSED_RELEASE(ofm_shape); + UNUSED_RELEASE(ifm_shape); + UNUSED_RELEASE(ker_shape); + + assert((node.param().padding.type == ir::PaddingType::SAME) || + (node.param().padding.type == ir::PaddingType::VALID)); + assert(ifm_shape.N == ofm_shape.N); + assert(ifm_shape.C == ker_shape.C); + assert(ker_shape.N == ofm_shape.C); +} + +void OperationValidator::visit(const ir::operation::Gather &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + + const auto ifm_index{node.getInputs().at(ir::operation::Gather::Input::INPUT)}; + const auto indices_index{node.getInputs().at(ir::operation::Gather::Input::INDICES)}; + + const auto axis = node.param().axis; + + const auto ifm_shape = _ctx.at(ifm_index).shape(); + const auto indices_shape = _ctx.at(indices_index).shape(); + const auto ofm_shape = _ctx.at(ofm_index).shape(); + + UNUSED_RELEASE(ifm_shape); + UNUSED_RELEASE(indices_shape); + UNUSED_RELEASE(ofm_shape); + UNUSED_RELEASE(axis); + + assert(ifm_shape.rank() <= 4); + assert(indices_shape.rank() <= 3); + assert(ofm_shape.rank() <= 4); +} + +void OperationValidator::visit(const ir::operation::Dequantize &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::Dequantize::Input::INPUT)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(input_index).shape().rank() <= 4); + assert(_ctx.at(input_index).shape() == _ctx.at(output_index).shape()); + assert(_ctx.at(input_index).typeInfo().type() == ir::DataType::QUANT8_ASYMM); + assert(_ctx.at(output_index).typeInfo().type() == ir::DataType::FLOAT32); +} + +void OperationValidator::visit(const ir::operation::Mean &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::Mean::Input::INPUT)}; + + const auto ifm_shape = _ctx.at(ifm_index).shape(); + const auto ofm_shape = _ctx.at(ofm_index).shape(); + + // NOTE For the 4-dimensions, if the rank of input and output are different, this runtime only + // supports cases reducing height and width or reducing depth. + // TODO We have to support all cases of dimensions up to 4. + // For correct permuting, we have to set output's shape to be equal in dimension position of the + // input. But the positions of the same dimensions in the input and output may be set differently. + // For example {2,3,4,5}(input's shape) can be reduced to {3,5}(output's shape). The original + // output shape should be {1,3,1,5}, but real output shape may be {3,5}. If you simply try to + // extend it in 4 dimensions, it should be {1,1,3,5}. + // Even if output shape is changed to {1,3,1,5}, there is another problem. It is that shape of + // output tensor used at next operation is changed to {1,3,1,5} after this operation even if the + // next operation is not desired. + if (ifm_shape.rank() == 4 && ifm_shape.rank() != ofm_shape.rank()) + { + if (ofm_shape.rank() == 2) + { + // Reducing HW + assert(ifm_shape.dim(0) == ofm_shape.dim(0) && ifm_shape.dim(3) == ofm_shape.dim(1)); + } + else if (ofm_shape.rank() == 3) + { + // Reducing C or + // (Reducing H and C(ifm and ofm) == 1) or (Reducing W and C(ifm and ofm) == 1) + assert((ifm_shape.dim(0) == ofm_shape.dim(0) && ifm_shape.dim(1) == ofm_shape.dim(1) && + ifm_shape.dim(2) == ofm_shape.dim(2)) || + (ifm_shape.dim(0) == ofm_shape.dim(0) && + (ifm_shape.dim(1) == ofm_shape.dim(1) || ifm_shape.dim(2) == ofm_shape.dim(1)) && + ifm_shape.dim(3) == 1 && ofm_shape.dim(2) == 1)); + } + } +} + +void OperationValidator::visit(const ir::operation::DepthToSpace &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::DepthToSpace::Input::INPUT)}; + + const auto frontend_layout = _current_op_seq_layout; + const auto output_shape = _ctx.at(output_index).shape().asFeature(frontend_layout); + const auto input_shape = _ctx.at(input_index).shape().asFeature(frontend_layout); + + UNUSED_RELEASE(output_shape); + UNUSED_RELEASE(input_shape); + + assert(_ctx.at(input_index).shape().rank() == 4); + assert(_ctx.at(output_index).shape().rank() == 4); + + int32_t block_size = node.param().block_size; + + UNUSED_RELEASE(block_size); + + assert(block_size > 0); + + { // assertions block + assert(output_shape.N == input_shape.N); + assert(output_shape.H == input_shape.H * block_size); + assert(output_shape.W == input_shape.W * block_size); + assert(input_shape.C % (block_size * block_size) == 0); + assert(output_shape.C == input_shape.C / (block_size * block_size)); + } +} + +void OperationValidator::visit(const ir::operation::Pack &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto num{node.param().num}; + const auto axis{node.param().axis}; + + const auto &output_shape = _ctx.at(output_index).shape(); + const auto output_rank = static_cast<int32_t>(output_shape.rank()); + + const auto input1_index{node.getInputs().at(0)}; + const auto input_shape = _ctx.at(input1_index).shape(); + + UNUSED_RELEASE(num); + UNUSED_RELEASE(axis); + UNUSED_RELEASE(output_rank); + + assert(num == static_cast<int32_t>(node.getInputs().size())); + assert(axis >= -output_rank && axis < output_rank); + for (const auto &index : node.getInputs()) + { + UNUSED_RELEASE(index); + assert(input_shape == _ctx.at(index).shape()); + } +} + +void OperationValidator::visit(const ir::operation::ReduceMin &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + const auto ifm_index{node.getInputs().at(ir::operation::ReduceMin::Input::INPUT)}; + const auto &axes = node.param().axes; + + auto ifm_shape = _ctx.at(ifm_index).shape(); + auto ofm_shape = _ctx.at(ofm_index).shape(); + + UNUSED_RELEASE(ifm_shape); + UNUSED_RELEASE(ofm_shape); + UNUSED_RELEASE(axes); + + assert(ifm_shape.rank() <= 4); + assert(ofm_shape.rank() <= ifm_shape.rank()); + + // NOTE For the 4-dimensions, if the rank of input and output are different, this runtime only + // supports cases reducing height and width or reducing depth. + // TODO We have to support all cases of dimensions up to 4. + // For correct permuting, we have to set output's shape to be equal in dimension position of the + // input. But the positions of the same dimensions in the input and output may be set differently. + // For example {2,3,4,5}(input's shape) can be reduced to {3,5}(output's shape). The original + // output shape should be {1,3,1,5}, but real output shape may be {3,5}. If you simply try to + // extend it in 4 dimensions, it should be {1,1,3,5}. + // Even if output shape is changed to {1,3,1,5}, there is another problem. It is that shape of + // output tensor used at next operation is changed to {1,3,1,5} after this operation even if the + // next operation is not desired. + if (ifm_shape.rank() == 4 && ifm_shape.rank() != ofm_shape.rank()) + { + if (ofm_shape.rank() == 2) + { + // Reducing HW + assert(ifm_shape.dim(0) == ofm_shape.dim(0) && ifm_shape.dim(3) == ofm_shape.dim(1)); + } + else if (ofm_shape.rank() == 3) + { + // Reducing C or + // (Reducing H and C(ifm and ofm) == 1) or (Reducing W and C(ifm and ofm) == 1) + assert((ifm_shape.dim(0) == ofm_shape.dim(0) && ifm_shape.dim(1) == ofm_shape.dim(1) && + ifm_shape.dim(2) == ofm_shape.dim(2)) || + (ifm_shape.dim(0) == ofm_shape.dim(0) && + (ifm_shape.dim(1) == ofm_shape.dim(1) || ifm_shape.dim(2) == ofm_shape.dim(1)) && + ifm_shape.dim(3) == 1 && ofm_shape.dim(2) == 1)); + } + } +} + +void OperationValidator::visit(const ir::operation::LSTM &node) +{ + // NOTE This validation is for static rnn(non-dynamic shape), but not for dynamic rnn + // TODO Support dynamic rnn + const auto scratch_buffer_index{ + node.getOutputs().at(ir::operation::LSTM::Output::SCRATCH_BUFFER)}; + const auto output_state_out_index{ + node.getOutputs().at(ir::operation::LSTM::Output::OUTPUT_STATE_OUT)}; + const auto cell_state_out_index{ + node.getOutputs().at(ir::operation::LSTM::Output::CELL_STATE_OUT)}; + const auto output_index{node.getOutputs().at(ir::operation::LSTM::Output::OUTPUT)}; + + const auto input_index{node.getInputs().at(ir::operation::LSTM::Input::INPUT)}; + const auto input_to_input_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_INPUT_WEIGHTS)}; + const auto input_to_forget_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_FORGET_WEIGHTS)}; + const auto input_to_cell_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_CELL_WEIGHTS)}; + const auto input_to_output_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_OUTPUT_WEIGHTS)}; + const auto recurrent_to_input_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_INPUT_WEIGHTS)}; + const auto recurrent_to_forget_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_FORGET_WEIGHTS)}; + const auto recurrent_to_cell_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_CELL_WEIGHTS)}; + const auto recurrent_to_output_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_OUTPUT_WEIGHTS)}; + const auto cell_to_input_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::CELL_TO_INPUT_WEIGHTS)}; + const auto cell_to_forget_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::CELL_TO_FORGET_WEIGHTS)}; + const auto cell_to_output_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::CELL_TO_OUTPUT_WEIGHTS)}; + const auto input_gate_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_GATE_BIAS)}; + const auto forget_gate_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::FORGET_GATE_BIAS)}; + const auto cell_bias_index{node.getInputs().at(ir::operation::LSTM::Input::CELL_BIAS)}; + const auto output_gate_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::OUTPUT_GATE_BIAS)}; + const auto projection_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::PROJECTION_WEIGHTS)}; + const auto projection_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::PROJECTION_BIAS)}; + const auto output_state_in_index{ + node.getInputs().at(ir::operation::LSTM::Input::OUTPUT_STATE_IN)}; + const auto cell_state_in_index{node.getInputs().at(ir::operation::LSTM::Input::CELL_STATE_IN)}; + + UNUSED_RELEASE(scratch_buffer_index); + UNUSED_RELEASE(output_state_out_index); + UNUSED_RELEASE(cell_state_out_index); + UNUSED_RELEASE(output_index); + + UNUSED_RELEASE(input_index); + UNUSED_RELEASE(input_to_input_weights_index); + UNUSED_RELEASE(input_to_forget_weights_index); + UNUSED_RELEASE(input_to_cell_weights_index); + UNUSED_RELEASE(input_to_output_weights_index); + UNUSED_RELEASE(recurrent_to_input_weights_index); + UNUSED_RELEASE(recurrent_to_forget_weights_index); + UNUSED_RELEASE(recurrent_to_cell_weights_index); + UNUSED_RELEASE(recurrent_to_output_weights_index); + UNUSED_RELEASE(cell_to_input_weights_index); + UNUSED_RELEASE(cell_to_forget_weights_index); + UNUSED_RELEASE(cell_to_output_weights_index); + UNUSED_RELEASE(input_gate_bias_index); + UNUSED_RELEASE(forget_gate_bias_index); + UNUSED_RELEASE(cell_bias_index); + UNUSED_RELEASE(output_gate_bias_index); + UNUSED_RELEASE(projection_weights_index); + UNUSED_RELEASE(projection_bias_index); + UNUSED_RELEASE(output_state_in_index); + UNUSED_RELEASE(cell_state_in_index); + + assert(_ctx.at(scratch_buffer_index).shape().rank() == 2 && + _ctx.at(output_state_out_index).shape().rank() == 2 && + _ctx.at(cell_state_out_index).shape().rank() == 2 && + _ctx.at(output_index).shape().rank() == 2 && _ctx.at(input_index).shape().rank() == 2 && + _ctx.at(input_to_input_weights_index).shape().rank() == 2 && + _ctx.at(input_to_forget_weights_index).shape().rank() == 2 && + _ctx.at(input_to_cell_weights_index).shape().rank() == 2 && + _ctx.at(input_to_output_weights_index).shape().rank() == 2 && + _ctx.at(recurrent_to_input_weights_index).shape().rank() == 2 && + _ctx.at(recurrent_to_forget_weights_index).shape().rank() == 2 && + _ctx.at(recurrent_to_cell_weights_index).shape().rank() == 2 && + _ctx.at(recurrent_to_output_weights_index).shape().rank() == 2 && + _ctx.at(projection_weights_index).shape().rank() == 2 && + _ctx.at(output_state_in_index).shape().rank() == 2 && + _ctx.at(cell_state_in_index).shape().rank() == 2); + + assert(_ctx.at(cell_to_input_weights_index).shape().rank() == 1 && + _ctx.at(cell_to_forget_weights_index).shape().rank() == 1 && + _ctx.at(cell_to_output_weights_index).shape().rank() == 1 && + _ctx.at(input_gate_bias_index).shape().rank() == 1 && + _ctx.at(forget_gate_bias_index).shape().rank() == 1 && + _ctx.at(cell_bias_index).shape().rank() == 1 && + _ctx.at(output_gate_bias_index).shape().rank() == 1 && + _ctx.at(projection_bias_index).shape().rank() == 1); + + // CIFG assertion + assert((_ctx.at(input_to_input_weights_index).shape().dim(0) == 0 && + _ctx.at(input_to_input_weights_index).shape().dim(1) == 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(0) == 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(1) == 0 && + _ctx.at(input_gate_bias_index).shape().dim(0) == 0 && + _ctx.at(cell_to_input_weights_index).shape().dim(0) == 0) || + (_ctx.at(input_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(input_to_input_weights_index).shape().dim(1) != 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(1) != 0 && + _ctx.at(input_gate_bias_index).shape().dim(0) != 0)); + + // Peephole assertion + assert((_ctx.at(cell_to_forget_weights_index).shape().dim(0) == 0 && + _ctx.at(cell_to_output_weights_index).shape().dim(0) == 0) || + (_ctx.at(cell_to_forget_weights_index).shape().dim(0) != 0 && + _ctx.at(cell_to_output_weights_index).shape().dim(0) != 0)); + + bool has_input_to_input_weights = _ctx.at(input_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(input_to_input_weights_index).shape().dim(1) != 0; + bool has_recurrent_to_input_weights = + _ctx.at(recurrent_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(1) != 0; + bool has_input_gate_bias = _ctx.at(input_gate_bias_index).shape().dim(0) != 0; + bool has_cell_to_input_weights = _ctx.at(cell_to_input_weights_index).shape().dim(0) != 0; + bool has_cell_to_forget_weights = _ctx.at(cell_to_forget_weights_index).shape().dim(0) != 0; + bool has_cell_to_output_weights = _ctx.at(cell_to_output_weights_index).shape().dim(0) != 0; + bool has_projection_weights = _ctx.at(projection_weights_index).shape().dim(0) != 0 && + _ctx.at(projection_weights_index).shape().dim(1) != 0; + bool has_projection_bias = _ctx.at(projection_bias_index).shape().dim(0); + + // NOTE The cell_to_input_weights do not exist in non-peephole although regular LSTM(non-CIFG). + // true: no CIFG + // false: CIFG + bool has_cifg_param = has_input_to_input_weights && has_recurrent_to_input_weights; + + // NOTE The cell_to_input_weights do not exist in regular CIFG although peephole. + // true: peephole + // false: no peephole + bool has_peephole_param = has_cell_to_forget_weights && has_cell_to_output_weights; + + // NOTE The projection weights may have data but the projection bias may not. + bool has_projection_param = has_projection_weights; + + UNUSED_RELEASE(has_input_to_input_weights); + UNUSED_RELEASE(has_recurrent_to_input_weights); + UNUSED_RELEASE(has_input_gate_bias); + UNUSED_RELEASE(has_cell_to_input_weights); + UNUSED_RELEASE(has_cell_to_forget_weights); + UNUSED_RELEASE(has_cell_to_output_weights); + UNUSED_RELEASE(has_projection_weights); + UNUSED_RELEASE(has_projection_bias); + UNUSED_RELEASE(has_cifg_param); + UNUSED_RELEASE(has_peephole_param); + UNUSED_RELEASE(has_projection_param); + + const auto batch_size = _ctx.at(input_index).shape().dim(0); + UNUSED_RELEASE(batch_size); + assert(batch_size == _ctx.at(output_state_in_index).shape().dim(0) && + batch_size == _ctx.at(cell_state_in_index).shape().dim(0) && + batch_size == _ctx.at(scratch_buffer_index).shape().dim(0) && + batch_size == _ctx.at(output_state_out_index).shape().dim(0) && + batch_size == _ctx.at(cell_state_out_index).shape().dim(0) && + batch_size == _ctx.at(output_index).shape().dim(0)); + + const auto input_size = _ctx.at(input_index).shape().dim(1); + UNUSED_RELEASE(input_size); + assert(input_size == _ctx.at(input_to_forget_weights_index).shape().dim(1) && + input_size == _ctx.at(input_to_cell_weights_index).shape().dim(1) && + input_size == _ctx.at(input_to_output_weights_index).shape().dim(1)); + + const auto num_units = _ctx.at(cell_state_out_index).shape().dim(1); + UNUSED_RELEASE(num_units); + assert(num_units == _ctx.at(input_to_forget_weights_index).shape().dim(0) && + num_units == _ctx.at(input_to_cell_weights_index).shape().dim(0) && + num_units == _ctx.at(input_to_output_weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_to_forget_weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_to_cell_weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_to_output_weights_index).shape().dim(0) && + num_units == _ctx.at(forget_gate_bias_index).shape().dim(0) && + num_units == _ctx.at(cell_bias_index).shape().dim(0) && + num_units == _ctx.at(output_gate_bias_index).shape().dim(0) && + num_units == _ctx.at(cell_state_in_index).shape().dim(1) && + (((num_units * 3) == _ctx.at(scratch_buffer_index).shape().dim(1)) || + ((num_units * 4) == _ctx.at(scratch_buffer_index).shape().dim(1)))); + + const auto output_size = _ctx.at(output_index).shape().dim(1); + UNUSED_RELEASE(output_size); + assert(output_size == _ctx.at(recurrent_to_forget_weights_index).shape().dim(1) && + output_size == _ctx.at(recurrent_to_cell_weights_index).shape().dim(1) && + output_size == _ctx.at(recurrent_to_output_weights_index).shape().dim(1) && + output_size == _ctx.at(output_state_in_index).shape().dim(1) && + output_size == _ctx.at(output_state_out_index).shape().dim(1)); + + if (has_cifg_param) + { + assert(input_size == _ctx.at(input_to_input_weights_index).shape().dim(1)); + assert(num_units == _ctx.at(input_to_input_weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_to_input_weights_index).shape().dim(0) && + (num_units == _ctx.at(cell_to_input_weights_index).shape().dim(0) || + _ctx.at(cell_to_input_weights_index).shape().dim(0) == 0 /* non-peephole */) && + num_units == _ctx.at(input_gate_bias_index).shape().dim(0)); + assert(output_size == _ctx.at(recurrent_to_input_weights_index).shape().dim(1)); + assert(has_input_to_input_weights && has_recurrent_to_input_weights && has_input_gate_bias); + if (has_cell_to_input_weights) + { + // NOTE The cell_to_input_weights exist only in case of non-CIFG and peephole. + assert(has_peephole_param); + } + assert(_ctx.at(scratch_buffer_index).shape().dim(1) == num_units * 4); + } + else + { + assert(_ctx.at(scratch_buffer_index).shape().dim(1) == num_units * 3); + } + + if (has_peephole_param) + { + assert(num_units == _ctx.at(cell_to_forget_weights_index).shape().dim(0) && + num_units == _ctx.at(cell_to_output_weights_index).shape().dim(0) && + (num_units == _ctx.at(cell_to_input_weights_index).shape().dim(0) || + _ctx.at(cell_to_input_weights_index).shape().dim(0) == 0 /* CIFG */)); + } + + if (has_projection_param) + { + assert(num_units == _ctx.at(projection_weights_index).shape().dim(1)); + assert(output_size == _ctx.at(projection_weights_index).shape().dim(0)); + if (has_projection_bias) + { + assert(output_size == _ctx.at(projection_bias_index).shape().dim(0)); + } + } +} + +void OperationValidator::visit(const ir::operation::Unpack &node) +{ + const auto input_index{node.getInputs().at(ir::operation::Unpack::Input::INPUT)}; + const auto num{node.param().num}; + const auto axis{node.param().axis}; + + const auto &input_shape = _ctx.at(input_index).shape(); + const auto input_rank = static_cast<int32_t>(input_shape.rank()); + + UNUSED_RELEASE(num); + UNUSED_RELEASE(axis); + UNUSED_RELEASE(input_rank); + + assert(num == static_cast<int32_t>(node.getOutputs().size())); + assert(axis >= -input_rank && axis < input_rank); +} + +void OperationValidator::visit(const ir::operation::Pad &node) +{ + const auto input_index{node.getInputs().at(ir::operation::Pad::Input::INPUT)}; + const auto pad_index{node.getInputs().at(ir::operation::Pad::Input::PAD)}; + const auto output_index{node.getInputs().at(0)}; + + const auto &pad_shape = _ctx.at(pad_index).shape(); + const auto input_rank = static_cast<int32_t>(_ctx.at(input_index).shape().rank()); + + UNUSED_RELEASE(pad_shape); + UNUSED_RELEASE(input_rank); + UNUSED_RELEASE(output_index); + + assert(pad_shape.rank() == 2); + assert(pad_shape.dim(0) == input_rank); + assert(pad_shape.dim(1) == 2); + assert(_ctx.at(pad_index).typeInfo().type() == ir::DataType::INT32); + assert(_ctx.at(input_index).shape().rank() == _ctx.at(output_index).shape().rank()); +} + +void OperationValidator::visit(const ir::operation::Min &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(ir::operation::Min::Input::LHS)}; + const auto rhs_index{node.getInputs().at(ir::operation::Min::Input::RHS)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(lhs_index); + UNUSED_RELEASE(rhs_index); + + assert(_ctx.at(lhs_index).typeInfo().type() == _ctx.at(rhs_index).typeInfo().type()); + assert(_ctx.at(lhs_index).typeInfo().type() == _ctx.at(output_index).typeInfo().type()); +} + +void OperationValidator::visit(const ir::operation::Max &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(ir::operation::Max::Input::LHS)}; + const auto rhs_index{node.getInputs().at(ir::operation::Max::Input::RHS)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(lhs_index); + UNUSED_RELEASE(rhs_index); + + assert(_ctx.at(lhs_index).typeInfo().type() == _ctx.at(rhs_index).typeInfo().type()); + assert(_ctx.at(lhs_index).typeInfo().type() == _ctx.at(output_index).typeInfo().type()); +} + +void OperationValidator::visit(const ir::operation::StridedSlice &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::StridedSlice::Input::INPUT)}; + const auto starts_index{node.getInputs().at(ir::operation::StridedSlice::Input::STARTS)}; + const auto ends_index{node.getInputs().at(ir::operation::StridedSlice::Input::ENDS)}; + const auto strides_index{node.getInputs().at(ir::operation::StridedSlice::Input::STRIDES)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + UNUSED_RELEASE(starts_index); + UNUSED_RELEASE(ends_index); + UNUSED_RELEASE(strides_index); + + assert(_ctx.at(output_index).typeInfo().type() == _ctx.at(input_index).typeInfo().type()); + assert(_ctx.at(input_index).shape().rank() <= 4); +} + +void OperationValidator::visit(const ir::operation::Split &node) +{ + const auto input_index{node.getInputs().at(ir::operation::Split::Input::INPUT)}; + const auto &num_splits = node.param().num_splits; + const auto &input_rank = node.param().rank; + const auto &axis = node.param().axis < 0 ? node.param().axis + input_rank : node.param().axis; + + UNUSED_RELEASE(input_index); + UNUSED_RELEASE(num_splits); + UNUSED_RELEASE(input_rank); + UNUSED_RELEASE(axis); + + assert(num_splits > 0 && num_splits <= 0xFFFF); + assert(axis >= 0 && axis < input_rank); + assert(_ctx.at(input_index).shape().dim(axis) % num_splits == 0); + assert(node.getOutputs().size() == static_cast<uint32_t>(num_splits)); +} + +void OperationValidator::visit(const ir::operation::Sin &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void OperationValidator::visit(const ir::operation::RSQRT &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void OperationValidator::visit(const ir::operation::Shape &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + UNUSED_RELEASE(output_index); + UNUSED_RELEASE(input_index); + + assert(_ctx.at(output_index).shape().rank() == 1); +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/OperationValidator.h b/runtime/onert/core/src/compiler/OperationValidator.h new file mode 100644 index 000000000..10b0525c1 --- /dev/null +++ b/runtime/onert/core/src/compiler/OperationValidator.h @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#ifndef __ONERT_COMPILER_OPERATION_VALIDATOR_H__ +#define __ONERT_COMPILER_OPERATION_VALIDATOR_H__ + +#include "ir/Layout.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +class Graph; +class Operands; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace compiler +{ + +class OperationValidator : public ir::OperationVisitor +{ +public: + OperationValidator(void) = delete; + OperationValidator(const ir::Graph &graph); + +public: + void operator()(); + +public: + void visit(const ir::operation::Abs &node) override; + void visit(const ir::operation::AvgPool2D &node) override; + void visit(const ir::operation::BatchToSpaceND &node) override; + void visit(const ir::operation::Cast &node) override; + void visit(const ir::operation::Comparison &node) override; + void visit(const ir::operation::Softmax &node) override; + void visit(const ir::operation::InstanceNorm &node) override; + void visit(const ir::operation::Permute &node) override; + void visit(const ir::operation::ReduceSum &node) override; + void visit(const ir::operation::Transpose &node) override; + void visit(const ir::operation::ReduceMax &node) override; + void visit(const ir::operation::RNN &node) override; + void visit(const ir::operation::SpaceToBatchND &node) override; + void visit(const ir::operation::SpaceToDepth &node) override; + void visit(const ir::operation::EmbeddingLookup &node) override; + void visit(const ir::operation::Exp &node) override; + void visit(const ir::operation::Floor &node) override; + void visit(const ir::operation::HashtableLookup &node) override; + void visit(const ir::operation::TransposeConv &node) override; + void visit(const ir::operation::Gather &node) override; + void visit(const ir::operation::Dequantize &node) override; + void visit(const ir::operation::Mean &node) override; + void visit(const ir::operation::DepthToSpace &node) override; + void visit(const ir::operation::Pack &node) override; + void visit(const ir::operation::ReduceMin &node) override; + void visit(const ir::operation::LSTM &node) override; + void visit(const ir::operation::Unpack &node) override; + void visit(const ir::operation::Pad &node) override; + void visit(const ir::operation::Min &node) override; + void visit(const ir::operation::Max &node) override; + void visit(const ir::operation::StridedSlice &node) override; + void visit(const ir::operation::Split &node) override; + void visit(const ir::operation::Sin &node) override; + void visit(const ir::operation::RSQRT &node) override; + void visit(const ir::operation::Shape &node) override; + +private: + // TODO Remove _ctx field + const ir::Graph &_graph; + const ir::Operands &_ctx; + ir::Layout _current_op_seq_layout; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_OPERATION_VALIDATOR_H__ diff --git a/runtime/onert/core/src/compiler/ParamChecker.cc b/runtime/onert/core/src/compiler/ParamChecker.cc new file mode 100644 index 000000000..c4f80f087 --- /dev/null +++ b/runtime/onert/core/src/compiler/ParamChecker.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 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 "ParamChecker.h" + +#include "ir/Graph.h" + +namespace onert +{ +namespace compiler +{ + +void ParamChecker::operator()() +{ + _model->operations().iterate( + [&](const ir::OperationIndex &, const ir::Operation &node) { node.accept(*this); }); +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ParamChecker.h b/runtime/onert/core/src/compiler/ParamChecker.h new file mode 100644 index 000000000..61429d521 --- /dev/null +++ b/runtime/onert/core/src/compiler/ParamChecker.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file ParamChecker.h + * @brief This file contains ParamChecker to check\n + * operations' parameters are compilable at machine independent phase\n + * ex) Check param is constant + */ +#ifndef __ONERT_COMPILER_PARAM_CHECKER_H__ +#define __ONERT_COMPILER_PARAM_CHECKER_H__ + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +class Graph; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace compiler +{ + +class ParamChecker : public ir::OperationVisitor +{ +public: + /** + * @brief Construct a new Param Checker object (deleted) + */ + ParamChecker(void) = delete; + /** + * @brief Construct a new Param Checker object + * @param[in] model Graph model to check + */ + ParamChecker(std::shared_ptr<ir::Graph> model) : _model{model} {} + +public: + /** + * @brief Run parameter analysis + */ + void operator()(); + /** + * @brief Return analysis result if model have non-const parameter + * @return @c true if there is non-const parameter, otherwise @c false + */ + bool haveNoneConstParam(void) { return _nonConstParam; } + +private: + const std::shared_ptr<ir::Graph> _model; + bool _nonConstParam{false}; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_OPERATION_VALIDATOR_H__ diff --git a/runtime/onert/core/src/dumper/dot/DotBuilder.cc b/runtime/onert/core/src/dumper/dot/DotBuilder.cc new file mode 100644 index 000000000..2a3a4af22 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotBuilder.cc @@ -0,0 +1,83 @@ +/* + * 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 "DotBuilder.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +// DotDumper +DotBuilder::DotBuilder() {} + +void DotBuilder::update(const Node &node_info) +{ + add(node_info); + for (auto edge : node_info.edges()) + { + addEdge(node_info, *edge); + } +} + +void DotBuilder::addOpSequence(const DotOpSequenceInfo &op_sequence_info) +{ + _dot << "op_sequence cluster_" << op_sequence_info.index().value() << " {\n"; + _dot << " label=\"" << op_sequence_info.label() << "\";\n"; + _dot << " style=filled;\n"; + _dot << " color=lightgrey;\n"; + _dot << " "; + for (auto op : op_sequence_info.operations()) + { + _dot << "operation" << op.value() << "; "; + } + for (auto op : op_sequence_info.operands()) + { + _dot << "operand" << op.value() << "; "; + } + _dot << "\n"; + _dot << "}\n"; +} + +void DotBuilder::writeDot(std::ostream &os) +{ + os << "digraph D {\n" + << _dot.str() << "\n" + << "}\n"; +} + +void DotBuilder::add(const Node &node) +{ + _dot << node.id(); + std::stringstream ss; + _dot << "["; + for (auto attr : node.attributes()) + { + _dot << attr.first << "=\"" << attr.second << "\" "; + } + _dot << "];\n"; +} + +void DotBuilder::addEdge(const Node &node1, const Node &node2) +{ + _dot << node1.id() << " -> " << node2.id() << ";\n"; +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/DotBuilder.h b/runtime/onert/core/src/dumper/dot/DotBuilder.h new file mode 100644 index 000000000..24a76533d --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotBuilder.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef __ONERT_DUMPER_DOT_DOT_BUILDER_H__ +#define __ONERT_DUMPER_DOT_DOT_BUILDER_H__ + +#include <sstream> + +#include "ir/Index.h" +#include "ir/Operation.h" +#include "ir/Operand.h" + +#include "OperationNode.h" +#include "OperandNode.h" +#include "DotOpSequenceInfo.h" + +using Operation = onert::ir::Operation; +using Object = onert::ir::Operand; + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +class DotBuilder +{ +public: + DotBuilder(); + +public: + void update(const Node &dotinfo); + void addOpSequence(const DotOpSequenceInfo &op_sequence_info); + + void writeDot(std::ostream &os); + +private: + void add(const Node &dotinfo); + void addEdge(const Node &dotinfo1, const Node &dotinfo2); + + std::stringstream _dot; +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_DUMPER_DOT_DOT_BUILDER_H__ diff --git a/runtime/onert/core/src/dumper/dot/DotDumper.cc b/runtime/onert/core/src/dumper/dot/DotDumper.cc new file mode 100644 index 000000000..9bd8ed0ef --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotDumper.cc @@ -0,0 +1,199 @@ +/* + * 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 <fstream> +#include <unordered_map> + +#include "DotDumper.h" +#include "DotBuilder.h" +#include "DotOpSequenceInfo.h" +#include "ir/OpSequence.h" +#include "ir/OperationIndexMap.h" +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "compiler/BackendManager.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +void DotDumper::dump(const std::string &tag) +{ + if (_level == Level::OFF) + { + return; + } + + onert::dumper::dot::DotBuilder dot_builder; + + auto &operations = _graph.operations(); + auto &operands = _graph.operands(); + + ir::OperationIndexMap<std::unique_ptr<Operation>> operation_nodes; + std::unordered_map<ir::OperandIndex, std::unique_ptr<Operand>> operand_nodes; + + operations.iterate([&](const ir::OperationIndex &index, const ir::Operation &op) { + auto node = std::make_unique<Operation>(index, op); + + for (auto output : op.getOutputs()) + { + using onert::dumper::dot::Operand; + auto child = std::make_shared<Operand>(output, Operand::Type::MODEL_OUTPUT); + node->addEdge(child); + } + + operation_nodes.emplace(index, std::move(node)); + }); + + auto backend_to_fillcolor = [](const backend::Backend *backend) { + static const auto map = []() { + std::unordered_map<const backend::Backend *, std::string> ret; + uint32_t index = 1; // Start from 1 to avoid 0(red) which is too dark :( + for (const auto backend : compiler::BackendManager::get().getAll()) + { + ret.emplace(backend, Node::BG_COLORS[index]); + index = (index + 1) % (sizeof(Node::BG_COLORS) / sizeof(Node::BG_COLORS[0])); + } + return ret; + }(); + + auto itr = map.find(backend); + if (itr == map.end()) + { + return Node::DEFAULT_FILLCOLOR; + } + else + { + return itr->second; + } + }; + + util::Set<ir::OperandIndex> shown_operand_set; + + operands.iterate([&](const ir::OperandIndex &index, const ir::Operand &object) { + bool showing_cond = false; + if (_level == Level::ALL) + { + showing_cond = true; + } + else + { + showing_cond = !object.isConstant(); + } + if (object.isConstant() || _graph.getInputs().contains(index)) + { + showing_cond = showing_cond && (object.getUses().size() > 0); + } + if (showing_cond) + { + shown_operand_set.add(index); + + auto type = [&]() { + using onert::dumper::dot::Operand; + if (_graph.getInputs().contains(index)) + return Operand::Type::MODEL_INPUT; + if (_graph.getOutputs().contains(index)) + return Operand::Type::MODEL_OUTPUT; + return Operand::Type::INTERNAL; + }(); + + auto node = std::make_unique<Operand>(index, type); + + { + // Display LowerInfo attributes + std::string label = std::to_string(index.value()); + std::string fillcolor = ""; + if (_lowered_graph) + { + auto lower_info = _lowered_graph->getLowerInfo(index); + const auto &def_factors = lower_info->def_factors(); + if (def_factors.size() > 0) + { + label += "\\n["; + label += def_factors.getOnlyElement().backend()->config()->id(); + label += "]"; + + fillcolor = backend_to_fillcolor(lower_info->def_factors().getOnlyElement().backend()); + } + } + node->setAttribute("label", label); + node->setAttribute("fillcolor", fillcolor); + } + + for (auto operation_index : object.getUses().list()) + { + auto &operation = operations.at(operation_index); + auto child = std::make_shared<Operation>(operation_index, operation); + node->addEdge(child); + } + + operand_nodes.emplace(index, std::move(node)); + } + }); + + if (_lowered_graph) + { + const auto &op_seqs = _lowered_graph->op_seqs(); + op_seqs.iterate([&](const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq) { + const auto lower_info = _lowered_graph->getLowerInfo(index); + auto fillcolor = backend_to_fillcolor(lower_info->backend()); + std::string label = + std::to_string(index.value()) + " [" + lower_info->backend()->config()->id() + "]"; + DotOpSequenceInfo op_sequence_info{index, op_seq, shown_operand_set}; + op_sequence_info.label(label); + op_sequence_info.fillcolor(fillcolor); + dot_builder.addOpSequence(op_sequence_info); + + // Set fillcolor of all operations in the op_seq + for (const auto &op : op_seq.operations()) + { + auto found = operation_nodes.find(op.index); + if (found != operation_nodes.end()) + { + auto &&op = found->second; + op->setAttribute("fillcolor", fillcolor); + } + } + }); + } + + for (const auto &e : operation_nodes) + dot_builder.update(*e.second); + for (const auto &e : operand_nodes) + dot_builder.update(*e.second); + + // Dump to file + { + std::string file_name; + file_name += tag; + file_name += ".dot"; + std::filebuf fb; + + fb.open(file_name, std::ios::out); + std::ostream os(&fb); + + dot_builder.writeDot(os); + + fb.close(); + } +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/DotDumper.h b/runtime/onert/core/src/dumper/dot/DotDumper.h new file mode 100644 index 000000000..668785a81 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotDumper.h @@ -0,0 +1,69 @@ +/* + * 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 "ir/Graph.h" +#include "ir/LoweredGraph.h" + +#ifndef __ONERT_DUMPER_DOT_DOT_DUMPER_H__ +#define __ONERT_DUMPER_DOT_DOT_DUMPER_H__ + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +class DotDumper +{ +public: + enum Level + { + OFF = 0, //< Do not dump + ALL_BUT_CONSTANTS = 1, //< Emit all operations and operands but constants + ALL = 2 //< Emit all operations and operands + }; + +public: + DotDumper(const ir::Graph &graph, Level level) + : _lowered_graph{nullptr}, _graph(graph), _level{level} + { + } + DotDumper(const ir::LoweredGraph *lowered_graph, Level level) + : _lowered_graph{lowered_graph}, _graph(_lowered_graph->graph()), _level{level} + { + } + +public: + /** + * @brief Dump to dot file as tag name if "GRAPH_DOT_DUMP" is set + * + * @param[in] tag The name of dot file that would be created + * @return N/A + */ + void dump(const std::string &tag); + +private: + const ir::LoweredGraph *_lowered_graph; + const ir::Graph &_graph; + Level _level; +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_DUMPER_DOT_DOT_DUMPER_H__ diff --git a/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.cc b/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.cc new file mode 100644 index 000000000..48dafc834 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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 "DotOpSequenceInfo.h" + +#include <sstream> + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +DotOpSequenceInfo::DotOpSequenceInfo(const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq, + const util::Set<ir::OperandIndex> &shown_operands) + : _index{index} +{ + for (const auto &element : op_seq.operations()) + { + _operations.insert(element.index); + for (auto o : element.node->getInputs()) + { + // Must be a shown operand, not op_seq's inputs + if (shown_operands.contains(o) && !op_seq.getInputs().contains(o)) + { + _operands.insert(o); + } + } + for (auto o : element.node->getOutputs()) + { + // Must be a shown operand, not op_seq's inputs + if (shown_operands.contains(o) && !op_seq.getOutputs().contains(o)) + { + _operands.insert(o); + } + } + } +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.h b/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.h new file mode 100644 index 000000000..c30626cbf --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotOpSequenceInfo.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_CORE_DUMPER_DOT_DOT_SUBGRAPH_INFO_H__ +#define __ONERT_CORE_DUMPER_DOT_DOT_SUBGRAPH_INFO_H__ + +#include <unordered_set> + +#include "ir/Index.h" +#include "ir/OpSequence.h" +#include "util/Set.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +class DotOpSequenceInfo +{ +public: + DotOpSequenceInfo(const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq, + const util::Set<ir::OperandIndex> &shown_operands); + + ir::OpSequenceIndex index() const { return _index; } + std::string label() const { return _label; } + void label(const std::string &val) { _label = val; } + std::string fillcolor() const { return _fillcolor; } + void fillcolor(const std::string &val) { _fillcolor = val; } + const std::unordered_set<ir::OperationIndex> &operations() const { return _operations; } + const std::unordered_set<ir::OperandIndex> &operands() const { return _operands; } + +private: + ir::OpSequenceIndex _index; + std::string _label; + std::string _fillcolor; + std::unordered_set<ir::OperationIndex> _operations; + std::unordered_set<ir::OperandIndex> _operands; +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_CORE_DUMPER_DOT_DOT_SUBGRAPH_INFO_H__ diff --git a/runtime/onert/core/src/dumper/dot/Node.cc b/runtime/onert/core/src/dumper/dot/Node.cc new file mode 100644 index 000000000..85d6e67a4 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/Node.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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 "Node.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +const std::string Node::DEFAULT_COLORSCHEME = "x11"; +const std::string Node::DEFAULT_FILLCOLOR = "white"; +// RED, BLUE, GREEN, PURPLE, ORANGE, YELLOW, BROWN, PINK +const std::string Node::BG_COLORS[8] = {"1", "2", "3", "4", "5", "6", "7", "8"}; + +Node::Node(const std::string &id) : _id{id} +{ + // Set default values + _attributes["style"] = "filled"; + _attributes["colorscheme"] = DEFAULT_COLORSCHEME; + _attributes["fillcolor"] = DEFAULT_FILLCOLOR; +} + +void Node::setAttribute(const std::string &key, const std::string &val) { _attributes[key] = val; } + +std::string Node::getAttribute(const std::string &key) +{ + auto itr = _attributes.find(key); + if (itr == _attributes.end()) + { + return ""; + } + else + { + return itr->second; + } +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/Node.h b/runtime/onert/core/src/dumper/dot/Node.h new file mode 100644 index 000000000..a5d1d51a4 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/Node.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file Node.h + * @brief This file contains Node class + * @ingroup COM_AI_RUNTIME + * + */ + +#ifndef __ONERT_DUMPER_DOT_NODE_H__ +#define __ONERT_DUMPER_DOT_NODE_H__ + +#include <string> +#include <memory> +#include <vector> +#include <unordered_map> + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +enum BGCOLORS : int +{ + RED, + BLUE, + GREEN, + PUPLE, + ORANGE, + YELLOW, + BROWN, + PINK +}; + +/** + * @brief Class that represents a Node in "dot" format + * + */ +class Node +{ +public: + const static std::string DEFAULT_FILLCOLOR; + const static std::string DEFAULT_COLORSCHEME; + const static std::string BG_COLORS[8]; + +public: + /** + * @brief Destroy the Node object + * + */ + virtual ~Node() = default; + + /** + * @brief Construct a new Node object + * + * @param id + */ + Node(const std::string &id); + + /** + * @brief return id + * + * @return id + */ + std::string id() const { return _id; } + + /** + * @brief return attributes + * + * @return const reference of attributes object + */ + const std::unordered_map<std::string, std::string> &attributes() const { return _attributes; } + /** + * @brief Store an attribute with key-value pair + * + * @param[in] key attribute's key + * @param[in] val attribute's value that is associated with the key + */ + void setAttribute(const std::string &key, const std::string &val); + /** + * @brief Get the attributte value that is associated with key + * + * @param[in] key key of the attribute + * @return value that is associated with the key + */ + std::string getAttribute(const std::string &key); + + /** + * @brief Add an edge in the graph, which is an outgoing edge + * + * @param[in] dotinfo A node that the new edge will be connected to + */ + void addEdge(std::shared_ptr<Node> dotinfo) { _children.emplace_back(dotinfo); } + /** + * @brief Return list of edges + * + * @return Edges + */ + const std::vector<std::shared_ptr<Node>> &edges() const { return _children; } + +private: + std::string _id; + std::unordered_map<std::string, std::string> _attributes; + std::vector<std::shared_ptr<Node>> _children; +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_DUMPER_DOT_NODE_H__ diff --git a/runtime/onert/core/src/dumper/dot/OperandNode.cc b/runtime/onert/core/src/dumper/dot/OperandNode.cc new file mode 100644 index 000000000..5a6015ca9 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/OperandNode.cc @@ -0,0 +1,60 @@ +/* + * 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 <sstream> + +#include "OperandNode.h" +#include "ir/Graph.h" +#include "ir/operand/LowerInfo.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +const std::string Operand::INPUT_SHAPE = "doublecircle"; +const std::string Operand::OUTPUT_SHAPE = "doublecircle"; +const std::string Operand::OPERAND_SHAPE = "ellipse"; +const std::string Operand::BG_COLOR_SCHEME = "set18"; + +Operand::Operand(const ir::OperandIndex &index, Type type) + : Node{"operand" + std::to_string(index.value())} +{ + { + auto type_to_shape = [](Type type) { + switch (type) + { + case Type::MODEL_INPUT: + return INPUT_SHAPE; + case Type::MODEL_OUTPUT: + return OUTPUT_SHAPE; + case Type::UNDEFINED: + case Type::INTERNAL: + default: + return OPERAND_SHAPE; + } + }; + setAttribute("shape", type_to_shape(type)); + } + + setAttribute("colorscheme", BG_COLOR_SCHEME); +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/OperandNode.h b/runtime/onert/core/src/dumper/dot/OperandNode.h new file mode 100644 index 000000000..2e7cc5861 --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/OperandNode.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/** + * @file Operand.h + * @brief This file contains Operand + * @ingroup COM_AI_RUNTIME + * + */ + +#ifndef __ONERT_DUMPER_DOT_DOT_OPERAND_INFO_H__ +#define __ONERT_DUMPER_DOT_DOT_OPERAND_INFO_H__ + +#include <vector> + +#include "Node.h" +#include "ir/Operand.h" +#include "ir/Index.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +/** + * @brief Class that represents an Operand + * + */ +class Operand : public Node +{ +public: + enum class Type + { + UNDEFINED, + MODEL_INPUT, + MODEL_OUTPUT, + INTERNAL + }; + +public: + static const std::string INPUT_SHAPE; + static const std::string OUTPUT_SHAPE; + static const std::string OPERAND_SHAPE; + static const std::string BG_COLOR_SCHEME; + +public: + /** + * @brief Construct a new Operand Node object + * + * @param[in] index Operand index + * @param[in] type Operand type + * @param[in] lower_info Operand LowerInfo + */ + Operand(const ir::OperandIndex &index, Type type); + +private: + void addBackendLabel(); +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_DUMPER_DOT_DOT_OPERAND_INFO_H__ diff --git a/runtime/onert/core/src/dumper/dot/OperationNode.cc b/runtime/onert/core/src/dumper/dot/OperationNode.cc new file mode 100644 index 000000000..bee137e7c --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/OperationNode.cc @@ -0,0 +1,46 @@ +/* + * 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 <sstream> + +#include "OperationNode.h" +#include "ir/Graph.h" +#include "ir/operation/LowerInfo.h" +#include "backend/IConfig.h" +#include "backend/Backend.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +const std::string Operation::OPERATION_SHAPE = "rect"; +const std::string Operation::BG_COLOR_SCHEME = "pastel18"; + +Operation::Operation(const ir::OperationIndex &index, const ir::Operation &node) + : Node{"operation" + std::to_string(index.value())} +{ + setAttribute("label", std::to_string(index.value()) + " : " + node.name()); + setAttribute("shape", OPERATION_SHAPE); + setAttribute("colorscheme", BG_COLOR_SCHEME); + setAttribute("fillcolor", DEFAULT_FILLCOLOR); +} + +} // namespace dot +} // namespace dumper +} // namespace onert diff --git a/runtime/onert/core/src/dumper/dot/OperationNode.h b/runtime/onert/core/src/dumper/dot/OperationNode.h new file mode 100644 index 000000000..74a37d3fb --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/OperationNode.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +/** + * @file Operation.h + * @brief This file contains Operation + * @ingroup COM_AI_RUNTIME + * + */ + +#ifndef __ONERT_DUMPER_DOT_DOT_NODE_INFO_H__ +#define __ONERT_DUMPER_DOT_DOT_NODE_INFO_H__ + +#include "Node.h" +#include "ir/Operation.h" +#include "ir/Index.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +/** + * @brief Class that represents an Operation + * + */ +class Operation : public Node +{ +public: + static const std::string OPERATION_SHAPE; + static const std::string BG_COLOR_SCHEME; + +public: + /** + * @brief Construct a new Operation Node object + * + * @param[in] index operation index + * @param[in] node operation object + */ + Operation(const ir::OperationIndex &index, const ir::Operation &node); +}; + +} // namespace dot +} // namespace dumper +} // namespace onert + +#endif // __ONERT_DUMPER_DOT_DOT_NODE_INFO_H__ diff --git a/runtime/onert/core/src/exec/DataflowExecutor.cc b/runtime/onert/core/src/exec/DataflowExecutor.cc new file mode 100644 index 000000000..fbd76d3c5 --- /dev/null +++ b/runtime/onert/core/src/exec/DataflowExecutor.cc @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 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 "DataflowExecutor.h" + +#include <cassert> + +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +int64_t DataflowExecutor::calculateRank(const std::vector<ir::Element> &operations) +{ + int64_t rank = 0; + if (!_indexed_ranks) + { + return rank; + } + for (const auto &element : operations) + { + auto it = _indexed_ranks->find(element.index); + if (it == _indexed_ranks->end()) + { + assert(element.node->opcode() == ir::OpCode::Permute && operations.size() == 1); + // run Permute ASAP for next operations to be ready for other backends + return std::numeric_limits<int64_t>::max(); + } + else + { + rank += it->second; + } + } + return rank; +} + +void DataflowExecutor::emplaceToReadyJobs(const uint32_t &id) +{ + auto &job = _waiting_jobs[id]; + assert(job != nullptr); + auto &op_seq = _lowered_graph->op_seqs().at(_job_to_op_seq[job->index()]); + auto rank = calculateRank(op_seq.operations()); + _ready_jobs.emplace(rank, std::move(job)); +} + +void DataflowExecutor::notify(uint32_t finished_job_id) +{ + for (auto id : _output_info[finished_job_id]) + { + assert(_input_info[id] > 0); + auto count = --_input_info[id]; + if (count == 0) // No dependent jobs left, ready for execution + { + emplaceToReadyJobs(id); + } + } +} +bool DataflowExecutor::noWaitingJobs() +{ + return std::all_of(_waiting_jobs.begin(), _waiting_jobs.end(), + [](const std::unique_ptr<Job> &job) { return job == nullptr; }); +} + +DataflowExecutor::DataflowExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const backend::TensorBuilderSet &tensor_builders, + compiler::CodeMap &&code_map) + : ExecutorBase{std::move(lowered_graph), tensor_builders}, _code_map{std::move(code_map)}, + _profiling{false} +{ + VERBOSE(DataflowExecutor) << "Constructing Dataflow Executor" << std::endl; + + const auto &op_seqs = _lowered_graph->op_seqs(); + // Assign jobs convert OpSequenceIndex to job index(uint32_t) + uint32_t next_job_index = 0; + std::unordered_map<ir::OpSequenceIndex, uint32_t> op_seq_to_job; + op_seqs.iterate([&](const ir::OpSequenceIndex &op_seq_index, const ir::OpSequence &) { + VERBOSE(DataflowExecutor) << "Create a job #" << next_job_index << " with OpSequenceIndex " + << op_seq_index.value() << std::endl; + _finished_jobs.emplace_back( + std::make_unique<Job>(next_job_index, _code_map.at(op_seq_index).fn_seq.get())); + op_seq_to_job[op_seq_index] = next_job_index++; + }); + + _waiting_jobs.resize(next_job_index); + _output_info.resize(next_job_index); + _initial_input_info.resize(next_job_index, 0); + + op_seqs.iterate([&](const ir::OpSequenceIndex &op_seq_index, const ir::OpSequence &op_seq) { + auto job_index = op_seq_to_job[op_seq_index]; + for (auto output : op_seq.getOutputs()) + { + // Update output and input info + op_seqs.iterate( + [&](const ir::OpSequenceIndex &op_seq_cur_index, const ir::OpSequence &op_seq_cur) { + if (op_seq_cur.getInputs().contains(output)) + { + auto dep_index = op_seq_to_job[op_seq_cur_index]; + ++_initial_input_info[dep_index]; + _output_info[job_index].push_back(dep_index); + } + }); + } + }); + for (const auto &s : op_seq_to_job) + _job_to_op_seq.emplace(s.second, s.first); + + _input_info = _initial_input_info; +} + +void DataflowExecutor::executeImpl() +{ + assert(noWaitingJobs()); + + // Execution setup + _waiting_jobs.swap(_finished_jobs); // Move finished jobs to waiting jobs + + for (uint32_t i = 0; i < _waiting_jobs.size(); ++i) + { + if (_input_info[i] == 0) + { + emplaceToReadyJobs(i); + } + } + assert(!_ready_jobs.empty()); // Cannot begin if there is no initial jobs + + _subject.notifyModelBegin(this); + + while (!_ready_jobs.empty()) + { + auto job = std::move((_ready_jobs.begin())->second); + _ready_jobs.erase(_ready_jobs.begin()); + auto job_index = job->index(); + VERBOSE(DataflowExecutor) << "Run job #" << job_index << std::endl; + + auto op_seq_index = _job_to_op_seq[job_index]; + auto op_seq = &_lowered_graph->op_seqs().at(op_seq_index); + const backend::Backend *backend = + _lowered_graph->getLowerInfo()->operation.at(op_seq_index)->backend(); + + _subject.notifyJobBegin(this, op_seq, backend); + + if (_profiling) + job->fn()->runSync(); + else + job->run(); + + _subject.notifyJobEnd(this, op_seq, backend); + notify(job_index); + _finished_jobs[job_index] = std::move(job); + } + assert(noWaitingJobs()); + + _subject.notifyModelEnd(this); + + // Reset input info for the next execution + _input_info = _initial_input_info; +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/DataflowExecutor.h b/runtime/onert/core/src/exec/DataflowExecutor.h new file mode 100644 index 000000000..b49df4386 --- /dev/null +++ b/runtime/onert/core/src/exec/DataflowExecutor.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_DATAFLOW_EXECUTOR_H__ +#define __ONERT_EXEC_DATAFLOW_EXECUTOR_H__ + +#include <list> +#include <map> +#include <unordered_map> + +#include "exec/FunctionSequence.h" +#include "Job.h" +#include "ir/OperandIndexSequence.h" +#include "ir/Index.h" +#include <memory> +#include "exec/ExecutorBase.h" +#include "compiler/CodeMap.h" + +namespace onert +{ +namespace exec +{ + +class DataflowExecutor : public ExecutorBase +{ + +protected: + virtual void notify(uint32_t finished_job_id); + bool noWaitingJobs(); + +public: + /** + * @brief Constructs a DataflowExecutor object + * + * @param lowered_graph LoweredGraph object + * @param tensor_builders Tensor builders that are currently used + * @param code_map OpSequence and its code map + */ + DataflowExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const backend::TensorBuilderSet &tensor_builders, compiler::CodeMap &&code_map); + + void executeImpl() override; + void setProfilingMode(bool profiling) { _profiling = profiling; } + +protected: + int64_t calculateRank(const std::vector<ir::Element> &operations); + void emplaceToReadyJobs(const uint32_t &id); + +protected: + compiler::CodeMap _code_map; + /** + * @brief A vector of finished jobs for current execution + * After a run it has all the jobs of this execution for the next run + */ + std::vector<std::unique_ptr<Job>> _finished_jobs; + /** + * @brief A vector of waiting jobs for current execution + * All the jobs are moved from #_finished_jobs to it when start a run + */ + std::vector<std::unique_ptr<Job>> _waiting_jobs; + /** + * @brief Jobs' output info + * Used for notifying after finishing a job + */ + std::vector<std::list<uint32_t>> _output_info; + std::vector<uint32_t> _initial_input_info; + std::vector<uint32_t> _input_info; + /** + * @brief A collection of jobs that are ready for execution + * Jobs in it are ready to be scheduled. + * Ordered by priority from `_indexed_ranks` + */ + std::multimap<int64_t, std::unique_ptr<Job>, std::greater<int64_t>> _ready_jobs; + + /// @brief Which job runs which op and function. + std::unordered_map<uint32_t, ir::OpSequenceIndex> _job_to_op_seq; + bool _profiling; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_DATAFLOW_EXECUTOR_H__ diff --git a/runtime/onert/core/src/exec/ExecTime.cc b/runtime/onert/core/src/exec/ExecTime.cc new file mode 100644 index 000000000..6bf2744a9 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecTime.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 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 "exec/ExecTime.h" + +#include <fstream> +#include <cassert> +#include <limits> +#include <algorithm> + +namespace onert +{ +namespace exec +{ + +int64_t ExecTime::getOperationExecTime(const backend::Backend *backend, + const std::string &operation, bool quant, + uint32_t op_size) const +{ + auto found_backend = _measurements.find(backend); + if (found_backend == _measurements.end()) + return NOT_FOUND; // no execution time for this backend + + auto found_operation_with_type = found_backend->second.find(operation); + if (found_operation_with_type == found_backend->second.end()) + // no execution time for this operation + return NOT_FOUND; + + auto found_operation = found_operation_with_type->second.find(quant); + if (found_operation == found_operation_with_type->second.end()) + // no execution time for this operation + return NOT_FOUND; + + auto found_size = found_operation->second.find(op_size); + if (found_size != found_operation->second.end()) + return found_size->second; // found execution time + + // Try to interpolate + if (found_operation->second.size() < 2) + // not possible to do linear interpolation + return found_operation->second.begin()->second; + + // if we reach here, then this means, that there is no record, that is equal to op_size + auto upper_bound = found_operation->second.upper_bound(op_size); // > op_size + auto lower_bound = upper_bound; + + if (upper_bound == found_operation->second.end()) // all values <= op_size + { + upper_bound--; + lower_bound = upper_bound; + lower_bound--; + } + else if (upper_bound == found_operation->second.begin()) // all values > op_size + { + upper_bound++; + } + else // op_size between + { + lower_bound--; + } + + // Linear interpolation + const auto x0 = static_cast<int64_t>(lower_bound->first); // size + const auto x1 = static_cast<int64_t>(upper_bound->first); // size + const int64_t y0 = lower_bound->second; // time + const int64_t y1 = upper_bound->second; // time + const auto x = static_cast<int64_t>(op_size); + + int64_t interpolated_value = y0 + (x - x0) * (y1 - y0) / (x1 - x0); + + // In some cases ops with smaller inputs is executed slower than the one + // with larger inputs, more likely because of a backend's load difference + if (interpolated_value < 0 && x > x1) + { + return y0; + } + // It must be non-positive ONLY if it's lesser than both of them + assert(interpolated_value > 0 || x < x0); + + // execution time must be non-negative + return std::max<int64_t>(interpolated_value, 1); +} + +void ExecTime::updateOperationExecTime(const backend::Backend *backend, + const std::string &operation, bool quant, uint32_t op_size, + int64_t time) +{ + // If the op is not implemented for some input, it should not be scheduled + const auto &recs = _measurements[backend][operation][quant]; + if (time == getMax() || + std::any_of(recs.begin(), recs.end(), + [](std::pair<const uint32_t, const int64_t> p) { return p.second == getMax(); })) + { + _measurements[backend][operation][quant].clear(); + _measurements[backend][operation][quant].emplace(op_size, getMax()); + } + else + { + auto it = _measurements[backend][operation][quant].emplace(op_size, time); + if (!it.second) + { + // affect of the last measurement is bigger than the previous ones: + // this prefers new metrics than older once, so will adapt backend changes + it.first->second = (it.first->second + time) / 2; + } + } +} + +void ExecTime::updatePermuteTime(const backend::Backend *from_backend, + const backend::Backend *to_backend, bool quant, uint32_t op_size, + int64_t time) +{ + updateOperationExecTime(from_backend, to_backend->config()->id(), quant, op_size, time); +} + +int64_t ExecTime::getPermuteTime(const backend::Backend *from_backend, + const backend::Backend *to_backend, bool quant, + uint32_t op_size) const +{ + return getOperationExecTime(from_backend, to_backend->config()->id(), quant, op_size); +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/Execution.cc b/runtime/onert/core/src/exec/Execution.cc new file mode 100644 index 000000000..9c66e51e4 --- /dev/null +++ b/runtime/onert/core/src/exec/Execution.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2019 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 "exec/Execution.h" + +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +Execution::Execution(const std::shared_ptr<IExecutor> &executor) : _executor{executor} +{ + _io_desc.inputs.resize(_executor->graph().getInputs().size()); + _io_desc.outputs.resize(_executor->graph().getOutputs().size()); +} + +// TODO Remove default parameter +void Execution::setInput(const ir::IOIndex &index, const void *buffer, size_t length, + ir::Layout layout) +{ + const auto input_index = graph().getInputs().at(index); + const auto info = graph().operands().at(input_index).info(); + + if (length < info.total_size()) + { + throw std::runtime_error{"Too small length"}; + } + + _io_desc.inputs.at(index.value()) = std::make_unique<InputDesc>(info, buffer, length, layout); +} + +// TODO Remove default parameter +void Execution::setInput(const ir::IOIndex &index, const ir::TypeInfo &type, const ir::Shape &shape, + const void *buffer, size_t length, ir::Layout layout) +{ + const ir::OperandInfo info{shape, type}; + + if (length < info.total_size()) + { + throw std::runtime_error{"Too small length"}; + } + + _io_desc.inputs.at(index.value()) = std::make_unique<InputDesc>(info, buffer, length, layout); +} + +// TODO Remove default parameter +void Execution::setOutput(const ir::IOIndex &index, void *buffer, size_t length, ir::Layout layout) +{ + const auto output_index = graph().getOutputs().at(index); + const auto info = graph().operands().at(output_index).info(); + + if (length < info.total_size()) + { + throw std::runtime_error{"Too small length"}; + } + + _io_desc.outputs.at(index.value()) = std::make_unique<OutputDesc>(info, buffer, length, layout); +} + +// TODO Remove default parameter +void Execution::setOutput(const ir::IOIndex &index, const ir::TypeInfo &type, + const ir::Shape &shape, void *buffer, size_t length, ir::Layout layout) +{ + const ir::OperandInfo info{shape, type}; + + if (length < info.total_size()) + { + throw std::runtime_error{"Too small length"}; + } + + _io_desc.outputs.at(index.value()) = std::make_unique<OutputDesc>(info, buffer, length, layout); +} + +void Execution::setInputLayout(const ir::IOIndex &index, ir::Layout layout) +{ + const auto &input_desc = _io_desc.inputs.at(index.value()); + _io_desc.inputs.at(index.value()) = + std::make_unique<InputDesc>(input_desc->info, input_desc->buffer, input_desc->size, layout); +} + +void Execution::setOutputLayout(const ir::IOIndex &index, ir::Layout layout) +{ + const auto &output_desc = _io_desc.outputs.at(index.value()); + _io_desc.outputs.at(index.value()) = std::make_unique<OutputDesc>( + output_desc->info, output_desc->buffer, output_desc->size, layout); +} + +void Execution::execute() +{ + VERBOSE(Execution) << "Start execution" << std::endl; + + _executor->execute(_io_desc); + finished = true; + + VERBOSE(Execution) << "Execution finished" << std::endl; +} + +void Execution::startExecute() +{ + VERBOSE(Execution) << "Create asynchronous execution thread" << std::endl; + + _exec_thread = std::make_unique<std::thread>(&Execution::execute, this); +} + +void Execution::waitFinish() +{ + VERBOSE(Execution) << "Wait to finish execution" << std::endl; + + _exec_thread->join(); + finished = true; +} + +bool Execution::isFinished(void) const { return finished; } + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ExecutionObservee.cc b/runtime/onert/core/src/exec/ExecutionObservee.cc new file mode 100644 index 000000000..ddb1fb6a0 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutionObservee.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 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 "ExecutionObservee.h" + +namespace onert +{ +namespace exec +{ + +void ExecutionObservee::add(std::unique_ptr<IExecutionObserver> observer) +{ + _observers.emplace_back(std::move(observer)); +} + +void ExecutionObservee::notifyModelBegin(IExecutor *executor) +{ + for (auto &o : _observers) + { + o->handleBegin(executor); + } +} + +void ExecutionObservee::notifyModelEnd(IExecutor *executor) +{ + for (auto &o : _observers) + { + o->handleEnd(executor); + } +} + +void ExecutionObservee::notifyJobBegin(IExecutor *executor, const ir::OpSequence *op_seq, + const backend::Backend *backend) +{ + for (auto &o : _observers) + { + o->handleBegin(executor, op_seq, backend); + } +} + +void ExecutionObservee::notifyJobEnd(IExecutor *executor, const ir::OpSequence *op_seq, + const backend::Backend *backend) +{ + for (auto &o : _observers) + { + o->handleEnd(executor, op_seq, backend); + } +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ExecutionObservee.h b/runtime/onert/core/src/exec/ExecutionObservee.h new file mode 100644 index 000000000..49d409a3a --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutionObservee.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_EXECUTION_OBSERVEE_H__ +#define __ONERT_EXEC_EXECUTION_OBSERVEE_H__ + +#include <list> + +#include "exec/ExecutionObservers.h" + +namespace onert +{ +namespace exec +{ + +/** + * @brief Class that + * + */ +class ExecutionObservee +{ +public: + /** + * @brief Register an observer + * + * @param observer Observer to be added + */ + void add(std::unique_ptr<IExecutionObserver> observer); + void notifyModelBegin(IExecutor *executor); + void notifyModelEnd(IExecutor *executor); + void notifyJobBegin(IExecutor *executor, const ir::OpSequence *op_seq, + const backend::Backend *backend); + void notifyJobEnd(IExecutor *executor, const ir::OpSequence *op_seq, + const backend::Backend *backend); + +private: + std::list<std::unique_ptr<IExecutionObserver>> _observers; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_EXECUTION_OBSERVEE__ diff --git a/runtime/onert/core/src/exec/ExecutionObservers.cc b/runtime/onert/core/src/exec/ExecutionObservers.cc new file mode 100644 index 000000000..e8fcafa2b --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutionObservers.cc @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 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 "exec/ExecutionObservers.h" + +#include <string> + +#include "util/logging.h" +#include "ir/operation/Permute.h" +#include "exec/IExecutor.h" +#include "misc/polymorphic_downcast.h" +#include "ir/OpSequence.h" + +namespace onert +{ + +namespace exec +{ + +void ProfileObserver::handleBegin(onert::exec::IExecutor *, const ir::OpSequence *, + const onert::backend::Backend *backend) +{ + _timer = backend->config()->timer(); + if (_timer == nullptr) + throw std::runtime_error("To profile backend timer() method must be implemented"); + _timer->handleBegin(); +} + +void ProfileObserver::handleEnd(IExecutor *exec, const ir::OpSequence *op_seq, + const backend::Backend *backend) +{ + _timer->handleEnd(); + const auto timer_res = _timer->getTime(); + + // NOTE This assumes there is just one operation in a op_seq + auto node = op_seq->operations().at(0).node; + auto node_name = node->name(); + VERBOSE(ProfileInfo) << "Time for " << node_name << " : " << timer_res << std::endl; + + // fill ExecTime: + bool is_quantized = exec->graph().operands().at(node->getInputs().at(0)).typeInfo().type() == + ir::DataType::QUANT8_ASYMM; + + uint32_t size = 0; + for (const auto &ind : node->getInputs() + node->getOutputs()) + { + size += exec->graph().operands().at(ind).info().total_size(); + } + if (node_name == "Permute") + { + auto *permute_node = nnfw::misc::polymorphic_downcast<const ir::operation::Permute *>(node); + assert(permute_node != nullptr); + _et->updatePermuteTime(permute_node->param().input_backend_ctx->backend(), + permute_node->param().output_backend_ctx->backend(), is_quantized, size, + timer_res); + } + else + { + _et->updateOperationExecTime(backend, node_name, is_quantized, size, timer_res); + } +}; + +ChromeTracingObserver::ChromeTracingObserver(const std::string &filepath) + : _ofs{filepath, std::ofstream::out}, _recorder{}, _collector{&_recorder} +{ +} + +ChromeTracingObserver::~ChromeTracingObserver() { _recorder.writeToFile(_ofs); } + +void ChromeTracingObserver::handleBegin(IExecutor *) +{ + _collector.onEvent(EventCollector::Event{EventCollector::Edge::BEGIN, "runtime", "Graph"}); +} + +void ChromeTracingObserver::handleBegin(IExecutor *, const ir::OpSequence *op_seq, + const backend::Backend *backend) +{ + std::string backend_id = backend->config()->id(); + _collector.onEvent( + EventCollector::Event{EventCollector::Edge::BEGIN, backend_id, opSequenceTag(op_seq)}); +} + +void ChromeTracingObserver::handleEnd(IExecutor *, const ir::OpSequence *op_seq, + const backend::Backend *backend) +{ + std::string backend_id = backend->config()->id(); + _collector.onEvent( + EventCollector::Event{EventCollector::Edge::END, backend_id, opSequenceTag(op_seq)}); +} + +void ChromeTracingObserver::handleEnd(IExecutor *) +{ + _collector.onEvent(EventCollector::Event{EventCollector::Edge::END, "runtime", "Graph"}); +} + +std::string ChromeTracingObserver::opSequenceTag(const ir::OpSequence *op_seq) +{ + if (op_seq->size() == 0) + return "Empty OpSequence"; + + auto first_op = op_seq->operations().at(0); + std::string tag = "$" + std::to_string(first_op.index.value()); + tag += " " + first_op.node->name(); + if (op_seq->size() > 1) + { + tag += " (+" + std::to_string(op_seq->size() - 1) + ")"; + } + return tag; +} + +} // namespace exec + +} // namespace onert diff --git a/runtime/onert/core/src/exec/ExecutorBase.cc b/runtime/onert/core/src/exec/ExecutorBase.cc new file mode 100644 index 000000000..dd7e84af8 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutorBase.cc @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019 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 "ExecutorBase.h" +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +ExecutorBase::ExecutorBase(std::unique_ptr<ir::LoweredGraph> &&lowered_graph, + const backend::TensorBuilderSet &tensor_builders) + : _lowered_graph{std::move(lowered_graph)}, _graph{_lowered_graph->graph()}, _mutex() +{ + auto build_itensor_list = [&](const onert::ir::OperandIndexSequence &ind_seq) { + std::vector<std::shared_ptr<backend::ITensor>> list; + for (auto ind : ind_seq) + { + std::shared_ptr<backend::ITensor> tensor; + for (auto &tensor_builder : tensor_builders) + { + tensor = tensor_builder->tensorAt(ind); + if (tensor != nullptr) + break; + } + assert(tensor != nullptr); + list.push_back(tensor); + } + return list; + }; + + _input_tensors = build_itensor_list(_graph.getInputs()); + _output_tensors = build_itensor_list(_graph.getOutputs()); + + // Prepare each TensorManager on each backend + for (auto &tensor_builder : tensor_builders) + { + auto tensor_manager = tensor_builder->releaseTensorManager(); + assert(tensor_manager != nullptr); + _tensor_mgrs.insert(std::move(tensor_manager)); + } +} + +std::unique_ptr<ISource> ExecutorBase::source(const ir::IOIndex &index, const ir::TypeInfo &type, + const void *buffer, size_t length, + ir::Layout io_layout) +{ + using ir::DataType; + switch (type.type()) + { + case DataType::FLOAT32: + return source<float>(index, buffer, length, io_layout); + case DataType::INT32: + return source<int32_t>(index, buffer, length, io_layout); + case DataType::UINT32: + return source<uint32_t>(index, buffer, length, io_layout); + case DataType::BOOL8: + case DataType::QUANT8_ASYMM: + case DataType::UINT8: + return source<uint8_t>(index, buffer, length, io_layout); + case DataType::QUANT8_SYMM: + return source<int8_t>(index, buffer, length, io_layout); + default: + throw std::runtime_error("Not supported yet"); + } +} + +std::unique_ptr<ISink> ExecutorBase::sink(const ir::IOIndex &index, const ir::TypeInfo &type, + void *buffer, size_t length, ir::Layout io_layout) +{ + using ir::DataType; + switch (type.type()) + { + case DataType::FLOAT32: + return sink<float>(index, buffer, length, io_layout); + case DataType::INT32: + return sink<int32_t>(index, buffer, length, io_layout); + case DataType::UINT32: + return sink<uint32_t>(index, buffer, length, io_layout); + case DataType::BOOL8: + case DataType::QUANT8_ASYMM: + case DataType::UINT8: + return sink<uint8_t>(index, buffer, length, io_layout); + case DataType::QUANT8_SYMM: + return sink<int8_t>(index, buffer, length, io_layout); + default: + throw std::runtime_error("Not supported yet"); + } +} + +void ExecutorBase::execute(const IODescription &desc) +{ + // For thread-safe, use mutex + // TODO: if all used backends on this executor are thread-safe, + // do not need to use mutex (otherwise, use mutex) + std::lock_guard<std::mutex> lock(_mutex); + + std::vector<std::unique_ptr<ISource>> sources{_graph.getInputs().size()}; + std::vector<std::unique_ptr<ISink>> sinks{_graph.getOutputs().size()}; + + // Set input(s) + for (uint32_t n = 0; n < _graph.getInputs().size(); ++n) + { + ir::IOIndex input_index{n}; + ir::OperandIndex index{_graph.getInputs().at(input_index)}; + + if (desc.inputs.at(n) == nullptr) + { + // Optional input + continue; + } + + const auto operand_li = _lowered_graph->getLowerInfo()->operand.at(index).get(); + if (operand_li->def_factors().empty()) + { + // This input is not used (i.e. constant, EX. reshape's axis) + continue; + } + + const auto &input = *desc.inputs.at(n); + sources.at(n) = + source(input_index, input.info.typeInfo(), input.buffer, input.size, input.layout); + + auto setter = [&](::onert::backend::ITensor &tensor) { sources.at(n)->push(tensor); }; + + _input_tensors[n]->access(setter); + } + + executeImpl(); + + // Get output(s) + for (uint32_t n = 0; n < _graph.getOutputs().size(); ++n) + { + ir::IOIndex output_index{n}; + // Optional output + if (desc.outputs.at(n) == nullptr) + { + continue; + } + const auto &output = *desc.outputs.at(n); + sinks.at(n) = + sink(output_index, output.info.typeInfo(), output.buffer, output.size, output.layout); + + auto getter = [&](::onert::backend::ITensor &tensor) { sinks.at(n)->pull(tensor); }; + + _output_tensors[n]->access(getter); + } +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ExecutorBase.h b/runtime/onert/core/src/exec/ExecutorBase.h new file mode 100644 index 000000000..cb5dde8eb --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutorBase.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_EXECUTOR_BASE_H__ +#define __ONERT_EXEC_EXECUTOR_BASE_H__ + +#include <mutex> + +#include "Source.h" +#include "exec/ExecutionObservers.h" +#include "Sink.h" +#include "exec/IExecutor.h" +#include "ir/LoweredGraph.h" +#include "ir/LowerInfoMap.h" +#include "backend/IConfig.h" +#include "backend/Backend.h" +#include "compiler/OperandContext.h" +#include "exec/ExecTime.h" +#include "exec/IFunction.h" +#include "backend/ITensorManager.h" +#include "backend/ITensorBuilder.h" +#include "exec/ExecutionObservee.h" +#include <list> + +namespace onert +{ +namespace exec +{ + +class ExecutorBase : public IExecutor +{ +public: + /** + * @brief Construct a new ExecutorBase object + * @param graph Graph object + * @param tensor_builders Tensor builders that are currently used + */ + ExecutorBase(std::unique_ptr<ir::LoweredGraph> &&lowered_graph, + const backend::TensorBuilderSet &tensor_builders); + + virtual ~ExecutorBase() = default; + + const ir::Graph &graph() final { return _graph; } + + void execute(const IODescription &desc) final; + + // Used only in Dataflow and Parallel Executors + void setIndexedRanks(std::shared_ptr<ir::OperationIndexMap<int64_t>> ranks) final + { + _indexed_ranks = std::move(ranks); + }; + + virtual void executeImpl(void) = 0; + + void addObserver(std::unique_ptr<IExecutionObserver> ref) { _subject.add(std::move(ref)); }; + +private: + std::unique_ptr<ISource> source(const ir::IOIndex &index, const ir::TypeInfo &type, + const void *buffer, size_t length, ir::Layout io_layout); + std::unique_ptr<ISink> sink(const ir::IOIndex &index, const ir::TypeInfo &type, void *buffer, + size_t length, ir::Layout io_layout); + + template <typename T> + std::unique_ptr<ISource> source(const ir::IOIndex &index, const void *buffer, size_t length, + ir::Layout io_layout) + { + const auto operand_index = _graph.getInputs().at(index); + const auto &operand = _graph.operands().at(operand_index); + + const auto tensor = _input_tensors[index.value()]; + const auto tensor_layout = tensor->layout(); + + if (((io_layout == ir::Layout::NHWC) && (tensor_layout == ir::Layout::NCHW)) || + ((io_layout == ir::Layout::NCHW) && (tensor_layout == ir::Layout::NHWC))) + { + return std::make_unique<PermutateSource<T>>(buffer, length, operand.shape(), io_layout); + } + // TODO Change this to return error + assert(io_layout != ir::Layout::UNKNOWN || + (tensor_layout != ir::Layout::NCHW && tensor_layout != ir::Layout::NCHW)); + + return std::make_unique<CopySource<T>>(buffer, length, operand.shape()); + } + + template <typename T> + std::unique_ptr<ISink> sink(const ir::IOIndex &index, void *buffer, size_t length, + ir::Layout io_layout) + { + const auto operand_index = _graph.getOutputs().at(index); + const auto &operand = _graph.operands().at(operand_index); + const auto tensor = _output_tensors[index.value()]; + const auto tensor_layout = tensor->layout(); + + if (((tensor_layout == ir::Layout::NCHW) && (io_layout == ir::Layout::NHWC)) || + ((tensor_layout == ir::Layout::NHWC) && (io_layout == ir::Layout::NCHW))) + { + return std::make_unique<PermutateSink<T>>(buffer, length, operand.shape(), io_layout); + } + // TODO Change this to return error + assert(io_layout != ir::Layout::UNKNOWN || + (tensor_layout != ir::Layout::NCHW && tensor_layout != ir::Layout::NCHW)); + + return std::make_unique<CopySink<T>>(buffer, length, operand.shape()); + } + +protected: + ExecutionObservee _subject; + std::shared_ptr<ir::OperationIndexMap<int64_t>> _indexed_ranks; + std::unique_ptr<ir::LoweredGraph> _lowered_graph; + const ir::Graph &_graph; + std::vector<std::shared_ptr<backend::ITensor>> _input_tensors; + std::vector<std::shared_ptr<backend::ITensor>> _output_tensors; + backend::TensorManagerSet _tensor_mgrs; + std::mutex _mutex; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_EXECUTOR_BASE_H__ diff --git a/runtime/onert/core/src/exec/FunctionSequence.cc b/runtime/onert/core/src/exec/FunctionSequence.cc new file mode 100644 index 000000000..bf205af80 --- /dev/null +++ b/runtime/onert/core/src/exec/FunctionSequence.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 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 "exec/FunctionSequence.h" + +namespace onert +{ +namespace exec +{ + +void FunctionSequence::run() +{ + for (const auto &function : _functions) + { + function->run(); + } +} + +void FunctionSequence::runSync() +{ + for (const auto &function : _functions) + { + function->runSync(); + } +} + +void FunctionSequence::prepare() +{ + for (const auto &function : _functions) + { + function->prepare(); + } +} + +void FunctionSequence::append(std::unique_ptr<IFunction> &&function) +{ + _functions.push_back(std::move(function)); +} + +void FunctionSequence::iterate(const std::function<void(IFunction &)> &fn) +{ + for (const auto &func : _functions) + { + fn(*func); + } +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/JSONExecTime.cc b/runtime/onert/core/src/exec/JSONExecTime.cc new file mode 100644 index 000000000..72a18def1 --- /dev/null +++ b/runtime/onert/core/src/exec/JSONExecTime.cc @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019 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 "exec/JSONExecTime.h" +#include "backend/IConfig.h" +#include <fstream> + +namespace onert +{ +namespace exec +{ +/** + * @brief Helper function for reading string from stream + * + * @param str Output string + * @param stream File stream + */ +void readString(std::string &str, std::ifstream &stream) +{ + str.clear(); + char buf; + while (stream.good()) + { + stream.get(buf); + if (buf == '"') + break; + str.push_back(buf); + } +} + +/** + * @brief Helper function for reading bool from stream + * + * @param quant Output bool + * @param stream File stream + */ +void readBool(bool &quant, std::ifstream &stream) +{ + char buf; + stream.get(buf); + quant = (buf == '1'); + stream.get(buf); +} + +void printString(const std::string &str, std::ofstream &stream) { stream << "\"" << str << "\""; } + +void printBool(bool quant, std::ofstream &stream) { stream << "\"" << quant << "\""; } + +void JSON::readOperation(const std::string &backend, const std::string &operation, bool quant, + std::ifstream &stream) +{ + uint32_t size = 0; + int64_t time = 0; + + std::string int_buf; + char buf; + int number_of_closed_braces = 0; + int number_of_commas = 0; + + while (stream.good()) + { + stream.get(buf); + + switch (buf) + { + case ']': + { + number_of_closed_braces++; + break; + } + case '[': + { + number_of_closed_braces--; + break; + } + default: + { + if (std::isdigit(buf)) + { + int_buf.push_back(buf); + } + break; + } + } + + if (number_of_closed_braces == 1) + break; + + if ((buf == ']' && number_of_closed_braces == 0) || + (buf == ',' && number_of_closed_braces == -1)) + { + switch (number_of_commas % 2) + { + case 0: + { + size = static_cast<uint32_t>(std::atoi(int_buf.c_str())); + break; + } + case 1: + { + time = static_cast<int64_t>(std::atol(int_buf.c_str())); + auto bf = _backends.find(backend); + if (bf != _backends.end()) + { + _measurements[bf->second][operation][quant][size] = time; + } // we ignore the records for unsupported backends + break; + } + } + number_of_commas++; + int_buf.clear(); + } + } +} +void JSON::printOperation(const std::map<uint32_t, int64_t> &operation_info, + std::ofstream &stream) const +{ + for (const auto &items : operation_info) + { + stream << "[" << items.first << ", " << items.second << "], "; + } + stream.seekp(-2, std::ofstream::end); +} + +void JSON::uploadOperationsExecTime() const +{ + std::ofstream stream(_measurement_file); + if (!stream.is_open()) + { + throw std::runtime_error("Failed to save backend config file"); + } + else + { + stream << "{"; + for (const auto &backend : _measurements) + { + printString(backend.first->config()->id(), stream); + stream << ": {"; + for (const auto &operation : backend.second) + { + printString(operation.first, stream); + stream << ": {"; + for (const auto &type : operation.second) + { + printBool(type.first, stream); + stream << ": ["; + printOperation(type.second, stream); + stream << "], "; + } + stream.seekp(-2, std::ofstream::end); + stream << "}, "; + } + stream.seekp(-2, std::ofstream::end); + stream << "}, "; + } + stream.seekp(-2, std::ofstream::end); + stream << "}"; + stream.close(); + } +} + +void JSON::loadOperationsExecTime() +{ + std::ifstream stream(_measurement_file); + if (stream.is_open()) + { + std::string backend; + std::string operation; + bool quant = false; + char buf; + int number_of_open_braces = 0; + + while (stream.good()) + { + stream.get(buf); + switch (buf) + { + case '{': + number_of_open_braces++; + break; + case '}': + number_of_open_braces--; + break; + case '"': + { + if (number_of_open_braces == 1) + { + // read backend string + readString(backend, stream); + } + if (number_of_open_braces == 2) + { + // read operation string + readString(operation, stream); + } + if (number_of_open_braces == 3) + { + // read operation string + readBool(quant, stream); + } + break; + } + case '[': + { + // reading and creating all info for operation + readOperation(backend, operation, quant, stream); + break; + } + default: + break; + } + } + stream.close(); + } +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/Job.cc b/runtime/onert/core/src/exec/Job.cc new file mode 100644 index 000000000..dc0d140f0 --- /dev/null +++ b/runtime/onert/core/src/exec/Job.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 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 "Job.h" + +#include <cassert> + +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +Job::Job(uint32_t index, IFunction *fn) : _index{index}, _fn{fn} {} + +void Job::run() { _fn->run(); } + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/Job.h b/runtime/onert/core/src/exec/Job.h new file mode 100644 index 000000000..ff08ec8ce --- /dev/null +++ b/runtime/onert/core/src/exec/Job.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_JOB_H__ +#define __ONERT_EXEC_JOB_H__ + +#include <unordered_set> + +#include "exec/IFunction.h" +#include "ir/Index.h" +#include "ir/OperandIndexSequence.h" +#include "backend/Backend.h" + +namespace onert +{ +namespace exec +{ + +class Job +{ +public: + /** + * @brief Constructs a Job object + * + * @param index Operation index for this job + * @param fn compiled code to run this job + * @param inputs Input operand list + * @param outputs Output operand list + */ + Job(uint32_t index, IFunction *fn); + /** + * @brief Execute the compiled code + */ + void run(); + /** + * @brief Return job index + * + * @return Job index + */ + uint32_t index() const { return _index; } + /** + * @brief Return the function to be executed + * + * @return Pointer of the function + */ + IFunction *fn() { return _fn; } + +private: + uint32_t _index; + IFunction *_fn; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_JOB_H__ diff --git a/runtime/onert/core/src/exec/LinearExecutor.cc b/runtime/onert/core/src/exec/LinearExecutor.cc new file mode 100644 index 000000000..188282d4d --- /dev/null +++ b/runtime/onert/core/src/exec/LinearExecutor.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "LinearExecutor.h" + +namespace onert +{ +namespace exec +{ + +void LinearExecutor::executeImpl() +{ + _subject.notifyModelBegin(this); + for (auto &&code : _code) + { + const auto op_seq = code.op_seq; + const auto backend = code.lower_info->backend(); + _subject.notifyJobBegin(this, op_seq, backend); + code.fn_seq->run(); + _subject.notifyJobEnd(this, op_seq, backend); + } + _subject.notifyModelEnd(this); +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/LinearExecutor.h b/runtime/onert/core/src/exec/LinearExecutor.h new file mode 100644 index 000000000..f49f51e3e --- /dev/null +++ b/runtime/onert/core/src/exec/LinearExecutor.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file LinearExecutor.h + * @brief This file contains LinearExecutor class to define and run execution phase + */ + +#ifndef __ONERT_EXEC_EXECUTOR_H_ +#define __ONERT_EXEC_EXECUTOR_H_ + +#include "ir/Index.h" +#include "ExecutorBase.h" +#include "compiler/Linear.h" +#include "exec/FunctionSequence.h" +#include "compiler/CodeMap.h" + +namespace onert +{ +namespace exec +{ + +/** + * @brief Class to handle execution phase. Simple run the sequence of operations that is sorted in + * topological order + */ +class LinearExecutor final : public ExecutorBase +{ +public: + /** + * @brief Construct a new LinearExecutor object + * @param lowered_graph LoweredGraph object + * @param tensor_builders Tensor builders that are currently used + * @param code_map OpSequence and its code map + */ + LinearExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const backend::TensorBuilderSet &tensor_builders, compiler::CodeMap &&code_map, + const std::vector<ir::OpSequenceIndex> &order) + : ExecutorBase{std::move(lowered_graph), tensor_builders} + { + for (auto index : order) + { + _code.emplace_back(std::move(code_map.at(index))); + } + } + +public: + void executeImpl(void) override; + +private: + std::vector<compiler::CodeAndInfo> _code; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_EXECUTOR_H_ diff --git a/runtime/onert/core/src/exec/ParallelExecutor.cc b/runtime/onert/core/src/exec/ParallelExecutor.cc new file mode 100644 index 000000000..f7e5de67d --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelExecutor.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2019 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 "ParallelExecutor.h" + +#include <cassert> + +#include "util/logging.h" +#include "exec/IFunction.h" + +namespace onert +{ +namespace exec +{ + +class HookFunction : public IFunction +{ +public: + HookFunction(IFunction *fn, const std::function<void()> &setup, + const std::function<void()> &teardown) + : _fn{fn}, _setup{setup}, _teardown{teardown} + { + } + +public: + void run() override + { + _setup(); + _fn->run(); + _teardown(); + } + void runSync() override { throw("runSync is needed just for profiling in Dataflow executor"); } + +private: + IFunction *_fn; + std::function<void()> _setup; + std::function<void()> _teardown; +}; + +void ParallelExecutor::notify(uint32_t finished_job_id) +{ + std::unique_lock<std::mutex> lock{_mu_jobs}; + + DataflowExecutor::notify(finished_job_id); + + lock.unlock(); + _cv_jobs.notify_all(); +} + +ParallelExecutor::ParallelExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const backend::TensorBuilderSet &tensor_builders, + compiler::CodeMap &&code_map) + : DataflowExecutor{std::move(lowered_graph), tensor_builders, std::move(code_map)} +{ + VERBOSE(ParallelExecutor) << "Constructing Parallel Executor" << std::endl; +} + +void ParallelExecutor::executeImpl() +{ + // Init scheduler + // TODO Consider to have distinct backend set in LowerInfoMap + ir::BackendSet backends; + for (auto &itr : _lowered_graph->getLowerInfo()->operation) + { + backends.add(itr.second->backend()); + } + _scheduler = std::make_unique<ParallelScheduler>(backends); + + assert(noWaitingJobs()); + + // Execution setup + _waiting_jobs.swap(_finished_jobs); // Move finished jobs to waiting jobs + + for (uint32_t i = 0; i < _waiting_jobs.size(); ++i) + { + VERBOSE(ParallelExecutor) << i << ": " << _input_info[i] << std::endl; + if (_input_info[i] == 0) + { + emplaceToReadyJobs(i); + } + } + assert(!_ready_jobs.empty()); // Cannot begin if there is no initial jobs + + VERBOSE(ParallelExecutor) << "INITIAL JOBS : " << _ready_jobs.size() << std::endl; + + _subject.notifyModelBegin(this); + while (true) + { + std::unique_lock<std::mutex> lock{_mu_jobs}; + + if (_ready_jobs.empty()) + { + _cv_jobs.wait(lock, [this] { return !_ready_jobs.empty() || noWaitingJobs(); }); + // Check finish condition + if (_ready_jobs.empty() && noWaitingJobs()) + { + break; + } + } + + auto job = std::move(_ready_jobs.begin()->second); + _ready_jobs.erase(_ready_jobs.begin()); + + lock.unlock(); + + VERBOSE(ParallelExecutor) << "Assigning fn #" << job->index() << std::endl; + + auto job_index = job->index(); + auto op_sequence_index = _job_to_op_seq[job_index]; + auto op_seq = &_lowered_graph->op_seqs().at(op_sequence_index); + auto backend = _lowered_graph->getLowerInfo()->operation.at(op_sequence_index)->backend(); + auto setup = [&, op_seq, backend]() { _subject.notifyJobBegin(this, op_seq, backend); }; + auto teardown = [&, job_index, op_seq, backend]() { + _subject.notifyJobEnd(this, op_seq, backend); + notify(job_index); + }; + + _scheduler->assign(std::make_unique<HookFunction>(job->fn(), setup, teardown), backend); + _finished_jobs[job_index] = std::move(job); + } + + assert(noWaitingJobs()); + + // Wait for all the jobs done + _scheduler->finish(); + _subject.notifyModelEnd(this); + + // Reset input info for the next execution + _input_info = _initial_input_info; +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ParallelExecutor.h b/runtime/onert/core/src/exec/ParallelExecutor.h new file mode 100644 index 000000000..d9387db5c --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelExecutor.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_PARALLEL_EXECUTOR_H__ +#define __ONERT_EXEC_PARALLEL_EXECUTOR_H__ + +#include <list> +#include <queue> +#include <unordered_map> + +#include "exec/FunctionSequence.h" +#include "Job.h" +#include "ir/OperandIndexSequence.h" +#include "ir/Index.h" +#include <memory> +#include "exec/DataflowExecutor.h" +#include "ParallelScheduler.h" + +namespace onert +{ +namespace exec +{ + +/** + * @brief Class to execute Graph in parallel + */ +class ParallelExecutor : public DataflowExecutor +{ +protected: + void notify(uint32_t finished_job_id) override; + +public: + /** + * @brief Constructs a ParallelExecutor object + * + * @param lowered_graph LoweredGraph object + * @param tensor_builders Tensor builders that are currently used + * @param code_map OpSequence and its code map + */ + ParallelExecutor(std::unique_ptr<ir::LoweredGraph> lowered_graph, + const backend::TensorBuilderSet &tensor_builders, compiler::CodeMap &&code_map); + + void executeImpl() override; + +private: + std::condition_variable _cv_jobs; + std::mutex _mu_jobs; + std::unique_ptr<ParallelScheduler> _scheduler; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_PARALLEL_EXECUTOR_H__ diff --git a/runtime/onert/core/src/exec/ParallelScheduler.cc b/runtime/onert/core/src/exec/ParallelScheduler.cc new file mode 100644 index 000000000..1b2cf80bc --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelScheduler.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 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 "ParallelScheduler.h" + +#include <cassert> + +#include <memory> +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +ParallelScheduler::ParallelScheduler(const ir::BackendSet &backends) +{ + assert(!backends.empty()); + + for (auto backend : backends) + { + _thread_pools[backend] = std::make_unique<ThreadPool>(); + } +} + +void ParallelScheduler::assign(std::unique_ptr<IFunction> &&fn, const backend::Backend *backend) +{ + assert(!_thread_pools.empty()); + + _thread_pools.at(backend)->enqueue(std::move(fn)); +} + +void ParallelScheduler::finish() +{ + for (auto &itr : _thread_pools) + { + itr.second->finish(); + } +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ParallelScheduler.h b/runtime/onert/core/src/exec/ParallelScheduler.h new file mode 100644 index 000000000..3a53b6c7f --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelScheduler.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_PARALLEL_SCHEDULER_H__ +#define __ONERT_EXEC_PARALLEL_SCHEDULER_H__ + +#include <unordered_map> +#include <memory> + +#include "exec/IFunction.h" +#include "ir/BackendSet.h" +#include "ThreadPool.h" + +namespace onert +{ +namespace exec +{ + +class ParallelScheduler +{ +public: + /** + * @brief Constructs ParallelScheduler object + * + * @param backends Backend set + */ + ParallelScheduler(const ir::BackendSet &backends); + /** + * @brief Assign a task to the given backend + * + * @param[in] fn Function to be assigned + * @param[in] fn Target backend + */ + void assign(std::unique_ptr<IFunction> &&fn, const backend::Backend *backend); + /** + * @brief Block until all jobs are finished + */ + void finish(); + +private: + std::unordered_map<const backend::Backend *, std::unique_ptr<ThreadPool>> _thread_pools; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_PARALLEL_SCHEDULER_H__ diff --git a/runtime/onert/core/src/exec/Sink.h b/runtime/onert/core/src/exec/Sink.h new file mode 100644 index 000000000..238b0eddd --- /dev/null +++ b/runtime/onert/core/src/exec/Sink.h @@ -0,0 +1,199 @@ +/* + * 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. + */ + +#ifndef __ONERT_EXEC_SINK_H__ +#define __ONERT_EXEC_SINK_H__ + +#include <cassert> + +#include <memory> +#include "util/feature/nchw/Reader.h" +#include "util/feature/nchw/View.h" +#include "util/feature/nhwc/Reader.h" +#include "util/feature/nhwc/View.h" +#include "util/Utils.h" +#include <misc/feature/IndexIterator.h> + +namespace onert +{ +namespace exec +{ +struct ISink +{ + virtual ~ISink() = default; + + virtual void pull(::onert::backend::ITensor &tensor) const = 0; +}; + +// Create second lever inheritance: the first lever is used as a reference type in use-case places +template <typename T> class ITemplSink : public ISink +{ +public: + ITemplSink(void *output_buffer, const size_t &output_size, const ir::Shape &shape, + const bool copy, ir::Layout io_layout) + : _output_buffer{reinterpret_cast<T *>(output_buffer)}, _output_size{output_size}, + _shape{shape}, _copy{copy}, _io_layout{io_layout} + { + } + +protected: + void pullUnif(onert::backend::ITensor &tensor) const + { + assert(((_io_layout == ir::Layout::NHWC && tensor.layout() == ir::Layout::NCHW) || + (_io_layout == ir::Layout::NCHW && tensor.layout() == ir::Layout::NHWC)) || + _copy); + auto input_buffer = tensor.buffer(); + auto rank = _shape.rank(); + + if (!tensor.has_padding() && rank < 4 + _copy) + { + memcpy(_output_buffer, input_buffer, _output_size); + return; + } + + switch (rank) + { + case 0: + case 1: + { + memcpy(_output_buffer, input_buffer, _output_size); + break; + } + case 2: + { + const int32_t copy_len = _shape.dim(1); + + for (auto i = 0; i < _shape.dim(0); ++i) + { + ir::Coordinates coords{i, 0}; + memcpy(_output_buffer + i * copy_len, input_buffer + tensor.calcOffset(coords), + copy_len * sizeof(T)); + } + break; + } + case 3: + { + const int32_t dim1 = _shape.dim(1); + const int32_t dim2 = _shape.dim(2); + + for (auto i = 0; i < _shape.dim(0); ++i) + { + for (auto j = 0; j < _shape.dim(1); ++j) + { + ir::Coordinates coords{i, j, 0}; + memcpy(_output_buffer + i * dim1 * dim2 + j * dim2, + input_buffer + tensor.calcOffset(coords), dim2 * sizeof(T)); + } + } + break; + } + case 4: + { + if (_copy) + { + const int32_t dim1 = _shape.dim(1); + const int32_t dim2 = _shape.dim(2); + const int32_t dim3 = _shape.dim(3); + + for (auto i = 0; i < _shape.dim(0); ++i) + { + for (auto j = 0; j < _shape.dim(1); ++j) + { + for (auto k = 0; k < _shape.dim(2); ++k) + { + ir::Coordinates coords{i, j, k, 0}; + memcpy(_output_buffer + i * dim1 * dim2 * dim3 + j * dim2 * dim3 + k * dim3, + input_buffer + tensor.calcOffset(coords), dim3 * sizeof(T)); + } + } + } + } + else + { + const auto shape = _shape.asFeature(_io_layout); + + if (_io_layout == ir::Layout::NHWC) + { + const util::feature::nchw::Reader<T> from(&tensor); + util::feature::nhwc::View<T> into(shape, _output_buffer, _output_size); + ::nnfw::misc::feature::iterate(shape) + << [&](uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(batch, ch, row, col); + into.at(batch, row, col, ch) = value; + }; + } + else if (_io_layout == ir::Layout::NCHW) + { + const util::feature::nhwc::Reader<T> from(&tensor); + util::feature::nchw::View<T> into(shape, _output_buffer, _output_size); + ::nnfw::misc::feature::iterate(shape) + << [&](uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(batch, row, col, ch); + into.at(batch, ch, row, col) = value; + }; + } + else + { + throw std::runtime_error("Wrong Layout"); + } + } + break; + } + default: + throw std::runtime_error("NYI: rank > 4"); + break; + } + } + +private: + T *_output_buffer; + const size_t _output_size; + const ir::Shape _shape; + const bool _copy; + const ir::Layout _io_layout; +}; + +template <typename T> class PermutateSink final : public ITemplSink<T> +{ +public: + PermutateSink(void *output_buffer, const size_t &output_size, const ir::Shape &shape, + ir::Layout io_layout) + : ITemplSink<T>(output_buffer, output_size, shape, false, io_layout) + { + } + +public: + void pull(onert::backend::ITensor &tensor) const override { ITemplSink<T>::pullUnif(tensor); } +}; + +// Only supports NHWC format front-end(NNAPI) now +template <typename T> class CopySink final : public ITemplSink<T> +{ +public: + CopySink(void *output_buffer, const size_t &output_size, const ir::Shape &shape, + ir::Layout io_layout = ir::Layout::UNKNOWN) + : ITemplSink<T>(output_buffer, output_size, shape, true, io_layout) + { + } + +public: + void pull(onert::backend::ITensor &tensor) const override { ITemplSink<T>::pullUnif(tensor); } +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_SINK_H__ diff --git a/runtime/onert/core/src/exec/Source.h b/runtime/onert/core/src/exec/Source.h new file mode 100644 index 000000000..5792d8f2e --- /dev/null +++ b/runtime/onert/core/src/exec/Source.h @@ -0,0 +1,208 @@ +/* + * 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. + */ + +#ifndef __ONERT_EXEC_SOURCE_H__ +#define __ONERT_EXEC_SOURCE_H__ + +#include <cassert> + +#include <memory> +#include "util/feature/nchw/Reader.h" +#include "util/feature/nchw/View.h" +#include "util/feature/nhwc/Reader.h" +#include "util/feature/nhwc/View.h" +#include "util/Utils.h" +#include <misc/feature/IndexIterator.h> +#include <ir/Layout.h> +#include "ir/Shape.h" + +namespace onert +{ +namespace exec +{ + +struct ISource +{ + virtual ~ISource() = default; + + virtual void push(::onert::backend::ITensor &tensor) const = 0; +}; + +// Create second lever inheritance: the first lever is used as a reference type in use-case places +template <typename T> class ITemplSource : public ISource +{ +public: + ITemplSource(const void *input_buffer, const size_t &input_size, const ir::Shape &shape, + const bool copy, ir::Layout io_layout) + : _input_buffer{reinterpret_cast<const T *>(input_buffer)}, _input_size{input_size}, + _shape{shape}, _copy(copy), _io_layout{io_layout} + { + } + + virtual void push(::onert::backend::ITensor &tensor) const = 0; + +protected: + void pushUnif(onert::backend::ITensor &tensor) const + { + assert(((_io_layout == ir::Layout::NHWC && tensor.layout() == ir::Layout::NCHW) || + (_io_layout == ir::Layout::NCHW && tensor.layout() == ir::Layout::NHWC)) || + _copy); + auto output_buffer = tensor.buffer(); + auto rank = _shape.rank(); + + if (!tensor.has_padding() && rank < 4 + _copy) + { + memcpy(output_buffer, _input_buffer, _input_size); + return; + } + + switch (rank) + { + case 0: + case 1: + { + memcpy(output_buffer, _input_buffer, _input_size); + break; + } + case 2: + { + const int32_t copy_len = _shape.dim(1); + + for (auto i = 0; i < _shape.dim(0); ++i) + { + ir::Coordinates coords{i, 0}; + memcpy(output_buffer + tensor.calcOffset(coords), _input_buffer + i * copy_len, + copy_len * sizeof(T)); + } + break; + } + case 3: + { + const int32_t dim1 = _shape.dim(1); + const int32_t dim2 = _shape.dim(2); + + for (auto i = 0; i < _shape.dim(0); ++i) + { + for (auto j = 0; j < _shape.dim(1); ++j) + { + ir::Coordinates coords{i, j, 0}; + memcpy(output_buffer + tensor.calcOffset(coords), + _input_buffer + i * dim1 * dim2 + j * dim2, dim2 * sizeof(T)); + } + } + break; + } + case 4: + { + if (_copy) + { + const int32_t dim1 = _shape.dim(1); + const int32_t dim2 = _shape.dim(2); + const int32_t dim3 = _shape.dim(3); + for (auto i = 0; i < _shape.dim(0); ++i) + { + for (auto j = 0; j < _shape.dim(1); ++j) + { + for (auto k = 0; k < _shape.dim(2); ++k) + { + ir::Coordinates coords{i, j, k, 0}; + memcpy(output_buffer + tensor.calcOffset(coords), + _input_buffer + i * dim1 * dim2 * dim3 + j * dim2 * dim3 + k * dim3, + dim3 * sizeof(T)); + } + } + } + } + else + { + const auto shape = _shape.asFeature(_io_layout); + + if (_io_layout == ir::Layout::NCHW) + { + const util::feature::nchw::Reader<T> from(shape, _input_buffer, _input_size); + util::feature::nhwc::View<T> into(&tensor); + ::nnfw::misc::feature::iterate(shape) + << [&](uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(batch, ch, row, col); + into.at(batch, row, col, ch) = value; + }; + } + else if (_io_layout == ir::Layout::NHWC) + { + const util::feature::nhwc::Reader<T> from(shape, _input_buffer, _input_size); + util::feature::nchw::View<T> into(&tensor); + ::nnfw::misc::feature::iterate(shape) + << [&](uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(batch, row, col, ch); + into.at(batch, ch, row, col) = value; + }; + } + else + { + throw std::runtime_error("Wrong Layout"); + } + } + + break; + } + default: + throw std::runtime_error("NYI: rank > 4"); + break; + } + } + +private: + const T *_input_buffer; + const size_t _input_size; + const ir::Shape _shape; + const bool _copy; + const ir::Layout _io_layout; +}; + +template <typename T> class PermutateSource final : public ITemplSource<T> +{ +public: + PermutateSource(const void *input_buffer, const size_t &input_size, const ir::Shape &shape, + ir::Layout io_layout) + : ITemplSource<T>(input_buffer, input_size, shape, false, io_layout) + { + } + +public: + void push(onert::backend::ITensor &tensor) const override + { + // do NHWC_TO_NCHW or NCHW_TO_NHWC permutation + ITemplSource<T>::pushUnif(tensor); + } +}; + +template <typename T> class CopySource final : public ITemplSource<T> +{ +public: + CopySource(const void *input_buffer, const size_t &input_size, const ir::Shape &shape, + ir::Layout io_layout = ir::Layout::UNKNOWN) + : ITemplSource<T>(input_buffer, input_size, shape, true, io_layout) + { + } + +public: + void push(onert::backend::ITensor &tensor) const override { ITemplSource<T>::pushUnif(tensor); } +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_SOURCE_H__ diff --git a/runtime/onert/core/src/exec/ThreadPool.cc b/runtime/onert/core/src/exec/ThreadPool.cc new file mode 100644 index 000000000..c8e0e3265 --- /dev/null +++ b/runtime/onert/core/src/exec/ThreadPool.cc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 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 "ThreadPool.h" + +#include <cassert> + +namespace onert +{ +namespace exec +{ + +ThreadPool::ThreadPool(uint32_t num_threads) +{ + assert(num_threads >= 1); + + for (uint32_t i = 0; i < num_threads; i++) + { + _threads.emplace_back(std::ref(_worker)); + } +} + +ThreadPool::~ThreadPool() +{ + if (!_threads.empty()) + { + _worker.terminate(); + join(); + } +} + +void ThreadPool::enqueue(std::unique_ptr<IFunction> &&fn) { _worker.enqueue(std::move(fn)); } + +uint32_t ThreadPool::numJobsInQueue() { return _worker.numJobsInQueue(); } + +void ThreadPool::join() +{ + for (auto &thread : _threads) + { + thread.join(); + } + _threads.clear(); +} + +void ThreadPool::finish() +{ + _worker.finish(); + join(); +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ThreadPool.h b/runtime/onert/core/src/exec/ThreadPool.h new file mode 100644 index 000000000..b638bd94c --- /dev/null +++ b/runtime/onert/core/src/exec/ThreadPool.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_THREAD_POOL_H__ +#define __ONERT_EXEC_THREAD_POOL_H__ + +#include <thread> +#include <memory> +#include <vector> + +#include "WorkQueue.h" + +namespace onert +{ +namespace exec +{ + +class ThreadPool +{ +public: + /** + * @brief Coustruct ThreadPool object + * + * @param num_threads Number of threads + */ + ThreadPool(uint32_t num_threads = 1); + /** + * @brief Destroy ThreadPool object + */ + ~ThreadPool(); + /** + * @brief Enqueue a function + * + * @param fn A function to be queued + */ + void enqueue(std::unique_ptr<IFunction> &&fn); + /** + * @brief Get number of jobs in worker's queue + * + * @return Number of jobs + */ + uint32_t numJobsInQueue(); + + /** + * @brief Block until all jobs are finished + */ + void finish(); + +private: + void join(); + +private: + WorkQueue _worker; + std::vector<std::thread> _threads; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_THREAD_POOL_H__ diff --git a/runtime/onert/core/src/exec/WorkQueue.cc b/runtime/onert/core/src/exec/WorkQueue.cc new file mode 100644 index 000000000..b37f6a387 --- /dev/null +++ b/runtime/onert/core/src/exec/WorkQueue.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 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 "WorkQueue.h" + +#include <cassert> + +namespace onert +{ +namespace exec +{ + +WorkQueue::~WorkQueue() +{ + { + std::unique_lock<std::mutex> lock(_mu); + _state = State::FORCE_FINISHING; + } + _cv.notify_all(); +} + +void WorkQueue::operator()() +{ + while (true) + { + std::unique_ptr<IFunction> fn = nullptr; + + { + std::unique_lock<std::mutex> lock{_mu}; + _cv.wait(lock, [this] { + return (_state == State::FORCE_FINISHING) || (_state == State::FINISHING) || + (_state == State::ONLINE && !_functions.empty()); + }); + + if (_state == State::FORCE_FINISHING) + { + assert(_functions.empty() && "Terminating with unfinished jobs"); + return; + } + else if (_state == State::FINISHING && _functions.empty()) + { + return; + } + else + { + assert(((_state == State::FINISHING) || (_state == State::ONLINE)) && !_functions.empty()); + fn = std::move(_functions.front()); + _functions.pop(); + } + } + + assert(fn); + fn->run(); + } +} + +void WorkQueue::enqueue(std::unique_ptr<IFunction> &&fn) +{ + { + std::unique_lock<std::mutex> lock{_mu}; + _functions.emplace(std::move(fn)); + } + _cv.notify_one(); +} + +void WorkQueue::terminate() +{ + { + std::unique_lock<std::mutex> lock{_mu}; + _state = State::FORCE_FINISHING; + } + _cv.notify_all(); +} + +void WorkQueue::finish() +{ + { + std::unique_lock<std::mutex> lock{_mu}; + _state = State::FINISHING; + } + _cv.notify_all(); +} + +uint32_t WorkQueue::numJobsInQueue() +{ + std::unique_lock<std::mutex> lock{_mu}; + return _functions.size(); +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/WorkQueue.h b/runtime/onert/core/src/exec/WorkQueue.h new file mode 100644 index 000000000..2e56d85e8 --- /dev/null +++ b/runtime/onert/core/src/exec/WorkQueue.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_EXEC_WORK_QUEUE_H__ +#define __ONERT_EXEC_WORK_QUEUE_H__ + +#include <condition_variable> +#include <memory> +#include <mutex> +#include <queue> + +#include "exec/IFunction.h" + +namespace onert +{ +namespace exec +{ + +class WorkQueue +{ +public: + enum class State + { + ONLINE, + FINISHING, + FORCE_FINISHING + }; + +public: + /** + * @brief Create WorkQueue object + */ + WorkQueue() = default; + /** + * @brief Destroy WorkQueue object + */ + ~WorkQueue(); + /** + * @brief Thread entry function + */ + void operator()(); + /** + * @brief Push the given Task to the job queue + * + * @param fn Function to be executed(a job) + */ + void enqueue(std::unique_ptr<IFunction> &&fn); + /** + * @brief Flag as terminating so all the worker threads can terminate + */ + void terminate(); + /** + * @brief Flag as terminating so all the worker threads can terminate + */ + void finish(); + /** + * @brief Check if it has pending jobs. Even if this returns fals, WorkQueue threads may be still + * running + * + * @return true if the job queue not empty otherwise false + */ + uint32_t numJobsInQueue(); + +private: + State _state{State::ONLINE}; + std::queue<std::unique_ptr<IFunction>> _functions; + std::mutex _mu; + std::condition_variable _cv; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_WORK_QUEUE_H__ diff --git a/runtime/onert/core/src/interp/Buffer.h b/runtime/onert/core/src/interp/Buffer.h new file mode 100644 index 000000000..24938f74f --- /dev/null +++ b/runtime/onert/core/src/interp/Buffer.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file Buffer.h + * @brief This file contains Buffer interface and InternalBuffer, ExternalBuffer class + */ +#ifndef __ONERT_INTERP_BUFFER_H__ +#define __ONERT_INTERP_BUFFER_H__ + +#include <memory> + +#include "ir/Data.h" + +namespace onert +{ +namespace interp +{ + +/** + * @brief Interface for writable data area + */ +class Buffer : public ir::Data +{ +public: + /** + * @brief Return writable pointer for data area + * @return Writable pointer + */ + virtual uint8_t *baseWritable(void) const = 0; +}; + +/** + * @brief Class for internally allocated data area + */ +class InternalBuffer final : public Buffer +{ +public: + InternalBuffer(size_t size) : _base{std::make_unique<uint8_t[]>(size)}, _size{size} + { + // DO NOTHING + } + +public: + size_t size(void) const override { return _size; } + const uint8_t *base(void) const override { return _base.get(); } + uint8_t *baseWritable(void) const override { return _base.get(); } + +private: + std::unique_ptr<uint8_t[]> _base; + size_t _size; +}; + +/** + * @brief Class for data area from outside + */ +class ExternalBuffer final : public Buffer +{ +public: + ExternalBuffer(uint8_t *base, size_t size) : _base{base}, _size{size} + { + // DO NOTHING + } + +public: + size_t size(void) const override { return _size; } + const uint8_t *base(void) const override { return _base; } + uint8_t *baseWritable(void) const override { return _base; } + +private: + uint8_t *_base; + size_t _size; +}; + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_BUFFER_H__ diff --git a/runtime/onert/core/src/interp/ExecEnv.h b/runtime/onert/core/src/interp/ExecEnv.h new file mode 100644 index 000000000..20805f1b3 --- /dev/null +++ b/runtime/onert/core/src/interp/ExecEnv.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file ExecEnv.h + * @brief This file contains ExecEnv to access interpreter tensor and execution status + */ +#ifndef __ONERT_INTERP_EXEC_ENV_H_ +#define __ONERT_INTERP_EXEC_ENV_H_ + +#include <unordered_set> + +#include "ir/Graph.h" +#include "Tensor.h" + +namespace onert +{ +namespace interp +{ + +/** + * @brief Class to gather interpreter execution environment + * Each interpreter instance own execution environment + */ +class ExecEnv +{ +public: + /** + * @brief Construct a new Exec Env object (deleted) + */ + ExecEnv(void) = delete; + /** + * @brief Construct a new ExecEnv object + * @param[in] graph Graph to execute by interpreter + */ + explicit ExecEnv(const ir::Graph &graph) : _graph(graph) + { + // DO NOTHING + } + +public: + /** + * @brief Return graph to execute + * @return Graph + */ + const ir::Graph &graph(void) const { return _graph; } + /** + * @brief Assign tensor to environment which have allocated or assigned buffer + * @param[in] index Tensor index + * @param[in] tensor Tensor + */ + void assignTensor(const ir::OperandIndex index, std::shared_ptr<ITensor> tensor) + { + assert(tensor->bufferRO() != nullptr); + _tensors.emplace(index, tensor); + } + + /** + * @brief Return tensor pointer in environment + * @param[in] index Tensor index + * @return Tensor pointer + */ + const ITensor *tensorAt(const ir::OperandIndex index) const { return _tensors.at(index).get(); } + + /** + * @brief Check environment contains tensor + * @param[in] index Tensor index + * @return @c true if environment contain tensor, otherwise @c false + */ + bool contains(const ir::OperandIndex index) const + { + return (_tensors.find(index) != _tensors.end()); + } + + /** + * @brief Allocate tensor using operand info + * @param[in] index Tensor index + * @param[in] info Operand info + * @note If already allocated, just return + * @TODO More smart allocation policy + */ + void allocateIfNeeded(const ir::OperandIndex index, const ir::OperandInfo &info) + { + // already allocated, or constant + if (contains(index)) + { + return; + } + + auto tensor = std::make_shared<Tensor>(info); + tensor->setBuffer(std::make_shared<InternalBuffer>(tensor->total_size())); + assignTensor(index, tensor); + _buffers.insert(index); + } + + /** + * @brief Allocate read-only tensor and share data with other tensor + * @param[in] index Tensor index + * @param[in] info Operand info + * @param[in] index_to_share Tensor index that have data to share + */ + void allocateAndShareIfNeeded(const ir::OperandIndex index, const ir::OperandInfo &info, + const ir::OperandIndex index_to_share) + { + if (!contains(index_to_share)) + { + throw std::runtime_error{"Cannot find tensor to share data"}; + } + + // already allocated + if (contains(index)) + { + return; + } + else + { + auto tensor = std::make_shared<ROTensor>(info); + tensor->setData(tensorAt(index_to_share)->shareData()); + assignTensor(index, tensor); + _buffers.insert(index); + } + } + + /** + * @brief Free buffer if allocated by allocateIfNeed + * @param[in] index Tensor index + * @note If allocated by outside, just return + */ + void freeIfAllocated(const ir::OperandIndex index) + { + if (_buffers.find(index) != _buffers.end()) + { + _tensors.at(index)->releaseData(); + } + } + +private: + const ir::Graph &_graph; + // Tensor map to use in interpreter + // It should map tensors that have allocated or assigned buffer pointer + std::unordered_map<ir::OperandIndex, std::shared_ptr<ITensor>> _tensors; + // Tensors allocated by allocateIfNeed (buffer) + std::unordered_set<ir::OperandIndex> _buffers; +}; + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_EXEC_ENV_H_ diff --git a/runtime/onert/core/src/interp/InterpExecutor.cc b/runtime/onert/core/src/interp/InterpExecutor.cc new file mode 100644 index 000000000..7a848412f --- /dev/null +++ b/runtime/onert/core/src/interp/InterpExecutor.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 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 "interp/InterpExecutor.h" +#include "interp/ExecEnv.h" +#include "interp/Interpreter.h" + +#include "util/logging.h" + +#include <memory> + +namespace onert +{ +namespace interp +{ + +void InterpExecutor::execute(const exec::IODescription &desc) +{ + /************************************************************************ + * Prepare execution model (submodel) + It may execute divided model + but now consider model inference is done at interpreter + ***********************************************************************/ + ir::OperandIndexMap<std::shared_ptr<ITensor>> tensor_map; + + for (uint32_t n = 0; n < _graph.getInputs().size(); n++) + { + ir::IOIndex index{n}; + const auto input_index = _graph.getInputs().at(index); + const auto &input = *desc.inputs.at(n); + + auto input_tensor = std::make_shared<ROTensor>(input.info); + input_tensor->setData(std::make_shared<const ir::ExternalData>( + reinterpret_cast<const uint8_t *>(input.buffer), input.size)); + tensor_map[input_index] = input_tensor; + } + + for (uint32_t n = 0; n < _graph.getOutputs().size(); n++) + { + ir::IOIndex index{n}; + const auto output_index = _graph.getOutputs().at(index); + const auto &output = *desc.outputs.at(n); + + auto output_tensor = std::make_shared<Tensor>(output.info); + output_tensor->setBuffer( + std::make_shared<ExternalBuffer>(reinterpret_cast<uint8_t *>(output.buffer), output.size)); + tensor_map[output_index] = output_tensor; + } + + /************************************************************************ + * Prepare execution environment + Execution environment will be assigned to invoked interpreter instance + ***********************************************************************/ + + std::unique_ptr<ExecEnv> interp_env = std::make_unique<ExecEnv>(_graph); + + // Assign input/output tensor into interpreter execution environment + for (auto index : _graph.getInputs() + _graph.getOutputs()) + { + if (tensor_map.find(index) != tensor_map.end()) + { + VERBOSE(INTERPRETER) << "Assign input/output tensor. operand index:" << index.value() + << std::endl; + interp_env->assignTensor(index, tensor_map.at(index)); + } + } + + // Allocate constant tensor + _graph.operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) { + if (obj.isConstant()) + { + VERBOSE(INTERPRETER) << "Allocate and assign constant tensor. operand index:" << ind.value() + << std::endl; + + assert(obj.data()); + auto const_tensor = std::make_shared<ROTensor>(obj.info()); + // Assume that interpreter's tensor layout is same with model (NHWC) + const_tensor->setData( + std::make_shared<ir::ExternalData>(obj.data()->base(), obj.info().total_size())); + interp_env->assignTensor(ind, const_tensor); + } + }); + + /***************************************************************************** + * Invoke interpreter + ****************************************************************************/ + + interp::Interpreter interp(std::move(interp_env)); + interp.run(); + + /***************************************************************************** + * Invoked interpreter run is finished + ****************************************************************************/ + + // If interpreter execute submodel + // 1. Get tensor output of submodel into tensor_map to save result + // 2. Generate new ExecEnv for next interpretation +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/InterpOps.lst b/runtime/onert/core/src/interp/InterpOps.lst new file mode 100644 index 000000000..7b0c33232 --- /dev/null +++ b/runtime/onert/core/src/interp/InterpOps.lst @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef INTERP_OP +#error Define INTERP_OP before including this file +#endif + +// Supported operation name in interpreter +// +// Same list with Operations.lst +// Make comment out if operation is not supported in interpreter +INTERP_OP(Add) +INTERP_OP(Sub) +//INTERP_OP(BatchToSpaceND) +//INTERP_OP(Cast) +INTERP_OP(Conv2D) +INTERP_OP(DepthwiseConv2D) +INTERP_OP(AvgPool2D) +INTERP_OP(MaxPool2D) +INTERP_OP(Concat) +INTERP_OP(FullyConnected) +//INTERP_OP(ReduceSum) +INTERP_OP(Reshape) +INTERP_OP(Mul) +INTERP_OP(Softmax) +//INTERP_OP(Squeeze) +//INTERP_OP(Slice) +//INTERP_OP(StridedSlice) +INTERP_OP(Tanh) +INTERP_OP(Logistic) +//INTERP_OP(Div) +//INTERP_OP(Transpose) +//INTERP_OP(Exp) +//INTERP_OP(ReduceMax) +//INTERP_OP(Comparison) +//INTERP_OP(LogicalAnd) +//INTERP_OP(LogicalOr) +//INTERP_OP(LogicalNot) +//INTERP_OP(LSTM) +//INTERP_OP(RSQRT) +INTERP_OP(ReLU) +//INTERP_OP(ResizeBilinear) +INTERP_OP(ReLU1) +INTERP_OP(ReLU6) +//INTERP_OP(RNN) +//INTERP_OP(Floor) +//INTERP_OP(SpaceToBatchND) +//INTERP_OP(SpaceToDepth) +//INTERP_OP(L2Pool2D) +//INTERP_OP(EmbeddingLookup) +//INTERP_OP(L2Normalization) +//INTERP_OP(HashtableLookup) +INTERP_OP(InstanceNorm) +//INTERP_OP(PReLU) +INTERP_OP(TransposeConv) +//INTERP_OP(SQRT) +//INTERP_OP(SquaredDifference) +//INTERP_OP(TopKV2) +INTERP_OP(Gather) +//INTERP_OP(Neg) +//INTERP_OP(Abs) +//INTERP_OP(ArgMax) +//INTERP_OP(Dequantize) +//INTERP_OP(Mean) +//INTERP_OP(LocalResponseNormalization) +//INTERP_OP(DepthToSpace) +//INTERP_OP(Pack) +//INTERP_OP(ReduceMin) +//INTERP_OP(Split) +//INTERP_OP(Unpack) +INTERP_OP(Pad) +//INTERP_OP(Custom) +//INTERP_OP(Permute) +//INTERP_OP(Min) +//INTERP_OP(Max) +//INTERP_OP(OneHot) diff --git a/runtime/onert/core/src/interp/Interpreter.cc b/runtime/onert/core/src/interp/Interpreter.cc new file mode 100644 index 000000000..e0cb8ce41 --- /dev/null +++ b/runtime/onert/core/src/interp/Interpreter.cc @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2019 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 "Interpreter.h" + +#include <stack> +#include <unordered_set> + +#include "Registration.h" + +#include "ir/OperandIndexMap.h" +#include "util/logging.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace interp +{ + +// TODO more structured execution kernel implementation +// TODO use cker for execution +// TODO divide tensor prepare and execution +// TODO introduce memory manager (buffer allocate and free) +class OperationExecutor +{ +public: + OperationExecutor(ExecEnv *env) : _env{env} + { +#define INTERP_OP(InternalName) _kernels[ir::OpCode::InternalName] = get##InternalName(); +#include "InterpOps.lst" +#undef INTERP_OP + } + + void execute(const ir::OperationIndex &idx) + { + const ir::Operation &node = _env->graph().operations().at(idx); + const auto nodeName = node.name(); + VERBOSE(INTERPRETER) << "Prepare output operands and execute " << nodeName + << " operation (id: " << idx.value() << ")" << std::endl; + + const auto nodeOpCode = node.opcode(); + if (_kernels[nodeOpCode]->prepare != nullptr) + { + _kernels[nodeOpCode]->prepare(_env, node); + } + _kernels[nodeOpCode]->invoke(_env, node); + } + +private: + ExecEnv *_env; + std::unordered_map<ir::OpCode, OpKernel *> _kernels; +}; + +void Interpreter::run() +{ + VERBOSE(INTERPRETER) << "Interpreter is invoked " << std::endl; + + // operand_stack: save operands prepared to use + std::stack<ir::OperandIndex> operand_stack; + + // Note: We should push input first, then constant. + // We use use-def for find operators ready to execution, + // but Use-Def cannot handle parameters (maybe constant, but not always) + // Note: If all model inputs are constant, it may not work (depend on tensors' order). + // But that scenario may not exist + for (auto ind : _env->graph().getInputs()) + { + VERBOSE(INTERPRETER) << "Input: Push to operand stack " << ind.value() << std::endl; + + operand_stack.push(ind); + } + + _env->graph().operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) { + if (obj.isConstant()) + { + VERBOSE(INTERPRETER) << "Constant: Push to operand stack " << ind.value() << std::endl; + + operand_stack.push(ind); + } + }); + + // Execution + std::unordered_set<ir::OperandIndex> ready_check; + std::unordered_set<ir::OperationIndex> executed; + OperationExecutor executor{_env.get()}; + while (!operand_stack.empty()) + { + const auto current_operand_index = operand_stack.top(); + operand_stack.pop(); + VERBOSE(INTERPRETER) << "Poped operand " << current_operand_index.value() + << " is checked ready to use" << std::endl; + + assert(ready_check.find(current_operand_index) == ready_check.end()); + ready_check.insert(current_operand_index); + + // Find prepared operations by scan use of current operand + std::stack<ir::OperationIndex> operation_stack; + auto use_operators = std::list<ir::OperationIndex>( + _env->graph().operands().at(current_operand_index).getUses().list()); + // Remove operation index duplication + // If one operation uses same operand tensor for multiple input, + // use-list have duplicated operation index + use_operators.unique(); + for (auto use_operator : use_operators) + { + // Assumption: all parameters are ready to use + bool operator_ready = true; + for (auto input_index : _env->graph().operations().at(use_operator).getInputs()) + { + if (ready_check.find(input_index) == ready_check.end()) + { + operator_ready = false; + break; + } + } + + if (operator_ready) + { + VERBOSE(INTERPRETER) << "Ready to execute operation " << use_operator.value() << std::endl; + operation_stack.push(use_operator); + } + } + + while (!operation_stack.empty()) + { + const auto current_operation_index = operation_stack.top(); + operation_stack.pop(); + VERBOSE(INTERPRETER) << "Poped operation: " << current_operation_index.value() << "(" + << _env->graph().operations().at(current_operation_index).name() << ")" + << std::endl; + + // execution + // 1. Prepare output tensor + // 2. Call operation kernel + executor.execute(current_operation_index); + executed.insert(current_operation_index); + + // 3. Push each output into operand stack + const auto def_operands = _env->graph().operations().at(current_operation_index).getOutputs(); + for (auto def_operand : def_operands) + { + VERBOSE(INTERPRETER) << "Buffer: Push to operand stack " << def_operand.value() + << std::endl; + operand_stack.push(def_operand); + } + + // 4. Free if lifetime of buffer operands used by input is finished + for (auto input_index : _env->graph().operations().at(current_operation_index).getInputs()) + { + const auto use_operators = _env->graph().operands().at(input_index).getUses(); + bool dead_buffer = true; + for (auto use_operator : use_operators.list()) + { + if (executed.find(use_operator) == executed.end()) + { + dead_buffer = false; + break; + } + } + + if (dead_buffer) + { + _env->freeIfAllocated(input_index); + } + } + } + } +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/Interpreter.h b/runtime/onert/core/src/interp/Interpreter.h new file mode 100644 index 000000000..d2165f538 --- /dev/null +++ b/runtime/onert/core/src/interp/Interpreter.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file Interpreter.h + * @brief This file contains Interpreter class for interpretation + */ +#ifndef __ONERT_INTERP_INTERPRETER_H__ +#define __ONERT_INTERP_INTERPRETER_H__ + +#include "ExecEnv.h" + +namespace onert +{ +namespace interp +{ + +/** + * @brief Class for interpretation + */ +class Interpreter +{ + +public: + /** + * @brief Construct a new Interpreter object (deleted) + */ + Interpreter() = delete; + /** + * @brief Construct a new Interpreter object + * @param[in] env Execution environment variable for interpreter object + */ + Interpreter(std::unique_ptr<ExecEnv> env) : _env{std::move(env)} + { + // DO NOTHING + } + +public: + /** + * @brief Run interpreter until there is no operation to execute + */ + void run(); + +private: + std::unique_ptr<ExecEnv> _env; +}; + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_INTERPRETER_H__ diff --git a/runtime/onert/core/src/interp/Registration.h b/runtime/onert/core/src/interp/Registration.h new file mode 100644 index 000000000..956b92a53 --- /dev/null +++ b/runtime/onert/core/src/interp/Registration.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_INTERP_REGISTRATION_H__ +#define __ONERT_INTERP_REGISTRATION_H__ + +#include "ExecEnv.h" + +#include "ir/Operation.h" + +namespace onert +{ +namespace interp +{ + +struct OpKernel +{ + std::function<void(ExecEnv *, const ir::Operation &)> prepare; + std::function<void(const ExecEnv *, const ir::Operation &)> invoke; +}; + +// Defined in operations/ directory +#define INTERP_OP(InternalName) OpKernel *get##InternalName(); +#include "InterpOps.lst" +#undef INTERP_OP + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_REGISTRATION_H__ diff --git a/runtime/onert/core/src/interp/Tensor.cc b/runtime/onert/core/src/interp/Tensor.cc new file mode 100644 index 000000000..07f8b75dc --- /dev/null +++ b/runtime/onert/core/src/interp/Tensor.cc @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 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 "Tensor.h" + +#define NO_USE(a) (void)(a) + +namespace onert +{ +namespace interp +{ + +void ITensor::access(const std::function<void(backend::ITensor &tensor)> &fn) { fn(*this); } + +size_t ROTensor::calcOffset(const ir::Coordinates &coords) const +{ + NO_USE(coords); + throw std::runtime_error("offset_element_in_bytes is not supported for cpu::Tensor now."); +} + +size_t Tensor::calcOffset(const ir::Coordinates &coords) const +{ + NO_USE(coords); + throw std::runtime_error("offset_element_in_bytes is not supported for cpu::Tensor now."); +} + +ir::Layout ROTensor::layout() const +{ + // TODO Changes to return frontend layout + return ir::Layout::NHWC; +} + +ir::Layout Tensor::layout() const +{ + // TODO Changes to return frontend layout + return ir::Layout::NHWC; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/Tensor.h b/runtime/onert/core/src/interp/Tensor.h new file mode 100644 index 000000000..d5cb81738 --- /dev/null +++ b/runtime/onert/core/src/interp/Tensor.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019 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. + */ + +/** + * @file Tensor.h + * @brief This file contains ITensor interface, ROTensor class, and Tensor class + */ +#ifndef __ONERT_INTERP_TENSOR_H__ +#define __ONERT_INTERP_TENSOR_H__ + +#include "Buffer.h" + +#include "ir/OperandInfo.h" +#include "backend/ITensor.h" +#include "ir/Layout.h" + +namespace onert +{ +namespace interp +{ + +/** + * @brief Interface to handle Tensor in interpreter + */ +class ITensor : public backend::ITensor +{ +public: + virtual ~ITensor() = default; + +public: + virtual uint8_t *buffer() const = 0; + /** + * @brief Return shared pointer for buffer + * @return Buffer shared pointer + */ + virtual std::shared_ptr<const Buffer> shareBuffer() const = 0; + /** + * @brief Return read-only buffer pointer + * @return Read-only buffer pointer + */ + virtual const uint8_t *bufferRO() const = 0; + /** + * @brief Return shared pointer for data + * @return Data shared pointer + */ + virtual std::shared_ptr<const ir::Data> shareData() const = 0; + /** + * @brief Set internal/external buffer + * @param[in] buffer Buffer pointer + */ + virtual void setBuffer(std::shared_ptr<const Buffer> buffer) = 0; + /** + * @brief Set data reference (including constant, input) + * @param[in] data Data pointer + */ + virtual void setData(std::shared_ptr<const ir::Data> data) = 0; + virtual void releaseData() = 0; + + virtual size_t total_size() const = 0; + virtual size_t dimension(size_t index) const = 0; + virtual size_t num_dimensions() const = 0; + virtual size_t calcOffset(const ir::Coordinates &coords) const = 0; + + virtual bool has_padding() const = 0; + /** + * @brief Return data type of tensor + * @return Data type of tensor + */ + virtual ir::DataType data_type() const = 0; + /** + * @brief Return TensorInfo + * @return TensorInfo + */ + virtual const ir::OperandInfo &tensorInfo() const = 0; + /** + * @brief Return number of elements + * @return Number of elements + */ + virtual uint64_t num_elements() const = 0; + void access(const std::function<void(backend::ITensor &tensor)> &fn) final; +}; + +/** + * @brief Class to handle tensor in interpreter as read-only + */ +class ROTensor final : public ITensor +{ +public: + ROTensor() = delete; + ROTensor(const ir::OperandInfo &info) : _info(info) + { + // DO NOTHING + } + +public: + uint8_t *buffer() const override { throw std::runtime_error{"Read only tensor"}; } + std::shared_ptr<const Buffer> shareBuffer() const override + { + throw std::runtime_error{"Read only tensor"}; + } + const uint8_t *bufferRO() const override { return _data->base(); } + std::shared_ptr<const ir::Data> shareData() const override { return _data; } + void setBuffer(std::shared_ptr<const Buffer> buffer) override { _data = buffer; } + void setData(std::shared_ptr<const ir::Data> data) override { _data = data; } + void releaseData() override { _data = nullptr; } + + size_t total_size() const override { return _info.total_size(); } + size_t dimension(size_t index) const override { return _info.shape().dim(index); } + size_t num_dimensions() const override { return _info.shape().rank(); } + size_t calcOffset(const ir::Coordinates &coords) const override; + ir::Layout layout() const override; + bool has_padding() const override { return false; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + const ir::OperandInfo &tensorInfo() const override { return _info; } + uint64_t num_elements() const override { return _info.shape().num_elements(); }; + +private: + const ir::OperandInfo _info; + std::shared_ptr<const ir::Data> _data{nullptr}; +}; + +/** + * @brief Class to handle tensor in interpreter as writable + */ +class Tensor final : public ITensor +{ +public: + Tensor() = delete; + Tensor(const ir::OperandInfo &info) : _info(info) + { + // DO NOTHING + } + +public: + uint8_t *buffer() const override { return _buffer->baseWritable(); } + std::shared_ptr<const Buffer> shareBuffer() const override { return _buffer; }; + const uint8_t *bufferRO() const override { return _buffer->base(); } + std::shared_ptr<const ir::Data> shareData() const override { return _buffer; } + void setBuffer(std::shared_ptr<const Buffer> buffer) override { _buffer = buffer; } + void setData(std::shared_ptr<const ir::Data>) override + { + throw std::runtime_error{"Passed data may read-only"}; + } + void releaseData() override { _buffer = nullptr; } + + size_t total_size() const override { return _info.total_size(); } + size_t dimension(size_t index) const override { return _info.shape().dim(index); } + size_t num_dimensions() const override { return _info.shape().rank(); } + size_t calcOffset(const ir::Coordinates &coords) const override; + ir::Layout layout() const override; + bool has_padding() const override { return false; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + const ir::OperandInfo &tensorInfo() const override { return _info; } + uint64_t num_elements() const override { return _info.shape().num_elements(); }; + +private: + const ir::OperandInfo _info; + std::shared_ptr<const Buffer> _buffer{nullptr}; +}; + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_TENSOR_H__ diff --git a/runtime/onert/core/src/interp/operations/AvgPool2D.cc b/runtime/onert/core/src/interp/operations/AvgPool2D.cc new file mode 100644 index 000000000..ef653fb2a --- /dev/null +++ b/runtime/onert/core/src/interp/operations/AvgPool2D.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019 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 <cker/operation/AveragePool.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/AvgPool2D.h" +#include "util/Utils.h" +#include "util/ShapeInference.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace avgpool2d +{ + +void prepareAvgPool2D(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + UNUSED_RELEASE(in_tensor); + + assert(in_tensor->num_dimensions() == 4); + + const auto output_info = env->graph().operands().at(out_index).info(); + if (output_info.total_size() == 0) + { + // Handle unspecified output shape + const auto &avgpool_node = + nnfw::misc::polymorphic_downcast<const ir::operation::AvgPool2D &>(node); + const auto infered_output_shapes = + shape_inference::inferAvgPoolShape(in_tensor->tensorInfo().shape(), avgpool_node.param()); + env->allocateIfNeeded(out_index, {infered_output_shapes[0], output_info.typeInfo()}); + } + else + { + env->allocateIfNeeded(out_index, output_info); + } + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Handle same ifm & ofm data type only + assert(in_tensor->data_type() == out_tensor->data_type()); + assert(out_tensor->num_dimensions() == 4); +} + +void invoke(const ITensor *in_tensor, const ITensor *out_tensor, + const ir::operation::AvgPool2D::Param ¶m) +{ + // TODO Support NCHW frontend + const auto ifm_shape = in_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto ofm_shape = out_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto padding = + ir::calculatePadding(param.padding, ifm_shape, ofm_shape, param.stride, param.kw, param.kh); + // Calculate + nnfw::cker::PoolParams cker_param; + calculateActivationRange(param.activation, &cker_param.float_activation_min, + &cker_param.float_activation_max); + cker_param.filter_width = param.kw; + cker_param.filter_height = param.kh; + cker_param.padding_values.width = padding.left; + cker_param.padding_values.height = padding.top; + cker_param.stride_width = param.stride.horizontal; + cker_param.stride_height = param.stride.vertical; + + const auto in_shape = convertShape(in_tensor->tensorInfo().shape()); + const auto out_shape = convertShape(out_tensor->tensorInfo().shape()); + const float *in_ptr = reinterpret_cast<const float *>(in_tensor->bufferRO()); + float *out_ptr = reinterpret_cast<float *>(out_tensor->buffer()); + + nnfw::cker::AveragePool(cker_param, in_shape, in_ptr, out_shape, out_ptr); +} + +void invokeAvgPool2D(const ExecEnv *env, const ir::Operation &node) +{ + const auto &avgpool_node = + nnfw::misc::polymorphic_downcast<const ir::operation::AvgPool2D &>(node); + + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + // Check lhs shape is same with rhs (with broadcast) + const auto in_tensor = env->tensorAt(in_index); + const auto out_tensor = env->tensorAt(out_index); + + const auto data_type = in_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(in_tensor, out_tensor, avgpool_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float only"}; + } +} +} // namespace avgpool2d + +OpKernel *getAvgPool2D() +{ + static OpKernel kernel = {avgpool2d::prepareAvgPool2D, avgpool2d::invokeAvgPool2D}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc b/runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc new file mode 100644 index 000000000..8ffc3cd33 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2019 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 <cker/operation/BinaryArithmeticOps.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Add.h" +#include "ir/operation/Sub.h" +#include "ir/operation/Mul.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +enum class OpType +{ + ADD, + SUB, + MUL +}; + +template <typename node_type> void prepareAdd(ExecEnv *env, const ir::Operation &node) +{ + const auto &add_node = nnfw::misc::polymorphic_downcast<const node_type &>(node); + + const auto lhs_index = node.getInputs().at(add_node.LHS); + const auto rhs_index = node.getInputs().at(add_node.RHS); + const auto out_index = node.getOutputs().at(0); + + const auto lhs_tensor = env->tensorAt(lhs_index); + const auto rhs_tensor = env->tensorAt(rhs_index); + + // Check shape and type lhs is same with rhs + // TODO Util function to compare TensorInfo + if (lhs_tensor->data_type() != rhs_tensor->data_type()) + { + throw std::runtime_error{"Interp(Add): Different input types"}; + } + + bool try_broadcast = (lhs_tensor->tensorInfo().shape() != rhs_tensor->tensorInfo().shape()); + if (try_broadcast) + { + bool success = true; + auto out_shape = calcBroadcastShape(lhs_tensor->tensorInfo().shape(), + rhs_tensor->tensorInfo().shape(), success); + if (!success) + { + throw std::runtime_error{"Interp(Add): Fail to brodcasting"}; + } + + auto output_info = ir::OperandInfo(out_shape, lhs_tensor->tensorInfo().typeInfo()); + // We can handle already allocated (ex. model output) + env->allocateIfNeeded(out_index, output_info); + } + else + { + // Output's shape and type is same with input + auto output_info = lhs_tensor->tensorInfo(); + // We can handle already allocated (ex. model output) + env->allocateIfNeeded(out_index, output_info); + } + + auto out_tensor = env->tensorAt(out_index); + // Check shape and type lhs is same with output + // TODO Util function to compare TensorInfo + if (lhs_tensor->data_type() != out_tensor->data_type()) + { + throw std::runtime_error{"Interp(Add): Invalid output type"}; + } +} + +inline void setActivationParams(float min, float max, nnfw::cker::BinaryArithmeticOpParam *params) +{ + params->float_activation_min = min; + params->float_activation_max = max; +} + +inline void setActivationParams(int32_t min, int32_t max, + nnfw::cker::BinaryArithmeticOpParam *params) +{ + params->quantized_activation_min = min; + params->quantized_activation_max = max; +} + +template <typename raw_type, typename param_type, OpType op_type> +void invoke(const ITensor *lhs_tensor, const ITensor *rhs_tensor, const ITensor *out_tensor, + const param_type ¶m) +{ + const auto lhs_buffer = lhs_tensor->bufferRO(); + const auto rhs_buffer = rhs_tensor->bufferRO(); + auto out_buffer = out_tensor->buffer(); + + nnfw::cker::BinaryArithmeticOpParam cker_param; + raw_type activation_min, activation_max; + calculateActivationRange(param.activation, &activation_min, &activation_max); + setActivationParams(activation_min, activation_max, &cker_param); + const raw_type *lhs_ptr = reinterpret_cast<const raw_type *>(lhs_buffer); + const raw_type *rhs_ptr = reinterpret_cast<const raw_type *>(rhs_buffer); + raw_type *out_ptr = reinterpret_cast<raw_type *>(out_buffer); + + cker_param.type = (op_type == OpType::ADD) + ? nnfw::cker::BinaryArithmeticOpType::ADD + : ((op_type == OpType::SUB) ? nnfw::cker::BinaryArithmeticOpType::SUB + : nnfw::cker::BinaryArithmeticOpType::MUL); + + if (lhs_tensor->tensorInfo().shape() != rhs_tensor->tensorInfo().shape()) + { + const auto lhs_shape = convertExtendShape(lhs_tensor->tensorInfo().shape()); + const auto rhs_shape = convertExtendShape(rhs_tensor->tensorInfo().shape()); + const auto out_shape = convertExtendShape(out_tensor->tensorInfo().shape()); + nnfw::cker::BroadcastBinaryArithmeticOpSlow(cker_param, lhs_shape, lhs_ptr, rhs_shape, rhs_ptr, + out_shape, out_ptr); + return; + } + + const auto lhs_shape = convertShape(lhs_tensor->tensorInfo().shape()); + const auto rhs_shape = convertShape(rhs_tensor->tensorInfo().shape()); + const auto out_shape = convertShape(out_tensor->tensorInfo().shape()); + nnfw::cker::BinaryArithmeticOp(cker_param, lhs_shape, lhs_ptr, rhs_shape, rhs_ptr, out_shape, + out_ptr); +} + +template <typename node_type, typename param_type, OpType op_type> +void invokeAdd(const ExecEnv *env, const ir::Operation &node) +{ + const auto &arithmetic_node = nnfw::misc::polymorphic_downcast<const node_type &>(node); + + const auto lhs_index = node.getInputs().at(arithmetic_node.LHS); + const auto rhs_index = node.getInputs().at(arithmetic_node.RHS); + const auto out_index = node.getOutputs().at(0); + const auto lhs_tensor = env->tensorAt(lhs_index); + const auto rhs_tensor = env->tensorAt(rhs_index); + const auto out_tensor = env->tensorAt(out_index); + const auto data_type = lhs_tensor->data_type(); + + if (data_type == ir::DataType::INT32) + { + invoke<int32_t, param_type, op_type>(lhs_tensor, rhs_tensor, out_tensor, + arithmetic_node.param()); + } + else if (data_type == ir::DataType::FLOAT32) + { + invoke<float, param_type, op_type>(lhs_tensor, rhs_tensor, out_tensor, arithmetic_node.param()); + } + else + { + throw std::runtime_error{"NYI: Unsupported data type"}; + } +} +} // namespace + +OpKernel *getAdd() +{ + static OpKernel kernel = {prepareAdd<ir::operation::Add>, + invokeAdd<ir::operation::Add, ir::operation::Add::Param, OpType::ADD>}; + return &kernel; +} + +OpKernel *getSub() +{ + static OpKernel kernel = {prepareAdd<ir::operation::Sub>, + invokeAdd<ir::operation::Sub, ir::operation::Sub::Param, OpType::SUB>}; + return &kernel; +} + +OpKernel *getMul() +{ + static OpKernel kernel = {prepareAdd<ir::operation::Mul>, + invokeAdd<ir::operation::Mul, ir::operation::Mul::Param, OpType::MUL>}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Concat.cc b/runtime/onert/core/src/interp/operations/Concat.cc new file mode 100644 index 000000000..53715e790 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Concat.cc @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019 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 <cker/operation/Concatenation.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Concat.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace concat +{ + +void prepareConcat(ExecEnv *env, const ir::Operation &node) +{ + const auto &concat_node = nnfw::misc::polymorphic_downcast<const ir::operation::Concat &>(node); + + const auto first_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto first_tensor = env->tensorAt(first_index); + uint32_t out_axis_dimension = 0; + const int32_t axis_raw = concat_node.param().axis; + const uint32_t axis = (axis_raw < 0) ? (axis_raw + first_tensor->num_dimensions()) : axis_raw; + + // All inputs shape should be same except axis dimension + // All inputs type should be same + for (auto input : node.getInputs()) + { + assert(first_tensor->num_dimensions() == env->tensorAt(input)->num_dimensions()); + assert(first_tensor->data_type() == env->tensorAt(input)->data_type()); + for (uint32_t i = 0; i < first_tensor->num_dimensions(); i++) + { + if (i == axis) + { + out_axis_dimension += env->tensorAt(input)->dimension(i); + continue; + } + assert(first_tensor->dimension(i) == env->tensorAt(input)->dimension(i)); + } + } + + // Make output tensor info using first input tensor info, and accumulated axis dimension value + auto out_shape = first_tensor->tensorInfo().shape(); + out_shape.dim(axis) = out_axis_dimension; + env->allocateIfNeeded(out_index, + ir::OperandInfo{out_shape, first_tensor->tensorInfo().typeInfo()}); + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Output shape should be same with input except axis dimension + // Output type should be same with input + assert(first_tensor->data_type() == out_tensor->data_type()); + for (uint32_t i = 0; i < first_tensor->num_dimensions(); i++) + { + if (i == axis) + { + continue; + } + assert(first_tensor->dimension(i) == out_tensor->dimension(i)); + } +} + +void invoke(const std::vector<const ITensor *> in_tensors, const ITensor *out_tensor, uint32_t axis) +{ + const uint32_t count = in_tensors.size(); + + // Calculate + nnfw::cker::ConcatenationParams cker_param; + cker_param.axis = (int8_t)axis; + cker_param.inputs_count = count; + + const auto out_shape = convertShape(out_tensor->tensorInfo().shape()); + + std::vector<nnfw::cker::Shape> in_shapes; + std::vector<const nnfw::cker::Shape *> in_shape_ptrs; + in_shapes.reserve(count); + in_shape_ptrs.reserve(count); + std::vector<const float *> in_ptrs; + for (uint32_t i = 0; i < count; i++) + { + in_shapes.push_back(convertShape(in_tensors[i]->tensorInfo().shape())); + in_shape_ptrs.push_back(&in_shapes[i]); + in_ptrs.push_back(reinterpret_cast<const float *>(in_tensors[i]->bufferRO())); + } + + auto out_buffer = out_tensor->buffer(); + float *out_ptr = reinterpret_cast<float *>(out_buffer); + + nnfw::cker::Concatenation<float>(cker_param, in_shape_ptrs.data(), in_ptrs.data(), out_shape, + out_ptr); +} + +void invokeConcat(const ExecEnv *env, const ir::Operation &node) +{ + const auto &concat_node = nnfw::misc::polymorphic_downcast<const ir::operation::Concat &>(node); + const int32_t axis_raw = concat_node.param().axis; + + std::vector<const ITensor *> in_tensors; + for (const auto &e : concat_node.getInputs()) + { + in_tensors.emplace_back(env->tensorAt(e)); + } + + const auto out_index = node.getOutputs().at(0); + const auto out_tensor = env->tensorAt(out_index); + const uint32_t axis = (axis_raw < 0) ? (axis_raw + out_tensor->num_dimensions()) : axis_raw; + + const auto data_type = in_tensors[0]->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(in_tensors, out_tensor, axis); + } + else + { + throw std::runtime_error{"NYI: Support float32 only"}; + } +} +} // namespace concat + +OpKernel *getConcat() +{ + static OpKernel kernel = {concat::prepareConcat, concat::invokeConcat}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Conv2D.cc b/runtime/onert/core/src/interp/operations/Conv2D.cc new file mode 100644 index 000000000..3c0087a61 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Conv2D.cc @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2019 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 <cker/operation/Conv.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Conv2D.h" +#include "util/Utils.h" +#include "util/ShapeInference.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace conv2d +{ + +void prepareConv2D(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(ir::operation::Conv2D::INPUT); + const auto kernel_index = node.getInputs().at(ir::operation::Conv2D::KERNEL); + const auto bias_index = node.getInputs().at(ir::operation::Conv2D::BIAS); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + const auto kernel_tensor = env->tensorAt(kernel_index); + const auto bias_tensor = env->tensorAt(bias_index); + + assert(in_tensor->num_dimensions() == 4); + assert(kernel_tensor->num_dimensions() == 4); + assert(bias_tensor->num_dimensions() == 1); + + UNUSED_RELEASE(in_tensor); + UNUSED_RELEASE(kernel_tensor); + UNUSED_RELEASE(bias_tensor); + + const auto output_info = env->graph().operands().at(out_index).info(); + if (output_info.total_size() == 0) + { + // Handle unspecified output shape + const auto &conv_node = nnfw::misc::polymorphic_downcast<const ir::operation::Conv2D &>(node); + const auto infered_output_shapes = shape_inference::inferConv2DShape( + in_tensor->tensorInfo().shape(), kernel_tensor->tensorInfo().shape(), conv_node.param()); + env->allocateIfNeeded(out_index, {infered_output_shapes[0], output_info.typeInfo()}); + } + else + { + env->allocateIfNeeded(out_index, output_info); + } + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Handle same ifm & ofm data type only + assert(in_tensor->data_type() == out_tensor->data_type()); + assert(out_tensor->num_dimensions() == 4); +} + +void invoke(const ITensor *ifm_tensor, const ITensor *ker_tensor, const ITensor *bias_tensor, + const ITensor *ofm_tensor, const ir::operation::Conv2D::Param ¶m) +{ + // TODO Support NCHW frontned + const auto ifm_shape = ifm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto ofm_shape = ofm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + // Kernel format is [depth_out, kernel_height, kernel_width, depth_in]. + const auto &ker_shape = ker_tensor->tensorInfo().shape(); + const auto ker_height = ker_shape.dim(1); + const auto ker_width = ker_shape.dim(2); + const auto padding = ir::calculatePadding(param.padding, ifm_shape, ofm_shape, param.stride, + ker_width, ker_height); + + // Calculate + float activation_min, activation_max; + calculateActivationRange(param.activation, &activation_min, &activation_max); + + nnfw::cker::ConvParams cker_param; + cker_param.padding_type = convertPaddingType(param.padding.type); + cker_param.padding_values.width = padding.left; + cker_param.padding_values.height = padding.top; + cker_param.stride_width = param.stride.horizontal; + cker_param.stride_height = param.stride.vertical; + cker_param.dilation_width_factor = 1; + cker_param.dilation_height_factor = 1; + cker_param.float_activation_min = activation_min; + cker_param.float_activation_max = activation_max; + + const auto cker_ifm_shape = convertShape(ifm_tensor->tensorInfo().shape()); + const auto cker_ker_shape = convertShape(ker_tensor->tensorInfo().shape()); + const auto cker_bias_shape = convertShape(bias_tensor->tensorInfo().shape()); + const auto cker_ofm_shape = convertShape(ofm_tensor->tensorInfo().shape()); + const float *ifm_ptr = reinterpret_cast<const float *>(ifm_tensor->bufferRO()); + const float *ker_ptr = reinterpret_cast<const float *>(ker_tensor->bufferRO()); + const float *bias_ptr = reinterpret_cast<const float *>(bias_tensor->bufferRO()); + float *ofm_ptr = reinterpret_cast<float *>(ofm_tensor->buffer()); + + nnfw::cker::Conv conv_kernel; + conv_kernel(cker_param, cker_ifm_shape, ifm_ptr, cker_ker_shape, ker_ptr, cker_bias_shape, + bias_ptr, cker_ofm_shape, ofm_ptr); +} + +void invokeConv2D(const ExecEnv *env, const ir::Operation &node) +{ + const auto &conv_node = nnfw::misc::polymorphic_downcast<const ir::operation::Conv2D &>(node); + + const auto ifm_index = node.getInputs().at(ir::operation::Conv2D::INPUT); + const auto ker_index = node.getInputs().at(ir::operation::Conv2D::KERNEL); + const auto bias_index = node.getInputs().at(ir::operation::Conv2D::BIAS); + const auto ofm_index = node.getOutputs().at(0); + + const auto ifm_tensor = env->tensorAt(ifm_index); + const auto ker_tensor = env->tensorAt(ker_index); + const auto bias_tensor = env->tensorAt(bias_index); + const auto ofm_tensor = env->tensorAt(ofm_index); + + const auto data_type = ifm_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(ifm_tensor, ker_tensor, bias_tensor, ofm_tensor, conv_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float32 only"}; + } +} +} // namespace conv2d + +OpKernel *getConv2D() +{ + static OpKernel kernel = {conv2d::prepareConv2D, conv2d::invokeConv2D}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/DepthwiseConv2D.cc b/runtime/onert/core/src/interp/operations/DepthwiseConv2D.cc new file mode 100644 index 000000000..2682fbd78 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/DepthwiseConv2D.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2019 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 <cker/operation/DepthwiseConv.h> +#include <misc/polymorphic_downcast.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/DepthwiseConv2D.h" +#include "util/Utils.h" +#include "util/ShapeInference.h" + +namespace onert +{ +namespace interp +{ + +namespace +{ + +void prepareDepthwiseConv(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(ir::operation::DepthwiseConv2D::INPUT); + const auto kernel_index = node.getInputs().at(ir::operation::DepthwiseConv2D::KERNEL); + const auto bias_index = node.getInputs().at(ir::operation::DepthwiseConv2D::BIAS); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + const auto kernel_tensor = env->tensorAt(kernel_index); + const auto bias_tensor = env->tensorAt(bias_index); + + assert(in_tensor->num_dimensions() == 4); + assert(kernel_tensor->num_dimensions() == 4); + assert(bias_tensor->num_dimensions() == 1); + + UNUSED_RELEASE(in_tensor); + UNUSED_RELEASE(kernel_tensor); + UNUSED_RELEASE(bias_tensor); + + // TODO handle unspecified output shape: + // calculate output shape using ifm shape, kernel shape, padding, stride + const auto output_info = env->graph().operands().at(out_index).info(); + if (output_info.total_size() == 0) + { + // Handle unspecified output shape + const auto &depth_conv_node = + nnfw::misc::polymorphic_downcast<const ir::operation::DepthwiseConv2D &>(node); + const auto infered_output_shapes = shape_inference::inferDepthwiseConv2DShape( + in_tensor->tensorInfo().shape(), kernel_tensor->tensorInfo().shape(), + depth_conv_node.param()); + env->allocateIfNeeded(out_index, {infered_output_shapes[0], output_info.typeInfo()}); + } + else + { + env->allocateIfNeeded(out_index, output_info); + } + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Handle same ifm & ofm data type only + assert(in_tensor->data_type() == out_tensor->data_type()); + assert(out_tensor->num_dimensions() == 4); +} + +void invoke(const ITensor *ifm_tensor, const ITensor *ker_tensor, const ITensor *bias_tensor, + const ITensor *ofm_tensor, const ir::operation::DepthwiseConv2D::Param ¶m) +{ + // TODO Support NCHW frontend + const auto ifm_shape = ifm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto ofm_shape = ofm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + // Kernel format is [1, kernel_height, kernel_width, depth_out]. + const auto &ker_shape = ker_tensor->tensorInfo().shape(); + const auto ker_height = ker_shape.dim(1); + const auto ker_width = ker_shape.dim(2); + const auto padding = ir::calculatePadding(param.padding, ifm_shape, ofm_shape, param.stride, + ker_width, ker_height); + + // Calculate + float activation_min, activation_max; + calculateActivationRange(param.activation, &activation_min, &activation_max); + + nnfw::cker::DepthwiseConvParams cker_param; + cker_param.padding_values.width = padding.left; + cker_param.padding_values.height = padding.top; + cker_param.depth_multiplier = param.multiplier; + cker_param.stride_width = param.stride.horizontal; + cker_param.stride_height = param.stride.vertical; + cker_param.dilation_width_factor = 1; + cker_param.dilation_height_factor = 1; + cker_param.float_activation_min = activation_min; + cker_param.float_activation_max = activation_max; + + const auto cker_ifm_shape = convertShape(ifm_tensor->tensorInfo().shape()); + const auto cker_ker_shape = convertShape(ker_tensor->tensorInfo().shape()); + const auto cker_bias_shape = convertShape(bias_tensor->tensorInfo().shape()); + const auto cker_ofm_shape = convertShape(ofm_tensor->tensorInfo().shape()); + const float *ifm_ptr = reinterpret_cast<const float *>(ifm_tensor->bufferRO()); + const float *ker_ptr = reinterpret_cast<const float *>(ker_tensor->bufferRO()); + const float *bias_ptr = reinterpret_cast<const float *>(bias_tensor->bufferRO()); + float *ofm_ptr = reinterpret_cast<float *>(ofm_tensor->buffer()); + + nnfw::cker::DepthwiseConv(cker_param, cker_ifm_shape, ifm_ptr, cker_ker_shape, ker_ptr, + cker_bias_shape, bias_ptr, cker_ofm_shape, ofm_ptr); +} + +void invokeDepthwiseConv(const ExecEnv *env, const ir::Operation &node) +{ + const auto &conv_node = static_cast<const ir::operation::DepthwiseConv2D &>(node); + + const auto ifm_index = node.getInputs().at(ir::operation::DepthwiseConv2D::INPUT); + const auto ker_index = node.getInputs().at(ir::operation::DepthwiseConv2D::KERNEL); + const auto bias_index = node.getInputs().at(ir::operation::DepthwiseConv2D::BIAS); + const auto ofm_index = node.getOutputs().at(0); + + const auto ifm_tensor = env->tensorAt(ifm_index); + const auto ker_tensor = env->tensorAt(ker_index); + const auto bias_tensor = env->tensorAt(bias_index); + const auto ofm_tensor = env->tensorAt(ofm_index); + + const auto data_type = ifm_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(ifm_tensor, ker_tensor, bias_tensor, ofm_tensor, conv_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float32 only"}; + } +} + +} // namespace + +OpKernel *getDepthwiseConv2D() +{ + static OpKernel kernel = {prepareDepthwiseConv, invokeDepthwiseConv}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/FullyConnected.cc b/runtime/onert/core/src/interp/operations/FullyConnected.cc new file mode 100644 index 000000000..8dfac43db --- /dev/null +++ b/runtime/onert/core/src/interp/operations/FullyConnected.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019 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 <cker/operation/FullyConnected.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/FullyConnected.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace fc +{ + +void prepareFC(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(ir::operation::FullyConnected::INPUT); + const auto kernel_index = node.getInputs().at(ir::operation::FullyConnected::WEIGHT); + const auto bias_index = node.getInputs().at(ir::operation::FullyConnected::BIAS); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + const auto kernel_tensor = env->tensorAt(kernel_index); + const auto bias_tensor = env->tensorAt(bias_index); + + UNUSED_RELEASE(in_tensor); + UNUSED_RELEASE(kernel_tensor); + UNUSED_RELEASE(bias_tensor); + + assert(in_tensor->num_dimensions() >= 2); + assert(kernel_tensor->num_dimensions() == 2); + assert(bias_tensor->num_dimensions() == 1); + + const auto input_size_with_batch = in_tensor->num_elements(); + const auto num_units = kernel_tensor->dimension(0); + const auto input_size = kernel_tensor->dimension(1); + const auto batch_size = input_size_with_batch / input_size; + assert(input_size_with_batch % input_size == 0); + assert(num_units == bias_tensor->dimension(0)); + + // Make output tensor info + ir::Shape output_shape(2); + output_shape.dim(0) = batch_size; + output_shape.dim(1) = num_units; + const ir::OperandInfo out_info{output_shape, in_tensor->tensorInfo().typeInfo()}; + env->allocateIfNeeded(out_index, out_info); + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Handle same ifm & ofm data type only + assert(in_tensor->data_type() == out_tensor->data_type()); + assert(out_tensor->num_dimensions() == 2); + assert(out_tensor->dimension(0) == batch_size); + assert(out_tensor->dimension(1) == num_units); +} + +void invoke(const ITensor *ifm_tensor, const ITensor *ker_tensor, const ITensor *bias_tensor, + const ITensor *ofm_tensor, const ir::operation::FullyConnected::Param ¶m) +{ + const auto ifm_buffer = ifm_tensor->bufferRO(); + const auto ker_buffer = ker_tensor->bufferRO(); + const auto bias_buffer = bias_tensor->bufferRO(); + auto ofm_buffer = ofm_tensor->buffer(); + + // Calculate + nnfw::cker::FullyConnectedParams cker_param; + cker_param.activation = convertActivationType(param.activation); + calculateActivationRange(param.activation, &cker_param.float_activation_min, + &cker_param.float_activation_max); + const auto cker_ifm_shape = convertShape(ifm_tensor->tensorInfo().shape()); + const auto cker_ker_shape = convertShape(ker_tensor->tensorInfo().shape()); + const auto cker_bias_shape = convertShape(bias_tensor->tensorInfo().shape()); + const auto cker_ofm_shape = convertShape(ofm_tensor->tensorInfo().shape()); + const float *ifm_ptr = reinterpret_cast<const float *>(ifm_buffer); + const float *ker_ptr = reinterpret_cast<const float *>(ker_buffer); + const float *bias_ptr = reinterpret_cast<const float *>(bias_buffer); + float *ofm_ptr = reinterpret_cast<float *>(ofm_buffer); + + nnfw::cker::FullyConnected(cker_param, cker_ifm_shape, ifm_ptr, cker_ker_shape, ker_ptr, + cker_bias_shape, bias_ptr, cker_ofm_shape, ofm_ptr); +} + +void invokeFC(const ExecEnv *env, const ir::Operation &node) +{ + const auto &conv_node = + nnfw::misc::polymorphic_downcast<const ir::operation::FullyConnected &>(node); + + const auto ifm_index = node.getInputs().at(ir::operation::FullyConnected::INPUT); + const auto ker_index = node.getInputs().at(ir::operation::FullyConnected::WEIGHT); + const auto bias_index = node.getInputs().at(ir::operation::FullyConnected::BIAS); + const auto ofm_index = node.getOutputs().at(0); + + const auto ifm_tensor = env->tensorAt(ifm_index); + const auto ker_tensor = env->tensorAt(ker_index); + const auto bias_tensor = env->tensorAt(bias_index); + const auto ofm_tensor = env->tensorAt(ofm_index); + + const auto data_type = ifm_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(ifm_tensor, ker_tensor, bias_tensor, ofm_tensor, conv_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float only"}; + } +} +} // namespace fc + +OpKernel *getFullyConnected() +{ + static OpKernel kernel = {fc::prepareFC, fc::invokeFC}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Gather.cc b/runtime/onert/core/src/interp/operations/Gather.cc new file mode 100644 index 000000000..b63e74886 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Gather.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019 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 <cker/operation/Gather.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Gather.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void prepareGather(ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(ir::operation::Gather::INPUT); + const auto indices_index = node.getInputs().at(ir::operation::Gather::INDICES); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + const auto indices_tensor = env->tensorAt(indices_index); + + // TODO handle unspecified output shape: + // calculate output shape using ifm shape, kernel shape, padding, stride + const auto output_info = env->graph().operands().at(output_index).info(); + if (output_info.total_size() == 0) + { + throw std::runtime_error{"Interp(Gather): NYI for unspecified output shape"}; + } + else + { + env->allocateIfNeeded(output_index, output_info); + } + + if (indices_tensor->data_type() != ir::DataType::INT32) + { + throw std::runtime_error{"Interp(Gather): Invalid indices data type"}; + } + + auto output_tensor = env->tensorAt(output_index); + auto output_rank = input_tensor->num_dimensions() + indices_tensor->num_dimensions() - 1; + + if (output_rank != output_tensor->num_dimensions()) + { + throw std::runtime_error{"Interp(Gather): Invalid output rank"}; + } + if (output_tensor->data_type() != input_tensor->data_type()) + { + throw std::runtime_error{"Interp(Gather): Invalid output data type"}; + } + + if (input_tensor->data_type() == ir::DataType::QUANT8_ASYMM && + input_tensor->tensorInfo().typeInfo() != output_tensor->tensorInfo().typeInfo()) + { + throw std::runtime_error{ + "Interp(Gather): Cannot handle different I/O QUANT8_ASYMM scale/offset"}; + } +} + +template <typename raw_type> +void invoke(const ITensor *input_tensors, const ITensor *indices_tensors, + const ITensor *output_tensor, uint32_t axis) +{ + // Calculate + nnfw::cker::GatherParams cker_param; + cker_param.axis = (int8_t)axis; + + const auto cker_input_shapes = convertShape(input_tensors->tensorInfo().shape()); + const auto cker_indices_shape = convertShape(indices_tensors->tensorInfo().shape()); + const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape()); + const raw_type *input_ptr = reinterpret_cast<const raw_type *>(input_tensors->bufferRO()); + const int32_t *indices_ptr = reinterpret_cast<const int32_t *>(indices_tensors->bufferRO()); + raw_type *output_ptr = reinterpret_cast<raw_type *>(output_tensor->buffer()); + + nnfw::cker::Gather<raw_type>(cker_param, cker_input_shapes, input_ptr, cker_indices_shape, + indices_ptr, cker_output_shape, output_ptr); +} + +void invokeGather(const ExecEnv *env, const ir::Operation &node) +{ + const auto &gather_node = nnfw::misc::polymorphic_downcast<const ir::operation::Gather &>(node); + const int32_t axis_raw = gather_node.param().axis; + + const auto input_index = node.getInputs().at(ir::operation::Gather::INPUT); + const auto indices_index = node.getInputs().at(ir::operation::Gather::INDICES); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + const auto indices_tensor = env->tensorAt(indices_index); + const auto output_tensor = env->tensorAt(output_index); + const uint32_t axis = (axis_raw < 0) ? (axis_raw + input_tensor->num_dimensions()) : axis_raw; + + const auto data_type = input_tensor->data_type(); + + switch (data_type) + { + case ir::DataType::FLOAT32: + invoke<float>(input_tensor, indices_tensor, output_tensor, axis); + break; + case ir::DataType::INT32: + invoke<int32_t>(input_tensor, indices_tensor, output_tensor, axis); + break; + case ir::DataType::QUANT8_ASYMM: + invoke<uint8_t>(input_tensor, indices_tensor, output_tensor, axis); + break; + default: + throw std::runtime_error{"Interp(Gather): NYI - Not supported type"}; + } +} + +} // namespace + +OpKernel *getGather() +{ + static OpKernel kernel = {prepareGather, invokeGather}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/InstanceNorm.cc b/runtime/onert/core/src/interp/operations/InstanceNorm.cc new file mode 100644 index 000000000..2538bcc39 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/InstanceNorm.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 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 <cker/operation/InstanceNorm.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/InstanceNorm.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace instancenorm +{ + +void prepareInstanceNorm(ExecEnv *env, const ir::Operation &node) +{ + const auto &instancenorm_node = + nnfw::misc::polymorphic_downcast<const ir::operation::InstanceNorm &>(node); + + const auto input_index = node.getInputs().at(instancenorm_node.INPUT); + const auto output_index = node.getOutputs().at(0); + const auto input_tensor = env->tensorAt(input_index); + + if (input_tensor->num_dimensions() != 4) + { + throw std::runtime_error{"Interp(InstanceNorm): Input should be 4D-tensor"}; + } + + // Output shape should be same with input + env->allocateIfNeeded(output_index, input_tensor->tensorInfo()); + + auto output_tensor = env->tensorAt(output_index); + UNUSED_RELEASE(output_tensor); + + // Handle same ifm & ofm data type only + assert(input_tensor->data_type() == output_tensor->data_type()); + assert(input_tensor->tensorInfo().shape() == output_tensor->tensorInfo().shape()); +} + +inline void setActivationParams(float min, float max, nnfw::cker::InstanceNormParams *params) +{ + params->float_activation_min = min; + params->float_activation_max = max; +} + +void invoke(const ITensor *input_tensor, const ITensor *gamma_tensor, const ITensor *beta_tensor, + const ITensor *output_tensor, const ir::operation::InstanceNorm::Param ¶m) +{ + // Calculate + float activation_min, activation_max; + calculateActivationRange(param.activation, &activation_min, &activation_max); + + nnfw::cker::InstanceNormParams cker_param; + cker_param.epsilon = param.epsilon; + cker_param.float_activation_min = activation_min; + cker_param.float_activation_max = activation_max; + + const auto cker_input_shape = convertShape(input_tensor->tensorInfo().shape()); + const auto cker_gamma_shape = convertShape(gamma_tensor->tensorInfo().shape()); + const auto cker_beta_shape = convertShape(beta_tensor->tensorInfo().shape()); + const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape()); + const float *input_ptr = reinterpret_cast<const float *>(input_tensor->bufferRO()); + const float *gamma_ptr = reinterpret_cast<const float *>(gamma_tensor->bufferRO()); + const float *beta_ptr = reinterpret_cast<const float *>(beta_tensor->bufferRO()); + float *output_ptr = reinterpret_cast<float *>(output_tensor->buffer()); + + nnfw::cker::InstanceNorm(cker_param, cker_input_shape, input_ptr, cker_gamma_shape, gamma_ptr, + cker_beta_shape, beta_ptr, cker_output_shape, output_ptr); +} + +void invokeInstanceNorm(const ExecEnv *env, const ir::Operation &node) +{ + const auto &instancenorm_node = + nnfw::misc::polymorphic_downcast<const ir::operation::InstanceNorm &>(node); + + const auto input_index = node.getInputs().at(instancenorm_node.INPUT); + const auto gamma_index = node.getInputs().at(instancenorm_node.GAMMA); + const auto beta_index = node.getInputs().at(instancenorm_node.BETA); + const auto out_index = node.getOutputs().at(0); + const auto input_tensor = env->tensorAt(input_index); + const auto gamma_tensor = env->tensorAt(gamma_index); + const auto beta_tensor = env->tensorAt(beta_index); + const auto out_tensor = env->tensorAt(out_index); + const auto data_type = input_tensor->data_type(); + + if (data_type == ir::DataType::FLOAT32) + { + invoke(input_tensor, gamma_tensor, beta_tensor, out_tensor, instancenorm_node.param()); + } + else + { + throw std::runtime_error{"NYI: Unsupported data type"}; + } +} +} // namespace instancenorm + +OpKernel *getInstanceNorm() +{ + static OpKernel kernel = {instancenorm::prepareInstanceNorm, instancenorm::invokeInstanceNorm}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Logistic.cc b/runtime/onert/core/src/interp/operations/Logistic.cc new file mode 100644 index 000000000..c23cbb782 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Logistic.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 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 <cker/operation/Logistic.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Logistic.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void prepareLogistic(ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(0); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + + const auto output_info = env->graph().operands().at(output_index).info(); + + // Check shape and type lhs is same with rhs + // TODO Util function to compare TensorInfo + if (output_info.total_size() == 0) + { + throw std::runtime_error{"Interp(TConv): NYI unspecified output shape"}; + } + else + { + env->allocateIfNeeded(output_index, output_info); + } + + const auto output_tensor = env->tensorAt(output_index); + if (input_tensor->data_type() != output_tensor->data_type()) + { + throw std::runtime_error{"Interp(Logistic): Invalid output type"}; + } +} + +void invoke(const ITensor *input_tensor, const ITensor *output_tensor) +{ + const auto input_buffer = input_tensor->bufferRO(); + auto output_buffer = output_tensor->buffer(); + + const auto cker_input_shape = convertShape(input_tensor->tensorInfo().shape()); + const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape()); + const float *input_ptr = reinterpret_cast<const float *>(input_buffer); + float *output_ptr = reinterpret_cast<float *>(output_buffer); + + nnfw::cker::Logistic(cker_input_shape, input_ptr, cker_output_shape, output_ptr); +} + +void invokeLogistic(const ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(0); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + const auto output_tensor = env->tensorAt(output_index); + + const auto data_type = input_tensor->data_type(); + + if (data_type == ir::DataType::FLOAT32) + { + invoke(input_tensor, output_tensor); + } + else + { + throw std::runtime_error{"Interp(Logistic): NYI - Unsupported data type"}; + } +} +} // namespace + +OpKernel *getLogistic() +{ + static OpKernel kernel = {prepareLogistic, invokeLogistic}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/MaxPool2D.cc b/runtime/onert/core/src/interp/operations/MaxPool2D.cc new file mode 100644 index 000000000..d524f356e --- /dev/null +++ b/runtime/onert/core/src/interp/operations/MaxPool2D.cc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019 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 <cker/operation/MaxPool.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/MaxPool2D.h" +#include "util/Utils.h" +#include "util/ShapeInference.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void prepareMaxPool2D(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + + assert(in_tensor->num_dimensions() == 4); + UNUSED_RELEASE(in_tensor); + + const auto output_info = env->graph().operands().at(out_index).info(); + if (output_info.total_size() == 0) + { + // Handle unspecified output shape + const auto &maxpool_node = + nnfw::misc::polymorphic_downcast<const ir::operation::MaxPool2D &>(node); + const auto infered_output_shapes = + shape_inference::inferMaxPoolShape(in_tensor->tensorInfo().shape(), maxpool_node.param()); + env->allocateIfNeeded(out_index, {infered_output_shapes[0], output_info.typeInfo()}); + } + else + { + env->allocateIfNeeded(out_index, output_info); + } + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Handle same ifm & ofm data type only + assert(in_tensor->data_type() == out_tensor->data_type()); + assert(out_tensor->num_dimensions() == 4); +} + +void invoke(const ITensor *in_tensor, const ITensor *out_tensor, + const ir::operation::MaxPool2D::Param ¶m) +{ + // TODO support NCHW frontend + const auto ifm_shape = in_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto ofm_shape = out_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto padding = + ir::calculatePadding(param.padding, ifm_shape, ofm_shape, param.stride, param.kw, param.kh); + // Calculate + nnfw::cker::PoolParams cker_param; + calculateActivationRange(param.activation, &cker_param.float_activation_min, + &cker_param.float_activation_max); + cker_param.filter_width = param.kw; + cker_param.filter_height = param.kh; + cker_param.padding_values.width = padding.left; + cker_param.padding_values.height = padding.top; + cker_param.stride_width = param.stride.horizontal; + cker_param.stride_height = param.stride.vertical; + + const auto in_shape = convertShape(in_tensor->tensorInfo().shape()); + const auto out_shape = convertShape(out_tensor->tensorInfo().shape()); + const float *in_ptr = reinterpret_cast<const float *>(in_tensor->bufferRO()); + float *out_ptr = reinterpret_cast<float *>(out_tensor->buffer()); + + nnfw::cker::MaxPool(cker_param, in_shape, in_ptr, out_shape, out_ptr); +} + +void invokeMaxPool2D(const ExecEnv *env, const ir::Operation &node) +{ + const auto &maxpool_node = + nnfw::misc::polymorphic_downcast<const ir::operation::MaxPool2D &>(node); + + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + const auto out_tensor = env->tensorAt(out_index); + + const auto data_type = in_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(in_tensor, out_tensor, maxpool_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float32 only"}; + } +} +} // namespace + +OpKernel *getMaxPool2D() +{ + static OpKernel kernel = {prepareMaxPool2D, invokeMaxPool2D}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/OperationUtil.h b/runtime/onert/core/src/interp/operations/OperationUtil.h new file mode 100644 index 000000000..cdabe508d --- /dev/null +++ b/runtime/onert/core/src/interp/operations/OperationUtil.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_INTERP_OPERATIONS_OPERATION_UTILS_H_ +#define __ONERT_INTERP_OPERATIONS_OPERATION_UTILS_H_ + +#include "ir/Shape.h" +#include "ir/InternalType.h" +#include "ir/Padding.h" + +#include <cker/Shape.h> +#include <cker/Types.h> + +namespace onert +{ +namespace interp +{ + +inline nnfw::cker::Shape convertShape(const ir::Shape &shape) +{ + auto dimensions = std::vector<uint32_t>(shape.dims().begin(), shape.dims().end()); + + std::vector<int32_t> raw_shape; + raw_shape.resize(4); + + for (uint32_t i = 0; i < 4; ++i) + { + if (i >= dimensions.size()) + { + raw_shape[i] = 1; + } + else + { + raw_shape[i] = dimensions[i]; + } + } + + return nnfw::cker::GetShape(raw_shape); +} + +inline nnfw::cker::Shape convertExtendShape(const ir::Shape &shape) +{ + auto dimensions = std::vector<uint32_t>(shape.dims().begin(), shape.dims().end()); + + std::vector<int32_t> raw_shape; + raw_shape.resize(4); + uint32_t start = 4 - dimensions.size(); + + for (uint32_t i = 0; i < 4; ++i) + { + if (i < start) + { + raw_shape[i] = 1; + } + else + { + raw_shape[i] = dimensions[i - start]; + } + } + + return nnfw::cker::GetShape(raw_shape); +} + +inline nnfw::cker::FusedActivationFunctionType +convertActivationType(const ir::Activation activation) +{ + switch (activation) + { + case ir::Activation::NONE: + return nnfw::cker::FusedActivationFunctionType::kNone; + case ir::Activation::RELU: + return nnfw::cker::FusedActivationFunctionType::kRelu; + case ir::Activation::RELU1: + return nnfw::cker::FusedActivationFunctionType::kRelu1; + case ir::Activation::RELU6: + return nnfw::cker::FusedActivationFunctionType::kRelu6; + default: + throw std::runtime_error{"CPU backend: Cannot convert activation type"}; + } +} + +template <typename T> +void calculateActivationRange(ir::Activation activation, T *activation_min, T *activation_max) +{ + if (activation == ir::Activation::RELU) + { + *activation_min = 0; + *activation_max = std::numeric_limits<T>::max(); + } + else if (activation == ir::Activation::RELU6) + { + *activation_min = 0; + *activation_max = 6; + } + else if (activation == ir::Activation::RELU1) + { + *activation_min = -1; + *activation_max = 1; + } + else if (activation == ir::Activation::NONE) + { + *activation_min = std::numeric_limits<T>::lowest(); + *activation_max = std::numeric_limits<T>::max(); + } + else + { + throw std::runtime_error{"Unsupported activation type"}; + } +} + +inline ir::Shape calcBroadcastShape(const ir::Shape &lhs, const ir::Shape &rhs, bool &success) +{ + int lhs_rank = lhs.rank(); + int rhs_rank = rhs.rank(); + + int out_rank = (lhs_rank > rhs_rank ? lhs_rank : rhs_rank); + ir::Shape out_shape(out_rank); + + int lhs_idim = lhs_rank - 1; + int rhs_idim = rhs_rank - 1; + success = true; + for (int out_idim = out_rank - 1; out_idim >= 0; out_idim--) + { + if (lhs_idim == -1 && rhs_idim == -1) + { + // invalid result + success = false; + break; + } + + if (lhs_idim == -1) + { + out_shape.dim(out_idim) = rhs.dim(rhs_idim); + rhs_idim--; + } + else if (rhs_idim == -1) + { + out_shape.dim(out_idim) = lhs.dim(lhs_idim); + lhs_idim--; + } + else + { + if (lhs.dim(lhs_idim) == rhs.dim(rhs_idim)) + { + out_shape.dim(out_idim) = lhs.dim(lhs_idim); + lhs_idim--; + rhs_idim--; + } + else if (lhs.dim(lhs_idim) == 1) + { + out_shape.dim(out_idim) = rhs.dim(rhs_idim); + lhs_idim--; + rhs_idim--; + } + else if (rhs.dim(rhs_idim) == 1) + { + out_shape.dim(out_idim) = lhs.dim(lhs_idim); + lhs_idim--; + rhs_idim--; + } + else + { + // invalid result + success = false; + break; + } + } + } + + if (lhs_idim != -1 || rhs_idim != -1) + { + // invalid result + success = false; + } + return out_shape; +} + +inline nnfw::cker::PaddingType convertPaddingType(ir::PaddingType ir_padding_type) +{ + switch (ir_padding_type) + { + case ir::PaddingType::EXPLICIT: + return nnfw::cker::PaddingType::kNone; + case ir::PaddingType::SAME: + return nnfw::cker::PaddingType::kSame; + case ir::PaddingType::VALID: + return nnfw::cker::PaddingType::kValid; + default: + throw std::runtime_error("Wrong padding type."); + break; + } +} + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_OPERATIONS_OPERATION_UTILS_H_ diff --git a/runtime/onert/core/src/interp/operations/Pad.cc b/runtime/onert/core/src/interp/operations/Pad.cc new file mode 100644 index 000000000..d2e3627b4 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Pad.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019 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 <cker/operation/Pad.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Pad.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void preparePad(ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(ir::operation::Pad::INPUT); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + + const auto output_info = env->graph().operands().at(output_index).info(); + + // Check shape and type lhs is same with rhs + // TODO Util function to compare TensorInfo + if (output_info.total_size() == 0) + { + throw std::runtime_error{"Interp(Pad): NYI unspecified output shape"}; + } + else + { + env->allocateIfNeeded(output_index, output_info); + } + + const auto output_tensor = env->tensorAt(output_index); + if (input_tensor->data_type() != output_tensor->data_type()) + { + throw std::runtime_error{"Interp(Pad): Invalid output type"}; + } +} + +void invoke(const ITensor *input_tensor, const ITensor *pad_tensor, const ITensor *output_tensor) +{ + const auto input_buffer = input_tensor->bufferRO(); + const auto pad_buffer = pad_tensor->bufferRO(); + auto output_buffer = output_tensor->buffer(); + + int32_t pad_rank = pad_tensor->dimension(0); + + const auto cker_input_shape = convertShape(input_tensor->tensorInfo().shape()); + const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape()); + const float *input_ptr = reinterpret_cast<const float *>(input_buffer); + const int32_t *pad_ptr = reinterpret_cast<const int32_t *>(pad_buffer); + float *output_ptr = reinterpret_cast<float *>(output_buffer); + + nnfw::cker::Pad(pad_ptr, pad_rank, cker_input_shape, input_ptr, cker_output_shape, output_ptr, + nullptr); +} + +void invokePad(const ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(ir::operation::Pad::INPUT); + const auto pad_index = node.getInputs().at(ir::operation::Pad::PAD); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + const auto pad_tensor = env->tensorAt(pad_index); + const auto output_tensor = env->tensorAt(output_index); + + const auto data_type = input_tensor->data_type(); + + if (data_type == ir::DataType::FLOAT32) + { + invoke(input_tensor, pad_tensor, output_tensor); + } + else + { + throw std::runtime_error{"Interp(Pad): NYI - Unsupported data type"}; + } +} +} // namespace + +OpKernel *getPad() +{ + static OpKernel kernel = {preparePad, invokePad}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Reshape.cc b/runtime/onert/core/src/interp/operations/Reshape.cc new file mode 100644 index 000000000..3a118456b --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Reshape.cc @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 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 "interp/Registration.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void prepare(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + // Unspecified shape is not supported in operation node spec now + const auto output_info = env->graph().operands().at(out_index).info(); + env->allocateAndShareIfNeeded(out_index, output_info, in_index); + + assert(output_info.total_size() == env->graph().operands().at(in_index).info().total_size()); +} + +void invoke(const ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + if (env->tensorAt(in_index)->bufferRO() == env->tensorAt(out_index)->bufferRO()) + { + // Same data + return; + } + + const auto output_info = env->graph().operands().at(out_index).info(); + memcpy(env->tensorAt(out_index)->buffer(), env->tensorAt(in_index)->bufferRO(), + output_info.total_size()); +} + +} // namespace + +OpKernel *getReshape() +{ + static OpKernel kernel = {prepare, invoke}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/Softmax.cc b/runtime/onert/core/src/interp/operations/Softmax.cc new file mode 100644 index 000000000..afc4e81f7 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Softmax.cc @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 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 <cker/operation/SoftMax.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Softmax.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void Softmax2D(const float *in, const int input_size, const int batch_size, const float beta, + float *out) +{ + assert(input_size > 0); + + // For each batch + for (int b = 0; b < batch_size; b++) + { + // Find the max coeff. + float max_coeff = in[0]; + for (int i = 1; i < input_size; i++) + { + if (in[i] > max_coeff) + max_coeff = in[i]; + } + + // Compute the normalized sum of exps. + float exp_sum = 0.0; + for (int i = 0; i < input_size; i++) + { + out[i] = std::exp((in[i] - max_coeff) * beta); + exp_sum += out[i]; + } + + // Divide by the sum of exps. + float reciprocal_sum_exp = 1.f / exp_sum; + for (int i = 0; i < input_size; i++) + { + out[i] *= reciprocal_sum_exp; + } + + // Advance in and out pointers for the next batch. + in += input_size; + out += input_size; + } +} + +void prepareSoftMax(ExecEnv *env, const ir::Operation &node) +{ + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + UNUSED_RELEASE(in_tensor); + + assert((in_tensor->num_dimensions() == 4) || (in_tensor->num_dimensions() == 2)); + + // Output shape should be same with input + // Output type is pre-defined in model + const auto output_shape = env->graph().operands().at(in_index).info().shape(); + const auto output_type = env->graph().operands().at(out_index).info().typeInfo(); + + const ir::OperandInfo output_info{output_shape, output_type}; + env->allocateIfNeeded(out_index, output_info); + + auto out_tensor = env->tensorAt(out_index); + UNUSED_RELEASE(out_tensor); + + // Check output shape is same with input + assert(out_tensor->num_dimensions() == out_tensor->num_dimensions()); + for (uint32_t i = 0; i < in_tensor->num_dimensions(); i++) + { + assert(in_tensor->dimension(i) == out_tensor->dimension(i)); + } +} + +void invoke(const ITensor *in_tensor, const ITensor *out_tensor, + const ir::operation::Softmax::Param ¶m) +{ + const float *in_ptr = reinterpret_cast<const float *>(in_tensor->bufferRO()); + float *out_ptr = reinterpret_cast<float *>(out_tensor->buffer()); + + float beta = param.beta; + + if (in_tensor->num_dimensions() == 2) + { + uint32_t batch_size = in_tensor->dimension(0); + uint32_t input_size = in_tensor->dimension(1); + + Softmax2D(in_ptr, input_size, batch_size, beta, out_ptr); + } + else if (in_tensor->num_dimensions() == 4) + { + const auto in_shape = convertShape(in_tensor->tensorInfo().shape()); + const auto out_shape = convertShape(out_tensor->tensorInfo().shape()); + + nnfw::cker::SoftmaxParams cker_param; + cker_param.beta = beta; + + nnfw::cker::Softmax(cker_param, in_shape, in_ptr, out_shape, out_ptr); + } + else + { + throw std::runtime_error{"Unsuported input dimension: support 2D or 4D"}; + } +} + +void invokeSoftMax(const ExecEnv *env, const ir::Operation &node) +{ + const auto &softmax_node = nnfw::misc::polymorphic_downcast<const ir::operation::Softmax &>(node); + + const auto in_index = node.getInputs().at(0); + const auto out_index = node.getOutputs().at(0); + + const auto in_tensor = env->tensorAt(in_index); + const auto out_tensor = env->tensorAt(out_index); + + const auto in_data_type = in_tensor->data_type(); + const auto out_data_type = out_tensor->data_type(); + if ((in_data_type == ir::DataType::FLOAT32) && (out_data_type == ir::DataType::FLOAT32)) + { + invoke(in_tensor, out_tensor, softmax_node.param()); + } + else + { + throw std::runtime_error{"NYI: Support float32 only"}; + } +} + +} // namespace + +OpKernel *getSoftmax() +{ + static OpKernel kernel = {prepareSoftMax, invokeSoftMax}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/TransposeConv.cc b/runtime/onert/core/src/interp/operations/TransposeConv.cc new file mode 100644 index 000000000..cc2ced26b --- /dev/null +++ b/runtime/onert/core/src/interp/operations/TransposeConv.cc @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019 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 <cker/operation/TransposeConv.h> +#include <misc/polymorphic_downcast.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/TransposeConv.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +void prepareTransposeConv(ExecEnv *env, const ir::Operation &node) +{ + const auto ifm_index = node.getInputs().at(ir::operation::TransposeConv::INPUT); + const auto ker_index = node.getInputs().at(ir::operation::TransposeConv::KERNEL); + const auto ofm_shape_index = node.getInputs().at(ir::operation::TransposeConv::OUTPUT_SHAPE); + const auto ofm_index = node.getOutputs().at(0); + + const auto ifm_tensor = env->tensorAt(ifm_index); + const auto ker_tensor = env->tensorAt(ker_index); + const auto ofm_shape_tensor = env->tensorAt(ofm_shape_index); + + assert(ifm_tensor->num_dimensions() == 4); + assert(ker_tensor->num_dimensions() == 4); + assert(ofm_shape_tensor->num_dimensions() == 1); + + UNUSED_RELEASE(ifm_tensor); + UNUSED_RELEASE(ker_tensor); + UNUSED_RELEASE(ofm_shape_tensor); + + const auto output_info = env->graph().operands().at(ofm_index).info(); + if (output_info.total_size() == 0) + { + // TODO: Handle unspecified output shape + throw std::runtime_error{"Interp(TConv): NYI unspecified output shape"}; + } + else + { + env->allocateIfNeeded(ofm_index, output_info); + } + + auto ofm_tensor = env->tensorAt(ofm_index); + UNUSED_RELEASE(ofm_tensor); + + // Handle same ifm & ofm data type only + if (ifm_tensor->data_type() != ofm_tensor->data_type()) + { + throw std::runtime_error{"Interp(TConv): Different I/O data dype"}; + } + + if (ofm_tensor->num_dimensions() != 4) + { + throw std::runtime_error{"Interp(TConv): Invalid output rank"}; + } +} + +void invoke(const ITensor *ifm_tensor, const ITensor *ker_tensor, const ITensor *ofm_tensor, + const ir::operation::TransposeConv::Param ¶m) +{ + const auto ifm_shape = ifm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + const auto ofm_shape = ofm_tensor->tensorInfo().shape().asFeature(ir::Layout::NHWC); + // Kernel format is [depth_out, kernel_height, kernel_width, depth_in]. + const auto ker_shape = ker_tensor->tensorInfo().shape(); + const auto ker_height = ker_shape.dim(1); + const auto ker_width = ker_shape.dim(2); + const auto padding = ir::calculatePadding(param.padding, ofm_shape, ifm_shape, param.stride, + ker_width, ker_height); + + nnfw::cker::TransposeConvParams cker_param; + cker_param.padding_values.width = padding.left; + cker_param.padding_values.height = padding.top; + cker_param.stride_width = param.stride.horizontal; + cker_param.stride_height = param.stride.vertical; + cker_param.dilation_width_factor = 1; + cker_param.dilation_height_factor = 1; + + const auto cker_ifm_shape = convertShape(ifm_tensor->tensorInfo().shape()); + const auto cker_ker_shape = convertShape(ker_tensor->tensorInfo().shape()); + const auto cker_ofm_shape = convertShape(ofm_tensor->tensorInfo().shape()); + const float *ifm_ptr = reinterpret_cast<const float *>(ifm_tensor->bufferRO()); + const float *ker_ptr = reinterpret_cast<const float *>(ker_tensor->bufferRO()); + float *ofm_ptr = reinterpret_cast<float *>(ofm_tensor->buffer()); + + nnfw::cker::TransposeConv(cker_param, cker_ifm_shape, ifm_ptr, cker_ker_shape, ker_ptr, + cker_ofm_shape, ofm_ptr); +} + +void invokeTransposeConv(const ExecEnv *env, const ir::Operation &node) +{ + const auto &tconv_node = + nnfw::misc::polymorphic_downcast<const ir::operation::TransposeConv &>(node); + + const auto ifm_index = node.getInputs().at(ir::operation::TransposeConv::INPUT); + const auto ker_index = node.getInputs().at(ir::operation::TransposeConv::KERNEL); + const auto ofm_index = node.getOutputs().at(0); + + const auto ifm_tensor = env->tensorAt(ifm_index); + const auto ker_tensor = env->tensorAt(ker_index); + const auto ofm_tensor = env->tensorAt(ofm_index); + + const auto data_type = ifm_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + invoke(ifm_tensor, ker_tensor, ofm_tensor, tconv_node.param()); + } + else + { + throw std::runtime_error{"Interp(TConv): Support float32 only"}; + } +} + +} // namespace + +OpKernel *getTransposeConv() +{ + static OpKernel kernel = {prepareTransposeConv, invokeTransposeConv}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/interp/operations/UnaryActivations.cc b/runtime/onert/core/src/interp/operations/UnaryActivations.cc new file mode 100644 index 000000000..ea5e2417b --- /dev/null +++ b/runtime/onert/core/src/interp/operations/UnaryActivations.cc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2020 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 <cmath> + +#include "OperationUtil.h" + +#include "interp/Registration.h" + +#include "ir/operation/ReLU.h" +#include "ir/operation/ReLU1.h" +#include "ir/operation/ReLU6.h" +#include "ir/operation/Tanh.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +enum class ActivationType +{ + ReLU, + ReLU1, + ReLU6, + Tanh +}; + +void prepare(ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(0); + const auto output_index = node.getOutputs().at(0); + + const auto input_tensor = env->tensorAt(input_index); + + const auto output_info = env->graph().operands().at(output_index).info(); + if (output_info.total_size() == 0) + { + // Output's shape and type is same with input + auto input_info = input_tensor->tensorInfo(); + // We can handle already allocated (ex. model output) + env->allocateIfNeeded(output_index, input_info); + } + else + { + env->allocateIfNeeded(output_index, output_info); + } + + const auto output_tensor = env->tensorAt(output_index); + // Check shape and type lhs is same with output + // TODO Util function to compare TensorInfo + if (input_tensor->data_type() != output_tensor->data_type()) + { + throw std::runtime_error{"Interp(Activations): Invalid output type"}; + } +} + +template <ActivationType act_type> +void evalFloat(const float *input_ptr, float *output_ptr, uint64_t num_elements) +{ + std::function<float(const float &)> fn = [](const float &) { return std::nanf(""); }; + switch (act_type) + { + case ActivationType::ReLU: + fn = [](const float &in) { return std::max(0.f, in); }; + break; + case ActivationType::ReLU1: + fn = [](const float &in) { return std::min(std::max(-1.f, in), 1.f); }; + break; + case ActivationType::ReLU6: + fn = [](const float &in) { return std::min(std::max(0.f, in), 6.f); }; + break; + case ActivationType::Tanh: + fn = [](const float &in) { return std::tanh(in); }; + break; + default: + throw std::runtime_error{"Interp(Activations): NYI - Unsupported activation"}; + break; + } + + const float *input_end = input_ptr + num_elements; + for (; input_ptr < input_end; input_ptr++, output_ptr++) + { + *output_ptr = fn(*input_ptr); + } +} + +template <ActivationType act_type> void invoke(const ExecEnv *env, const ir::Operation &node) +{ + const auto input_index = node.getInputs().at(0); + const auto output_index = node.getOutputs().at(0); + + // Check lhs shape is same with rhs (with broadcast) + const auto input_tensor = env->tensorAt(input_index); + const auto output_tensor = env->tensorAt(output_index); + + const auto data_type = input_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + uint64_t elements = input_tensor->num_elements(); + const float *input_start = reinterpret_cast<const float *>(input_tensor->bufferRO()); + float *out = reinterpret_cast<float *>(output_tensor->buffer()); + + evalFloat<act_type>(input_start, out, elements); + } + else + { + throw std::runtime_error{"Interp(ReLU6): NYI - Support float only"}; + } +} + +} // namespace + +OpKernel *getReLU() +{ + static OpKernel kernel = {prepare, invoke<ActivationType::ReLU>}; + return &kernel; +} + +OpKernel *getReLU1() +{ + static OpKernel kernel = {prepare, invoke<ActivationType::ReLU1>}; + return &kernel; +} + +OpKernel *getReLU6() +{ + static OpKernel kernel = {prepare, invoke<ActivationType::ReLU6>}; + return &kernel; +} + +OpKernel *getTanh() +{ + static OpKernel kernel = {prepare, invoke<ActivationType::Tanh>}; + return &kernel; +} + +} // namespace interp +} // namespace onert diff --git a/runtime/onert/core/src/ir/Coordinates.cc b/runtime/onert/core/src/ir/Coordinates.cc new file mode 100644 index 000000000..a02a56567 --- /dev/null +++ b/runtime/onert/core/src/ir/Coordinates.cc @@ -0,0 +1,50 @@ +/* + * 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 "ir/Coordinates.h" + +#include <cassert> + +namespace onert +{ +namespace ir +{ + +Coordinates convertCoordinates(const Coordinates &from_coordinates, Layout from_layout, + Layout to_layout) +{ + assert(from_coordinates.size() == 4); + Coordinates to{from_coordinates}; + if (from_layout == Layout::NHWC && to_layout == Layout::NCHW) + { + to.set(0, from_coordinates[0]); + to.set(1, from_coordinates[3]); + to.set(2, from_coordinates[1]); + to.set(3, from_coordinates[2]); + } + else if (from_layout == Layout::NCHW && to_layout == Layout::NHWC) + { + to.set(0, from_coordinates[0]); + to.set(1, from_coordinates[2]); + to.set(2, from_coordinates[3]); + to.set(3, from_coordinates[1]); + } + + return to; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Graph.cc b/runtime/onert/core/src/ir/Graph.cc new file mode 100644 index 000000000..837a88a6b --- /dev/null +++ b/runtime/onert/core/src/ir/Graph.cc @@ -0,0 +1,103 @@ +/* + * 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 "ir/Graph.h" + +#include <algorithm> +#include <bitset> +#include <sstream> + +#include "util/logging.h" +#include "verifier/Verifier.h" +#include "ir/operation/LowerInfo.h" +#include "ir/operand/LowerInfo.h" +#include "ir/operand/PermuteFactor.h" +#include "ir/GraphIterator.h" +#include "backend/IConfig.h" + +namespace onert +{ +namespace ir +{ + +Graph::Graph() = default; + +Graph::~Graph(void) = default; + +OperandIndex Graph::addOperand(const Shape &shape, const TypeInfo &type) +{ + return _operands.emplace(shape, type); +} + +OperationIndex Graph::addOperation(std::unique_ptr<Operation> &&node) +{ + assert(isBuildingPhase()); + return _operations.push(std::move(node)); +} + +void Graph::setOperandValue(const OperandIndex &ind, std::shared_ptr<Data> data) +{ + assert(isBuildingPhase()); + assert(_operands.exist(ind)); + _operands.at(ind).data(std::move(data)); +} + +void Graph::addInput(const OperandIndex &ind) +{ + assert(isBuildingPhase()); + _inputs.append(ind); +} + +void Graph::addOutput(const OperandIndex &ind) +{ + assert(isBuildingPhase()); + _outputs.append(ind); +} + +void Graph::finishBuilding(void) +{ + assert(isBuildingPhase()); + _phase = Phase::MODEL; + + // Initialize operand use-def + initializeUseDef(); + + // Call graph verifications for the MODEL phase + { + assert(verifier::DAGChecker().verify(*this)); + assert(verifier::EdgeConsistencyChecker().verify(*this)); + } +} + +void Graph::initializeUseDef() +{ + operations().iterate([&](const OperationIndex &index, const Operation &node) -> void { + auto outputs = node.getOutputs(); + for (auto output : outputs) + { + operands().at(output).appendDef(index); + } + + auto inputs = node.getInputs(); + for (auto input : inputs) + { + operands().at(input).appendUse(index); + } + }); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/GraphIterator.cc b/runtime/onert/core/src/ir/GraphIterator.cc new file mode 100644 index 000000000..9e8b3e533 --- /dev/null +++ b/runtime/onert/core/src/ir/GraphIterator.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 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 "GraphIterator.h" + +#include "ir/OperationIndexMap.h" +#include "ir/Graph.h" + +namespace onert +{ +namespace ir +{ + +// Explicit instantiations to have implementation in the source file. + +template class DefaultIterator<true>; +template class DefaultIterator<false>; + +template class PostDfsIterator<true>; +template class PostDfsIterator<false>; + +// +// Graph::DefaultIterator +// + +template <bool is_const> +void DefaultIterator<is_const>::iterate(GraphRef graph, const IterFn &fn) const +{ + graph.operations().iterate( + [&](const OperationIndex &index, NodeRef node) -> void { fn(index, node); }); +} + +// +// Graph::PostDfsIterator +// + +template <bool is_const> +void PostDfsIterator<is_const>::iterate(GraphRef graph, const IterFn &fn) const +{ + assert(!graph.isBuildingPhase()); // Restrict iteration condition + + OperationIndexMap<bool> visited; + graph.operations().iterate([&](const OperationIndex &index, NodeRef) { visited[index] = false; }); + + std::function<void(const OperationIndex &, NodeRef)> dfs_recursive = + [&](const OperationIndex &index, NodeRef node) -> void { + if (visited[index]) + return; + visited[index] = true; + + for (auto output : node.getOutputs()) + { + const auto &operand = graph.operands().at(output); + for (const auto &use : operand.getUses().list()) + { + dfs_recursive(use, graph.operations().at(use)); + } + } + + fn(index, node); + }; + + graph.operations().iterate(dfs_recursive); + + // All of the operations(nodes) must have been visited. + assert(std::all_of(visited.begin(), visited.end(), + [](const std::pair<const OperationIndex, bool> &v) { return v.second; })); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/GraphIterator.h b/runtime/onert/core/src/ir/GraphIterator.h new file mode 100644 index 000000000..42c851895 --- /dev/null +++ b/runtime/onert/core/src/ir/GraphIterator.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef __ONERT_IR_GRAPH_ITERATOR_H__ +#define __ONERT_IR_GRAPH_ITERATOR_H__ + +#include <type_traits> + +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +class Graph; +class Operation; + +template <bool is_const> class Iterator +{ +public: + using GraphRef = typename std::conditional<is_const, const Graph &, Graph &>::type; + using IndexRef = const OperationIndex &; + using NodeRef = typename std::conditional<is_const, const Operation &, Operation &>::type; + using IterFn = std::function<void(IndexRef, NodeRef)>; + +public: + virtual ~Iterator() = default; + virtual void iterate(GraphRef graph, const IterFn &fn) const = 0; +}; + +template <bool is_const = false> class DefaultIterator final : public Iterator<is_const> +{ +public: + using GraphRef = typename Iterator<is_const>::GraphRef; + using IndexRef = typename Iterator<is_const>::IndexRef; + using NodeRef = typename Iterator<is_const>::NodeRef; + using IterFn = typename Iterator<is_const>::IterFn; + +public: + void iterate(GraphRef graph, const IterFn &fn) const; +}; +using DefaultConstIterator = DefaultIterator<true>; + +template <bool is_const = false> class PostDfsIterator final : public Iterator<is_const> +{ +public: + using GraphRef = typename Iterator<is_const>::GraphRef; + using IndexRef = typename Iterator<is_const>::IndexRef; + using NodeRef = typename Iterator<is_const>::NodeRef; + using IterFn = typename Iterator<is_const>::IterFn; + +public: + void iterate(GraphRef graph, const IterFn &fn) const; +}; +using PostDfsConstIterator = PostDfsIterator<true>; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_GRAPH_ITERATOR_H__ diff --git a/runtime/onert/core/src/ir/LayoutSet.cc b/runtime/onert/core/src/ir/LayoutSet.cc new file mode 100644 index 000000000..bd3f438ad --- /dev/null +++ b/runtime/onert/core/src/ir/LayoutSet.cc @@ -0,0 +1,66 @@ +/* + * 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 "LayoutSet.h" + +namespace onert +{ +namespace ir +{ + +LayoutSet::LayoutSet(std::initializer_list<Layout> layouts) +{ + for (auto layout : layouts) + { + _set.insert(layout); + } +} + +LayoutSet LayoutSet::operator|(const LayoutSet &other) const +{ + auto ret = *this; + for (auto layout : other) + { + ret.add(layout); + } + return ret; +} + +LayoutSet LayoutSet::operator&(const LayoutSet &other) const +{ + LayoutSet ret; + for (auto layout : other) + { + if (contains(layout)) + { + ret.add(layout); + } + } + return ret; +} + +LayoutSet LayoutSet::operator-(const LayoutSet &other) const +{ + auto ret = *this; + for (auto layout : other) + { + ret.remove(layout); + } + return ret; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/LayoutSet.h b/runtime/onert/core/src/ir/LayoutSet.h new file mode 100644 index 000000000..6ce4e38c6 --- /dev/null +++ b/runtime/onert/core/src/ir/LayoutSet.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef __ONERT_IR_LAYOUT_SET_H__ +#define __ONERT_IR_LAYOUT_SET_H__ + +#include <initializer_list> +#include <unordered_set> + +#include "ir/Layout.h" + +namespace onert +{ +namespace ir +{ + +class LayoutSet +{ +public: + LayoutSet() = default; + LayoutSet(std::initializer_list<Layout> layouts); + +public: + void add(const Layout &layout) { _set.insert(layout); } + void remove(const Layout &layout) { _set.erase(layout); } + uint32_t size() const { return static_cast<uint32_t>(_set.size()); } + bool contains(const Layout &layout) const { return _set.find(layout) != _set.end(); } + +public: + LayoutSet operator|(const LayoutSet &other) const; // Union + LayoutSet operator&(const LayoutSet &other) const; // Intersect + LayoutSet operator-(const LayoutSet &other) const; // Minus + +public: + std::unordered_set<Layout>::const_iterator begin() const { return _set.begin(); } + std::unordered_set<Layout>::const_iterator end() const { return _set.end(); } + +private: + std::unordered_set<Layout> _set; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_LAYOUT_SET_H__ diff --git a/runtime/onert/core/src/ir/LoweredGraph.cc b/runtime/onert/core/src/ir/LoweredGraph.cc new file mode 100644 index 000000000..0018650e2 --- /dev/null +++ b/runtime/onert/core/src/ir/LoweredGraph.cc @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2020 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 "ir/LoweredGraph.h" + +#include <assert.h> +#include <sstream> +#include "util/logging.h" +#include "pass/ConstantInsertionPass.h" +#include "pass/PermutationOperationPass.h" +#include "pass/PermutationInsertionPass.h" +#include "ir/GraphIterator.h" +#include "verifier/Verifier.h" +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "compiler/BackendResolver.h" +#include "compiler/ManualScheduler.h" +#include "compiler/HEScheduler.h" + +namespace onert +{ +namespace ir +{ + +LoweredGraph::LoweredGraph(const Graph &graph, const compiler::CompilerOptions &options) + : _graph{graph} +{ + // Build backend contexts + auto &backend_manager = compiler::BackendManager::get(); + for (auto backend_str : options.backend_list) + { + backend_manager.loadBackend(backend_str); + auto backend = backend_manager.get(backend_str); + + // TODO As the default value of backend list contains "cpu", "acl_cl" and "acl_neon", and some + // are not available on x64 or some other platforms. So this may be a workaround for x64 and + // we should change it back(throw if backend is not loaded) later. + if (!backend) + { + VERBOSE(LoweredGraph) << "Cannot load backend - " << backend_str; + continue; + } + + _backend_contexts.emplace(backend, backend->newContext(_graph, _graph.getKernelBuilder(), + options.executor == "Linear")); + } + if (backend_manager.getAll().size() == 0) + throw std::runtime_error{"No available backends loaded."}; + + // TODO Move "schedule" phase out of here + // Schedule + if (options.he_scheduler) + { + auto scheduler = compiler::HEScheduler(_backend_contexts, options); + _backend_resolver = scheduler.schedule(_graph); + _indexed_ranks = scheduler.getIndexedRanks(); + } + else + { + auto scheduler = compiler::ManualScheduler(options.manual_scheduler_options); + _backend_resolver = scheduler.schedule(_graph); + } + + { + // operand::LowerInfo holder + OperandIndexMap<std::unique_ptr<operand::LowerInfo>> operands_lower_info; + + _graph.operands().iterate([&](const OperandIndex &index, const Operand &) { + operands_lower_info[index] = std::make_unique<operand::LowerInfo>(); + }); + + // Make op_seqs while checking whether a node can be merged into a op_seq. + makeOpSequences(operands_lower_info, options); + + _op_seqs.iterate([&](const OpSequenceIndex &, OpSequence &op_seq) { + assert(op_seq.operations().size() > 0); + std::reverse(std::begin(op_seq.operations()), std::end(op_seq.operations())); + }); + + _op_seqs.dump("merged and sorted operations without permutation"); + + pass::ConstantInsertionPass ci_pass(*this); + ci_pass.run(); + + // Set LowerInfo for each operand from the operand::LowerInfo holder + manipulateLowerInfo(operands_lower_info); + + dumpLowerInfo(); + } + + // Run Permutation Passes + { + pass::PermutationOperationPass po_pass(*this); + po_pass.run(); + + pass::PermutationInsertionPass pi_pass(*this); + pi_pass.run(); + // Implemented code no longer works. + // pass::PermutationEliminationPass pe_pass(*this); + // pe_pass.run(); + + // TODO merge perm op_seqs if possible + _op_seqs.dump("merged and sorted operations with permutation"); + } + + // Graph verifications + { + assert(verifier::DAGChecker().verify(_graph)); + assert(verifier::EdgeConsistencyChecker().verify(_graph)); + } +} + +const operation::LowerInfo *LoweredGraph::getLowerInfo(const OpSequenceIndex &op_seq_index) const +{ + auto itr = _lower_info_map.operation.find(op_seq_index); + if (itr == _lower_info_map.operation.end()) + return nullptr; + return itr->second.get(); +} + +void LoweredGraph::setLowerInfo(const OpSequenceIndex &op_seq_index, + std::unique_ptr<operation::LowerInfo> &&lower_info) +{ + _lower_info_map.operation.insert(std::make_pair(op_seq_index, std::move(lower_info))); +} + +void LoweredGraph::removeLowerInfo(const OpSequenceIndex &op_seq_index) +{ + auto &op_seq_lower_info = _lower_info_map.operation; + assert(op_seq_lower_info.find(op_seq_index) != op_seq_lower_info.end()); + for (auto it = op_seq_lower_info.begin(); it != op_seq_lower_info.end(); ++it) + { + if (it->first == op_seq_index) + { + op_seq_lower_info.erase(it); + break; + } + } +} + +const operand::LowerInfo *LoweredGraph::getLowerInfo(const OperandIndex &index) const +{ + auto itr = _lower_info_map.operand.find(index); + if (itr == _lower_info_map.operand.end()) + return nullptr; + return itr->second.get(); +} + +operand::LowerInfo *LoweredGraph::getLowerInfo(const OperandIndex &index) +{ + auto itr = _lower_info_map.operand.find(index); + if (itr == _lower_info_map.operand.end()) + return nullptr; + return itr->second.get(); +} + +void LoweredGraph::setLowerInfo(const OperandIndex &index, + std::unique_ptr<operand::LowerInfo> &&lower_info) +{ + _lower_info_map.operand.insert(std::make_pair(index, std::move(lower_info))); +} + +void LoweredGraph::removeLowerInfo(const OperandIndex &index) +{ + _lower_info_map.operand.erase(index); +} + +OpSequenceIndex LoweredGraph::appendFreshSingleOpSequence(const OperationIndex &node_index, + const Operation &node) +{ + // Create a fresh op_seq with one operation, and append it to op_seqs + // Create a fresh op_seq + auto op_seq = std::make_unique<OpSequence>(_graph.layout()); + + // Add an operation + op_seq->appendOperation(node_index, node); + + // Update input/output + op_seq->setOutputs(node.getOutputs()); + op_seq->setInputs(node.getInputs()); + + return _op_seqs.emplace(std::move(op_seq)); +} + +void LoweredGraph::makeOpSequences( + OperandIndexMap<std::unique_ptr<operand::LowerInfo>> &operands_lower_info, + const compiler::CompilerOptions &options) +{ + // if SUBG_MAX_NODE == 0, no limit on nodes of a op_seq + const int op_seq_max_node = options.op_seq_max_node; + assert(op_seq_max_node >= 0); + + bool is_profiling = options.he_profiling_mode; + OpSequence *op_seq = nullptr; + OpSequenceIndex op_seq_index; + + // NOTE: The below method appends nodes while making one op_seq if needed. If something better + // ways, happy to update this code. + PostDfsConstIterator{}.iterate( + _graph, [&](const OperationIndex &node_index, const Operation &node) { + // LowerInfo for in/output operands + auto backend = _backend_resolver->getBackend(node_index); + + // Set backend's layout to frontend layout + auto backend_layout = _graph.layout(); + + // The layout of each backend should be set at another place + // TODO Change setting layout of each backend at another place + // TODO Remove getting id of backend + if (backend->config()->id() == "acl_cl" || backend->config()->id() == "acl_neon") + { + const std::string acl_layout_str = util::getConfigString(util::config::ACL_LAYOUT); + if (acl_layout_str == "NHWC") + { + backend_layout = Layout::NHWC; + } + else if (acl_layout_str == "NCHW") + { + backend_layout = Layout::NCHW; + } + } + else if (backend->config()->id() == "srcn") + { + const std::string ncnn_layout_str = util::getConfigString(util::config::NCNN_LAYOUT); + if (ncnn_layout_str == "NHWC") + { + backend_layout = Layout::NHWC; + } + else if (ncnn_layout_str == "NCHW") + { + backend_layout = Layout::NCHW; + } + } + else if (backend->config()->id() == "cpu") + { + backend_layout = Layout::NHWC; + } + + for (auto operand : node.getInputs()) + { + auto &&lower_info = operands_lower_info.at(operand); + lower_info->addUsePermuteFactor(operand::PermuteFactor{backend, backend_layout}); + } + for (auto operand : node.getOutputs()) + { + auto &&lower_info = operands_lower_info.at(operand); + lower_info->addDefPermuteFactor(operand::PermuteFactor{backend, backend_layout}); + } + + bool new_op_seq = (op_seq == nullptr || + (op_seq_max_node != 0 && + op_seq->operations().size() >= static_cast<size_t>(op_seq_max_node))); + + // for profiling each op_seq must contain just one node, + // so that we can measure a node separately + if (new_op_seq || is_profiling || !mergeable(op_seq_index, node_index, backend_layout)) + { + auto new_op_seq_index = appendFreshSingleOpSequence(node_index, node); + + // OpSequence LowerInfo + setLowerInfo(new_op_seq_index, + std::make_unique<operation::LowerInfo>(backend, backend_layout)); + + op_seq_index = new_op_seq_index; + op_seq = &(_op_seqs.at(new_op_seq_index)); + + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " is created for " + << "NODE#" << node_index.value() << "(" << node.name() << ")" << std::endl; + } + else + { + op_seq->appendOperation(node_index, node); + op_seq->setInputs(node.getInputs()); + + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " merges " + << "NODE#" << node_index.value() << "(" << node.name() << ")" << std::endl; + } + }); +} + +void LoweredGraph::manipulateLowerInfo( + OperandIndexMap<std::unique_ptr<operand::LowerInfo>> &operands_lower_info) +{ + const auto default_backend = compiler::BackendManager::get().getDefault(); + for (auto index : _graph.getInputs()) + { + // Pick just any one from the uses, here the first one is chosen + // For the other uses, Permute operations will be inserted later + auto &&lower_info = operands_lower_info.at(index); + assert(lower_info->use_factors().size() > 0); + lower_info->addDefPermuteFactor(*lower_info->use_factors().begin()); + } + for (auto index : _graph.getOutputs()) + { + auto &&lower_info = operands_lower_info.at(index); + if (_graph.operands().at(index).isConstant()) + { + lower_info->addDefPermuteFactor(operand::PermuteFactor{ + default_backend, + Layout::NHWC // TODO Get frontend layout of this node from IR + }); + } + } + + // Set LowerInfo for each operand from the operand::LowerInfo holder + _graph.operands().iterate([&](const OperandIndex &index, Operand &) { + setLowerInfo(index, std::move(operands_lower_info[index])); + }); +} + +void LoweredGraph::dumpLowerInfo() +{ + if (::onert::util::logging::ctx.enabled() == false) + return; + + std::map<uint32_t, std::string> dumps; + + _graph.operands().iterate([&](const OperandIndex &index, Operand &object) { + std::stringstream sstream; + if (!getLowerInfo(index)->def_factors().empty() || !getLowerInfo(index)->use_factors().empty()) + { + auto factors_to_string = [](const operand::PermuteFactorSet &factors) { + std::string str; + for (auto factor : factors) + { + str += factor.backend()->config()->id(); + str += "(" + to_string(factor.layout()) + ")"; + str += " "; + } + return "{ " + str + "}"; + }; + + auto operation_index_to_string = [](const OperationIndexList &operations) { + std::string str; + for (auto op : operations.list()) + { + str += std::to_string(op.value()); + str += " "; + } + return "{ " + str + "}"; + }; + + const auto lower_info = getLowerInfo(index); + const auto &shape = object.shape(); + std::string def_ops = operation_index_to_string(object.getDef()); + std::string use_ops = operation_index_to_string(object.getUses()); + std::string def_layouts = factors_to_string(lower_info->def_factors()); + std::string use_layouts = factors_to_string(lower_info->use_factors()); + sstream << "Operand #" << index.value() << " LowerInfo" << std::endl; + sstream << " - Shape : { " << (shape.rank() > 0 ? shape.dim(0) : 0) << " " + << (shape.rank() > 1 ? shape.dim(1) : 0) << " " + << (shape.rank() > 2 ? shape.dim(2) : 0) << " " + << (shape.rank() > 3 ? shape.dim(3) : 0) << " " + << "}" << std::endl; + sstream << " - Def Operations : " << def_ops << std::endl; + sstream << " - Use Operations : " << use_ops << std::endl; + sstream << " - Lower Info" << std::endl; + sstream << " - Def Backends : " << def_layouts << std::endl; + sstream << " - Use Backends : " << use_layouts << std::endl; + } + dumps.emplace(index.value(), sstream.str()); + }); + + for (const auto &e : dumps) + { + if (!e.second.empty()) + { + VERBOSE(Lower) << e.second; + } + } +} + +bool LoweredGraph::mergeable(const OpSequenceIndex &op_seq_index, const OperationIndex &node_index, + Layout layout) +{ + // Are they mergeable? + // 1. the same backend id and layout? + // 2. Is op_seq or node branched? + // 3. if 1 is true, the op_seq and a node are connected? + const auto &op_seq = _op_seqs.at(op_seq_index); + const auto &node = _graph.operations().at(node_index); + + // The same backend id and layout? + { + const auto op_seq_backend_layout = getLowerInfo(op_seq_index)->layout(); + const auto &op_seq_backend_id = getLowerInfo(op_seq_index)->backend()->config()->id(); + const auto &node_backend_id = _backend_resolver->getBackend(node_index)->config()->id(); + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " { " << op_seq_backend_id << "(" + << to_string(op_seq_backend_layout) << ") } " + << " NODE#" << node_index.value() << " (" << node.name() << ") { " + << node_backend_id << "(" << to_string(layout) << ") } " << std::endl; + if (op_seq_backend_id != node_backend_id || op_seq_backend_layout != layout) + return false; + } + + // Branched? + { + std::unordered_set<OperationIndex> branched_set; + + // Check for branching up + const auto &inputs = op_seq.getInputs(); + for (const auto &input : inputs) + { + const auto &input_obj = _graph.operands().at(input); + for (const auto &def : input_obj.getDef().list()) + { + branched_set.insert(def); + if (branched_set.size() > 1) + { + return false; + } + } + } + branched_set.clear(); + + // Check for branching down + const auto &outputs = node.getOutputs(); + for (const auto &output : outputs) + { + const auto &output_obj = _graph.operands().at(output); + for (const auto &use : output_obj.getUses().list()) + { + branched_set.insert(use); + if (branched_set.size() > 1) + { + return false; + } + } + } + } + + // Connected? + // an input of one node is an output of the other node? or vice-versa? + { + const auto &node_inputs = node.getInputs(); + const auto &node_outputs = node.getOutputs(); + + // op_seq's operations are in order so that we just check the first and the last + std::vector<Element> op_seq_ops{op_seq.operations()[0]}; + if (op_seq.operations().size() > 1) + op_seq_ops.emplace_back(op_seq.operations()[op_seq.operations().size() - 1]); + + for (const auto &elem : op_seq_ops) + { + const auto &n_index = elem.index; + const auto &n = *elem.node; + + // node's output == op_seq's input? + const auto &n_inputs = n.getInputs(); + for (auto input : n_inputs) + { + if (node_outputs.contains(input)) + { + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " 's NODE#" << n_index.value() << "(" + << n.name() << ") is connected to NODE#" << node_index.value() << "(" + << node.name() << ")" << std::endl; + return true; + } + } + + // node's input == op_seq's output? + const auto &n_outputs = n.getOutputs(); + for (auto output : n_outputs) + { + if (node_inputs.contains(output)) + { + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " 's NODE#" << n_index.value() + << " (" << n.name() << ") is connected to NODE#" << node_index.value() + << std::endl; + return true; + } + } + } + + VERBOSE(Lower) << "SUBG#" << op_seq_index.value() << " is not connected to NODE#" + << node_index.value() << "(" << node.name() << ")" << std::endl; + } + + return false; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OpCode.cc b/runtime/onert/core/src/ir/OpCode.cc new file mode 100644 index 000000000..3f8182916 --- /dev/null +++ b/runtime/onert/core/src/ir/OpCode.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 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 "ir/OpCode.h" + +#include <unordered_map> + +namespace onert +{ +namespace ir +{ + +const char *toString(OpCode opcode) +{ + static const std::unordered_map<OpCode, const char *> map{{OpCode::Invalid, "Invalid"}, +#define OP(Name) {OpCode::Name, #Name}, +#include "ir/Operations.lst" +#undef OP + {OpCode::COUNT, "COUNT"}}; + return map.at(opcode); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OpSequence.cc b/runtime/onert/core/src/ir/OpSequence.cc new file mode 100644 index 000000000..74c7854e5 --- /dev/null +++ b/runtime/onert/core/src/ir/OpSequence.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019 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 "ir/OpSequence.h" +#include "ir/OperationVisitor.h" +#include <sstream> + +namespace +{ + +std::string getStrFromIndice(const onert::ir::OperandIndexSequence &indice) +{ + std::string str; + for (const auto &ind : indice) + { + str += std::to_string(ind.value()); + str.push_back(','); + } + if (str.back() == ',') + str.pop_back(); + + return str; +} +} + +namespace onert +{ +namespace ir +{ + +OpSequence::OpSequence(Layout layout) : _layout{layout} +{ + // DO NOTHING +} + +void OpSequence::accept(OperationVisitor &v) const { v.visit(*this); } + +// TODO: Impl Dumper instead of this method +std::string OpSequence::getStr() const +{ + // " OpSequence IN(0,1,2) -> { op0(0,1,2:3), op1(3:4), op2(4:5) } -> OUT(5)" + std::stringstream ss; + ss << " OpSequence IN(" << getStrFromIndice(getInputs()) << ") -> {"; + for (const auto &elem : _operations) + { + ss << " " << elem.index.value() << "(" << elem.node->name() << ":" + << getStrFromIndice(elem.node->getInputs()) << ":" + << getStrFromIndice(elem.node->getOutputs()) << ")"; + } + ss << " } -> OUT(" << getStrFromIndice(getOutputs()) << ")"; + return ss.str(); +} + +void OpSequence::remove(const OperationIndex &index) +{ + assert(exist(index)); + for (auto it = _operations.cbegin(); it != _operations.cend(); ++it) + { + if (it->index == index) + { + _operations.erase(it); + break; + } + } +} + +bool OpSequence::exist(const OperationIndex &index) const +{ + for (const auto &element : _operations) + { + if (element.index == index) + { + return true; + } + } + return false; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OpSequences.cc b/runtime/onert/core/src/ir/OpSequences.cc new file mode 100644 index 000000000..991e96c9d --- /dev/null +++ b/runtime/onert/core/src/ir/OpSequences.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 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 "ir/OpSequences.h" +#include "util/logging.h" +#include <memory> + +#include <cassert> +#include <string> + +namespace onert +{ +namespace ir +{ + +OpSequenceIndex OpSequences::emplace(const OperationIndex &index, const Operation &node, + Layout layout) +{ + std::unique_ptr<OpSequence> op_seq = std::make_unique<OpSequence>(layout); + op_seq->appendOperation(index, node); + return push(std::move(op_seq)); +} + +OpSequenceIndex OpSequences::emplace(std::unique_ptr<OpSequence> &&op_seq) +{ + return push(std::move(op_seq)); +} + +bool OpSequences::containsOperation(const OperationIndex &operation_index) const +{ + return findOperation(operation_index).valid(); +} + +OpSequenceIndex OpSequences::getOperation(const OperationIndex &operation_index) const +{ + OpSequenceIndex ret = findOperation(operation_index); + assert(ret.valid()); + return ret; +} + +// TODO: Extract this into external helper function +void OpSequences::dump(const std::string &msg) const +{ + VERBOSE(OpSequences) << "OpSequences(" << msg << ")" << std::endl; + iterate([&](const OpSequenceIndex &idx, const OpSequence &op_seq) { + VERBOSE(OpSequences) << idx.value() << "] " << op_seq.getStr() << std::endl; + }); +} + +void OpSequences::removeFromOpSequence(const OperationIndex &operation_index) +{ + const auto op_seq_index = findOperation(operation_index); + auto &op_seq = at(op_seq_index); + op_seq.remove(operation_index); + if (op_seq.size() == 0) + { + remove(op_seq_index); + } +} + +OpSequenceIndex OpSequences::findOperation(const OperationIndex &operation_index) const +{ + OpSequenceIndex ret; + iterate([&](const OpSequenceIndex &index, const OpSequence &object) { + for (const auto &elem : object.operations()) + { + if (elem.index == operation_index) + ret = index; + } + }); + return ret; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Operand.cc b/runtime/onert/core/src/ir/Operand.cc new file mode 100644 index 000000000..39f9d6c90 --- /dev/null +++ b/runtime/onert/core/src/ir/Operand.cc @@ -0,0 +1,61 @@ +/* + * 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 "ir/Operand.h" + +namespace onert +{ +namespace ir +{ + +size_t Operand::operandSize(void) const +{ + const uint32_t ranks = shape().rank(); + int32_t elements = 1; + + for (uint32_t rank = 0; rank < ranks; rank++) + { + elements *= shape().dim(rank); + } + + DataType type = typeInfo().type(); + size_t element_size = sizeOfDataType(type); + + // Value of type is matched with OperandCode enum in NeuralNetworks.h + return element_size * elements; +} + +void Operand::appendUse(const OperationIndex &idx) { _uses.append(idx); } + +void Operand::removeUse(const OperationIndex &idx) { _uses.remove(idx); } + +void Operand::appendDef(const OperationIndex &idx) +{ + assert(!isConstant()); + assert(_def.size() == 0); + + _def.append(idx); +} + +void Operand::removeDef(const OperationIndex &idx) +{ + assert(_def.contains(idx)); + + _def.remove(idx); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperandIndexSequence.cc b/runtime/onert/core/src/ir/OperandIndexSequence.cc new file mode 100644 index 000000000..2044310e7 --- /dev/null +++ b/runtime/onert/core/src/ir/OperandIndexSequence.cc @@ -0,0 +1,65 @@ +/* + * 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 "ir/OperandIndexSequence.h" + +#include <algorithm> + +namespace onert +{ +namespace ir +{ + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<OperandIndex> list) : _set(list) +{ + // DO NOTHING +} + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<int32_t> list) +{ + for (auto val : list) + { + _set.emplace_back(static_cast<uint32_t>(val)); + } +} + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<uint32_t> list) +{ + for (auto val : list) + { + _set.emplace_back(val); + } +} + +bool OperandIndexSequence::contains(const OperandIndex &index) const +{ + return std::find(_set.begin(), _set.end(), index) != _set.end(); +} + +void OperandIndexSequence::replace(const OperandIndex &from, const OperandIndex &to) +{ + std::replace(_set.begin(), _set.end(), from, to); +} + +OperandIndexSequence OperandIndexSequence::operator+(const OperandIndexSequence &other) const +{ + OperandIndexSequence ret = *this; + ret.append(other); + return ret; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Operands.cc b/runtime/onert/core/src/ir/Operands.cc new file mode 100644 index 000000000..ab32e478a --- /dev/null +++ b/runtime/onert/core/src/ir/Operands.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 "ir/Operands.h" + +#include <memory> +#include "util/logging.h" + +namespace onert +{ +namespace ir +{ + +Operands::Operands(const Operands &obj) +{ + obj.iterate([&](const OperandIndex &index, const Operand &operand) { + _objects.emplace(index, std::make_unique<Operand>(operand)); + }); + _index_count = obj._index_count; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Operation.cc b/runtime/onert/core/src/ir/Operation.cc new file mode 100644 index 000000000..d2a60747f --- /dev/null +++ b/runtime/onert/core/src/ir/Operation.cc @@ -0,0 +1,55 @@ +/* + * 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 "ir/Operation.h" + +#include <cassert> + +namespace onert +{ +namespace ir +{ + +Operation::Operation(OperandConstraint input_constr, const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : _input_constr{input_constr}, _inputs{inputs}, _outputs{outputs} +{ +} + +Operation::Operation(OperandConstraint input_constr) : _input_constr{input_constr} {} + +Operation::~Operation() = default; + +void Operation::setInputs(const OperandIndexSequence &indexes) +{ + assert(_input_constr.check(indexes.size())); + _inputs = indexes; +} + +void Operation::setOutputs(const OperandIndexSequence &indexes) { _outputs = indexes; } + +void Operation::replaceInput(const OperandIndex &from, const OperandIndex &to) +{ + _inputs.replace(from, to); +} + +void Operation::replaceOutput(const OperandIndex &from, const OperandIndex &to) +{ + _outputs.replace(from, to); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperationCloner.cc b/runtime/onert/core/src/ir/OperationCloner.cc new file mode 100644 index 000000000..b4e60f0bc --- /dev/null +++ b/runtime/onert/core/src/ir/OperationCloner.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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 "OperationCloner.h" + +#include <assert.h> + +namespace onert +{ +namespace ir +{ + +#define OP(Name) \ + void OperationCloner::visit(const operation::Name &o) \ + { \ + assert(!_return_op); \ + _return_op = std::make_unique<operation::Name>(o); \ + } +#include "ir/Operations.lst" +#undef OP + +std::unique_ptr<Operation> OperationCloner::releaseClone() +{ + assert(_return_op); + return std::move(_return_op); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperationCloner.h b/runtime/onert/core/src/ir/OperationCloner.h new file mode 100644 index 000000000..0e8cda2a0 --- /dev/null +++ b/runtime/onert/core/src/ir/OperationCloner.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef __ONERT_IR_OPERATION_CLONER_H__ +#define __ONERT_IR_OPERATION_CLONER_H__ + +#include <memory> +#include "ir/OperationVisitor.h" +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ + +class OperationCloner : public OperationVisitor +{ +public: +#define OP(Name) void visit(const operation::Name &o) override; +#include "ir/Operations.lst" +#undef OP + +public: + std::unique_ptr<Operation> releaseClone(); + +private: + std::unique_ptr<Operation> _return_op; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_CLONER_H__ diff --git a/runtime/onert/core/src/ir/OperationDumper.cc b/runtime/onert/core/src/ir/OperationDumper.cc new file mode 100644 index 000000000..789d2869d --- /dev/null +++ b/runtime/onert/core/src/ir/OperationDumper.cc @@ -0,0 +1,634 @@ +/* + * 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 "OperationDumper.h" + +#include <string> + +#include "util/logging.h" + +namespace onert +{ +namespace ir +{ + +using namespace operation; + +void OperationDumper::visit(const Abs &node) +{ + VERBOSE(LIR) << "* Abs" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Abs::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Add &node) +{ + VERBOSE(LIR) << "* Add" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Add::Input::LHS).value() << ", " + << node.getInputs().at(Add::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ArgMax &node) +{ + VERBOSE(LIR) << "* ArgMax" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ArgMax::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const AvgPool2D &node) +{ + VERBOSE(LIR) << "* AvgPool2D(Implicit)" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(AvgPool2D::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Cast &node) +{ + VERBOSE(LIR) << "* Cast" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Cast::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Comparison &node) +{ + VERBOSE(LIR) << "* Comparison" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Comparison::Input::INPUT0).value() + << ", " << node.getInputs().at(Comparison::Input::INPUT1).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Concat &node) +{ + VERBOSE(LIR) << "* Concat" << std::endl; + std::string inputs; + for (auto i : node.getInputs()) + { + inputs += std::to_string(i.value()) + ","; + } + VERBOSE(LIR) << " - Inputs : IFM(" << inputs << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Conv2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + VERBOSE(LIR) << "* Conv2D(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(Conv2D::Input::INPUT).value() + << ") Kernel(" << node.getInputs().at(Conv2D::Input::KERNEL).value() << ") Bias(" + << node.getInputs().at(Conv2D::Input::BIAS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const DepthToSpace &node) +{ + VERBOSE(LIR) << "* DepthToSpace" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(DepthToSpace::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const DepthwiseConv2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + VERBOSE(LIR) << "* DepthwiseConv2D(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(DepthwiseConv2D::Input::INPUT).value() + << ") Kernel(" << node.getInputs().at(DepthwiseConv2D::Input::KERNEL).value() + << ") Bias(" << node.getInputs().at(DepthwiseConv2D::Input::BIAS).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Dequantize &node) +{ + VERBOSE(LIR) << "* Dequantize" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Dequantize::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Div &node) +{ + VERBOSE(LIR) << "* Div" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Div::Input::LHS).value() << ", " + << node.getInputs().at(Div::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const EmbeddingLookup &node) +{ + VERBOSE(LIR) << "* EmbeddingLookup" << std::endl; + VERBOSE(LIR) << " - Inputs : Lookups(" + << node.getInputs().at(EmbeddingLookup::Input::LOOKUPS).value() << ") VALUES(" + << node.getInputs().at(EmbeddingLookup::Input::VALUES).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Exp &node) +{ + VERBOSE(LIR) << "* Exp" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Exp::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Floor &node) +{ + VERBOSE(LIR) << "* Floor" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Floor::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const FullyConnected &node) +{ + VERBOSE(LIR) << "* FullyConnected" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(FullyConnected::Input::INPUT).value() + << ") Weight(" << node.getInputs().at(FullyConnected::Input::WEIGHT).value() + << ") Bias(" << node.getInputs().at(FullyConnected::Input::BIAS).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Gather &node) +{ + VERBOSE(LIR) << "* Gather" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Gather::Input::INPUT).value() + << ") Indices(" << node.getInputs().at(Gather::Input::INDICES).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const HashtableLookup &node) +{ + VERBOSE(LIR) << "* HashTableLookup" << std::endl; + VERBOSE(LIR) << " - Inputs : Lookups(" + << node.getInputs().at(HashtableLookup::Input::LOOKUPS).value() << ") Keys(" + << node.getInputs().at(HashtableLookup::Input::KEYS).value() << ") Values(" + << node.getInputs().at(HashtableLookup::Input::VALUES).value() << ")" << std::endl; + VERBOSE(LIR) << " - Outputs : Output(" + << node.getInputs().at(HashtableLookup::Output::OUTPUT).value() << ") Hits(" + << node.getInputs().at(HashtableLookup::Output::HITS).value() << ")" << std::endl; +} + +void OperationDumper::visit(const InstanceNorm &node) +{ + VERBOSE(LIR) << "* InstanceNorm" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(InstanceNorm::Input::INPUT).value() + << ") Gamma(" << node.getInputs().at(InstanceNorm::Input::GAMMA).value() << ") Beta(" + << node.getInputs().at(InstanceNorm::Input::BETA).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const L2Normalization &node) +{ + VERBOSE(LIR) << "* L2Normalization" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" + << node.getInputs().at(L2Normalization::Input::INPUT).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const L2Pool2D &node) +{ + VERBOSE(LIR) << "* L2Pool2D" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(L2Pool2D::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const LocalResponseNormalization &node) +{ + VERBOSE(LIR) << "* LocalResponseNormalization" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" + << node.getInputs().at(LocalResponseNormalization::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const LSTM &node) +{ + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(LSTM::Input::INPUT).value() + << ") Input To Input Weights(" + << node.getInputs().at(LSTM::Input::INPUT_TO_INPUT_WEIGHTS).value() + << ") Input To Forget Weights(" + << node.getInputs().at(LSTM::Input::INPUT_TO_FORGET_WEIGHTS).value() + << ") Input To Cell Weights(" + << node.getInputs().at(LSTM::Input::INPUT_TO_CELL_WEIGHTS).value() + << ") Input To Output Weights(" + << node.getInputs().at(LSTM::Input::INPUT_TO_OUTPUT_WEIGHTS).value() + << ") Recurrent To Input Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_INPUT_WEIGHTS).value() + << ") Recurrent To Forget Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_FORGET_WEIGHTS).value() + << ") Recurrent To Cell Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_CELL_WEIGHTS).value() + << ") Recurrent To Output Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_OUTPUT_WEIGHTS).value() + << ") Cell To Input Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_INPUT_WEIGHTS).value() + << ") Cell To Forget Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_FORGET_WEIGHTS).value() + << ") Cell To OUTPUT Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_OUTPUT_WEIGHTS).value() + << ") Input Gate Bias(" << node.getInputs().at(LSTM::Input::INPUT_GATE_BIAS).value() + << ") Forget Gate Bias(" + << node.getInputs().at(LSTM::Input::FORGET_GATE_BIAS).value() << ") Cell Bias(" + << node.getInputs().at(LSTM::Input::CELL_BIAS).value() << ") Output Gate Bias(" + << node.getInputs().at(LSTM::Input::OUTPUT_GATE_BIAS).value() + << ") Projection Weights(" + << node.getInputs().at(LSTM::Input::PROJECTION_WEIGHTS).value() + << ") Projection Bias(" << node.getInputs().at(LSTM::Input::PROJECTION_BIAS).value() + << ") Output State In(" << node.getInputs().at(LSTM::Input::OUTPUT_STATE_IN).value() + << ") Cell State In(" << node.getInputs().at(LSTM::Input::CELL_STATE_IN).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Scratch Buffer(" + << node.getOutputs().at(LSTM::Output::SCRATCH_BUFFER).value() + << ") Output State Out(" + << node.getInputs().at(LSTM::Output::OUTPUT_STATE_OUT).value() << ") Cell State Out(" + << node.getInputs().at(LSTM::Output::CELL_STATE_OUT).value() << ") Output(" + << node.getInputs().at(LSTM::Output::OUTPUT).value() << ")" << std::endl; +} + +void OperationDumper::visit(const LogicalAnd &node) +{ + VERBOSE(LIR) << "* LogicalAnd" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(LogicalAnd::Input::INPUT0).value() + << ", " << node.getInputs().at(LogicalAnd::Input::INPUT1).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const LogicalNot &node) +{ + VERBOSE(LIR) << "* LogicalNot" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(LogicalNot::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const LogicalOr &node) +{ + VERBOSE(LIR) << "* LogicalOr" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(LogicalOr::Input::INPUT0).value() + << ", " << node.getInputs().at(LogicalOr::Input::INPUT1).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Logistic &node) +{ + VERBOSE(LIR) << "* Logistic" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Logistic::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const MaxPool2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + VERBOSE(LIR) << "* MaxPool2D(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(MaxPool2D::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Mean &node) +{ + VERBOSE(LIR) << "* Mean" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Mean::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Mul &node) +{ + VERBOSE(LIR) << "* Mul" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Mul::Input::LHS).value() << ", " + << node.getInputs().at(Mul::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Neg &node) +{ + VERBOSE(LIR) << "* Neg" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Neg::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Pack &node) +{ + VERBOSE(LIR) << "* Pack" << std::endl; + std::string inputs; + const auto &input_indices = node.getInputs(); + for (auto it = std::begin(input_indices); it != std::end(input_indices); ++it) + { + inputs += std::to_string(it->value()); + if (std::next(it) != std::end(input_indices)) + inputs += ", "; + } + VERBOSE(LIR) << " - Inputs : Inputs(" << inputs << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Permute &node) +{ + std::string permute_type = "Unknown"; + switch (node.getPermuteType()) + { + case Permute::Type::COPY: + permute_type = "Copy"; + break; + case Permute::Type::NHWC_TO_NCHW: + permute_type = "NHWC to NCHW"; + break; + case Permute::Type::NCHW_TO_NHWC: + permute_type = "NCHW to NHWC"; + break; + } + + VERBOSE(LIR) << "* Permute(" + permute_type + ")" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(0).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const PReLU &node) +{ + VERBOSE(LIR) << "* PReLU" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(PReLU::Input::INPUT).value() + << ") Alpha(" << node.getInputs().at(PReLU::Input::ALPHA).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReduceMax &node) +{ + VERBOSE(LIR) << "* ReduceMax" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReduceMax::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReduceMin &node) +{ + VERBOSE(LIR) << "* ReduceMin" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReduceMin::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReduceSum &node) +{ + VERBOSE(LIR) << "* ReduceSum" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReduceSum::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReLU &node) +{ + VERBOSE(LIR) << "* ReLU" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReLU::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReLU1 &node) +{ + VERBOSE(LIR) << "* ReLU1" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReLU1::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ReLU6 &node) +{ + VERBOSE(LIR) << "* ReLU6" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ReLU6::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Reshape &node) +{ + VERBOSE(LIR) << "* Reshape" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Reshape::Input::INPUT).value() + << ")"; + // optional param + if (node.getInputs().size() == 2) + { + VERBOSE(LIR) << " Shape(" << node.getInputs().at(Reshape::Input::SHAPE).value() << ")"; + } + else + { + VERBOSE(LIR) << " Shape(not provided)"; + } + VERBOSE(LIR) << std::endl; + + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const ResizeBilinear &node) +{ + VERBOSE(LIR) << "* ResizeBilinear" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(ResizeBilinear::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const RNN &node) +{ + VERBOSE(LIR) << "* RNN" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(RNN::Input::INPUT).value() + << ") Weights" << node.getInputs().at(RNN::Input::WEIGHTS).value() + << ") Recurrent Weights" + << node.getInputs().at(RNN::Input::RECURRENT_WEIGHTS).value() << ") Bias" + << node.getInputs().at(RNN::Input::BIAS).value() << ") Hidden State" + << node.getInputs().at(RNN::Input::HIDDEN_STATE_IN).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(RNN::Output::OUTPUT).value() + << ") Hidden State" << node.getInputs().at(RNN::Output::HIDDEN_STATE_OUT).value() + << ")" << std::endl; +} + +void OperationDumper::visit(const RSQRT &node) +{ + VERBOSE(LIR) << "* RSQRT" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(RSQRT::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Softmax &node) +{ + VERBOSE(LIR) << "* Softmax" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Softmax::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const SpaceToDepth &node) +{ + VERBOSE(LIR) << "* SpaceToDepth" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(SpaceToDepth::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Split &node) +{ + VERBOSE(LIR) << "* Split" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Split::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const SQRT &node) +{ + VERBOSE(LIR) << "* SQRT" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(SQRT::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const SquaredDifference &node) +{ + VERBOSE(LIR) << "* SquaredDifference" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" + << node.getInputs().at(SquaredDifference::Input::LHS).value() << ", " + << node.getInputs().at(SquaredDifference::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Squeeze &node) +{ + VERBOSE(LIR) << "* Squeeze" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Squeeze::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Slice &node) +{ + VERBOSE(LIR) << "* Slice" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Slice::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const StridedSlice &node) +{ + VERBOSE(LIR) << "* StridedSlice" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(StridedSlice::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Sub &node) +{ + VERBOSE(LIR) << "* Sub" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Sub::Input::LHS).value() << ", " + << node.getInputs().at(Sub::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Tanh &node) +{ + VERBOSE(LIR) << "* TanH" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Tanh::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const TopKV2 &node) +{ + VERBOSE(LIR) << "* TopKV2" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(TopKV2::Input::INPUT).value() << ")" + << std::endl; + VERBOSE(LIR) << " - Outputs : Values(" + << node.getOutputs().at(TopKV2::Output::OUTPUT_VALUES).value() << ") Indices(" + << node.getOutputs().at(TopKV2::Output::OUTPUT_INDICES).value() << ")" << std::endl; +} + +void OperationDumper::visit(const TransposeConv &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + VERBOSE(LIR) << "* TransposeConv(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : Output Shape(" + << node.getInputs().at(TransposeConv::Input::OUTPUT_SHAPE).value() << ") KERNEL(" + << node.getInputs().at(TransposeConv::Input::KERNEL).value() << ") IFM(" + << node.getInputs().at(TransposeConv::Input::INPUT).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Transpose &node) +{ + VERBOSE(LIR) << "* Transpose" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Transpose::Input::INPUT).value() + << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Unpack &node) +{ + VERBOSE(LIR) << "* Unpack" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Unpack::Input::INPUT).value() << ")" + << std::endl; + std::string outputs; + const auto &output_indices = node.getOutputs(); + for (auto it = std::begin(output_indices); it != std::end(output_indices); ++it) + { + outputs += std::to_string(it->value()); + if (std::next(it) != std::end(output_indices)) + outputs += ", "; + } + VERBOSE(LIR) << " - Outputs : Outputs(" << outputs << ")" << std::endl; +} + +void OperationDumper::visit(const Min &node) +{ + VERBOSE(LIR) << "* Min" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Min::Input::LHS).value() << ", " + << node.getInputs().at(Min::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const Max &node) +{ + VERBOSE(LIR) << "* Max" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Max::Input::LHS).value() << ", " + << node.getInputs().at(Max::Input::RHS).value() << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +void OperationDumper::visit(const OneHot &node) +{ + VERBOSE(LIR) << "* OneHot" << std::endl; + VERBOSE(LIR) << " - Inputs : " + << "Indices(" << node.getInputs().at(OneHot::Input::INDICES).value() << ") " + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0).value() << ")" << std::endl; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperationDumper.h b/runtime/onert/core/src/ir/OperationDumper.h new file mode 100644 index 000000000..77d100cee --- /dev/null +++ b/runtime/onert/core/src/ir/OperationDumper.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef __ONERT_OPERATION_DUMPER_H__ +#define __ONERT_OPERATION_DUMPER_H__ + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ + +class OperationDumper : public OperationVisitor +{ +public: + OperationDumper() = default; + +public: + void visit(const operation::Abs &) override; + void visit(const operation::Add &node) override; + void visit(const operation::ArgMax &) override; + void visit(const operation::AvgPool2D &node) override; + void visit(const operation::Cast &) override; + void visit(const operation::Comparison &) override; + void visit(const operation::Concat &node) override; + void visit(const operation::Conv2D &node) override; + void visit(const operation::DepthToSpace &) override; + void visit(const operation::DepthwiseConv2D &node) override; + void visit(const operation::Dequantize &) override; + void visit(const operation::Div &) override; + void visit(const operation::EmbeddingLookup &) override; + void visit(const operation::Exp &) override; + void visit(const operation::Floor &) override; + void visit(const operation::FullyConnected &node) override; + void visit(const operation::Gather &) override; + void visit(const operation::HashtableLookup &) override; + void visit(const operation::InstanceNorm &) override; + void visit(const operation::L2Normalization &) override; + void visit(const operation::L2Pool2D &) override; + void visit(const operation::LocalResponseNormalization &) override; + void visit(const operation::LogicalAnd &) override; + void visit(const operation::LogicalNot &) override; + void visit(const operation::LogicalOr &) override; + void visit(const operation::Logistic &) override; + void visit(const operation::LSTM &) override; + void visit(const operation::MaxPool2D &node) override; + void visit(const operation::Mean &) override; + void visit(const operation::Mul &) override; + void visit(const operation::Neg &) override; + void visit(const operation::Pack &) override; + void visit(const operation::Permute &node) override; + void visit(const operation::PReLU &) override; + void visit(const operation::ReduceMax &) override; + void visit(const operation::ReduceMin &) override; + void visit(const operation::ReduceSum &) override; + void visit(const operation::ReLU &) override; + void visit(const operation::ReLU1 &) override; + void visit(const operation::ReLU6 &) override; + void visit(const operation::Reshape &node) override; + void visit(const operation::ResizeBilinear &) override; + void visit(const operation::RNN &) override; + void visit(const operation::RSQRT &) override; + void visit(const operation::Softmax &node) override; + void visit(const operation::SpaceToDepth &) override; + void visit(const operation::Split &) override; + void visit(const operation::SQRT &) override; + void visit(const operation::SquaredDifference &) override; + void visit(const operation::Squeeze &) override; + void visit(const operation::Slice &) override; + void visit(const operation::StridedSlice &) override; + void visit(const operation::Sub &) override; + void visit(const operation::Tanh &) override; + void visit(const operation::TopKV2 &) override; + void visit(const operation::TransposeConv &) override; + void visit(const operation::Transpose &) override; + void visit(const operation::Unpack &) override; + void visit(const operation::Min &) override; + void visit(const operation::Max &) override; + void visit(const operation::OneHot &) override; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_OPERATION_DUMPER_H__ diff --git a/runtime/onert/core/src/ir/OperationIndexList.cc b/runtime/onert/core/src/ir/OperationIndexList.cc new file mode 100644 index 000000000..bf51f9abb --- /dev/null +++ b/runtime/onert/core/src/ir/OperationIndexList.cc @@ -0,0 +1,37 @@ +/* + * 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 "ir/OperationIndexList.h" + +#include <algorithm> + +namespace onert +{ +namespace ir +{ + +OperationIndexList::OperationIndexList(std::initializer_list<OperationIndex> list) : _list(list) +{ + // DO NOTHING +} + +bool OperationIndexList::contains(const OperationIndex &index) const +{ + return std::find(_list.begin(), _list.end(), index) != _list.end(); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Operations.cc b/runtime/onert/core/src/ir/Operations.cc new file mode 100644 index 000000000..64d0bd6f0 --- /dev/null +++ b/runtime/onert/core/src/ir/Operations.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 "ir/Operations.h" + +#include "OperationCloner.h" + +namespace onert +{ +namespace ir +{ + +Operations::Operations(const Operations &obj) +{ + obj.iterate([&](const OperationIndex &index, const Operation &op) { + OperationCloner cloner; + op.accept(cloner); + _objects.emplace(index, cloner.releaseClone()); + }); + _index_count = obj._index_count; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Padding.cc b/runtime/onert/core/src/ir/Padding.cc new file mode 100644 index 000000000..31969911f --- /dev/null +++ b/runtime/onert/core/src/ir/Padding.cc @@ -0,0 +1,154 @@ +/* + * 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 "ir/Padding.h" + +#include "util/Utils.h" + +#include <stdexcept> +#include <cassert> + +namespace onert +{ +namespace ir +{ +namespace +{ + +inline ExplicitPadding validPadding(void) +{ + // + // ANEURALNETWORKS_PADDING_VALID + // + // VALID padding. No padding. + // + // When the input size is not evenly divisible by the filter size, + // the input at the end that could not fill the whole filter tile + // will simply be ignored. + // + ExplicitPadding padding; + + padding.top = 0; + padding.bottom = 0; + padding.left = 0; + padding.right = 0; + + return padding; +} + +inline ExplicitPadding samePaddingUsingIFM(const FeatureShape &ifm_shape, const Stride &stride, + uint32_t kw, uint32_t kh) +{ + ExplicitPadding padding; + + // ANEURALNETWORKS_PADDING_SAME (from NNAPI spec) + // + // SAME padding. Padding on both ends are the "same": + // + // padding_to_beginning = total_padding / 2 + // padding_to_end = (total_padding + 1)/2. + // + const int32_t vertical_expected_output = (ifm_shape.H + stride.vertical - 1) / stride.vertical; + const int32_t horizontal_expected_output = + (ifm_shape.W + stride.horizontal - 1) / stride.horizontal; + + const int32_t vertical_needed_input = (vertical_expected_output - 1) * stride.vertical + kh; + const int32_t vertical_total_padding = std::max(0, vertical_needed_input - ifm_shape.H); + + const int32_t horizontal_needed_input = (horizontal_expected_output - 1) * stride.horizontal + kw; + const int32_t horizontal_total_padding = std::max(0, horizontal_needed_input - ifm_shape.W); + + padding.top = vertical_total_padding / 2; + padding.bottom = (vertical_total_padding + 1) / 2; + padding.left = horizontal_total_padding / 2; + padding.right = (horizontal_total_padding + 1) / 2; + + return padding; +} + +inline ExplicitPadding samePadding(const FeatureShape &ifm_shape, const FeatureShape &ofm_shape, + const Stride &stride, uint32_t kw, uint32_t kh) +{ + const int32_t vertical_expected_output = (ifm_shape.H + stride.vertical - 1) / stride.vertical; + const int32_t horizontal_expected_output = + (ifm_shape.W + stride.horizontal - 1) / stride.horizontal; + assert(vertical_expected_output == ofm_shape.H); + assert(horizontal_expected_output == ofm_shape.W); + + UNUSED_RELEASE(ofm_shape); + UNUSED_RELEASE(vertical_expected_output); + UNUSED_RELEASE(horizontal_expected_output); + + return samePaddingUsingIFM(ifm_shape, stride, kw, kh); +} + +} // namespace + +inline std::string to_string(const PaddingType type) +{ + switch (type) + { + case PaddingType::EXPLICIT: + return "Padding::EXPLICIT"; + case PaddingType::SAME: + return "Padding::SAME"; + case PaddingType::VALID: + return "Padding::VALID"; + default: + throw std::runtime_error{"Fail to convert string: wrong padding type"}; + } +} + +Padding::Padding(void) : type{PaddingType::EXPLICIT}, param{0, 0, 0, 0} +{ + // DO NOTHING +} + +Padding::Padding(PaddingType paddingType) : type{paddingType}, param{0, 0, 0, 0} +{ + assert(paddingType != PaddingType::EXPLICIT); +} + +Padding::Padding(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) + : type{PaddingType::EXPLICIT}, param{left, right, top, bottom} +{ + // DO NOTHING +} + +const ExplicitPadding calculatePadding(const Padding &padding, const FeatureShape &ifm_shape, + const FeatureShape &ofm_shape, const Stride &stride, + uint32_t kw, uint32_t kh) +{ + if (padding.type == PaddingType::EXPLICIT) + { + return padding.param; + } + else if (padding.type == PaddingType::SAME) + { + return samePadding(ifm_shape, ofm_shape, stride, kw, kh); + } + else if (padding.type == PaddingType::VALID) + { + return validPadding(); + } + else + { + throw std::runtime_error{"Cannot handle padding type"}; + } +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/Shape.cc b/runtime/onert/core/src/ir/Shape.cc new file mode 100644 index 000000000..92999a4fa --- /dev/null +++ b/runtime/onert/core/src/ir/Shape.cc @@ -0,0 +1,104 @@ +/* + * 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 "ir/Shape.h" + +#include <cassert> +#include <functional> +#include <numeric> +#include <algorithm> + +namespace onert +{ +namespace ir +{ + +FeatureShape Shape::asFeature(Layout layout) const +{ + assert(rank() == 4); + + if (layout == Layout::NHWC) + { + // Feature Map in NHWC layout + // - Dimension(0) -> Batch + // - Dimension(1) -> Height + // - Dimension(2) -> Width + // - Dimension(3) -> Depth + const auto batch = dim(0); + const auto depth = dim(3); + const auto height = dim(1); + const auto width = dim(2); + + return {batch, depth, height, width}; + } + else if (layout == Layout::NCHW) + { + // Feature Map in NHWC layout + // - Dimension(0) -> Batch + // - Dimension(1) -> Depth + // - Dimension(2) -> Height + // - Dimension(3) -> Width + const auto batch = dim(0); + const auto depth = dim(1); + const auto height = dim(2); + const auto width = dim(3); + + return {batch, depth, height, width}; + } + else + { + throw std::runtime_error("Wrong Layout"); + } +} + +// Extended dimension is filled with 1. +void Shape::extendRank(int to_rank) +{ + assert(to_rank - rank() >= 0); + _dimensions.insert(_dimensions.cbegin(), to_rank - rank(), 1); +} + +uint64_t Shape::num_elements() const +{ + // All of the nodes must have non-negative dimension + assert(std::all_of(_dimensions.begin(), _dimensions.end(), + [](const int32_t &v) { return (v >= 0); })); + + return std::accumulate(_dimensions.cbegin(), _dimensions.cend(), UINT64_C(1), + std::multiplies<uint64_t>()); +} + +Shape permuteShape(const Shape &shape, Layout frontend_layout, Layout backend_layout) +{ + assert(shape.rank() <= 4); + Shape backend_shape{shape}; + if (shape.rank() == 4 && frontend_layout == Layout::NHWC && backend_layout == Layout::NCHW) + { + backend_shape.dim(1) = shape.dim(3); + backend_shape.dim(2) = shape.dim(1); + backend_shape.dim(3) = shape.dim(2); + } + else if (shape.rank() == 4 && frontend_layout == Layout::NCHW && backend_layout == Layout::NHWC) + { + backend_shape.dim(1) = shape.dim(2); + backend_shape.dim(2) = shape.dim(3); + backend_shape.dim(3) = shape.dim(1); + } + return backend_shape; +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/TypeInfo.cc b/runtime/onert/core/src/ir/TypeInfo.cc new file mode 100644 index 000000000..ab8af287e --- /dev/null +++ b/runtime/onert/core/src/ir/TypeInfo.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 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 "ir/TypeInfo.h" + +namespace onert +{ +namespace ir +{ + +bool operator==(const TypeInfo &lhs, const TypeInfo &rhs) +{ + if (lhs.type() != rhs.type()) + { + return false; + } + + if (lhs.offset() != rhs.offset()) + { + return false; + } + + if (lhs.scale() != rhs.scale()) + { + return false; + } + + return true; +} + +bool operator!=(const TypeInfo &lhs, const TypeInfo &rhs) { return !(lhs == rhs); } + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Abs.cc b/runtime/onert/core/src/ir/operation/Abs.cc new file mode 100644 index 000000000..b06705d07 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Abs.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Abs.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Abs::accept(OperationVisitor &v) const { v.visit(*this); } + +Abs::Abs(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Add.cc b/runtime/onert/core/src/ir/operation/Add.cc new file mode 100644 index 000000000..2fa30f8ed --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Add.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/Add.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Add::accept(OperationVisitor &v) const { v.visit(*this); } + +Add::Add(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ArgMax.cc b/runtime/onert/core/src/ir/operation/ArgMax.cc new file mode 100644 index 000000000..1275ae43a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ArgMax.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ArgMax.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ArgMax::accept(OperationVisitor &v) const { v.visit(*this); } + +ArgMax::ArgMax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/AvgPool2D.cc b/runtime/onert/core/src/ir/operation/AvgPool2D.cc new file mode 100644 index 000000000..28d4fcb54 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/AvgPool2D.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/AvgPool2D.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void AvgPool2D::accept(OperationVisitor &v) const { v.visit(*this); } + +AvgPool2D::AvgPool2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/BatchToSpaceND.cc b/runtime/onert/core/src/ir/operation/BatchToSpaceND.cc new file mode 100644 index 000000000..0b3955c5c --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BatchToSpaceND.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/BatchToSpaceND.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void BatchToSpaceND::accept(OperationVisitor &v) const { v.visit(*this); } + +BatchToSpaceND::BatchToSpaceND(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Cast.cc b/runtime/onert/core/src/ir/operation/Cast.cc new file mode 100644 index 000000000..09d9c327e --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Cast.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Cast.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Cast::accept(OperationVisitor &v) const { v.visit(*this); } + +Cast::Cast(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Comparison.cc b/runtime/onert/core/src/ir/operation/Comparison.cc new file mode 100644 index 000000000..2f6775411 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Comparison.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Comparison.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Comparison::accept(OperationVisitor &v) const { v.visit(*this); } + +Comparison::Comparison(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Concat.cc b/runtime/onert/core/src/ir/operation/Concat.cc new file mode 100644 index 000000000..608bc29a6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Concat.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/Concat.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Concat::accept(OperationVisitor &v) const { v.visit(*this); } + +Concat::Concat(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createAtLeast(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Conv2D.cc b/runtime/onert/core/src/ir/operation/Conv2D.cc new file mode 100644 index 000000000..3a2e1d1fe --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Conv2D.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/Conv2D.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Conv2D::accept(OperationVisitor &v) const { v.visit(*this); } + +Conv2D::Conv2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ConvertFp16ToFp32.cc b/runtime/onert/core/src/ir/operation/ConvertFp16ToFp32.cc new file mode 100644 index 000000000..676e039fa --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ConvertFp16ToFp32.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 "ir/operation/ConvertFp16ToFp32.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ConvertFp16ToFp32::accept(OperationVisitor &v) const { v.visit(*this); } + +ConvertFp16ToFp32::ConvertFp16ToFp32(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ConvertFp32ToFp16.cc b/runtime/onert/core/src/ir/operation/ConvertFp32ToFp16.cc new file mode 100644 index 000000000..bcfcbfc04 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ConvertFp32ToFp16.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 "ir/operation/ConvertFp32ToFp16.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ConvertFp32ToFp16::accept(OperationVisitor &v) const { v.visit(*this); } + +ConvertFp32ToFp16::ConvertFp32ToFp16(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Custom.cc b/runtime/onert/core/src/ir/operation/Custom.cc new file mode 100644 index 000000000..25c53e1ba --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Custom.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Custom.h" + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Custom::accept(OperationVisitor &v) const { v.visit(*this); } + +Custom::Custom(OperandConstraint input_constr, const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, std::string id, const Userdata &userdata) + : Operation{input_constr, inputs, outputs}, _id(std::move(id)), _userdata(userdata) +{ +} + +const std::string &Custom::id() const { return _id; } + +const Custom::Userdata &Custom::userdata() const { return _userdata; } + +std::string Custom::name() const { return id(); } + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/DepthToSpace.cc b/runtime/onert/core/src/ir/operation/DepthToSpace.cc new file mode 100644 index 000000000..f2d6c7c1b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/DepthToSpace.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/DepthToSpace.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void DepthToSpace::accept(OperationVisitor &v) const { v.visit(*this); } + +DepthToSpace::DepthToSpace(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/DepthwiseConv2D.cc b/runtime/onert/core/src/ir/operation/DepthwiseConv2D.cc new file mode 100644 index 000000000..d587a5591 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/DepthwiseConv2D.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/DepthwiseConv2D.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void DepthwiseConv2D::accept(OperationVisitor &v) const { v.visit(*this); } + +DepthwiseConv2D::DepthwiseConv2D(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Dequantize.cc b/runtime/onert/core/src/ir/operation/Dequantize.cc new file mode 100644 index 000000000..14d6362bd --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Dequantize.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Dequantize.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Dequantize::accept(OperationVisitor &v) const { v.visit(*this); } + +Dequantize::Dequantize(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Div.cc b/runtime/onert/core/src/ir/operation/Div.cc new file mode 100644 index 000000000..b095d9811 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Div.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Div.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Div::accept(OperationVisitor &v) const { v.visit(*this); } + +Div::Div(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/EmbeddingLookup.cc b/runtime/onert/core/src/ir/operation/EmbeddingLookup.cc new file mode 100644 index 000000000..b300b004e --- /dev/null +++ b/runtime/onert/core/src/ir/operation/EmbeddingLookup.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/EmbeddingLookup.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void EmbeddingLookup::accept(OperationVisitor &v) const { v.visit(*this); } + +EmbeddingLookup::EmbeddingLookup(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Exp.cc b/runtime/onert/core/src/ir/operation/Exp.cc new file mode 100644 index 000000000..0b22e080a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Exp.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Exp.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Exp::accept(OperationVisitor &v) const { v.visit(*this); } + +Exp::Exp(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Floor.cc b/runtime/onert/core/src/ir/operation/Floor.cc new file mode 100644 index 000000000..dc01535ad --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Floor.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Floor.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Floor::accept(OperationVisitor &v) const { v.visit(*this); } + +Floor::Floor(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/FullyConnected.cc b/runtime/onert/core/src/ir/operation/FullyConnected.cc new file mode 100644 index 000000000..118ae554a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/FullyConnected.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/FullyConnected.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void FullyConnected::accept(OperationVisitor &v) const { v.visit(*this); } + +FullyConnected::FullyConnected(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Gather.cc b/runtime/onert/core/src/ir/operation/Gather.cc new file mode 100644 index 000000000..11d46e75b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Gather.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Gather.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Gather::accept(OperationVisitor &v) const { v.visit(*this); } + +Gather::Gather(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/HashtableLookup.cc b/runtime/onert/core/src/ir/operation/HashtableLookup.cc new file mode 100644 index 000000000..e9a7a82ff --- /dev/null +++ b/runtime/onert/core/src/ir/operation/HashtableLookup.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/HashtableLookup.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void HashtableLookup::accept(OperationVisitor &v) const { v.visit(*this); } + +HashtableLookup::HashtableLookup(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(3u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/InstanceNorm.cc b/runtime/onert/core/src/ir/operation/InstanceNorm.cc new file mode 100644 index 000000000..2334560ef --- /dev/null +++ b/runtime/onert/core/src/ir/operation/InstanceNorm.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/InstanceNorm.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void InstanceNorm::accept(OperationVisitor &v) const { v.visit(*this); } + +InstanceNorm::InstanceNorm(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/L2Normalization.cc b/runtime/onert/core/src/ir/operation/L2Normalization.cc new file mode 100644 index 000000000..d1c92fa8b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/L2Normalization.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/L2Normalization.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void L2Normalization::accept(OperationVisitor &v) const { v.visit(*this); } + +L2Normalization::L2Normalization(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/L2Pool2D.cc b/runtime/onert/core/src/ir/operation/L2Pool2D.cc new file mode 100644 index 000000000..8f21b93e0 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/L2Pool2D.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/L2Pool2D.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void L2Pool2D::accept(OperationVisitor &v) const { v.visit(*this); } + +L2Pool2D::L2Pool2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LSTM.cc b/runtime/onert/core/src/ir/operation/LSTM.cc new file mode 100644 index 000000000..30a865326 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LSTM.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/LSTM.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LSTM::accept(OperationVisitor &v) const { v.visit(*this); } + +LSTM::LSTM(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(23u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LocalResponseNormalization.cc b/runtime/onert/core/src/ir/operation/LocalResponseNormalization.cc new file mode 100644 index 000000000..1ae97c142 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LocalResponseNormalization.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 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 "ir/operation/LocalResponseNormalization.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LocalResponseNormalization::accept(OperationVisitor &v) const { v.visit(*this); } + +LocalResponseNormalization::LocalResponseNormalization(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LogicalAnd.cc b/runtime/onert/core/src/ir/operation/LogicalAnd.cc new file mode 100644 index 000000000..0d50706ca --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LogicalAnd.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/LogicalAnd.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LogicalAnd::accept(OperationVisitor &v) const { v.visit(*this); } + +LogicalAnd::LogicalAnd(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LogicalNot.cc b/runtime/onert/core/src/ir/operation/LogicalNot.cc new file mode 100644 index 000000000..8f1142102 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LogicalNot.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/LogicalNot.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LogicalNot::accept(OperationVisitor &v) const { v.visit(*this); } + +LogicalNot::LogicalNot(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LogicalOr.cc b/runtime/onert/core/src/ir/operation/LogicalOr.cc new file mode 100644 index 000000000..d75207c4a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LogicalOr.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/LogicalOr.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LogicalOr::accept(OperationVisitor &v) const { v.visit(*this); } + +LogicalOr::LogicalOr(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Logistic.cc b/runtime/onert/core/src/ir/operation/Logistic.cc new file mode 100644 index 000000000..77d9d17de --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Logistic.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Logistic.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Logistic::accept(OperationVisitor &v) const { v.visit(*this); } + +Logistic::Logistic(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/LowerInfo.cc b/runtime/onert/core/src/ir/operation/LowerInfo.cc new file mode 100644 index 000000000..249918bd6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LowerInfo.cc @@ -0,0 +1,34 @@ +/* + * 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 "ir/operation/LowerInfo.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +LowerInfo::LowerInfo(const backend::Backend *backend, Layout layout) + : _permute_factor{backend, layout} +{ + // DO NOTHING +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Max.cc b/runtime/onert/core/src/ir/operation/Max.cc new file mode 100644 index 000000000..281f9d451 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Max.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Max.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Max::accept(OperationVisitor &v) const { v.visit(*this); } + +Max::Max(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/MaxPool2D.cc b/runtime/onert/core/src/ir/operation/MaxPool2D.cc new file mode 100644 index 000000000..eac53cc5e --- /dev/null +++ b/runtime/onert/core/src/ir/operation/MaxPool2D.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/MaxPool2D.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void MaxPool2D::accept(OperationVisitor &v) const { v.visit(*this); } + +MaxPool2D::MaxPool2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Mean.cc b/runtime/onert/core/src/ir/operation/Mean.cc new file mode 100644 index 000000000..5313a4434 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Mean.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Mean.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Mean::accept(OperationVisitor &v) const { v.visit(*this); } + +Mean::Mean(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Min.cc b/runtime/onert/core/src/ir/operation/Min.cc new file mode 100644 index 000000000..8be7f0cc8 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Min.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Min.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Min::accept(OperationVisitor &v) const { v.visit(*this); } + +Min::Min(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Mul.cc b/runtime/onert/core/src/ir/operation/Mul.cc new file mode 100644 index 000000000..03cdf1b61 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Mul.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Mul.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Mul::accept(OperationVisitor &v) const { v.visit(*this); } + +Mul::Mul(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Neg.cc b/runtime/onert/core/src/ir/operation/Neg.cc new file mode 100644 index 000000000..df623a13b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Neg.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Neg.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Neg::accept(OperationVisitor &v) const { v.visit(*this); } + +Neg::Neg(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/OneHot.cc b/runtime/onert/core/src/ir/operation/OneHot.cc new file mode 100644 index 000000000..22935e7d6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/OneHot.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 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 "ir/operation/OneHot.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void OneHot::accept(OperationVisitor &v) const { v.visit(*this); } + +OneHot::OneHot(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(4u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/PReLU.cc b/runtime/onert/core/src/ir/operation/PReLU.cc new file mode 100644 index 000000000..a2e37e0ad --- /dev/null +++ b/runtime/onert/core/src/ir/operation/PReLU.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/PReLU.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void PReLU::accept(OperationVisitor &v) const { v.visit(*this); } + +PReLU::PReLU(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Pack.cc b/runtime/onert/core/src/ir/operation/Pack.cc new file mode 100644 index 000000000..f0908a2c6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Pack.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Pack.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void Pack::accept(OperationVisitor &v) const { v.visit(*this); } +Pack::Pack(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createAtLeast(3u), inputs, outputs}, _param{param} +{ +} +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Pad.cc b/runtime/onert/core/src/ir/operation/Pad.cc new file mode 100644 index 000000000..a958b5241 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Pad.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Pad.h" + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Pad::accept(OperationVisitor &v) const { v.visit(*this); } + +Pad::Pad(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Permute.cc b/runtime/onert/core/src/ir/operation/Permute.cc new file mode 100644 index 000000000..fd495ed4b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Permute.cc @@ -0,0 +1,44 @@ +/* + * 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 "ir/operation/Permute.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Permute::accept(OperationVisitor &v) const { v.visit(*this); } + +Permute::Permute(const OperandIndex &input, const OperandIndex &output, + const backend::BackendContext *input_backend_ctx, + const backend::BackendContext *output_backend_ctx, Type type, DataType data_type) + : Operation{OperandConstraint::createExact(1u)}, _param{input_backend_ctx, output_backend_ctx}, + _type{type}, _dataType{data_type} +{ + setInputs({input}); + setOutputs({output}); +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/RNN.cc b/runtime/onert/core/src/ir/operation/RNN.cc new file mode 100644 index 000000000..298c5e745 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/RNN.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/RNN.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void RNN::accept(OperationVisitor &v) const { v.visit(*this); } + +RNN::RNN(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(5u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/RSQRT.cc b/runtime/onert/core/src/ir/operation/RSQRT.cc new file mode 100644 index 000000000..2bce1fa28 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/RSQRT.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/RSQRT.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void RSQRT::accept(OperationVisitor &v) const { v.visit(*this); } + +RSQRT::RSQRT(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReLU.cc b/runtime/onert/core/src/ir/operation/ReLU.cc new file mode 100644 index 000000000..f0c88478b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReLU.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReLU.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReLU::accept(OperationVisitor &v) const { v.visit(*this); } + +ReLU::ReLU(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReLU1.cc b/runtime/onert/core/src/ir/operation/ReLU1.cc new file mode 100644 index 000000000..734f0b65b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReLU1.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReLU1.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReLU1::accept(OperationVisitor &v) const { v.visit(*this); } + +ReLU1::ReLU1(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReLU6.cc b/runtime/onert/core/src/ir/operation/ReLU6.cc new file mode 100644 index 000000000..5972329af --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReLU6.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReLU6.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReLU6::accept(OperationVisitor &v) const { v.visit(*this); } + +ReLU6::ReLU6(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReduceMax.cc b/runtime/onert/core/src/ir/operation/ReduceMax.cc new file mode 100644 index 000000000..f7faf8744 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReduceMax.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReduceMax.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReduceMax::accept(OperationVisitor &v) const { v.visit(*this); } + +ReduceMax::ReduceMax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReduceMin.cc b/runtime/onert/core/src/ir/operation/ReduceMin.cc new file mode 100644 index 000000000..83cdccb78 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReduceMin.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReduceMin.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReduceMin::accept(OperationVisitor &v) const { v.visit(*this); } + +ReduceMin::ReduceMin(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ReduceSum.cc b/runtime/onert/core/src/ir/operation/ReduceSum.cc new file mode 100644 index 000000000..c25a5ac5c --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ReduceSum.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ReduceSum.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ReduceSum::accept(OperationVisitor &v) const { v.visit(*this); } + +ReduceSum::ReduceSum(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Reshape.cc b/runtime/onert/core/src/ir/operation/Reshape.cc new file mode 100644 index 000000000..b3a0d30b6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Reshape.cc @@ -0,0 +1,39 @@ +/* + * 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 "ir/operation/Reshape.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Reshape::accept(OperationVisitor &v) const { v.visit(*this); } + +Reshape::Reshape(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ResizeBilinear.cc b/runtime/onert/core/src/ir/operation/ResizeBilinear.cc new file mode 100644 index 000000000..d0d89f45f --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ResizeBilinear.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/ResizeBilinear.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ResizeBilinear::accept(OperationVisitor &v) const { v.visit(*this); } + +ResizeBilinear::ResizeBilinear(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/SQRT.cc b/runtime/onert/core/src/ir/operation/SQRT.cc new file mode 100644 index 000000000..ad887d89a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/SQRT.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/SQRT.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void SQRT::accept(OperationVisitor &v) const { v.visit(*this); } + +SQRT::SQRT(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Shape.cc b/runtime/onert/core/src/ir/operation/Shape.cc new file mode 100644 index 000000000..2a63d6dcf --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Shape.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 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 "ir/operation/Shape.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Shape::accept(OperationVisitor &v) const { v.visit(*this); } + +Shape::Shape(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Sin.cc b/runtime/onert/core/src/ir/operation/Sin.cc new file mode 100644 index 000000000..631505f36 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Sin.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 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 "ir/operation/Sin.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Sin::accept(OperationVisitor &v) const { v.visit(*this); } + +Sin::Sin(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Slice.cc b/runtime/onert/core/src/ir/operation/Slice.cc new file mode 100644 index 000000000..674bd7093 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Slice.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Slice.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Slice::accept(OperationVisitor &v) const { v.visit(*this); } + +Slice::Slice(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Softmax.cc b/runtime/onert/core/src/ir/operation/Softmax.cc new file mode 100644 index 000000000..3f1aa0af1 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Softmax.cc @@ -0,0 +1,40 @@ +/* + * 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 "ir/operation/Softmax.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Softmax::accept(OperationVisitor &v) const { v.visit(*this); } + +Softmax::Softmax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/SpaceToBatchND.cc b/runtime/onert/core/src/ir/operation/SpaceToBatchND.cc new file mode 100644 index 000000000..53fab4fa9 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/SpaceToBatchND.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/SpaceToBatchND.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void SpaceToBatchND::accept(OperationVisitor &v) const { v.visit(*this); } + +SpaceToBatchND::SpaceToBatchND(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(3u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/SpaceToDepth.cc b/runtime/onert/core/src/ir/operation/SpaceToDepth.cc new file mode 100644 index 000000000..d8a45aee5 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/SpaceToDepth.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/SpaceToDepth.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void SpaceToDepth::accept(OperationVisitor &v) const { v.visit(*this); } + +SpaceToDepth::SpaceToDepth(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Split.cc b/runtime/onert/core/src/ir/operation/Split.cc new file mode 100644 index 000000000..244884e41 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Split.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Split.h" +#include <cassert> +#include "ir/OperationVisitor.h" +namespace onert +{ +namespace ir +{ +namespace operation +{ +void Split::accept(OperationVisitor &v) const { v.visit(*this); } +Split::Split(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/SquaredDifference.cc b/runtime/onert/core/src/ir/operation/SquaredDifference.cc new file mode 100644 index 000000000..49e58aaf2 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/SquaredDifference.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/SquaredDifference.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void SquaredDifference::accept(OperationVisitor &v) const { v.visit(*this); } + +SquaredDifference::SquaredDifference(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Squeeze.cc b/runtime/onert/core/src/ir/operation/Squeeze.cc new file mode 100644 index 000000000..8cf928fb4 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Squeeze.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Squeeze.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Squeeze::accept(OperationVisitor &v) const { v.visit(*this); } + +Squeeze::Squeeze(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param(param) +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/StridedSlice.cc b/runtime/onert/core/src/ir/operation/StridedSlice.cc new file mode 100644 index 000000000..adc8beaa3 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/StridedSlice.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/StridedSlice.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void StridedSlice::accept(OperationVisitor &v) const { v.visit(*this); } + +StridedSlice::StridedSlice(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Sub.cc b/runtime/onert/core/src/ir/operation/Sub.cc new file mode 100644 index 000000000..d71071686 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Sub.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Sub.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Sub::accept(OperationVisitor &v) const { v.visit(*this); } + +Sub::Sub(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Tanh.cc b/runtime/onert/core/src/ir/operation/Tanh.cc new file mode 100644 index 000000000..8fab0c0f3 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Tanh.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Tanh.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Tanh::accept(OperationVisitor &v) const { v.visit(*this); } + +Tanh::Tanh(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/TopKV2.cc b/runtime/onert/core/src/ir/operation/TopKV2.cc new file mode 100644 index 000000000..a5e6c6a85 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/TopKV2.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/TopKV2.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void TopKV2::accept(OperationVisitor &v) const { v.visit(*this); } + +TopKV2::TopKV2(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Transpose.cc b/runtime/onert/core/src/ir/operation/Transpose.cc new file mode 100644 index 000000000..3a663fbce --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Transpose.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Transpose.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Transpose::accept(OperationVisitor &v) const { v.visit(*this); } + +Transpose::Transpose(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/TransposeConv.cc b/runtime/onert/core/src/ir/operation/TransposeConv.cc new file mode 100644 index 000000000..7f29ca44e --- /dev/null +++ b/runtime/onert/core/src/ir/operation/TransposeConv.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "ir/operation/TransposeConv.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void TransposeConv::accept(OperationVisitor &v) const { v.visit(*this); } + +TransposeConv::TransposeConv(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Unpack.cc b/runtime/onert/core/src/ir/operation/Unpack.cc new file mode 100644 index 000000000..67aa54ab5 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Unpack.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 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 "ir/operation/Unpack.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void Unpack::accept(OperationVisitor &v) const { v.visit(*this); } +Unpack::Unpack(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/ConstantInsertionPass.cc b/runtime/onert/core/src/ir/pass/ConstantInsertionPass.cc new file mode 100644 index 000000000..5f4612d35 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/ConstantInsertionPass.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 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 "ConstantInsertionPass.h" + +#include "backend/Backend.h" +#include <ir/Graph.h> +#include <util/Utils.h> + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +void ConstantInsertionPass::callback(const OperationIndex &node_index, Operation &node) +{ + const auto &op_sequence_index = _lowered_graph.op_seqs().getOperation(node_index); + const auto op_seq_lower_info = _lowered_graph.getLowerInfo(op_sequence_index); + const auto backend = op_seq_lower_info->backend(); + const auto layout = op_seq_lower_info->layout(); + const auto factor = operand::PermuteFactor{backend, layout}; + + for (const auto input : node.getInputs()) + { + auto &object = _graph.operands().at(input); + + if (object.isConstant()) + { + const auto key = ReplaceKey{input, factor}; + if (_replace_operands_map.count(key) == 0) + { + auto new_object = object; + // TODO Remove const_case + const_cast<std::list<OperationIndex> &>(new_object.getDef().list()).clear(); + const_cast<std::list<OperationIndex> &>(new_object.getUses().list()).clear(); + const auto new_index = _graph.operands().emplace(new_object); + _replace_operands_map[key] = new_index; + + _lowered_graph.setLowerInfo(new_index, std::make_unique<operand::LowerInfo>()); + _lowered_graph.getLowerInfo(new_index)->addDefPermuteFactor(factor); + } + + const auto replaced_input = _replace_operands_map[key]; + // Update op_seq + if (_lowered_graph.op_seqs().at(op_sequence_index).getInputs().contains(input)) + { + _lowered_graph.op_seqs().at(op_sequence_index).replaceInput(input, replaced_input); + } + + // Update node + node.replaceInput(input, replaced_input); + + // Update operand + auto &replaced_object = _graph.operands().at(replaced_input); + replaced_object.appendUse(node_index); + + // Update lower_info + auto replaced_lower_info = _lowered_graph.getLowerInfo(replaced_input); + replaced_lower_info->addUsePermuteFactor(factor); + + // Remove this node from def and uses of origin operand + if (object.getDef().contains(node_index)) + { + object.removeDef(node_index); + } + object.removeUse(node_index); + + // Remove origin operand + if (object.getDef().size() == 0 && object.getUses().size() == 0) + { + _graph.removeOperand(input); + _lowered_graph.removeLowerInfo(input); + } + } + } + + // Now this runtime does not support the node making output as constant + for (const auto &output : node.getOutputs()) + { + UNUSED_RELEASE(output); + assert(!_graph.operands().at(output).isConstant()); + } +} + +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/ConstantInsertionPass.h b/runtime/onert/core/src/ir/pass/ConstantInsertionPass.h new file mode 100644 index 000000000..3ea4dc397 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/ConstantInsertionPass.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_GRAPH_PASS_CONSTANT_INSERTION_PASS_H__ +#define __ONERT_GRAPH_PASS_CONSTANT_INSERTION_PASS_H__ + +#include <ir/operand/PermuteFactor.h> +#include <ir/Index.h> +#include "LoweredOperationPass.h" +#include <unordered_map> +#include <utility> + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class ConstantInsertionPass : public LoweredOperationPass +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "ConstantInsertionPass"; } + +public: + void callback(const OperationIndex &index, Operation &node) final; + +private: + struct ReplaceKey + { + OperandIndex index; + operand::PermuteFactor factor; + + bool operator==(const ReplaceKey &other) const + { + return index == other.index && factor == other.factor; + } + }; + + /** + * @brief Structure that provides hash function of ReplaceKey + */ + struct KeyHasher + { + std::size_t operator()(const ReplaceKey &key) const noexcept + { + using std::hash; + return hash<OperandIndex>()(key.index) ^ (hash<operand::PermuteFactor>()(key.factor) << 1); + } + }; + + std::unordered_map<ReplaceKey, OperandIndex, KeyHasher> _replace_operands_map; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_CONSTANT_INSERTION_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/LoweredOperandPass.h b/runtime/onert/core/src/ir/pass/LoweredOperandPass.h new file mode 100644 index 000000000..eefb8ddfb --- /dev/null +++ b/runtime/onert/core/src/ir/pass/LoweredOperandPass.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef __ONERT_IR_PASS_LOWERED_OPERAND_PASS_H__ +#define __ONERT_IR_PASS_LOWERED_OPERAND_PASS_H__ + +#include "OperandPass.h" +#include "ir/LoweredGraph.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class LoweredOperandPass : public OperandPass +{ +public: + LoweredOperandPass(ir::LoweredGraph &lowered_graph) + : OperandPass{lowered_graph.graph()}, _lowered_graph{lowered_graph} + { + // DO NOTHING + } + + virtual ~LoweredOperandPass() = default; + + std::string id() override = 0; + void callback(const OperandIndex &i, Operand &o) override = 0; + +protected: + ir::LoweredGraph &_lowered_graph; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_PASS_LOWERED_OPERAND_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/LoweredOperationPass.h b/runtime/onert/core/src/ir/pass/LoweredOperationPass.h new file mode 100644 index 000000000..0138712d7 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/LoweredOperationPass.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef __ONERT_IR_PASS_LOWERED_OPERATION_PASS_H__ +#define __ONERT_IR_PASS_LOWERED_OPERATION_PASS_H__ + +#include "OperationPass.h" +#include "ir/LoweredGraph.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class LoweredOperationPass : public OperationPass +{ +public: + LoweredOperationPass(ir::LoweredGraph &lowered_graph) + : OperationPass{lowered_graph.graph()}, _lowered_graph{lowered_graph} + { + // DO NOTHING + } + + virtual ~LoweredOperationPass() = default; + + std::string id() override = 0; + void callback(const OperationIndex &i, Operation &o) override = 0; + +protected: + ir::LoweredGraph &_lowered_graph; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_PASS_LOWERED_OPERATION_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/OperandPass.cc b/runtime/onert/core/src/ir/pass/OperandPass.cc new file mode 100644 index 000000000..693a0f493 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/OperandPass.cc @@ -0,0 +1,36 @@ +/* + * 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 "OperandPass.h" + +#include "ir/Graph.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +void OperandPass::run() +{ + _graph.operands().iterate( + [&](const OperandIndex &index, Operand &object) { callback(index, object); }); +} + +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/OperandPass.h b/runtime/onert/core/src/ir/pass/OperandPass.h new file mode 100644 index 000000000..393060741 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/OperandPass.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef __ONERT_GRAPH_PASS_OPERAND_PASS_H__ +#define __ONERT_GRAPH_PASS_OPERAND_PASS_H__ + +#include "Pass.h" +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ +class Operand; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class OperandPass : public Pass +{ +public: + using Pass::Pass; + virtual ~OperandPass() = default; + +public: + std::string id() override = 0; + void run() override final; + virtual void callback(const OperandIndex &i, Operand &o) = 0; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_OPERAND_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/OperationPass.cc b/runtime/onert/core/src/ir/pass/OperationPass.cc new file mode 100644 index 000000000..84b1da3ee --- /dev/null +++ b/runtime/onert/core/src/ir/pass/OperationPass.cc @@ -0,0 +1,38 @@ +/* + * 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 "OperationPass.h" + +#include "ir/Index.h" +#include "ir/Operation.h" +#include "ir/Graph.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +void OperationPass::run() +{ + _graph.operations().iterate( + [&](const OperationIndex &index, Operation &node) { callback(index, node); }); +} + +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/OperationPass.h b/runtime/onert/core/src/ir/pass/OperationPass.h new file mode 100644 index 000000000..1733f87ed --- /dev/null +++ b/runtime/onert/core/src/ir/pass/OperationPass.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +/** + * @file OperationPass.h + * @brief This file contains OperationPass class + */ + +#ifndef __ONERT_GRAPH_PASS_OPERATION_PASS_H__ +#define __ONERT_GRAPH_PASS_OPERATION_PASS_H__ + +#include "Pass.h" +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ +class Operation; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +/** + * @brief Class to iterate over operations and calls callback() method + */ +class OperationPass : public Pass +{ +public: + using Pass::Pass; + virtual ~OperationPass() = default; + +public: + /** + * @brief Returns string id for this pass. Same with class name. + * + * @return string id + */ + std::string id() override = 0; + + /** + * @brief Be called for all nodes of graph. + * @param index is the index of a node in graph + * @param node is the node in graph + */ + virtual void callback(const OperationIndex &index, Operation &node) = 0; + + /** + * @brief Run the pass + */ + void run() final; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_OPERATION_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/Pass.h b/runtime/onert/core/src/ir/pass/Pass.h new file mode 100644 index 000000000..1c6628f6f --- /dev/null +++ b/runtime/onert/core/src/ir/pass/Pass.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef __ONERT_GRAPH_PASS_PASS_H__ +#define __ONERT_GRAPH_PASS_PASS_H__ + +#include <string> + +namespace onert +{ +namespace ir +{ +class Graph; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class Pass +{ +public: + Pass(Graph &graph) : _graph{graph} {} + virtual ~Pass() = default; + +public: + virtual std::string id() = 0; + virtual void run() = 0; + +protected: + Graph &_graph; +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/PermutationEliminationPass.cc b/runtime/onert/core/src/ir/pass/PermutationEliminationPass.cc new file mode 100644 index 000000000..f4793d411 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationEliminationPass.cc @@ -0,0 +1,195 @@ +/* + * 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 "PermutationEliminationPass.h" + +#include "ir/Operand.h" +#include "ir/operand/LowerInfo.h" +#include "ir/Graph.h" +#include "backend/IConfig.h" +#include "util/logging.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ +void PermutationEliminationPass::callback(const OperandIndex &inp_index, Operand &object) +{ + if (_graph.getInputs().contains(inp_index)) + { + eliminateInput(inp_index, object); + } + else if (_graph.getOutputs().contains(inp_index)) + { + eliminateOutput(inp_index, object); + } +} + +void PermutationEliminationPass::eliminateInput(const OperandIndex &inp_index, Operand &object) +{ + auto &model_inputs = _graph.getInputs(); + + // get uses of the model's given input + auto uses = object.getUses(); + + // input must be used just by permutation + if (uses.size() != 1) + { + return; + } + + for (auto input_use : uses.list()) + { + auto &perm_operation = _graph.operations().at(input_use); + auto perm_inputs = perm_operation.getInputs(); + + auto perm_outputs = perm_operation.getOutputs(); + + if (!isPermuteLayerToEliminate(perm_inputs, perm_outputs, true)) + { + return; + } + + assert(perm_inputs.at(0) == inp_index); + + VERBOSE(PermutationEliminationPass::EliminateInput) << "remove NHWC_TO_NCHW permutation\n"; + + // set model's new input, which was output of permutation + model_inputs.replace(inp_index, perm_outputs.at(0)); + + // remove model's input, which is also input of permutation + _graph.removeOperand(inp_index); + + // remove permutation operation + assert(_lowered_graph.op_seqs().containsOperation(input_use)); + auto op_seq_idx = _lowered_graph.op_seqs().getOperation(input_use); + _lowered_graph.op_seqs().remove(op_seq_idx); + _graph.operations().remove(input_use); + + VERBOSE(PermutationEliminationPass::EliminateInput) + << inp_index.value() << " is model's input and is removed. New input is " + << perm_outputs.at(0).value() << "\n" + << input_use.value() << " is removed permutation operation\n"; + } +} + +void PermutationEliminationPass::eliminateOutput(const OperandIndex &out_index, Operand &object) +{ + auto &model_outputs = _graph.getOutputs(); + + // get defs of the model's given output + auto defs = object.getDef(); + + // output must use just permutation + if (defs.size() != 1) + { + return; + } + + for (auto output_def : defs.list()) + { + auto &perm_operation = _graph.operations().at(output_def); + auto perm_outputs = perm_operation.getOutputs(); + + auto perm_inputs = perm_operation.getInputs(); + if (!isPermuteLayerToEliminate(perm_inputs, perm_outputs, false)) + { + return; + } + + assert(perm_outputs.at(0) == out_index); + + VERBOSE(PermutationEliminationPass::EliminateOutput) << "remove NCHW_TO_NHWC permutation\n"; + + // Update operations' output that is used by permute operand + for (auto perm_input_index : perm_inputs) + { + auto &perm_input_operand = _graph.operands().at(perm_input_index); + perm_input_operand.removeUse(output_def); + } + + // set model's new output, which was input of permutation + model_outputs.replace(out_index, perm_inputs.at(0)); + + // remove model's output, which is also output of permutation + _graph.removeOperand(out_index); + + // remove permutation operation + assert(_lowered_graph.op_seqs().containsOperation(output_def)); + auto op_seq_idx = _lowered_graph.op_seqs().getOperation(output_def); + _lowered_graph.op_seqs().remove(op_seq_idx); + _graph.operations().remove(output_def); + + VERBOSE(PermutationEliminationPass::EliminateOutput) + << out_index.value() << " is model's output and is removed. New output is " + << perm_inputs.at(0).value() << "\n" + << output_def.value() << " is removed permutation operation\n"; + } +} + +bool PermutationEliminationPass::isPermuteLayerToEliminate(const OperandIndexSequence &inp_indexes, + const OperandIndexSequence &out_indexes, + bool is_for_model_input) +{ + auto input_def_factors = _lowered_graph.getLowerInfo(inp_indexes.at(0))->def_factors(); + auto output_def_factors = _lowered_graph.getLowerInfo(out_indexes.at(0))->def_factors(); + + auto input_layout = input_def_factors.getOnlyElement().layout(); + auto output_layout = output_def_factors.getOnlyElement().layout(); + + if (input_def_factors.size() != 1 || output_def_factors.size() != 1) + { + return false; + } + + // all operands' factor must be the same + for (auto index : inp_indexes) + { + auto op_factor_set = _lowered_graph.getLowerInfo(index)->def_factors(); + if (op_factor_set.size() != 1 || + input_layout != _lowered_graph.getLowerInfo(index)->def_factors().getOnlyElement().layout()) + { + return false; + } + } + // all operands' factor must be the same + for (auto index : out_indexes) + { + auto op_factor_set = _lowered_graph.getLowerInfo(index)->def_factors(); + if (op_factor_set.size() != 1 || + output_layout != + _lowered_graph.getLowerInfo(index)->def_factors().getOnlyElement().layout()) + { + return false; + } + } + + if (is_for_model_input) + { + // check if this is NHWC_TO_NCHW permutation: must have single input, which is model's input + return (inp_indexes.size() == 1 && input_layout == Layout::NHWC && + output_layout == Layout::NCHW); + } + + // check if this is NCHW_TO_NHWC permutation: must have single output, which is model's output + return (out_indexes.size() == 1 && input_layout == Layout::NCHW && output_layout == Layout::NHWC); +} + +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/PermutationEliminationPass.h b/runtime/onert/core/src/ir/pass/PermutationEliminationPass.h new file mode 100644 index 000000000..1c8430062 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationEliminationPass.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#ifndef __ONERT_GRAPH_PASS_PERMUTATION_ELIMINATION_PASS_H__ +#define __ONERT_GRAPH_PASS_PERMUTATION_ELIMINATION_PASS_H__ + +#include "LoweredOperandPass.h" +#include "ir/Operand.h" +#include "ir/OperandIndexSequence.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class PermutationEliminationPass : public LoweredOperandPass +{ +public: + using LoweredOperandPass::LoweredOperandPass; + +public: + std::string id() override { return "PermutationEliminationPass"; } + + void callback(const OperandIndex &index, Operand &object) override; + +private: + /** + * @brief Remove Permute operation that permutates input + * + * Note: This function aslo removes model's input and + * sets output of permutation as model's new input + * + * @param inp_index is the target operand index for the elimination + * @param object is the target operand object for the elimination + * + * @return + */ + void eliminateInput(const OperandIndex &inp_index, Operand &object); + + /** + * @brief Remove Permute operation that permutates output of a model + * + * Note: This function aslo removes model's output and + * sets input of permutation as model's new output + * + * @param out_index is the target operand index for the elimination + * @param object is the target operand object for the elimination + * + * @return + */ + void eliminateOutput(const OperandIndex &out_index, Operand &object); + + /** + * @brief Determine if passed operands are permute layer's input and output, that must be + * eliminated + * + * @param inp_index indexes of the input operand to operation + * @param out_index indexes of the output operand to operation + * @param is_for_model_input checking for model's input or output + * + * @return if it is permutation layer + */ + bool isPermuteLayerToEliminate(const OperandIndexSequence &inp_indexes, + const OperandIndexSequence &out_indexes, bool is_for_model_input); +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_PERMUTATION_ELIMINATION_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/PermutationInsertionPass.cc b/runtime/onert/core/src/ir/pass/PermutationInsertionPass.cc new file mode 100644 index 000000000..32db3d878 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationInsertionPass.cc @@ -0,0 +1,207 @@ +/* + * 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 "PermutationInsertionPass.h" + +#include <cassert> +#include <utility> +#include <unordered_map> + +#include "ir/Operand.h" +#include "ir/operation/LowerInfo.h" +#include "ir/Graph.h" +#include "backend/IConfig.h" +#include "util/logging.h" +#include <memory> +#include "ir/operation/Permute.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +void PermutationInsertionPass::callback(const OperandIndex &index, Operand &object) +{ + auto &&operand_li = _lowered_graph.getLowerInfo(index); + assert(operand_li); + + // NOTE Later, constants also will have Def + // Ignore constants + if (operand_li->def_factors().size() == 0) + { + return; + } + + std::list<OperationIndex> permute_indexes; + + // Build a map for all necessary type of operands + std::unordered_map<operand::PermuteFactor, OperandIndex> factor_to_index; + { + assert(operand_li->def_factors().size() == 1); + for (auto factor : operand_li->def_factors()) + { + factor_to_index.emplace(factor, index); + } + + auto insert_set = operand_li->use_factors() - operand_li->def_factors(); + for (auto factor : insert_set) + { + const auto permute_operation_index = insertPermute(index, factor); + permute_indexes.push_back(permute_operation_index); + VERBOSE(PermutationInsertionPass) << "Insert 'Permute' operation for operand " + << index.value() << std::endl; + const auto &permute_operation = _graph.operations().at(permute_operation_index); + const auto permuted_operand_index = permute_operation.getOutputs().at(0); + factor_to_index.emplace(factor, permuted_operand_index); + } + } + + // Update operations' input that uses this operand + { + std::list<OperationIndex> remove_list; + + auto uses = object.getUses(); + for (auto use : uses.list()) + { + // If permute operation, ignore it + if (std::find(permute_indexes.begin(), permute_indexes.end(), use) != permute_indexes.end()) + continue; + + auto &operation = _graph.operations().at(use); + assert(_lowered_graph.op_seqs().containsOperation(use)); + auto op_seq_index = _lowered_graph.op_seqs().getOperation(use); + auto op_seq_li = _lowered_graph.getLowerInfo(op_seq_index); + assert(op_seq_li); + const auto op_seq_layout = op_seq_li->layout(); + const backend::Backend *backend = op_seq_li->backend(); + assert(backend); + auto use_node_inputs = operation.getInputs(); + assert(use_node_inputs.contains(index)); + + auto new_index = factor_to_index.at({backend, op_seq_layout}); + if (index != new_index) + { + // Update from op_seq + _lowered_graph.op_seqs().at(op_seq_index).replaceInput(index, new_index); + + // Update from operation + operation.replaceInput(index, new_index); + + // Update from operand + remove_list.push_back( + use); // Removal should be done in another loop since we are in the loop + _graph.operands().at(new_index).appendUse(use); + } + } + + for (auto &operation : remove_list) + { + object.removeUse(operation); + } + } +} + +OperationIndex PermutationInsertionPass::insertPermute(const OperandIndex &operand_index, + const operand::PermuteFactor &factor) +{ + assert(!_graph.isBuildingPhase()); + + auto &operand = _graph.operands().at(operand_index); + + // Generate output operand and permute operation + auto out_operand_index = _graph.addOperand(operand.shape(), operand.typeInfo()); + // change model output if operand_index is model output index + auto &model_outputs = _graph.getOutputs(); + if (model_outputs.contains(operand_index)) + { + model_outputs.replace(operand_index, out_operand_index); + } + + // Find Permute information + auto input_backend = + _lowered_graph.getLowerInfo(operand_index)->def_factors().getOnlyElement().backend(); + auto output_backend = factor.backend(); + // NOTE Permute may not have specific layout because the layout of input and output may be + // different. + const auto permute_node_layout = Layout::UNKNOWN; + const auto permute_node_backend = compiler::BackendManager::get().getDefault(); + const operand::PermuteFactor permute_node_factor{permute_node_backend, permute_node_layout}; + + // Update LowerInfo of input operand + auto operand_lower_info = _lowered_graph.getLowerInfo(operand_index); + operand_lower_info->removeUsePermuteFactor(factor); + operand_lower_info->addUsePermuteFactor(permute_node_factor); + + // Update LowerInfo of output operand + auto out_operand_li = std::make_unique<operand::LowerInfo>(); + + // The input and output factors of all nodes will be the same except Permute. So Tensor's + // allocators allocates memory using only the information of def permutation factor now. + // TODO Change param to permute_node_factor + out_operand_li->addDefPermuteFactor(factor); + out_operand_li->addUsePermuteFactor(factor); + _lowered_graph.setLowerInfo(out_operand_index, std::move(out_operand_li)); + + auto input_backend_ctx = _lowered_graph.backend_contexts().at(input_backend).get(); + auto output_backend_ctx = _lowered_graph.backend_contexts().at(output_backend).get(); + + // Insert permute operation to the graph + const auto input_layout = + _lowered_graph.getLowerInfo(operand_index)->def_factors().getOnlyElement().layout(); + const auto output_layout = factor.layout(); + using Permute = operation::Permute; + const auto permute_type = [&]() { + if (input_layout == Layout::NHWC && output_layout == Layout::NCHW) + { + return Permute::Type::NHWC_TO_NCHW; + } + else if (input_layout == Layout::NCHW && output_layout == Layout::NHWC) + { + return Permute::Type::NCHW_TO_NHWC; + } + else + { + return Permute::Type::COPY; + } + }(); + auto insert_node = std::make_unique<Permute>(operand_index, out_operand_index, input_backend_ctx, + output_backend_ctx, permute_type); + + auto node_index = _graph.operations().push(std::move(insert_node)); + const auto &node = _graph.operations().at(node_index); + + // OpSequence + { + auto op_seq_index = _lowered_graph.op_seqs().emplace(node_index, node, permute_node_layout); + auto &op_seq = _lowered_graph.op_seqs().at(op_seq_index); + op_seq.setInputs(node.getInputs()); + op_seq.setOutputs(node.getOutputs()); + _lowered_graph.setLowerInfo(op_seq_index, std::make_unique<operation::LowerInfo>( + permute_node_backend, permute_node_layout)); + } + + // Update Use/Def info + { + _graph.operands().at(operand_index).appendUse(node_index); + _graph.operands().at(out_operand_index).appendDef(node_index); + } + return node_index; +} +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/PermutationInsertionPass.h b/runtime/onert/core/src/ir/pass/PermutationInsertionPass.h new file mode 100644 index 000000000..314a54c95 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationInsertionPass.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef __ONERT_GRAPH_PASS_PERMUTATION_INSERTION_PASS_H__ +#define __ONERT_GRAPH_PASS_PERMUTATION_INSERTION_PASS_H__ + +#include "LoweredOperandPass.h" +#include "compiler/BackendManager.h" +#include "ir/Operand.h" //for OperationIndex +#include "ir/operand/PermuteFactor.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class PermutationInsertionPass : public LoweredOperandPass +{ +public: + using LoweredOperandPass::LoweredOperandPass; + +public: + std::string id() override { return "PermutationInsertionPass"; } + void callback(const OperandIndex &index, Operand &object) override; + + /** + * @brief Insert Permute operation that has given operand as input + * + * @param operand_index is the target operand index for the insertion + * @param factor is the output operand's backend type and layout + * + * @return OperationIndex + */ + OperationIndex insertPermute(const OperandIndex &operand_index, + const operand::PermuteFactor &factor); + +private: +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_PERMUTATION_INSERTION_PASS_H__ diff --git a/runtime/onert/core/src/ir/pass/PermutationOperationPass.cc b/runtime/onert/core/src/ir/pass/PermutationOperationPass.cc new file mode 100644 index 000000000..f2172c4b0 --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationOperationPass.cc @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019 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 "PermutationOperationPass.h" + +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "ir/Graph.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +void PermutationOperationPass::callback(const OperationIndex &, Operation &node) +{ + node.accept(*this); +}; + +void PermutationOperationPass::changeToKeepLayout(const Operation &node) +{ + const auto &output_ind = node.getOutputs().at(0); + const auto &output_obj = _graph.operands().at(output_ind); + + assert(output_obj.getDef().size() == 1); + const auto &node_index = output_obj.getDef().list().front(); + const auto &op_seq_index = _lowered_graph.op_seqs().getOperation(node_index); + + const auto frontend_layout = _lowered_graph.op_seqs().at(op_seq_index).getLayout(); + const auto backend_layout = _lowered_graph.getLowerInfo(op_seq_index)->layout(); + + if (frontend_layout == backend_layout) + { + return; + } + + // CPU supports only NHWC now + if (_lowered_graph.getLowerInfo(op_seq_index)->backend()->config()->id() != "cpu") + { + // TODO Change backend of this node + assert(frontend_layout == Layout::NHWC || backend_layout == Layout::UNKNOWN); + } + + // Divide op_seq based on target operation + { + auto &prev_op_seq = _lowered_graph.op_seqs().at(op_seq_index); + + // Create new op_seq and move information from existing op_seq to new op_seq if target + // node is the end of op_seq + auto it = prev_op_seq.begin(); + // Find iterator of target node in op_seq + while ((it++)->index != node_index) + ; + if (it != prev_op_seq.end()) + { + const auto &next_op_seq_index = + _lowered_graph.op_seqs().emplace(it->index, *it->node, prev_op_seq.getLayout()); + auto &next_op_seq = _lowered_graph.op_seqs().at(next_op_seq_index); + next_op_seq.setInputs(it->node->getInputs()); + next_op_seq.setOutputs(it->node->getOutputs()); + + std::vector<OperationIndex> remove_list; + remove_list.emplace_back(it->index); + while (++it != prev_op_seq.end()) + { + next_op_seq.appendOperation(it->index, *it->node); + next_op_seq.setOutputs(it->node->getOutputs()); + remove_list.emplace_back(it->index); + } + + prev_op_seq.setOutputs(node.getOutputs()); + for (const auto &index : remove_list) + { + prev_op_seq.remove(index); + } + + const auto op_seq_li = _lowered_graph.getLowerInfo(op_seq_index); + _lowered_graph.setLowerInfo( + next_op_seq_index, + std::make_unique<operation::LowerInfo>(op_seq_li->backend(), op_seq_li->layout())); + } + } + + // Remove target operation from op_seq and insert the target operation to new op_seq + { + const auto backend = _lowered_graph.getLowerInfo(op_seq_index)->backend(); + + // Remove target operation from op_sequence + _lowered_graph.op_seqs().removeFromOpSequence(node_index); + + if (!_lowered_graph.op_seqs().exist(op_seq_index)) + { + // Remove lowerinfo for op_seq of target operation if the op_seq does not exist + _lowered_graph.removeLowerInfo(op_seq_index); + } + else + { + // Update op_seq of target operation if the op_seq exists + auto &prev_op_seq = _lowered_graph.op_seqs().at(op_seq_index); + const auto last_node = (--prev_op_seq.end())->node; + prev_op_seq.setOutputs(last_node->getOutputs()); + } + + // Create new op_seq and set information to the op_seq + auto new_op_seq_index = _lowered_graph.op_seqs().emplace(node_index, node, frontend_layout); + auto &new_op_seq = _lowered_graph.op_seqs().at(new_op_seq_index); + new_op_seq.setInputs(node.getInputs()); + new_op_seq.setOutputs(node.getOutputs()); + _lowered_graph.setLowerInfo(new_op_seq_index, + std::make_unique<operation::LowerInfo>(backend, frontend_layout)); + } + + // Change PermuteFactors of operands of target node + { + const auto &op_seq_index = _lowered_graph.op_seqs().getOperation(node_index); + const auto op_seq_li = _lowered_graph.getLowerInfo(op_seq_index); + const auto backend = op_seq_li->backend(); + const operand::PermuteFactor removed_factor{backend, backend_layout}; + const operand::PermuteFactor new_factor{backend, frontend_layout}; + for (const auto &input : node.getInputs()) + { + bool canRemove = true; + for (const auto &use : _graph.operands().at(input).getUses().list()) + { + if (use != node_index) + { + const auto &use_op_seq_index = _lowered_graph.op_seqs().getOperation(use); + auto use_op_seq_li = _lowered_graph.getLowerInfo(use_op_seq_index); + if (use_op_seq_li->backend() == backend && use_op_seq_li->layout() == backend_layout) + { + canRemove = false; + break; + } + } + } + + auto lower_info = _lowered_graph.getLowerInfo(input); + if (canRemove) + { + lower_info->removeUsePermuteFactor(removed_factor); + } + lower_info->addUsePermuteFactor(new_factor); + + // Whether if node's input is an input of model or a constant + if (_graph.operands().at(input).getDef().size() == 0) + { + assert(_graph.getInputs().contains(input) || _graph.operands().at(input).isConstant()); + lower_info->removeDefPermuteFactor(removed_factor); + lower_info->addDefPermuteFactor(new_factor); + } + } + + for (const auto &output : node.getOutputs()) + { + auto lower_info = _lowered_graph.getLowerInfo(output); + lower_info->removeDefPermuteFactor(removed_factor); + lower_info->addDefPermuteFactor(new_factor); + + // Whether if node's output is an output of model + if (_graph.operands().at(output).getUses().size() == 0) + { + assert(_graph.getOutputs().contains(output)); + lower_info->removeUsePermuteFactor(removed_factor); + lower_info->addUsePermuteFactor(new_factor); + } + } + } +} + +void PermutationOperationPass::visit(const operation::FullyConnected &node) +{ + const auto &input_ind = node.getInputs().at(operation::FullyConnected::Input::INPUT); + const auto &input_obj = _graph.operands().at(input_ind); + const auto &input_shape = input_obj.shape(); + + if (input_shape.rank() == 4) + { + changeToKeepLayout(node); + } +} + +void PermutationOperationPass::visit(const operation::Gather &node) +{ + const auto &input_ind = node.getInputs().at(operation::Gather::Input::INPUT); + const auto &input_obj = _graph.operands().at(input_ind); + const auto &input_shape = input_obj.shape(); + + const auto &output_ind = node.getOutputs().at(0); + const auto &output_obj = _graph.operands().at(output_ind); + const auto &output_shape = output_obj.shape(); + + if (input_shape.rank() >= 4 || output_shape.rank() >= 4) + { + changeToKeepLayout(node); + } +} + +void PermutationOperationPass::visit(const operation::Reshape &node) +{ + const auto &input_ind = node.getInputs().at(operation::Reshape::Input::INPUT); + const auto &input_obj = _graph.operands().at(input_ind); + const auto &input_shape = input_obj.shape(); + + const auto &output_ind = node.getOutputs().at(0); + const auto &output_obj = _graph.operands().at(output_ind); + const auto &output_shape = output_obj.shape(); + + if (input_shape.rank() >= 4 || output_shape.rank() >= 4) + { + changeToKeepLayout(node); + } +} + +} // namespace pass +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/pass/PermutationOperationPass.h b/runtime/onert/core/src/ir/pass/PermutationOperationPass.h new file mode 100644 index 000000000..e68065bfb --- /dev/null +++ b/runtime/onert/core/src/ir/pass/PermutationOperationPass.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef __ONERT_GRAPH_PASS_PERMUTATION_OPERATION_PASS_H__ +#define __ONERT_GRAPH_PASS_PERMUTATION_OPERATION_PASS_H__ + +#include "ir/OperationVisitor.h" +#include "LoweredOperationPass.h" + +namespace onert +{ +namespace ir +{ +namespace pass +{ + +class PermutationOperationPass : public LoweredOperationPass, public OperationVisitor +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "PermutationOperationPass"; } + +public: + void callback(const OperationIndex &i, Operation &n) final; + +public: + void visit(const operation::FullyConnected &) final; + void visit(const operation::Gather &) final; + void visit(const operation::Reshape &) final; + +private: + void changeToKeepLayout(const Operation &); +}; + +} // namespace pass +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_PASS_PERMUTATION_OPERATION_PASS_H__ diff --git a/runtime/onert/core/src/ir/verifier/Verifier.cc b/runtime/onert/core/src/ir/verifier/Verifier.cc new file mode 100644 index 000000000..c4655ee42 --- /dev/null +++ b/runtime/onert/core/src/ir/verifier/Verifier.cc @@ -0,0 +1,96 @@ +/* + * 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 "Verifier.h" + +#include "ir/Graph.h" +#include "ir/OperationIndexMap.h" + +#include "util/logging.h" + +namespace onert +{ +namespace ir +{ +namespace verifier +{ + +// +// DAGChecker +// + +bool DAGChecker::verify(const Graph &graph) const +{ + auto &operations = graph.operations(); + bool cyclic = false; + + OperationIndexMap<bool> visited; + operations.iterate( + [&](const OperationIndex &index, const Operation &) { visited[index] = false; }); + OperationIndexMap<bool> on_stack = visited; // Copy from visited + + std::function<void(const OperationIndex &index, const Operation &)> dfs_recursive = + [&](const OperationIndex &index, const Operation &node) -> void { + if (on_stack[index]) + cyclic = true; + if (visited[index]) + return; + visited[index] = true; + on_stack[index] = true; + + for (auto output : node.getOutputs()) + { + const auto &operand = graph.operands().at(output); + for (const auto &use : operand.getUses().list()) + { + dfs_recursive(use, graph.operations().at(use)); + } + } + + on_stack[index] = false; + }; + + operations.iterate(dfs_recursive); + + return !cyclic; +} + +// +// EdgeConsistencyVerifier +// + +bool EdgeConsistencyChecker::verify(const Graph &graph) const +{ + auto &operations = graph.operations(); + uint32_t mismatches = 0; + operations.iterate([&](const OperationIndex &index, const Operation &node) { + for (auto operand_index : node.getInputs()) + { + auto &operand = graph.operands().at(operand_index); + mismatches += (operand.getUses().contains(index) ? 0 : 1); + } + for (auto operand_index : node.getOutputs()) + { + auto &operand = graph.operands().at(operand_index); + mismatches += (operand.getDef().contains(index) ? 0 : 1); + } + }); + return mismatches == 0; +} + +} // namespace verifier +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/verifier/Verifier.h b/runtime/onert/core/src/ir/verifier/Verifier.h new file mode 100644 index 000000000..0bc22bc47 --- /dev/null +++ b/runtime/onert/core/src/ir/verifier/Verifier.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef __ONERT_GRAPH_VERIFIER_VERIFIER_H__ +#define __ONERT_GRAPH_VERIFIER_VERIFIER_H__ + +namespace onert +{ +namespace ir +{ +class Graph; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace verifier +{ + +struct IVerifier +{ + virtual ~IVerifier() = default; + virtual bool verify(const Graph &graph) const = 0; +}; + +} // namespace verifier +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace verifier +{ + +class DAGChecker : public IVerifier +{ +public: + bool verify(const Graph &graph) const override; +}; + +class EdgeConsistencyChecker : public IVerifier +{ +public: + bool verify(const Graph &graph) const override; +}; + +} // namespace verifier +} // namespace ir +} // namespace onert + +#endif // __ONERT_GRAPH_VERIFIER_VERIFIER_H__ diff --git a/runtime/onert/core/src/library_info.cc b/runtime/onert/core/src/library_info.cc new file mode 100644 index 000000000..6d7579cca --- /dev/null +++ b/runtime/onert/core/src/library_info.cc @@ -0,0 +1,17 @@ +/* + * 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. + */ + +volatile const char info[] = "library information : runtime=onert"; diff --git a/runtime/onert/core/src/util/ConfigSource.cc b/runtime/onert/core/src/util/ConfigSource.cc new file mode 100644 index 000000000..45cce662e --- /dev/null +++ b/runtime/onert/core/src/util/ConfigSource.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019 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 "util/ConfigSource.h" +#include "util/GeneralConfigSource.h" +#include "util/EnvConfigSource.h" + +#include <array> +#include <algorithm> +#include <cassert> + +#include <memory> + +namespace onert +{ +namespace util +{ + +static std::unique_ptr<IConfigSource> _source; + +void config_source(std::unique_ptr<IConfigSource> &&source) { _source = std::move(source); } + +static IConfigSource *config_source() +{ + if (!_source) + { +#ifdef ENVVAR_FOR_DEFAULT_CONFIG + // Default ConfigSource is EnvConfigSource + _source = std::make_unique<EnvConfigSource>(); +#else + _source = std::make_unique<GeneralConfigSource>(); +#endif // ENVVAR_FOR_DEFAULT_CONFIG + } + return _source.get(); +} + +static std::string getConfigOrDefault(const std::string &key) +{ + static std::unordered_map<std::string, std::string> defaults; + if (defaults.empty()) + { +#define CONFIG(Name, Type, Default) \ + { \ + auto name = std::string{#Name}; \ + defaults.emplace(name, std::string{Default}); \ + } + +#include "util/Config.lst" + +#undef CONFIG + } + + // Treat empty string and absence of the value to be the same + auto ret = config_source()->get(key); + if (ret.empty()) + { + auto itr = defaults.find(key); + if (itr != defaults.end()) + { + // Return the default value if exists + ret = itr->second; + } + } + + return ret; +} + +bool toBool(const std::string &val) +{ + static const std::array<std::string, 5> false_list{"0", "OFF", "FALSE", "N", "NO"}; + auto false_found = std::find(false_list.begin(), false_list.end(), val); + return false_found == false_list.end(); +} + +int toInt(const std::string &val) { return std::stoi(val); } + +bool getConfigBool(const std::string &key) +{ + auto raw = getConfigOrDefault(key); + return toBool(raw); +} + +int getConfigInt(const std::string &key) +{ + auto raw = getConfigOrDefault(key); + return toInt(raw); +} + +std::string getConfigString(const std::string &key) { return getConfigOrDefault(key); } + +} // namespace util +} // namespace onert + +namespace onert +{ +namespace util +{ +namespace config +{ + +#define CONFIG(Name, Type, Default) const char *Name = #Name; + +#include "util/Config.lst" + +#undef CONFIG + +} // namespace config +} // namespace util +} // namespace onert diff --git a/runtime/onert/core/src/util/EnvConfigSource.cc b/runtime/onert/core/src/util/EnvConfigSource.cc new file mode 100644 index 000000000..0d25b7353 --- /dev/null +++ b/runtime/onert/core/src/util/EnvConfigSource.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 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 "util/EnvConfigSource.h" + +#include <cstdlib> + +namespace onert +{ +namespace util +{ + +std::string EnvConfigSource::get(const std::string &key) const +{ + const char *value = std::getenv(key.c_str()); + if (value != nullptr) + { + return value; + } + else + { + return GeneralConfigSource::get(key); + } +} + +} // namespace util +} // namespace onert diff --git a/runtime/onert/core/src/util/EventCollectorGlobal.cc b/runtime/onert/core/src/util/EventCollectorGlobal.cc new file mode 100644 index 000000000..ab8ddab60 --- /dev/null +++ b/runtime/onert/core/src/util/EventCollectorGlobal.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 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 "util/EventCollectorGlobal.h" + +#include <cassert> +#include <fstream> + +#include "util/ConfigSource.h" + +namespace onert +{ +namespace util +{ + +EventCollectorGlobal::EventCollectorGlobal() : _recorder{}, _collector{&_recorder} +{ + // DO NOTHING +} + +EventCollectorGlobal::~EventCollectorGlobal() +{ + if (!_recorder.empty()) + { + // TODO Need better way for saved file path than the hardcoded path + std::ofstream ofs{"trace.global.json"}; + _recorder.writeToFile(ofs); + } +} + +EventCollectorGlobal &EventCollectorGlobal::get() +{ + static EventCollectorGlobal instance; + return instance; +} + +EventDurationBlock::EventDurationBlock(const std::string &tag) : _tag{tag} +{ + auto &glob = EventCollectorGlobal::get(); + glob.collector().onEvent(EventCollector::Event{EventCollector::Edge::BEGIN, "0", _tag}); +} +EventDurationBlock::~EventDurationBlock() +{ + auto &glob = EventCollectorGlobal::get(); + glob.collector().onEvent(EventCollector::Event{EventCollector::Edge::END, "0", _tag}); +} + +EventDurationManual::EventDurationManual(const std::string &tag) : _tag{tag}, _pair{true} {} + +EventDurationManual::~EventDurationManual() +{ + // Check if it has called begin-end pair + assert(_pair); +} + +void EventDurationManual::begin() +{ + _pair = false; + auto &glob = EventCollectorGlobal::get(); + glob.collector().onEvent(EventCollector::Event{EventCollector::Edge::BEGIN, "0", _tag}); +} + +void EventDurationManual::end() +{ + assert(!_pair); + _pair = true; + auto &glob = EventCollectorGlobal::get(); + glob.collector().onEvent(EventCollector::Event{EventCollector::Edge::END, "0", _tag}); +} + +} // namespace util +} // namespace onert diff --git a/runtime/onert/core/src/util/GeneralConfigSource.cc b/runtime/onert/core/src/util/GeneralConfigSource.cc new file mode 100644 index 000000000..7d2757e58 --- /dev/null +++ b/runtime/onert/core/src/util/GeneralConfigSource.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 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 "util/GeneralConfigSource.h" +#include "util/logging.h" + +namespace onert +{ +namespace util +{ + +std::string GeneralConfigSource::get(const std::string &key) const +{ + auto itr = _map.find(key); + if (itr == _map.end()) + { + return ""; + } + else + { + return itr->second; + } +} + +void GeneralConfigSource::set(const std::string &key, const std::string &val) +{ + VERBOSE(GeneralConfigSource) << key << " : " << val << std::endl; + _map[key] = val; +} + +} // namespace util +} // namespace onert diff --git a/runtime/onert/core/src/util/ShapeInference.cc b/runtime/onert/core/src/util/ShapeInference.cc new file mode 100644 index 000000000..ace16959d --- /dev/null +++ b/runtime/onert/core/src/util/ShapeInference.cc @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2019 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 "util/Utils.h" +#include "ir/InternalType.h" +#include "ir/Shape.h" +#include "ir/operation/AvgPool2D.h" +#include "ir/operation/MaxPool2D.h" +#include "util/ShapeInference.h" + +namespace onert +{ +namespace shape_inference +{ + +// +// Helper functions +// + +namespace +{ + +template <typename T, typename U> +typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value, + typename std::common_type<T, U>::type>::type +ceil_div(T dividend, U divisor) +{ + assert(dividend > 0 && divisor > 0 && "this implementations is for positive numbers only"); + return (dividend + divisor - 1) / divisor; +} + +// Calculate the result of broadcast of two shapes +ir::Shape broadcastShapes(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape) +{ + ir::Shape out_shape; + auto max_rank = std::max(lhs_shape.rank(), rhs_shape.rank()); + + for (int idx = 0; idx < max_rank; ++idx) + { + // Go over operands dimensions from right to left + int lhs_idx = lhs_shape.rank() - idx - 1; + int rhs_idx = rhs_shape.rank() - idx - 1; + + int32_t lhs_dim = lhs_idx >= 0 ? lhs_shape.dim(lhs_idx) : 1; + int32_t rhs_dim = rhs_idx >= 0 ? rhs_shape.dim(rhs_idx) : 1; + + if (lhs_dim != 1 && rhs_dim != 1 && lhs_dim != rhs_dim) + throw std::runtime_error("Incompatible shapes for broadcast"); + + out_shape.prepend(std::max(lhs_dim, rhs_dim)); + } + + return out_shape; +} + +// Calculate output height and width of convolution-like operation +std::pair<int, int> calcConvLikeHeightAndWidth(const int in_h, const int in_w, const int ker_h, + const int ker_w, const ir::Padding pad, + const ir::Stride stride) +{ + int32_t out_h = 0, out_w = 0; + + switch (pad.type) + { + case ir::PaddingType::SAME: + out_h = ceil_div(in_h, stride.vertical); + out_w = ceil_div(in_w, stride.horizontal); + break; + case ir::PaddingType::VALID: + out_h = ceil_div(in_h - ker_h + 1, stride.vertical); + out_w = ceil_div(in_w - ker_w + 1, stride.horizontal); + break; + case ir::PaddingType::EXPLICIT: + out_h = (in_h + pad.param.top + pad.param.bottom - ker_h) / stride.vertical + 1; + out_w = (in_w + pad.param.left + pad.param.right - ker_w) / stride.horizontal + 1; + break; + default: + assert(false); + } + + return {out_h, out_w}; +} + +} // namespace + +// +// Shape inference +// + +Shapes inferEltwiseShape(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape) +{ + return {broadcastShapes(lhs_shape, rhs_shape)}; +} + +Shapes inferAvgPoolShape(const ir::Shape &in_shape, const ir::operation::AvgPool2D::Param ¶m, + const ir::Layout layout) +{ + assert(layout == ir::Layout::NHWC); + auto ifm_shape = in_shape.asFeature(layout); + const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, param.kh, param.kw, + param.padding, param.stride); + // Pooling don't change number of channels and batch size + return {ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, ifm_shape.C}}; +} + +Shapes inferConcatShape(const Shapes &in_shapes, const ir::operation::Concat::Param ¶m) +{ + const int32_t concat_axis = param.axis; + const auto &first_in_shape = in_shapes[0]; + + // Check that all shapes are equal except for concat axis dimension + for (const auto &in_shape : in_shapes) + { + assert(in_shape.rank() == first_in_shape.rank()); + for (int64_t dim_idx = 0; dim_idx < in_shape.rank(); ++dim_idx) + assert(dim_idx == concat_axis || in_shape.dim(dim_idx) == first_in_shape.dim(dim_idx)); + } + + // Calculate output shape + ir::Shape out_shape(first_in_shape); + out_shape.dim(concat_axis) = 0; + for (const auto &in_shape : in_shapes) + out_shape.dim(concat_axis) += in_shape.dim(concat_axis); + return {out_shape}; +} + +Shapes inferMaxPoolShape(const ir::Shape &in_shape, const ir::operation::MaxPool2D::Param ¶m, + const ir::Layout layout) +{ + assert(layout == ir::Layout::NHWC); + auto ifm_shape = in_shape.asFeature(layout); + const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, param.kh, param.kw, + param.padding, param.stride); + // Pooling don't change number of channels and batch size + return {ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, ifm_shape.C}}; +} + +Shapes inferConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::Conv2D::Param ¶m, ir::Layout layout) +{ + assert(layout == ir::Layout::NHWC); + auto ifm_shape = in_shape.asFeature(layout); + + // Kernel format is [depth_out, kernel_height, kernel_width, depth_in] + auto kf_shape = ker_shape.asFeature(layout); + assert(ifm_shape.C == kf_shape.C); + + const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, kf_shape.H, kf_shape.W, + param.padding, param.stride); + + return {ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.N}}; +} + +Shapes inferDepthwiseConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::DepthwiseConv2D::Param ¶m, + ir::Layout layout) +{ + assert(layout == ir::Layout::NHWC); + auto ifm_shape = in_shape.asFeature(layout); + + // Kernel format is [1, kernel_height, kernel_width, depth_out] + auto kf_shape = ker_shape.asFeature(layout); + assert(kf_shape.C == static_cast<int32_t>(ifm_shape.C * param.multiplier)); + assert(kf_shape.N == 1); + + const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, kf_shape.H, kf_shape.W, + param.padding, param.stride); + + return {ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.C}}; +} + +Shapes inferFullyConnectedShape(const ir::Shape &in_shape, const ir::Shape &ker_shape) +{ + assert(in_shape.rank() >= 2); + assert(ker_shape.rank() == 2); + + const auto input_size_with_batch = in_shape.num_elements(); + const auto num_units = ker_shape.dim(0); + const auto input_size = ker_shape.dim(1); + const auto batch_size = input_size_with_batch / input_size; + assert(input_size_with_batch % input_size == 0); + + return {{ir::Shape({static_cast<int32_t>(batch_size), num_units})}}; +} + +/* + StaticInferer +*/ + +void StaticInferer::visit(const ir::operation::Reshape &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Reshape::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // if input is dynamic, output also becomes dynamic + if (input.info().memAllocType() == ir::MemAllocType::DYNAMIC) + { + output.info().memAllocType(ir::MemAllocType::DYNAMIC); + return; + } + + if (op.getInputs().size() == 1) + { + // no change on output shape + return; + } + + // Let's check the second input + const auto shape_idx{op.getInputs().at(ir::operation::Reshape::Input::SHAPE)}; + const auto &shape = _operands.at(shape_idx); + + if (shape.isConstant()) + { + // if shape is from Const, TFLC put the shape of output into tensor + // no change on output shape + return; + } + + // if shape is NOT Const, set output shape to be dynamic_ + output.info().memAllocType(ir::MemAllocType::DYNAMIC); +} + +} // namespace shape_inference +} // namespace onert diff --git a/runtime/onert/core/src/util/logging.cc b/runtime/onert/core/src/util/logging.cc new file mode 100644 index 000000000..6309d25e5 --- /dev/null +++ b/runtime/onert/core/src/util/logging.cc @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 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 "util/logging.h" + +onert::util::logging::Context &onert::util::logging::Context::get() noexcept +{ + static Context ctx; + return ctx; +} |