diff options
Diffstat (limited to 'runtime/onert/core')
413 files changed, 40309 insertions, 0 deletions
diff --git a/runtime/onert/core/CMakeLists.txt b/runtime/onert/core/CMakeLists.txt new file mode 100644 index 000000000..ea212a42b --- /dev/null +++ b/runtime/onert/core/CMakeLists.txt @@ -0,0 +1,54 @@ +file(GLOB_RECURSE SOURCES "src/*.cc") +file(GLOB_RECURSE TESTS "*.test.cc") +list(REMOVE_ITEM SOURCES ${TESTS}) + +nnfw_find_package(Ruy REQUIRED) + +add_library(onert_core SHARED ${SOURCES}) +set_target_properties(onert_core PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(onert_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(onert_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) +target_link_libraries(onert_core PUBLIC nnfw_lib_misc half) +target_link_libraries(onert_core PRIVATE nnfw_lib_cker) +target_link_libraries(onert_core PRIVATE nnfw_common) +target_link_libraries(onert_core PRIVATE nnfw_coverage) +target_link_libraries(onert_core PRIVATE dl ${LIB_PTHREAD}) +target_link_libraries(onert_core PRIVATE jsoncpp) +target_link_libraries(onert_core PRIVATE ruy) +target_link_libraries(onert_core INTERFACE ruy_instrumentation) + +if(CMAKE_BUILD_TYPE_LC STREQUAL "release") + add_custom_command(TARGET onert_core POST_BUILD + COMMAND ${CMAKE_STRIP} "--strip-unneeded" $<TARGET_FILE_NAME:onert_core>) +endif() + +# NOTE Below line is added to remove warning for android build +# It will be removed after android build uses gold linker +if (ANDROID) + target_link_libraries(onert_core INTERFACE log) +endif (ANDROID) + +if(ENVVAR_ONERT_CONFIG) + target_compile_definitions(onert_core PRIVATE ENVVAR_FOR_DEFAULT_CONFIG) +endif(ENVVAR_ONERT_CONFIG) + +install(TARGETS onert_core LIBRARY DESTINATION lib) +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" + DESTINATION "include/onert" + FILES_MATCHING PATTERN "*.h" PATTERN "*.lst" + ) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +# Unit Tests +set(TEST_ONERT_BACKEND_CPU_COMMON test_onert_backend_cpu_common) + +add_executable(${TEST_ONERT_BACKEND_CPU_COMMON} ${TESTS}) + +target_link_libraries(${TEST_ONERT_BACKEND_CPU_COMMON} onert_core) +target_link_libraries(${TEST_ONERT_BACKEND_CPU_COMMON} gtest gtest_main dl ${LIB_PTHREAD}) + +add_test(${TEST_ONERT_BACKEND_CPU_COMMON} ${TEST_ONERT_BACKEND_CPU_COMMON}) +install(TARGETS ${TEST_ONERT_BACKEND_CPU_COMMON} DESTINATION unittest_standalone) diff --git a/runtime/onert/core/include/backend/Backend.h b/runtime/onert/core/include/backend/Backend.h new file mode 100644 index 000000000..4f6ebbba7 --- /dev/null +++ b/runtime/onert/core/include/backend/Backend.h @@ -0,0 +1,50 @@ +/* + * 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_BACKEND_BACKEND_H__ +#define __ONERT_BACKEND_BACKEND_H__ + +#include <memory> + +#include "ir/Graph.h" +#include "backend/IConfig.h" +#include "backend/BackendContext.h" + +namespace onert +{ +namespace backend +{ + +namespace custom +{ +class IKernelBuilder; +} + +class Backend +{ +public: + virtual ~Backend() = default; + virtual std::shared_ptr<onert::backend::IConfig> config() const = 0; + + virtual std::unique_ptr<BackendContext> + newContext(const ir::Graph &graph, const std::shared_ptr<backend::custom::IKernelBuilder> &kb, + bool is_linear_executor) const = 0; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_BACKEND_H__ diff --git a/runtime/onert/core/include/backend/BackendContext.h b/runtime/onert/core/include/backend/BackendContext.h new file mode 100644 index 000000000..1eba29550 --- /dev/null +++ b/runtime/onert/core/include/backend/BackendContext.h @@ -0,0 +1,92 @@ +/* + * 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_BACKEND_BACKEND_CONTEXT_H__ +#define __ONERT_BACKEND_BACKEND_CONTEXT_H__ + +#include <memory> +#include "ir/Graph.h" + +namespace onert +{ +namespace backend +{ + +class Backend; +class IConstantInitializer; +class IKernelGenerator; +class ITensorRegister; +struct ITensorRegistry; +struct ITensorBuilder; +struct IOptimizer; + +class BackendContext +{ +public: + struct OperationInfo + { + ir::OperationIndex index; + ir::Layout layout; + + OperationInfo(ir::OperationIndex index, ir::Layout layout) : index{index}, layout{layout} {} + }; + +public: + BackendContext(const Backend *backend, const ir::Graph *graph, + std::shared_ptr<ITensorRegistry> tensor_registry = nullptr, + std::shared_ptr<ITensorBuilder> tensor_builder = nullptr, + std::shared_ptr<IConstantInitializer> constant_initializer = nullptr, + std::shared_ptr<IKernelGenerator> kernel_gen = nullptr, + std::shared_ptr<ITensorRegister> tensor_register = nullptr, + std::shared_ptr<IOptimizer> optimizer = nullptr) + : _backend{backend}, _graph{graph}, tensor_registry{tensor_registry}, + tensor_builder{tensor_builder}, constant_initializer{constant_initializer}, + kernel_gen{kernel_gen}, tensor_register{tensor_register}, optimizer{optimizer} + { + } + + virtual ~BackendContext() = default; + + void initialize(const std::vector<OperationInfo> &operation_list, + const std::vector<ir::OperandIndex> &operand_list); + void initConsts(); + + const Backend *backend() const { return _backend; } + const ir::Graph *graph() const { return _graph; } + const std::vector<OperationInfo> &operation_list() { return _operation_list; } + const std::vector<ir::OperandIndex> &operand_list() { return _operand_list; } + +private: + const Backend *_backend{nullptr}; + const ir::Graph *_graph{nullptr}; + std::vector<OperationInfo> _operation_list; + std::vector<ir::OperandIndex> _operand_list; + +public: + std::shared_ptr<ITensorRegistry> tensor_registry; + std::shared_ptr<ITensorBuilder> tensor_builder; + std::shared_ptr<IConstantInitializer> constant_initializer; + std::shared_ptr<IKernelGenerator> kernel_gen; + std::shared_ptr<ITensorRegister> tensor_register; + std::shared_ptr<IOptimizer> optimizer; +}; + +using BackendContexts = std::unordered_map<const Backend *, std::unique_ptr<BackendContext>>; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_BACKEND_CONTEXT_H__ diff --git a/runtime/onert/core/include/backend/CustomKernelBuilder.h b/runtime/onert/core/include/backend/CustomKernelBuilder.h new file mode 100644 index 000000000..cae2fc1a3 --- /dev/null +++ b/runtime/onert/core/include/backend/CustomKernelBuilder.h @@ -0,0 +1,76 @@ +/* + * 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_BACKEND_CUSTOM_KERNEL_BUILDER_H__ +#define __ONERT_BACKEND_CUSTOM_KERNEL_BUILDER_H__ + +#include "backend/IPortableTensor.h" +#include "ir/Shape.h" +#include "ir/DataType.h" + +#include <vector> +#include <memory> + +namespace onert +{ +namespace exec +{ + +class IFunction; + +} // namespace exec +} // namespace onert + +namespace onert +{ +namespace backend +{ +namespace custom +{ + +struct TypeInfo +{ + ir::Shape shape; + ir::DataType dtype; +}; + +struct CustomKernelConfigParams +{ + std::vector<backend::IPortableTensor *> input_tensors; + std::vector<TypeInfo> input_types; + + std::vector<backend::IPortableTensor *> output_tensors; + std::vector<TypeInfo> output_types; + + char *userdata; + size_t userdata_size; +}; + +class IKernelBuilder +{ +public: + virtual ~IKernelBuilder() = default; + virtual std::unique_ptr<exec::IFunction> buildKernel(const std::string &id, + CustomKernelConfigParams &¶ms) const = 0; +}; + +} // namespace custom + +} // namespace backend + +} // namespace onert + +#endif // __ONERT_BACKEND_CUSTOM_KERNEL_BUILDER_H__ diff --git a/runtime/onert/core/include/backend/IConfig.h b/runtime/onert/core/include/backend/IConfig.h new file mode 100644 index 000000000..ef9c5cdb2 --- /dev/null +++ b/runtime/onert/core/include/backend/IConfig.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. + */ + +#ifndef __ONERT_BACKEND_ICONFIG_H__ +#define __ONERT_BACKEND_ICONFIG_H__ + +#include "ir/Layout.h" +#include "ir/Operation.h" +#include "util/ITimer.h" + +#include <memory> +#include <string> + +namespace onert +{ +namespace backend +{ + +struct IConfig +{ + virtual ~IConfig() = default; + /** + * @brief Returns ID of the backend + * + * @return std::string ID of this backend + */ + virtual std::string id() = 0; + /** + * @brief Initialize the backend. This is called as soon as the backend is loaded. + * + * @return true Initialization succeeded + * @return false Initialization failed, so it cannot use this backend + */ + virtual bool initialize() = 0; + /** + * @brief Returns supported layout for the given \p node and \p frontend_layout + * + * @param node Operation + * @param frontend_layout The layout defined in the model + * @return ir::Layout The layout that the backend kernel actually uses + */ + virtual ir::Layout supportLayout(const ir::Operation &node, ir::Layout frontend_layout) = 0; + /** + * @brief The function that is called after each OpSequence run on profiling mode. + * This may be useful for profiling GPU-based or special computing units. + */ + virtual void sync() const {} + /** + * @brief Returns Timer object for this backend. For some computing units, it may need its own + * Timer implementation. + * + * @return std::unique_ptr<util::ITimer> Timer object for this backend + */ + virtual std::unique_ptr<util::ITimer> timer() { return nullptr; } + + virtual bool supportPermutation() = 0; + virtual bool supportDynamicTensor() = 0; + virtual bool supportFP16() = 0; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ICONFIG_H__ diff --git a/runtime/onert/core/include/backend/IConstantInitializer.h b/runtime/onert/core/include/backend/IConstantInitializer.h new file mode 100644 index 000000000..149acecb4 --- /dev/null +++ b/runtime/onert/core/include/backend/IConstantInitializer.h @@ -0,0 +1,230 @@ +/* + * 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_BACKEND_ICONSTANT_INITIALIZER_H__ +#define __ONERT_BACKEND_ICONSTANT_INITIALIZER_H__ + +#include <unordered_map> +#include <functional> + +#include "ITensorBuilder.h" +#include "ir/Coordinates.h" +#include "ir/Layout.h" +#include "ir/Operand.h" +#include "ir/Operands.h" +#include "ir/OperationVisitor.h" +#include "ir/OpSequence.h" +#include "util/logging.h" + +namespace +{ +template <typename T> +static void Init(const onert::ir::Operand &model_obj, onert::backend::ITensor &obj, const bool copy, + const onert::ir::Layout frontend_layout = onert::ir::Layout::UNKNOWN) +{ + const auto shape = model_obj.shape(); + assert(model_obj.data()); + auto base = reinterpret_cast<const T *>(model_obj.data()->base()); + + obj.access([&](::onert::backend::ITensor &tensor) { + switch (shape.rank()) + { + case 0: + { + assert(model_obj.data()->size() == sizeof(T)); + const auto value = *reinterpret_cast<const T *>(base); + T *into = reinterpret_cast<T *>(tensor.buffer()); + *into = value; + break; + } + case 1: + { + auto vec_size = shape.dim(0); + for (int32_t n = 0; n < vec_size; ++n) + { + const T *from = reinterpret_cast<const T *>(base) + n; + const auto value = *from; + + T *into = reinterpret_cast<T *>(tensor.buffer()) + n; + + *into = value; + } + break; + } + case 2: + { + const int32_t copy_len = shape.dim(1); + + for (auto i = 0; i < shape.dim(0); ++i) + { + ::onert::ir::Coordinates coords{i, 0}; + memcpy(tensor.buffer() + tensor.calcOffset(coords), base + i * copy_len, + copy_len * sizeof(T)); + } + break; + } + case 3: + { + const int32_t width = shape.dim(1); + const int32_t copy_len = shape.dim(2); + + for (auto i = 0; i < shape.dim(0); ++i) + { + for (auto j = 0; j < shape.dim(1); ++j) + { + ::onert::ir::Coordinates coords{i, j, 0}; + memcpy(tensor.buffer() + tensor.calcOffset(coords), + base + i * width * copy_len + j * copy_len, copy_len * sizeof(T)); + } + } + break; + } + case 4: + { + const int32_t height = shape.dim(1); + const int32_t width = shape.dim(2); + const int32_t copy_len = 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) + { + if (copy) + { + ::onert::ir::Coordinates coords{i, j, k, 0}; + memcpy(tensor.buffer() + tensor.calcOffset(coords), + base + i * height * width * copy_len + j * width * copy_len + k * copy_len, + copy_len * sizeof(T)); + } + else + { + for (auto l = 0; l < shape.dim(3); ++l) + { + const auto coords = ::onert::ir::convertCoordinates({i, j, k, l}, frontend_layout, + tensor.layout()); + T *into = reinterpret_cast<T *>(tensor.buffer() + tensor.calcOffset(coords)); + T value = *(base + i * height * width * copy_len + j * width * copy_len + + k * copy_len + l); + *into = value; + } + } + } + } + } + break; + } + default: + throw std::runtime_error{"Not yet supported"}; + } + }); +} + +template <typename T> +void copyInit(const onert::ir::Operand &model_obj, onert::backend::ITensor &obj) +{ + Init<T>(model_obj, obj, true); +} + +template <typename T> +void permuteInit(const onert::ir::Operand &model_obj, onert::backend::ITensor &obj, + const onert::ir::Layout frontend_layout) +{ + const bool copy = frontend_layout == obj.layout(); + Init<T>(model_obj, obj, copy, frontend_layout); +} + +} // namespace + +namespace onert +{ +namespace backend +{ + +class IConstantInitializer : public ir::OperationVisitor +{ +public: + virtual ~IConstantInitializer() = default; + +public: + void run() + { + assert(tensor_registry()); + for (const auto &it : _init_map) + { + const auto &ind = it.first; + const auto &fn = it.second; + + const auto &model_obj = _operands.at(ind); + auto tensor_obj = tensor_registry()->getNativeITensor(ind); + assert(tensor_obj != nullptr); + fn(model_obj, *tensor_obj); + VERBOSE(FillOperandData) << "Fill data for operand " << ind.value() << std::endl; + } + _init_map.clear(); + } + +public: + IConstantInitializer(const ir::Operands &operands) + : _operands{operands}, _current_op_seq_layout{ir::Layout::UNKNOWN} + { + } + +public: + using Initializer = std::function<void(const ir::Operand &, backend::ITensor &)>; + + void setLayout(ir::Layout layout) { _current_op_seq_layout = layout; } + +protected: + virtual std::shared_ptr<ITensorRegistry> tensor_registry() const = 0; + +public: + virtual void registerDefaultInitializer(const ir::OperandIndex &index, const ir::Operand &obj) + { + registerPermuteInitializer(index, obj); // as default + } + +public: + void registerCopyInitializer(const ir::OperandIndex &index, const ir::Operand &obj); + void registerPermuteInitializer(const ir::OperandIndex &index, const ir::Operand &obj); + +public: + void registerCustomInitializer(const ir::OperandIndex &index, const ir::Operand &obj, + void (*customInit)(const onert::ir::Operand &model_obj, + onert::backend::ITensor &obj)) + { + // For only CONSTANTS + // TODO Add to check if tensor has been allocated + if (!obj.isConstant()) + return; + + using namespace std::placeholders; + _init_map[index] = std::bind(customInit, _1, _2); + } + +public: + bool exist(const ir::OperandIndex &ind) { return _init_map.find(ind) != _init_map.end(); } + +protected: + const ir::Operands &_operands; + std::unordered_map<ir::OperandIndex, Initializer> _init_map; + ir::Layout _current_op_seq_layout; // TODO Rename this to _current_layout +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ICONSTANT_INITIALIZER_H__ diff --git a/runtime/onert/core/include/backend/IDynamicTensorManager.h b/runtime/onert/core/include/backend/IDynamicTensorManager.h new file mode 100644 index 000000000..67cfda24e --- /dev/null +++ b/runtime/onert/core/include/backend/IDynamicTensorManager.h @@ -0,0 +1,60 @@ +/* + * 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_BACKEND_IDYNAMICTENSOR_MANAGER_H__ +#define __ONERT_BACKEND_IDYNAMICTENSOR_MANAGER_H__ + +#include "ITensorManager.h" + +#include <ir/Index.h> +#include <ir/Operation.h> +#include <ir/Shape.h> +#include <backend/ITensor.h> + +namespace onert +{ +namespace backend +{ + +/** + * @brief Interface as an abstract tensor manager, providing ways to handle memory + * for dynamic tensors. + */ +struct IDynamicTensorManager : public ITensorManager +{ + virtual ~IDynamicTensorManager() = default; + +public: + /** + * @brief Plan when to delete a tensor. Note this planning is done at compilation time. + * @param op_ind operation index + * @param tensor candidate ITensor to dealloc. Tensor can be static + * or dynamic since tensor type may not be clearly known at compilation time. + */ + virtual void planDealloc(ir::OperationIndex op_ind, backend::ITensor *tensor) = 0; + + /** + * @brief Deallocate input tensors of op if an input tensor is a dynamic tensor and it won't + * be used anymore + * @note This will work after calling planDealloc + */ + virtual void deallocInput(ir::OperationIndex op_ind) = 0; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_IDYNAMICTENSOR_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/IExternalContext.h b/runtime/onert/core/include/backend/IExternalContext.h new file mode 100644 index 000000000..88ffb502c --- /dev/null +++ b/runtime/onert/core/include/backend/IExternalContext.h @@ -0,0 +1,34 @@ +/* + * 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_BACKEND_IEXTERNAL_CONTEXT_H__ +#define __ONERT_BACKEND_IEXTERNAL_CONTEXT_H__ + +namespace onert +{ +namespace backend +{ + +struct IExternalContext +{ + virtual ~IExternalContext() = default; + virtual void setMaxNumThreads(int) = 0; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_IEXTERNAL_CONTEXT__ diff --git a/runtime/onert/core/include/backend/IKernelGenerator.h b/runtime/onert/core/include/backend/IKernelGenerator.h new file mode 100644 index 000000000..afc34ec21 --- /dev/null +++ b/runtime/onert/core/include/backend/IKernelGenerator.h @@ -0,0 +1,76 @@ +/* + * 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_BACKEND_IKERNEL_GENERATOR_H__ +#define __ONERT_BACKEND_IKERNEL_GENERATOR_H__ + +#include <assert.h> +#include <memory> +#include <functional> + +#include "ITensorBuilder.h" +#include "ir/OperationVisitor.h" +#include "ir/OpSequence.h" +#include <memory> +#include "exec/FunctionSequence.h" + +namespace onert +{ +namespace backend +{ + +class IKernelGenerator : public ir::OperationVisitor +{ +public: + virtual ~IKernelGenerator() = default; + + std::unique_ptr<exec::IFunction> releaseFunction() + { + assert(_return_fn); + return std::move(_return_fn); + } + + std::unique_ptr<exec::FunctionSequence> generate(const ir::OpSequence &op_seq) + { + op_seq.accept(*this); + return std::move(_return_fn_seq); + } + +protected: + using OperationVisitor::visit; + + void visit(const ir::OpSequence &) override + { + throw std::runtime_error("KernelGenerator: NYI for operation 'OpSequence'"); + } + +#define OP(InternalName) \ + void visit(const ir::operation::InternalName &) override \ + { \ + throw std::runtime_error("KernelGenerator: NYI for operation '" #InternalName "'"); \ + } +#include "ir/Operations.lst" +#undef OP + +protected: + std::unique_ptr<exec::IFunction> _return_fn; + std::unique_ptr<exec::FunctionSequence> _return_fn_seq; // TODO Extract this out +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_IKERNEL_GENERATOR_H__ diff --git a/runtime/onert/core/include/backend/IMemoryManager.h b/runtime/onert/core/include/backend/IMemoryManager.h new file mode 100644 index 000000000..bad2fd51a --- /dev/null +++ b/runtime/onert/core/include/backend/IMemoryManager.h @@ -0,0 +1,49 @@ +/* + * 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_BACKEND_IMEMORY_MANAGER_H__ +#define __ONERT_BACKEND_IMEMORY_MANAGER_H__ + +namespace onert +{ +namespace backend +{ + +struct IMemoryManager +{ + virtual ~IMemoryManager() = default; + + virtual void allocate(void) = 0; + virtual void deallocate(void) = 0; +}; + +} // namespace backend +} // namespace onert + +#include <unordered_set> +#include <memory> + +namespace onert +{ +namespace backend +{ + +using MemoryManagerSet = std::unordered_set<std::unique_ptr<backend::IMemoryManager>>; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_IMEMORY_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/IOptimizer.h b/runtime/onert/core/include/backend/IOptimizer.h new file mode 100644 index 000000000..4844d21b9 --- /dev/null +++ b/runtime/onert/core/include/backend/IOptimizer.h @@ -0,0 +1,51 @@ +/* + * 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_BACKEND_I_OPTIMIZER_H__ +#define __ONERT_BACKEND_I_OPTIMIZER_H__ + +namespace onert +{ +namespace ir +{ +class LoweredGraph; +} +} // namespace onert + +namespace onert +{ +namespace backend +{ + +/** + * @brief Class for backend optimizations. This is an optional class so not all backends must have + * it. + * + */ +struct IOptimizer +{ + virtual ~IOptimizer() = default; + /** + * @brief Run optimization + * + */ + virtual void optimize() = 0; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_I_OPTIMIZER_H__ diff --git a/runtime/onert/core/include/backend/IPortableTensor.h b/runtime/onert/core/include/backend/IPortableTensor.h new file mode 100644 index 000000000..1b1f05fe1 --- /dev/null +++ b/runtime/onert/core/include/backend/IPortableTensor.h @@ -0,0 +1,58 @@ +/* + * 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_BACKEND_I_PORTABLE_TENSOR_H__ +#define __ONERT_BACKEND_I_PORTABLE_TENSOR_H__ + +#include "backend/ITensor.h" +#include "ir/OperandInfo.h" +#include "ir/Sparsity.h" + +namespace onert +{ +namespace backend +{ + +/** + * @brief A tensor class that is portable for other backends + * + * Backends that use derivatives of this interface can reuse each other's tensors without copying. + * Here's criterion to be a portable tensor: + * - it must not have any paddings + * - No special operations on @c access method + * - e.g. CL memory must map/unmap to use it from CPU, the memory so it cannot be portable + */ +class IPortableTensor : public ITensor +{ +public: + IPortableTensor(const ir::OperandInfo &info) : _info(info) {} + + virtual ~IPortableTensor(); + virtual const ir::Sparsity *sparsity() const { return nullptr; } + const ir::OperandInfo &get_info() const { return _info; } + +public: + bool has_padding() const final { return false; } + void access(const std::function<void(ITensor &tensor)> &fn) final { fn(*this); } + +protected: + ir::OperandInfo _info; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_I_PORTABLE_TENSOR_H__ diff --git a/runtime/onert/core/include/backend/IStaticTensorManager.h b/runtime/onert/core/include/backend/IStaticTensorManager.h new file mode 100644 index 000000000..cef1f8a0a --- /dev/null +++ b/runtime/onert/core/include/backend/IStaticTensorManager.h @@ -0,0 +1,35 @@ +/* + * 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_BACKEND_ISTATICTENSOR_MANAGER_H__ +#define __ONERT_BACKEND_ISTATICTENSOR_MANAGER_H__ + +#include "ITensorManager.h" + +namespace onert +{ +namespace backend +{ + +struct IStaticTensorManager : public ITensorManager +{ + virtual ~IStaticTensorManager() = default; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ISTATICTENSOR_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/ITensor.h b/runtime/onert/core/include/backend/ITensor.h new file mode 100644 index 000000000..3fadda1f5 --- /dev/null +++ b/runtime/onert/core/include/backend/ITensor.h @@ -0,0 +1,122 @@ +/* + * 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_BACKEND_OPERAND_I_TENSOR_H__ +#define __ONERT_BACKEND_OPERAND_I_TENSOR_H__ + +#include <cstring> +#include <cstdint> +#include <functional> + +#include "ir/DataType.h" +#include "ir/Layout.h" +#include "ir/Shape.h" +#include "ir/Coordinates.h" +#include "util/Utils.h" + +namespace onert +{ +namespace backend +{ + +struct IDynamicTensorManager; + +class ITensor +{ +public: + virtual ~ITensor() = default; + +public: + virtual uint8_t *buffer() const = 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 ir::Layout layout() const = 0; + virtual ir::DataType data_type() const = 0; + virtual float data_scale() const = 0; + virtual int32_t data_offset() const = 0; + virtual bool has_padding() const = 0; + virtual void access(const std::function<void(ITensor &tensor)> &fn) = 0; + + /** + * @brief Set the shape to @c shape and possibly re-allocate the buffer + * + * If a tensor is dynamic tensor and previously allocated memory exists, + * it will be deallocated. + * If a tensor is static tensor (with previously allocated memory by StaticTensorManager), + * @c buffer() will be overwriten + * + * @param shape tensor's new shape. While allocating memory for this new_shape, + * tensor's shape is set to new_shape + * @return true If applying shape is successful + * @return false If not applying shape is not supported (it throws for other errors) + */ + virtual bool applyShape(const ir::Shape &) { return false; } + + /** + * @brief Return true if the tensor is constant + */ + virtual bool is_constant() const + { + throw std::runtime_error("This backend does not support checking constant"); + } + + /** + * @brief Return true if the tensor needs dynamic allocation, meaning that during compile-time + * the outpus shape cannot be known and the output shape is calculated during + * kernel execution-time. + */ + virtual bool is_dynamic() const = 0; + + /// @brief set this tensor dynamic + virtual void set_dynamic() + { + throw std::runtime_error("This backend does not support dynamic tensor"); + } + + /** + * @brief Set the shape of tenser to new_shape + * @note Higer dimension will be placed on front. + */ + virtual void setShape(const ir::Shape &new_shape) + { + UNUSED_RELEASE(new_shape); + throw std::runtime_error("This backend does not support dynamic setShape"); + } + + /** + * @brief Get ir::Shape of tensor + * @note Higer dimension will be placed on front. + */ + virtual ir::Shape getShape() const; + + virtual bool is_subtensor() const { return false; } + virtual bool needMemoryMap() const { return false; } + virtual void enqueueWriteBuffer(const void *, bool) + { + throw std::runtime_error("This backend does not support enqueueWriteBuffer"); + } + virtual void enqueueReadBuffer(void *, bool) + { + throw std::runtime_error("This backend does not support enqueueReadBuffer"); + } +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_OPERAND_I_TENSOR_H__ diff --git a/runtime/onert/core/include/backend/ITensorBuilder.h b/runtime/onert/core/include/backend/ITensorBuilder.h new file mode 100644 index 000000000..97721cf19 --- /dev/null +++ b/runtime/onert/core/include/backend/ITensorBuilder.h @@ -0,0 +1,108 @@ +/* + * 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_BACKEND_ITENSOR_BUILDER_H__ +#define __ONERT_BACKEND_ITENSOR_BUILDER_H__ + +#include <map> + +#include "ir/Index.h" +#include "ir/OperandInfo.h" +#include "ir/Operation.h" +#include "ir/Layout.h" +#include "ITensor.h" +#include "ITensorManager.h" +#include "ITensorRegistry.h" +#include "IDynamicTensorManager.h" + +namespace onert +{ +namespace backend +{ + +struct ITensorBuilder +{ + using IterateFunction = std::function<void(const ir::OperandIndex &)>; + + virtual ~ITensorBuilder(void) = default; + + /** + * @brief Register tensor information to allocate on backend + * + * @param ind Index + * @param info Info + * @param backend_layout Backend layout + * @param as_const Whether this tensor is constant + */ + virtual void registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info, + ir::Layout backend_layout) = 0; + + /** + * @brief Check if the tensor has been registered with @c registerTensorInfo + * + * @return true If the tensor has been registered + * @return false Otherwise + */ + virtual bool isRegistered(const ir::OperandIndex &) const = 0; + +public: // methods for static tensor allocation + /** + * @brief Let the tensor builder know first use(start of lifetime) of a tensor + * Must be called before calling @c prepare + * Must be run up to once for each tensor before calling @c notifyLastUse + * NOTE: Useful only for static models + */ + virtual void notifyFirstUse(const ir::OperandIndex &) = 0; + /** + * @brief Let the tensor builder know last use(end of lifetime) of a tensor + * Must be run up to once for each tensor after calling @c notifyFirstUse + * NOTE: Useful only for static models + */ + virtual void notifyLastUse(const ir::OperandIndex &) = 0; + /** + * @brief Prepare the tensors + * Before calling this, all the tensors must be registered + */ + virtual void prepare(void) = 0; + /** + * @brief Allocate the tensors + * Before calling this, @c prepare must be called + */ + virtual void allocate() = 0; + /** + * @brief Some actions after functions' @c IFunction::prepare method. + * This is called right after each function's @c IFunction::prepare function has been + * called. + */ + virtual void postFunctionPrepare() = 0; + +public: // methods for dynamic tensor allocation + /** + * @brief Get dynamicTensorManager. If a backend does not support dynamic tensor, exception + * will be thrown. + * + * @return pointer of IDynamicTensorManager object + * + * @note Since it is a pointer, its life time is from the cration of TensorBuilder + * to the end of execution + */ + virtual IDynamicTensorManager *dynamicTensorManager(void) { return nullptr; } +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ITENSOR_BUILDER_H__ diff --git a/runtime/onert/core/include/backend/ITensorManager.h b/runtime/onert/core/include/backend/ITensorManager.h new file mode 100644 index 000000000..4974b6645 --- /dev/null +++ b/runtime/onert/core/include/backend/ITensorManager.h @@ -0,0 +1,52 @@ +/* + * 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_BACKEND_ITENSOR_MANAGER_H__ +#define __ONERT_BACKEND_ITENSOR_MANAGER_H__ + +namespace onert +{ +namespace backend +{ + +// NOTE This name ITensorManager has been discussed whether or not the name is proper. +// Anyone can argue with any better name. +/** + * @brief Interface as an abstract tensor manager which has MemoryManager + * This is used as a base class for IStaticTensorManager and IDynamicTensorManager + */ +struct ITensorManager +{ + virtual ~ITensorManager() = default; +}; + +} // namespace backend +} // namespace onert + +#include <unordered_set> +#include <memory> + +namespace onert +{ +namespace backend +{ + +using TensorManagerSet = std::unordered_set<std::unique_ptr<backend::ITensorManager>>; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ITENSOR_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/ITensorRegister.h b/runtime/onert/core/include/backend/ITensorRegister.h new file mode 100644 index 000000000..b8e521ce3 --- /dev/null +++ b/runtime/onert/core/include/backend/ITensorRegister.h @@ -0,0 +1,97 @@ +/* + * 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_BACKEND_ITENSOR_REGISTER_H__ +#define __ONERT_BACKEND_ITENSOR_REGISTER_H__ + +#include "ir/LowerInfoMap.h" +#include "ITensorBuilder.h" +#include "ir/Layout.h" +#include "ir/OperandIndexSequence.h" +#include "ir/OperandInfo.h" +#include "ir/Operands.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace backend +{ + +class ITensorRegister : public ir::OperationVisitor +{ +public: + virtual ~ITensorRegister() = default; + +public: + void registerTensors(const ir::OpSequence &op_seq, const ir::LowerInfoMap *lower_info_map) + { + _current_op_seq_layout = op_seq.getLayout(); + _lower_info_map = lower_info_map; + assert(_lower_info_map != nullptr); + assert(tensor_builder().get() != nullptr); + op_seq.accept(*this); + } + +protected: + virtual const ir::Operands &operands() const = 0; + virtual std::shared_ptr<ITensorBuilder> tensor_builder() const = 0; + +protected: +#define OP(InternalName) \ + void visit(const ir::operation::InternalName &node) override \ + { \ + for (const auto &ind : (node.getInputs() | ir::Remove::UNDEFINED) + node.getOutputs()) \ + { \ + defaultRegisterTensorInfo(ind); \ + } \ + } +#include "ir/Operations.lst" +#undef OP + +protected: + void defaultRegisterTensorInfo(const ir::OperandIndex &index) const + { + if (tensor_builder()->isRegistered(index)) + { + return; + } + + const auto &obj = operands().at(index); + const auto frontend_layout = frontendLayout(); + const auto backend_layout = backendLayout(index); + ir::OperandInfo backend_info{permuteShape(obj.shape(), frontend_layout, backend_layout), + obj.typeInfo(), obj.info().memAllocType(), obj.isConstant()}; + tensor_builder()->registerTensorInfo(index, backend_info, backend_layout); + } + +protected: + ir::Layout frontendLayout() const { return _current_op_seq_layout; } + ir::Layout backendLayout(const ir::OperandIndex &index) const + { + assert(_lower_info_map != nullptr); + const auto lower_info = _lower_info_map->operand.at(index).get(); + return lower_info->def_factors().getOnlyElement().layout(); + } + +private: + ir::Layout _current_op_seq_layout; + const ir::LowerInfoMap *_lower_info_map{nullptr}; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ITENSOR_REGISTER_H__ diff --git a/runtime/onert/core/include/backend/ITensorRegistry.h b/runtime/onert/core/include/backend/ITensorRegistry.h new file mode 100644 index 000000000..b256a1fb8 --- /dev/null +++ b/runtime/onert/core/include/backend/ITensorRegistry.h @@ -0,0 +1,146 @@ +/* + * 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_BACKEND_ITENSOR_REGISTRY__ +#define __ONERT_BACKEND_ITENSOR_REGISTRY__ + +#include <memory> + +#include "ir/Index.h" +#include "backend/ITensor.h" +#include "backend/IPortableTensor.h" + +namespace onert +{ +namespace backend +{ + +struct ITensorRegistry +{ + /** + * @brief Deconstruct itself + */ + virtual ~ITensorRegistry() = default; + + /** + * @brief Returns pointer of ITensor among native and migrant tensors + * + * Native Tensor is a tensor that is managed by this backend + * Migrant Tensor is a tensor that is imported from another backend + * + * @note Return tensor cannot be used longer than dynamic tensor manager + */ + virtual ITensor *getITensor(const ir::OperandIndex &) = 0; + /** + * @brief Returns pointer of ITensor among native tensors + * + * Unlike @c getITensor , this function only searches from native tensors + * + * @note Returned tensor cannot be used longer than dynamic tensor manager + */ + virtual ITensor *getNativeITensor(const ir::OperandIndex &) = 0; + /** + * @brief Set the Migrant Tensor which are from other backends + * + * @return true if supported + * @return false if not supported + */ + virtual bool setMigrantTensor(const ir::OperandIndex &, IPortableTensor *) { return false; } +}; + +} // namespace backend +} // namespace onert + +#include "ir/OperandIndexMap.h" + +namespace onert +{ +namespace backend +{ + +/** + * @brief TensorRegistry template class for the convenience of backend implementations + * + * If a backend uses @c IPortableTensor , and there is no special reason to implement @c + * ITensorRegistry on your own, you may just use this default implementation. + * + * @tparam T_Tensor Tensor type. Must be a subclass of @c onert::backend::IPortableTensor . + */ +template <typename T_Tensor> class PortableTensorRegistryTemplate : public ITensorRegistry +{ +public: + ITensor *getITensor(const ir::OperandIndex &ind) override + { + static_assert(std::is_base_of<ITensor, T_Tensor>::value, "T_Tensor must derive from ITensor."); + auto _migrant_tensor = _migrant.find(ind); + if (_migrant_tensor != _migrant.end()) + return _migrant_tensor->second; + return getNativeTensor(ind); + } + + ITensor *getNativeITensor(const ir::OperandIndex &ind) override { return getNativeTensor(ind); } + + IPortableTensor *getPortableTensor(const ir::OperandIndex &ind) + { + auto _migrant_tensor = _migrant.find(ind); + if (_migrant_tensor != _migrant.end()) + { + if (_migrant_tensor->second) + return _migrant_tensor->second; + } + return getNativeTensor(ind); + } + + T_Tensor *getNativeTensor(const ir::OperandIndex &ind) + { + auto tensor = _native.find(ind); + if (tensor != _native.end()) + return tensor->second.get(); + return nullptr; + } + + bool setMigrantTensor(const ir::OperandIndex &ind, IPortableTensor *tensor) override + { + assert(tensor != nullptr); + auto itr = _native.find(ind); + if (itr != _native.end()) + throw std::runtime_error{"Tried to set a migrant tensor but a native tensor already exists."}; + _migrant[ind] = tensor; + return true; + } + + void setNativeTensor(const ir::OperandIndex &ind, std::unique_ptr<T_Tensor> &&tensor) + { + assert(tensor != nullptr); + auto itr = _migrant.find(ind); + if (itr != _migrant.end()) + throw std::runtime_error{"Tried to set a native tensor but a migrant tensor already exists."}; + _native[ind] = std::move(tensor); + } + + const ir::OperandIndexMap<std::unique_ptr<T_Tensor>> &native_tensors() { return _native; } + + const ir::OperandIndexMap<IPortableTensor *> &migrant_tensors() { return _migrant; } + +private: + ir::OperandIndexMap<IPortableTensor *> _migrant; + ir::OperandIndexMap<std::unique_ptr<T_Tensor>> _native; +}; + +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_ITENSOR_REGISTRY__ diff --git a/runtime/onert/core/include/backend/cpu_common/Allocator.h b/runtime/onert/core/include/backend/cpu_common/Allocator.h new file mode 100644 index 000000000..fa67fc7c4 --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/Allocator.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +/** + * @file       Allocator.h + * @brief      This file contains Allocator related classes + */ + +#ifndef __ONERT_BACKEND_CPU_COMMON_ALLOCATOR_H__ +#define __ONERT_BACKEND_CPU_COMMON_ALLOCATOR_H__ + +#include <memory> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +/** + * @brief Class to allocate memory + */ +class Allocator +{ +public: + Allocator(uint32_t capacity); + /** + * @brief Get memory base pointer + * @return base pointer + */ + uint8_t *base() const { return _base.get(); } + void release() { _base.reset(); } + +private: + std::unique_ptr<uint8_t[]> _base; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_ALLOCATOR_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/DynamicTensorManager.h b/runtime/onert/core/include/backend/cpu_common/DynamicTensorManager.h new file mode 100644 index 000000000..c4e06aa82 --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/DynamicTensorManager.h @@ -0,0 +1,76 @@ +/* + * 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_BACKEND_CPU_COMMON_DYNAMICTENSOR_MANAGER_H__ +#define __ONERT_BACKEND_CPU_COMMON_DYNAMICTENSOR_MANAGER_H__ + +#include "MemoryManager.h" +#include "TensorRegistry.h" + +#include <backend/IDynamicTensorManager.h> +#include <ir/OperandInfo.h> +#include <ir/Operation.h> +#include <ir/Index.h> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +// TODO Find optimized algorithm to manage memory. + +/** + * @brief Class to manage dynamic tensor and its memory + */ +class DynamicTensorManager : public backend::IDynamicTensorManager +{ +public: + DynamicTensorManager(const std::shared_ptr<TensorRegistry> ®); + + virtual ~DynamicTensorManager() = default; + + void buildTensor(const ir::OperandIndex &ind, const ir::OperandInfo &tensor_info, + ir::Layout backend_layout); + + void planDealloc(ir::OperationIndex op_ind, backend::ITensor *tensor) override; + void deallocInput(ir::OperationIndex op_ind) override; + + std::shared_ptr<DynamicMemoryManager> dynamic_mem_mgr() { return _dynamic_mem_mgr; } + +private: + const ITensor *getRawITensor(ir::OperandIndex ind); + +private: + /** + * @brief Memory manager for dynamic tensor. + * @todo DynamicMemoryManager is not optimized. Optimized one is needed + */ + std::shared_ptr<DynamicMemoryManager> _dynamic_mem_mgr; + const std::shared_ptr<TensorRegistry> _tensors; + + // contains list of dynamic tensor index, which can be deallocated after running operation + // note: this map could contain static tensor index too. Careful use is required. + std::unordered_map<ir::OperationIndex, std::unordered_set<backend::ITensor *>> + _dealloc_tensor_map; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_DYNAMICTENSOR_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/IMemoryPlanner.h b/runtime/onert/core/include/backend/cpu_common/IMemoryPlanner.h new file mode 100644 index 000000000..335f8f5c0 --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/IMemoryPlanner.h @@ -0,0 +1,74 @@ +/* + * 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_BACKEND_IMEMORY_PLANNER_H__ +#define __ONERT_BACKEND_IMEMORY_PLANNER_H__ + +#include "ir/OperandIndexMap.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +/** + * @brief Structure to have memory offset and size + */ +struct Block +{ + uint32_t offset; + size_t size; +}; + +/** + * @brief Interface to plan memory + */ +struct IMemoryPlanner +{ + using MemoryPlans = ir::OperandIndexMap<Block>; + + /** + * @brief Claim memory for operand + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + virtual void claim(const ir::OperandIndex &, size_t) = 0; + /** + * @brief Release memory for operand + * @param[in] index The operand index + */ + virtual void release(const ir::OperandIndex &) = 0; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + virtual uint32_t capacity() = 0; + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + virtual MemoryPlans &memory_plans() = 0; + + virtual ~IMemoryPlanner() = default; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_IMEMORY_PLANNER_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/MemoryManager.h b/runtime/onert/core/include/backend/cpu_common/MemoryManager.h new file mode 100644 index 000000000..28ec6b803 --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/MemoryManager.h @@ -0,0 +1,76 @@ +/* + * 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_BACKEND_CPU_MEMORY_MANAGER_H__ +#define __ONERT_BACKEND_CPU_MEMORY_MANAGER_H__ + +#include "Allocator.h" +#include "backend/IMemoryManager.h" +#include "IMemoryPlanner.h" + +namespace onert +{ +namespace backend +{ + +class ITensor; + +namespace cpu_common +{ + +class MemoryManager : public backend::IMemoryManager +{ +public: + MemoryManager(); + MemoryManager(const std::string); + virtual ~MemoryManager() = default; + + void allocate(void) override; + uint8_t *getBuffer(const ir::OperandIndex &ind) const; + void deallocate(void) override { _mem_alloc->release(); } + + void claimPlan(const ir::OperandIndex &ind, uint32_t size); + void releasePlan(const ir::OperandIndex &ind); + +private: + IMemoryPlanner *createMemoryPlanner(); + IMemoryPlanner *createMemoryPlanner(const std::string); + +private: + ir::OperandIndexMap<Block> _tensor_mem_map; + std::shared_ptr<IMemoryPlanner> _mem_planner; + std::shared_ptr<Allocator> _mem_alloc; +}; + +class DynamicMemoryManager +{ +public: + DynamicMemoryManager() = default; + virtual ~DynamicMemoryManager() = default; + + std::shared_ptr<Allocator> allocate(const ITensor *tensor, uint32_t capacity); + void deallocate(const ITensor *tensor); + void deallocate(void); + +private: + std::unordered_map<const ITensor *, std::shared_ptr<Allocator>> _mem_alloc_map; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_MEMORY_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/StaticTensorManager.h b/runtime/onert/core/include/backend/cpu_common/StaticTensorManager.h new file mode 100644 index 000000000..fa50b551e --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/StaticTensorManager.h @@ -0,0 +1,68 @@ +/* + * 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_BACKEND_CPU_COMMON_STATICTENSOR_MANAGER_H__ +#define __ONERT_BACKEND_CPU_COMMON_STATICTENSOR_MANAGER_H__ + +#include "MemoryManager.h" + +#include "backend/IStaticTensorManager.h" +#include "ir/OperandIndexMap.h" +#include "ir/OperandInfo.h" +#include "TensorRegistry.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +class DynamicTensorManager; + +class StaticTensorManager : public backend::IStaticTensorManager +{ +public: + StaticTensorManager(const std::shared_ptr<TensorRegistry> ®, + DynamicMemoryManager *dynamic_mem_mgr); + virtual ~StaticTensorManager() = default; + + void allocateConsts(void); + void allocateNonconsts(void); + void deallocateConsts(void); + void deallocateNonconsts(void); + + void buildTensor(const ir::OperandIndex &ind, const ir::OperandInfo &tensor_info, + ir::Layout backend_layout, bool as_const); + + void claimPlan(const ir::OperandIndex &ind, uint32_t size); + void releasePlan(const ir::OperandIndex &ind); + + void iterate(const std::function<void(const ir::OperandIndex &)> &fn); + +private: + std::unique_ptr<DynamicMemoryManager> _const_mgr; + std::unique_ptr<MemoryManager> _nonconst_mgr; + const std::shared_ptr<TensorRegistry> _tensors; + ir::OperandIndexMap<bool> _as_constants; + DynamicMemoryManager *_dynamic_mem_mgr; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_STATICTENSOR_MANAGER_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/Tensor.h b/runtime/onert/core/include/backend/cpu_common/Tensor.h new file mode 100644 index 000000000..5fa20e15d --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/Tensor.h @@ -0,0 +1,184 @@ +/* + * 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_BACKEND_CPU_COMMON_TENSOR_H__ +#define __ONERT_BACKEND_CPU_COMMON_TENSOR_H__ + +#include "Allocator.h" + +#include <backend/IPortableTensor.h> +#include <ir/OperandInfo.h> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +class DynamicMemoryManager; + +class Tensor : public IPortableTensor +{ +public: + Tensor() = delete; + virtual ~Tensor(); + +public: + Tensor(const ir::OperandInfo &info, const ir::Layout layout, + DynamicMemoryManager *dynamic_mem_mgr) + : IPortableTensor(info), _layout(layout), _buffer(nullptr), _num_references(0), + _dynamic_mem_mgr(dynamic_mem_mgr), _allocator(nullptr) + { + // DO NOTHING + } + +public: + // Only one of two method 'setBuffer' must be called once + + /** + * @brief Set the Buffer object. This method is called for static and non-const tensor + */ + void setBuffer(uint8_t *buffer) + { + assert(_buffer == nullptr); + _buffer = buffer; + } + + /** + * @brief Set the Buffer object. This method is called for dynamic or const tensor + */ + void setBuffer(const std::shared_ptr<Allocator> &alloc) + { + assert(_buffer == nullptr); + _allocator = alloc; + _buffer = alloc->base(); + } + + // This works just as setBuffer but it simply overwrite existing Allocator without nullptr check + void overwriteBuffer(const std::shared_ptr<Allocator> &alloc) + { + _allocator = alloc; + _buffer = alloc->base(); + } + + /** + * @brief Mark this tensor does not have memory. + * Real memory deallocation should be done by caller. + */ + void resetBuffer() + { + _allocator.reset(); + _buffer = nullptr; + } + +public: + uint8_t *buffer() const override { return _buffer; } + /** + * @brief Get dimension by index + * + * @param index Index to get diemension + * @return size_t Dimension at index + * @note N : dimension(0) + * H : dimension(1) + * W : dimension(2) + * C : dimension(3) + */ + size_t dimension(size_t index) const final override { return _info.shape().dim(index); } + size_t num_dimensions() const override { return _info.shape().rank(); } + size_t total_size() const override { return _info.total_size(); } + size_t calcOffset(const ir::Coordinates &coords) const override; + ir::Layout layout() const override { return _layout; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + float data_scale() const override { return _info.typeInfo().scale(); } + int32_t data_offset() const override { return _info.typeInfo().offset(); } + bool is_constant() const override { return _info.isConstant(); } + bool is_dynamic() const override { return _info.isDynamic(); } + void set_dynamic() override { _info.setDynamic(); } + bool applyShape(const ir::Shape &new_shape) override; + const ir::Sparsity *sparsity() const override { return _info.typeInfo().sparsity(); } + + virtual void increase_ref() + { + assert(is_dynamic() || + // when not dynamic + (_buffer != nullptr)); + + ++_num_references; + } + + virtual void decrease_ref() + { + assert(_buffer != nullptr || _allocator != nullptr); + assert(_num_references > 0); + --_num_references; + // constant tensor and dynamic tensor has _allocator + if (_num_references == 0) + { + if (_buffer != nullptr) + _buffer = nullptr; + if (_allocator != nullptr) + { + _allocator->release(); + _allocator = nullptr; + } + } + } + + /** + * @brief Reset reference count to zero and release data + */ + virtual void reset_ref() + { + assert(_buffer != nullptr || _allocator != nullptr); + assert(_num_references > 0); + _num_references = 0; + + // Only constant tensor has allocator pointer + if (_buffer != nullptr) + _buffer = nullptr; + else + { + _allocator->release(); + _allocator = nullptr; + } + } + + virtual int32_t num_references() { return _num_references; } + + void setShape(const ir::Shape &new_shape) override; + +protected: + ir::Layout _layout; + uint8_t *_buffer; + int32_t _num_references; + DynamicMemoryManager *_dynamic_mem_mgr; + +private: + /** + * @brief Memory allocator for dynamic tensor and const tensor + * Since maintaing _allocator and also _buffer makes confusion, + * we will mainly use _buffer (not _allocator.base()) for memory pointer in this code. + * _allocator(shared_ptr) is used to guarantee that we have valid _buffer. + */ + std::shared_ptr<Allocator> _allocator; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_TENSOR_H__ diff --git a/runtime/onert/core/include/backend/cpu_common/TensorRegistry.h b/runtime/onert/core/include/backend/cpu_common/TensorRegistry.h new file mode 100644 index 000000000..5896fb7ad --- /dev/null +++ b/runtime/onert/core/include/backend/cpu_common/TensorRegistry.h @@ -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. + */ + +#ifndef __ONERT_BACKEND_CPU_COMMON_TENSOR_REGISTRY__ +#define __ONERT_BACKEND_CPU_COMMON_TENSOR_REGISTRY__ + +#include "backend/ITensorRegistry.h" +#include "Tensor.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +using TensorRegistry = PortableTensorRegistryTemplate<cpu_common::Tensor>; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_TENSOR_REGISTRY__ diff --git a/runtime/onert/core/include/compiler/BackendManager.h b/runtime/onert/core/include/compiler/BackendManager.h new file mode 100644 index 000000000..af13d13f7 --- /dev/null +++ b/runtime/onert/core/include/compiler/BackendManager.h @@ -0,0 +1,82 @@ +/* + * 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_BACKEND_MANAGER_H__ +#define __ONERT_COMPILER_BACKEND_MANAGER_H__ + +#include <memory> +#include <map> + +#include "ir/Operands.h" +#include "backend/Backend.h" +#include "backend/controlflow/Backend.h" + +namespace onert +{ +namespace compiler +{ + +class BackendManager +{ +public: + using backend_create_t = backend::Backend *(*)(); + using backend_destroy_t = void (*)(backend::Backend *); + using dlhandle_destroy_t = void (*)(void *); + + static BackendManager &get(); + +public: + backend::Backend *get(const std::string &key); + const backend::Backend *get(const std::string &key) const; + const backend::controlflow::Backend *getControlflow() const; + const std::vector<const backend::Backend *> getAll() const + { + std::vector<const backend::Backend *> v; + for (const auto &p : _gen_map) + v.emplace_back(p.second.get()); + return v; + } + size_t num_backends() const { return _gen_map.size(); } + /** + * @brief load backend plugin + * + * @param backend backend to be loaded + * + * @return + */ + void loadBackend(const std::string &backend); + +private: + BackendManager(); + +private: + std::map<std::string, std::unique_ptr<void, dlhandle_destroy_t>> _handle_map; + std::map<std::string, std::unique_ptr<backend::Backend, backend_destroy_t>> _gen_map; + backend::controlflow::Backend *_controlflow{nullptr}; + /** + * @brief load controlflow backend + * + * @param backend backend to be loaded + * + * @return + */ + void loadControlflowBackend(); +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_BACKEND_MANAGER_H__ diff --git a/runtime/onert/core/include/compiler/BackendResolver.h b/runtime/onert/core/include/compiler/BackendResolver.h new file mode 100644 index 000000000..a316b4335 --- /dev/null +++ b/runtime/onert/core/include/compiler/BackendResolver.h @@ -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. + */ + +#ifndef __ONERT_COMPILER_BACKEND_RESOLVER_H__ +#define __ONERT_COMPILER_BACKEND_RESOLVER_H__ + +#include <unordered_map> +#include <typeindex> + +#include "backend/Backend.h" +#include "ir/OperationIndexMap.h" + +namespace onert +{ +namespace compiler +{ + +class BackendResolver +{ +public: + const backend::Backend *getBackend(const ir::OperationIndex &index) const + { + return _gen_map.at(index); + } + + void setBackend(const ir::OperationIndex &index, const backend::Backend *backend) + { + _gen_map[index] = backend; + } + + void + iterate(const std::function<void(const ir::OperationIndex &, const backend::Backend &)> &fn) const + { + for (const auto &e : _gen_map) + { + fn(e.first, *e.second); + } + } + +private: + ir::OperationIndexMap<const backend::Backend *> _gen_map; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_BACKEND_RESOLVER_H__ diff --git a/runtime/onert/core/include/compiler/CodeMap.h b/runtime/onert/core/include/compiler/CodeMap.h new file mode 100644 index 000000000..e13d3334c --- /dev/null +++ b/runtime/onert/core/include/compiler/CodeMap.h @@ -0,0 +1,45 @@ +/* + * 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_CODE_MAP_H__ +#define __ONERT_COMPILER_CODE_MAP_H__ + +#include <unordered_map> + +namespace onert +{ +namespace compiler +{ + +struct CodeAndInfo +{ + const ir::OpSequence *op_seq; + const ir::operation::LowerInfo *lower_info; + std::unique_ptr<exec::FunctionSequence> fn_seq; + + CodeAndInfo(const ir::OpSequence *op_seq, const ir::operation::LowerInfo *lower_info, + std::unique_ptr<exec::FunctionSequence> &&fn_seq) + : op_seq{op_seq}, lower_info{lower_info}, fn_seq{std::move(fn_seq)} + { + } +}; + +using CodeMap = std::unordered_map<ir::OpSequenceIndex, CodeAndInfo>; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_CODE_MAP_H__ diff --git a/runtime/onert/core/include/compiler/Compiler.h b/runtime/onert/core/include/compiler/Compiler.h new file mode 100644 index 000000000..3098be7ba --- /dev/null +++ b/runtime/onert/core/include/compiler/Compiler.h @@ -0,0 +1,122 @@ +/* + * 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 Compiler.h + * @brief This file contains Compiler class to define and run compilation phase + */ + +#ifndef __ONERT_COMPILER_COMPILE_H_ +#define __ONERT_COMPILER_COMPILE_H_ + +#include "ir/Graph.h" +#include "exec/IExecutor.h" + +namespace onert +{ + +namespace compiler +{ + +enum class State +{ + CREATED, // Before compilation + COMPILED // Success compilation +}; + +struct ManualSchedulerOptions +{ + std::string backend_for_all; + std::unordered_map<ir::OpCode, std::string> opcode_to_backend; + std::unordered_map<ir::OperationIndex, std::string> index_to_backend; +}; + +struct CompilerOptions +{ + // GENERAL OPTIONS + std::vector<std::string> backend_list; + bool is_primary_subgraph; // TODO Remove this out of this struct as it is not user-given option + + // OPTIONS ONLY FOR DEBUGGING/PROFILING + std::string trace_filepath; //< File path to save trace records + int graph_dump_level; //< Graph dump level, values between 0 and 2 are valid + int op_seq_max_node; //< Number of nodes that can be + std::string executor; //< Executor name to use + ManualSchedulerOptions manual_scheduler_options; //< Options for ManualScheduler + bool he_scheduler; //< HEScheduler if true, ManualScheduler otherwise + bool he_profiling_mode; //< Whether HEScheduler profiling mode ON/OFF + bool disable_compile; //< Run with Interpreter if true, try compilation otherwise + bool fp16_enable; //< Whether fp16 mode ON/OFF +}; + +CompilerOptions fetchCompilerOptionsFromGlobalConfig(const ir::Subgraphs &subgs); + +/** + * @brief Class to compile graph model + */ +class Compiler +{ +public: + /** + * @brief Construct a new Compiler object + * @param[in] subgs All subgraphs of a model + */ + Compiler(const std::shared_ptr<ir::Subgraphs> &subgs); + +public: + /** + * @brief Do compilation with the options + * + * @return std::shared_ptr<exec::ExecutorMap> Executors as a result of compilation + */ + std::shared_ptr<exec::ExecutorMap> compile(void); + + State state(void) const { return _state; } + + /** + * @brief Check if model can compile + * @return @c true if model can compile, otherwise @c false + * @note This method don't check model correctness,\n + * so model verification should be done before calling this method + */ + bool checkCompilable(); + CompilerOptions &options() { return _options; } + + /** + * @brief Allow to compute float32 using float16 data type + */ + void enableToFp16(); + +private: + void checkProfilerConditions(); + std::shared_ptr<ir::Graph> &primary_subgraph() { return _subgraphs->at(ir::SubgraphIndex{0}); } + +private: + std::shared_ptr<ir::Subgraphs> _subgraphs; + // NOTE These executors does not have duplicated subgraph. This mean they do not allow support + // subgraphs being called recursively because data of non-constant tensor of parent executor will + // be updated by child executor. If you want to support subgraphs being called recursively, you + // have to add allocate non-constant tensor memory of executors in execution time when each + // subgraph is called. + State _state; + CompilerOptions _options; +}; + +} // namespace compiler + +} // namespace onert + +#endif // __ONERT_COMPILER_COMPILE_H_ diff --git a/runtime/onert/core/include/compiler/ExecutionBuilder.h b/runtime/onert/core/include/compiler/ExecutionBuilder.h new file mode 100644 index 000000000..d54d9d046 --- /dev/null +++ b/runtime/onert/core/include/compiler/ExecutionBuilder.h @@ -0,0 +1,49 @@ +/* + * 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_EXECUTION_BUILDER_H__ +#define __ONERT_COMPILER_EXECUTION_BUILDER_H__ + +#include <memory> + +#include "ir/operation/LowerInfo.h" +#include "ir/OpSequence.h" +#include "exec/FunctionSequence.h" +#include "CodeMap.h" + +namespace onert +{ +namespace compiler +{ + +class ExecutionBuilder +{ +public: + void append(const ir::OpSequenceIndex index, CodeAndInfo &&code_and_info) + { + _code_map.emplace(index, std::move(code_and_info)); + } + + CodeMap releaseCodeMap() { return std::move(_code_map); } + +private: + CodeMap _code_map; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_EXECUTION_BUILDER_H__ diff --git a/runtime/onert/core/include/compiler/LoweredGraph.h b/runtime/onert/core/include/compiler/LoweredGraph.h new file mode 100644 index 000000000..aadba6857 --- /dev/null +++ b/runtime/onert/core/include/compiler/LoweredGraph.h @@ -0,0 +1,90 @@ +/* + * 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_LOWERED_GRAPH_H__ +#define __ONERT_IR_LOWERED_GRAPH_H__ + +#include "ir/Graph.h" +#include "ir/LowerInfoMap.h" +#include "ir/OpSequences.h" +#include "compiler/BackendResolver.h" +#include "compiler/Compiler.h" + +namespace onert +{ +namespace compiler +{ + +/** + * @brief Class that contains lowering information on graph. + * In addition, after lowering, operands in graph will be set to "dynamic" + * if the shape of output of an operation cannot be decided at compilation time. + */ +class LoweredGraph +{ +public: + LoweredGraph(const ir::Graph &graph, const compiler::CompilerOptions &options); + + ir::Graph &graph() { return _graph; } + const ir::Graph &graph() const { return _graph; } + const ir::LowerInfoMap *getLowerInfo() const { return &_lower_info_map; } + const ir::operation::LowerInfo *getLowerInfo(const ir::OpSequenceIndex &op_seq_index) const; + void setLowerInfo(const ir::OpSequenceIndex &op_seq_index, + std::unique_ptr<ir::operation::LowerInfo> &&lower_info); + void removeLowerInfo(const ir::OpSequenceIndex &op_seq_index); + const ir::operand::LowerInfo *getLowerInfo(const ir::OperandIndex &index) const; + ir::operand::LowerInfo *getLowerInfo(const ir::OperandIndex &index); + void setLowerInfo(const ir::OperandIndex &index, + std::unique_ptr<ir::operand::LowerInfo> &&lower_info); + void removeLowerInfo(const ir::OperandIndex &index); + ir::OpSequences &op_seqs() { return _op_seqs; } + const ir::OpSequences &op_seqs() const { return _op_seqs; } + void iterateTopolOpSeqs( + const std::function<void(const ir::OpSequenceIndex &, const ir::OpSequence &)> &fn) const; + void + iterateTopolOpSeqs(const std::function<void(const ir::OpSequenceIndex &, ir::OpSequence &)> &fn); + const backend::BackendContexts &backend_contexts() { return _backend_contexts; } + const backend::BackendContexts &backend_contexts() const { return _backend_contexts; } + std::shared_ptr<ir::OperationIndexMap<int64_t>> indexed_ranks() { return _indexed_ranks; } + +private: + void + makeOpSequences(ir::OperandIndexMap<std::unique_ptr<ir::operand::LowerInfo>> &operands_lower_info, + const compiler::CompilerOptions &options, + const compiler::BackendResolver &backend_resolver); + + void manipulateLowerInfo( + ir::OperandIndexMap<std::unique_ptr<ir::operand::LowerInfo>> &operands_lower_info, + bool is_primary); + void dumpLowerInfo(); + bool mergeable(const ir::OpSequenceIndex &op_seq_index, const ir::OperationIndex &node_index, + ir::Layout layout, const compiler::BackendResolver &backend_resolver); + ir::OpSequenceIndex appendFreshSingleOpSequence(const ir::OperationIndex &node_index, + const ir::Operation &node); + +private: + ir::Graph _graph; + backend::BackendContexts _backend_contexts; + std::shared_ptr<ir::OperationIndexMap<int64_t>> _indexed_ranks; + ir::LowerInfoMap _lower_info_map; + // Pass(for Perm) can accept only graph so that Graph has OpSequences as a member + ir::OpSequences _op_seqs; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_IR_LOWERED_GRAPH_H__ diff --git a/runtime/onert/core/include/compiler/StaticShapeInferer.h b/runtime/onert/core/include/compiler/StaticShapeInferer.h new file mode 100644 index 000000000..05f2679fc --- /dev/null +++ b/runtime/onert/core/include/compiler/StaticShapeInferer.h @@ -0,0 +1,141 @@ +/* + * 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_STATIC_SHAPE_INFERER_H__ +#define __ONERT_COMPILER_STATIC_SHAPE_INFERER_H__ + +#include "ir/OperationVisitor.h" +#include "ir/OpSequence.h" +#include "compiler/LoweredGraph.h" +#include "ir/Index.h" + +#include <memory> +#include <unordered_map> + +namespace onert +{ +namespace compiler +{ + +/** + * @brief Class to infer shape before running kernels. It does the following: + * - re-calculate and set output shape at compile time (before running kernels) + * - if calculation cannot be done at compile time, mark the outputs to be dynamic, meaning + * shapes of outputs will be calculated during running kernels + */ +class StaticShapeInferer : public ir::OperationVisitor +{ +public: + StaticShapeInferer( + const ir::SubgraphIndex &subg_idx, + const std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>> + &lowered_subgs) + : _lowered_subgs(lowered_subgs), _operands(lowered_subgs.at(subg_idx)->graph().operands()), + _operations(lowered_subgs.at(subg_idx)->graph().operations()), + _return_has_dynamic_tensor(false) + { /* empty */ + } + virtual ~StaticShapeInferer() = default; + +public: + /** + * @brief Infer shape of operands beloning to ops and set the output shape. + * If output shape cannot be known without running op, mark it so that it can be allocated + * when running kernel. + * @param op_seq sequence of operations + * @return @c true if op_seq's input or output has any dynamic tensor; @c false otherwise. + */ + bool infer(const ir::OpSequence &op_seq); + + void dump(); + +private: + bool checkDynamicInput(const ir::Operation &op); + void setDynamicOutput(const ir::Operation &op); + +private: + // TODO Define visitors for operations. List them in alphabetic order. + void visit(const ir::operation::ArgMax &op) override; + void visit(const ir::operation::BatchMatMul &op) override; + void visit(const ir::operation::BCQFullyConnected &op) override; + void visit(const ir::operation::BCQGather &op) override; + void visit(const ir::operation::BinaryArithmetic &op) override; + void visit(const ir::operation::BroadcastTo &op) override; + void visit(const ir::operation::Comparison &op) override; + void visit(const ir::operation::Concat &op) override; + void visit(const ir::operation::Conv2D &op) override; + void visit(const ir::operation::ElementwiseActivation &op) override; + void visit(const ir::operation::ElementwiseBinary &op) override; + void visit(const ir::operation::ElementwiseUnary &op) override; + void visit(const ir::operation::ExpandDims &op) override; + void visit(const ir::operation::Fill &op) override; + void visit(const ir::operation::FullyConnected &op) override; + void visit(const ir::operation::FusedBatchNorm &op) override; + void visit(const ir::operation::Gather &op) override; + void visit(const ir::operation::If &op) override; + void visit(const ir::operation::L2Normalization &op) override; + void visit(const ir::operation::LSTM &op) override; + void visit(const ir::operation::MatrixBandPart &op) override; + void visit(const ir::operation::OneHot &op) override; + void visit(const ir::operation::Pack &op) override; + void visit(const ir::operation::Pad &op) override; + void visit(const ir::operation::Permute &op) override; + void visit(const ir::operation::Pow &op) override; + void visit(const ir::operation::Range &op) override; + void visit(const ir::operation::Reduce &op) override; + void visit(const ir::operation::Reshape &op) override; + void visit(const ir::operation::ResizeBilinear &op) override; + void visit(const ir::operation::Reverse &op) override; + void visit(const ir::operation::Select &op) override; + void visit(const ir::operation::Shape &op) override; + void visit(const ir::operation::Slice &op) override; + void visit(const ir::operation::Softmax &op) override; + void visit(const ir::operation::SpaceToBatchND &op) override; + void visit(const ir::operation::Split &op) override; + void visit(const ir::operation::Squeeze &op) override; + void visit(const ir::operation::StridedSlice &op) override; + void visit(const ir::operation::SquaredDifference &op) override; + void visit(const ir::operation::Tile &op) override; + void visit(const ir::operation::Transpose &op) override; + void visit(const ir::operation::Unpack &op) override; + void visit(const ir::operation::While &op) override; + +private: + /** + * @brief Performs shape inference for arithmetic operation + */ + void handleBinaryArithmeticOp(const ir::Operation &op, const ir::OperandIndex lhs_idx, + const ir::OperandIndex rhs_idx); + + /** + * @brief Performs shape inference for unary op whose output shape is + * always same with input shape + */ + void handleSimpleUnaryOp(const ir::Operation &op, const ir::OperandIndex input_idx); + +private: + const std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>> + &_lowered_subgs; + // _operands and _operations can be changed by controlflow operation + ir::Operands &_operands; // operands of current subgraph + ir::Operations &_operations; // operations of current subgraph + bool _return_has_dynamic_tensor; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_STATIC_SHAPE_INFERER_H__ diff --git a/runtime/onert/core/include/exec/DynamicShapeInferer.h b/runtime/onert/core/include/exec/DynamicShapeInferer.h new file mode 100644 index 000000000..d2eb83159 --- /dev/null +++ b/runtime/onert/core/include/exec/DynamicShapeInferer.h @@ -0,0 +1,123 @@ +/* + * 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_EXEC_DYNAMIC_SHAPE_INFERER_H__ +#define __ONERT_EXEC_DYNAMIC_SHAPE_INFERER_H__ + +#include "ir/Operands.h" +#include "ir/OperationVisitor.h" +#include "ir/Index.h" +#include "backend/IDynamicTensorManager.h" +#include "backend/ITensorManager.h" +#include "backend/ITensorRegistry.h" + +#include <map> + +namespace onert +{ +namespace exec +{ + +/** + * @brief Class to infer shape of output tensor at execution time and + * allocate memory fo output tensor if needed + */ +class DynamicShapeInferer : public ir::OperationVisitor +{ +public: + DynamicShapeInferer(const ir::Operands &operands, + const std::shared_ptr<backend::ITensorRegistry> &tensor_registry) + : _operands(operands), _tensor_registry(tensor_registry) + { + UNUSED_RELEASE(_operands); + UNUSED_RELEASE(_tensor_registry); + } + +public: + // TODO Define visitors for operations. List them in alphabetic order. + // Remove TODO when any op starting from the alphabet is added + void visit(const ir::operation::ArgMax &op) override; + void visit(const ir::operation::BatchMatMul &op) override; + void visit(const ir::operation::BCQFullyConnected &op) override; + void visit(const ir::operation::BCQGather &op) override; + void visit(const ir::operation::BinaryArithmetic &op) override; + void visit(const ir::operation::BroadcastTo &op) override; + void visit(const ir::operation::Comparison &op) override; + void visit(const ir::operation::Concat &op) override; + void visit(const ir::operation::Conv2D &op) override; + void visit(const ir::operation::ElementwiseActivation &op) override; + void visit(const ir::operation::ElementwiseBinary &op) override; + void visit(const ir::operation::ElementwiseUnary &op) override; + void visit(const ir::operation::ExpandDims &op) override; + void visit(const ir::operation::Fill &op) override; + void visit(const ir::operation::FullyConnected &op) override; + void visit(const ir::operation::FusedBatchNorm &op) override; + void visit(const ir::operation::Gather &op) override; + void visit(const ir::operation::L2Normalization &op) override; + void visit(const ir::operation::LSTM &op) override; + void visit(const ir::operation::MatrixBandPart &op) override; + void visit(const ir::operation::OneHot &op) override; + void visit(const ir::operation::Pack &op) override; + void visit(const ir::operation::Pad &op) override; + void visit(const ir::operation::Permute &op) override; + void visit(const ir::operation::Pow &op) override; + // TODO write op starting from Q + void visit(const ir::operation::Range &op) override; + void visit(const ir::operation::Reduce &op) override; + void visit(const ir::operation::Reshape &op) override; + void visit(const ir::operation::ResizeBilinear &op) override; + void visit(const ir::operation::Reverse &op) override; + void visit(const ir::operation::Select &op) override; + void visit(const ir::operation::Shape &op) override; + void visit(const ir::operation::Slice &op) override; + void visit(const ir::operation::Softmax &op) override; + void visit(const ir::operation::SpaceToBatchND &op) override; + void visit(const ir::operation::Split &op) override; + void visit(const ir::operation::Squeeze &op) override; + void visit(const ir::operation::StridedSlice &op) override; + void visit(const ir::operation::SquaredDifference &op) override; + void visit(const ir::operation::Tile &op) override; + void visit(const ir::operation::Transpose &op) override; + void visit(const ir::operation::Unpack &op) override; + // TODO write op starting from V + +private: + /** + * @brief Performs shape inference and memory allocation for arithmetic operation + */ + void handleBinaryArithmeticOp(const ir::Operation &op, const ir::OperandIndex lhs_idx, + const ir::OperandIndex rhs_idx); + /** + * @brief Performs shape inference and memory allocation for unary op whose output shape is + * always same with input shape + */ + void handleSimpleUnaryOp(const ir::Operation &op, const ir::OperandIndex input_idx); + +private: + /** + * @brief To get operand-level info, e.g., ir::Operand::isConstant() + */ + const ir::Operands &_operands; + /** + * @brief To get tensor object and access tensor-level info, e.g., ITensor::buffer() + */ + std::shared_ptr<backend::ITensorRegistry> _tensor_registry; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_DYNAMIC_SHAPE_INFERER_H__ diff --git a/runtime/onert/core/include/exec/Execution.h b/runtime/onert/core/include/exec/Execution.h new file mode 100644 index 000000000..d3c5b6dda --- /dev/null +++ b/runtime/onert/core/include/exec/Execution.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 Execution.h + * @brief This file defines execution + */ +#ifndef __ONERT_EXEC_EXECUTION_H__ +#define __ONERT_EXEC_EXECUTION_H__ + +#include "ir/Layout.h" +#include "exec/IExecutor.h" +#include "IODescription.h" + +#include <thread> + +namespace onert +{ +namespace exec +{ + +/** + * @brief Class to define execution instance to collect input/output information for inference + * and prepare executor run (TODO) + */ +class Execution +{ + +public: + /** + * @brief Construct a new Execution object + * @param[in] executor Model executor + */ + Execution(const std::shared_ptr<ExecutorMap> &executors); + +public: + /** + * @brief Returns primary graph object + * @return Graph object + */ + const ir::Graph &primary_subgraph() const { return primary_executor()->graph(); } + + /** + * @brief Change input shape + * @param[in] index Input index + * @param[in] new_shape shape to change + */ + void changeInputShape(const ir::IOIndex &index, const ir::Shape &new_shape); + + /** + * @brief Set input data's information + * @param[in] index Input index + * @param[in] buffer Input data's buffer pointer + * @param[in] length Input data's length + * @param[in] layout Input data's data format + */ + void setInput(const ir::IOIndex &index, const void *buffer, size_t length, + ir::Layout layout = ir::Layout::NHWC); + /** + * @brief Set input data's information, especially to specify unknown dimensions on model + * build time. + * @param[in] index Input index + * @param[in] type Input data's type info + * @param[in] shape Input data's shape + * @param[in] buffer Input data's buffer pointer + * @param[in] length Input data's length + * @param[in] layout Input data's data format + */ + void setInput(const ir::IOIndex &index, const ir::TypeInfo &type, const ir::Shape &shape, + const void *buffer, size_t length, ir::Layout layout = ir::Layout::NHWC); + /** + * @brief Set output data's information + * @param[in] index Output index + * @param[in] buffer Output data's buffer pointer + * @param[in] length Output data's length + * @param[in] layout Output data's data format + */ + void setOutput(const ir::IOIndex &index, void *buffer, size_t length, + ir::Layout layout = ir::Layout::NHWC); + /** + * @brief Set output data's information, especially to specify unknown dimensions on model + * build time. + * @param[in] index Output index + * @param[in] type Output data's type info + * @param[in] shape Output data's shape + * @param[in] buffer Output data's buffer pointer + * @param[in] length Output data's length + * @param[in] layout Output data's data format + */ + void setOutput(const ir::IOIndex &index, const ir::TypeInfo &type, const ir::Shape &shape, + void *buffer, size_t length, ir::Layout layout = ir::Layout::NHWC); + /** + * @brief Set input data's data format + * @param[in] index Input index + * @param[in] layout Input data's data format + */ + void setInputLayout(const ir::IOIndex &index, ir::Layout layout); + /** + * @brief Set output data's data format + * @param[in] index Output index + * @param[in] layout Output data's data format + */ + void setOutputLayout(const ir::IOIndex &index, ir::Layout layout); + /** + * @brief Execution + * @note It should be called after setting input and output buffer + */ + void execute(); + + /** + * @brief Start asynchronous execution + * @note It returns after execution thread is started + * It should be called after setting input and output buffer + */ + void startExecute(void); + + /** + * @brief Return when execution is finished + * @note It waits until execution is finished + */ + void waitFinish(void); + + /** + * @brief Check execution is finished + * @return @c true if execution is finished, otherwise @c false + */ + bool isFinished(void) const; + + ir::Shape getInputShape(ir::IOIndex ind) const; + ir::Shape getOutputShape(ir::IOIndex ind) const; + +private: + const std::unique_ptr<IExecutor> &primary_executor() const + { + return _executors->at(ir::SubgraphIndex{0}); + }; + std::unique_ptr<IExecutor> &primary_executor() { return _executors->at(ir::SubgraphIndex{0}); }; + +private: + const std::shared_ptr<ExecutorMap> _executors; + IODescription _io_desc; + std::unique_ptr<std::thread> _exec_thread; + bool finished{false}; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_EXECUTION_H__ diff --git a/runtime/onert/core/include/exec/FunctionSequence.h b/runtime/onert/core/include/exec/FunctionSequence.h new file mode 100644 index 000000000..6ec6e60ad --- /dev/null +++ b/runtime/onert/core/include/exec/FunctionSequence.h @@ -0,0 +1,132 @@ +/* + * 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_FUNCTION_SEQUENCE_H__ +#define __ONERT_EXEC_FUNCTION_SEQUENCE_H__ + +#include <memory> +#include <cassert> +#include <vector> +#include <functional> + +#include "exec/IFunction.h" +#include "exec/DynamicShapeInferer.h" +#include "ir/Operations.h" +#include "backend/ITensorRegistry.h" +#include "backend/IDynamicTensorManager.h" + +namespace onert +{ +namespace exec +{ + +class FunctionSequence : public IFunction +{ +public: + template <typename... Args> FunctionSequence(Args &&... args) { initialize(std::move(args)...); } + +private: + void initialize() + { + // Template base case : do nothing + } + + template <typename T, typename... Args> void initialize(std::unique_ptr<T> &&fn, Args &&... args) + { + _functions.emplace_back(std::move(fn)); + initialize(std::move(args)...); + } + +public: + virtual ~FunctionSequence() = default; + + void run() override; + void prepare() override; + + /** + * @brief Appends an IFunction object to the function sequence + * + * @param function IFunction object to be appended + */ + void append(std::unique_ptr<IFunction> &&function); + + void iterate(const std::function<void(IFunction &)> &fn); + + template <typename T, typename... Args> void wrap(Args &&... args) + { + for (auto &function : _functions) + { + function = std::make_unique<T>(std::move(function), args...); + } + } + +public: // methods related to dynamic tensor + struct DynamicTensorCtx + { + const ir::OpSequence *op_seq = nullptr; + const ir::Operations *operations = nullptr; + std::shared_ptr<exec::DynamicShapeInferer> dynamic_shape_inferer = nullptr; + backend::IDynamicTensorManager *dynamic_tensor_manager = nullptr; + }; + + /** + * @brief Prepare to run FunctionSequence which "might" handle dynamic tensor + * @note Calling this does not mean that run() will handle dynamic tensor. + * enableDynamicShapeInferer(true) will make run() will handle dynamic tensor. + */ + void dynamic_tensor_ctx(std::shared_ptr<DynamicTensorCtx> &dynamic_tensor_ctx) + { + _dynamic_tensor_ctx = dynamic_tensor_ctx; + } + + std::shared_ptr<DynamicTensorCtx> &dynamic_tensor_ctx() { return _dynamic_tensor_ctx; } + + /** + * @brief Call this function by passing @c true if this FunctionSequence handles dynamic tensors + * and should run DynamicShapeInferer. This function can be called multiple times and + * if @c false is passed during multiple calls, DynamicShapeInfere will not be run. + * @note This must be called before run(). If not called, run() assumes that all tensors are + * dynamic and DynamicShapeInferer will be run. + */ + void enableDynamicShapeInferer(bool enable) + { + _enable_dynamic_shape_inferer = _enable_dynamic_shape_inferer || enable; + } + + /** + * @brief Call this function to initialize vars before running + * @note When we run a model with static tensor input and then run with dynamic tensor input, + * _enable_dynamic_shape_inferer is set to @c false at first run. + * Once _enable_dynamic_shape_inferer is set to @c true it cannot be changed to @c false + * only with calling enableDynamicShapeInferer(). So initializing it to @c false is + * necessary. + * @todo This is a quick fix. Adding this will increase time for run(). Find way to optimize. + */ + void initRunning() { _enable_dynamic_shape_inferer = false; } + +protected: + std::vector<std::unique_ptr<IFunction>> _functions; + +protected: + bool _enable_dynamic_shape_inferer = false; + + std::shared_ptr<DynamicTensorCtx> _dynamic_tensor_ctx = nullptr; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FUNCTION_SEQUENCE_H__ diff --git a/runtime/onert/core/include/exec/IExecutor.h b/runtime/onert/core/include/exec/IExecutor.h new file mode 100644 index 000000000..1d2831dd0 --- /dev/null +++ b/runtime/onert/core/include/exec/IExecutor.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. + */ + +/** + * @file IExecutor.h + * @brief This file defines interface of Executor + */ +#ifndef __ONERT_EXEC_I_EXECUTOR_H_ +#define __ONERT_EXEC_I_EXECUTOR_H_ + +#include "ir/Graph.h" +#include "IFunction.h" +#include "IODescription.h" +#include "ir/OperationIndexMap.h" +#include "backend/IDynamicTensorManager.h" + +namespace onert +{ +namespace exec +{ +class IExecutionObserver; +/** + * @brief Struct to define interface of Executor + */ +struct IExecutor +{ + /** + * @brief Construct a new IExecutor object + */ + IExecutor() = default; + /** + * @brief Destroy the IExecutor object + */ + virtual ~IExecutor() = default; + + /** + * @brief Returns graph object + * + * @return Graph object + */ + virtual const ir::Graph &graph() = 0; + + /** + * @brief Set an ordering on operations + * @param[in] ranks The table encoding the ordering + */ + virtual void setIndexedRanks(std::shared_ptr<ir::OperationIndexMap<int64_t>>) = 0; + + /** + * @brief Start execution + * @param[in] desc Input and output description + * @note This method should be thread-safe + */ + virtual void execute(const IODescription &desc) = 0; +}; + +using ExecutorMap = std::unordered_map<ir::SubgraphIndex, std::unique_ptr<IExecutor>>; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_I_EXECUTOR_H_ diff --git a/runtime/onert/core/include/exec/IFunction.h b/runtime/onert/core/include/exec/IFunction.h new file mode 100644 index 000000000..18ba2457a --- /dev/null +++ b/runtime/onert/core/include/exec/IFunction.h @@ -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. + */ + +#ifndef __ONERT_EXEC_I_FUNCTION_H__ +#define __ONERT_EXEC_I_FUNCTION_H__ + +namespace onert +{ +namespace exec +{ + +class IFunction +{ +public: + virtual ~IFunction() = default; + virtual void run() = 0; + virtual void prepare() {} +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_I_FUNCTION_H__ diff --git a/runtime/onert/core/include/exec/IODescription.h b/runtime/onert/core/include/exec/IODescription.h new file mode 100644 index 000000000..d1810ec3b --- /dev/null +++ b/runtime/onert/core/include/exec/IODescription.h @@ -0,0 +1,72 @@ +/* + * 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_IO_DESCRIPTION_H__ +#define __ONERT_EXEC_IO_DESCRIPTION_H__ + +#include <vector> +#include <unordered_map> + +#include "ir/OperandInfo.h" +#include "ir/Index.h" + +namespace onert +{ +namespace exec +{ + +struct InputDesc +{ + const ir::OperandInfo info; + const void *buffer; + const size_t size; + const ir::Layout layout; + + InputDesc(void) = delete; + InputDesc(const ir::OperandInfo &info, const void *buffer, const size_t size, ir::Layout layout) + : info(info), buffer(buffer), size(size), layout(layout) + { + } +}; + +struct OutputDesc +{ + // not `const` because shape should be modified after execution in case when output is + // a dynamic tensor + ir::OperandInfo info; + void *buffer; + const size_t size; + const ir::Layout layout; + + OutputDesc(void) = delete; + OutputDesc(const ir::OperandInfo &info, void *buffer, const size_t size, ir::Layout layout) + : info(info), buffer(buffer), size(size), layout(layout) + { + } +}; + +struct IODescription +{ + std::vector<std::unique_ptr<InputDesc>> inputs; + std::vector<std::unique_ptr<OutputDesc>> outputs; + // Contains shape of input set by nnfw_set_input_tensorinfo(..) + std::unordered_map<ir::IOIndex, ir::Shape> dynamic_input_shapes; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_IO_DESCRIPTION_H__ diff --git a/runtime/onert/core/include/exec/NopFunction.h b/runtime/onert/core/include/exec/NopFunction.h new file mode 100644 index 000000000..d0ed55921 --- /dev/null +++ b/runtime/onert/core/include/exec/NopFunction.h @@ -0,0 +1,48 @@ +/* + * 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 NopFunction.h + * @brief This file defines NopFunction + */ +#ifndef __ONERT_EXEC_NOP_FUNCTION_H_ +#define __ONERT_EXEC_NOP_FUNCTION_H_ + +#include "IFunction.h" + +namespace onert +{ +namespace exec +{ + +/** + * @brief A derivative of IFunction tha does nothing + * + */ +class NopFunction : public IFunction +{ +public: + NopFunction() = default; + void run() override + { + // DO NOTHING + } +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_NOP_FUNCTION_H_ diff --git a/runtime/onert/core/include/ir/Coordinates.h b/runtime/onert/core/include/ir/Coordinates.h new file mode 100644 index 000000000..9963cab4c --- /dev/null +++ b/runtime/onert/core/include/ir/Coordinates.h @@ -0,0 +1,128 @@ +/* + * 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_IR_COORDINATES_H__ +#define __ONERT_IR_COORDINATES_H__ + +#include <cassert> +#include <stdint.h> +#include <vector> + +#include "Layout.h" + +namespace onert +{ +namespace ir +{ + +/** + * @brief Class to represent position(offset) of tensor.\n + * Assume that the front is higher dimensional. + * i.g. N: 0, C: 1, H: 2, W: 3 for NCHW layout + */ +class Coordinates final +{ +public: + static constexpr size_t num_max_dimensions = 4; + +public: + /** + * @brief Construct a new Coordinates object with zero dimension + * @return N/A + */ + Coordinates() = default; + /** + * @brief Construct a new Coordinates object + * @param[in] init The initialzer_list with coordinates + * @return + */ + Coordinates(std::initializer_list<int32_t> init) : _coordinates{init} + { + assert(init.size() <= num_max_dimensions); + } + /** + * @brief Construct a new Coordinates object + * @param[in] init The initialzer_list with coordinates + * @return + */ + Coordinates(std::initializer_list<uint32_t> init) : _coordinates{init.begin(), init.end()} + { + assert(init.size() <= num_max_dimensions); + } + /** + * @brief Construct a new Coordinates object with rank + * @param[in] rank The rank of coordinates + * @return + */ + explicit Coordinates(int rank) : _coordinates(rank, 0) {} + +public: + /** + * @brief Set the coordinate of one of the coordinates. + * + * @param[in] dimension Dimension for which the coordinate is set. + * @param[in] Coordinate Coordinate to be set for the dimension. + */ + void set(size_t dimension, int32_t coordinate) + { + assert(dimension < num_max_dimensions); + if (dimension >= _coordinates.size()) + { + _coordinates.resize(dimension + 1, 0); + } + _coordinates[dimension] = coordinate; + } + +public: + /** + * @brief Return size of coordinates + * + * @return size of coordinates + */ + size_t size() const { return _coordinates.size(); } + +public: + int32_t operator[](size_t dimension) const + { + assert(dimension < _coordinates.size()); + return _coordinates[dimension]; + } + +public: + /** + * @brief begin() of const_iterator for this class + * + * @return The first iterator of the coordinates + */ + std::vector<int32_t>::const_iterator begin() const { return _coordinates.begin(); } + /** + * @brief end() of const_iterator for this class + * + * @return The last iterator of the coordinates + */ + std::vector<int32_t>::const_iterator end() const { return _coordinates.end(); } + +private: + std::vector<int32_t> _coordinates; +}; + +Coordinates convertCoordinates(const Coordinates &from_coordinates, Layout from_layout, + Layout to_layout); + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_COORDINATES_H__ diff --git a/runtime/onert/core/include/ir/Data.h b/runtime/onert/core/include/ir/Data.h new file mode 100644 index 000000000..d31191b4f --- /dev/null +++ b/runtime/onert/core/include/ir/Data.h @@ -0,0 +1,107 @@ +/* + * 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_DATA_H__ +#define __ONERT_IR_DATA_H__ + +#include <algorithm> +#include <sys/mman.h> + +namespace onert +{ +namespace ir +{ + +struct Data +{ + virtual ~Data() = default; + + virtual size_t size(void) const = 0; + virtual const uint8_t *base(void) const = 0; +}; + +class CachedData final : public Data +{ +public: + CachedData(const uint8_t *base, size_t size) : _base{new uint8_t[size]}, _size{size} + { + std::copy(base, base + size, _base); + } + +public: + ~CachedData() { delete[] _base; } + +public: + size_t size(void) const override { return _size; } + const uint8_t *base(void) const override { return _base; } + +private: + uint8_t *_base; + size_t _size; +}; + +class ExternalData : public Data +{ +public: + ExternalData(const 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; } + +private: + const uint8_t *_base; + const size_t _size; +}; + +class MMapedData final : public ExternalData +{ +public: + MMapedData(int fd, const std::ptrdiff_t mmap_offset, const size_t mmap_size, + const std::ptrdiff_t data_offset, const size_t data_size) + : ExternalData(nullptr, data_size), + _mmap_base( + static_cast<uint8_t *>(mmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, mmap_offset))), + _mmap_size(mmap_size), _offset(data_offset - mmap_offset) + { + // DO NOTHING + } + +public: + ~MMapedData() + { + if (_mmap_size > 0) + { + munmap(const_cast<uint8_t *>(_mmap_base), _mmap_size); + } + } + +public: + const uint8_t *base(void) const override { return _mmap_base + _offset; } + +private: + const uint8_t *_mmap_base; + size_t _mmap_size; + std::ptrdiff_t _offset; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_DATA_H__ diff --git a/runtime/onert/core/include/ir/DataType.h b/runtime/onert/core/include/ir/DataType.h new file mode 100644 index 000000000..9f09de3fb --- /dev/null +++ b/runtime/onert/core/include/ir/DataType.h @@ -0,0 +1,47 @@ +/* + * 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_DATATYPE_H__ +#define __ONERT_IR_DATATYPE_H__ + +#include <cstdlib> + +namespace onert +{ +namespace ir +{ + +enum class DataType +{ + FLOAT32 = 0, + INT32 = 1, + UINT32 = 2, + QUANT_UINT8_ASYMM = 3, + BOOL8 = 4, + UINT8 = 5, + QUANT_INT8_SYMM = 6, + FLOAT16 = 7, + INT64 = 8, + QUANT_INT8_ASYMM = 9, + QUANT_INT16_ASYMM = 10, +}; + +size_t sizeOfDataType(DataType data_type); + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_DATATYPE_H__ diff --git a/runtime/onert/core/include/ir/Graph.h b/runtime/onert/core/include/ir/Graph.h new file mode 100644 index 000000000..2103e6e64 --- /dev/null +++ b/runtime/onert/core/include/ir/Graph.h @@ -0,0 +1,124 @@ +/* + * 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_GRAPH_H__ +#define __ONERT_IR_GRAPH_H__ + +#include <functional> +#include <unordered_map> + +#include "ir/Operands.h" +#include "ir/Operations.h" +#include "ir/OpSequence.h" +#include "ir/OpSequences.h" +#include "ir/Subgraphs.h" + +namespace onert +{ +namespace backend +{ +namespace custom +{ +class IKernelBuilder; +} // namespace custom +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace ir +{ + +class Graph +{ +private: + enum class Phase + { + BUILDING, + MODEL + }; + +public: + Graph(void); + ~Graph(void); + + // Graph Building +public: + OperandIndex addOperand(const Shape &shape, const TypeInfo &type); + OperationIndex addOperation(std::unique_ptr<Operation> &&node); + void setOperandValue(const OperandIndex &ind, std::shared_ptr<Data> data); + void addInput(const OperandIndex &ind, const std::string &name = ""); + void addOutput(const OperandIndex &ind, const std::string &name = ""); + void finishBuilding(void); + void removeOperand(const OperandIndex &ind) { _operands.remove(ind); } + bool isBuildingPhase(void) const { return _phase == Phase::BUILDING; } + void setLayout(Layout layout) { _layout = layout; } + void setSubgraphs(const std::shared_ptr<Subgraphs> &subgs) { _subgraphs = subgs; } + +private: + void initializeUseDef(); + void sweepGarbageOperands(); + + // Custom operations support +public: + void + bindKernelBuilder(const std::shared_ptr<onert::backend::custom::IKernelBuilder> &kernel_builder) + { + _kernel_builder = kernel_builder; + } + + const std::shared_ptr<backend::custom::IKernelBuilder> &getKernelBuilder() const + { + return _kernel_builder; + } + +private: + std::shared_ptr<backend::custom::IKernelBuilder> _kernel_builder; + + // Accessors +public: + const OperandIndexSequence &getInputs() const { return _inputs; } + OperandIndexSequence &getInputs() { return _inputs; } + const OperandIndexSequence &getOutputs() const { return _outputs; } + OperandIndexSequence &getOutputs() { return _outputs; } + IOIndex getInputIndex(const std::string &name) const; + IOIndex getOutputIndex(const std::string &name) const; + const Operands &operands() const { return _operands; } + Operands &operands() { return _operands; } // TODO Remove this non-const accessor + const Operations &operations() const { return _operations; } + Operations &operations() { return _operations; } + const std::shared_ptr<Subgraphs> &subgraphs() const { return _subgraphs; } + std::shared_ptr<Subgraphs> &subgraphs() { return _subgraphs; } + Layout layout() const { return _layout; } + +private: + Phase _phase{Phase::BUILDING}; + Operations _operations; + Operands _operands; + OperandIndexSequence _inputs; + OperandIndexSequence _outputs; + std::unordered_map<std::string, IOIndex> _name_to_input; + std::unordered_map<std::string, IOIndex> _name_to_output; + // Child subgraphs + std::shared_ptr<Subgraphs> _subgraphs; + // TFLite and circle's default layout is NHWC; + Layout _layout{Layout::NHWC}; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_GRAPH_H__ diff --git a/runtime/onert/core/include/ir/Index.h b/runtime/onert/core/include/ir/Index.h new file mode 100644 index 000000000..2538301a4 --- /dev/null +++ b/runtime/onert/core/include/ir/Index.h @@ -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. + */ + +#ifndef __ONERT_IR_INDEX_H__ +#define __ONERT_IR_INDEX_H__ + +#include "util/Index.h" + +namespace onert +{ +namespace ir +{ + +struct OperationIndexTag; +using OperationIndex = ::onert::util::Index<uint32_t, OperationIndexTag>; + +struct OperandIndexTag; +using OperandIndex = ::onert::util::Index<uint32_t, OperandIndexTag>; + +struct IOIndexTag; +using IOIndex = ::onert::util::Index<uint32_t, IOIndexTag>; + +struct OpSequenceIndexTag; +using OpSequenceIndex = ::onert::util::Index<uint32_t, OpSequenceIndexTag>; + +struct SubgraphIndexTag; +using SubgraphIndex = ::onert::util::Index<uint32_t, SubgraphIndexTag>; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_INDEX_H__ diff --git a/runtime/onert/core/include/ir/InternalType.h b/runtime/onert/core/include/ir/InternalType.h new file mode 100644 index 000000000..50292e812 --- /dev/null +++ b/runtime/onert/core/include/ir/InternalType.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_IR_INTERNAL_TYPE_H__ +#define __ONERT_IR_INTERNAL_TYPE_H__ + +#include <cstdint> + +namespace onert +{ +namespace ir +{ + +enum class Activation +{ + NONE = 0, + RELU = 1, + RELU1 = 2, + RELU6 = 3, + TANH = 4, + SIGMOID = 5 +}; + +struct Stride +{ + uint32_t vertical; + uint32_t horizontal; +}; + +struct Dilation +{ + uint32_t width_factor; + uint32_t height_factor; +}; + +enum class FullyConnectedWeightsFormat +{ + Default = 0, + Shuffled4x16Int8 = 1, + Shuffled16x1Float32 = 127 +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_INTERNAL_TYPE_H__ diff --git a/runtime/onert/core/include/ir/Layout.h b/runtime/onert/core/include/ir/Layout.h new file mode 100644 index 000000000..082810172 --- /dev/null +++ b/runtime/onert/core/include/ir/Layout.h @@ -0,0 +1,67 @@ +/* + * 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_H__ +#define __ONERT_IR_LAYOUT_H__ + +#include <functional> +#include <string> + +namespace onert +{ +namespace ir +{ + +enum class Layout +{ + UNKNOWN = 0, + NHWC, + NCHW +}; + +inline std::string to_string(Layout layout) +{ + switch (layout) + { + case Layout::NHWC: + return std::string{"NHWC"}; + case Layout::NCHW: + return std::string{"NCHW"}; + case Layout::UNKNOWN: + return std::string{"UNKNOWN"}; + default: + throw std::runtime_error("WRONG LAYOUT"); + } +} + +} // namespace ir +} // namespace onert + +namespace std +{ + +template <> struct hash<onert::ir::Layout> +{ + size_t operator()(onert::ir::Layout value) const noexcept + { + using type = typename std::underlying_type<onert::ir::Layout>::type; + return hash<type>()(static_cast<type>(value)); + } +}; + +} // namespace std + +#endif // __ONERT_IR_LAYOUT_H__ diff --git a/runtime/onert/core/include/ir/LowerInfoMap.h b/runtime/onert/core/include/ir/LowerInfoMap.h new file mode 100644 index 000000000..fbabaf39d --- /dev/null +++ b/runtime/onert/core/include/ir/LowerInfoMap.h @@ -0,0 +1,42 @@ +/* + * 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_IR_LOWER_INFO_MAP_H__ +#define __ONERT_IR_LOWER_INFO_MAP_H__ + +#include <memory> +#include <unordered_map> + +#include "ir/operand/LowerInfo.h" +#include "ir/operation/LowerInfo.h" +#include "ir/OperandIndexMap.h" +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +struct LowerInfoMap +{ + std::unordered_map<OpSequenceIndex, std::unique_ptr<operation::LowerInfo>> op_seq; + OperandIndexMap<std::unique_ptr<operand::LowerInfo>> operand; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_LOWER_INFO_MAP_H__ diff --git a/runtime/onert/core/include/ir/OpCode.h b/runtime/onert/core/include/ir/OpCode.h new file mode 100644 index 000000000..32e47796e --- /dev/null +++ b/runtime/onert/core/include/ir/OpCode.h @@ -0,0 +1,58 @@ +/* + * 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_IR_OP_CODE_H__ +#define __ONERT_IR_OP_CODE_H__ + +#include <functional> +#include <stdint.h> +#include <string> + +namespace onert +{ +namespace ir +{ + +enum class OpCode +{ + Invalid, //< Unused +#define OP(Name) Name, //< All operations +#include "ir/Operations.lst" +#undef OP + COUNT +}; + +const char *toString(OpCode opcode); +OpCode toOpCode(const std::string str); + +} // namespace ir +} // namespace onert + +namespace std +{ + +template <> struct hash<onert::ir::OpCode> +{ + size_t operator()(onert::ir::OpCode value) const noexcept + { + using type = typename std::underlying_type<onert::ir::OpCode>::type; + return hash<type>()(static_cast<type>(value)); + } +}; + +} // namespace std + +#endif // __ONERT_IR_OP_CODE_H__ diff --git a/runtime/onert/core/include/ir/OpSequence.h b/runtime/onert/core/include/ir/OpSequence.h new file mode 100644 index 000000000..754cf3b34 --- /dev/null +++ b/runtime/onert/core/include/ir/OpSequence.h @@ -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. + */ + +#ifndef __ONERT_IR_OP_SEQUENCE_H__ +#define __ONERT_IR_OP_SEQUENCE_H__ + +#include <vector> +#include <string> +#include <memory> + +#include "ir/Layout.h" +#include "ir/Index.h" +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ + +class Operations; + +class OpSequence +{ +public: + explicit OpSequence(Layout layout); + OpSequence(const OpSequence &) = delete; + +public: + void accept(OperationVisitor &v) const; + +public: + const OperandIndexSequence &getInputs() const { return _inputs; } + const OperandIndexSequence &getOutputs() const { return _outputs; } + void setInputs(const OperandIndexSequence &indexes) { _inputs = indexes; } + void setOutputs(const OperandIndexSequence &indexes) { _outputs = indexes; } + void replaceInputs(const OperandIndex &from, const OperandIndex &to) + { + _inputs.replace(from, to); + } + void replaceOutputs(const OperandIndex &from, const OperandIndex &to) + { + _outputs.replace(from, to); + } + + void appendOperation(const OperationIndex &index) { _operations.emplace_back(index); } + + std::vector<OperationIndex> &operations(void) { return _operations; } + + const std::vector<OperationIndex> &operations(void) const { return _operations; } + + uint32_t size(void) const { return _operations.size(); } + +public: + void remove(const OperationIndex &index); + + bool exist(const OperationIndex &index) const; + +public: + Layout getLayout() const { return _layout; } + +public: + std::vector<OperationIndex>::const_iterator begin() const { return _operations.begin(); } + std::vector<OperationIndex>::const_iterator end() const { return _operations.end(); } + +public: + /** + * @brief Set @c true if any operation in this opSequence has dynamic input + * or dynamic output; + * @c false if all operations' inputs and outputs are static tensors + */ + void has_dynamic_tensor(bool has_dynamic_tensor) { _has_dynamic_tensor = has_dynamic_tensor; } + bool has_dynamic_tensor() const { return _has_dynamic_tensor; } + +private: + OperandIndexSequence _inputs; + OperandIndexSequence _outputs; + std::vector<OperationIndex> _operations; + +private: + Layout _layout; + bool _has_dynamic_tensor; +}; + +std::string getStrFromOpSeq(const OpSequence &op_seq, const Operations &operations); + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OP_SEQUENCE_H__ diff --git a/runtime/onert/core/include/ir/OpSequences.h b/runtime/onert/core/include/ir/OpSequences.h new file mode 100644 index 000000000..ab258f395 --- /dev/null +++ b/runtime/onert/core/include/ir/OpSequences.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. + */ + +#ifndef __ONERT_IR_OP_SEQUENCES_H__ +#define __ONERT_IR_OP_SEQUENCES_H__ + +#include "ir/Index.h" +#include "ir/OpSequence.h" +#include "util/ObjectManager.h" + +namespace onert +{ +namespace ir +{ + +/** + * @brief Class that manages OpSequence objects + */ +class OpSequences : public util::ObjectManager<OpSequenceIndex, OpSequence> +{ +public: + /** + * @brief Create an instance of OpSequence with given op and push it to objects + * + * @param[in] op_idx Operation index that is emplaced + * @param[in] layout OpSequence's layout + * @return OpSequenceIndex + */ + OpSequenceIndex emplace(const OperationIndex &op_index, Layout layout); + + /** + * @brief Push an instance of OpSequence to objects + * + * @param[in] op_seq An instance of OpSequence + * @return OpSequenceIndex + */ + OpSequenceIndex emplace(std::unique_ptr<OpSequence> &&op_seq); + /** + * @brief Check if an operation does exist in any OpSequences + * + * @param operation_index Operation index to find + * @return true If such operation exists in any OpSequences otherwise false + */ + bool containsOperation(const OperationIndex &operation_index) const; + /** + * @brief Find an operation from all OpSequences + * + * @param operation_index Operation index to find + * @return OpSequenceIndex Index of OpSequence that contains given operation index + */ + OpSequenceIndex getOperation(const OperationIndex &operation_index) const; + /** + * @brief Remove an operation from OpSequence + * + * @param operation_index Operation index to be removed + */ + void removeFromOpSequence(const OperationIndex &operation_index); + +private: + void cacheSequenceIndex(const OpSequenceIndex &seq_index, const OperationIndex &op_index) const; + OpSequenceIndex *findSequenceIndex(const OperationIndex &operation_index) const; + + OpSequenceIndex findOperation(const OperationIndex &operation_index) const; + mutable std::unordered_map<OperationIndex, OpSequenceIndex> _seq_indexes; +}; + +/** + * @brief Dump OpSequences + * + * @param op_seqs Operation Sequences + * @param operations Operation context + */ +void dumpOpSequences(const OpSequences &op_seqs, const Operations &operations); + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OP_SEQUENCES_H__ diff --git a/runtime/onert/core/include/ir/Operand.h b/runtime/onert/core/include/ir/Operand.h new file mode 100644 index 000000000..f149a744b --- /dev/null +++ b/runtime/onert/core/include/ir/Operand.h @@ -0,0 +1,117 @@ +/* + * 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_OPERAND_H__ +#define __ONERT_IR_OPERAND_H__ + +#include <cassert> +#include <cstdint> +#include <memory> +#include <algorithm> + +#include "ir/Data.h" +#include "ir/DataType.h" +#include "ir/OperandInfo.h" +#include "ir/OperationIndexSet.h" + +namespace onert +{ +namespace ir +{ + +class Operand +{ +public: + explicit Operand(const Shape &shape, const TypeInfo &type) + : _info{shape, type, MemAllocType::STATIC} + { + // DO NOTHING + } + explicit Operand(const Operand &) = default; + +public: + const Shape &shape(void) const { return _info.shape(); } + const TypeInfo &typeInfo(void) const { return _info.typeInfo(); } + const OperandInfo &info(void) const { return _info; } + OperandInfo &info(void) { return _info; } + size_t operandSize(void) const; + + const OperationIndexSet &getUses() const { return _uses; } + OperationIndex getDef() const { return _def; } + void insertUse(const OperationIndex &idx); + void removeUse(const OperationIndex &idx); + void setDef(const OperationIndex &idx); + void unsetDef(); + +public: + void type(const DataType type) { _info.type(type); }; + +public: + void data(std::shared_ptr<Data> &&data) + { + _data = std::move(data); + _info.setAsConstant(); + } + const Data *data(void) const { return _data.get(); } + + void releaseData(void) { _data.reset(); } + + std::shared_ptr<Data> shareData(void) const { return _data; } + + /** + * @brief Get true if Operand is const, otherwise @c false + a @return @c true if Operand is const, otherwise @c false + */ + bool isConstant(void) const { return _info.isConstant(); } + +public: + template <typename T, typename... Args> void data(Args &&... args) + { + data(std::make_unique<T>(std::forward<Args>(args)...)); + } + +public: + template <typename T> T asScalar(void) const + { + assert((shape().rank() == 0) || ((shape().rank() == 1) && (shape().dim(0) == 1))); + assert(_data != nullptr); + assert((_data->base() != nullptr) && (_data->size() == sizeof(T))); + + return *(reinterpret_cast<const T *>(_data->base())); + } + + template <typename T> std::vector<T> asVector() const + { + assert(_data != nullptr); + assert(_data->size() % sizeof(T) == 0); + + const auto *base = reinterpret_cast<const T *>(_data->base()); + const std::size_t size = _data->size() / sizeof(T); + return std::vector<T>(base, base + size); + } + +private: + OperandInfo _info; + std::shared_ptr<Data> _data; + + OperationIndexSet _uses; + OperationIndex _def; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERAND_H__ diff --git a/runtime/onert/core/include/ir/OperandConstraint.h b/runtime/onert/core/include/ir/OperandConstraint.h new file mode 100644 index 000000000..8da922bea --- /dev/null +++ b/runtime/onert/core/include/ir/OperandConstraint.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_MODEL_OPERAND_CONSTRAINT_H__ +#define __ONERT_MODEL_OPERAND_CONSTRAINT_H__ + +#include <stdint.h> +#include <limits> +#include <set> + +namespace onert +{ +namespace ir +{ + +class OperandConstraint +{ +private: + static const uint32_t INF = std::numeric_limits<uint32_t>::max(); + +public: + static OperandConstraint createAny() { return OperandConstraint{0u, INF}; } + static OperandConstraint createExact(uint32_t exact) { return OperandConstraint{exact, exact}; } + static OperandConstraint createAtMost(uint32_t end) { return OperandConstraint{0u, end}; } + static OperandConstraint createAtLeast(uint32_t begin) { return OperandConstraint{begin, INF}; } + static OperandConstraint createInRange(uint32_t begin, uint32_t end) + { + return OperandConstraint{begin, end}; + } + +private: + OperandConstraint(uint32_t begin, uint32_t end) : _begin{begin}, _end{end} {} + +public: + bool check(uint32_t ind) const { return _begin <= ind && ind <= _end; } + +private: + uint32_t _begin; + uint32_t _end; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_MODEL_OPERAND_CONSTRAINT_H__ diff --git a/runtime/onert/core/include/ir/OperandIndexMap.h b/runtime/onert/core/include/ir/OperandIndexMap.h new file mode 100644 index 000000000..468162ffb --- /dev/null +++ b/runtime/onert/core/include/ir/OperandIndexMap.h @@ -0,0 +1,34 @@ +/* + * 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_IR_OPERAND_INDEX_MAP_H__ +#define __ONERT_IR_OPERAND_INDEX_MAP_H__ + +#include <unordered_map> + +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +template <typename T> using OperandIndexMap = std::unordered_map<OperandIndex, T>; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERAND_INDEX_MAP_H__ diff --git a/runtime/onert/core/include/ir/OperandIndexSequence.h b/runtime/onert/core/include/ir/OperandIndexSequence.h new file mode 100644 index 000000000..2f78cc832 --- /dev/null +++ b/runtime/onert/core/include/ir/OperandIndexSequence.h @@ -0,0 +1,95 @@ +/* + * 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_MODEL_OPERAND_INDEX_SEQUENCE_H__ +#define __ONERT_MODEL_OPERAND_INDEX_SEQUENCE_H__ + +#include <initializer_list> +#include <vector> + +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +enum class Remove +{ + DUPLICATED = 1, + UNDEFINED = 2 +}; + +class OperandIndexSequence +{ +public: + OperandIndexSequence(void) = default; + OperandIndexSequence(std::initializer_list<OperandIndex> list); + OperandIndexSequence(std::initializer_list<int32_t> list); + OperandIndexSequence(std::initializer_list<uint32_t> list); + +public: + void append(const OperandIndex &index) { _vec.emplace_back(index); } + void append(const OperandIndexSequence &l) { _vec.insert(_vec.end(), l.begin(), l.end()); } + +public: + uint32_t size() const { return static_cast<uint32_t>(_vec.size()); } + const OperandIndex &at(IOIndex set_index) const { return _vec.at(set_index.value()); } + const OperandIndex &at(uint32_t index) const { return _vec.at(index); } + bool contains(const OperandIndex &index) const; + void replace(const OperandIndex &from, const OperandIndex &to); + OperandIndexSequence operator|(ir::Remove filter) const + { + switch (filter) + { + case ir::Remove::DUPLICATED: + { + ir::OperandIndexSequence seq; + for (const auto &ind : _vec) + if (!seq.contains(ind)) + seq.append(ind); + return seq; + } + case ir::Remove::UNDEFINED: + { + ir::OperandIndexSequence seq; + for (const auto &ind : _vec) + if (!ind.undefined()) + seq.append(ind); + return seq; + } + } + return *this; + } + +public: + OperandIndexSequence operator+(const OperandIndexSequence &other) const; + friend std::ostream &operator<<(std::ostream &o, const OperandIndexSequence &op_seq); + +public: + std::vector<OperandIndex>::const_iterator begin(void) const { return _vec.begin(); } + std::vector<OperandIndex>::const_iterator end(void) const { return _vec.end(); } + std::vector<OperandIndex>::iterator begin(void) { return _vec.begin(); } + std::vector<OperandIndex>::iterator end(void) { return _vec.end(); } + +private: + std::vector<OperandIndex> _vec; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_MODEL_OPERAND_INDEX_SET_H__ diff --git a/runtime/onert/core/include/ir/OperandInfo.h b/runtime/onert/core/include/ir/OperandInfo.h new file mode 100644 index 000000000..dc89f8726 --- /dev/null +++ b/runtime/onert/core/include/ir/OperandInfo.h @@ -0,0 +1,151 @@ +/* + * 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 OperandInfo.h + * @brief This file contains OperandInfo class + */ +#ifndef __ONERT_IR_OPERAND_INFO_H__ +#define __ONERT_IR_OPERAND_INFO_H__ + +#include "ir/Shape.h" +#include "ir/TypeInfo.h" +#include "ir/Layout.h" + +namespace onert +{ +namespace ir +{ + +/** + * @brief enum class indicating when the memory for a tensor is allocated + */ +enum class MemAllocType +{ + /** + * @brief At compile time, shape for a tensor is known, thus requried memory capacity can be + * calculated + */ + STATIC, + + /** + * @brief At kernel execution time, shape for a tensor is known, thus requried memory capacity + * can be calculated + */ + DYNAMIC +}; + +/** + * @brief Class to save tensor's shape and type + */ +class OperandInfo +{ +public: + /** + * @brief Construct a new OperandInfo object (deleted) + */ + OperandInfo() = delete; + + /** + * @brief Construct a new OperandInfo object + * @param[in] shape Tensor shape + * @param[in] typeInfo Tensor data type + * @param[in] alloc_type When the thesor needs memory allocation + */ + OperandInfo(const Shape &shape, const TypeInfo &typeInfo, MemAllocType alloc_type, + bool is_const = false, bool is_variable = false) + : _shape(shape), _typeInfo(typeInfo), _alloc_type(alloc_type), _const(is_const), + _variable(is_variable) + { + // DO NOTHING + } + /** + * @brief Construct a new OperandInfo object + * @param[in] origin info for copy + */ + OperandInfo(const OperandInfo &origin) = default; + + /** + * @brief Create a static OperandInfo object + */ + static OperandInfo createStaticInfo(const Shape &shape, const TypeInfo &typeInfo) + { + return OperandInfo(shape, typeInfo, MemAllocType::STATIC); + } + +public: + /** + * @brief Return tensor shape + * @return Tensor shape + */ + const Shape &shape() const { return _shape; } + /** + * @brief Return mutable tensor shape + * @return Tensor shape + */ + Shape &shape() { return _shape; } + /** + * @brief set shape + */ + void shape(const ir::Shape &new_shape) { _shape = new_shape; } + /** + * @brief Return tensor data type info + * @return Tensor data type + */ + const TypeInfo &typeInfo() const { return _typeInfo; } + /** + * @brief Set tensor data type + */ + void type(const DataType type) { _typeInfo.type(type); } + /** + * @brief Return size of tensor (bytes) + * @return Tensor size + */ + size_t total_size() const { return _shape.num_elements() * sizeOfDataType(_typeInfo.type()); } + + MemAllocType memAllocType() const { return _alloc_type; } + void setAsConstant() { _const = true; } + void setAsNonConst() { _const = false; } + bool isConstant() const + { + // Impossible case: constant and dynamic operand + assert(!(isDynamic() && _const)); + return _const; + } + void setAsVariable() + { + // Impossible case: constant or dynamic operand + // The variable operand with buffer is not supported yet + assert(!(isDynamic() || _const)); + _variable = true; + } + bool isVariable() const { return _variable; } + bool isDynamic() const { return _alloc_type == MemAllocType::DYNAMIC; } + void setDynamic() { _alloc_type = MemAllocType::DYNAMIC; } + +private: + Shape _shape; + TypeInfo _typeInfo; + + MemAllocType _alloc_type; + bool _const; + bool _variable; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERAND_INFO_H__ diff --git a/runtime/onert/core/include/ir/Operands.h b/runtime/onert/core/include/ir/Operands.h new file mode 100644 index 000000000..be7b7061f --- /dev/null +++ b/runtime/onert/core/include/ir/Operands.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERANDS_H__ +#define __ONERT_IR_OPERANDS_H__ + +#include <memory> +#include <unordered_map> + +#include "ir/Operand.h" +#include "ir/Index.h" +#include "util/ObjectManager.h" + +namespace onert +{ +namespace ir +{ + +class Operands : public util::ObjectManager<OperandIndex, Operand> +{ +public: + Operands() = default; + Operands(const Operands &obj); + Operands(Operands &&) = default; + Operands &operator=(const Operands &) = delete; + Operands &operator=(Operands &&) = default; + ~Operands() = default; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_MODEL_OPERAND_SET_H__ diff --git a/runtime/onert/core/include/ir/Operation.h b/runtime/onert/core/include/ir/Operation.h new file mode 100644 index 000000000..89f7e340d --- /dev/null +++ b/runtime/onert/core/include/ir/Operation.h @@ -0,0 +1,76 @@ +/* + * 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_OPERATION_H__ +#define __ONERT_IR_OPERATION_H__ + +#include <memory> + +#include "ir/OpCode.h" +#include "ir/Operand.h" +#include "ir/OperandIndexSequence.h" +#include "ir/OperandConstraint.h" + +namespace onert +{ +namespace ir +{ + +struct OperationVisitor; + +class Operation +{ +public: + // TODO Remove default parameter + Operation(OperandConstraint input_constr, const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, + OperandConstraint output_constr = OperandConstraint::createAny()); + explicit Operation(OperandConstraint input_constr, + OperandConstraint output_constr = OperandConstraint::createAny()); + + Operation(const Operation &) = default; + Operation(Operation &&) = default; + Operation &operator=(const Operation &) = default; + Operation &operator=(Operation &&) = default; + + virtual ~Operation(); + +public: + virtual void accept(OperationVisitor &v) const = 0; + virtual std::string name() const { return std::string{toString(opcode())}; } + virtual OpCode opcode() const = 0; + +public: + void replaceInputs(const OperandIndex &from, const OperandIndex &to); + void replaceOutputs(const OperandIndex &from, const OperandIndex &to); + OperandIndexSequence &getInputs() { return _inputs; } + const OperandIndexSequence &getInputs() const { return _inputs; } + const OperandIndexSequence &getOutputs() const { return _outputs; } + // It's for only input/output tensors but const data. + void setInputs(const OperandIndexSequence &indexes); + void setOutputs(const OperandIndexSequence &indexes); + +private: + OperandConstraint _input_constr; + OperandConstraint _output_constr; + OperandIndexSequence _inputs; + OperandIndexSequence _outputs; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_H__ diff --git a/runtime/onert/core/include/ir/OperationIndexMap.h b/runtime/onert/core/include/ir/OperationIndexMap.h new file mode 100644 index 000000000..50c21c0ab --- /dev/null +++ b/runtime/onert/core/include/ir/OperationIndexMap.h @@ -0,0 +1,34 @@ +/* + * 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_IR_OPERATION_INDEX_MAP_H__ +#define __ONERT_IR_OPERATION_INDEX_MAP_H__ + +#include <unordered_map> + +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +template <typename T> using OperationIndexMap = std::unordered_map<OperationIndex, T>; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_INDEX_MAP_H__ diff --git a/runtime/onert/core/include/ir/OperationIndexSet.h b/runtime/onert/core/include/ir/OperationIndexSet.h new file mode 100644 index 000000000..067aa19e1 --- /dev/null +++ b/runtime/onert/core/include/ir/OperationIndexSet.h @@ -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. + */ + +#ifndef __ONERT_MODEL_OPERATION_INDEX_SET_H__ +#define __ONERT_MODEL_OPERATION_INDEX_SET_H__ + +#include <algorithm> +#include <cassert> +#include <initializer_list> +#include <unordered_set> + +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ + +class OperationIndexSet +{ +public: + OperationIndexSet(void) = default; + OperationIndexSet(std::initializer_list<OperationIndex> list); + +public: + void insert(const OperationIndex &index) { _set.insert(index); } + void clear(void) { _set.clear(); } + void remove(const OperationIndex &index) + { + auto itr = std::find(_set.begin(), _set.end(), index); + assert(itr != _set.end()); + _set.erase(itr); + } + +public: + uint32_t size() const { return static_cast<uint32_t>(_set.size()); } + bool contains(const OperationIndex &index) const; + +public: + std::unordered_set<OperationIndex>::iterator begin(void) { return _set.begin(); } + std::unordered_set<OperationIndex>::iterator end(void) { return _set.end(); } + std::unordered_set<OperationIndex>::const_iterator begin(void) const { return _set.begin(); } + std::unordered_set<OperationIndex>::const_iterator end(void) const { return _set.end(); } + +private: + std::unordered_set<OperationIndex> _set; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_MODEL_OPERATION_INDEX_SET_H__ diff --git a/runtime/onert/core/include/ir/OperationVisitor.h b/runtime/onert/core/include/ir/OperationVisitor.h new file mode 100644 index 000000000..a27770744 --- /dev/null +++ b/runtime/onert/core/include/ir/OperationVisitor.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_VISITOR_H__ +#define __ONERT_IR_OPERATION_VISITOR_H__ + +#include "ir/Operations.Include.h" +#include "ir/OpSequence.h" + +namespace onert +{ +namespace ir +{ + +struct OperationVisitor +{ + virtual ~OperationVisitor() = default; + +#define OP(InternalName) \ + virtual void visit(const operation::InternalName &) {} +#include "ir/Operations.lst" +#undef OP + + // This OpSequence node should be handled specially so that + // Op.lst doesn't have OpSequence + // TODO Remove by pushing it down to derived classes. + virtual void visit(const OpSequence &) + { + throw std::runtime_error{ + "OperationVisitor: This does not privide visit function in OpSequence"}; + } +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_VISITOR_H__ diff --git a/runtime/onert/core/include/ir/Operations.Include.h b/runtime/onert/core/include/ir/Operations.Include.h new file mode 100644 index 000000000..1f20ee665 --- /dev/null +++ b/runtime/onert/core/include/ir/Operations.Include.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +// This file has no ifdef guard intentionally + +#include "ir/operation/AddN.h" +#include "ir/operation/BatchToSpaceND.h" +#include "ir/operation/BinaryArithmetic.h" +#include "ir/operation/BroadcastTo.h" +#include "ir/operation/Conv2D.h" +#include "ir/operation/Pool2D.h" +#include "ir/operation/Concat.h" +#include "ir/operation/Reshape.h" +#include "ir/operation/Fill.h" +#include "ir/operation/FullyConnected.h" +#include "ir/operation/Softmax.h" +#include "ir/operation/Transpose.h" +#include "ir/operation/Permute.h" +#include "ir/operation/Reduce.h" +#include "ir/operation/DepthwiseConv2D.h" +#include "ir/operation/Slice.h" +#include "ir/operation/StridedSlice.h" +#include "ir/operation/Squeeze.h" +#include "ir/operation/ElementwiseActivation.h" +#include "ir/operation/ElementwiseBinary.h" +#include "ir/operation/ElementwiseUnary.h" +#include "ir/operation/ExpandDims.h" +#include "ir/operation/Comparison.h" +#include "ir/operation/LSTM.h" +#include "ir/operation/ResizeBilinear.h" +#include "ir/operation/ResizeNearestNeighbor.h" +#include "ir/operation/Reverse.h" +#include "ir/operation/RNN.h" +#include "ir/operation/SpaceToBatchND.h" +#include "ir/operation/SpaceToDepth.h" +#include "ir/operation/EmbeddingLookup.h" +#include "ir/operation/L2Normalization.h" +#include "ir/operation/HashtableLookup.h" +#include "ir/operation/InstanceNorm.h" +#include "ir/operation/PReLU.h" +#include "ir/operation/TransposeConv.h" +#include "ir/operation/SquaredDifference.h" +#include "ir/operation/TopKV2.h" +#include "ir/operation/Gather.h" +#include "ir/operation/ArgMax.h" +#include "ir/operation/LocalResponseNormalization.h" +#include "ir/operation/DepthToSpace.h" +#include "ir/operation/Pack.h" +#include "ir/operation/Select.h" +#include "ir/operation/Split.h" +#include "ir/operation/SplitV.h" +#include "ir/operation/Unpack.h" +#include "ir/operation/Pad.h" +#include "ir/operation/Custom.h" +#include "ir/operation/Einsum.h" +#include "ir/operation/OneHot.h" +#include "ir/operation/Shape.h" +#include "ir/operation/ConvertFp32ToFp16.h" +#include "ir/operation/ConvertFp16ToFp32.h" +#include "ir/operation/If.h" +#include "ir/operation/While.h" +#include "ir/operation/Pow.h" +#include "ir/operation/Tile.h" +#include "ir/operation/Range.h" +#include "ir/operation/Rank.h" +#include "ir/operation/BCQFullyConnected.h" +#include "ir/operation/BCQGather.h" +#include "ir/operation/MatrixBandPart.h" +#include "ir/operation/BatchMatMul.h" +#include "ir/operation/FusedBatchNorm.h" +#include "ir/operation/LogSoftmax.h" +#include "ir/operation/StatelessRandomUniform.h" diff --git a/runtime/onert/core/include/ir/Operations.h b/runtime/onert/core/include/ir/Operations.h new file mode 100644 index 000000000..0b5fbf529 --- /dev/null +++ b/runtime/onert/core/include/ir/Operations.h @@ -0,0 +1,43 @@ +/* + * 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_OPERATIONS_H__ +#define __ONERT_IR_OPERATIONS_H__ + +#include "ir/Index.h" +#include "ir/Operation.h" +#include "util/ObjectManager.h" + +namespace onert +{ +namespace ir +{ + +class Operations : public util::ObjectManager<OperationIndex, Operation> +{ +public: + Operations() = default; + Operations(const Operations &obj); + Operations(Operations &&) = default; + Operations &operator=(const Operations &) = delete; + Operations &operator=(Operations &&) = default; + ~Operations() = default; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_MODEL_OPERATION_MANAGER_H__ diff --git a/runtime/onert/core/include/ir/Operations.lst b/runtime/onert/core/include/ir/Operations.lst new file mode 100644 index 000000000..ccde4d179 --- /dev/null +++ b/runtime/onert/core/include/ir/Operations.lst @@ -0,0 +1,88 @@ +/* + * 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 OP +#error Define OP before including this file +#endif + +// Internal Name +OP(AddN) +OP(BatchToSpaceND) +OP(BinaryArithmetic) +OP(BroadcastTo) +OP(Conv2D) +OP(DepthwiseConv2D) +OP(Pool2D) +OP(Concat) +OP(Fill) +OP(FullyConnected) +OP(Reduce) +OP(Reshape) +OP(Softmax) +OP(Squeeze) +OP(Slice) +OP(StridedSlice) +OP(Transpose) +OP(ElementwiseActivation) +OP(ElementwiseBinary) +OP(ElementwiseUnary) +OP(ExpandDims) +OP(Comparison) +OP(LSTM) +OP(ResizeBilinear) +OP(ResizeNearestNeighbor) +OP(Reverse) +OP(RNN) +OP(SpaceToBatchND) +OP(SpaceToDepth) +OP(EmbeddingLookup) +OP(L2Normalization) +OP(HashtableLookup) +OP(InstanceNorm) +OP(PReLU) +OP(TransposeConv) +OP(SquaredDifference) +OP(TopKV2) +OP(Gather) +OP(ArgMax) +OP(Einsum) +OP(LocalResponseNormalization) +OP(DepthToSpace) +OP(Pack) +OP(Select) +OP(Split) +OP(SplitV) +OP(Unpack) +OP(Pad) +OP(Custom) +OP(Permute) +OP(OneHot) +OP(Shape) +OP(ConvertFp32ToFp16) +OP(ConvertFp16ToFp32) +OP(If) +OP(While) +OP(Pow) +OP(Tile) +OP(Range) +OP(Rank) +OP(BCQFullyConnected) +OP(BCQGather) +OP(MatrixBandPart) +OP(BatchMatMul) +OP(FusedBatchNorm) +OP(LogSoftmax) +OP(StatelessRandomUniform) diff --git a/runtime/onert/core/include/ir/Padding.h b/runtime/onert/core/include/ir/Padding.h new file mode 100644 index 000000000..8a7bcdbeb --- /dev/null +++ b/runtime/onert/core/include/ir/Padding.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_PADDIGN_H__ +#define __ONERT_IR_PADDIGN_H__ + +#include "Shape.h" +#include "InternalType.h" + +#include <cstdint> +#include <string> + +namespace onert +{ +namespace ir +{ + +enum class PaddingType +{ + EXPLICIT = 0, + SAME = 1, + VALID = 2 +}; + +/** + * @brief Converts a internal padding type to const char* + * @param[in] type Padding type to be converted + * @return A string holding the converted value + */ +inline std::string to_string(const PaddingType type); + +struct ExplicitPadding +{ + uint32_t left; + uint32_t right; + uint32_t top; + uint32_t bottom; +}; + +// TODO Resolve explicit padding param at frontend and save in value field +struct Padding +{ + Padding(void); + Padding(PaddingType paddingType); + Padding(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom); + + // TODO Change to private field + PaddingType type; + ExplicitPadding param; +}; + +// TODO Change to Padding struct's method +const ExplicitPadding calculatePadding(const Padding &padding, const FeatureShape &ifm_shape, + const FeatureShape &ofm_shape, const Stride &stride, + uint32_t kw, uint32_t kh, uint32_t dwf = 1, + uint32_t dhf = 1); + +} // namespace ir +} // namespace onert + +#endif diff --git a/runtime/onert/core/include/ir/Shape.h b/runtime/onert/core/include/ir/Shape.h new file mode 100644 index 000000000..a0b4bb196 --- /dev/null +++ b/runtime/onert/core/include/ir/Shape.h @@ -0,0 +1,151 @@ +/* + * 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_SHAPE_H__ +#define __ONERT_IR_SHAPE_H__ + +#include "ir/Layout.h" + +#include <cassert> +#include <cstdint> +#include <vector> +#include <algorithm> + +namespace onert +{ +namespace ir +{ + +/** + * @brief Structure to have values of dimensions for feature + */ +struct FeatureShape +{ + int32_t N; /**< The batch value */ + int32_t C; /**< The depth value */ + int32_t H; /**< The height value */ + int32_t W; /**< The width value */ + + /** + * @brief Construct FeatureShape object using default constrcutor + */ + FeatureShape() = default; + /** + * @brief Construct FeatureShape object with three values of dimensions + * @param[in] depth The depth value + * @param[in] height The height value + * @param[in] width The width value + */ + FeatureShape(int32_t depth, int32_t height, int32_t width) : N{1}, C{depth}, H{height}, W{width} + { + // DO NOTHING + } + /** + * @brief Construct FeatureShape object with four values of dimensions + * @param[in] batch The batch value + * @param[in] depth The depth value + * @param[in] height The height value + * @param[in] width The width value + */ + FeatureShape(int32_t batch, int32_t depth, int32_t height, int32_t width) + : N{batch}, C{depth}, H{height}, W{width} + { + // DO NOTHING + } +}; + +struct Shape +{ +public: + static int32_t const UNSPECIFIED_DIM; + static int32_t const MAX_RANK; + + Shape() = default; + + explicit Shape(int rank) : _dimensions(rank) {} + + Shape(std::initializer_list<int32_t> dimensions) : _dimensions(dimensions) {} + + int rank() const { return _dimensions.size(); } + + const std::vector<int32_t> &dims() const { return _dimensions; } + + int32_t dim(int i) const + { + assert(rank() != 0 || i == 0); + return rank() == 0 ? 1 : _dimensions.at(i); + } + + int32_t &dim(int i) { return _dimensions.at(i); } + + /** + * @brief Returns number of elements when rank or dim is specified + */ + uint64_t num_elements() const; + +public: + FeatureShape asFeature(Layout layout) const; + + /** + * @brief Add dimension to the beginning + * @param[in] d dimension to add to the beginning + */ + void prepend(int32_t d) { _dimensions.insert(_dimensions.cbegin(), d); } + + /** + * @brief Add dimension to the end + * @param[in] d dimension to add to the end + */ + void append(int32_t d) { _dimensions.emplace_back(d); } + + /** + * @brief Extend rank of Shape object for operand with param. + * @param[in] to_rank The rank value to be extended to + */ + void extendRank(int to_rank); + + /** + * @brief Find out if any dimension is unspecified. If the rank is not specified, it returns + * false. + * \see https://developer.android.com/ndk/reference/struct/a-neural-networks-operand-type + * @note base_loader set dim to -1 when there is unknown dim in input tensor + */ + bool hasUnspecifiedDims() const + { + return (std::find(_dimensions.begin(), _dimensions.end(), UNSPECIFIED_DIM) != + _dimensions.end()); + } + +private: + std::vector<int32_t> _dimensions; +}; + +inline bool operator==(const Shape &lhs, const Shape &rhs) { return lhs.dims() == rhs.dims(); } +inline bool operator!=(const Shape &lhs, const Shape &rhs) { return lhs.dims() != rhs.dims(); } + +Shape permuteShape(const Shape &shape, Layout frontend_layout, Layout backend_layout); + +/** +* @brief Find out if tha rank in this shape is "maybe" unspecified. +* Note that when rank == 0, shape could represent scalar or unspecified rank +* \see https://developer.android.com/ndk/reference/struct/a-neural-networks-operand-type +*/ +inline bool rankMaybeUnspecified(const ir::Shape &shape) { return (shape.rank() == 0); } + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_SHAPE_H__ diff --git a/runtime/onert/core/include/ir/Sparsity.h b/runtime/onert/core/include/ir/Sparsity.h new file mode 100644 index 000000000..ad4d8259b --- /dev/null +++ b/runtime/onert/core/include/ir/Sparsity.h @@ -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. +*/ + +#ifndef __ONERT_IR_SPARSITY_H__ +#define __ONERT_IR_SPARSITY_H__ + +#include <cassert> +#include <cstdint> +#include <vector> + +namespace onert +{ +namespace ir +{ + +/** + * @brief Structure for Sparse Tensor + */ +struct Sparsity +{ +public: + Sparsity() = default; + Sparsity(std::vector<uint16_t> &&w1_segments, std::vector<uint16_t> &&w1_indices, + std::vector<int32_t> &&block_size) + : _w1_segments(w1_segments), _w1_indices(w1_indices), _block_size(block_size) + { + } + + /** + * @brief Returns segments array. See compressed sparse row format. + */ + const uint16_t *w1_segments() const { return _w1_segments.data(); } + /** + * @brief Returns indices array. See compressed sparse row format. + */ + const uint16_t *w1_indices() const { return _w1_indices.data(); } + /** + * @brief Returns block size which is used for block sparsity + */ + const std::vector<int32_t> &block_size() const { return _block_size; } + +private: + std::vector<uint16_t> _w1_segments; + std::vector<uint16_t> _w1_indices; + std::vector<int32_t> _block_size; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_SPARSITY_H__ diff --git a/runtime/onert/core/include/ir/Subgraphs.h b/runtime/onert/core/include/ir/Subgraphs.h new file mode 100644 index 000000000..7b4c33b76 --- /dev/null +++ b/runtime/onert/core/include/ir/Subgraphs.h @@ -0,0 +1,139 @@ +/* + * 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_SUBGRAPHS_H__ +#define __ONERT_IR_SUBGRAPHS_H__ + +#include <memory> +#include <unordered_map> + +#include "ir/Index.h" +#include "util/ObjectManager.h" + +namespace onert +{ +namespace ir +{ + +class Graph; + +class Subgraphs +{ +public: + Subgraphs() = default; + Subgraphs(const Subgraphs &obj) = default; + Subgraphs(Subgraphs &&) = default; + Subgraphs &operator=(const Subgraphs &) = default; + Subgraphs &operator=(Subgraphs &&) = default; + ~Subgraphs() = default; + + /** + * @brief Put subgraph in the container with a new Index for that + * + * @param[in] subg Subgraph to be pushed + * @param[in] index Index of subgraph to be pushed + * @return Created + */ + void push(SubgraphIndex index, const std::shared_ptr<Graph> &subg) { _subgraphs[index] = subg; } + + /** + * @brief Remove the subgraph that is associated with the given index + * + * @param[in] index Index of the subgraph to be removed + * @return N/A + */ + void remove(const SubgraphIndex &index) { _subgraphs.erase(index); } + + /** + * @brief Get the subgraph that is associated with the given index + * + * @param[in] index Index of the subgraph to be returned + * @return Graph + */ + const std::shared_ptr<Graph> &at(const SubgraphIndex &index) const + { + return _subgraphs.at(index); + } + /** + * @brief Get the subgraph that is associated with the given index + * + * @param[in] index Index of the subgraph to be returned + * @return Graph + */ + std::shared_ptr<Graph> &at(const SubgraphIndex &index) { return _subgraphs.at(index); } + + /** + * @brief Get the subgraph that is associated with the given index + * + * @param[in] index Index of the subgraph to be returned + * @return true if such entry exists otherwise false + */ + bool exist(const SubgraphIndex &index) const + { + auto it = _subgraphs.find(index); + return it != _subgraphs.end(); + } + + /** + * @brief Iterate over the container with given function + * + * @param[in] fn Function to be run for every container entry + * @return N/A + */ + void iterate(const std::function<void(const SubgraphIndex &, const Graph &)> &fn) const + { + for (const auto &e : _subgraphs) + { + fn(e.first, *e.second); + } + } + + /** + * @brief Iterate over the container with given function + * + * @param[in] fn Function to be run for every container entry + * @return N/A + */ + void iterate(const std::function<void(const SubgraphIndex &, Graph &)> &fn) + { + for (const auto &e : _subgraphs) + { + fn(e.first, *e.second); + } + } + + /** + * @brief Get count of Subgraphs + * + * @return count of Subgraphs + */ + size_t count() { return _subgraphs.size(); } + + /** + * @brief Return the primary subgraph + * + * @return std::shared_ptr<Graph> Primary sugraph + */ + std::shared_ptr<Graph> primary() const { return _subgraphs.at(SubgraphIndex{0}); } + +private: + std::unordered_map<SubgraphIndex, std::shared_ptr<Graph>> _subgraphs; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_SUBGRAPHS_H__ diff --git a/runtime/onert/core/include/ir/TypeInfo.h b/runtime/onert/core/include/ir/TypeInfo.h new file mode 100644 index 000000000..a1ae4d2e4 --- /dev/null +++ b/runtime/onert/core/include/ir/TypeInfo.h @@ -0,0 +1,67 @@ +/* + * 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_TYPEINFO_H__ +#define __ONERT_IR_TYPEINFO_H__ + +#include <cstdint> +#include <memory> +#include <vector> + +#include "ir/DataType.h" +#include "ir/Sparsity.h" + +namespace onert +{ +namespace ir +{ + +class TypeInfo +{ +public: + TypeInfo() = delete; + + explicit TypeInfo(DataType type, float scale = 0, int32_t offset = 0) + : _type(type), _scale(scale), _offset(offset), _sparsity(nullptr) + { + } + +public: + DataType type() const { return _type; } + float scale() const { return _scale; } + int32_t offset() const { return _offset; } + const ir::Sparsity *sparsity() const { return _sparsity.get(); } + void sparsity(std::shared_ptr<ir::Sparsity> sparsity) { _sparsity = sparsity; } + +public: + void type(const DataType type) { _type = type; } + +private: + DataType _type; + // for quantization + float _scale; + int32_t _offset; + // for sparsity + std::shared_ptr<ir::Sparsity> _sparsity; +}; + +bool operator==(const TypeInfo &lhs, const TypeInfo &rhs); +bool operator!=(const TypeInfo &lhs, const TypeInfo &rhs); + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_TYPEINFO_H__ diff --git a/runtime/onert/core/include/ir/operand/LowerInfo.h b/runtime/onert/core/include/ir/operand/LowerInfo.h new file mode 100644 index 000000000..b7f032b02 --- /dev/null +++ b/runtime/onert/core/include/ir/operand/LowerInfo.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. + */ + +#ifndef __ONERT_IR_OPERAND_LOWER_INFO_H__ +#define __ONERT_IR_OPERAND_LOWER_INFO_H__ + +#include <functional> +#include <stdint.h> + +#include "ir/operand/PermuteFactor.h" +#include "util/Set.h" + +namespace onert +{ +namespace backend +{ +class Backend; +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace operand +{ +using PermuteFactorSet = util::Set<PermuteFactor>; + +class LowerInfo +{ +public: + LowerInfo() + { + // DO NOTHING + } + +public: + const PermuteFactorSet &def_factors(void) const { return _def_factors; } + const PermuteFactorSet &use_factors(void) const { return _use_factors; } + +public: + void addDefPermuteFactor(const PermuteFactor &factor) { _def_factors.add(factor); } + void addUsePermuteFactor(const PermuteFactor &factor) { _use_factors.add(factor); } + void removeDefPermuteFactor(const PermuteFactor &factor) { _def_factors.remove(factor); } + void removeUsePermuteFactor(const PermuteFactor &factor) { _use_factors.remove(factor); } + +private: + PermuteFactorSet _def_factors; + PermuteFactorSet _use_factors; +}; + +} // namespace operand +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERAND_LOWER_INFO_H__ diff --git a/runtime/onert/core/include/ir/operand/PermuteFactor.h b/runtime/onert/core/include/ir/operand/PermuteFactor.h new file mode 100644 index 000000000..d0bfed337 --- /dev/null +++ b/runtime/onert/core/include/ir/operand/PermuteFactor.h @@ -0,0 +1,130 @@ +/* + * 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 PermuteFactor.h + * @brief This file contains onert::ir::operand::PermuteFactor class + * @ingroup COM_AI_RUNTIME + */ + +#ifndef __ONERT_IR_OPERAND_PERMUTE_FACTOR_H__ +#define __ONERT_IR_OPERAND_PERMUTE_FACTOR_H__ + +#include <functional> + +#include "ir/Layout.h" + +namespace onert +{ +namespace backend +{ +class Backend; +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace operand +{ + +/** + * @brief Class that has factors of permutation + */ +class PermuteFactor +{ +public: + /** + * @brief Construct PermuteFactor object. + * @param backend The backend factor + * @param layout The layout factor + */ + PermuteFactor(const backend::Backend *backend, Layout layout) : _backend{backend}, _layout{layout} + { + // DO NOTHING + } + /** + * @brief Construct PermuteFactor object by copy semantics. + */ + PermuteFactor(const PermuteFactor &f) : _backend{f._backend}, _layout{f._layout} + { + // DO NOTHING + } + /** + * @brief Construct PermuteFactor object by move semantics. + */ + PermuteFactor(PermuteFactor &&) = default; + +public: + /** + * @brief Get backend + * + * @return Backend factor + */ + const backend::Backend *backend() const { return _backend; } + /** + * @brief Get layout + * + * @return Layout factor + */ + Layout layout() const { return _layout; } + +public: + /** + * @brief operator overloading function for `==` + * + * @return Whether two PermuteFactor are the same + */ + bool operator==(const PermuteFactor &other) const + { + return _backend == other.backend() && _layout == other.layout(); + } + /** + * @brief operator overloading function for `!=` + * + * @return Whether two PermuteFactor are differenct + */ + bool operator!=(const PermuteFactor &other) const { return !(*this == other); } + +private: + const backend::Backend *_backend{nullptr}; + Layout _layout{Layout::UNKNOWN}; +}; + +} // namespace operand +} // namespace ir +} // namespace onert + +namespace std +{ + +/** + * @brief Structure that provides hash value of PermuteFactor + */ +template <> struct hash<onert::ir::operand::PermuteFactor> +{ + size_t operator()(const onert::ir::operand::PermuteFactor &factor) const noexcept + { + hash<const onert::backend::Backend *> b_hash{}; + hash<onert::ir::Layout> l_hash{}; + return b_hash(factor.backend()) ^ (l_hash(factor.layout()) << 1); + } +}; + +} // namespace std + +#endif // __ONERT_IR_OPERAND_PERMUTE_FACTOR_H__ diff --git a/runtime/onert/core/include/ir/operation/AddN.h b/runtime/onert/core/include/ir/operation/AddN.h new file mode 100644 index 000000000..7a307efa5 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/AddN.h @@ -0,0 +1,43 @@ +/* + * 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_ADDN_H__ +#define __ONERT_IR_OPERATION_ADDN_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class AddN : public Operation +{ +public: + AddN(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::AddN; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ADDN_H__ diff --git a/runtime/onert/core/include/ir/operation/ArgMax.h b/runtime/onert/core/include/ir/operation/ArgMax.h new file mode 100644 index 000000000..ea7eabb83 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ArgMax.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_IR_OPERATION_ARG_MAX_H__ +#define __ONERT_IR_OPERATION_ARG_MAX_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ArgMax : public Operation +{ +public: + enum Input + { + INPUT = 0, + AXIS = 1 + }; + + struct Param + { + DataType output_type; + }; + +public: + ArgMax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ArgMax; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ARG_MAX_H__ diff --git a/runtime/onert/core/include/ir/operation/BCQFullyConnected.h b/runtime/onert/core/include/ir/operation/BCQFullyConnected.h new file mode 100644 index 000000000..4bf3a0bdb --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BCQFullyConnected.h @@ -0,0 +1,67 @@ +/* + * 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_BCQFULLYCONNECTED_H__ +#define __ONERT_IR_OPERATION_BCQFULLYCONNECTED_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BCQFullyConnected : public Operation +{ +public: + enum Input + { + INPUT = 0, + WEIGHTS_SCALES, + WEIGHTS_BINARY, + BIAS, + WEIGHTS_CLUSTERS, + }; + + struct Param + { + uint32_t weights_hidden_size; + Activation activation; + }; + +public: + BCQFullyConnected(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::BCQFullyConnected; } + + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BCQFULLYCONNECTED_H__ diff --git a/runtime/onert/core/include/ir/operation/BCQGather.h b/runtime/onert/core/include/ir/operation/BCQGather.h new file mode 100644 index 000000000..1349b8c6d --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BCQGather.h @@ -0,0 +1,66 @@ +/* + * 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_BCQGATHER_H__ +#define __ONERT_IR_OPERATION_BCQGATHER_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BCQGather : public Operation +{ +public: + enum Input + { + INPUT_SCALES = 0, + INPUT_BINARY, + INDICES, + INPUT_CLUSTERS, + }; + + struct Param + { + uint32_t input_hidden_size; + uint32_t axis; + }; + +public: + BCQGather(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::BCQGather; } + + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BCQGATHER_H__ diff --git a/runtime/onert/core/include/ir/operation/BatchMatMul.h b/runtime/onert/core/include/ir/operation/BatchMatMul.h new file mode 100644 index 000000000..183f60abe --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BatchMatMul.h @@ -0,0 +1,63 @@ +/* + * 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_BATCH_MATMUL_H__ +#define __ONERT_IR_OPERATION_BATCH_MATMUL_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BatchMatMul : public Operation +{ +public: + enum Input + { + LHS = 0, + RHS + }; + + struct Param + { + bool adj_x; + bool adj_y; + }; + +public: + BatchMatMul(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::BatchMatMul; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BATCH_MATMUL_H__ diff --git a/runtime/onert/core/include/ir/operation/BatchToSpaceND.h b/runtime/onert/core/include/ir/operation/BatchToSpaceND.h new file mode 100644 index 000000000..3e69b42c7 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BatchToSpaceND.h @@ -0,0 +1,51 @@ +/* + * 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_IR_OPERATION_BATCH_TO_SPACE_ND_H__ +#define __ONERT_IR_OPERATION_BATCH_TO_SPACE_ND_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BatchToSpaceND : public Operation +{ +public: + enum Input + { + INPUT = 0, + BLOCK_SIZE = 1, + CROPS_DATA = 2 + }; + +public: + BatchToSpaceND(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::BatchToSpaceND; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BATCH_TO_SPACE_ND_H__ diff --git a/runtime/onert/core/include/ir/operation/BinaryArithmetic.h b/runtime/onert/core/include/ir/operation/BinaryArithmetic.h new file mode 100644 index 000000000..110fff565 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BinaryArithmetic.h @@ -0,0 +1,73 @@ +/* + * 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_BINARY_ARITHMETIC_H__ +#define __ONERT_IR_OPERATION_BINARY_ARITHMETIC_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BinaryArithmetic final : public Operation +{ +public: + enum Input + { + LHS = 0, + RHS + }; + + enum class ArithmeticType + { + ADD, + SUB, + MUL, + DIV + }; + + struct Param + { + ArithmeticType arithmetic_type; + Activation activation; + }; + +public: + BinaryArithmetic(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::BinaryArithmetic; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BINARY_ARITHMETIC_H__ diff --git a/runtime/onert/core/include/ir/operation/BroadcastTo.h b/runtime/onert/core/include/ir/operation/BroadcastTo.h new file mode 100644 index 000000000..06c033497 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/BroadcastTo.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_OPERATION_BROADCAST_TO_H__ +#define __ONERT_IR_OPERATION_BROADCAST_TO_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class BroadcastTo : public Operation +{ +public: + enum Input + { + INPUT = 0, + SHAPE = 1 + }; + +public: + BroadcastTo(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::BroadcastTo; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_BROADCAST_TO_H__ diff --git a/runtime/onert/core/include/ir/operation/Comparison.h b/runtime/onert/core/include/ir/operation/Comparison.h new file mode 100644 index 000000000..8b53f163b --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Comparison.h @@ -0,0 +1,72 @@ +/* + * 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_COMPARISON_H__ +#define __ONERT_IR_OPERATION_COMPARISON_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Comparison : public Operation +{ +public: + enum Input + { + INPUT0 = 0, + INPUT1 + }; + + enum class ComparisonType + { + Equal, + NotEqual, + Greater, + GreaterEqual, + Less, + LessEqual + }; + + struct Param + { + ComparisonType comparison_type; + }; + +public: + Comparison(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Comparison; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_COMPARISON_H__ diff --git a/runtime/onert/core/include/ir/operation/Concat.h b/runtime/onert/core/include/ir/operation/Concat.h new file mode 100644 index 000000000..2dff04e93 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Concat.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_OPERATION_CONCAT_H__ +#define __ONERT_IR_OPERATION_CONCAT_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Concat : public Operation +{ +public: + struct Param + { + int32_t axis; + }; + +public: + Concat(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Concat; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_CONCAT_H__ diff --git a/runtime/onert/core/include/ir/operation/Conv2D.h b/runtime/onert/core/include/ir/operation/Conv2D.h new file mode 100644 index 000000000..d8c7b671b --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Conv2D.h @@ -0,0 +1,70 @@ +/* + * 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_OPERATION_CONV2D_H__ +#define __ONERT_IR_OPERATION_CONV2D_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" +#include "ir/Padding.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Conv2D : public Operation +{ +public: + enum Input + { + INPUT = 0, + KERNEL, + BIAS + }; + + struct Param + { + Stride stride; + Padding padding; + Activation activation; + Dilation dilation; + }; + +public: + Conv2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Conv2D; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_CONV2D_H__ diff --git a/runtime/onert/core/include/ir/operation/ConvertFp16ToFp32.h b/runtime/onert/core/include/ir/operation/ConvertFp16ToFp32.h new file mode 100644 index 000000000..15c48357f --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ConvertFp16ToFp32.h @@ -0,0 +1,49 @@ +/* + * 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_CONVERT_FP16_TO_FP32_H__ +#define __ONERT_IR_OPERATION_CONVERT_FP16_TO_FP32_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ConvertFp16ToFp32 : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + +public: + ConvertFp16ToFp32(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ConvertFp16ToFp32; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_CONVERT_FP16_TO_FP32_H__ diff --git a/runtime/onert/core/include/ir/operation/ConvertFp32ToFp16.h b/runtime/onert/core/include/ir/operation/ConvertFp32ToFp16.h new file mode 100644 index 000000000..983ce4891 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ConvertFp32ToFp16.h @@ -0,0 +1,49 @@ +/* + * 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_CONVERT_FP32_TO_FP16_H__ +#define __ONERT_IR_OPERATION_CONVERT_FP32_TO_FP16_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ConvertFp32ToFp16 : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + +public: + ConvertFp32ToFp16(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ConvertFp32ToFp16; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_CONVERT_FP32_TO_FP16_H__ diff --git a/runtime/onert/core/include/ir/operation/Custom.h b/runtime/onert/core/include/ir/operation/Custom.h new file mode 100644 index 000000000..c2a4b354a --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Custom.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_IR_OPERATION_CUSTOM_H__ +#define __ONERT_IR_OPERATION_CUSTOM_H__ + +#include "ir/Operation.h" + +#include <cstring> + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Custom : public Operation +{ +public: + struct Userdata + { + char *data; + size_t size; + + Userdata() : data{nullptr}, size{0} {} + Userdata(const Userdata &o) + { + size = o.size; + data = new char[size]; + std::memcpy(data, o.data, size); + } + ~Userdata() { delete[] data; } + }; + + Custom(OperandConstraint input_constr, const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, std::string id, const Userdata &userdata); + + void accept(OperationVisitor &v) const override; + +public: + /** + * @return unique operation identifier + */ + const std::string &id() const; + + std::string name() const override; + OpCode opcode() const final { return OpCode::Custom; } + + /** + * @return user-provided data + */ + const Userdata &userdata() const; + +private: + std::string _id; + Userdata _userdata; +}; + +} // namespace operation +} // namespace ir +} // namespace onert +#endif // __ONERT_IR_OPERATION_CUSTOM_H__ diff --git a/runtime/onert/core/include/ir/operation/DepthToSpace.h b/runtime/onert/core/include/ir/operation/DepthToSpace.h new file mode 100644 index 000000000..a5315051d --- /dev/null +++ b/runtime/onert/core/include/ir/operation/DepthToSpace.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_DEPTH_TO_SPACE_H__ +#define __ONERT_IR_OPERATION_DEPTH_TO_SPACE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class DepthToSpace : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + std::int32_t block_size; + }; + +public: + DepthToSpace(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::DepthToSpace; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_DEPTH_TO_SPACE_H__ diff --git a/runtime/onert/core/include/ir/operation/DepthwiseConv2D.h b/runtime/onert/core/include/ir/operation/DepthwiseConv2D.h new file mode 100644 index 000000000..38e2b5cd6 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/DepthwiseConv2D.h @@ -0,0 +1,71 @@ +/* + * 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_IR_OPERATION_DEPTHWISECONV2D_H__ +#define __ONERT_IR_OPERATION_DEPTHWISECONV2D_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" +#include "ir/Padding.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class DepthwiseConv2D : public Operation +{ +public: + enum Input + { + INPUT = 0, + KERNEL, + BIAS + }; + + struct Param + { + Stride stride; + Padding padding; + uint32_t multiplier; + Activation activation; + Dilation dilation; + }; + +public: + DepthwiseConv2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::DepthwiseConv2D; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_DEPTHWISECONV2D_H__ diff --git a/runtime/onert/core/include/ir/operation/Einsum.h b/runtime/onert/core/include/ir/operation/Einsum.h new file mode 100644 index 000000000..9892c24b8 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Einsum.h @@ -0,0 +1,57 @@ +/* + * 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_EINSUM_H__ +#define __ONERT_IR_OPERATION_EINSUM_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Einsum : public Operation +{ +public: + struct Param + { + std::string equation; + }; + +public: + Einsum(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Einsum; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_EINSUM_H__ diff --git a/runtime/onert/core/include/ir/operation/ElementwiseActivation.h b/runtime/onert/core/include/ir/operation/ElementwiseActivation.h new file mode 100644 index 000000000..b2a1d3d2d --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ElementwiseActivation.h @@ -0,0 +1,77 @@ +/* + * 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_ELEMENTWISE_ACTIVATION_H__ +#define __ONERT_IR_OPERATION_ELEMENTWISE_ACTIVATION_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ElementwiseActivation : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + enum class Type + { + ELU, + LOGISTIC, + RELU, + TANH, + LEAKY_RELU + }; + + struct Param + { + Type op_type; + float alpha; + float beta; + Param() : op_type(Type::ELU), alpha(0.0f), beta(0.0f) {} + }; + +public: + ElementwiseActivation(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::ElementwiseActivation; } + +public: + const Param ¶m() const { return _param; } + +public: + static float infinity; + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ELEMENTWISE_ACTIVATION_H__ diff --git a/runtime/onert/core/include/ir/operation/ElementwiseBinary.h b/runtime/onert/core/include/ir/operation/ElementwiseBinary.h new file mode 100644 index 000000000..dd07f6058 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ElementwiseBinary.h @@ -0,0 +1,71 @@ +/* + * 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_ELEMENTWISEBINARY_H__ +#define __ONERT_IR_OPERATION_ELEMENTWISEBINARY_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ElementwiseBinary : public Operation +{ +public: + enum Input + { + LHS = 0, + RHS + }; + + enum class ElementwiseBinaryType + { + LOGICAL_AND, + LOGICAL_OR, + MAX, + MIN + }; + + struct Param + { + ElementwiseBinaryType op_type; + }; + +public: + ElementwiseBinary(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::ElementwiseBinary; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ELEMENTWISEBINARY_H__ diff --git a/runtime/onert/core/include/ir/operation/ElementwiseUnary.h b/runtime/onert/core/include/ir/operation/ElementwiseUnary.h new file mode 100644 index 000000000..c40778a56 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ElementwiseUnary.h @@ -0,0 +1,83 @@ +/* + * 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_ELEMENTWISEUNARY_H__ +#define __ONERT_IR_OPERATION_ELEMENTWISEUNARY_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ElementwiseUnary : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + enum class Type + { + ABS, + CAST, + COS, + DEQUANTIZE, + ERF, + EXP, + FLOOR, + LOG, + LOGICAL_NOT, + NEG, + QUANTIZE, + ROUND, + RSQRT, + SIN, + SQRT, + SQURE, + ZEROS_LIKE + }; + + struct Param + { + Type op_type; + }; + +public: + ElementwiseUnary(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::ElementwiseUnary; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ELEMENTWISEUNARY_H__ diff --git a/runtime/onert/core/include/ir/operation/EmbeddingLookup.h b/runtime/onert/core/include/ir/operation/EmbeddingLookup.h new file mode 100644 index 000000000..54064faf0 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/EmbeddingLookup.h @@ -0,0 +1,50 @@ +/* + * 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_IR_OPERATION_EMBEDDING_LOOKUP_H__ +#define __ONERT_IR_OPERATION_EMBEDDING_LOOKUP_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class EmbeddingLookup : public Operation +{ +public: + enum Input + { + LOOKUPS = 0, + VALUES = 1 + }; + +public: + EmbeddingLookup(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::EmbeddingLookup; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_EMBEDDING_LOOKUP_H__ diff --git a/runtime/onert/core/include/ir/operation/ExpandDims.h b/runtime/onert/core/include/ir/operation/ExpandDims.h new file mode 100644 index 000000000..09669a40b --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ExpandDims.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_OPERATION_EXPANDDIMS_H__ +#define __ONERT_IR_OPERATION_EXPANDDIMS_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ExpandDims : public Operation +{ +public: + enum Input + { + INPUT = 0, + AXIS = 1 + }; + +public: + ExpandDims(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ExpandDims; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_EXPANDDIMS_H__ diff --git a/runtime/onert/core/include/ir/operation/Fill.h b/runtime/onert/core/include/ir/operation/Fill.h new file mode 100644 index 000000000..524e41385 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Fill.h @@ -0,0 +1,50 @@ +/* + * 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_FILL_H__ +#define __ONERT_IR_OPERATION_FILL_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Fill : public Operation +{ +public: + enum Input + { + INPUT = 0, + VALUE, + }; + +public: + Fill(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Fill; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_FILL_H__ diff --git a/runtime/onert/core/include/ir/operation/FullyConnected.h b/runtime/onert/core/include/ir/operation/FullyConnected.h new file mode 100644 index 000000000..f83a64557 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/FullyConnected.h @@ -0,0 +1,67 @@ +/* + * 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_OPERATION_FULLYCONNECTED_H__ +#define __ONERT_IR_OPERATION_FULLYCONNECTED_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class FullyConnected : public Operation +{ +public: + enum Input + { + INPUT = 0, + WEIGHT, + BIAS + }; + + struct Param + { + Activation activation; + FullyConnectedWeightsFormat weights_format; + }; + +public: + FullyConnected(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::FullyConnected; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_FULLYCONNECTED_H__ diff --git a/runtime/onert/core/include/ir/operation/FusedBatchNorm.h b/runtime/onert/core/include/ir/operation/FusedBatchNorm.h new file mode 100644 index 000000000..989ee2b98 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/FusedBatchNorm.h @@ -0,0 +1,68 @@ +/* + * 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_FUSEDBATCHNORM_H__ +#define __ONERT_IR_OPERATION_FUSEDBATCHNORM_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class FusedBatchNorm : public Operation +{ +public: + enum Input + { + INPUT = 0, + SCALE, + OFFSET, + MEAN, + VARIANCE + }; + + struct Param + { + bool is_training; + std::string data_format; + float epsilon; + }; + +public: + FusedBatchNorm(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::FusedBatchNorm; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_FUSEDBATCHNORM_H__ diff --git a/runtime/onert/core/include/ir/operation/Gather.h b/runtime/onert/core/include/ir/operation/Gather.h new file mode 100644 index 000000000..544eb3b19 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Gather.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. + */ + +#ifndef __ONERT_IR_OPERATION_GATHER_H__ +#define __ONERT_IR_OPERATION_GATHER_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Gather : public Operation +{ +public: + enum Input + { + INPUT = 0, + INDICES, + }; + + struct Param + { + int32_t axis; + }; + +public: + Gather(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Gather; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_GATHER_H__ diff --git a/runtime/onert/core/include/ir/operation/HashtableLookup.h b/runtime/onert/core/include/ir/operation/HashtableLookup.h new file mode 100644 index 000000000..4b6cf9362 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/HashtableLookup.h @@ -0,0 +1,57 @@ +/* + * 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_IR_OPERATION_HASHTABLE_LOOKUP_H__ +#define __ONERT_IR_OPERATION_HASHTABLE_LOOKUP_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class HashtableLookup : public Operation +{ +public: + enum Input + { + LOOKUPS = 0, + KEYS = 1, + VALUES = 2 + }; + + enum Output + { + OUTPUT = 0, + HITS = 1 + }; + +public: + HashtableLookup(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::HashtableLookup; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_HASHTABLE_LOOKUP_H__ diff --git a/runtime/onert/core/include/ir/operation/If.h b/runtime/onert/core/include/ir/operation/If.h new file mode 100644 index 000000000..41cd4e239 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/If.h @@ -0,0 +1,57 @@ +/* + * 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_IF_H__ +#define __ONERT_IR_OPERATION_IF_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class If : public Operation +{ +public: + struct Param + { + SubgraphIndex then_subg_index; + SubgraphIndex else_subg_index; + }; + +public: + If(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::If; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_IF_H__ diff --git a/runtime/onert/core/include/ir/operation/InstanceNorm.h b/runtime/onert/core/include/ir/operation/InstanceNorm.h new file mode 100644 index 000000000..6a3bb5189 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/InstanceNorm.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_INSTANCE_NORM_H__ +#define __ONERT_IR_OPERATION_INSTANCE_NORM_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class InstanceNorm : public Operation +{ +public: + enum Input + { + INPUT = 0, + GAMMA, + BETA + }; + + struct Param + { + Activation activation; + float epsilon; + }; + +public: + InstanceNorm(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::InstanceNorm; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_INSTANCE_NORM_H__ diff --git a/runtime/onert/core/include/ir/operation/L2Normalization.h b/runtime/onert/core/include/ir/operation/L2Normalization.h new file mode 100644 index 000000000..abbd68c97 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/L2Normalization.h @@ -0,0 +1,49 @@ +/* + * 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_IR_OPERATION_L2_NORMALIZATION_H__ +#define __ONERT_IR_OPERATION_L2_NORMALIZATION_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class L2Normalization : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + +public: + L2Normalization(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::L2Normalization; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_L2_NORMALIZATION_H__ diff --git a/runtime/onert/core/include/ir/operation/LSTM.h b/runtime/onert/core/include/ir/operation/LSTM.h new file mode 100644 index 000000000..027bc6b42 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/LSTM.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_IR_OPERATION_LSTM_H__ +#define __ONERT_IR_OPERATION_LSTM_H__ + +#include "ir/InternalType.h" +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +// This operation supports only unidirectional sequence lstm +class LSTM : public Operation +{ +public: + enum Input + { + INPUT = 0, + INPUT_TO_INPUT_WEIGHTS = 1, + INPUT_TO_FORGET_WEIGHTS = 2, + INPUT_TO_CELL_WEIGHTS = 3, + INPUT_TO_OUTPUT_WEIGHTS = 4, + RECURRENT_TO_INPUT_WEIGHTS = 5, + RECURRENT_TO_FORGET_WEIGHTS = 6, + RECURRENT_TO_CELL_WEIGHTS = 7, + RECURRENT_TO_OUTPUT_WEIGHTS = 8, + CELL_TO_INPUT_WEIGHTS = 9, + CELL_TO_FORGET_WEIGHTS = 10, + CELL_TO_OUTPUT_WEIGHTS = 11, + INPUT_GATE_BIAS = 12, + FORGET_GATE_BIAS = 13, + CELL_BIAS = 14, + OUTPUT_GATE_BIAS = 15, + PROJECTION_WEIGHTS = 16, + PROJECTION_BIAS = 17, + OUTPUT_STATE_IN = 18, + CELL_STATE_IN = 19, + INPUT_LAYER_NORMALIZATION_WEIGHTS = 20, + FORGET_LAYER_NORMALIZATION_WEIGHTS = 21, + CELL_LAYER_NORMALIZATION_WEIGHTS = 22, + OUTPUT_LAYER_NORMALIZATION_WEIGHTS = 23, + }; + + enum Output + { + SCRATCH_BUFFER = 0, + OUTPUT_STATE_OUT = 1, + CELL_STATE_OUT = 2, + OUTPUT = 3 + }; + + struct Param + { + Activation activation; + float cell_threshold; + float projection_threshold; + bool time_major; + }; + +public: + LSTM(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::LSTM; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_LSTM_H__ diff --git a/runtime/onert/core/include/ir/operation/LocalResponseNormalization.h b/runtime/onert/core/include/ir/operation/LocalResponseNormalization.h new file mode 100644 index 000000000..2946cfbad --- /dev/null +++ b/runtime/onert/core/include/ir/operation/LocalResponseNormalization.h @@ -0,0 +1,66 @@ +/* + * 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_IR_OPERATION_LOCAL_RESPONSE_NORMALIZATION_H__ +#define __ONERT_IR_OPERATION_LOCAL_RESPONSE_NORMALIZATION_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class LocalResponseNormalization : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + int radius; + float bias; + float alpha; + float beta; + }; + +public: + LocalResponseNormalization(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::LocalResponseNormalization; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_LOCAL_RESPONSE_NORMALIZATION_H__ diff --git a/runtime/onert/core/include/ir/operation/LogSoftmax.h b/runtime/onert/core/include/ir/operation/LogSoftmax.h new file mode 100644 index 000000000..391b4ba4a --- /dev/null +++ b/runtime/onert/core/include/ir/operation/LogSoftmax.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_LOGSOFTMAX_H__ +#define __ONERT_IR_OPERATION_LOGSOFTMAX_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class LogSoftmax : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + float beta; + int axis; + }; + +public: + LogSoftmax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::LogSoftmax; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_LOGSOFTMAX_H__ diff --git a/runtime/onert/core/include/ir/operation/LowerInfo.h b/runtime/onert/core/include/ir/operation/LowerInfo.h new file mode 100644 index 000000000..7ef53b8c7 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/LowerInfo.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_IR_OPERATION_LOWER_INFO_H__ +#define __ONERT_IR_OPERATION_LOWER_INFO_H__ + +#include <string> + +#include <ir/operand/PermuteFactor.h> + +namespace onert +{ +namespace backend +{ +class Backend; +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class LowerInfo +{ +public: + LowerInfo(const backend::Backend *backend, Layout layout); + const backend::Backend *backend() const { return _permute_factor.backend(); } + Layout layout() const { return _permute_factor.layout(); } + +private: + operand::PermuteFactor _permute_factor; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_LOWER_INFO_H__ diff --git a/runtime/onert/core/include/ir/operation/MatrixBandPart.h b/runtime/onert/core/include/ir/operation/MatrixBandPart.h new file mode 100644 index 000000000..291826635 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/MatrixBandPart.h @@ -0,0 +1,53 @@ +/* + * 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_MATRIX_BAND_PART_H__ +#define __ONERT_IR_OPERATION_MATRIX_BAND_PART_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class MatrixBandPart : public Operation +{ +public: + enum Input + { + INPUT = 0, + NUM_LOWER_DIAG, + NUM_UPPER_DIAG, + }; + +public: + MatrixBandPart(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::MatrixBandPart; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_MATRIX_BAND_PART_H__ diff --git a/runtime/onert/core/include/ir/operation/OneHot.h b/runtime/onert/core/include/ir/operation/OneHot.h new file mode 100644 index 000000000..6264cd15e --- /dev/null +++ b/runtime/onert/core/include/ir/operation/OneHot.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_IR_OPERATION_ONEHOT_H__ +#define __ONERT_IR_OPERATION_ONEHOT_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class OneHot : public Operation +{ +public: + enum Input + { + INDICES = 0, + DEPTH = 1, + ON_VALUE = 2, + OFF_VALUE = 3, + }; + + struct Param + { + int axis; + }; + +public: + OneHot(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::OneHot; } + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_ONEHOT_H__ diff --git a/runtime/onert/core/include/ir/operation/PReLU.h b/runtime/onert/core/include/ir/operation/PReLU.h new file mode 100644 index 000000000..2981ffc6a --- /dev/null +++ b/runtime/onert/core/include/ir/operation/PReLU.h @@ -0,0 +1,50 @@ +/* + * 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_IR_OPERATION_PRELU_H__ +#define __ONERT_IR_OPERATION_PRELU_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class PReLU : public Operation +{ +public: + enum Input + { + INPUT = 0, + ALPHA = 1 + }; + +public: + PReLU(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::PReLU; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_PRELU_H__ diff --git a/runtime/onert/core/include/ir/operation/Pack.h b/runtime/onert/core/include/ir/operation/Pack.h new file mode 100644 index 000000000..cf07541e0 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Pack.h @@ -0,0 +1,52 @@ +/* + * 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_IR_OPERATION_PACK_H__ +#define __ONERT_IR_OPERATION_PACK_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +class Pack : public Operation +{ +public: + struct Param + { + int32_t num; + int32_t axis; + }; + +public: + Pack(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Pack; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; +} // namespace operation +} // namespace ir +} // namespace onert +#endif // __ONERT_IR_OPERATION_PACK_H__ diff --git a/runtime/onert/core/include/ir/operation/Pad.h b/runtime/onert/core/include/ir/operation/Pad.h new file mode 100644 index 000000000..00481cd50 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Pad.h @@ -0,0 +1,51 @@ +/* + * 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_IR_OPERATION_PAD_H__ +#define __ONERT_IR_OPERATION_PAD_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Pad : public Operation +{ +public: + enum Input + { + INPUT = 0, + PAD = 1, + VALUE = 2 + }; + +public: + Pad(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Pad; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_PAD_H__ diff --git a/runtime/onert/core/include/ir/operation/Permute.h b/runtime/onert/core/include/ir/operation/Permute.h new file mode 100644 index 000000000..10f09b9a0 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Permute.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_PERMUTE_H__ +#define __ONERT_IR_OPERATION_PERMUTE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace backend +{ +class BackendContext; +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Permute : public Operation +{ +public: + enum class Type + { + NHWC_TO_NCHW, + NCHW_TO_NHWC, + COPY + }; + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Permute; } + +public: + Permute(const OperandIndex &input, const OperandIndex &output, Type type); + +public: + Type getPermuteType() const { return _type; } + +private: + Type _type; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_PERMUTE_H__ diff --git a/runtime/onert/core/include/ir/operation/Pool2D.h b/runtime/onert/core/include/ir/operation/Pool2D.h new file mode 100644 index 000000000..22425b4c2 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Pool2D.h @@ -0,0 +1,78 @@ +/* + * 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_POOL2D_H__ +#define __ONERT_IR_OPERATION_POOL2D_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" +#include "ir/Padding.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Pool2D : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + enum class PoolType + { + AVG, + L2, + MAX, + }; + + struct Param + { + PoolType op_type; + uint32_t kh; + uint32_t kw; + Stride stride; + Padding padding; + Activation activation; + }; + +public: + Pool2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::Pool2D; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_POOL2D_H__ diff --git a/runtime/onert/core/include/ir/operation/Pow.h b/runtime/onert/core/include/ir/operation/Pow.h new file mode 100644 index 000000000..ca28ddfe7 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Pow.h @@ -0,0 +1,51 @@ +/* + * 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_POW_H__ +#define __ONERT_IR_OPERATION_POW_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Pow : public Operation +{ +public: + enum Input + { + LHS = 0, + RHS + }; + +public: + Pow(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Pow; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_POW_H__ diff --git a/runtime/onert/core/include/ir/operation/RNN.h b/runtime/onert/core/include/ir/operation/RNN.h new file mode 100644 index 000000000..087075da2 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/RNN.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. + */ +#ifndef __ONERT_IR_OPERATION_RNN_H__ +#define __ONERT_IR_OPERATION_RNN_H__ + +#include "ir/InternalType.h" +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class RNN : public Operation +{ +public: + enum Input + { + INPUT = 0, + WEIGHTS = 1, + RECURRENT_WEIGHTS = 2, + BIAS = 3, + HIDDEN_STATE_IN = 4 + }; + + enum Output + { + OUTPUT = 0, + HIDDEN_STATE_OUT = 1 + }; + + struct Param + { + Activation activation; + }; + +public: + RNN(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::RNN; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RNN_H__ diff --git a/runtime/onert/core/include/ir/operation/Range.h b/runtime/onert/core/include/ir/operation/Range.h new file mode 100644 index 000000000..81e170be9 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Range.h @@ -0,0 +1,51 @@ +/* + * 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_RANGE_H__ +#define __ONERT_IR_OPERATION_RANGE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Range : public Operation +{ +public: + enum Input + { + START = 0, + LIMIT = 1, + DELTA = 2 + }; + +public: + Range(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Range; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RANGE_H__ diff --git a/runtime/onert/core/include/ir/operation/Rank.h b/runtime/onert/core/include/ir/operation/Rank.h new file mode 100644 index 000000000..2fd24ce23 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Rank.h @@ -0,0 +1,51 @@ +/* + * 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_RANK_H__ +#define __ONERT_IR_OPERATION_RANK_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Rank : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + +public: + Rank(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Rank; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RANK_H__ diff --git a/runtime/onert/core/include/ir/operation/Reduce.h b/runtime/onert/core/include/ir/operation/Reduce.h new file mode 100644 index 000000000..26bcf5ec9 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Reduce.h @@ -0,0 +1,77 @@ +/* + * 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_REDUCE_H__ +#define __ONERT_IR_OPERATION_REDUCE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Reduce : public Operation +{ +public: + enum Input + { + INPUT = 0, + AXES = 1 + }; + + enum class ReduceType + { + ALL, + ANY, + MAX, + MEAN, + MIN, + PROD, + SUM + }; + + struct Param + { + ReduceType reduce_type; + bool keep_dims; + }; + +public: + Reduce(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + std::string name() const override; + OpCode opcode() const final { return OpCode::Reduce; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_REDUCE_ALL_H__ diff --git a/runtime/onert/core/include/ir/operation/Reshape.h b/runtime/onert/core/include/ir/operation/Reshape.h new file mode 100644 index 000000000..c2c0e8c99 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Reshape.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_IR_OPERATION_RESHAPE_H__ +#define __ONERT_IR_OPERATION_RESHAPE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Reshape : public Operation +{ +public: + enum Input + { + INPUT = 0, + SHAPE = 1 + }; + + struct Param + { + std::vector<int32_t> new_shape; + }; + +public: + Reshape(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Reshape; } + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RESHAPE_H__ diff --git a/runtime/onert/core/include/ir/operation/ResizeBilinear.h b/runtime/onert/core/include/ir/operation/ResizeBilinear.h new file mode 100644 index 000000000..ab330c826 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ResizeBilinear.h @@ -0,0 +1,68 @@ +/* + * 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_IR_OPERATION_RESIZE_BILINEAR_H__ +#define __ONERT_IR_OPERATION_RESIZE_BILINEAR_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ResizeBilinear : public Operation +{ +public: + enum Input + { + INPUT = 0, + SIZE = 1, + }; + + struct Param + { + // If the input SIZE exists in inputs, height_out and width_out are not set. Ignore these params + int32_t height_out; + int32_t width_out; + bool align_corners; + bool half_pixel_centers; + }; + +public: + ResizeBilinear(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ResizeBilinear; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RESIZE_BILINEAR_H__ diff --git a/runtime/onert/core/include/ir/operation/ResizeNearestNeighbor.h b/runtime/onert/core/include/ir/operation/ResizeNearestNeighbor.h new file mode 100644 index 000000000..10827803e --- /dev/null +++ b/runtime/onert/core/include/ir/operation/ResizeNearestNeighbor.h @@ -0,0 +1,67 @@ +/* + * 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_RESIZE_NEAREST_NEIGHBOR_H__ +#define __ONERT_IR_OPERATION_RESIZE_NEAREST_NEIGHBOR_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class ResizeNearestNeighbor : public Operation +{ +public: + enum Input + { + INPUT = 0, + SIZE = 1, + }; + + struct Param + { + // If the input SIZE exists in inputs, Be height_out and width_out not set. Ignore these params + int32_t height_out; + int32_t width_out; + bool align_corners; + }; + +public: + ResizeNearestNeighbor(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::ResizeNearestNeighbor; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_RESIZE_NEAREST_NEIGHBOR_H__ diff --git a/runtime/onert/core/include/ir/operation/Reverse.h b/runtime/onert/core/include/ir/operation/Reverse.h new file mode 100644 index 000000000..3d7f3fc89 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Reverse.h @@ -0,0 +1,50 @@ +/* + * 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_REVERSE_H__ +#define __ONERT_IR_OPERATION_REVERSE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Reverse : public Operation +{ +public: + enum Input + { + INPUT = 0, + AXIS = 1 + }; + +public: + Reverse(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Reverse; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_REVERSE_H__ diff --git a/runtime/onert/core/include/ir/operation/Select.h b/runtime/onert/core/include/ir/operation/Select.h new file mode 100644 index 000000000..33bf67886 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Select.h @@ -0,0 +1,51 @@ +/* + * 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_SELECT_H__ +#define __ONERT_IR_OPERATION_SELECT_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Select : public Operation +{ +public: + enum Input + { + CONDITION = 0, + INPUT_TRUE = 1, + INPUT_FALSE = 2 + }; + +public: + Select(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Select; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SELECT_H__ diff --git a/runtime/onert/core/include/ir/operation/Shape.h b/runtime/onert/core/include/ir/operation/Shape.h new file mode 100644 index 000000000..4dea7e424 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Shape.h @@ -0,0 +1,51 @@ +/* + * 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_SHAPE_H__ +#define __ONERT_IR_OPERATION_SHAPE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Shape : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + +public: + Shape(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Shape; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SHAPE_H__ diff --git a/runtime/onert/core/include/ir/operation/Slice.h b/runtime/onert/core/include/ir/operation/Slice.h new file mode 100644 index 000000000..c86a9893a --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Slice.h @@ -0,0 +1,51 @@ +/* + * 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_IR_OPERATION_SLICE_H__ +#define __ONERT_IR_OPERATION_SLICE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Slice : public Operation +{ +public: + enum Input + { + INPUT = 0, + BEGINS = 1, + SIZES = 2, + }; + +public: + Slice(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Slice; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SLICE_H__ diff --git a/runtime/onert/core/include/ir/operation/Softmax.h b/runtime/onert/core/include/ir/operation/Softmax.h new file mode 100644 index 000000000..db7ae910e --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Softmax.h @@ -0,0 +1,63 @@ +/* + * 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_OPERATION_SOFTMAX_H__ +#define __ONERT_IR_OPERATION_SOFTMAX_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Softmax : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + float beta; + }; + +public: + Softmax(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Softmax; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SOFTMAX_H__ diff --git a/runtime/onert/core/include/ir/operation/SpaceToBatchND.h b/runtime/onert/core/include/ir/operation/SpaceToBatchND.h new file mode 100644 index 000000000..99928ff24 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/SpaceToBatchND.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_SPACE_TO_BATCH_ND_H__ +#define __ONERT_IR_OPERATION_SPACE_TO_BATCH_ND_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class SpaceToBatchND : public Operation +{ +public: + enum Input + { + INPUT = 0, + BLOCK_SIZE = 1, + PADDINGS = 2 + }; + +public: + SpaceToBatchND(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::SpaceToBatchND; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SPACE_TO_BATCH_ND_H__ diff --git a/runtime/onert/core/include/ir/operation/SpaceToDepth.h b/runtime/onert/core/include/ir/operation/SpaceToDepth.h new file mode 100644 index 000000000..6c8b09130 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/SpaceToDepth.h @@ -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. + */ + +#ifndef __ONERT_IR_OPERATION_SPACE_TO_DEPTH_H__ +#define __ONERT_IR_OPERATION_SPACE_TO_DEPTH_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class SpaceToDepth : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + std::int32_t block_size; + }; + +public: + SpaceToDepth(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::SpaceToDepth; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SPACE_TO_DEPTH_H__ diff --git a/runtime/onert/core/include/ir/operation/Split.h b/runtime/onert/core/include/ir/operation/Split.h new file mode 100644 index 000000000..c415941a4 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Split.h @@ -0,0 +1,58 @@ +/* + * 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_IR_OPERATION_SPLIT_H__ +#define __ONERT_IR_OPERATION_SPLIT_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +class Split : public Operation +{ +public: + enum Input + { + AXIS = 0, + INPUT = 1, + }; + + struct Param + { + int num_splits; + }; + +public: + Split(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Split; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; +} // namespace operation +} // namespace ir +} // namespace onert +#endif // __ONERT_IR_OPERATION_SPLIT_H__ diff --git a/runtime/onert/core/include/ir/operation/SplitV.h b/runtime/onert/core/include/ir/operation/SplitV.h new file mode 100644 index 000000000..99a06ee7f --- /dev/null +++ b/runtime/onert/core/include/ir/operation/SplitV.h @@ -0,0 +1,59 @@ +/* + * 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_SPLIT_V_H__ +#define __ONERT_IR_OPERATION_SPLIT_V_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +class SplitV : public Operation +{ +public: + enum Input + { + INPUT = 0, + SIZE_SPLITS = 1, + SPLIT_DIM = 2 + }; + + struct Param + { + int num_splits; + }; + +public: + SplitV(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::SplitV; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; +} // namespace operation +} // namespace ir +} // namespace onert +#endif // __ONERT_IR_OPERATION_SPLIT_V_H__ diff --git a/runtime/onert/core/include/ir/operation/SquaredDifference.h b/runtime/onert/core/include/ir/operation/SquaredDifference.h new file mode 100644 index 000000000..392b11448 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/SquaredDifference.h @@ -0,0 +1,50 @@ +/* + * 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_IR_OPERATION_SQUARED_DIFFERENCE_H__ +#define __ONERT_IR_OPERATION_SQUARED_DIFFERENCE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class SquaredDifference : public Operation +{ +public: + enum Input + { + LHS = 0, + RHS + }; + +public: + SquaredDifference(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::SquaredDifference; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SQUARED_DIFFERENCE_H__ diff --git a/runtime/onert/core/include/ir/operation/Squeeze.h b/runtime/onert/core/include/ir/operation/Squeeze.h new file mode 100644 index 000000000..c370472b7 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Squeeze.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_IR_OPERATION_SQUEEZE_H__ +#define __ONERT_IR_OPERATION_SQUEEZE_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Squeeze : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + // Please see tensorflow/lite/c/builtin_op_data.h and squeeze.cc. + // tensorflow lite supports only for ndim <= 8. + int dims[8]; + int ndim; + }; + +public: + Squeeze(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Squeeze; } + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_SQUEEZE_H__ diff --git a/runtime/onert/core/include/ir/operation/StatelessRandomUniform.h b/runtime/onert/core/include/ir/operation/StatelessRandomUniform.h new file mode 100644 index 000000000..112a748fd --- /dev/null +++ b/runtime/onert/core/include/ir/operation/StatelessRandomUniform.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_OPERATION_STATELESS_RANDOM_UNIFORM_H__ +#define __ONERT_IR_OPERATION_STATELESS_RANDOM_UNIFORM_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class StatelessRandomUniform : public Operation +{ +public: + enum Input + { + SHAPE = 0, + SEED = 1 + }; + +public: + StatelessRandomUniform(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::StatelessRandomUniform; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_STATELESS_RANDOM_UNIFORM_H__ diff --git a/runtime/onert/core/include/ir/operation/StridedSlice.h b/runtime/onert/core/include/ir/operation/StridedSlice.h new file mode 100644 index 000000000..4a5e06410 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/StridedSlice.h @@ -0,0 +1,68 @@ +/* + * 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_IR_OPERATION_STRIDED_SLICE_H__ +#define __ONERT_IR_OPERATION_STRIDED_SLICE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class StridedSlice : public Operation +{ +public: + enum Input + { + INPUT = 0, + STARTS = 1, + ENDS = 2, + STRIDES = 3 + }; + + struct Param + { + int32_t begin_mask; + int32_t end_mask; + int32_t shrink_axis_mask; + }; + +public: + StridedSlice(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::StridedSlice; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_STRIDED_SLICE_H__ diff --git a/runtime/onert/core/include/ir/operation/Tile.h b/runtime/onert/core/include/ir/operation/Tile.h new file mode 100644 index 000000000..388c452c8 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Tile.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_OPERATION_TILE_H__ +#define __ONERT_IR_OPERATION_TILE_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Tile : public Operation +{ +public: + enum Input + { + INPUT = 0, + MULTIPLES, + }; + +public: + Tile(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Tile; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_TILE_H__ diff --git a/runtime/onert/core/include/ir/operation/TopKV2.h b/runtime/onert/core/include/ir/operation/TopKV2.h new file mode 100644 index 000000000..179a599ca --- /dev/null +++ b/runtime/onert/core/include/ir/operation/TopKV2.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_IR_OPERATION_TOPK_V2_H__ +#define __ONERT_IR_OPERATION_TOPK_V2_H__ + +#include <memory> + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class TopKV2 : public Operation +{ +public: + enum Input + { + INPUT + }; + + enum Output + { + OUTPUT_VALUES = 0, + OUTPUT_INDICES, + }; + + struct Param + { + std::int32_t k; + }; + +public: + TopKV2(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::TopKV2; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_TOPK_V2_H__ diff --git a/runtime/onert/core/include/ir/operation/Transpose.h b/runtime/onert/core/include/ir/operation/Transpose.h new file mode 100644 index 000000000..665c9bbce --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Transpose.h @@ -0,0 +1,52 @@ +/* + * 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_IR_OPERATION_TRANSPOSE_H__ +#define __ONERT_IR_OPERATION_TRANSPOSE_H__ + +#include "ir/Operation.h" + +#include <utility> + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class Transpose : public Operation +{ +public: + enum Input + { + INPUT = 0, // for an n-D tensor, specifying the tensor to be transposed. + PERMUTATION = 1, + }; + +public: + Transpose(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Transpose; } +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_TRANSPOSE_H__ diff --git a/runtime/onert/core/include/ir/operation/TransposeConv.h b/runtime/onert/core/include/ir/operation/TransposeConv.h new file mode 100644 index 000000000..05137ccf8 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/TransposeConv.h @@ -0,0 +1,68 @@ +/* + * 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_IR_OPERATION_TRANSPOSE_CONV_H__ +#define __ONERT_IR_OPERATION_TRANSPOSE_CONV_H__ + +#include <memory> + +#include "ir/Operation.h" +#include "ir/InternalType.h" +#include "ir/Padding.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class TransposeConv : public Operation +{ +public: + enum Input + { + OUTPUT_SHAPE = 0, + KERNEL, + INPUT + }; + + struct Param + { + Padding padding; + Stride stride; + }; + +public: + TransposeConv(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::TransposeConv; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_TRANSPOSE_CONV_H__ diff --git a/runtime/onert/core/include/ir/operation/Unpack.h b/runtime/onert/core/include/ir/operation/Unpack.h new file mode 100644 index 000000000..092583a97 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/Unpack.h @@ -0,0 +1,58 @@ +/* + * 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_IR_OPERATION_UNPACK_H__ +#define __ONERT_IR_OPERATION_UNPACK_H__ + +#include "ir/Operation.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +class Unpack : public Operation +{ +public: + enum Input + { + INPUT = 0 + }; + + struct Param + { + int32_t num; + int32_t axis; + }; + +public: + Unpack(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::Unpack; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; +} // namespace operation +} // namespace ir +} // namespace onert +#endif // __ONERT_IR_OPERATION_UNPACK_H__ diff --git a/runtime/onert/core/include/ir/operation/While.h b/runtime/onert/core/include/ir/operation/While.h new file mode 100644 index 000000000..cf310d596 --- /dev/null +++ b/runtime/onert/core/include/ir/operation/While.h @@ -0,0 +1,58 @@ +/* + * 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_WHILE_H__ +#define __ONERT_IR_OPERATION_WHILE_H__ + +#include "ir/Operation.h" +#include "ir/InternalType.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +class While : public Operation +{ +public: + struct Param + { + SubgraphIndex cond_subg_index; + SubgraphIndex body_subg_index; + }; + +public: + While(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m); + +public: + void accept(OperationVisitor &v) const override; + OpCode opcode() const final { return OpCode::While; } + +public: + const Param ¶m() const { return _param; } + +private: + Param _param; +}; + +} // namespace operation +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_WHILE_H__ diff --git a/runtime/onert/core/include/util/Config.lst b/runtime/onert/core/include/util/Config.lst new file mode 100644 index 000000000..30f211011 --- /dev/null +++ b/runtime/onert/core/include/util/Config.lst @@ -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. + */ + +#ifndef CONFIG +#error Define CONFIG before including this file +#endif + +// Name | Type | Default +CONFIG(GRAPH_DOT_DUMP , int , "0") +CONFIG(BACKENDS , std::string , "cpu;acl_cl;acl_neon;bcq") // FIXME Remove bcq +CONFIG(OP_BACKEND_ALLOPS , std::string , "") +CONFIG(OP_BACKEND_MAP , std::string , "") +CONFIG(DISABLE_COMPILE , bool , "0") +CONFIG(ONERT_LOG_ENABLE , bool , "0") +CONFIG(CPU_MEMORY_PLANNER , std::string , "WIC") +CONFIG(EXECUTOR , std::string , "Linear") +CONFIG(ACL_LAYOUT , std::string , "none") +CONFIG(NCNN_LAYOUT , std::string , "NCHW") +CONFIG(PROFILING_MODE , bool , "0") +CONFIG(USE_SCHEDULER , bool , "0") +CONFIG(OP_SEQ_MAX_NODE , int , "0") +CONFIG(TRACE_FILEPATH , std::string , "") +CONFIG(FP16_ENABLE , bool , "0") +CONFIG(RUY_THREADS , int , "-1") +CONFIG(USE_MMAPED_DATA , bool , "0") + +// Auto-generate all operations + +#define OP(InternalName) \ + CONFIG(OP_BACKEND_ ## InternalName, std::string, "") +#include "ir/Operations.lst" +#undef OP + diff --git a/runtime/onert/core/include/util/ConfigSource.h b/runtime/onert/core/include/util/ConfigSource.h new file mode 100644 index 000000000..b6a8144fd --- /dev/null +++ b/runtime/onert/core/include/util/ConfigSource.h @@ -0,0 +1,58 @@ +/* + * 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_UTIL_CONFIG_SOURCE_H__ +#define __ONERT_UTIL_CONFIG_SOURCE_H__ + +#include <memory> + +#include "IConfigSource.h" + +namespace onert +{ +namespace util +{ + +void config_source(std::unique_ptr<IConfigSource> &&source); + +bool toBool(const std::string &val); +int toInt(const std::string &val); + +bool getConfigBool(const std::string &key); +int getConfigInt(const std::string &key); +std::string getConfigString(const std::string &key); + +} // namespace util +} // namespace onert + +namespace onert +{ +namespace util +{ +namespace config +{ + +#define CONFIG(Name, Type, Default) extern const char *Name; + +#include "Config.lst" + +#undef CONFIG + +} // namespace config +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_CONFIG_SOURCE_H__ diff --git a/runtime/onert/core/include/util/EnvConfigSource.h b/runtime/onert/core/include/util/EnvConfigSource.h new file mode 100644 index 000000000..8c5d0e8e9 --- /dev/null +++ b/runtime/onert/core/include/util/EnvConfigSource.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_UTIL_ENV_CONFIG_SOURCE_H__ +#define __ONERT_UTIL_ENV_CONFIG_SOURCE_H__ + +#include <unordered_map> + +#include "util/GeneralConfigSource.h" + +namespace onert +{ +namespace util +{ + +class EnvConfigSource final : public GeneralConfigSource +{ +public: + std::string get(const std::string &key) const override; + +private: + std::unordered_map<std::string, std::string> _default_attributes; +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_ENV_CONFIG_SOURCE_H__ diff --git a/runtime/onert/core/include/util/Exceptions.h b/runtime/onert/core/include/util/Exceptions.h new file mode 100644 index 000000000..fc3fa0f64 --- /dev/null +++ b/runtime/onert/core/include/util/Exceptions.h @@ -0,0 +1,48 @@ +/* + * 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_UTIL_ONERTEXCEPTION_H__ +#define __ONERT_UTIL_ONERTEXCEPTION_H__ + +#include <string> + +namespace onert +{ + +class OnertException : public std::exception +{ +public: + OnertException(const std::string &msg) : _msg{msg} {} + OnertException(const std::string &tag, const std::string &msg) : _msg{tag + " : " + msg} {} + + const char *what() const noexcept override { return _msg.c_str(); } + +private: + std::string _msg; +}; + +class InsufficientBufferSizeException : public OnertException +{ +public: + InsufficientBufferSizeException(const std::string &msg) + : OnertException{"InsufficientBufferSize", msg} + { + } +}; + +} // namespace onert + +#endif // __ONERT_UTIL_ONERTEXCEPTION_H__ diff --git a/runtime/onert/core/include/util/GeneralConfigSource.h b/runtime/onert/core/include/util/GeneralConfigSource.h new file mode 100644 index 000000000..dedc820ec --- /dev/null +++ b/runtime/onert/core/include/util/GeneralConfigSource.h @@ -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. + */ + +#ifndef __ONERT_UTIL_GLOBAL_CONFIG_SOURCE_H__ +#define __ONERT_UTIL_GLOBAL_CONFIG_SOURCE_H__ + +#include <unordered_map> + +#include "util/IConfigSource.h" + +namespace onert +{ +namespace util +{ + +class GeneralConfigSource : public IConfigSource +{ +public: + GeneralConfigSource() = default; + + std::string get(const std::string &key) const override; + void set(const std::string &key, const std::string &val); + +private: + std::unordered_map<std::string, std::string> _map; +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_GLOBAL_CONFIG_SOURCE_H__ diff --git a/runtime/onert/core/include/util/IConfigSource.h b/runtime/onert/core/include/util/IConfigSource.h new file mode 100644 index 000000000..07b09848a --- /dev/null +++ b/runtime/onert/core/include/util/IConfigSource.h @@ -0,0 +1,46 @@ +/* + * 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_UTIL_I_CONFIG_SOURCE_H__ +#define __ONERT_UTIL_I_CONFIG_SOURCE_H__ + +#include <string> + +namespace onert +{ +namespace util +{ + +struct IConfigSource +{ + /** + * @brief Destroy the IConfigSource object + */ + virtual ~IConfigSource() = default; + + /** + * @brief get the value for the matching key + * + * @param key string key to search + * @return string value associated with the key + */ + virtual std::string get(const std::string &key) const = 0; +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_I_CONFIG_SOURCE_H__ diff --git a/runtime/onert/core/include/util/ITimer.h b/runtime/onert/core/include/util/ITimer.h new file mode 100644 index 000000000..d5a4e1eb0 --- /dev/null +++ b/runtime/onert/core/include/util/ITimer.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_UTIL_ITIMER_H__ +#define __ONERT_UTIL_ITIMER_H__ + +#include <chrono> + +namespace onert +{ +namespace util +{ + +class ITimer +{ +public: + virtual void handleBegin() = 0; + virtual void handleEnd() = 0; + int getTime() { return _timer_res; }; + + virtual ~ITimer() = default; + +protected: + int _timer_res{0}; +}; + +class CPUTimer : public ITimer +{ +public: + void handleBegin() override { _start_time = std::chrono::steady_clock::now(); }; + + void handleEnd() override + { + const auto end_time = std::chrono::steady_clock::now(); + _timer_res = + std::chrono::duration_cast<std::chrono::microseconds>(end_time - _start_time).count(); + }; + +private: + std::chrono::steady_clock::time_point _start_time; // in microseconds +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_ITIMER_H__ diff --git a/runtime/onert/core/include/util/Index.h b/runtime/onert/core/include/util/Index.h new file mode 100644 index 000000000..e8f59282d --- /dev/null +++ b/runtime/onert/core/include/util/Index.h @@ -0,0 +1,169 @@ +/* + * 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_UTIL_INDEX_H__ +#define __ONERT_UTIL_INDEX_H__ + +#include <functional> +#include <limits> +#include <stdint.h> +#include <string> + +namespace onert +{ +namespace util +{ + +/** + * @brief A wrapper class for unsigned integral Index + * NOTE : Max value of the underlying type is used as the invalid value + * + * @tparam T Underlying type. Must be unsigned integral type otherwise its behavior is undefined. + * @tparam DummyTag Dummy type to distinguish types with a same underlying type. Using an opaque + * type is recommended. + */ +template <typename T, typename DummyTag> class Index +{ +private: + static const T UNDEFINED = std::numeric_limits<T>::max(); + +public: + /** + * @brief Construct a new Index object + */ + explicit Index(void) : _index{UNDEFINED} {} + /** + * @brief Construct a new Index object with a value in the underlying type + * + * @param o Value in the underlying type + */ + explicit Index(const T o) : _index{o} {} + /** + * @brief Copy Constructor + * + * @param o Object to be copied + */ + Index(const Index &o) = default; + + /** + * @brief Assign a value in the underlying time + * + * @param o Value in the underlying type + * @return Index& Reference of this pointer + */ + Index &operator=(const T o) + { + _index = o; + return *this; + } + + /** + * @brief Copy assignment operator + * + * @param o Object to be copied + * @return Index& Reference of this pointer + */ + Index &operator=(const Index &o) = default; + + /** + * @brief Equality operator + * + * @param o The other value in the underlying type to compare + * @return true if underlying value is the same, false otherwise + */ + bool operator==(T o) const { return _index == o; } + /** + * @brief Equality operator + * + * @param o The other object to compare + * @return true if underlying value is the same, false otherwise + */ + bool operator==(const Index &o) const { return _index == o._index; } + /** + * @brief Inquality operator + * + * @param o The other value in the underlying type to compare + * @return true if underlying value is different, false otherwise + */ + bool operator!=(T o) const { return !(*this == o); } + /** + * @brief Inquality operator + * + * @param o The other object to compare + * @return true if underlying value is different, false otherwise + */ + bool operator!=(const Index &o) const { return !(*this == o); } + + /** + * @brief Post increment operator + * + * @return Index Index before increment + */ + Index operator++(int) + { + Index temp = *this; + _index++; + return temp; + } + + /** + * @brief Check whether the value is valid or not + * + * @return true if valid, false otherwise + */ + bool valid() const { return _index != UNDEFINED; } + /** + * @brief Check whether the value is undefined + * + * @return true if undefined, false otherwise + */ + bool undefined() const { return _index == UNDEFINED; } + /** + * @brief Return underlying value + * + * @return T Underlying value + */ + T value() const { return _index; } + + friend std::ostream &operator<<(std::ostream &o, const Index &t) + { + if (t.undefined()) + return o << std::string("undefined"); + else + return o << t.value(); + } + +private: + T _index; +}; + +} // namespace util +} // namespace onert + +namespace std +{ + +template <typename T, typename Tag> struct hash<::onert::util::Index<T, Tag>> +{ + size_t operator()(const ::onert::util::Index<T, Tag> &index) const noexcept + { + return hash<T>()(index.value()); + } +}; + +} // namespace std + +#endif // __ONERT_UTIL_INDEX_H__ diff --git a/runtime/onert/core/include/util/ObjectManager.h b/runtime/onert/core/include/util/ObjectManager.h new file mode 100644 index 000000000..d2dd881a8 --- /dev/null +++ b/runtime/onert/core/include/util/ObjectManager.h @@ -0,0 +1,148 @@ +/* + * 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_UTIL_OBJECT_MANAGER_H__ +#define __ONERT_UTIL_OBJECT_MANAGER_H__ + +#include <unordered_map> +#include <memory> +#include <list> +#include <functional> + +#include <memory> + +namespace onert +{ +namespace util +{ + +/** + * @brief Class that owns objects and maps them with indices as a handle for them + * + */ +template <typename Index, typename Object> class ObjectManager +{ +public: + ObjectManager() : _index_count{0u} {} + +public: + /** + * @brief Create an object with args and put it in the container with a new Index for that + * + * @param[in] args Arguments for creating Operand object + * @return Created index that is associated to the object + */ + template <class... Args> Index emplace(Args &&... args) + { + auto index = generateIndex(); + _objects.emplace(index, std::make_unique<Object>(std::forward<Args>(args)...)); + return index; + } + + /** + * @brief Put object in the container with a new Index for that + * + * @param[in] object Object to be pushed + * @return Created index that is associated to the object + */ + Index push(std::unique_ptr<Object> &&object) + { + auto index = generateIndex(); + _objects.emplace(index, std::move(object)); + return index; + } + + /** + * @brief Remove the object that is associated with the given index + * + * @param[in] index Index of the object to be removed + * @return N/A + */ + void remove(const Index &index) { _objects.erase(index); } + + /** + * @brief Get the object that is associated with the given index + * + * @param[in] index Index of the object to be returned + * @return Object + */ + const Object &at(const Index &index) const { return *(_objects.at(index)); } + /** + * @brief Get the object that is associated with the given index + * + * @param[in] index Index of the object to be returned + * @return Object + */ + Object &at(const Index &index) { return *(_objects.at(index)); } + /** + * @brief Get the object that is associated with the given index + * + * @param[in] index Index of the object to be returned + * @return true if such entry exists otherwise false + */ + bool exist(const Index &index) const + { + auto it = _objects.find(index); + return it != _objects.end(); + } + /** + * @brief Iterate over the container with given function + * + * @param[in] fn Function to be run for every container entry + * @return N/A + */ + void iterate(const std::function<void(const Index &, const Object &)> &fn) const + { + for (const auto &e : _objects) + { + fn(e.first, *e.second); + } + } + /** + * @brief Iterate over the container with given function + * + * @param[in] fn Function to be run for every container entry + * @return N/A + */ + void iterate(const std::function<void(const Index &, Object &)> &fn) + { + // TODO Remove this workaround + // This implementation is a workaround in case of adding operands while iteration + std::list<Index> l; + + for (auto &e : _objects) + { + l.push_back(e.first); + } + + for (auto index : l) + { + fn(index, *_objects[index]); + } + } + +private: + Index generateIndex() { return Index{_index_count++}; } + +protected: + std::unordered_map<Index, std::unique_ptr<Object>> _objects; + uint32_t _index_count; +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_OBJECT_MANAGER_H__ diff --git a/runtime/onert/core/include/util/Set.h b/runtime/onert/core/include/util/Set.h new file mode 100644 index 000000000..ee4062d25 --- /dev/null +++ b/runtime/onert/core/include/util/Set.h @@ -0,0 +1,166 @@ +/* + * 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 Set.h + * @brief This file contains onert::util::Set class + * @ingroup COM_AI_RUNTIME + */ + +#ifndef __ONERT_UTIL_SET_H__ +#define __ONERT_UTIL_SET_H__ + +#include <cassert> +#include <unordered_set> + +namespace onert +{ +namespace util +{ + +/** + * @brief Class for set of custom element + & @tparam Element Key type of Set + */ +template <typename Element> class Set +{ +public: + /** + * @brief Construct default Set object. + */ + Set() = default; + /** + * @brief Construct Set object by copy semantics. + */ + Set(const Set<Element> &) = default; + /** + * @brief Construct move Set object by move semantics. + */ + Set(Set<Element> &&) = default; + +public: + /** + * @brief Add a given element to the set + * + * @param e Element added + */ + void add(const Element &e) { _set.insert(e); } + /** + * @brief remove a given element from the set + * + * @param e Element removed + */ + void remove(const Element &e) { _set.erase(e); } + /** + * @brief Get size of the set + * + * @return The size of the set + */ + uint32_t size() const { return static_cast<uint32_t>(_set.size()); } + /** + * @brief Get whether the set is empty + * + * @return Whether the set is empty + */ + bool empty() const { return _set.empty(); } + /** + * @brief Get whether a given element exists in the set + * + * @param e A given element + * + * @return Whether a given element exists in the set + */ + bool contains(const Element &e) const { return _set.find(e) != _set.end(); } + /** + * @brief Get first element of the set + * + * @return first element of the set + */ + const Element &getOnlyElement() const + { + assert(_set.size() == 1u); + return *_set.begin(); + } + +public: + /** + * @brief operator overloading function for `|` + * + * @return A set with two sets combined + */ + Set<Element> operator|(const Set<Element> &other) const // Union + { + auto ret = *this; + for (auto e : other) + { + ret.add(e); + } + return ret; + } + /** + * @brief operator overloading function for `&` + * + * @return A set of elements that overlap in two sets + */ + Set<Element> operator&(const Set<Element> &other) const // Intersect + { + Set<Element> ret; + for (auto e : other) + { + if (contains(e)) + { + ret.add(e); + } + } + return ret; + } + /** + * @brief operator overloading function for `-` + * + * @return A set of subtracted from another set + */ + Set<Element> operator-(const Set<Element> &other) const // Minus + { + auto ret = *this; + for (auto e : other) + { + ret.remove(e); + } + return ret; + } + +public: + /** + * @brief begin() of const_iterator for this class + * + * @return The first iterator of the set + */ + typename std::unordered_set<Element>::const_iterator begin() const { return _set.begin(); } + /** + * @brief end() of const_iterator for this class + * + * @return The last iterator of the set + */ + typename std::unordered_set<Element>::const_iterator end() const { return _set.end(); } + +private: + std::unordered_set<Element> _set; +}; + +} // namespace util +} // namespace onert + +#endif // __ONERT_UTIL_SET_H__ diff --git a/runtime/onert/core/include/util/ShapeInference.h b/runtime/onert/core/include/util/ShapeInference.h new file mode 100644 index 000000000..701b835d2 --- /dev/null +++ b/runtime/onert/core/include/util/ShapeInference.h @@ -0,0 +1,153 @@ +/* + * 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_SHAPE_INFERENCE_H__ +#define __ONERT_GRAPH_SHAPE_INFERENCE_H__ + +#include "Utils.h" + +#include "ir/operation/Concat.h" +#include "ir/operation/Conv2D.h" +#include "ir/operation/DepthwiseConv2D.h" +#include "ir/operation/Pool2D.h" +#include "ir/operation/Reshape.h" +#include "ir/operation/StridedSlice.h" +#include "compiler/LoweredGraph.h" +#include "ir/Index.h" +#include "ir/Layout.h" +#include "ir/OperationVisitor.h" +#include "backend/IDynamicTensorManager.h" +#include "backend/ITensor.h" +#include "backend/ITensorRegistry.h" + +namespace onert +{ +namespace shape_inference +{ + +using Shapes = std::vector<ir::Shape>; + +// Define shape calculation for operations. List them in alphabetic order. + +ir::Shape inferArgMaxShape(const ir::Shape &input_shape, int axis, int rank); + +ir::Shape inferBatchMatMulShape(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape, + const ir::operation::BatchMatMul::Param ¶m); + +ir::Shape inferBCQFullyConnectedShape(const ir::Shape &in_shape, const ir::Shape &cluster_shape, + const int32_t *cluster_buf); + +ir::Shape inferBCQGatherShape(const ir::Shape &indices_shape, const ir::Shape &cluster_shape, + const int32_t *cluster_buf, int rank, + const ir::operation::BCQGather::Param ¶m); + +ir::Shape inferBroadcastToShape(const ir::Shape shp_shape, const int32_t *shp_buf); + +ir::Shape inferConcatShape(const Shapes &in_shapes, const ir::operation::Concat::Param ¶m); + +ir::Shape inferConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::Conv2D::Param ¶m, + ir::Layout layout = ir::Layout::NHWC); + +ir::Shape inferDepthwiseConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::DepthwiseConv2D::Param ¶m, + ir::Layout layout = ir::Layout::NHWC); + +ir::Shape inferEltwiseShape(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape); + +ir::Shape inferExpandDimsShape(const ir::Shape &in_shape, int32_t axis); + +ir::Shape inferFillShape(const ir::Shape &in_shape, const int32_t *in_buf); + +ir::Shape inferFullyConnectedShape(const ir::Shape &in_shape, const ir::Shape &ker_shape); + +ir::Shape inferGatherShape(const ir::Shape &input_shape, const ir::Shape &indices_shape, int axis, + int rank); + +ir::Shape inferOnehotShape(const ir::Shape &input_shape, const int depth, int axis); + +ir::Shape inferPackShape(const ir::Shape &input_shape, int axis, int rank, int num); + +ir::Shape inferPadShape(const ir::Shape &in_shape, const int32_t *pad_buf, const size_t num_pads); + +ir::Shape inferPoolShape(const ir::Shape &in_shape, const ir::operation::Pool2D::Param ¶m, + ir::Layout layout = ir::Layout::NHWC); + +template <typename T> ir::Shape inferRangeShape(T start_val, T limit_val, T delta_val); + +ir::Shape inferReshapeShape(const int32_t *shape_buf, const int32_t shape_num_elements, + const size_t total_num_elements); + +ir::Shape inferReduceShape(const ir::Shape &input_shape, const std::vector<int> &axes, + bool keep_dims); + +template <float *> ir::Shape inferRangeShape(float *start_val, float *limit_val, float *delta_val); + +template <typename T> ir::Shape inferRangeShape(T start_val, T limit_val, T delta_val); + +ir::Shape inferResizeBilinearShape(const ir::Shape &in_shape, const int32_t output_height, + const int32_t output_width); + +ir::Shape inferSelectShape(const ir::Shape &input_cond_shape, const ir::Shape &input_true_shape, + const ir::Shape &input_false_shape); + +ir::Shape inferSliceShape(const ir::Shape &input_shape, const int32_t *begins_buf, + const int32_t *sizes_buf); + +ir::Shape inferSpaceToBatchNDShape(const ir::Shape &input_shape, const ir::Shape &block_shape_shape, + const ir::Shape &padding_shape, const int32_t *block_shape_buf, + const int32_t *padding_buf); + +ir::Shape inferSplitShape(const ir::Shape input_shape, int axis_value, int num_splits); + +ir::Shape inferSqueezeShape(const ir::Shape &in_shape, const ir::operation::Squeeze::Param ¶m); + +struct StridedSliceParams +{ + int8_t start_indices_count; + int16_t start_indices[4]; + int8_t stop_indices_count; + int16_t stop_indices[4]; + int8_t strides_count; + int16_t strides[4]; + + int16_t begin_mask; + int16_t ellipsis_mask; + int16_t end_mask; + int16_t new_axis_mask; + int16_t shrink_axis_mask; +}; + +template <typename T> +StridedSliceParams buildStridedSliceParams(const T *begin, const T *end, const T *strides, + const uint32_t begin_mask, const uint32_t end_mask, + const uint32_t shrink_axis_mask, const uint8_t rank); + +ir::Shape inferStridedSliceShape(const ir::Shape &input_shape, const StridedSliceParams &op_params, + uint32_t rank); + +ir::Shape inferTileShape(const ir::Shape &in_shape, const int32_t *multiplier_buf, + const int32_t multiplier_size); + +ir::Shape inferTransposeShape(const ir::Shape &in_shape, const int32_t *perm_buf, + const int32_t rank); + +ir::Shape inferUnpackShape(const ir::Shape &input_shape, int axis, int rank); + +} // namespace shape_inference +} // namespace onert + +#endif // __ONERT_GRAPH_SHAPE_INFERENCE_H__ diff --git a/runtime/onert/core/include/util/Utils.h b/runtime/onert/core/include/util/Utils.h new file mode 100644 index 000000000..8a4eea32b --- /dev/null +++ b/runtime/onert/core/include/util/Utils.h @@ -0,0 +1,108 @@ +/* + * 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 Utils.h + * @brief This file contains utility macro + */ + +#ifndef __ONERT_UTIL_UTILS_H__ +#define __ONERT_UTIL_UTILS_H__ + +#include "ir/Coordinates.h" +#include "ir/Shape.h" + +#define UNUSED_RELEASE(a) (void)(a) + +template <size_t from, size_t to, typename Enable = void> struct ForEachDimension +{ + template <typename L, typename... Args> + static void unroll(const onert::ir::Shape &shape, onert::ir::Coordinates &coords, + L &&lambda_function, Args &&... args) + { + static_assert(from < to, "from must not be less than to"); + assert(static_cast<int>(to) <= shape.rank()); + const auto &d = shape.dim(from); + + for (auto v = 0; v < d; v++) + { + coords.set(from, v); + ForEachDimension<from + 1, to>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + } + } +}; + +template <size_t from, size_t to> +struct ForEachDimension<from, to, typename std::enable_if<from == to>::type> +{ + template <typename L, typename... Args> + static void unroll(const onert::ir::Shape &shape, onert::ir::Coordinates &coords, + L &&lambda_function, Args &&... args) + { + UNUSED_RELEASE(shape); + assert(static_cast<int>(to) <= shape.rank()); + lambda_function(coords, std::forward<Args>(args)...); + } +}; + +template <typename L, typename... Args> +inline void ShapeLoop(const onert::ir::Shape &shape, L &&lambda_function, Args &&... args) +{ + assert(shape.rank() > 0); + for (auto i = 0; i < shape.rank(); ++i) + { + assert(shape.dim(i) > 0); + } + + onert::ir::Coordinates coords; + switch (shape.rank()) + { + case 0: + coords.set(0, 0); + ForEachDimension<0, 0>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 1: + ForEachDimension<0, 1>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 2: + ForEachDimension<0, 2>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 3: + ForEachDimension<0, 3>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 4: + ForEachDimension<0, 4>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 5: + ForEachDimension<0, 5>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + case 6: + ForEachDimension<0, 6>::unroll(shape, coords, std::forward<L>(lambda_function), + std::forward<Args>(args)...); + break; + default: + assert(false && "ShapeLoop, 1 <= Shape'rank <= 6"); + break; + } +} +#endif // __ONERT_UTIL_UTILS_H__ diff --git a/runtime/onert/core/include/util/logging.h b/runtime/onert/core/include/util/logging.h new file mode 100644 index 000000000..76cfb8d60 --- /dev/null +++ b/runtime/onert/core/include/util/logging.h @@ -0,0 +1,67 @@ +/* + * 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_UTIL_LOGGING_H__ +#define __ONERT_UTIL_LOGGING_H__ + +#include <iostream> + +#include "util/ConfigSource.h" + +namespace onert +{ +namespace util +{ +namespace logging +{ + +class Context +{ +public: + Context() noexcept : _enabled{false} + { + const auto env = util::getConfigBool(util::config::ONERT_LOG_ENABLE); + + if (env) + { + _enabled = true; + } + } + + static Context &get() noexcept; + +public: + bool enabled(void) const { return _enabled; } + +private: + bool _enabled; +}; + +static Context &ctx = Context::get(); + +} // namespace logging +} // namespace util +} // namespace onert + +#define VERBOSE(name) \ + if (::onert::util::logging::ctx.enabled()) \ + std::cout << "[" << #name << "] " + +#define VERBOSE_F() \ + if (::onert::util::logging::ctx.enabled()) \ + std::cout << "[" << __func__ << "] " + +#endif // __ONERT_UTIL_LOGGING_H__ diff --git a/runtime/onert/core/src/backend/BackendContext.cc b/runtime/onert/core/src/backend/BackendContext.cc new file mode 100644 index 000000000..bafa36d28 --- /dev/null +++ b/runtime/onert/core/src/backend/BackendContext.cc @@ -0,0 +1,55 @@ +/* + * 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/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::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->registerDefaultInitializer(ind, obj); + } + } + + constant_initializer->run(); +} + +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/IConstantInitializer.cc b/runtime/onert/core/src/backend/IConstantInitializer.cc new file mode 100644 index 000000000..6fb9757e0 --- /dev/null +++ b/runtime/onert/core/src/backend/IConstantInitializer.cc @@ -0,0 +1,114 @@ +/* + * 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/IConstantInitializer.h" + +#include <Half.h> + +using float16 = Half; + +namespace onert +{ +namespace backend +{ + +void IConstantInitializer::registerCopyInitializer(const ir::OperandIndex &index, + const ir::Operand &obj) +{ + // For only CONSTANTS + // TODO Add to check if tensor has been allocated + if (!obj.isConstant()) + return; + + const auto type = obj.typeInfo().type(); + using ir::DataType; + + switch (type) + { + case DataType::FLOAT32: + _init_map[index] = copyInit<float>; + break; + case DataType::INT32: + _init_map[index] = copyInit<int32_t>; + break; + case DataType::UINT32: + _init_map[index] = copyInit<uint32_t>; + break; + case DataType::BOOL8: + case DataType::QUANT_UINT8_ASYMM: + _init_map[index] = copyInit<uint8_t>; + break; + case DataType::QUANT_INT8_SYMM: + case DataType::QUANT_INT8_ASYMM: + _init_map[index] = copyInit<int8_t>; + break; + case DataType::FLOAT16: + _init_map[index] = copyInit<float16>; + break; + case DataType::INT64: + _init_map[index] = copyInit<int64_t>; + break; + default: + throw std::runtime_error("Not supported, yet"); + break; + } +} + +void IConstantInitializer::registerPermuteInitializer(const ir::OperandIndex &index, + const ir::Operand &obj) +{ + // For only CONSTANTS + // TODO Add to check if tensor has been allocated + if (!obj.isConstant()) + return; + + const auto type = obj.typeInfo().type(); + using ir::DataType; + using namespace std::placeholders; + + switch (type) + { + case DataType::FLOAT32: + _init_map[index] = std::bind(permuteInit<float>, _1, _2, _current_op_seq_layout); + break; + case DataType::INT32: + _init_map[index] = std::bind(permuteInit<int32_t>, _1, _2, _current_op_seq_layout); + break; + case DataType::UINT32: + _init_map[index] = std::bind(permuteInit<uint32_t>, _1, _2, _current_op_seq_layout); + break; + case DataType::BOOL8: + case DataType::QUANT_UINT8_ASYMM: + _init_map[index] = std::bind(permuteInit<uint8_t>, _1, _2, _current_op_seq_layout); + break; + case DataType::QUANT_INT8_SYMM: + case DataType::QUANT_INT8_ASYMM: + _init_map[index] = std::bind(permuteInit<int8_t>, _1, _2, _current_op_seq_layout); + break; + case DataType::FLOAT16: + _init_map[index] = std::bind(permuteInit<float16>, _1, _2, _current_op_seq_layout); + break; + case DataType::INT64: + _init_map[index] = std::bind(permuteInit<int64_t>, _1, _2, _current_op_seq_layout); + break; + default: + throw std::runtime_error("Not supported, yet"); + break; + } +} + +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/IPortableTensor.cc b/runtime/onert/core/src/backend/IPortableTensor.cc new file mode 100644 index 000000000..cec34e780 --- /dev/null +++ b/runtime/onert/core/src/backend/IPortableTensor.cc @@ -0,0 +1,29 @@ +/* + * 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/IPortableTensor.h" + +namespace onert +{ +namespace backend +{ + +// `dynamic_cast` not working across library boundaries on NDK +// With this as a key function, `dynamic_cast` works across dl +IPortableTensor::~IPortableTensor() {} + +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/ITensor.cc b/runtime/onert/core/src/backend/ITensor.cc new file mode 100644 index 000000000..7127ed93d --- /dev/null +++ b/runtime/onert/core/src/backend/ITensor.cc @@ -0,0 +1,34 @@ +/* + * 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/ITensor.h" + +namespace onert +{ +namespace backend +{ + +ir::Shape ITensor::getShape() const +{ + onert::ir::Shape shape(num_dimensions()); + for (uint32_t d = 0; d < num_dimensions(); d++) + shape.dim(d) = dimension(d); + + return shape; +} + +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/Backend.h b/runtime/onert/core/src/backend/controlflow/Backend.h new file mode 100644 index 000000000..cc8346e6b --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/Backend.h @@ -0,0 +1,88 @@ +/* + * 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_BACKEND_CONTROLFLOW_BACKEND_H__ +#define __ONERT_BACKEND_CONTROLFLOW_BACKEND_H__ + +#include "BackendContext.h" +#include "Config.h" +#include "ConstantInitializer.h" +#include "KernelGenerator.h" +#include "TensorBuilder.h" +#include "Tensor.h" + +#include <backend/Backend.h> + +#include <memory> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class Backend : public ::onert::backend::Backend +{ +public: + Backend() : _config{std::make_shared<Config>()} {} + + std::shared_ptr<IConfig> config() const override { return _config; } + + std::unique_ptr<onert::backend::BackendContext> + newContext(const ir::Graph &graph, const std::shared_ptr<custom::IKernelBuilder> &, + bool) const override + { + const auto &operands = graph.operands(); + auto context = std::make_unique<BackendContext>(this, &graph); + // ControlFlow backend may not build tensors for itself because the backend's operation uses + // tensors of other baceknd instead + // But the backend builds tensors in case of that the controlflow operation may have constant + // input or that consecutive controflow operations exist. We have to make them not to be built + // later + // 1. Constant input + // These tensors cannot be dynamic tensor, so let's do it as follows: + // - always skip copying + // - if it is operation's input in child subgraph: register "use" as constant input of the + // operations in child subgraph + // - if it is child subgraph's output: register "use" as constant input of the operations + // using it + // 2. Consecutive controflow operation's intermediate tensor + // These tensors can be dynamic tensor and this is complicated to support without copying. But + // there is no such case until now, let's support it later + // TODO Remove TensorBuilder and ConstantInitializer + // TODO Support Consecutive controflow operation's intermediate tensor + auto tr = std::make_shared<TensorRegistry>(); + auto tb = std::make_shared<TensorBuilder>(tr); + context->tensor_registry = tr; + context->tensor_builder = tb; + context->constant_initializer = std::make_shared<ConstantInitializer>(operands, tr); + context->kernel_gen = std::make_shared<KernelGenerator>(graph, tb->dynamicTensorManager(), tr, + context->external_context()); + context->tensor_register = nullptr; + context->optimizer = nullptr; + return context; + } + +private: + std::shared_ptr<IConfig> _config; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_BACKEND_H__ diff --git a/runtime/onert/core/src/backend/controlflow/BackendContext.h b/runtime/onert/core/src/backend/controlflow/BackendContext.h new file mode 100644 index 000000000..3647338a0 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/BackendContext.h @@ -0,0 +1,60 @@ +/* + * 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_BACKEND_CONTROLFLOW_BACKEND_CONTEXT_H__ +#define __ONERT_BACKEND_CONTROLFLOW_BACKEND_CONTEXT_H__ + +#include <backend/BackendContext.h> +#include "ExternalContext.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class BackendContext : public onert::backend::BackendContext +{ +public: + BackendContext(const Backend *backend, const ir::Graph *graph, + std::shared_ptr<ITensorRegistry> tensor_registry = nullptr, + std::shared_ptr<ITensorBuilder> tensor_builder = nullptr, + std::shared_ptr<IConstantInitializer> constant_initializer = nullptr, + std::shared_ptr<IKernelGenerator> kernel_gen = nullptr, + std::shared_ptr<ITensorRegister> tensor_register = nullptr, + std::shared_ptr<IOptimizer> optimizer = nullptr) + : onert::backend::BackendContext(backend, graph, tensor_registry, tensor_builder, + constant_initializer, kernel_gen, tensor_register, + optimizer), + _external_context(std::make_shared<ExternalContext>()) + { + } + + std::shared_ptr<ExternalContext> external_context() { return _external_context; } + +private: + // NOTE ruy context has a thread pool, and when multiple ruy contexts are created, + // the thread pool is also created in duplicate + // TODO Create one ruy context for session + std::shared_ptr<ExternalContext> _external_context; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_BACKEND_CONTEXT_H__ diff --git a/runtime/onert/core/src/backend/controlflow/Config.cc b/runtime/onert/core/src/backend/controlflow/Config.cc new file mode 100644 index 000000000..5ec01fe11 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/Config.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 "Config.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +std::string Config::ID = "controlflow"; + +bool Config::initialize() { return true; } + +ir::Layout Config::supportLayout(const ir::Operation &, ir::Layout frontend_layout) +{ + return frontend_layout; +} + +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/Config.h b/runtime/onert/core/src/backend/controlflow/Config.h new file mode 100644 index 000000000..6645ed59d --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/Config.h @@ -0,0 +1,53 @@ +/* + * 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_BACKEND_CONTROLFLOW_CONFIG_H__ +#define __ONERT_BACKEND_CONTROLFLOW_CONFIG_H__ + +#include <backend/IConfig.h> +#include <memory> +#include <util/ITimer.h> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class Config : public IConfig +{ +public: + static std::string ID; + std::string id() override { return ID; } + bool initialize() override; + ir::Layout supportLayout(const ir::Operation &node, ir::Layout frontend_layout) override; + bool supportPermutation() override { return false; } + bool supportDynamicTensor() override + { + // TODO Make this backend to support dynamic tensor or not to build non-constant tensor + return true; + } + bool supportFP16() override { return false; } + + std::unique_ptr<util::ITimer> timer() override { return std::make_unique<util::CPUTimer>(); } +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_CONFIG_H__ diff --git a/runtime/onert/core/src/backend/controlflow/ConstantInitializer.h b/runtime/onert/core/src/backend/controlflow/ConstantInitializer.h new file mode 100644 index 000000000..e21a8f357 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/ConstantInitializer.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_COMPILER_CONTROLFLOW_CONSTANT_INITIALIZER_H__ +#define __ONERT_COMPILER_CONTROLFLOW_CONSTANT_INITIALIZER_H__ + +#include "TensorRegistry.h" + +#include <backend/IConstantInitializer.h> +#include <ir/Operands.h> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class ConstantInitializer : public IConstantInitializer +{ +public: + ConstantInitializer(const ir::Operands &operands, + const std::shared_ptr<ITensorRegistry> &tensor_reg) + : IConstantInitializer{operands}, _tensor_reg{tensor_reg} + { + } + +private: + std::shared_ptr<ITensorRegistry> tensor_registry() const override { return _tensor_reg; } + +private: + std::shared_ptr<ITensorRegistry> _tensor_reg; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_COMPILER_CONTROLFLOW_CONSTANT_INITIALIZER_H__ diff --git a/runtime/onert/core/src/backend/controlflow/DynamicTensorManager.h b/runtime/onert/core/src/backend/controlflow/DynamicTensorManager.h new file mode 100644 index 000000000..c962d6ef1 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/DynamicTensorManager.h @@ -0,0 +1,38 @@ +/* + * 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_BACKEND_CONTROLFLOW_DYNAMICTENSOR_MANAGER_H__ +#define __ONERT_BACKEND_CONTROLFLOW_DYNAMICTENSOR_MANAGER_H__ + +#include "TensorRegistry.h" +#include "Tensor.h" + +#include <backend/cpu_common/DynamicTensorManager.h> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +using DynamicTensorManager = cpu_common::DynamicTensorManager; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_DYNAMICTENSOR_MANAGER_H__ diff --git a/runtime/onert/core/src/backend/controlflow/ExternalContext.h b/runtime/onert/core/src/backend/controlflow/ExternalContext.h new file mode 100644 index 000000000..3db6829a9 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/ExternalContext.h @@ -0,0 +1,80 @@ +/* + * 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_BACKEND_CONTROLFLOW_EXTERNAL_CONTEXT_H__ +#define __ONERT_BACKEND_CONTROLFLOW_EXTERNAL_CONTEXT_H__ + +#include <backend/IExternalContext.h> +#include <util/ConfigSource.h> + +#include <ruy/context.h> +#include <ruy/context_get_ctx.h> +#include <ruy/ctx.h> +#include <ruy/tune.h> + +namespace +{ +const int kDefaultNumThreadpoolThreads = 1; +} + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +// TODO Unify this with cpu::ExternalContext +class ExternalContext : public IExternalContext +{ +public: + ExternalContext() : _ruy_context(std::make_unique<ruy::Context>()) + { + setMaxNumThreads(onert::util::getConfigInt(onert::util::config::RUY_THREADS)); + initPerThreadState(); + } + + void setMaxNumThreads(int max_num_threads) + { + const int target_num_threads = + max_num_threads > -1 ? max_num_threads : kDefaultNumThreadpoolThreads; + _ruy_context->set_max_num_threads(target_num_threads); + } + + ruy::Context *ruy_context() const { return _ruy_context.get(); } + +private: + void initPerThreadState() + { + // Initialize per-thread state. + const int thread_count = _ruy_context->max_num_threads(); + auto ctx = ruy::get_ctx(_ruy_context.get()); + ctx->EnsureThreadSpecificResources(thread_count); + for (int i = 0; i < thread_count; i++) + { + ctx->GetThreadSpecificTuningResolver(i)->SetTuning(ctx->explicit_tuning()); + } + } + +private: + const std::unique_ptr<ruy::Context> _ruy_context; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_EXTERNAL_CONTEXT_H__ diff --git a/runtime/onert/core/src/backend/controlflow/KernelGenerator.cc b/runtime/onert/core/src/backend/controlflow/KernelGenerator.cc new file mode 100644 index 000000000..8e39ee527 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/KernelGenerator.cc @@ -0,0 +1,160 @@ +/* + * 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 "KernelGenerator.h" + +#include <backend/BackendContext.h> +#include <util/Utils.h> +#include "kernel/IfLayer.h" +#include "kernel/WhileLayer.h" +#include "kernel/PermuteLayer.h" +#include "exec/ExecutorBase.h" +#include "exec/FunctionSequence.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +KernelGenerator::KernelGenerator(const ir::Graph &graph, IDynamicTensorManager *dyn_tensor_manager, + const std::shared_ptr<TensorRegistry> &tensor_reg, + const std::shared_ptr<ExternalContext> &external_context) + : _graph{graph}, _dyn_tensor_manager{dyn_tensor_manager}, _tensor_reg{tensor_reg}, + _tensor_registries{}, _executor_map{nullptr}, _external_context{external_context} +{ + UNUSED_RELEASE(_graph); + UNUSED_RELEASE(_tensor_registries); + UNUSED_RELEASE(_executor_map); +} + +void KernelGenerator::visit(const ir::OpSequence &op_seq) +{ + assert(!_return_fn_seq); + assert(_dyn_tensor_manager); + assert(_tensor_reg); + + auto dyn_shape_inferer = + std::make_unique<exec::DynamicShapeInferer>(_graph.operands(), _tensor_reg); + + _return_fn_seq = std::make_unique<exec::FunctionSequence>(); + + // Prepare to handle dynamic tensors later + auto dyn_ctx = std::make_shared<exec::FunctionSequence::DynamicTensorCtx>(); + { + dyn_ctx->op_seq = &op_seq; + dyn_ctx->operations = &_graph.operations(); + dyn_ctx->dynamic_shape_inferer = std::move(dyn_shape_inferer); + dyn_ctx->dynamic_tensor_manager = _dyn_tensor_manager; + + _return_fn_seq->dynamic_tensor_ctx(dyn_ctx); + } + + for (const auto &op_idx : op_seq.operations()) + { + const auto &node = _graph.operations().at(op_idx); + node.accept(*this); + _return_fn_seq->append(releaseFunction()); + } +} + +void KernelGenerator::visit(const ir::operation::If &node) +{ + const auto then_subg_index = node.param().then_subg_index; + const auto else_subg_index = node.param().else_subg_index; + + std::vector<backend::ITensor *> input_tensors; + for (const auto input_index : node.getInputs()) + { + auto input_tensor = getTensor(input_index); + + input_tensors.emplace_back(input_tensor); + } + + std::vector<backend::ITensor *> output_tensors; + for (const auto output_index : node.getOutputs()) + { + auto output_tensor = getTensor(output_index); + output_tensors.emplace_back(output_tensor); + } + + // IfLayer just set ExecutorMap instead of then and else executor to avoid complexity of + // creating executor recusively + const auto cond_tensor = input_tensors.front(); + input_tensors.erase(input_tensors.begin()); + auto fn = std::make_unique<::onert::backend::controlflow::kernel::IfLayer>( + cond_tensor, input_tensors, output_tensors, node.getOutputs(), _graph, then_subg_index, + else_subg_index, _executor_map, _external_context); + + _return_fn = std::move(fn); +} + +void KernelGenerator::visit(const ir::operation::Permute &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + // Add PermuteLayer + std::vector<ITensor *> output_tensors{getTensor(output_index)}; + std::vector<ITensor *> input_tensors{getTensor(input_index)}; + + auto fn = + std::make_unique<kernel::PermuteLayer>(input_tensors, output_tensors, _external_context); + _return_fn = std::move(fn); +} + +void KernelGenerator::visit(const ir::operation::While &node) +{ + const auto cond_subg_index = node.param().cond_subg_index; + const auto body_subg_index = node.param().body_subg_index; + + // This op does not support input as a constant, because controlflow backend does not have + // TensorBuilder + std::vector<backend::ITensor *> input_tensors; + for (const auto input_index : node.getInputs()) + { + auto input_tensor = getTensor(input_index); + + input_tensors.emplace_back(input_tensor); + } + + std::vector<backend::ITensor *> output_tensors; + for (const auto output_index : node.getOutputs()) + { + auto output_tensor = getTensor(output_index); + output_tensors.emplace_back(output_tensor); + } + + // WhileLayer just set ExecutorMap instead of cond and body executor to avoid complexity of + // creating executor recusively + auto fn = std::make_unique<::onert::backend::controlflow::kernel::WhileLayer>( + input_tensors, output_tensors, node.getOutputs(), _graph, cond_subg_index, body_subg_index, + _executor_map, _external_context); + + _return_fn = std::move(fn); +} + +backend::ITensor *KernelGenerator::getTensor(const ir::OperandIndex &index) +{ + backend::ITensor *ret = _tensor_registries.getITensor(index); + assert(ret != nullptr); + return ret; +} + +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/KernelGenerator.h b/runtime/onert/core/src/backend/controlflow/KernelGenerator.h new file mode 100644 index 000000000..c2c124339 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/KernelGenerator.h @@ -0,0 +1,76 @@ +/* + * 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_BACKEND_CONTROLFLOW_KERNEL_GENERATOR_H__ +#define __ONERT_BACKEND_CONTROLFLOW_KERNEL_GENERATOR_H__ + +#include <backend/IKernelGenerator.h> +#include <backend/ITensorBuilder.h> +#include <exec/IExecutor.h> +#include "ExternalContext.h" +#include <ir/Graph.h> +#include "TensorBuilder.h" +#include "compiler/TensorRegistries.h" +#include "TensorRegistry.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class KernelGenerator : public IKernelGenerator +{ +public: + KernelGenerator(const ir::Graph &graph, IDynamicTensorManager *dyn_tensor_manager, + const std::shared_ptr<TensorRegistry> &tensor_reg, + const std::shared_ptr<ExternalContext> &external_context); + + void setTensorRegistries(const compiler::TensorRegistries &tensor_registries) + { + _tensor_registries = tensor_registries; + } + void setExecutorMap(const std::shared_ptr<exec::ExecutorMap> &executor_map) + { + // FIXME Using shared_ptr's raw pointer! + _executor_map = executor_map.get(); + } + + using IKernelGenerator::visit; + + void visit(const ir::OpSequence &) override; + void visit(const ir::operation::If &) override; + void visit(const ir::operation::Permute &) override; + void visit(const ir::operation::While &) override; + +private: + backend::ITensor *getTensor(const ir::OperandIndex &index); + +private: + const ir::Graph &_graph; + IDynamicTensorManager *_dyn_tensor_manager; + std::shared_ptr<TensorRegistry> _tensor_reg; + compiler::TensorRegistries _tensor_registries; + exec::ExecutorMap *_executor_map; + const std::shared_ptr<ExternalContext> _external_context; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_KERNEL_GENERATOR_H__ diff --git a/runtime/onert/core/src/backend/controlflow/Tensor.h b/runtime/onert/core/src/backend/controlflow/Tensor.h new file mode 100644 index 000000000..ba5bafd75 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/Tensor.h @@ -0,0 +1,35 @@ +/* + * 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_BACKEND_CONTROLFLOW_TENSOR_H__ +#define __ONERT_BACKEND_CONTROLFLOW_TENSOR_H__ + +#include <backend/cpu_common/Tensor.h> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +using Tensor = cpu_common::Tensor; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_TENSOR_H__ diff --git a/runtime/onert/core/src/backend/controlflow/TensorBuilder.cc b/runtime/onert/core/src/backend/controlflow/TensorBuilder.cc new file mode 100644 index 000000000..e4b0388f9 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/TensorBuilder.cc @@ -0,0 +1,117 @@ +/* + * 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 "TensorBuilder.h" + +#include <util/logging.h> + +#include <cassert> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +TensorBuilder::TensorBuilder(const std::shared_ptr<TensorRegistry> &tensor_reg) + : _tensor_reg{tensor_reg}, + _dynamic_tensor_mgr{new DynamicTensorManager(_tensor_reg->base_reg())}, + _static_tensor_mgr{new cpu_common::StaticTensorManager( + _tensor_reg->base_reg(), _dynamic_tensor_mgr->dynamic_mem_mgr().get())} +{ + /* empty */ +} + +void TensorBuilder::registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info, + ir::Layout backend_layout) +{ + _tensor_info_map.emplace(ind, info); + + _tensor_layout_map.insert({ind, backend_layout}); + + if (info.isDynamic()) + { + _dynamic_tensor_mgr->buildTensor(ind, info, _tensor_layout_map[ind]); + } + else + { + _static_tensor_mgr->buildTensor(ind, info, _tensor_layout_map[ind], info.isConstant()); + } +} + +void TensorBuilder::notifyFirstUse(const ir::OperandIndex &ind) +{ + // TODO Enhance the way of checking user tensors + if (_tensor_info_map.find(ind) == _tensor_info_map.end()) // Do not proceed for user tensors + return; + + const auto tensor_info = _tensor_info_map.at(ind); + + if (!nativeOwnTensorAt(ind)->is_dynamic()) + { + const auto size = tensor_info.total_size(); + _static_tensor_mgr->claimPlan(ind, size); + } +} + +void TensorBuilder::notifyLastUse(const ir::OperandIndex &ind) +{ + // TODO Enhance the way of checking user tensors + if (_tensor_info_map.find(ind) == _tensor_info_map.end()) // Do not proceed for user tensors + return; + + if (!nativeOwnTensorAt(ind)->is_dynamic()) + { + _static_tensor_mgr->releasePlan(ind); + } +} + +bool TensorBuilder::isRegistered(const ir::OperandIndex &ind) const +{ + // User tensors are not registered in _tensor_info_map but objects for them are exist + // in the tensor registry. + // TODO Enhance the way of checking user tensors + if (_tensor_reg->getITensor(ind)) + return true; + return _tensor_info_map.find(ind) != _tensor_info_map.end(); +} + +void TensorBuilder::prepare(void) +{ + _static_tensor_mgr->allocateConsts(); + _static_tensor_mgr->allocateNonconsts(); +} + +void TensorBuilder::allocate() +{ + // NOTE For now nothing to do. Allocation is done in prepare stage, which is not appropriate + // This is because CPU kernels require `ITensor`s to be allocated before Kernel Generation. +} + +IDynamicTensorManager *TensorBuilder::dynamicTensorManager(void) +{ + return _dynamic_tensor_mgr.get(); +} + +cpu_common::Tensor *TensorBuilder::nativeOwnTensorAt(const ir::OperandIndex &ind) +{ + return _tensor_reg->getNativeOwnTensor(ind); +} + +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/TensorBuilder.h b/runtime/onert/core/src/backend/controlflow/TensorBuilder.h new file mode 100644 index 000000000..695994761 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/TensorBuilder.h @@ -0,0 +1,83 @@ +/* + * 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_BACKEND_CONTROLFLOW_TENSOR_BUILDER_H__ +#define __ONERT_BACKEND_CONTROLFLOW_TENSOR_BUILDER_H__ + +#include <backend/cpu_common/StaticTensorManager.h> +#include <backend/cpu_common/TensorRegistry.h> +#include <backend/cpu_common/Tensor.h> + +#include <backend/ITensorBuilder.h> +#include <ir/OperandIndexMap.h> + +#include <unordered_map> + +#include "DynamicTensorManager.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +class TensorBuilder : public ITensorBuilder +{ +public: + TensorBuilder(const std::shared_ptr<TensorRegistry> &tensor_reg); + + /** + * @brief Register tensor information to allocate on CPU backend + * @param[in] ind Operand index + * @param[in] info Operand information + * @param[in] layout Operand data layout + */ + void registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info, + ir::Layout backend_layout) override; + + void notifyFirstUse(const ir::OperandIndex &) override; + void notifyLastUse(const ir::OperandIndex &) override; + + bool isRegistered(const ir::OperandIndex &) const override; + + void prepare(void) override; + void allocate() override; + void postFunctionPrepare() override { /* DO NOTHING */} + + IDynamicTensorManager *dynamicTensorManager(void) override; + + /** + * @brief Get tensor with a specific OperandIndex. + * @param ind OperandIndex for the tensor. There must exist a tensor with this ind. + * If not, program will crash with assert or exception. + * @return operand::Tensor * + */ + cpu_common::Tensor *nativeOwnTensorAt(const ir::OperandIndex &ind); + +private: + const std::shared_ptr<TensorRegistry> _tensor_reg; + std::unique_ptr<DynamicTensorManager> _dynamic_tensor_mgr; + std::unique_ptr<cpu_common::StaticTensorManager> _static_tensor_mgr; + ir::OperandIndexMap<ir::OperandInfo> _tensor_info_map; + ir::OperandIndexMap<ir::Layout> _tensor_layout_map; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_TENSOR_BUILDER_H__ diff --git a/runtime/onert/core/src/backend/controlflow/TensorRegistry.h b/runtime/onert/core/src/backend/controlflow/TensorRegistry.h new file mode 100644 index 000000000..94f71bb9c --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/TensorRegistry.h @@ -0,0 +1,133 @@ +/* + * 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_BACKEND_CONTROLFLOW_TENSOR_REGISTRY_H__ +#define __ONERT_BACKEND_CONTROLFLOW_TENSOR_REGISTRY_H__ + +#include "backend/cpu_common/TensorRegistry.h" +#include "backend/ITensorRegistry.h" +#include "Tensor.h" +#include "UserTensor.h" +#include <assert.h> + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +/** + * @brief Tensor registry class for controlflow backend + * + * This class contains three types of tensors. Two native tensors(tensors that are managed by this + * backend) and the other is migrant tensor. + * + * - NativeUserTensor - @c UserTensor managed by this backend, buffer is user-given + * - NativeOwnTensor - @c cpu_common::Tensor managed by this backend ( in @c _base_reg ) + * - MigrantTensor - @c IPortableTensor managed by other backends ( in @c _base_reg ) + * + * @note @c _base_reg is used in implementation to reuse @c cpu_common::StaticTensorManager + * + */ +class TensorRegistry : public ITensorRegistry +{ +public: + TensorRegistry() : _base_reg{new cpu_common::TensorRegistry} {} + + ITensor *getITensor(const ir::OperandIndex &ind) override + { + auto base_tensor = _base_reg->getITensor(ind); + if (base_tensor) + return base_tensor; + return getNativeUserTensor(ind); + } + + ITensor *getNativeITensor(const ir::OperandIndex &ind) override + { + auto base_tensor = _base_reg->getNativeITensor(ind); + if (base_tensor) + return base_tensor; + return getNativeUserTensor(ind); + } + + IPortableTensor *getPortableTensor(const ir::OperandIndex &ind) + { + auto base_tensor = _base_reg->getPortableTensor(ind); + if (base_tensor) + return base_tensor; + return getNativeUserTensor(ind); + } + + IPortableTensor *getNativeTensor(const ir::OperandIndex &ind) + { + auto base_tensor = _base_reg->getNativeTensor(ind); + if (base_tensor) + return base_tensor; + return getNativeUserTensor(ind); + } + + Tensor *getNativeOwnTensor(const ir::OperandIndex &ind) + { + return _base_reg->getNativeTensor(ind); + } + + UserTensor *getNativeUserTensor(const ir::OperandIndex &ind) + { + auto tensor = _native_user_tensors.find(ind); + if (tensor != _native_user_tensors.end()) + return tensor->second.get(); + return nullptr; + } + + bool setMigrantTensor(const ir::OperandIndex &ind, IPortableTensor *tensor) override + { + assert(tensor); + assert(!getITensor(ind)); // For the ind, tensor is not registered yet + _base_reg->setMigrantTensor(ind, tensor); + return true; + } + + void setNativeOwnTensor(ir::OperandIndex ind, std::unique_ptr<Tensor> &&tensor) + { + assert(tensor); + assert(!getITensor(ind)); // For the ind, tensor is not registered yet + _base_reg->setNativeTensor(ind, std::move(tensor)); + } + + void setNativeUserTensor(ir::OperandIndex ind, std::unique_ptr<UserTensor> &&tensor) + { + assert(tensor); + assert(!getITensor(ind)); // For the ind, tensor is not registered yet + _native_user_tensors[ind] = std::move(tensor); + } + + const ir::OperandIndexMap<std::unique_ptr<UserTensor>> &native_user_tensors() + { + return _native_user_tensors; + } + std::shared_ptr<cpu_common::TensorRegistry> base_reg() { return _base_reg; } + +private: + std::shared_ptr<cpu_common::TensorRegistry> _base_reg; + ir::OperandIndexMap<std::unique_ptr<UserTensor>> _native_user_tensors; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // ifndef __ONERT_BACKEND_CONTROLFLOW_TENSOR_REGISTRY_H__ diff --git a/runtime/onert/core/src/backend/controlflow/UserTensor.cc b/runtime/onert/core/src/backend/controlflow/UserTensor.cc new file mode 100644 index 000000000..5081a90ea --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/UserTensor.cc @@ -0,0 +1,53 @@ +/* + * 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 "UserTensor.h" + +#include "util/Exceptions.h" +#include "ir/DataType.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +size_t UserTensor::calcOffset(const ir::Coordinates &coords) const +{ + size_t rank = num_dimensions(); + size_t offset = 0; + for (size_t i = 0; i < rank; ++i) + { + offset = offset * dimension(i) + coords[i]; + } + offset *= sizeOfDataType(data_type()); + return offset; +} + +bool UserTensor::applyShape(const ir::Shape &new_shape) +{ + // User tensors cannot be reallocated. + auto new_size = new_shape.num_elements() * ir::sizeOfDataType(data_type()); + if (total_size() < new_size) + throw InsufficientBufferSizeException{"User given buffer size is too small."}; + setShape(new_shape); + return true; +} + +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/UserTensor.h b/runtime/onert/core/src/backend/controlflow/UserTensor.h new file mode 100644 index 000000000..7aa62a8a9 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/UserTensor.h @@ -0,0 +1,85 @@ +/* + * 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_BACKEND_CONTROLFLOW_USER_TENSOR_H__ +#define __ONERT_BACKEND_CONTROLFLOW_USER_TENSOR_H__ + +#include "ir/OperandInfo.h" +#include "backend/IPortableTensor.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ + +/** + * @brief Tensor object that is for Input and Output tensors from the user. + * + * This class is a wrapped buffer that is allocated by the user. So it does not have resposibility + * on allocation nor deallocation. All the model input/output tensors are wrapped with this class + * for execution. + * + */ +class UserTensor : public IPortableTensor +{ +public: + UserTensor(const ir::OperandInfo &info, ir::Layout layout, uint8_t *buffer, size_t size) + : IPortableTensor{info}, _layout{layout}, _buffer{buffer}, _size{size}, _dynamic{false} + { + } + + UserTensor(const ir::OperandInfo &info, ir::Layout layout) : UserTensor{info, layout, nullptr, 0} + { + } + +public: + void setBuffer(uint8_t *buffer, size_t size) + { + _buffer = buffer; + _size = size; + } + +public: + uint8_t *buffer() const override { return _buffer; } + size_t total_size() const override { return _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 { return _layout; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + float data_scale() const override { return _info.typeInfo().scale(); } + int32_t data_offset() const override { return _info.typeInfo().offset(); } + bool is_dynamic() const override { return _dynamic; } + void set_dynamic() override { _dynamic = true; } + ir::Shape getShape() const override { return _info.shape(); } + void setShape(const ir::Shape &new_shape) override { _info.shape(new_shape); } + bool is_constant() const override { return false; } + bool applyShape(const ir::Shape &) override; + +private: + ir::Layout _layout; + uint8_t *_buffer; + size_t _size; + bool _dynamic; +}; + +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_USER_TENSOR_H__ diff --git a/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.cc b/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.cc new file mode 100644 index 000000000..de91b850a --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.cc @@ -0,0 +1,131 @@ +/* + * 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 "IfLayer.h" + +#include <backend/ITensor.h> +#include "exec/ExecutorBase.h" +#include <misc/polymorphic_downcast.h> +#include "PermuteLayer.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +IfLayer::IfLayer(backend::ITensor *cond_tensor, const std::vector<backend::ITensor *> input_tensors, + const std::vector<backend::ITensor *> output_tensors, + const ir::OperandIndexSequence &output_indices, const ir::Graph &graph, + const ir::SubgraphIndex &then_subg_index, const ir::SubgraphIndex &else_subg_index, + exec::ExecutorMap *executor_map, + const std::shared_ptr<ExternalContext> &external_context) + : _cond_tensor{cond_tensor}, _input_tensors{input_tensors}, _output_tensors{output_tensors}, + _output_indices{output_indices}, _graph{graph}, _then_subg_index{then_subg_index}, + _else_subg_index{else_subg_index}, _executor_map{executor_map}, + _external_context{external_context} +{ + // At this point, executor_map may not have executors of then subg and else subg +} + +void IfLayer::run() +{ + // Check condition + // // If true + // // // Copy _input_tensors -> then subg's inputs + // // // Run then subg + // // // Copy outputs of then subg -> _output_tensors + // // Else + // // // Copy _input_tensors -> else subg's inputs if false + // // // Run else subg + // // // Copy outputs of else subg -> _output_tensors + auto getResultCond = [](backend::ITensor *tensor) -> bool { + bool ret = false; + tensor->access([&](ITensor &tensor) { ret = *reinterpret_cast<bool *>(tensor.buffer()); }); + return ret; + }; + + exec::ExecutorBase *subg_exec = nullptr; + bool cond_result = getResultCond(_cond_tensor); + if (cond_result) + { + VERBOSE(If) << "Call to $" << _then_subg_index << " (then)" << std::endl; + subg_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>( + _executor_map->at(_then_subg_index).get()); + } + else + { + VERBOSE(If) << "Call to $" << _else_subg_index << " (else)" << std::endl; + subg_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>( + _executor_map->at(_else_subg_index).get()); + } + + const auto &subg_graph = subg_exec->graph(); + + std::vector<backend::ITensor *> src_tensors; + std::vector<backend::ITensor *> dst_tensors; + // Add tensors used in subgraph or contained in outputs of subgraph + assert(subg_graph.getInputs().size() == _input_tensors.size()); + assert(subg_graph.getInputs().size() == subg_exec->getInputTensors().size()); + for (uint32_t i = 0; i < subg_graph.getInputs().size(); ++i) + { + const auto &subg_input_index = subg_graph.getInputs().at(i); + const auto &subg_input = subg_graph.operands().at(subg_input_index); + if (subg_input.getUses().size() > 0 || subg_graph.getOutputs().contains(subg_input_index)) + { + src_tensors.emplace_back(_input_tensors.at(i)); + dst_tensors.emplace_back(subg_exec->getInputTensors().at(i)); + } + } + const auto permute_op_input_to_subg_input = + std::make_shared<PermuteLayer>(src_tensors, dst_tensors, _external_context); + + // Add tensors used as output of operation or contained in outputs of operation + src_tensors.clear(); + dst_tensors.clear(); + assert(_output_indices.size() == subg_exec->getOutputTensors().size()); + assert(_output_indices.size() == _output_tensors.size()); + for (uint32_t i = 0; i < _output_indices.size(); ++i) + { + const auto &output_index = _output_indices.at(i); + const auto &output = _graph.operands().at(output_index); + if (output.getUses().size() > 0 || _graph.getOutputs().contains(output_index)) + { + src_tensors.emplace_back(subg_exec->getOutputTensors().at(i)); + dst_tensors.emplace_back(_output_tensors.at(i)); + } + } + const auto permute_subg_output_to_op_output = + std::make_shared<PermuteLayer>(src_tensors, dst_tensors, _external_context); + + // Remove copying of unused tensor + permute_op_input_to_subg_input->prepare(); + permute_subg_output_to_op_output->prepare(); + + // Copy & run + subg_exec->execute(_input_tensors, permute_op_input_to_subg_input); + permute_subg_output_to_op_output->run(); + VERBOSE(If) << "Return from $" << (cond_result ? _then_subg_index : _else_subg_index) + << std::endl; +} + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.h b/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.h new file mode 100644 index 000000000..9e944bccc --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/IfLayer.h @@ -0,0 +1,63 @@ +/* + * 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_BACKEND_CONTROLFLOW_KERNEL_IF_LAYER_H__ +#define __ONERT_BACKEND_CONTROLFLOW_KERNEL_IF_LAYER_H__ + +#include <backend/ITensor.h> +#include <exec/IExecutor.h> +#include "../ExternalContext.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +class IfLayer : public ::onert::exec::IFunction +{ +public: + IfLayer(backend::ITensor *cond_tensor, const std::vector<backend::ITensor *> input_tensors, + const std::vector<backend::ITensor *> output_tensors, + const ir::OperandIndexSequence &output_indices, const ir::Graph &graph, + const ir::SubgraphIndex &then_subg_index, const ir::SubgraphIndex &else_subg_index, + exec::ExecutorMap *executor_map, + const std::shared_ptr<ExternalContext> &external_context); + +public: + void run() override; + +private: + backend::ITensor *_cond_tensor; + const std::vector<backend::ITensor *> _input_tensors; + const std::vector<backend::ITensor *> _output_tensors; + const ir::OperandIndexSequence &_output_indices; + const ir::Graph &_graph; + const ir::SubgraphIndex _then_subg_index; + const ir::SubgraphIndex _else_subg_index; + exec::ExecutorMap *_executor_map; + const std::shared_ptr<ExternalContext> _external_context; +}; + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_KERNEL_IF_LAYER_H__ diff --git a/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.cc b/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.cc new file mode 100644 index 000000000..8b79ea070 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.cc @@ -0,0 +1,311 @@ +/* + * 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 "PermuteLayer.h" + +#include "exec/ShapeConverter.h" + +#include "ruy/context.h" // from @ruy + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +PermuteLayer::PermuteLayer(const std::vector<ITensor *> &src_tensors, + const std::vector<ITensor *> &dst_tensors, + const std::shared_ptr<ExternalContext> &external_context) + : _external_context{external_context}, _tasks_map{} +{ + assert(src_tensors.size() == dst_tensors.size()); + _src_tensors = src_tensors; + _dst_tensors = dst_tensors; + _src_tensors_offsets.resize(src_tensors.size()); + _dst_tensors_offsets.resize(dst_tensors.size()); +} + +void PermuteLayer::optimize() +{ + // Remove copying of tensor as nullptr + auto src_it = _src_tensors.begin(); + auto dst_it = _dst_tensors.begin(); + auto src_offsets_it = _src_tensors_offsets.begin(); + auto dst_offsets_it = _dst_tensors_offsets.begin(); + while (src_it != _src_tensors.end()) + { + if ((*src_it == *dst_it) || (*src_it == nullptr || *dst_it == nullptr)) + { + src_it = _src_tensors.erase(src_it); + dst_it = _dst_tensors.erase(dst_it); + src_offsets_it = _src_tensors_offsets.erase(src_offsets_it); + dst_offsets_it = _dst_tensors_offsets.erase(dst_offsets_it); + } + else + { + auto src = *src_it; + auto dst = *dst_it; + src_offsets_it->resize(0); + dst_offsets_it->resize(0); + if (underlying_type(src->data_type()) != underlying_type(dst->data_type())) + throw std::runtime_error("data type does not match"); + const auto permute_type = [&]() -> PermuteType { + if (src->num_dimensions() == 4 && src->layout() == ir::Layout::NHWC && + dst->layout() == ir::Layout::NCHW) + { + return PermuteType::NHWC_TO_NCHW; + } + else if (src->num_dimensions() == 4 && src->layout() == ir::Layout::NCHW && + dst->layout() == ir::Layout::NHWC) + { + return PermuteType::NCHW_TO_NHWC; + } + else + { + return PermuteType::COPY; + } + }(); + auto fn = [&](backend::ITensor &src_tensor) { + dst->access([&](backend::ITensor &dst_tensor) { + // NOTE The buffer of both tensor can be nullptr in this step + const auto data_size = ir::sizeOfDataType(src_tensor.data_type()); + + if (permute_type == PermuteType::COPY) + { + if ((!src_tensor.has_padding() && !dst_tensor.has_padding())) + { + const auto num_elements = src_tensor.getShape().num_elements(); + const int thread_count = _external_context->ruy_context()->max_num_threads() < + static_cast<int>(num_elements) + ? _external_context->ruy_context()->max_num_threads() + : num_elements; + + std::vector<PermuteWorkerTask> tasks; + auto start = 0; + for (auto i = 0; i < thread_count; ++i) + { + int end = start + (num_elements - start) / (thread_count - i); + tasks.emplace_back(src_tensor.buffer(), dst_tensor.buffer(), start * data_size, + start * data_size, (end - start) * data_size); + start = end; + } + assert(tasks.size() >= 1); + _tasks_map[src] = std::move(tasks); + } + else + { + auto loop_shape = src_tensor.getShape(); + + auto copy_axis = loop_shape.rank() - 1; + copy_axis = copy_axis < 0 ? 1 : copy_axis; + const auto copy_len = loop_shape.dim(copy_axis) * data_size; + loop_shape.dim(copy_axis) = 1; + + appendPermuteTasks(src, dst, loop_shape, copy_len); + } + } + else + { + assert(src_tensor.num_dimensions() == 4 && (permute_type == PermuteType::NHWC_TO_NCHW || + permute_type == PermuteType::NCHW_TO_NHWC)); + const auto loop_shape = src_tensor.getShape(); + const auto copy_len = data_size; + + appendPermuteTasks(src, dst, loop_shape, copy_len); + } + }); + }; + src->access(fn); + src_it++; + dst_it++; + src_offsets_it++; + dst_offsets_it++; + } + } +} + +void PermuteLayer::appendPermuteTasks(const ITensor *src_tensor, ITensor *dst_tensor, + const ir::Shape &loop_shape, size_t size) +{ + size_t distributed_dim = 0; + if (src_tensor->layout() == dst_tensor->layout()) + { + for (size_t i = 1; i < src_tensor->num_dimensions() - 1; ++i) + { + distributed_dim = + src_tensor->dimension(distributed_dim) < src_tensor->dimension(i) ? i : distributed_dim; + } + } + const auto distributed_dim_val = src_tensor->dimension(distributed_dim); + const int thread_count = + _external_context->ruy_context()->max_num_threads() < static_cast<int>(distributed_dim_val) + ? _external_context->ruy_context()->max_num_threads() + : distributed_dim_val; + // NOTE Do not remove this assertion. It would cause performance degradation by new threads to be + // created in the context's thread pool + assert(thread_count <= _external_context->ruy_context()->max_num_threads()); + + std::vector<PermuteWorkerTask> tasks; + int start = 0; + auto one_thread_loop_shape = loop_shape; + for (auto i = 0; i < thread_count; ++i) + { + ir::Coordinates start_coords(one_thread_loop_shape.rank()); + start_coords.set(distributed_dim, start); + int end = start + (distributed_dim_val - start) / (thread_count - i); + one_thread_loop_shape.dim(distributed_dim) = end - start; + tasks.emplace_back(*src_tensor, *dst_tensor, start_coords, one_thread_loop_shape, size); + start = end; + } + assert(tasks.size() >= 1); + _tasks_map[src_tensor] = std::move(tasks); +} + +void PermuteLayer::runPermuteTasks(backend::ITensor *src, uint8_t *dst_buffer) +{ + assert(src->getShape().num_elements() * ir::sizeOfDataType(src->data_type()) <= + src->total_size()); + std::vector<PermuteWorkerTask> &tasks = _tasks_map.at(src); + for (size_t i = 0; i < tasks.size(); ++i) + { + tasks.at(i).setBuffers(src->buffer(), dst_buffer); + } + assert(tasks.size() >= 1); + _external_context->ruy_context()->mutable_thread_pool()->Execute(tasks.size(), tasks.data()); +} + +void PermuteLayer::run() +{ + assert(_src_tensors.size() == _dst_tensors.size()); + // PermuteLayer infers dynamic shape inside itself whenever run is called for the following + // reasons: + // 1. PermuteLayer has to access dynamic tensor manager for input/output tensors of other backends + // 2. Other controlflow operation(If/While) uses this layout for copying tensors of other + // subgraphs(with other backends) + // 3. This infering code is placed here to avoid duplicated code that can be caused by above 2 + // reasons + + // check if output is not dynamic + for (size_t i = 0; i < _src_tensors.size(); ++i) + { + auto dst_tensor = _dst_tensors.at(i); + auto src_tensor = _src_tensors.at(i); + if (src_tensor->is_dynamic() || dst_tensor->is_dynamic()) + { + // getting output shape + auto src_shape = src_tensor->getShape(); + + // set output shape and output buffer + ir::Shape new_shape = + exec::convertShape(src_shape, src_tensor->layout(), dst_tensor->layout()); + + try + { + if (!dst_tensor->applyShape(new_shape)) + throw std::runtime_error{ + "Error: PermuteLayer: output's TensorManager does not support dynamic tensor"}; + assert(dst_tensor->buffer() != nullptr); + } + catch (const std::out_of_range &e) + { + std::cerr << "Error: out_of_range in PermuteLayer: output's TensorManager does not support " + "dynamic tensor" + << '\n'; + throw; + } + } + assert(exec::convertShape(src_tensor->getShape(), src_tensor->layout(), dst_tensor->layout()) == + dst_tensor->getShape()); + } + assert(_src_tensors.size() == _dst_tensors.size()); + assert(_src_tensors.size() == _src_tensors_offsets.size()); + assert(_dst_tensors.size() == _dst_tensors_offsets.size()); + auto src_it = _src_tensors.begin(); + auto dst_it = _dst_tensors.begin(); + auto src_offsets_it = _src_tensors_offsets.begin(); + auto dst_offsets_it = _dst_tensors_offsets.begin(); + while (src_it != _src_tensors.end()) + { + auto src = *src_it; + auto dst = *dst_it; + auto &src_offsets = *src_offsets_it; + auto &dst_offsets = *dst_offsets_it; + + if (src->total_size() == 0) + { + assert(dst->total_size() == 0); + } + else + { + if (src != dst) + { + // Conditions to run permutation with multithreading + // 1. The tasks for multithreathing was created + // 2. The tasks's size > 1 + // 3. Both tensors are not dynamic + if (_tasks_map.find(src) == _tasks_map.end() || _tasks_map.at(src).size() == 1 || + src->is_dynamic() || dst->is_dynamic()) + { + permute(src, dst, src->num_dimensions(), src_offsets, dst_offsets); + } + // If dst is subtensor, we have to use clEnqueueMapBuffer instead of clEnqueueWirteBuffer + else if (dst->needMemoryMap() && !dst->is_subtensor()) + { + if (!src->has_padding() && !dst->has_padding() && src->layout() == dst->layout()) + { + // This is more effective than multi-threading + src->access([&](backend::ITensor &) { dst->enqueueWriteBuffer(src->buffer(), false); }); + } + else + { + // TODO Optimize this block in case of that padding size of dst is big. + _buffers_map[dst].reserve(dst->total_size()); + auto dst_buffer = _buffers_map[dst].data(); + + src->access([&](backend::ITensor &) { runPermuteTasks(src, dst_buffer); }); + dst->enqueueWriteBuffer(dst_buffer, false); + } + } + else if (src->needMemoryMap() && !src->is_subtensor() && !src->has_padding() && + !dst->has_padding() && src->layout() == dst->layout()) + { + // This is more effective than multi-threading + assert(!dst->needMemoryMap()); + dst->access([&](backend::ITensor &) { src->enqueueReadBuffer(dst->buffer(), true); }); + } + else + { + auto fn = [&](backend::ITensor &) { + dst->access([&](backend::ITensor &) { runPermuteTasks(src, dst->buffer()); }); + }; + src->access(fn); + } + } + } + src_it++; + dst_it++; + src_offsets_it++; + dst_offsets_it++; + } +} + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.h b/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.h new file mode 100644 index 000000000..5d0f1918e --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/PermuteLayer.h @@ -0,0 +1,150 @@ +/* + * 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_BACKEND_CONTROLFLOW_KERNEL_PERMUTELAYER_H__ +#define __ONERT_BACKEND_CONTROLFLOW_KERNEL_PERMUTELAYER_H__ + +#include "backend/ITensorBuilder.h" +#include "exec/IPermuteFunction.h" +#include "exec/IExecutor.h" +#include "../ExternalContext.h" +#include "ruy/thread_pool.h" // from @ruy + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +class PermuteLayer : public onert::exec::IPermuteFunction +{ +public: + PermuteLayer(const std::vector<ITensor *> &src_tensors, const std::vector<ITensor *> &dst_tensors, + const std::shared_ptr<ExternalContext> &external_context); + + void optimize() override; + + void run() override; + +private: + std::shared_ptr<ExternalContext> _external_context; + +private: + void appendPermuteTasks(const ITensor *src_tensor, ITensor *dst_tensor, + const ir::Shape &loop_shape, size_t size); + + void runPermuteTasks(backend::ITensor *src, uint8_t *dst_buffer); + + struct PermuteWorkerTask : ruy::Task + { + using Strides = ir::Coordinates; + + PermuteWorkerTask(const ITensor &src_tensor, ITensor &dst_tensor, + const ir::Coordinates &start_coords, const ir::Shape &loop_shape, size_t size) + : _src_buffer{src_tensor.buffer()}, _dst_buffer{dst_tensor.buffer()}, + _src_start_offset{src_tensor.calcOffset(start_coords)}, + _dst_start_offset{dst_tensor.calcOffset(start_coords)}, _src_strides{}, _dst_strides{}, + _loop_shape{loop_shape}, _size{size}, _src_layout{src_tensor.layout()}, + _dst_layout{dst_tensor.layout()}, _is_permutation{true} + { + // Set strides + setStrides(src_tensor, &_src_strides); + setStrides(dst_tensor, &_dst_strides); + + _is_permutation = (_src_layout != _dst_layout && loop_shape.rank() == 4); + } + // Constructor for a copy + PermuteWorkerTask(const uint8_t *src_buffer, uint8_t *dst_buffer, uint32_t src_start_offset, + uint32_t dst_start_offset, size_t size) + : _src_buffer{src_buffer}, _dst_buffer{dst_buffer}, _src_start_offset{src_start_offset}, + _dst_start_offset{dst_start_offset}, _src_strides{0}, _dst_strides{0}, _loop_shape{1}, + _size{size}, _src_layout{}, _dst_layout{}, _is_permutation{false} + { + // DO NOTHING + } + void setBuffers(const uint8_t *src_buffer, uint8_t *dst_buffer) + { + _src_buffer = src_buffer; + _dst_buffer = dst_buffer; + } + void Run() override + { + ShapeLoop(_loop_shape, [&](const onert::ir::Coordinates &coords) { + size_t src_offset = _src_start_offset; + size_t dst_offset = _dst_start_offset; + assert(static_cast<size_t>(_loop_shape.rank()) == coords.size()); + ir::Coordinates dst_coords = coords; + if (_is_permutation) + { + dst_coords = ir::convertCoordinates(coords, _src_layout, _dst_layout); + } + for (auto i = 0; i < _loop_shape.rank(); ++i) + { + assert(coords[i] >= 0 && dst_coords[i] >= 0); + src_offset += coords[i] * _src_strides[i]; + dst_offset += dst_coords[i] * _dst_strides[i]; + } + memcpy(_dst_buffer + dst_offset, _src_buffer + src_offset, _size); + }); + } + + private: + void setStrides(const ITensor &tensor, Strides *strides) + { + const size_t rank = tensor.num_dimensions(); + for (size_t i = 0; i < rank; ++i) + { + ir::Coordinates no_step(rank), one_step(rank); + one_step.set(i, 1); + if (tensor.dimension(i) > 1) + { + strides->set(i, tensor.calcOffset(one_step) - tensor.calcOffset(no_step)); + } + else + { + // If dimension value is 0 or 1, the stride of the dimension will be not used + // Do not call calcOffset() with coordinate value that is greater than dimension value + strides->set(i, 0); + } + assert((*strides)[i] >= 0); + } + } + + private: + const uint8_t *_src_buffer; + uint8_t *_dst_buffer; + size_t _src_start_offset; + size_t _dst_start_offset; + Strides _src_strides; + Strides _dst_strides; + const ir::Shape _loop_shape; + const size_t _size; + const ir::Layout _src_layout; + const ir::Layout _dst_layout; + bool _is_permutation; + }; + std::unordered_map<const ITensor *, std::vector<PermuteWorkerTask>> _tasks_map; +}; + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_KERNEL_PERMUTELAYER_H__ diff --git a/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.cc b/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.cc new file mode 100644 index 000000000..a0d478603 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.cc @@ -0,0 +1,222 @@ +/* + * 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 "WhileLayer.h" + +#include <backend/ITensor.h> +#include "exec/ExecutorBase.h" +#include <misc/polymorphic_downcast.h> +#include "PermuteLayer.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +WhileLayer::WhileLayer(const std::vector<backend::ITensor *> input_tensors, + const std::vector<backend::ITensor *> output_tensors, + const ir::OperandIndexSequence &output_indices, const ir::Graph &graph, + const ir::SubgraphIndex &cond_subg_index, + const ir::SubgraphIndex &body_subg_index, exec::ExecutorMap *executor_map, + const std::shared_ptr<ExternalContext> &external_context) + : _cond_subg_index{cond_subg_index}, _body_subg_index{body_subg_index}, + _output_indices{output_indices}, _graph{graph}, _input_tensors{input_tensors}, + _output_tensors{output_tensors}, _executor_map{executor_map}, + _external_context{external_context} +{ + // At this point, executor_map may not have executors of cond subg and body subg +} + +void WhileLayer::run() +{ + // Copy "_input_tensors" -> "cond subg inputs" + // Run cond subg + // Start loop while output of cond subg is ture + // // Copy "_input_tensors" -> "body subg inputs" in the first iteration, then copy "body subg + // outputs" -> "body subg inputs" in the second or more iterations + // // Run body subg + // // Copy "body subg outputs" -> "cond subg inputs" + // // Run cond subg + // If there is no loop copy "_input_tensors" -> "_dst_tensors", else copy "cond subg inputs" -> + // "_dst_tensors" + auto cond_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>( + _executor_map->at(_cond_subg_index).get()); + auto body_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>( + _executor_map->at(_body_subg_index).get()); + + const auto &cond_graph = cond_exec->graph(); + const auto &body_graph = body_exec->graph(); + + std::vector<backend::ITensor *> input_tensors; + std::vector<backend::ITensor *> cond_input_tensors; + std::vector<backend::ITensor *> body_input_tensors; + std::vector<backend::ITensor *> body_output_tensors; + std::vector<backend::ITensor *> output_tensors; + + // Add only used tensors in cond subgraph + assert(cond_graph.getInputs().size() == _input_tensors.size()); + assert(cond_graph.getInputs().size() == cond_exec->getInputTensors().size()); + for (uint32_t i = 0; i < cond_graph.getInputs().size(); ++i) + { + const auto &cond_input = cond_graph.operands().at(cond_graph.getInputs().at(i)); + if (cond_input.getUses().size() > 0) + { + input_tensors.emplace_back(_input_tensors.at(i)); + cond_input_tensors.emplace_back(cond_exec->getInputTensors().at(i)); + } + } + const auto permute_op_input_to_cond_input = + std::make_shared<PermuteLayer>(input_tensors, cond_input_tensors, _external_context); + + // Add only used tensors among outputs of while operation + assert(_output_indices.size() == _input_tensors.size()); + assert(_output_indices.size() == _output_tensors.size()); + input_tensors.clear(); + output_tensors.clear(); + for (size_t i = 0; i < _output_indices.size(); ++i) + { + const auto &output_index = _output_indices.at(i); + const auto &output = _graph.operands().at(output_index); + if (output.getUses().size() > 0 || _graph.getOutputs().contains(output_index)) + { + input_tensors.emplace_back(_input_tensors.at(i)); + output_tensors.emplace_back(_output_tensors.at(i)); + } + } + const auto permute_op_input_to_op_output = + std::make_shared<PermuteLayer>(input_tensors, output_tensors, _external_context); + + // Add all tensors with unused tensors in body subgraph because unused input tensors will be + // copied output tensors in body subgraph + assert(_input_tensors.size() == body_exec->getInputTensors().size()); + input_tensors = _input_tensors; + body_input_tensors = body_exec->getInputTensors(); + const auto permute_op_input_to_body_input = + std::make_shared<PermuteLayer>(input_tensors, body_input_tensors, _external_context); + + // Add only used tensors in cond subgraph + assert(cond_graph.getInputs().size() == body_exec->getOutputTensors().size()); + assert(cond_graph.getInputs().size() == cond_exec->getInputTensors().size()); + body_output_tensors.clear(); + cond_input_tensors.clear(); + for (uint32_t i = 0; i < cond_graph.getInputs().size(); ++i) + { + const auto &cond_input = cond_graph.operands().at(cond_graph.getInputs().at(i)); + if (cond_input.getUses().size() > 0) + { + body_output_tensors.emplace_back(body_exec->getOutputTensors().at(i)); + cond_input_tensors.emplace_back(cond_exec->getInputTensors().at(i)); + } + } + const auto permute_body_output_to_cond_input = + std::make_shared<PermuteLayer>(body_output_tensors, cond_input_tensors, _external_context); + + // Add only used tensors in body subgraph + assert(body_graph.getInputs().size() == body_exec->getOutputTensors().size()); + assert(body_graph.getInputs().size() == body_exec->getInputTensors().size()); + body_output_tensors.clear(); + body_input_tensors.clear(); + for (uint32_t i = 0; i < body_graph.getInputs().size(); ++i) + { + const auto &body_input_index = body_graph.getInputs().at(i); + const auto &body_input = body_graph.operands().at(body_input_index); + if (body_input.getUses().size() > 0 && + !body_exec->graph().getOutputs().contains(body_input_index)) + { + body_output_tensors.emplace_back(body_exec->getOutputTensors().at(i)); + body_input_tensors.emplace_back(body_exec->getInputTensors().at(i)); + } + } + const auto permute_body_output_to_body_input = + std::make_shared<PermuteLayer>(body_output_tensors, body_input_tensors, _external_context); + + // Add only used tensors among outputs of while operation + assert(_output_indices.size() == body_exec->getOutputTensors().size()); + assert(_output_indices.size() == _output_tensors.size()); + body_output_tensors.clear(); + output_tensors.clear(); + for (size_t i = 0; i < _output_indices.size(); ++i) + { + const auto &output_index = _output_indices.at(i); + const auto &output = _graph.operands().at(output_index); + if (output.getUses().size() > 0 || _graph.getOutputs().contains(output_index)) + { + body_output_tensors.emplace_back(body_exec->getOutputTensors().at(i)); + output_tensors.emplace_back(_output_tensors.at(i)); + } + } + const auto permute_body_output_to_op_output = + std::make_shared<PermuteLayer>(body_output_tensors, output_tensors, _external_context); + + // Remove copying of unused tensor + permute_op_input_to_cond_input->prepare(); + permute_op_input_to_op_output->prepare(); + permute_op_input_to_body_input->prepare(); + permute_body_output_to_cond_input->prepare(); + permute_body_output_to_body_input->prepare(); + permute_body_output_to_op_output->prepare(); + + VERBOSE(While) << "Call to $" << _cond_subg_index << " (cond)" << std::endl; + cond_exec->execute(_input_tensors, permute_op_input_to_cond_input); + VERBOSE(While) << "Return from $" << _cond_subg_index << std::endl; + + assert(cond_exec->getOutputTensors().size() == 1); + auto &cond_output_tensor = cond_exec->getOutputTensors().at(0); + auto getResultCond = [](backend::ITensor *tensor) -> bool { + bool ret = false; + tensor->access([&](ITensor &tensor) { ret = *reinterpret_cast<bool *>(tensor.buffer()); }); + return ret; + }; + + const auto body_execute_with_op_inputs = [&]() { + VERBOSE(While) << "Call to $" << _body_subg_index << " (body)" << std::endl; + body_exec->execute(_input_tensors, permute_op_input_to_body_input); + VERBOSE(While) << "Return from $" << _body_subg_index << std::endl; + }; + + const auto body_execute_with_body_outputs = [&]() { + VERBOSE(While) << "Call to $" << _body_subg_index << " (body)" << std::endl; + body_exec->execute(body_exec->getOutputTensors(), permute_body_output_to_body_input); + VERBOSE(While) << "Return from $" << _body_subg_index << std::endl; + }; + + std::function<void()> body_execute = body_execute_with_op_inputs; + const auto cond_execute = [&]() { + VERBOSE(While) << "Call to $" << _cond_subg_index << " (cond)" << std::endl; + cond_exec->execute(body_exec->getOutputTensors(), permute_body_output_to_cond_input); + VERBOSE(While) << "Return from $" << _cond_subg_index << std::endl; + }; + auto permute_to_outputs_fn = permute_op_input_to_op_output; + + // Loop while Cond subgraph's output is true + while (getResultCond(cond_output_tensor)) + { + body_execute(); + cond_execute(); + body_execute = body_execute_with_body_outputs; + permute_to_outputs_fn = permute_body_output_to_op_output; + } + permute_to_outputs_fn->run(); +} + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.h b/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.h new file mode 100644 index 000000000..8f82bd973 --- /dev/null +++ b/runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.h @@ -0,0 +1,65 @@ +/* + * 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_BACKEND_CONTROLFLOW_KERNEL_WHILE_LAYER_H__ +#define __ONERT_BACKEND_CONTROLFLOW_KERNEL_WHILE_LAYER_H__ + +#include <backend/ITensor.h> +#include <exec/IExecutor.h> +#include <exec/IFunction.h> +#include <ir/OperandIndexSequence.h> +#include <ir/Graph.h> +#include "../ExternalContext.h" + +namespace onert +{ +namespace backend +{ +namespace controlflow +{ +namespace kernel +{ + +class WhileLayer : public ::onert::exec::IFunction +{ +public: + WhileLayer(const std::vector<backend::ITensor *> input_tensors, + const std::vector<backend::ITensor *> output_tensors, + const ir::OperandIndexSequence &output_indices, const ir::Graph &graph, + const ir::SubgraphIndex &cond_subg_index, const ir::SubgraphIndex &body_subg_index, + exec::ExecutorMap *executor_map, + const std::shared_ptr<ExternalContext> &external_context); + +public: + void run() override; + +private: + const ir::SubgraphIndex _cond_subg_index; + const ir::SubgraphIndex _body_subg_index; + const ir::OperandIndexSequence &_output_indices; + const ir::Graph &_graph; + const std::vector<backend::ITensor *> _input_tensors; + const std::vector<backend::ITensor *> _output_tensors; + exec::ExecutorMap *_executor_map; + const std::shared_ptr<ExternalContext> _external_context; +}; + +} // namespace kernel +} // namespace controlflow +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CONTROLFLOW_KERNEL_WHILE_LAYER_H__ diff --git a/runtime/onert/core/src/backend/cpu_common/Allocator.cc b/runtime/onert/core/src/backend/cpu_common/Allocator.cc new file mode 100644 index 000000000..0ba444ee6 --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/Allocator.cc @@ -0,0 +1,38 @@ +/* + * 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/cpu_common/Allocator.h" + +#include "util/logging.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +Allocator::Allocator(uint32_t capacity) +{ + _base = std::make_unique<uint8_t[]>(capacity); + + VERBOSE(ALLOC) << "allocation capacity: " << capacity << std::endl; + VERBOSE(ALLOC) << "base pointer: " << static_cast<void *>(_base.get()) << std::endl; +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/DynamicTensorManager.cc b/runtime/onert/core/src/backend/cpu_common/DynamicTensorManager.cc new file mode 100644 index 000000000..740248ccd --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/DynamicTensorManager.cc @@ -0,0 +1,80 @@ +/* + * 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/cpu_common/DynamicTensorManager.h" + +#include "util/logging.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +DynamicTensorManager::DynamicTensorManager(const std::shared_ptr<TensorRegistry> ®) + : _dynamic_mem_mgr{new DynamicMemoryManager()}, _tensors{reg} +{ + // DO NOTHING +} + +void DynamicTensorManager::buildTensor(const ir::OperandIndex &ind, + const ir::OperandInfo &tensor_info, + ir::Layout backend_layout) +{ + assert(_tensors->getNativeTensor(ind) == nullptr); + auto tensor = std::make_unique<Tensor>(tensor_info, backend_layout, _dynamic_mem_mgr.get()); + _tensors->setNativeTensor(ind, std::move(tensor)); +} + +void DynamicTensorManager::planDealloc(ir::OperationIndex op_ind, backend::ITensor *tensor) +{ + _dealloc_tensor_map[op_ind].emplace(tensor); +} + +void DynamicTensorManager::deallocInput(ir::OperationIndex op_ind) +{ + auto find = _dealloc_tensor_map.find(op_ind); + if (find == _dealloc_tensor_map.end()) + return; + + auto &input_set = find->second; + for (auto *tensor : input_set) + { + if (!tensor->is_dynamic()) + continue; + + _dynamic_mem_mgr->deallocate(tensor); + + auto *cpu_tensor = nnfw::misc::polymorphic_downcast<cpu_common::Tensor *>(tensor); + cpu_tensor->resetBuffer(); + + VERBOSE(DynamicTensorManager) << "Deallocating tensor " << (void *)cpu_tensor + << " (input of op_ind: " << op_ind.value() << ")" << std::endl; + } +} + +const ITensor *DynamicTensorManager::getRawITensor(ir::OperandIndex ind) +{ + auto ptr = _tensors->getITensor(ind); + assert(ptr); + return ptr; +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryManager.cc b/runtime/onert/core/src/backend/cpu_common/MemoryManager.cc new file mode 100644 index 000000000..9f179d9ee --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryManager.cc @@ -0,0 +1,108 @@ +/* + * 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 <backend/cpu_common/MemoryManager.h> + +#include <cassert> + +#include "MemoryPlannerFactory.h" +#include "util/ConfigSource.h" +#include "util/logging.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +MemoryManager::MemoryManager() : _mem_planner{createMemoryPlanner()} +{ + // DO NOTHING +} + +MemoryManager::MemoryManager(const std::string planner_id) + : _mem_planner{createMemoryPlanner(planner_id)} +{ + // DO NOTHING +} + +cpu_common::IMemoryPlanner *MemoryManager::createMemoryPlanner() +{ + auto planner_id = util::getConfigString(util::config::CPU_MEMORY_PLANNER); + return cpu_common::MemoryPlannerFactory::get().create(planner_id); +} + +cpu_common::IMemoryPlanner *MemoryManager::createMemoryPlanner(const std::string planner_id) +{ + return cpu_common::MemoryPlannerFactory::get().create(planner_id); +} + +void MemoryManager::claimPlan(const ir::OperandIndex &ind, uint32_t size) +{ + _mem_planner->claim(ind, size); +} + +void MemoryManager::releasePlan(const ir::OperandIndex &ind) { _mem_planner->release(ind); } + +void MemoryManager::allocate(void) +{ + _mem_alloc = std::make_shared<cpu_common::Allocator>(_mem_planner->capacity()); + assert(_mem_alloc->base()); +} + +uint8_t *MemoryManager::getBuffer(const ir::OperandIndex &ind) const +{ + assert(_mem_planner->memory_plans().find(ind) != _mem_planner->memory_plans().end()); + const auto &mem_blk = _mem_planner->memory_plans().at(ind); + return _mem_alloc->base() + mem_blk.offset; +} + +std::shared_ptr<cpu_common::Allocator> DynamicMemoryManager::allocate(const ITensor *tensor, + uint32_t capacity) +{ + auto find = _mem_alloc_map.find(tensor); + if (find != _mem_alloc_map.end()) + throw std::runtime_error("Cannot allocate memory for a tensor. It was already allocated."); + + _mem_alloc_map[tensor] = std::make_shared<cpu_common::Allocator>(capacity); + return _mem_alloc_map[tensor]; +} + +void DynamicMemoryManager::deallocate(const ITensor *tensor) +{ + auto find = _mem_alloc_map.find(tensor); + if (find == _mem_alloc_map.end()) + throw std::runtime_error("Cannot find Allocator for the requested index"); + + find->second->release(); // explicitly erase memory + _mem_alloc_map.erase(find); // remove tensor and alloc +} + +void DynamicMemoryManager::deallocate(void) +{ + for (auto &mem_alloc : _mem_alloc_map) + { + // Release memory buffer of mem_alloc + mem_alloc.second->release(); + } + + _mem_alloc_map.clear(); +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.cc b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.cc new file mode 100644 index 000000000..01cd1a0fe --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.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 "MemoryPlanner.h" +#include "util/logging.h" +#include <cassert> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +void BumpPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + Block blk{_capacity, size}; + _mem_plans[ind] = blk; + _capacity += size; + + VERBOSE(BP_PLANNER) << "CLAIM(#" << ind.value() << "): " << blk.offset << ", " << blk.size + << std::endl; +} + +void BumpPlanner::release(const ir::OperandIndex &ind) +{ + VERBOSE(BP_PLANNER) << "RELEASE(#" << ind.value() << "): " + << "NOTHING does" << std::endl; +} + +// There are some assumptions for claiming memory(== making a reservation for memory). +// 1. About _claim_table(std::map). +// - The table's data structure is std::map so that it always sorts +// value(OperandIndex) by key(base_offset). +// - This claim() inserts key/value into _claim_table and the release() removes the key/value from +// _claim_table. +// - _claim_table shows the memory status at a certain point in time. Therefore, +// - If _claim_table has an offset and a certain size at a certain point in time, +// it means the place at the offset has been already claimed(== can't claim now. need to find +// someplace new). +// - If _claim_table doesn't have any element for an offset and a certain size at a certain +// point in time, it means the place at the offset can be claimed. +// 2. In the loop for _claim_table, we can assume the current claim_base_offset value is bigger than +// the previous claim_base_offset. +void FirstFitPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + // Find the right position for claiming + uint32_t next_offset = 0; + for (auto &mem_claim : _claim_table) + { + auto claimed_base_offset = mem_claim.first; + auto claimed_size = _mem_plans[mem_claim.second].size; + if (next_offset + size <= claimed_base_offset) + { + break; + } + else + { + next_offset = claimed_base_offset + claimed_size; + } + } + + // Now next_offset is set to the proper offset + _claim_table[next_offset] = ind; + _mem_plans[ind] = {next_offset, size}; + + VERBOSE(FF_PLANNER) << "claim(#" << ind.value() << "): [+" << next_offset << ", " << size << "sz]" + << std::endl; + + if (_capacity < next_offset + size) + { + _capacity = next_offset + size; + } +} + +void FirstFitPlanner::release(const ir::OperandIndex &ind) +{ + for (auto it = _claim_table.cbegin(); it != _claim_table.cend(); ++it) + { + if (it->second == ind) + { + uint32_t offset = it->first; + uint32_t index = ind.value(); + uint32_t size = _mem_plans[ind].size; + + _claim_table.erase(it); + + VERBOSE(FF_PLANNER) << "release(#" << index << "): [+" << offset << ", " << size << "sz]" + << std::endl; + return; + } + } + assert(!"Cannot release for given index. It has been not claimed or released already."); +} + +WICPlanner::WICPlanner() + : _initialized(false), _capacity(0), _mem_plans(), _live_operands(), _interference_graph(), + _operands() +{ + // DO NOTHING +} + +void WICPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + _operands.emplace(size, ind); + _interference_graph[ind].insert(_interference_graph[ind].end(), _live_operands.cbegin(), + _live_operands.cend()); + for (const auto &live_operand : _live_operands) + { + _interference_graph[live_operand].emplace_back(ind); + } + _live_operands.emplace(ind); + + VERBOSE(WIC_PLANNER) << "claim(#" << ind.value() << "): [" << size << "sz]" << std::endl; +} + +void WICPlanner::release(const ir::OperandIndex &ind) +{ + _live_operands.erase(ind); + VERBOSE(WIC_PLANNER) << "release(#" << ind.value() << ")" << std::endl; +} + +/* + * Build memory plans using liveness and size of operands + * 1. Build inference graph at claim + * - Two operands interfere if they have overlapped live range + * 2. Sort operands in descending order of size + * - Use std::multimap to sort operands + * 3. Allocate memory block for sorted operands + * - Find free memory block which does not overlap with interfered operands + */ +void WICPlanner::buildMemoryPlans() +{ + for (const auto &operand : _operands) + { + uint32_t size = operand.first; + const ir::OperandIndex &ind = operand.second; + VERBOSE(WIC_PLANNER) << "build_plan(#" << ind.value() << "): [" << size << "sz]" << std::endl; + + uint32_t next_offset = 0; + if (_interference_graph.count(ind)) + { + // Find interfered memory plans and sort them by offset + std::multimap<uint32_t, uint32_t> interfered_plans; + for (const auto &interference : _interference_graph[ind]) + { + if (_mem_plans.count(interference)) + interfered_plans.emplace(_mem_plans[interference].offset, _mem_plans[interference].size); + } + + // Find free memory block in first-fit manner + for (const auto &interfered_plan : interfered_plans) + { + auto claimed_base_offset = interfered_plan.first; + auto claimed_size = interfered_plan.second; + VERBOSE(WIC_PLANNER) << "interfere : [+" << claimed_base_offset << ", " << claimed_size + << "sz]" << std::endl; + if (next_offset + size <= claimed_base_offset) + { + break; + } + else if (next_offset < claimed_base_offset + claimed_size) + { + next_offset = claimed_base_offset + claimed_size; + } + } + } + else + { + VERBOSE(WIC_PLANNER) << "No interference" << std::endl; + } + + _mem_plans[ind] = {next_offset, size}; + VERBOSE(WIC_PLANNER) << "alloc(#" << ind.value() << "): [+" << next_offset << ", " << size + << "sz]" << std::endl; + + if (_capacity < next_offset + size) + { + _capacity = next_offset + size; + } + } + _initialized = true; + _interference_graph.clear(); + _operands.clear(); +} + +WICPlanner::MemoryPlans &WICPlanner::memory_plans() +{ + if (!_initialized) + buildMemoryPlans(); + return _mem_plans; +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.h b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.h new file mode 100644 index 000000000..7c387e542 --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.h @@ -0,0 +1,160 @@ +/* + * 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       MemoryPlanner.h + * @brief      This file contains Memory Planning related classes + */ + +#ifndef __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ +#define __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ + +#include <map> +#include <vector> +#include <unordered_set> +#include <memory> + +#include "backend/cpu_common/Allocator.h" +#include "backend/cpu_common/IMemoryPlanner.h" +#include "ir/OperandIndexMap.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +/** + * @brief Class to plan memory by bump way + */ +class BumpPlanner : public IMemoryPlanner +{ +public: + /** + * @brief Claim memory for operand by bump way + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by bump way + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override { return _capacity; } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override { return _mem_plans; } + +private: + uint32_t _capacity = 0; + MemoryPlans _mem_plans; +}; + +/** + * @brief Class to plan memory by firstfit way + */ +class FirstFitPlanner : public IMemoryPlanner +{ +public: + /** + * @brief Claim memory for operand by firstfit way + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by firstfit way + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override { return _capacity; } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override { return _mem_plans; } + +private: + uint32_t _capacity = 0; + MemoryPlans _mem_plans; + // Use std::map because claim() assumes that _claim_table is sorted by uint32_t(base_offset) + std::map<uint32_t, ir::OperandIndex> _claim_table; +}; + +/** + * @brief Class to plan memory by Weighted Interval Color algorithm + */ +class WICPlanner : public IMemoryPlanner +{ +public: + WICPlanner(); + + /** + * @brief Claim memory for operand by WIC algorithm + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by WIC algorithm + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override + { + if (!_initialized) + buildMemoryPlans(); + return _capacity; + } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override; + +private: + void buildMemoryPlans(); + + bool _initialized; + uint32_t _capacity; + MemoryPlans _mem_plans; + std::unordered_set<ir::OperandIndex> _live_operands; + ir::OperandIndexMap<std::vector<ir::OperandIndex>> _interference_graph; + // Sort operands by descending order of size + std::multimap<uint32_t, ir::OperandIndex, std::greater<uint32_t>> _operands; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.test.cc b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.test.cc new file mode 100644 index 000000000..5208a94d4 --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryPlanner.test.cc @@ -0,0 +1,193 @@ +/* + * 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 <gtest/gtest.h> + +#include "MemoryPlanner.h" +#include "ir/Index.h" + +TEST(Allocator, allocate_test) +{ + ::onert::backend::cpu_common::Allocator allocator(1024); + ASSERT_NE(allocator.base(), nullptr); +} + +TEST(BumpPlanner, claim_test) +{ + ::onert::backend::cpu_common::BumpPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size, uint32_t expected_offset) { + onert::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + claim(0, 10, 0); + claim(1, 20, 10); + claim(2, 30, 30); +} + +TEST(FirstFitPlanner, claim_release_test) +{ + ::onert::backend::cpu_common::FirstFitPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size, uint32_t expected_offset) { + onert::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + auto release = [&planner](uint32_t index) { + onert::ir::OperandIndex mem_idx(index); + planner.release(mem_idx); + }; + + // 0 CLAIM - 10 + claim(0, 10, 0); + + // 1 CLAIM - 20 + claim(1, 20, 10); + + // 2 CLAIM - 30 + claim(2, 30, 30); + + // 0 RELEASE - 10 + release(0); + + // 3 CLAIM - 20 + claim(3, 20, 60); + + // 4 CLAIM - 5 + claim(4, 5, 0); + + // 5 CLAIM - 10 + claim(5, 10, 80); + + // 6 CLAIM - 5 + claim(6, 5, 5); + + // 2 RELEASE - 30 + release(2); + + // 7 CLAIM - 35 + claim(7, 35, 90); + + // 8 CLAIM - 10 + claim(8, 10, 30); + + // 4 RELEASE - 5 + release(4); + + // 9 CLAIM - 10 + claim(9, 10, 40); + + // 10 CLAIM - 10 + claim(10, 10, 50); + + // 6 RELEASE + release(6); + + // 1 RELEASE + release(1); + + // 8 RELEASE + release(8); + + // 9 RELEASE + release(9); + + // 10 RELEASE + release(10); + + // 3 RELEASE + release(3); + + // 5 RELEASE + release(5); + + // 7 RELEASE + release(7); +} + +TEST(WICPlanner, claim_release_test) +{ + ::onert::backend::cpu_common::WICPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size) { + onert::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + }; + + auto release = [&planner](uint32_t index) { + onert::ir::OperandIndex mem_idx(index); + planner.release(mem_idx); + }; + + auto verify = [&planner](uint32_t index, uint32_t size, uint32_t expected_offset) { + onert::ir::OperandIndex mem_idx(index); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + auto capacity = [&planner](uint32_t expected_capacity) { + auto actual_capacity = planner.capacity(); + ASSERT_EQ(actual_capacity, expected_capacity); + }; + + claim(0, 20); + claim(1, 5); + release(0); + claim(2, 10); + release(1); + claim(3, 10); + release(2); + claim(4, 10); + release(3); + claim(5, 20); + release(4); + claim(6, 20); + release(5); + release(7); + + // VERIFY 0 - 0 + verify(0, 20, 0); + + // VERIFY 1 - 20 + verify(1, 5, 20); + + // VERIFY 2 - 0 + verify(2, 10, 0); + + // VERIFY 3 - 10 + verify(3, 10, 10); + + // VERIFY 4 - 20 + verify(4, 10, 20); + + // VERIFY 5 - 0 + verify(5, 20, 0); + + // VERIFY 6 - 20 + verify(6, 20, 20); + + // CAPACITY - 40 + capacity(40); +} diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.cc b/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.cc new file mode 100644 index 000000000..ead4f3294 --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.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 "MemoryPlannerFactory.h" + +#include "MemoryPlanner.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +MemoryPlannerFactory &MemoryPlannerFactory::get() +{ + static MemoryPlannerFactory instance; + return instance; +} + +IMemoryPlanner *MemoryPlannerFactory::create(const std::string &key) +{ + if (key == "FirstFit") + { + return new FirstFitPlanner; + } + else if (key == "Bump") + { + return new BumpPlanner; + } + else if (key == "WIC") + { + return new WICPlanner; + } + return new FirstFitPlanner; // Default Planner +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.h b/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.h new file mode 100644 index 000000000..d14ec13ca --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/MemoryPlannerFactory.h @@ -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. + */ + +#ifndef __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ +#define __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ + +#include "backend/cpu_common/IMemoryPlanner.h" + +#include <string> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +class MemoryPlannerFactory +{ +public: + static MemoryPlannerFactory &get(); + +private: + MemoryPlannerFactory() = default; + +public: + IMemoryPlanner *create(const std::string &key); +}; + +} // namespace cpu_common +} // namespace backend +} // namespace onert + +#endif // __ONERT_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ diff --git a/runtime/onert/core/src/backend/cpu_common/StaticTensorManager.cc b/runtime/onert/core/src/backend/cpu_common/StaticTensorManager.cc new file mode 100644 index 000000000..cac43babe --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/StaticTensorManager.cc @@ -0,0 +1,118 @@ +/* + * 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 "backend/cpu_common/StaticTensorManager.h" + +#include "backend/cpu_common/DynamicTensorManager.h" +#include <util/logging.h> + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +StaticTensorManager::StaticTensorManager(const std::shared_ptr<TensorRegistry> ®, + DynamicMemoryManager *dynamic_mem_mgr) + : _const_mgr{new DynamicMemoryManager()}, _nonconst_mgr{new MemoryManager()}, _tensors{reg}, + _dynamic_mem_mgr{dynamic_mem_mgr} +{ + // DO NOTHING +} + +void StaticTensorManager::allocateConsts(void) +{ + for (auto &pair : _tensors->native_tensors()) + { + const auto &ind = pair.first; + auto tensor = pair.second.get(); + if (_as_constants[ind]) + { + auto mem_alloc = _const_mgr->allocate(_tensors->getITensor(ind), tensor->total_size()); + tensor->setBuffer(mem_alloc); + auto buffer = mem_alloc->base(); + VERBOSE(CPU_COMMON_StaticTensorManager) << "CONSTANT TENSOR(#" << ind.value() + << "): " << static_cast<void *>(buffer) + << "size : " << tensor->total_size() << std::endl; + } + } +} + +void StaticTensorManager::allocateNonconsts(void) +{ + _nonconst_mgr->allocate(); + + for (auto &pair : _tensors->native_tensors()) + { + const auto &ind = pair.first; + auto tensor = pair.second.get(); + if (!_as_constants[ind] && !tensor->is_dynamic()) + { + auto *buffer = _nonconst_mgr->getBuffer(ind); + tensor->setBuffer(buffer); + + VERBOSE(CPU_COMMON_StaticTensorManager) << "TENSOR(#" << ind.value() + << "): " << static_cast<void *>(buffer) << std::endl; + } + } +} + +void StaticTensorManager::deallocateConsts(void) { _const_mgr->deallocate(); } + +void StaticTensorManager::deallocateNonconsts(void) { _nonconst_mgr->deallocate(); } + +void StaticTensorManager::buildTensor(const ir::OperandIndex &ind, + const ir::OperandInfo &tensor_info, ir::Layout backend_layout, + bool as_const) +{ + assert(!_tensors->getNativeTensor(ind)); + auto tensor = std::make_unique<Tensor>(tensor_info, backend_layout, _dynamic_mem_mgr); + _tensors->setNativeTensor(ind, std::move(tensor)); + _as_constants[ind] = as_const; +} + +void StaticTensorManager::claimPlan(const ir::OperandIndex &ind, uint32_t size) +{ + assert(_tensors->getNativeTensor(ind)); + + // This method is called only when a tensor has proper shape + assert(!_tensors->getNativeTensor(ind)->is_dynamic()); + + if (!_as_constants[ind]) + _nonconst_mgr->claimPlan(ind, size); +} + +void StaticTensorManager::releasePlan(const ir::OperandIndex &ind) +{ + assert(_tensors->getNativeTensor(ind)); + + // This method is called only when a tensor has proper shape + assert(!_tensors->getNativeTensor(ind)->is_dynamic()); + + if (!_as_constants[ind]) + _nonconst_mgr->releasePlan(ind); +} + +void StaticTensorManager::iterate(const std::function<void(const ir::OperandIndex &)> &fn) +{ + for (const auto &it : _tensors->native_tensors()) + fn(it.first); +} + +} // namespace cpu_common +} // namespace backend +} // namespace onert diff --git a/runtime/onert/core/src/backend/cpu_common/Tensor.cc b/runtime/onert/core/src/backend/cpu_common/Tensor.cc new file mode 100644 index 000000000..d3dcf9a6d --- /dev/null +++ b/runtime/onert/core/src/backend/cpu_common/Tensor.cc @@ -0,0 +1,97 @@ +/* + * 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 "backend/cpu_common/Tensor.h" + +#include "ir/DataType.h" +#include "backend/cpu_common/MemoryManager.h" + +namespace onert +{ +namespace backend +{ +namespace cpu_common +{ + +Tensor::~Tensor() {} + +size_t Tensor::calcOffset(const ir::Coordinates &coords) const +{ + size_t rank = num_dimensions(); + rank = rank == 0 ? 1 : rank; + size_t offset = 0; + for (size_t i = 0; i < rank; ++i) + { + offset = offset * dimension(i) + coords[i]; + } + offset *= sizeOfDataType(data_type()); + return offset; +} + +void Tensor::setShape(const ir::Shape &new_shape) { _info.shape(new_shape); } + +bool Tensor::applyShape(const ir::Shape &new_shape) +{ + bool previously_dynamic = is_dynamic(); + + auto allocTensorMem = [&](bool overwrite = false) { + auto capacity = total_size(); + auto alloc = _dynamic_mem_mgr->allocate(this, capacity); + + if (overwrite) + overwriteBuffer(alloc); + else + setBuffer(alloc); + }; + + if (!previously_dynamic) + { + // TODO deallocate tensor->buffer() + // issue is that staticTensorManager might have allocate this memory + setShape(new_shape); + set_dynamic(); + allocTensorMem(true); + } + else if (buffer() == nullptr) + { + setShape(new_shape); + set_dynamic(); + allocTensorMem(); + } + // when buffer was already allocated and new_shape requires different size + else + { + auto previous_size = total_size(); + auto new_size = new_shape.num_elements() * ir::sizeOfDataType(data_type()); + if (previous_size != new_size) + { + _dynamic_mem_mgr->deallocate(this); + + setShape(new_shape); + set_dynamic(); + allocTensorMem(true); + } + else + { // when buffer with same size was already allocated, shape could differ + setShape(new_shape); + } + } + return true; +} + +} // namespace cpu_common +} // 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..0093f50fd --- /dev/null +++ b/runtime/onert/core/src/compiler/BackendManager.cc @@ -0,0 +1,146 @@ +/* + * 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/controlflow/Backend.h" +#include "backend/controlflow/Config.h" +#include "backend/IConfig.h" +#include "util/logging.h" +#include "util/ConfigSource.h" +#include "misc/string_helpers.h" + +static const char *SHARED_LIB_EXT = +#if defined(__APPLE__) && defined(__MACH__) + ".dylib"; +#else + ".so"; +#endif + +namespace onert +{ +namespace compiler +{ + +BackendManager &BackendManager::get() +{ + static BackendManager object; + return object; +} + +BackendManager::BackendManager() { loadControlflowBackend(); } + +void BackendManager::loadControlflowBackend() +{ + auto backend_object = std::unique_ptr<backend::controlflow::Backend, backend_destroy_t>( + new backend::controlflow::Backend, [](backend::Backend *backend) { delete backend; }); + + bool initialized = backend_object->config()->initialize(); // Call initialize here? + if (!initialized) + { + throw std::runtime_error(backend::controlflow::Config::ID + " backend initialization failed"); + } + _controlflow = backend_object.get(); // Save the controlflow backend implementation pointer + assert(_controlflow); + _gen_map.emplace(backend_object->config()->id(), std::move(backend_object)); +} + +void BackendManager::loadBackend(const std::string &backend) +{ + if (get(backend) != nullptr) + { + return; + } + + // TODO Remove indentation + { + const std::string backend_so = "libbackend_" + backend + SHARED_LIB_EXT; + void *handle = dlopen(backend_so.c_str(), RTLD_LAZY | RTLD_LOCAL); + + if (handle == nullptr) + { + VERBOSE_F() << "Failed to load backend '" << backend << "' - " << dlerror() << std::endl; + return; + } + + VERBOSE_F() << "Successfully loaded '" << backend << "' - " << backend_so << "\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); + bool initialized = backend_object->config()->initialize(); // Call initialize here? + if (!initialized) + { + VERBOSE_F() << 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)); + } + + // 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::controlflow::Backend *BackendManager::getControlflow() const { return _controlflow; } + +} // 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/Compiler.cc b/runtime/onert/core/src/compiler/Compiler.cc new file mode 100644 index 000000000..c2844bd7c --- /dev/null +++ b/runtime/onert/core/src/compiler/Compiler.cc @@ -0,0 +1,318 @@ +/* + * 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 "ShapeValidator.h" +#include "Fp32ToFp16Converter.h" + +#include <backend/controlflow/Config.h> +#include "compiler/BackendManager.h" +#include "compiler/IScheduler.h" +#include "compiler/ManualScheduler.h" +#include "compiler/HEScheduler.h" +#include "compiler/StaticShapeInferer.h" +#include "compiler/pass/ConstantOutputPass.h" +#include "compiler/pass/OddOutputPass.h" +#include "compiler/pass/PassRunner.h" +#include "exec/ExecTime.h" +#include "ir/operation/LowerInfo.h" +#include "ir/verifier/Verifier.h" +#include "dumper/dot/DotDumper.h" +#include "compiler/Linear.h" +#include "interp/InterpExecutor.h" +#include "util/ConfigSource.h" +#include "util/logging.h" +#include "ir/OperationDumper.h" +#include "misc/string_helpers.h" + +namespace onert +{ + +namespace compiler +{ + +CompilerOptions fetchCompilerOptionsFromGlobalConfig(const ir::Subgraphs &subgs) +{ + CompilerOptions options; + options.backend_list = nnfw::misc::split(util::getConfigString(util::config::BACKENDS), ';'); + options.is_primary_subgraph = false; + 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.disable_compile = util::getConfigBool(util::config::DISABLE_COMPILE); + options.fp16_enable = util::getConfigBool(util::config::FP16_ENABLE); +#ifdef RUY_PROFILER + options.op_seq_max_node = 1; +#endif + + { + // 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 + // TODO Support multiple subgraphs for manual scheduling + 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)); + + subgs.at(ir::SubgraphIndex{0}) + ->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::Subgraphs> &subgs) + : _subgraphs{subgs}, _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(*subgs); +} + +void Compiler::enableToFp16() { _options.fp16_enable = true; } + +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"); +} + +std::shared_ptr<exec::ExecutorMap> Compiler::compile(void) +{ + // Set control flow backend for control flow operators + { + _options.manual_scheduler_options.opcode_to_backend[ir::OpCode::If] = + backend::controlflow::Config::ID; + _options.manual_scheduler_options.opcode_to_backend[ir::OpCode::While] = + backend::controlflow::Config::ID; + _options.manual_scheduler_options.opcode_to_backend[ir::OpCode::Permute] = + backend::controlflow::Config::ID; + } + + // FIXME This is a workaround for bcq operations, should remove it + { + _options.manual_scheduler_options.opcode_to_backend[ir::OpCode::BCQFullyConnected] = "bcq"; + _options.manual_scheduler_options.opcode_to_backend[ir::OpCode::BCQGather] = "bcq"; + } + + { + VERBOSE(Compiler) << std::boolalpha; + VERBOSE(Compiler) << "==== Compiler Options ====" << std::endl; + VERBOSE(Compiler) << "backend_list : " + << nnfw::misc::join(_options.backend_list.begin(), + _options.backend_list.end(), "/") + << std::endl; + VERBOSE(Compiler) << "trace_filepath : " << _options.trace_filepath << std::endl; + VERBOSE(Compiler) << "graph_dump_level : " << _options.graph_dump_level << std::endl; + VERBOSE(Compiler) << "op_seq_max_node : " << _options.op_seq_max_node << std::endl; + VERBOSE(Compiler) << "executor : " << _options.executor << std::endl; + VERBOSE(Compiler) << "manual_scheduler_options : (Too many things to print)" << std::endl; + VERBOSE(Compiler) << "he_scheduler : " << _options.he_scheduler << std::endl; + VERBOSE(Compiler) << "he_profiling_mode : " << _options.he_profiling_mode << std::endl; + VERBOSE(Compiler) << "disable_compile : " << _options.disable_compile << std::endl; + VERBOSE(Compiler) << "fp16_enable : " << _options.fp16_enable << std::endl; + VERBOSE(Compiler) << std::noboolalpha; + } + + _subgraphs->iterate([&](const ir::SubgraphIndex &, ir::Graph &subg) { + // Mandatory passes + pass::PassRunner{} + .append(std::make_unique<pass::ConstantOutputPass>(subg)) + .append(std::make_unique<pass::OddOutputPass>(subg)) + .run(); + }); + + /*************************************************** + * Prepare compilation phase + ***************************************************/ + auto executors = std::make_shared<exec::ExecutorMap>(); + + // Compilable check + // TODO: Support hybrid execution - + // execution between interpreter and compiled executor (including control flow) + if (!checkCompilable()) + { + _subgraphs->iterate([&](const ir::SubgraphIndex &index, ir::Graph &subg) { + executors->emplace(index, std::make_unique<interp::InterpExecutor>(subg)); + }); + _state = State::COMPILED; + return executors; + } + + // 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); + + // Lower: Assign backend + std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>> lowered_subgs; + _subgraphs->iterate([&](const ir::SubgraphIndex &index, ir::Graph &subg) { + _options.is_primary_subgraph = (index == ir::SubgraphIndex{0}); + onert::dumper::dot::DotDumper dot_dumper(subg, dump_level); + dot_dumper.dump(nnfw::misc::str("before_lower_subg-", index.value())); + + // Lower: Assign backend + lowered_subgs[index] = std::make_unique<compiler::LoweredGraph>(subg, _options); + + // Check backend(s) for subgraph support FP16 + bool backends_support_fp16 = true; + auto &contexts = (*lowered_subgs[index]).backend_contexts(); + for (auto it = contexts.begin(); it != contexts.end(); it++) + { + // Controlflow backend is not for actual computaion of operations so it is an exception + if (it->first->config()->id() != backend::controlflow::Config::ID) + backends_support_fp16 &= it->first->config()->supportFP16(); + } + + if (_options.fp16_enable && backends_support_fp16) + { + // NOTE: the only acl_cl backend enables fp16 mode + Fp32ToFp16Converter(*lowered_subgs[index]).run(); + } + + subg.setSubgraphs(nullptr); + }); + + _subgraphs.reset(); + + // Shape inference. + { + const auto primary_subg_idx = ir::SubgraphIndex{0}; + StaticShapeInferer inferer(primary_subg_idx, lowered_subgs); + lowered_subgs.at(primary_subg_idx) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + auto has_dynamic_tensor = inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + inferer.dump(); + } + + // Shape validation + // TODO Move shape independent feature check from ShapeValidator to OperationValidator + // TODO Move ShapeValidator into shape inference + // - Check input tensor shape validation + // - Check parameter value validation which valid value is depend on input tensor shape + // - Output tensor shape validation check is needless because + // static/dynamic shape inferer will make valid output shape + for (auto &pair : lowered_subgs) + { + auto &lowered_subg = pair.second; + compiler::ShapeValidator{lowered_subg->graph()}(); + } + + /************************************************************* + * Backend independent analysis & optimization phase finished + *************************************************************/ + + executors = std::make_shared<exec::ExecutorMap>(); + for (auto &pair : lowered_subgs) + { + const auto &subg_index = pair.first; + auto &lowered_subg = pair.second; + auto indexed_ranks = lowered_subg->indexed_ranks(); + + _options.is_primary_subgraph = (subg_index == ir::SubgraphIndex{0}); + + onert::dumper::dot::DotDumper dot_dumper_lowered(lowered_subg.get(), dump_level); + dot_dumper_lowered.dump("after_lower_subg-" + std::to_string(subg_index.value())); + + ir::OperationDumper dumper("START SUBGRAPH " + std::to_string(subg_index.value())); + lowered_subg->graph().operations().iterate( + [&](const ir::OperationIndex &, const ir::Operation &op) { op.accept(dumper); }); + auto executor = std::unique_ptr<exec::IExecutor>{ + ExecutorFactory::get().create(std::move(lowered_subg), _options, executors)}; + executor->setIndexedRanks(indexed_ranks); + executors->insert(std::make_pair(subg_index, std::move(executor))); + } + + /******************************** + * Code generation phase finished + ********************************/ + _state = State::COMPILED; + return executors; +} + +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 + for (uint32_t i = 0; i < _subgraphs->count(); ++i) + { + auto graph = _subgraphs->at(ir::SubgraphIndex{i}); + 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..bb325ffbc --- /dev/null +++ b/runtime/onert/core/src/compiler/ExecutorFactory.cc @@ -0,0 +1,501 @@ +/* + * 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 "compiler/TensorBuilders.h" +#include "backend/IConstantInitializer.h" +#include "backend/IKernelGenerator.h" +#include "backend/IOptimizer.h" +#include "backend/IPortableTensor.h" +#include "backend/ITensorRegister.h" +#include "backend/controlflow/Config.h" +#include "backend/controlflow/KernelGenerator.h" +#include "backend/controlflow/UserTensor.h" +#include "backend/controlflow/TensorBuilder.h" +#include <memory> + +namespace onert +{ +namespace +{ + +class SyncFunction final : public exec::IFunction +{ +public: + virtual ~SyncFunction() = default; + SyncFunction(std::unique_ptr<exec::IFunction> fn, const std::shared_ptr<backend::IConfig> config) + : _fn{std::move(fn)}, _config{config} + { + assert(_fn); + assert(_config); + } + + void run() override + { + _fn->run(); + _config->sync(); + } + + void prepare() override { _fn->prepare(); } + +private: + std::unique_ptr<exec::IFunction> _fn; + std::shared_ptr<backend::IConfig> _config; +}; + +} // namespace +} // namespace onert + +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, + std::placeholders::_3, false); + _map["Parallel"] = std::bind(createDataflowExecutor, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, true); +} + +exec::IExecutor *ExecutorFactory::create(std::unique_ptr<compiler::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map) +{ + return _map.at(options.executor)(std::move(lowered_graph), options, executor_map); +} + +void ExecutorFactory::initializeBackendContext(compiler::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()->op_seq; + auto backend = op_seq_li.at(op_seq_index)->backend(); + for (auto &operation_idx : op_seq.operations()) + { + backend_assets[backend].operation_list.emplace_back(operation_idx, 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(compiler::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; + auto model_io = lowered_graph->graph().getInputs() + lowered_graph->graph().getOutputs(); + + if (tensor_register) + { + // Custom registration + tensor_register->registerTensors(op_seq, lowered_graph->getLowerInfo()); + } + else + { + // Default registration + for (const auto op_idx : op_seq) + { + const auto &op = lowered_graph->graph().operations().at(op_idx); + for (const auto &index : + (op.getInputs() | ir::Remove::UNDEFINED) + (op.getOutputs() | ir::Remove::UNDEFINED)) + { + if (!tensor_builder->isRegistered(index) && !model_io.contains(index)) + { + const auto &operand_lower_info = + lowered_graph->getLowerInfo(index)->def_factors().getOnlyElement(); + + // E.g., permute (CPU) -> tensor A -> MaxPool2D(acl_cl) + // op.getOutputs() of permute (CPU) returns tensor A + // but tensor A belongs to the backend of acl_cl. + // So, we have to make this tensor NOT registered for CPU. + if (operand_lower_info.backend() != backend) + continue; + + 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(), obj.info().memAllocType(), + obj.isConstant()}; + tensor_builder->registerTensorInfo(index, backend_info, backend_layout); + } + } + } + } + } +} + +std::vector<backend::ITensor *> +ExecutorFactory::initializeModelIOTensors(compiler::LoweredGraph &lowered_graph, + const ir::OperandIndexSequence &indices) +{ + std::vector<backend::ITensor *> ret; + + // TODO Store controlflow backend in BackendContext + std::shared_ptr<backend::controlflow::TensorBuilder> cf_tensor_builder; + std::shared_ptr<backend::controlflow::TensorRegistry> cf_tensor_reg; + for (const auto &e : lowered_graph.backend_contexts()) + { + auto backend = e.first; + auto &context = e.second; + if (backend->config()->id() == backend::controlflow::Config::ID) + { + cf_tensor_builder = + std::dynamic_pointer_cast<backend::controlflow::TensorBuilder>(context->tensor_builder); + cf_tensor_reg = + std::dynamic_pointer_cast<backend::controlflow::TensorRegistry>(context->tensor_registry); + } + } + assert(cf_tensor_builder); + assert(cf_tensor_reg); + + for (auto ind : indices) + { + const auto &operand = lowered_graph.graph().operands().at(ind); + auto tensor = std::make_unique<backend::controlflow::UserTensor>( + operand.info(), + ir::Layout::NHWC /* FIXME find op_seq for this operand and use frontend_layout */ + ); + + // Add tensor to controlflow TensorRegistry. + cf_tensor_reg->setNativeUserTensor(ind, std::move(tensor)); + auto *itensor = cf_tensor_reg->getITensor(ind); + ret.push_back(itensor); + } + return ret; +} + +void ExecutorFactory::prepareMigrantTensors(compiler::LoweredGraph &lowered_graph) +{ + TensorRegistries tensor_regs{lowered_graph.backend_contexts(), true}; + + 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 &backend_ctx = lowered_graph.backend_contexts().at(lower_info->backend()); + for (auto ind : (op_seq.getInputs() + op_seq.getOutputs()) | ir::Remove::DUPLICATED | + ir::Remove::UNDEFINED) + { + // If an OpSequence input/output tensor does not have a own tensor object, + // it must be using migrant tensors, so find the tensor from other tensor builders and + // set the tensor to this tensor builder if portable + if (!backend_ctx->tensor_registry->getITensor(ind)) + { + auto tensor = tensor_regs.getITensor(ind); + assert(tensor); // The tensor must have been registered + auto ptensor = dynamic_cast<backend::IPortableTensor *>(tensor); + if (ptensor) + backend_ctx->tensor_registry->setMigrantTensor(ind, ptensor); + } + } + }); +} + +exec::IExecutor * +ExecutorFactory::createLinearExecutor(std::unique_ptr<compiler::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map) +{ + const auto &backend_contexts = lowered_graph->backend_contexts(); + + initializeBackendContext(lowered_graph.get()); + + // linearize + assert(!lowered_graph->graph().isBuildingPhase()); + + /************************************************* + * 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); + + std::vector<backend::ITensor *> input_tensors; + std::vector<backend::ITensor *> output_tensors; + if (options.is_primary_subgraph) + { + input_tensors = initializeModelIOTensors(*lowered_graph, lowered_graph->graph().getInputs()); + output_tensors = initializeModelIOTensors(*lowered_graph, lowered_graph->graph().getOutputs()); + } + + Linear::dump(*lowered_graph, order); + Linear::planTensors(*lowered_graph, order); + + TensorBuilders tensor_builders{lowered_graph->backend_contexts(), true}; + TensorRegistries tensor_regs{lowered_graph->backend_contexts(), true}; + + for (auto &tensor_builder : tensor_builders) + { + tensor_builder->prepare(); + } + + prepareMigrantTensors(*lowered_graph); + + ExecutionBuilder builder; + + // Generate kernels + lowered_graph->iterateTopolOpSeqs([&](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; + // Set TensorBuilderSet and ExecutorMap to kernel_gen of control flow + auto cf_kernel_gen = dynamic_cast<backend::controlflow::KernelGenerator *>(kernel_gen.get()); + if (cf_kernel_gen != nullptr) + { + cf_kernel_gen->setTensorRegistries(tensor_regs); + cf_kernel_gen->setExecutorMap(executor_map); + } + auto fn_seq = kernel_gen->generate(op_seq); + if (options.he_profiling_mode) + { + fn_seq->wrap<SyncFunction>(lower_info->backend()->config()); + } + 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(); + } + + lowered_graph->graph().operands().iterate( + [](const ir::OperandIndex &, ir::Operand &obj) { obj.releaseData(); }); + + 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), input_tensors, output_tensors, tensor_regs, + 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->graph()); + exec->addObserver(std::move(ctp)); + } + + return exec; +} + +exec::IExecutor *ExecutorFactory::createDataflowExecutor( + std::unique_ptr<compiler::LoweredGraph> lowered_graph, const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map, bool parallel) +{ + const auto &backend_contexts = lowered_graph->backend_contexts(); + + initializeBackendContext(lowered_graph.get()); + + auto order = Linear::linearize(*lowered_graph); + runTensorRegistration(lowered_graph.get(), order); + + std::vector<backend::ITensor *> input_tensors; + std::vector<backend::ITensor *> output_tensors; + if (options.is_primary_subgraph) + { + input_tensors = initializeModelIOTensors(*lowered_graph, lowered_graph->graph().getInputs()); + output_tensors = initializeModelIOTensors(*lowered_graph, lowered_graph->graph().getOutputs()); + } + + TensorBuilders tensor_builders{lowered_graph->backend_contexts(), true}; + TensorRegistries tensor_regs{lowered_graph->backend_contexts(), true}; + + // 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(); + } + + prepareMigrantTensors(*lowered_graph); + + ExecutionBuilder builder; + + // Generate kernels + lowered_graph->iterateTopolOpSeqs([&](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; + // Set TensorBuilderSet and ExecutorMap to kernel_gen of control flow + auto cf_kernel_gen = dynamic_cast<backend::controlflow::KernelGenerator *>(kernel_gen.get()); + if (cf_kernel_gen != nullptr) + { + assert(cf_kernel_gen != nullptr); + cf_kernel_gen->setTensorRegistries(tensor_regs); + cf_kernel_gen->setExecutorMap(executor_map); + } + auto fn_seq = kernel_gen->generate(op_seq); + if (options.he_profiling_mode) + { + fn_seq->wrap<SyncFunction>(lower_info->backend()->config()); + } + 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(); + } + + lowered_graph->graph().operands().iterate( + [](const ir::OperandIndex &, ir::Operand &obj) { obj.releaseData(); }); + + 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), input_tensors, output_tensors, + tensor_regs, std::move(code_map)}; + } + else + { + auto dataflow_exec = new exec::DataflowExecutor{ + std::move(lowered_graph), input_tensors, output_tensors, tensor_regs, 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->graph()); + dataflow_exec->addObserver(std::move(obs)); + } + exec = dataflow_exec; + } + + if (!options.trace_filepath.empty()) + { + std::unique_ptr<exec::IExecutionObserver> ctp = + std::make_unique<exec::ChromeTracingObserver>(options.trace_filepath, exec->graph()); + 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..e76b721ea --- /dev/null +++ b/runtime/onert/core/src/compiler/ExecutorFactory.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_COMPILER_EXECUTOR_FACTORY_H__ +#define __ONERT_COMPILER_EXECUTOR_FACTORY_H__ + +#include <unordered_map> + +#include "backend/ITensor.h" +#include "exec/IExecutor.h" +#include "compiler/LoweredGraph.h" +#include "TensorRegistries.h" + +namespace onert +{ +namespace compiler +{ + +class ExecutorFactory +{ +public: + static ExecutorFactory &get(); + +public: + exec::IExecutor *create(std::unique_ptr<compiler::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map); + +private: + ExecutorFactory(); + +private: + static void initializeBackendContext(compiler::LoweredGraph *lowered_graph); + static void runTensorRegistration(compiler::LoweredGraph *lowered_graph, + const std::vector<ir::OpSequenceIndex> &order); + static std::vector<backend::ITensor *> + initializeModelIOTensors(compiler::LoweredGraph &lowered_graph, + const ir::OperandIndexSequence &indices); + static void prepareMigrantTensors(compiler::LoweredGraph &lowered_graph); + static exec::IExecutor * + createLinearExecutor(std::unique_ptr<compiler::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map); + static exec::IExecutor * + createDataflowExecutor(std::unique_ptr<compiler::LoweredGraph> lowered_graph, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map, bool parallel); + +private: + std::unordered_map<std::string, std::function<exec::IExecutor *( + std::unique_ptr<compiler::LoweredGraph>, + const compiler::CompilerOptions &options, + const std::shared_ptr<exec::ExecutorMap> &executor_map)>> + _map; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_EXECUTOR_FACTORY_H__ diff --git a/runtime/onert/core/src/compiler/Fp32ToFp16Converter.cc b/runtime/onert/core/src/compiler/Fp32ToFp16Converter.cc new file mode 100644 index 000000000..23a6a253d --- /dev/null +++ b/runtime/onert/core/src/compiler/Fp32ToFp16Converter.cc @@ -0,0 +1,954 @@ +/* + * 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 "Fp32ToFp16Converter.h" +#include "ir/operation/ConvertFp32ToFp16.h" +#include "ir/operation/ConvertFp16ToFp32.h" +#include "util/logging.h" + +#include <Half.h> + +using float16 = Half; + +namespace +{ + +const std::string kAclClBackendConfigId = "acl_cl"; + +void copyDataFromFp32ToFp16(const float *from, float16 *into, size_t num_elements) +{ + for (size_t i = 0; i < num_elements; ++i) + { + into[i] = static_cast<float16>(from[i]); + } +} + +} // namespace + +namespace onert +{ + +namespace compiler +{ + +Fp32ToFp16Converter::Fp32ToFp16Converter(compiler::LoweredGraph &lowered_graph) + : _lowered_graph{lowered_graph} +{ + VERBOSE(Fp32ToFp16Converter) << "Fp16 Enable on" << std::endl; +} + +// For example, two OpSequences are there and each OpSequence has an Operation +// +// OP#0 // model input +// | +// [OPERATION] // OpSeq#0 +// | +// OP#1 +// | +// [OPERATION] // OpSeq#1 +// | +// OP#2 // model output +// +// +// AFTER `appendOpSequences()`, +// note that model_input and model_output are not changed. +// +// OP#0 +// | +// [FP32TO16] // OpSeq#2 +// | +// OP#3 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#4 +// | +// [FP16TO32] // OpSeq#3 +// | +// OP#1 +// | +// [FP32TO16] // OpSeq#4 +// | +// OP#5 +// | +// [OPERATION] // OpSeq#1 +// | +// OP#6 +// | +// [FP16TO32] // OpSeq#5 +// | +// OP#2 +// +// +// AFTER `optimize()`, +// +// OP#0 +// | +// [FP32TO16] // OpSeq#2 +// | +// OP#3 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#4 +// | +// [OPERATION] // OpSeq#1 +// | +// OP#6 +// | +// [FP16TO32] // OpSeq#5 +// | +// OP#2 +// +// +// AFTER `convertOperands()`, +// +// OP#0 // model_input, not fp16 +// | +// [FP32TO16] // OpSeq#2 +// | +// OP#3 // fp16 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#4 // fp16 +// | +// [OPERATION] // OpSeq#1 +// | +// OP#6 // fp16 +// | +// [FP16TO32] // OpSeq#5 +// | +// OP#2 // model_output, notfp16 +// +// +// AFTER `convertDatas()`, +// +// OP#0 // model_input, not fp16 +// | +// [FP32TO16] // OpSeq#2 +// | +// OP#3 // fp16 +// | +// [OPERATION] // OpSeq#0, constants are fp16 +// | +// OP#4 // fp16 +// | +// [OPERATION] // OpSeq#1, constants are fp16 +// | +// OP#6 // fp16 +// | +// [FP16TO32] // OpSeq#5 +// | +// OP#2 // model_output, notfp16 +// +void Fp32ToFp16Converter::run() +{ + // Append new OpSequence which includes ConvertFp32ToFp16 + // and append new OpSequence which includes ConvertFp16ToFp32 + appendOpSequences(); + + // Remove unnecessary converting operations + optimize(); + + // Convert operands' data types from fp32 to fp16 + convertOperands(); + + // Convert Datas + convertDatas(); + + // Print the result + printOpSequences("FINAL OpSequences"); +} + +void Fp32ToFp16Converter::appendOpSequences() +{ + _lowered_graph.op_seqs().iterate( + [&](const ir::OpSequenceIndex &op_seq_ind, ir::OpSequence &op_seq) { + const auto lower_info = _lowered_graph.getLowerInfo(op_seq_ind); + assert(lower_info != nullptr); + + // For now, the only acl_cl supports fully fp16 type + // TODO Support fp16 on acl_neon. Current acl_neon supports the only reshape and concat + // operations. + // To do this, we could check the support by `operation by operation`. After that, we + // would partition an op_seq if it contains unsupported operations. + if (lower_info->backend()->config()->id() != kAclClBackendConfigId) + return; + + // OpSeq's input set should be included in the first operation's input set or + // OpSeq's output set should be included in the last operation's output set + assert(checkOperandsOfOpSequence(op_seq)); + + // Append converting OpSequence for fp16 but all operands' types are not fp16 still. + appendNewOpSeqForConvertFp32ToFp16(op_seq_ind, op_seq); + appendNewOpSeqForConvertFp16ToFp32(op_seq_ind, op_seq); + }); +} + +// +// BEFORE +// +// OP#0 // model input +// | +// [OPERATION] // OpSeq#0 +// | +// OP#1 // model output +// +// +// AFTER +// +// OP#0 // model input +// | +// [FP32TO16] // OpSeq#1 +// | +// OP#2 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#1 // model output +// +void Fp32ToFp16Converter::appendNewOpSeqForConvertFp32ToFp16(const ir::OpSequenceIndex &op_seq_ind, + ir::OpSequence &op_seq) +{ + // OpSeq's input set is included in the first operation's input set + const ir::OperandIndexSequence op_seq_inputs = op_seq.getInputs(); // copied + + // NOTE Please do not change sequence of op_seq_inputs. It can change the sequence of inputs of + // Subgraph + for (const auto &op_seq_input_ind : + op_seq_inputs | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + if (checkOperandType(op_seq_input_ind) == false) + continue; + + // new operand w/ datatype fp32 + const auto new_op_ind = newCopiedOperand(op_seq_input_ind); + + // set new lower_info for operand + setNewOperandLowerInfo(op_seq_ind, new_op_ind); + + // manipulate input of operation and op_seq + // - replace the first operation's input to new operand + // with old operand's removeUse and new operand's appendUse() + manipulateInput(op_seq_ind, op_seq_input_ind, new_op_ind); + + // new op + const auto new_node_ind = newOperationConvertFp32ToFp16(op_seq_input_ind, new_op_ind); + + // new op_seq + const auto new_op_seq_ind = newOpSequence(op_seq_ind, new_node_ind); + + // set new lower_info for op_seq + setNewOpSequenceLowerInfo(op_seq_ind, new_op_seq_ind); + + _list_fp32_to_fp16.insert(new_op_seq_ind); + + VERBOSE(Fp32ToFp16Converter) << "NEW |Fp32To16]" + << ir::getStrFromOpSeq(_lowered_graph.op_seqs().at(new_op_seq_ind), + _lowered_graph.graph().operations()) + << std::endl; + } +} + +// +// BEFORE +// +// OP#0 // model input +// | +// [FP32TO16] // OpSeq#1 +// | +// OP#2 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#1 // model output +// +// +// AFTER +// +// OP#0 // model input +// | +// [FP32TO16] // OpSeq#1 +// | +// OP#2 +// | +// [OPERATION] // OpSeq#0 +// | +// OP#3 +// | +// [FP16TO32] // OpSeq#2 +// | +// OP#1 // model output +// +void Fp32ToFp16Converter::appendNewOpSeqForConvertFp16ToFp32(const ir::OpSequenceIndex &op_seq_ind, + ir::OpSequence &op_seq) +{ + // OpSeq's output set is included in the last operation's output set + const ir::OperandIndexSequence op_seq_outputs = op_seq.getOutputs(); // copied + + // NOTE Please do not change sequence of op_seq_outputs. It can change the sequence of outputs of + // Subgraph + for (const auto &op_seq_output_ind : + op_seq_outputs | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + if (checkOperandType(op_seq_output_ind) == false) + continue; + + // new operand w/ datatype fp32 + const auto new_op_ind = newCopiedOperand(op_seq_output_ind); + + // set new lower_info for operand + setNewOperandLowerInfo(op_seq_ind, new_op_ind); + + // manipulate output of operation and op_seq + // - replace output of the last operation's output to new operand + // with old operand's unsetDef and new operand's appendDef() + manipulateOutput(op_seq_ind, op_seq_output_ind, new_op_ind); + + // new op + auto new_node_ind = newOperationConvertFp16ToFp32(op_seq_output_ind, new_op_ind); + + // new op_seq + auto new_op_seq_ind = newOpSequence(op_seq_ind, new_node_ind); + + // set new lower_info for op_seq + setNewOpSequenceLowerInfo(op_seq_ind, new_op_seq_ind); + + _list_fp16_to_fp32.insert(new_op_seq_ind); + + VERBOSE(Fp32ToFp16Converter) << "NEW |Fp16To32]" + << ir::getStrFromOpSeq(_lowered_graph.op_seqs().at(new_op_seq_ind), + _lowered_graph.graph().operations()) + << std::endl; + } +} + +void Fp32ToFp16Converter::optimize() +{ + printOpSequences("BEFORE opt"); + + removeContiguousConvertOpSequences(); + + printOpSequences("AFTER removeContiguousConverts"); + + // TODO Handle Split from the beginning of the model. ex) MODELS/inception_module + // + // BEFORE) + // + // OP#0---------------------. // model_input + // | | + // [FP32TO16] // OpSeq#0 [FP32TO16] // OpSeq#1 + // | | + // OP#1 OP#2 + // | | + // [OPERATION] // OpSeq#2 [OPERATION] // OpSeq#3 + // + // + // AFTER) + // + // OP#0 // model_input + // | + // [FP32TO16] // OpSeq#4 + // | + // OP#3---------------------------. + // | | + // [OPERATION] // OpSeq#2 [OPERATION] // OpSeq#3 +} + +void Fp32ToFp16Converter::convertOperands() +{ + _lowered_graph.op_seqs().iterate( + [&](const ir::OpSequenceIndex &op_seq_ind, ir::OpSequence &op_seq) { + const auto lower_info = _lowered_graph.getLowerInfo(op_seq_ind); + assert(lower_info != nullptr); + // For now, the only acl_cl supports fully fp16 + if (lower_info->backend()->config()->id() != kAclClBackendConfigId) + return; + + // Convert input,output operands' type to fp16 + convertOperandsOfOpSequence(op_seq); + }); +} + +void Fp32ToFp16Converter::convertOperandsOfOpSequence(ir::OpSequence &op_seq) +{ + auto &operands = _lowered_graph.graph().operands(); + const auto &operations = _lowered_graph.graph().operations(); + const auto &op_seq_inputs = _lowered_graph.graph().getInputs(); + const auto &op_seq_outputs = _lowered_graph.graph().getOutputs(); + + for (auto &op_idx : op_seq) + { + const auto &node = operations.at(op_idx); + for (auto &ind : node.getInputs() | ir::Remove::UNDEFINED) + { + if (node.opcode() == ir::OpCode::ConvertFp32ToFp16 || op_seq_inputs.contains(ind)) + continue; + + auto &obj = operands.at(ind); + if (obj.isConstant() || obj.typeInfo().type() != ir::DataType::FLOAT32) + continue; + + obj.type(ir::DataType::FLOAT16); + + VERBOSE(Fp32ToFp16Converter) << "Input Operand #" << ind.value() << ": fp16" << std::endl; + } + + for (auto &ind : node.getOutputs()) + { + if (node.opcode() == ir::OpCode::ConvertFp16ToFp32 || op_seq_outputs.contains(ind)) + continue; + + auto &obj = operands.at(ind); + if (obj.isConstant() || obj.typeInfo().type() != ir::DataType::FLOAT32) + continue; + + obj.type(ir::DataType::FLOAT16); + + VERBOSE(Fp32ToFp16Converter) << "Output Operand #" << ind.value() << ": fp16" << std::endl; + } + } +} + +void Fp32ToFp16Converter::convertDatas() +{ + _lowered_graph.graph().operands().iterate([&](const ir::OperandIndex &ind, ir::Operand &obj) { + const auto type = obj.typeInfo().type(); + if (type == ir::DataType::FLOAT32 && obj.isConstant()) + { + auto data = obj.data(); + assert(data != nullptr); + + size_t num_elements = obj.operandSize() / ir::sizeOfDataType(type); + size_t new_ptr_size = num_elements * sizeof(float16); + auto new_ptr = std::make_unique<uint8_t[]>(new_ptr_size); + copyDataFromFp32ToFp16(reinterpret_cast<const float *>(data->base()), + reinterpret_cast<float16 *>(new_ptr.get()), num_elements); + obj.releaseData(); + + auto new_data = std::make_unique<ir::CachedData>(new_ptr.get(), new_ptr_size); + + obj.data(std::move(new_data)); + obj.type(ir::DataType::FLOAT16); + VERBOSE(Fp32ToFp16Converter) << "Constant Operand #" << ind.value() << ": fp16" << std::endl; + } + }); +} + +void Fp32ToFp16Converter::printOpSequences(const std::string &pre_msg, const std::string &post_msg) +{ + if (pre_msg.empty() == false) + { + VERBOSE(Fp32ToFp16Converter) << pre_msg << std::endl; + } + + _lowered_graph.op_seqs().iterate([&](const ir::OpSequenceIndex &, const ir::OpSequence &op_seq) { + VERBOSE(Fp32ToFp16Converter) << ir::getStrFromOpSeq(op_seq, _lowered_graph.graph().operations()) + << std::endl; + }); + + if (post_msg.empty() == false) + { + VERBOSE(Fp32ToFp16Converter) << post_msg << std::endl; + } +} + +bool Fp32ToFp16Converter::checkOperandType(const ir::OperandIndex &op_ind) const +{ + const auto &operands = _lowered_graph.graph().operands(); + const auto &obj = operands.at(op_ind); + return (obj.isConstant() == false && obj.typeInfo().type() == ir::DataType::FLOAT32); +} + +bool Fp32ToFp16Converter::checkOperandsOfOpSequence(const ir::OpSequence &op_seq) const +{ + const auto &operations = _lowered_graph.graph().operations(); + + // the first node's input + const auto &first_node_ind = op_seq.operations().at(0); + const auto &first_node = operations.at(first_node_ind); + const auto &first_node_inputs = first_node.getInputs(); + for (const auto &op_seq_input_ind : op_seq.getInputs() | ir::Remove::UNDEFINED) + { + if (first_node_inputs.contains(op_seq_input_ind) == false) + return false; + } + + // the last node's output + size_t last_ind = op_seq.size() - 1; + const auto &last_node_ind = op_seq.operations().at(last_ind); + const auto &last_node = operations.at(last_node_ind); + const auto &last_node_outputs = last_node.getOutputs(); + for (const auto &op_seq_output_ind : op_seq.getOutputs()) + { + if (last_node_outputs.contains(op_seq_output_ind) == false) + return false; + } + + return true; +} + +ir::OperandIndex Fp32ToFp16Converter::newCopiedOperand(const ir::OperandIndex &op_ind) +{ + auto &operands = _lowered_graph.graph().operands(); + const auto &obj = operands.at(op_ind); + auto new_op_ind = operands.emplace(obj.shape(), obj.typeInfo()); + return new_op_ind; +} + +void Fp32ToFp16Converter::setNewOperandLowerInfo(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &new_op_ind) +{ + const auto lower_info = _lowered_graph.getLowerInfo(op_seq_ind); + assert(lower_info != nullptr); + auto new_lower_info = std::make_unique<ir::operand::LowerInfo>(); + auto permute_factor = ir::operand::PermuteFactor(lower_info->backend(), lower_info->layout()); + new_lower_info->addDefPermuteFactor(permute_factor); + new_lower_info->addUsePermuteFactor(permute_factor); + _lowered_graph.setLowerInfo(new_op_ind, std::move(new_lower_info)); +} + +void Fp32ToFp16Converter::setNewOpSequenceLowerInfo(const ir::OpSequenceIndex &op_seq_ind, + const ir::OpSequenceIndex &new_op_seq_ind) +{ + const auto lower_info = _lowered_graph.getLowerInfo(op_seq_ind); + assert(lower_info != nullptr); + + auto new_lower_info = + std::make_unique<ir::operation::LowerInfo>(lower_info->backend(), lower_info->layout()); + _lowered_graph.setLowerInfo(new_op_seq_ind, std::move(new_lower_info)); +} + +void Fp32ToFp16Converter::manipulateInput(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &op_seq_input_ind, + const ir::OperandIndex &new_op_ind) +{ + auto &operands = _lowered_graph.graph().operands(); + auto &operations = _lowered_graph.graph().operations(); + + auto &op_seq = _lowered_graph.op_seqs().at(op_seq_ind); + + auto &first_node_ind = op_seq.operations().at(0); + auto &first_node = operations.at(first_node_ind); + assert(first_node.getInputs().contains(op_seq_input_ind)); + + auto &input_obj = operands.at(op_seq_input_ind); + assert(input_obj.isConstant() == false); + + auto &new_op_obj = operands.at(new_op_ind); + + // The same inputs having the index as op_seq_input_ind are replaced all at once + op_seq.replaceInputs(op_seq_input_ind, new_op_ind); + first_node.replaceInputs(op_seq_input_ind, new_op_ind); + + // op_seq_obj doesn't have uses/def + input_obj.removeUse(first_node_ind); + new_op_obj.insertUse(first_node_ind); +} + +void Fp32ToFp16Converter::manipulateOutput(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &op_seq_output_ind, + const ir::OperandIndex &new_op_ind) +{ + auto &operands = _lowered_graph.graph().operands(); + auto &operations = _lowered_graph.graph().operations(); + + auto &op_seq = _lowered_graph.op_seqs().at(op_seq_ind); + + size_t last_ind = op_seq.size() - 1; + auto &last_node_ind = op_seq.operations().at(last_ind); + auto &last_node = operations.at(last_node_ind); + assert(last_node.getOutputs().contains(op_seq_output_ind)); + + auto &output_obj = operands.at(op_seq_output_ind); + assert(output_obj.isConstant() == false); + + auto &new_op_obj = operands.at(new_op_ind); + + // The same outputs having the index as op_seq_output_ind are replaced all at once + op_seq.replaceOutputs(op_seq_output_ind, new_op_ind); + last_node.replaceOutputs(op_seq_output_ind, new_op_ind); + + // op_seq_obj doesn't have uses/def + assert(output_obj.getDef() == last_node_ind); + output_obj.unsetDef(); + new_op_obj.setDef(last_node_ind); +} + +ir::OperationIndex +Fp32ToFp16Converter::newOperationConvertFp32ToFp16(const ir::OperandIndex &op_seq_input_ind, + const ir::OperandIndex &new_op_ind) +{ + auto &operands = _lowered_graph.graph().operands(); + auto &operations = _lowered_graph.graph().operations(); + + auto &input_obj = operands.at(op_seq_input_ind); + auto &new_op_obj = operands.at(new_op_ind); + + std::unique_ptr<ir::Operation> new_node( + new ir::operation::ConvertFp32ToFp16({op_seq_input_ind}, {new_op_ind})); + const auto new_node_ind = operations.push(std::move(new_node)); + + input_obj.insertUse(new_node_ind); + new_op_obj.setDef(new_node_ind); + + return new_node_ind; +} + +ir::OperationIndex +Fp32ToFp16Converter::newOperationConvertFp16ToFp32(const ir::OperandIndex &op_seq_output_ind, + const ir::OperandIndex &new_op_ind) +{ + auto &operands = _lowered_graph.graph().operands(); + auto &operations = _lowered_graph.graph().operations(); + + auto &output_obj = operands.at(op_seq_output_ind); + auto &new_op_obj = operands.at(new_op_ind); + + std::unique_ptr<ir::Operation> new_node( + new ir::operation::ConvertFp16ToFp32({new_op_ind}, {op_seq_output_ind})); + const auto new_node_ind = operations.push(std::move(new_node)); + + new_op_obj.insertUse(new_node_ind); + output_obj.setDef(new_node_ind); + + return new_node_ind; +} + +ir::OpSequenceIndex Fp32ToFp16Converter::newOpSequence(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperationIndex &node_index) +{ + auto &node = _lowered_graph.graph().operations().at(node_index); + const auto lower_info = _lowered_graph.getLowerInfo(op_seq_ind); + assert(lower_info != nullptr); + auto layout = lower_info->layout(); + + auto op_seq = std::make_unique<ir::OpSequence>(layout); + op_seq->appendOperation(node_index); + op_seq->setOutputs(node.getOutputs()); + op_seq->setInputs(node.getInputs()); + + return _lowered_graph.op_seqs().emplace(std::move(op_seq)); +} + +// The op_seq(Fp16To32)'s output operand is the next to op_seq (Fp32To16)? +// If so, connect Fp16To32's previous OpSeq to Fp32To16's next OpSeq +// +// Assume that an OpSequence has an operation for easy explaination +// +// BEFORE) +// +// [OPERATION] // OpSeq#0 +// | +// OP#0 +// | +// [FP16TO32] // OpSeq#1 +// | +// OP#1 +// | +// [FP32TO16] // OpSeq#2 +// | +// OP#2 +// | +// [OPERATION] // OpSeq#3 +// +// +// AFTER) +// +// [OPERATION] // OpSeq#0 +// | +// OP#0 +// | +// [OPERATION] // OpSeq#3 +// +void Fp32ToFp16Converter::removeContiguousConvertOpSequences() +{ + // Prepare InputToOpSeqs map + const auto input_to_op_seqs = prepareInputToOpSeqs(); + + // Find OpSequences to delete while manipulating input of OpSeq. + auto opseq_map_to_delete = findOpSequencesContiguous(input_to_op_seqs); + + // Find Operations to delete + auto list_to_delete_op_seqs = getListOpSequences(opseq_map_to_delete); + auto list_to_delete_ops = findOperationsToDelete(list_to_delete_op_seqs); + + // Before deleting, manipulateInputs of OpSeq & Operation + manipulateContiguousOpSequences(input_to_op_seqs, opseq_map_to_delete); + + // Delete OpSequences & Operations & obj's use/def & operands + deleteContiguousOpSequences(list_to_delete_op_seqs, list_to_delete_ops); +} + +Fp32ToFp16Converter::OpSeqIndexToOpSeqIndexList +Fp32ToFp16Converter::findOpSequencesContiguous(const InputToOpSeqs &input_to_op_seqs) const +{ + const auto &op_seqs = _lowered_graph.op_seqs(); + OpSeqIndexToOpSeqIndexList opseq_map_to_delete; + + // + // Assume that an Operation an OpSequence for easy explaination + // + // [OPERATION] + // | + // OP#0 + // | + // [FP16TO32] // op_seq_ind_fp16_to_fp32 & op_seq_fp16_to_fp32 + // | + // OP#1 // output_ind_fp16_fp32 + // | + // [FP32TO16] // op_seq_ind + // | + // OP#2 + // | + // [OPERATION] + // + for (auto it = _list_fp16_to_fp32.cbegin(); it != _list_fp16_to_fp32.cend(); ++it) + { + // fp16_to_fp32's input/output num is always 1 + auto &op_seq_ind_fp16_to_fp32 = *it; + auto &op_seq_fp16_to_fp32 = op_seqs.at(op_seq_ind_fp16_to_fp32); + assert(op_seq_fp16_to_fp32.size() == 1); + assert(op_seq_fp16_to_fp32.getInputs().size() == 1); + + auto &output_ind_fp16_to_fp32 = op_seq_fp16_to_fp32.getOutputs().at(0); + auto found_input_in_op_seqs = input_to_op_seqs.find(output_ind_fp16_to_fp32); + if (found_input_in_op_seqs == input_to_op_seqs.end()) + { + continue; + } + + // DO NOT FORGET THE CASE + // + // | + // [FP16TO32] + // | + // OP#0---------------------. + // | | + // [FP32TO16] [FP32TO16] + // | | + // OP#1 OP#2 + // | | + // [OPERATION] [OPERATION] + // + for (auto &op_seq_ind : found_input_in_op_seqs->second) + { + auto found_in_fp32_to_fp16 = _list_fp32_to_fp16.find(op_seq_ind); + if (found_in_fp32_to_fp16 != _list_fp32_to_fp16.end()) + { + if (opseq_map_to_delete.find(op_seq_ind_fp16_to_fp32) == opseq_map_to_delete.end()) + { + opseq_map_to_delete[op_seq_ind_fp16_to_fp32].emplace(op_seq_ind); + } + else + { + opseq_map_to_delete[op_seq_ind_fp16_to_fp32].insert(op_seq_ind); + } + + VERBOSE(Fp32ToFp16Converter) + << "Contiguous from OpSeq#" << op_seq_ind_fp16_to_fp32.value() << "(ToFp32)" + << " to OpSeq#" << op_seq_ind.value() << "(ToFp16)" << std::endl; + } + } + } + + return opseq_map_to_delete; +} + +Fp32ToFp16Converter::InputToOpSeqs Fp32ToFp16Converter::prepareInputToOpSeqs() const +{ + const auto &op_seqs = _lowered_graph.op_seqs(); + + InputToOpSeqs input_to_op_seqs; + op_seqs.iterate([&](const ir::OpSequenceIndex &op_seq_idx, const ir::OpSequence &op_seq) { + for (auto input : op_seq.getInputs() | ir::Remove::UNDEFINED) + { + auto it = input_to_op_seqs.find(input); + if (it == input_to_op_seqs.end()) + { + input_to_op_seqs[input].emplace(op_seq_idx); + } + else + { + input_to_op_seqs[input].insert(op_seq_idx); + } + } + }); + + return input_to_op_seqs; +} + +Fp32ToFp16Converter::OpSeqIndexList +Fp32ToFp16Converter::getListOpSequences(const OpSeqIndexToOpSeqIndexList &opseq_map_to_delete) const +{ + OpSeqIndexList list; + for (const auto &it : opseq_map_to_delete) + { + auto &opseq_ind_fp16_to_fp32 = it.first; + if (list.find(opseq_ind_fp16_to_fp32) == list.end()) + { + list.emplace(opseq_ind_fp16_to_fp32); + } + + for (auto &opseq_ind_fp32_to_fp16 : it.second) + { + if (list.find(opseq_ind_fp32_to_fp16) == list.end()) + { + list.emplace(opseq_ind_fp32_to_fp16); + } + } + } + return list; +} + +ir::OperandIndexSequence +Fp32ToFp16Converter::findOperationsToDelete(const OpSeqIndexList &list_to_delete_op_seqs) const +{ + const auto &operations = _lowered_graph.graph().operations(); + const auto &op_seqs = _lowered_graph.op_seqs(); + + ir::OperandIndexSequence list_to_delete_ops; + for (const auto &op_seq_ind : list_to_delete_op_seqs) + { + const auto &op_seq = op_seqs.at(op_seq_ind); + assert(op_seq.size() == 1); + + const auto &first_node_ind = op_seq.operations().at(0); + const auto &first_node = operations.at(first_node_ind); + assert(first_node.opcode() == ir::OpCode::ConvertFp32ToFp16 || + first_node.opcode() == ir::OpCode::ConvertFp16ToFp32); + + for (const auto &ind : first_node.getOutputs()) + { + list_to_delete_ops.append(ind); + } + } + + return list_to_delete_ops; +} + +void Fp32ToFp16Converter::manipulateContiguousOpSequences( + const InputToOpSeqs &input_to_op_seqs, const OpSeqIndexToOpSeqIndexList &opseq_map_to_delete) +{ + auto &op_seqs = _lowered_graph.op_seqs(); + + // + // [OPERATION] + // | + // OP#0 // input_ind_fp16_to_fp32 + // | + // [FP16TO32] // op_seq_ind_fp16_to_fp32 & op_seq_fp16_to_fp32 + // | + // OP#1 + // | + // [FP32TO16] // op_seq_ind_fp32_to_fp16, op_seq_fp32_to_fp16 + // | + // OP#2 // output_ind_fp32_to_fp16 + // | + // [OPERATION] // op_seq_ind_next_to_fp16 + // + for (auto it : opseq_map_to_delete) + { + // fp16_to_fp32's input/output num is always 1 + auto &op_seq_ind_fp16_to_fp32 = it.first; + auto &op_seq_fp16_to_fp32 = op_seqs.at(op_seq_ind_fp16_to_fp32); + auto &input_ind_fp16_to_fp32 = op_seq_fp16_to_fp32.getInputs().at(0); + + for (auto &op_seq_ind_fp32_to_fp16 : it.second) + { + auto &op_seq_fp32_to_fp16 = op_seqs.at(op_seq_ind_fp32_to_fp16); + assert(op_seq_fp32_to_fp16.size() == 1); + assert(op_seq_fp32_to_fp16.getInputs().size() == 1); + + auto &output_ind_fp32_to_fp16 = op_seq_fp32_to_fp16.getOutputs().at(0); + auto found_next_to_fp16 = input_to_op_seqs.find(output_ind_fp32_to_fp16); + assert(found_next_to_fp16 != input_to_op_seqs.end()); + + for (auto &op_seq_ind_next_to_fp16 : found_next_to_fp16->second) + { + manipulateInput(op_seq_ind_next_to_fp16, output_ind_fp32_to_fp16, input_ind_fp16_to_fp32); + } + // + // [OPERATION] + // | + // OP#0 // input_ind_fp16_to_fp32 + // | + // [OPERATION] // op_seq_ind_next_to_fp16 + // + } + } +} + +void Fp32ToFp16Converter::deleteContiguousOpSequences( + const OpSeqIndexList &list_to_delete_op_seqs, + const ir::OperandIndexSequence &list_to_delete_ops) +{ + auto &operands = _lowered_graph.graph().operands(); + auto &operations = _lowered_graph.graph().operations(); + auto &op_seqs = _lowered_graph.op_seqs(); + + for (auto &op_seq_ind : list_to_delete_op_seqs) + { + auto &op_seq = op_seqs.at(op_seq_ind); + assert(op_seq.size() == 1); + VERBOSE(Fp32ToFp16Converter) << "Delete OpSeq #" << op_seq_ind.value() << std::endl; + + auto &first_node_ind = op_seq.operations().at(0); + auto &first_node = operations.at(first_node_ind); + assert(first_node.opcode() == ir::OpCode::ConvertFp32ToFp16 || + first_node.opcode() == ir::OpCode::ConvertFp16ToFp32); + VERBOSE(Fp32ToFp16Converter) << "Delete Node #" << first_node_ind.value() << std::endl; + + // Uses + for (auto &ind : first_node.getInputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + auto &obj = operands.at(ind); + obj.removeUse(first_node_ind); + VERBOSE(Fp32ToFp16Converter) << "Operand #" << ind.value() << "'s Use(Node#" + << first_node_ind.value() << ") is removed" << std::endl; + } + + // Def + for (auto &ind : first_node.getOutputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + auto &obj = operands.at(ind); + assert(obj.getDef() == first_node_ind); + obj.unsetDef(); + VERBOSE(Fp32ToFp16Converter) << "Operand #" << ind.value() << "'s Def(Node#" + << first_node_ind.value() << ") is removed" << std::endl; + } + + // Operation + operations.remove(first_node_ind); + VERBOSE(Fp32ToFp16Converter) << "Node#" << first_node_ind.value() << " is removed" << std::endl; + + // OpSequence + op_seqs.remove(op_seq_ind); + VERBOSE(Fp32ToFp16Converter) << "OpSeq#" << op_seq_ind.value() << " is removed" << std::endl; + } + + // Operand + for (auto &ind : list_to_delete_ops) + { + operands.remove(ind); + VERBOSE(Fp32ToFp16Converter) << "Operand #" << ind.value() << " is removed" << std::endl; + } +} + +} // namespace compiler + +} // namespace onert diff --git a/runtime/onert/core/src/compiler/Fp32ToFp16Converter.h b/runtime/onert/core/src/compiler/Fp32ToFp16Converter.h new file mode 100644 index 000000000..eeecb9846 --- /dev/null +++ b/runtime/onert/core/src/compiler/Fp32ToFp16Converter.h @@ -0,0 +1,101 @@ +/* + * 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_FP32_TO_FP16_CONVERTER_H__ +#define __ONERT_COMPILER_FP32_TO_FP16_CONVERTER_H__ + +#include "compiler/LoweredGraph.h" + +namespace onert +{ + +namespace compiler +{ + +class Fp32ToFp16Converter +{ +public: + Fp32ToFp16Converter(compiler::LoweredGraph &lowered_graph); + +public: + void run(); + +private: + using OpSeqIndexList = std::unordered_set<ir::OpSequenceIndex>; + using InputToOpSeqs = std::unordered_map<ir::OperandIndex, OpSeqIndexList>; + using OpSeqIndexToOpSeqIndexList = std::unordered_map<ir::OpSequenceIndex, OpSeqIndexList>; + +private: + void appendOpSequences(); + void optimize(); + void convertOperands(); + void convertDatas(); + void printOpSequences(const std::string &pre_msg = std::string(), + const std::string &post_msg = std::string()); + + bool checkOperandType(const ir::OperandIndex &op_ind) const; + bool checkOperandsOfOpSequence(const ir::OpSequence &op_seq) const; + + void appendNewOpSeqForConvertFp32ToFp16(const ir::OpSequenceIndex &op_seq_ind, + ir::OpSequence &op_seq); + void appendNewOpSeqForConvertFp16ToFp32(const ir::OpSequenceIndex &op_seq_ind, + ir::OpSequence &op_seq); + + ir::OperandIndex newCopiedOperand(const ir::OperandIndex &op_ind); + ir::OperationIndex newOperationConvertFp32ToFp16(const ir::OperandIndex &op_seq_input_ind, + const ir::OperandIndex &new_op_ind); + ir::OperationIndex newOperationConvertFp16ToFp32(const ir::OperandIndex &op_seq_output_ind, + const ir::OperandIndex &new_op_ind); + ir::OpSequenceIndex newOpSequence(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperationIndex &node_index); + + void setNewOperandLowerInfo(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &new_op_ind); + void setNewOpSequenceLowerInfo(const ir::OpSequenceIndex &op_seq_ind, + const ir::OpSequenceIndex &new_op_seq_ind); + + void manipulateInput(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &op_seq_input_ind, + const ir::OperandIndex &new_op_ind); + void manipulateOutput(const ir::OpSequenceIndex &op_seq_ind, + const ir::OperandIndex &op_seq_output_ind, + const ir::OperandIndex &new_op_ind); + + void removeContiguousConvertOpSequences(); + InputToOpSeqs prepareInputToOpSeqs() const; + OpSeqIndexToOpSeqIndexList + findOpSequencesContiguous(const InputToOpSeqs &intput_to_op_seqs) const; + OpSeqIndexList getListOpSequences(const OpSeqIndexToOpSeqIndexList &opseq_map_to_delete) const; + ir::OperandIndexSequence + findOperationsToDelete(const OpSeqIndexList &list_to_delete_op_seqs) const; + void manipulateContiguousOpSequences(const InputToOpSeqs &input_to_op_seqs, + const OpSeqIndexToOpSeqIndexList &opseq_map_to_delete); + void deleteContiguousOpSequences(const OpSeqIndexList &list_to_delete_op_seqs, + const ir::OperandIndexSequence &list_to_delete_ops); + + void convertOperandsOfOpSequence(ir::OpSequence &op_seq); + +private: + compiler::LoweredGraph &_lowered_graph; + OpSeqIndexList _list_fp32_to_fp16; + OpSeqIndexList _list_fp16_to_fp32; +}; + +} // namespace compiler + +} // namespace onert + +#endif // __ONERT_COMPILER_FP32_TO_FP16_CONVERTER_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..349b1c221 --- /dev/null +++ b/runtime/onert/core/src/compiler/HEScheduler.cc @@ -0,0 +1,600 @@ +/* + * 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 "util/logging.h" +#include "util/Utils.h" +#include "exec/FunctionSequence.h" +#include <cassert> +#include <cmath> +#include <chrono> + +namespace +{ + +using namespace onert; + +uint32_t getOperationsFlattenedIOSize(const ir::Graph &graph, const ir::Operation &node) +{ + uint32_t size = 0; + for (const auto &ind : + (node.getInputs() | ir::Remove::UNDEFINED) + (node.getOutputs() | ir::Remove::UNDEFINED)) + { + size += graph.operands().at(ind).info().total_size(); + } + return size; +} + +bool isQuant(const ir::Graph &graph, const ir::Operation &node) +{ + for (const auto &input : node.getInputs() | ir::Remove::UNDEFINED) + { + const auto &obj = graph.operands().at(input); + if (obj.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM) + { + return true; + } + } + return false; +} + +bool isWorkaroundSkip(const ir::Graph &, const backend::Backend *, const ir::Operation &, bool) +{ + // Now, there is no workaround + return false; +} + +// if a node can be merged into op_seq +bool isMergeable(const ir::Graph &graph, const ir::Operation &node) +{ + size_t prev_op_cnt = 0; + for (const auto &input : node.getInputs() | ir::Remove::UNDEFINED) + { + // 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().valid()) + ++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().size() > 1) + { + return false; + } + } + return true; +} + +} // namespace + +namespace onert +{ + +namespace compiler +{ + +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()); + // One of the last nodes + if (only_out_operand.getUses().size() == 0) + { + return; + } + loc_index = *only_out_operand.getUses().begin(); + /* 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) +{ + // TODO Change it to getOperationExecTime() + const auto time = _exec_time->getPermuteTime(src_backend, dst_backend, quant, size); + + if (time != _exec_time->NOT_FOUND) + return time; + + // FIXME permute time is not recorded so the control reaches here always + // Makes the scheduler prefer keeping computations on one backend + return size / 400; +} + +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 + { + // DO NOTHING + + _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() | ir::Remove::UNDEFINED) + { + const auto &operand = _graph->operands().at(output); + const bool quant = operand.typeInfo().type() == ir::DataType::QUANT_UINT8_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; + } + // TODO Change it to controlflow backend + 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()) + { + 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() | ir::Remove::UNDEFINED) + { + const auto &input_operand = _graph->operands().at(input_operand_idx); + const bool quant = input_operand.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM; + + auto input_node_idx = input_operand.getDef(); + if (input_node_idx.valid()) + { + // 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..b9cee5881 --- /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) + : _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) + { + if (entry.first->config()->id() == backend::controlflow::Config::ID) + continue; + _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 + 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; + const backend::Backend *_cpu_backend{nullptr}; // TODO Change this to controlflow_backend + 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..30c8f72a5 --- /dev/null +++ b/runtime/onert/core/src/compiler/Linear.cc @@ -0,0 +1,251 @@ +/* + * 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/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 compiler::LoweredGraph &lowered_graph) +{ + std::vector<ir::OpSequenceIndex> order; + lowered_graph.iterateTopolOpSeqs( + [&](const ir::OpSequenceIndex &index, const ir::OpSequence &) -> void { + order.emplace_back(index); + }); + return order; +} + +void Linear::dump(const compiler::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); + const auto &operations = lowered_graph.graph().operations(); + VERBOSE(Linear) << "* OP_SEQ " << toString(lower_info->backend()) << " " + << ir::getStrFromOpSeq(op_seq, operations) << std::endl; + } + } +} + +void Linear::planTensors(const compiler::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; + } + + // Unused input of subgraph + // TODO Register unused input as nullptr in tensor_builder + 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().valid() ? 1 : 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); + } + + tensor_builder_map[ind] = tensor_builder; + }); + + const auto io_tensors = + (graph.getInputs() + graph.getOutputs()) | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED; + + // If a tensor is model output, increase the use of the tensor. + // This aim is same to above one. + for (const auto &ind : io_tensors) + { + 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() | ir::Remove::DUPLICATED) + { + 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 DEF of inputs. If variable tensor, allocate it + // 3. 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_idx : op_seq.operations()) + { + for (const auto &ind : graph.operations().at(op_idx).getOutputs() | ir::Remove::DUPLICATED | + ir::Remove::UNDEFINED) + { + assert(def_map.find(ind) != def_map.end()); + if (def_map[ind]) + { + def_map[ind] = 0; + tensor_builder_map[ind]->notifyFirstUse(ind); + } + } + + // Scan variable tensors + // This tensor has features like constant. But OperandInfo and LowerInfo treat them as + // non-constant because of less memory usage by memory planning in here + for (const auto &ind : graph.operations().at(op_idx).getInputs() | ir::Remove::DUPLICATED | + ir::Remove::UNDEFINED) + { + const auto &operand = graph.operands().at(ind); + if (operand.info().isVariable()) + { + // The variable tensor with buffer is not supported yet + assert(operand.data() == nullptr); + assert(operand.getUses().size() == 1 && !operand.getDef().valid()); + assert(lowered_graph.getLowerInfo(ind)->def_factors().size() == 1 && + lowered_graph.getLowerInfo(ind)->use_factors().size() == 1); + assert(uses_map[ind] == 1 && def_map[ind] == 0); + tensor_builder_map[ind]->notifyFirstUse(ind); + } + } + + for (const auto &ind : graph.operations().at(op_idx).getInputs() | ir::Remove::DUPLICATED | + ir::Remove::UNDEFINED) + { + assert(uses_map.find(ind) != uses_map.end()); + assert(uses_map[ind] > 0); + uses_map[ind]--; + if (uses_map[ind] == 0) + { + // plan for deallocation of static tensornode + tensor_builder_map[ind]->notifyLastUse(ind); + + // plan for deallocation of dynamic tensor + auto dyn_tensor_manager = tensor_builder_map[ind]->dynamicTensorManager(); + if (dyn_tensor_manager) + { + const auto *backend = + lowered_graph.getLowerInfo(ind)->def_factors().getOnlyElement().backend(); + auto &tensor_registry = lowered_graph.backend_contexts().at(backend)->tensor_registry; + auto *tensor = tensor_registry->getITensor(ind); + assert(tensor); + if (!io_tensors.contains(ind)) // I/O tensors cannot be deallocated + dyn_tensor_manager->planDealloc(op_idx, tensor); + } + } + } + } + } + + // Dispose and validate + for (const auto &ind : io_tensors) + { + --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..1e24cf92b --- /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 "compiler/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 compiler::LoweredGraph &lowered_graph); + static void dump(const compiler::LoweredGraph &lowered_graph, + const std::vector<ir::OpSequenceIndex> &order); + static void planTensors(const compiler::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/LoweredGraph.cc b/runtime/onert/core/src/compiler/LoweredGraph.cc new file mode 100644 index 000000000..673d7d3e8 --- /dev/null +++ b/runtime/onert/core/src/compiler/LoweredGraph.cc @@ -0,0 +1,580 @@ +/* + * 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 "compiler/LoweredGraph.h" + +#include <assert.h> +#include <sstream> +#include "util/logging.h" +#include "compiler/pass/ConstantInsertionPass.h" +#include "compiler/pass/ConstantLoweringPass.h" +#include "compiler/pass/PassRunner.h" +#include "compiler/pass/PermutationOperationPass.h" +#include "compiler/pass/PermutationInsertionPass.h" +#include "compiler/pass/PermutationEliminationPass.h" +#include "ir/GraphIterator.h" +#include "ir/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 compiler +{ + +LoweredGraph::LoweredGraph(const ir::Graph &graph, const CompilerOptions &options) : _graph{graph} +{ + bool linear_executor = (options.executor == "Linear"); + + // Build backend contexts + auto &backend_manager = BackendManager::get(); + + // Always create Controlflow backend context + auto cf_backend = backend_manager.getControlflow(); + _backend_contexts.emplace( + cf_backend, cf_backend->newContext(_graph, _graph.getKernelBuilder(), linear_executor)); + + // Create contexts for other backends + 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 << std::endl; + continue; + } + + _backend_contexts.emplace( + backend, backend->newContext(_graph, _graph.getKernelBuilder(), linear_executor)); + } + if (backend_manager.num_backends() == 0) + throw std::runtime_error{"No available backends loaded."}; + + // TODO Move "schedule" phase out of here + // Schedule + std::unique_ptr<BackendResolver> backend_resolver; + if (options.he_scheduler) + { + auto scheduler = HEScheduler(_backend_contexts, options); + backend_resolver = scheduler.schedule(_graph); + _indexed_ranks = scheduler.getIndexedRanks(); + } + else + { + auto scheduler = ManualScheduler(_backend_contexts, options); + backend_resolver = scheduler.schedule(_graph); + } + + { + // operand::LowerInfo holder + ir::OperandIndexMap<std::unique_ptr<ir::operand::LowerInfo>> operands_lower_info; + + _graph.operands().iterate([&](const ir::OperandIndex &index, const ir::Operand &) { + operands_lower_info[index] = std::make_unique<ir::operand::LowerInfo>(); + }); + + // Make op_seqs while checking whether a node can be merged into a op_seq. + makeOpSequences(operands_lower_info, options, *backend_resolver); + + _op_seqs.iterate([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + assert(op_seq.operations().size() > 0); + std::reverse(std::begin(op_seq.operations()), std::end(op_seq.operations())); + }); + + VERBOSE(OpSequences) << "dump before permutation insertion" << std::endl; + dumpOpSequences(_op_seqs, _graph.operations()); + + // Mandatory passes + pass::PassRunner{} + .append(std::make_unique<pass::ConstantInsertionPass>(*this)) + .append(std::make_unique<pass::ConstantLoweringPass>(*this)) + .run(); + + // Set LowerInfo for each operand from the operand::LowerInfo holder + manipulateLowerInfo(operands_lower_info, options.is_primary_subgraph); + + dumpLowerInfo(); + } + + // Mandatory passes + pass::PassRunner{} + .append(std::make_unique<pass::PermutationOperationPass>(*this)) + .append(std::make_unique<pass::PermutationInsertionPass>(*this)) + .run(); + + // Optimization passes + pass::PassRunner{}.append(std::make_unique<pass::PermutationEliminationPass>(*this)).run(); + + VERBOSE(OpSequences) << "Dump after permutation insertion" << std::endl; + dumpOpSequences(_op_seqs, _graph.operations()); + + // Graph verifications + { + assert(ir::verifier::InputOutputChecker().verify(_graph)); + assert(ir::verifier::DAGChecker().verify(_graph)); + assert(ir::verifier::EdgeConsistencyChecker().verify(_graph)); + } +} + +const ir::operation::LowerInfo * +LoweredGraph::getLowerInfo(const ir::OpSequenceIndex &op_seq_index) const +{ + auto itr = _lower_info_map.op_seq.find(op_seq_index); + if (itr == _lower_info_map.op_seq.end()) + return nullptr; + return itr->second.get(); +} + +void LoweredGraph::setLowerInfo(const ir::OpSequenceIndex &op_seq_index, + std::unique_ptr<ir::operation::LowerInfo> &&lower_info) +{ + _lower_info_map.op_seq.insert(std::make_pair(op_seq_index, std::move(lower_info))); +} + +void LoweredGraph::removeLowerInfo(const ir::OpSequenceIndex &op_seq_index) +{ + auto &op_seq_lower_info = _lower_info_map.op_seq; + 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 ir::operand::LowerInfo *LoweredGraph::getLowerInfo(const ir::OperandIndex &index) const +{ + auto itr = _lower_info_map.operand.find(index); + if (itr == _lower_info_map.operand.end()) + return nullptr; + return itr->second.get(); +} + +ir::operand::LowerInfo *LoweredGraph::getLowerInfo(const ir::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 ir::OperandIndex &index, + std::unique_ptr<ir::operand::LowerInfo> &&lower_info) +{ + _lower_info_map.operand.insert(std::make_pair(index, std::move(lower_info))); +} + +void LoweredGraph::removeLowerInfo(const ir::OperandIndex &index) +{ + _lower_info_map.operand.erase(index); +} + +void LoweredGraph::iterateTopolOpSeqs( + const std::function<void(const ir::OpSequenceIndex &, const ir::OpSequence &)> &fn) const +{ + // Topological Sorting for ir::OpSequences + std::vector<ir::OpSequenceIndex> topol_sorted; + ir::PostDfsIterator<true>{}.iterateOpSeqs( + *this, [&](const ir::OpSequenceIndex &index, const ir::OpSequence &) { + topol_sorted.emplace_back(index); + }); + std::reverse(topol_sorted.begin(), topol_sorted.end()); + for (const auto op_seq_idx : topol_sorted) + { + const auto &op_seq = _op_seqs.at(op_seq_idx); + fn(op_seq_idx, op_seq); + } +} + +void LoweredGraph::iterateTopolOpSeqs( + const std::function<void(const ir::OpSequenceIndex &, ir::OpSequence &)> &fn) +{ + // Topological Sorting for ir::OpSequences + std::vector<ir::OpSequenceIndex> topol_sorted; + ir::PostDfsIterator<false>{}.iterateOpSeqs( + *this, [&](const ir::OpSequenceIndex &index, ir::OpSequence &) { + topol_sorted.emplace_back(index); + }); + std::reverse(topol_sorted.begin(), topol_sorted.end()); + for (const auto op_seq_idx : topol_sorted) + { + auto &op_seq = _op_seqs.at(op_seq_idx); + fn(op_seq_idx, op_seq); + } +} + +ir::OpSequenceIndex LoweredGraph::appendFreshSingleOpSequence(const ir::OperationIndex &node_index, + const ir::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<ir::OpSequence>(_graph.layout()); + + // Add an operation + op_seq->appendOperation(node_index); + + // Update input/output + op_seq->setOutputs(node.getOutputs()); + op_seq->setInputs(node.getInputs()); + + return _op_seqs.emplace(std::move(op_seq)); +} + +void LoweredGraph::makeOpSequences( + ir::OperandIndexMap<std::unique_ptr<ir::operand::LowerInfo>> &operands_lower_info, + const CompilerOptions &options, const BackendResolver &backend_resolver) +{ + // 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; + ir::OpSequence *op_seq = nullptr; + ir::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. + ir::PostDfsConstIterator{}.iterate( + _graph, [&](const ir::OperationIndex &node_index, const ir::Operation &node) { + // LowerInfo for in/output operands + auto backend = backend_resolver.getBackend(node_index); + + // Get frontend's layout + auto frontend_layout = _graph.layout(); + + // The layout of each backend should be set at another place + // TODO Change setting layout of each backend at another place + auto backend_layout = backend->config()->supportLayout(node, frontend_layout); + + for (auto operand : node.getInputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(operand); + lower_info->addUsePermuteFactor(ir::operand::PermuteFactor{backend, backend_layout}); + } + for (auto operand : node.getOutputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(operand); + lower_info->addDefPermuteFactor(ir::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, backend_resolver)) + { + auto new_op_seq_index = appendFreshSingleOpSequence(node_index, node); + + // ir::OpSequence LowerInfo + setLowerInfo(new_op_seq_index, + std::make_unique<ir::operation::LowerInfo>(backend, backend_layout)); + + op_seq_index = new_op_seq_index; + op_seq = &(_op_seqs.at(new_op_seq_index)); + + VERBOSE(Lower) << "OpSequence#" << op_seq_index.value() << " is created for " + << "NODE#" << node_index.value() << "(" << node.name() << ")" << std::endl; + } + else + { + op_seq->appendOperation(node_index); + // Set inputs + auto new_inputs = node.getInputs(); + // Add inputs except outputs of the previous node + for (auto ind : op_seq->getInputs()) + { + if (!node.getOutputs().contains(ind)) + new_inputs.append(ind); + } + op_seq->setInputs(new_inputs); + + VERBOSE(Lower) << "OpSequence#" << op_seq_index.value() << " merges " + << "NODE#" << node_index.value() << "(" << node.name() << ")" << std::endl; + } + }); +} + +void LoweredGraph::manipulateLowerInfo( + ir::OperandIndexMap<std::unique_ptr<ir::operand::LowerInfo>> &operands_lower_info, + bool is_primary) +{ + const auto controlflow_backend = BackendManager::get().getControlflow(); + + // TODO Rather than handling primary graph specially, + // let the permute inserted and remove it later + if (is_primary) + { + // TODO Rather than using NHWC Get frontend layout of this node from IR + auto factor = ir::operand::PermuteFactor{controlflow_backend, ir::Layout::NHWC}; + for (auto index : _graph.getInputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(index); + assert(lower_info->def_factors().empty()); + lower_info->addDefPermuteFactor(factor); + } + for (auto index : _graph.getOutputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(index); + lower_info->addUsePermuteFactor(factor); + } + } + else + { + for (auto index : _graph.getInputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(index); + if (!(lower_info->def_factors().size() == 0 && lower_info->use_factors().size() == 0)) + { + // In case of not that Graph's input is not used in any operation and not the graph's + // output. + // In other words, it is not unused input in Graph. + lower_info->addDefPermuteFactor(*lower_info->use_factors().begin()); + } + else + { + // In case of that an operand is Graph's input and not input or output of any operation + lower_info->addDefPermuteFactor(ir::operand::PermuteFactor{ + controlflow_backend, + ir::Layout::NHWC // TODO Get frontend layout of this node from IR + }); + } + } + } + for (auto index : _graph.getOutputs() | ir::Remove::UNDEFINED) + { + auto &&lower_info = operands_lower_info.at(index); + if (lower_info->def_factors().size() == 0) + { + // In case of that an operand is Graph's output and not input or output of any operation + lower_info->addDefPermuteFactor(ir::operand::PermuteFactor{ + controlflow_backend, + ir::Layout::NHWC // TODO Get frontend layout of this node from IR + }); + } + } + + // 1. Add def of variable operand + // 2. Set LowerInfo for each operand from the operand::LowerInfo holder + _graph.operands().iterate([&](const ir::OperandIndex &index, ir::Operand &operand) { + // Some inputs of an operation could be non-constant, but not existed in graph inputs/outputs + // and not undefined operand. Those inputs must have exist as a Tensor. For example, + // UnidirectionalSequenceLSTM operation could have state inputs such as it. + if (operand.info().isVariable()) + { + // The variable operand with buffer is not supported yet + assert(operand.data() == nullptr); + assert(operand.getUses().size() == 1 && !operand.getDef().valid()); + auto &lowered_info = operands_lower_info[index]; + assert(lowered_info->def_factors().empty()); + lowered_info->addDefPermuteFactor(lowered_info->use_factors().getOnlyElement()); + } + + 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 ir::OperandIndex &index, ir::Operand &object) { + std::stringstream sstream; + if (!getLowerInfo(index)->def_factors().empty() || !getLowerInfo(index)->use_factors().empty()) + { + auto factors_to_string = [](const ir::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 ir::OperationIndexSet &operations) { + std::string str; + for (auto op : operations) + { + str += std::to_string(op.value()); + str += " "; + } + return "{ " + str + "}"; + }; + + const auto lower_info = getLowerInfo(index); + const auto &shape = object.shape(); + std::string def_ops = + object.getDef().valid() ? std::to_string(object.getDef().value()) : "N/A"; + 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 : { "; + for (auto i = 0; i < shape.rank(); ++i) + { + sstream << (shape.dim(i)) << " "; + } + sstream << "}" << std::endl; + sstream << " - Def ir::Operations : " << def_ops << std::endl; + sstream << " - Use ir::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 ir::OpSequenceIndex &op_seq_index, + const ir::OperationIndex &node_index, ir::Layout layout, + const BackendResolver &backend_resolver) +{ + // 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) << "OpSequence#" << 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<ir::OperationIndex> branched_set; + + // Check for branching up + for (const auto &input : op_seq.getInputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + const auto &input_obj = _graph.operands().at(input); + auto def = input_obj.getDef(); + if (def.valid()) + { + branched_set.insert(def); + if (branched_set.size() > 1) + { + return false; + } + } + } + branched_set.clear(); + + // Check for branching down + for (const auto &output : node.getOutputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + // TODO Fix this workaround for the case of model outputs that are used by another operation + // This is needed since the branching is decided by operation, but for model outputs, + // there is controlflow backen(use backend) but no actual use operation exists + if (_graph.getOutputs().contains(output)) + return false; + + const auto &output_obj = _graph.operands().at(output); + for (const auto &use : output_obj.getUses()) + { + 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<ir::OperationIndex> 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 &n_index : op_seq_ops) + { + const auto &n = _graph.operations().at(n_index); + + // node's output == op_seq's input? + for (const auto input : n.getInputs() | ir::Remove::UNDEFINED) + { + if (node_outputs.contains(input)) + { + VERBOSE(Lower) << "OpSequence#" << 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? + for (const auto output : n.getOutputs() | ir::Remove::UNDEFINED) + { + if (node_inputs.contains(output)) + { + VERBOSE(Lower) << "OpSequence#" << op_seq_index.value() << " 's NODE#" << n_index.value() + << " (" << n.name() << ") is connected to NODE#" << node_index.value() + << std::endl; + return true; + } + } + } + + VERBOSE(Lower) << "OpSequence#" << op_seq_index.value() << " is not connected to NODE#" + << node_index.value() << "(" << node.name() << ")" << std::endl; + } + + return false; +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ManualScheduler.cc b/runtime/onert/core/src/compiler/ManualScheduler.cc new file mode 100644 index 000000000..ed49ee56f --- /dev/null +++ b/runtime/onert/core/src/compiler/ManualScheduler.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 "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 backend::BackendContexts &backend_contexts, + const compiler::CompilerOptions &options) + : _backend_contexts{backend_contexts}, _options{options} +{ +} + +std::unique_ptr<BackendResolver> ManualScheduler::schedule(const ir::Graph &graph) +{ + const auto &manual_options = _options.manual_scheduler_options; + auto backend_resolver = std::make_unique<compiler::BackendResolver>(); + + // This fallback will be used in case that `backend_for_all` is unavailable + auto fallback = [&]() -> const backend::Backend * { + for (auto backend_id : _options.backend_list) + { + auto backend = resolveBackend(backend_id); + if (backend) + return backend; + } + return nullptr; + }(); + if (fallback == nullptr) + throw std::runtime_error{"No loaded backends available."}; + + // 1. Backend for All operations + const backend::Backend *backend_all = resolveBackend(manual_options.backend_for_all, fallback); + VERBOSE(ManualScheduler) << "Default backend for all ops: " << backend_all->config()->id() + << 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 : manual_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 : manual_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)); // TODO Ensure this backend is available in backend contexts + } + catch (...) + { + VERBOSE(ManualScheduler) << "Invalid value while OperationIndex to Backend mapping : @" + << key.value() << " -> \"" << val << "\"" << std::endl; + } + } + + // 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; +} + +const backend::Backend *ManualScheduler::resolveBackend(const std::string &id, + const backend::Backend *fallback) +{ + // Ensure if the backend is available in the current backend context + const backend::Backend *backend = BackendManager::get().get(id); + if (!backend || _backend_contexts.find(backend) == _backend_contexts.end()) + { + backend = fallback; + } + return backend; +} + +} // 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..41503f7ff --- /dev/null +++ b/runtime/onert/core/src/compiler/ManualScheduler.h @@ -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. + */ + +#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 backend::BackendContexts &backend_contexts, + const compiler::CompilerOptions &options); + std::unique_ptr<BackendResolver> schedule(const ir::Graph &graph) override; + +private: + const backend::Backend *resolveBackend(const std::string &id, + const backend::Backend *fallback = nullptr); + +private: + const backend::BackendContexts &_backend_contexts; + compiler::CompilerOptions _options; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_CORE_COMPILER_MANUAL_SCHEDULER_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/compiler/ShapeValidator.cc b/runtime/onert/core/src/compiler/ShapeValidator.cc new file mode 100644 index 000000000..c18178da9 --- /dev/null +++ b/runtime/onert/core/src/compiler/ShapeValidator.cc @@ -0,0 +1,1038 @@ +/* + * 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 "ShapeValidator.h" + +#include <typeinfo> + +#include "ir/Graph.h" +#include "ir/operation/LowerInfo.h" + +#include "util/logging.h" +#include "util/Utils.h" + +#define OP_REQUIRES(EXP) \ + do \ + { \ + if (!(EXP)) \ + throw std::runtime_error("ShapeValidator failed at line " + std::to_string(__LINE__)); \ + } while (0) + +namespace onert +{ +namespace compiler +{ + +ShapeValidator::ShapeValidator(const ir::Graph &graph) + : _graph{graph}, _ctx{graph.operands()}, _current_op_seq_layout{ir::Layout::UNKNOWN} +{ +} + +void ShapeValidator::checkUnaryOp(const ir::Operation &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + if (_ctx.at(output_index).info().isDynamic()) + return; + + // Check if I/O shapes match + OP_REQUIRES(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void ShapeValidator::operator()() +{ + // There is no reason for each subgraph to have subgraphs since compiler has subgraphs when + // creating Compiler + assert(_graph.subgraphs() == nullptr); + + _current_op_seq_layout = _graph.layout(); + + _graph.operations().iterate( + [&](const ir::OperationIndex &, const ir::Operation &node) { node.accept(*this); }); +} + +void ShapeValidator::visit(const ir::operation::BatchMatMul &node) +{ + const auto lhs_index(node.getInputs().at(ir::operation::BatchMatMul::Input::LHS)); + const auto rhs_index(node.getInputs().at(ir::operation::BatchMatMul::Input::RHS)); + const auto out_index{node.getOutputs().at(0)}; + + if (_ctx.at(out_index).info().isDynamic()) + return; + + OP_REQUIRES(_ctx.at(lhs_index).shape().rank() <= 4); + OP_REQUIRES(_ctx.at(rhs_index).shape().rank() <= 4); + OP_REQUIRES(_ctx.at(lhs_index).shape().rank() >= 2); + OP_REQUIRES(_ctx.at(rhs_index).shape().rank() >= 2); +} + +void ShapeValidator::visit(const ir::operation::BatchToSpaceND &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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); + + // All requirement as per NNAPI specification. + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(block_size_index).shape().rank() == 1); + + OP_REQUIRES(_ctx.at(block_size_index).shape().dim(0) == 2); + + OP_REQUIRES(input_shape.C == output_shape.C); +} + +void ShapeValidator::visit(const ir::operation::BCQFullyConnected &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + const auto ifm_index{node.getInputs().at(ir::operation::BCQFullyConnected::Input::INPUT)}; + const auto weight_scales_index{ + node.getInputs().at(ir::operation::BCQFullyConnected::Input::WEIGHTS_SCALES)}; + const auto weight_binary_index{ + node.getInputs().at(ir::operation::BCQFullyConnected::Input::WEIGHTS_BINARY)}; + const auto weight_cluster_index{ + node.getInputs().at(ir::operation::BCQFullyConnected::Input::WEIGHTS_CLUSTERS)}; + // const auto bias_index{node.getInputs().at(ir::operation::BCQFullyConnected::Input::BIAS)}; + + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 2); + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == 2); + OP_REQUIRES(_ctx.at(weight_scales_index).shape().rank() == 1); + OP_REQUIRES(_ctx.at(weight_binary_index).shape().rank() == 2); + OP_REQUIRES(_ctx.at(weight_cluster_index).shape().rank() == 2); + + OP_REQUIRES(_ctx.at(ifm_index).shape().dim(1) == _ctx.at(ofm_index).shape().dim(1)); + + OP_REQUIRES(_ctx.at(weight_cluster_index).shape().dim(0) > 0); + OP_REQUIRES(_ctx.at(weight_cluster_index).shape().dim(1) == 2); + + // more shape validation will be done inside kernel. + + // TODO Check bias dimension (can be null tensor) +} + +void ShapeValidator::visit(const ir::operation::BCQGather &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + const auto indices_index{node.getInputs().at(ir::operation::BCQGather::Input::INDICES)}; + const auto input_binary_index{node.getInputs().at(ir::operation::BCQGather::Input::INPUT_BINARY)}; + const auto input_scales_index{node.getInputs().at(ir::operation::BCQGather::Input::INPUT_SCALES)}; + const auto input_clusters_index{ + node.getInputs().at(ir::operation::BCQGather::Input::INPUT_CLUSTERS)}; + + OP_REQUIRES(_ctx.at(indices_index).shape().rank() <= 2); // TODO : support rank up to 4 or more + OP_REQUIRES(_ctx.at(input_binary_index).shape().rank() == 2); + OP_REQUIRES(_ctx.at(input_scales_index).shape().rank() == 1); + OP_REQUIRES(_ctx.at(input_clusters_index).shape().rank() == 2); + + OP_REQUIRES(_ctx.at(input_clusters_index).shape().dim(0) > 0); + OP_REQUIRES(_ctx.at(input_clusters_index).shape().dim(1) == 2); + + // more shape validation will be done inside kernel. +} + +void ShapeValidator::visit(const ir::operation::Comparison &) +{ + // TODO Shape validation of comparison +} + +void ShapeValidator::visit(const ir::operation::Softmax &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(0)}; + + OP_REQUIRES(_ctx.at(output_index).shape().rank() == _ctx.at(input_index).shape().rank()); +} + +void ShapeValidator::visit(const ir::operation::InstanceNorm &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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)}; + + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(ifm_index).shape() == _ctx.at(ofm_index).shape()); + OP_REQUIRES(_ctx.at(gamma_index).shape().rank() == 1); + OP_REQUIRES(_ctx.at(beta_index).shape().rank() == 1); +} + +void ShapeValidator::visit(const ir::operation::Pool2D &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + const auto ifm_index{node.getInputs().at(ir::operation::Pool2D::Input::INPUT)}; + + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 4); +} + +void ShapeValidator::visit(const ir::operation::Permute &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(0)}; + + OP_REQUIRES(_ctx.at(output_index).shape().rank() == _ctx.at(input_index).shape().rank()); +} + +void ShapeValidator::visit(const ir::operation::Reduce &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(ir::operation::Reduce::Input::INPUT)}; + const auto input_shape = _ctx.at(input_index).shape(); + const auto output_shape = _ctx.at(output_index).shape(); + + OP_REQUIRES(input_shape.rank() <= 4); + OP_REQUIRES(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 + OP_REQUIRES(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) + OP_REQUIRES((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 ShapeValidator::visit(const ir::operation::Transpose &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(ir::operation::Transpose::Input::INPUT)}; + const auto perm_index{node.getInputs().at(ir::operation::Transpose::Input::PERMUTATION)}; + + const auto &output_shape = _ctx.at(output_index).shape(); + const auto &input_shape = _ctx.at(input_index).shape(); + + OP_REQUIRES(_ctx.at(perm_index).shape().num_elements() == 0 || + input_shape.rank() == static_cast<int>(_ctx.at(perm_index).shape().num_elements())); + OP_REQUIRES(input_shape.rank() == output_shape.rank()); +} + +void ShapeValidator::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)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + 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); + + OP_REQUIRES(_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); + OP_REQUIRES(_ctx.at(bias_index).shape().rank() == 1); + + OP_REQUIRES(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)); + OP_REQUIRES(_ctx.at(input_index).shape().dim(1) == _ctx.at(weights_index).shape().dim(1)); + + OP_REQUIRES(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)); + OP_REQUIRES(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 ShapeValidator::visit(const ir::operation::SpaceToBatchND &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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); + + // All requirement as per NNAPI specification. + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(block_size_index).shape().rank() == 1); + OP_REQUIRES(_ctx.at(paddings_index).shape().rank() == 2); + + OP_REQUIRES(_ctx.at(block_size_index).shape().dim(0) == 2); + OP_REQUIRES(_ctx.at(paddings_index).shape().dim(0) == 2); + OP_REQUIRES(_ctx.at(paddings_index).shape().dim(1) == 2); + + OP_REQUIRES(input_shape.C == output_shape.C); +} + +void ShapeValidator::visit(const ir::operation::SpaceToDepth &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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; + + // All assertions as per NNAPI specification. + OP_REQUIRES(_ctx.at(ifm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == 4); + OP_REQUIRES((input_shape.H % block_size == 0) && (input_shape.W % block_size == 0)); + OP_REQUIRES(input_shape.N == output_shape.N); + OP_REQUIRES(input_shape.C * block_size * block_size == output_shape.C); +} + +void ShapeValidator::visit(const ir::operation::ElementwiseActivation &node) { checkUnaryOp(node); } + +void ShapeValidator::visit(const ir::operation::ElementwiseBinary &) +{ + // TODO Shape validation of ElementwiseBinary +} + +void ShapeValidator::visit(const ir::operation::ElementwiseUnary &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT)}; + + if (_ctx.at(output_index).info().isDynamic()) + return; + + OP_REQUIRES(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void ShapeValidator::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); + + // Verify operand here, not at SimpleEmbeddingLookup::configure() to avoid acl's modifying + // TensorShape sometimes(Issue: https://github.sec.samsung.net/STAR/nnfw/issues/729) + { + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto &output_shape = output_obj.shape(); + const auto &lookups_shape = lookups_obj.shape(); + const auto &values_shape = values_obj.shape(); + + OP_REQUIRES(lookups_shape.rank() == 1); + OP_REQUIRES(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. + OP_REQUIRES(output_shape.rank() == values_shape.rank()); + OP_REQUIRES(output_shape.dim(0) == lookups_shape.dim(0)); + for (int n = 1; n < output_shape.rank(); ++n) + { + OP_REQUIRES(output_shape.dim(n) == values_shape.dim(n)); + } + } +} + +void ShapeValidator::visit(const ir::operation::ExpandDims &node) +{ + const auto axis_index{node.getInputs().at(ir::operation::ExpandDims::Input::AXIS)}; + + if (_ctx.at(axis_index).info().isDynamic()) + return; + OP_REQUIRES(_ctx.at(axis_index).shape().rank() <= 1); +} + +void ShapeValidator::visit(const ir::operation::HashtableLookup &node) +{ + const auto output_index{node.getOutputs().at(ir::operation::HashtableLookup::Output::OUTPUT)}; + 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 &lookups_obj = _ctx.at(lookups_index); + const auto &keys_obj = _ctx.at(keys_index); + const auto &values_obj = _ctx.at(values_index); + + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto &output_shape = output_obj.shape(); + const auto &lookups_shape = lookups_obj.shape(); + const auto &keys_shape = keys_obj.shape(); + const auto &values_shape = values_obj.shape(); + + OP_REQUIRES(values_shape.rank() == output_shape.rank()); + OP_REQUIRES(lookups_shape.rank() == 1); + OP_REQUIRES(keys_shape.rank() == 1); + OP_REQUIRES(values_shape.dim(0) == keys_shape.dim(0)); + OP_REQUIRES(lookups_shape.dim(0) == output_shape.dim(0)); +} + +void ShapeValidator::visit(const ir::operation::TransposeConv &node) +{ + // shape check + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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 + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(ofm_index).shape().rank() == _ctx.at(ifm_index).shape().rank()); + OP_REQUIRES(_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); + + OP_REQUIRES(ifm_shape.N == ofm_shape.N); + OP_REQUIRES(ifm_shape.C == ker_shape.C); + OP_REQUIRES(ker_shape.N == ofm_shape.C); +} + +void ShapeValidator::visit(const ir::operation::Gather &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + 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 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(); + + OP_REQUIRES(ifm_shape.rank() <= 4); + OP_REQUIRES(indices_shape.rank() <= 3); + OP_REQUIRES(ofm_shape.rank() <= 4); +} + +void ShapeValidator::visit(const ir::operation::DepthToSpace &node) +{ + int32_t block_size = node.param().block_size; + + // shape check + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + 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); + + OP_REQUIRES(_ctx.at(input_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(output_index).shape().rank() == 4); + + { + OP_REQUIRES(output_shape.N == input_shape.N); + OP_REQUIRES(output_shape.H == input_shape.H * block_size); + OP_REQUIRES(output_shape.W == input_shape.W * block_size); + OP_REQUIRES(input_shape.C % (block_size * block_size) == 0); + OP_REQUIRES(output_shape.C == input_shape.C / (block_size * block_size)); + } +} + +void ShapeValidator::visit(const ir::operation::Pack &node) +{ + const auto axis{node.param().axis}; + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + // shape check + 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(); + + OP_REQUIRES(axis >= -output_rank && axis < output_rank); + for (const auto &index : node.getInputs()) + { + OP_REQUIRES(input_shape == _ctx.at(index).shape()); + } +} + +void ShapeValidator::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 output_index{node.getOutputs().at(ir::operation::LSTM::Output::OUTPUT)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto scratch_buffer_index{ + node.getOutputs().at(ir::operation::LSTM::Output::SCRATCH_BUFFER)}; // Optional + const auto output_state_out_index{ + node.getOutputs().at(ir::operation::LSTM::Output::OUTPUT_STATE_OUT)}; // Optional + const auto cell_state_out_index{ + node.getOutputs().at(ir::operation::LSTM::Output::CELL_STATE_OUT)}; // Optional + + 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)}; // Optional + 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)}; // Optional + 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)}; // Optional + const auto cell_to_forget_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::CELL_TO_FORGET_WEIGHTS)}; // Optional + const auto cell_to_output_weights_index{ + node.getInputs().at(ir::operation::LSTM::Input::CELL_TO_OUTPUT_WEIGHTS)}; // Optional + const auto input_gate_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::INPUT_GATE_BIAS)}; // Optional + 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)}; // Optional + const auto projection_bias_index{ + node.getInputs().at(ir::operation::LSTM::Input::PROJECTION_BIAS)}; // Optional + 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)}; + + OP_REQUIRES(_ctx.at(input_index).shape().rank() == _ctx.at(output_index).shape().rank()); + for (int i = 0; i < _ctx.at(input_index).shape().rank() - 1; ++i) + { + OP_REQUIRES(_ctx.at(input_index).shape().dim(i) == _ctx.at(output_index).shape().dim(i)); + } + OP_REQUIRES( + (_ctx.at(output_index).shape().rank() == 2 || _ctx.at(output_index).shape().rank() == 3) && + (_ctx.at(input_index).shape().rank() == 2 || _ctx.at(input_index).shape().rank() == 3) && + (!_ctx.exist(input_to_input_weights_index) || + _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.exist(recurrent_to_input_weights_index) || + _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.exist(projection_weights_index) || + _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); + + OP_REQUIRES( + (!_ctx.exist(cell_to_input_weights_index) || + _ctx.at(cell_to_input_weights_index).shape().rank() == 1) && + (!_ctx.exist(cell_to_forget_weights_index) || + _ctx.at(cell_to_forget_weights_index).shape().rank() == 1) && + (!_ctx.exist(cell_to_output_weights_index) || + _ctx.at(cell_to_output_weights_index).shape().rank() == 1) && + (!_ctx.exist(input_gate_bias_index) || _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.exist(projection_bias_index) || _ctx.at(projection_bias_index).shape().rank() == 1)); + + // CIFG assertion + OP_REQUIRES( + ((!_ctx.exist(input_to_input_weights_index) || + (_ctx.at(input_to_input_weights_index).shape().dim(0) == 0 && + _ctx.at(input_to_input_weights_index).shape().dim(1) == 0)) && + (!_ctx.exist(recurrent_to_input_weights_index) || + (_ctx.at(recurrent_to_input_weights_index).shape().dim(0) == 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(1) == 0)) && + (!_ctx.exist(input_gate_bias_index) || _ctx.at(input_gate_bias_index).shape().dim(0) == 0) && + (!_ctx.exist(cell_to_input_weights_index) || + _ctx.at(cell_to_input_weights_index).shape().dim(0) == 0)) || + ((_ctx.exist(input_to_input_weights_index) && + (_ctx.at(input_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(input_to_input_weights_index).shape().dim(1) != 0)) && + (_ctx.exist(recurrent_to_input_weights_index) && + (_ctx.at(recurrent_to_input_weights_index).shape().dim(0) != 0 && + _ctx.at(recurrent_to_input_weights_index).shape().dim(1) != 0)) && + (_ctx.exist(input_gate_bias_index) && _ctx.at(input_gate_bias_index).shape().dim(0) != 0))); + + // Peephole assertion + OP_REQUIRES(((!_ctx.exist(cell_to_forget_weights_index) || + _ctx.at(cell_to_forget_weights_index).shape().dim(0) == 0) && + (!_ctx.exist(cell_to_output_weights_index) || + _ctx.at(cell_to_output_weights_index).shape().dim(0) == 0)) || + ((_ctx.exist(cell_to_forget_weights_index) && + _ctx.at(cell_to_forget_weights_index).shape().dim(0) != 0) && + (_ctx.exist(cell_to_output_weights_index) && + _ctx.at(cell_to_output_weights_index).shape().dim(0) != 0))); + + bool has_input_to_input_weights = _ctx.exist(input_to_input_weights_index) && + (_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.exist(recurrent_to_input_weights_index) && + (_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.exist(input_gate_bias_index) && _ctx.at(input_gate_bias_index).shape().dim(0) != 0; + bool has_cell_to_input_weights = _ctx.exist(cell_to_input_weights_index) && + _ctx.at(cell_to_input_weights_index).shape().dim(0) != 0; + bool has_cell_to_forget_weights = _ctx.exist(cell_to_forget_weights_index) && + _ctx.at(cell_to_forget_weights_index).shape().dim(0) != 0; + bool has_cell_to_output_weights = _ctx.exist(cell_to_output_weights_index) && + _ctx.at(cell_to_output_weights_index).shape().dim(0) != 0; + bool has_projection_weights = _ctx.exist(projection_weights_index) && + (_ctx.at(projection_weights_index).shape().dim(0) != 0 && + _ctx.at(projection_weights_index).shape().dim(1) != 0); + bool has_projection_bias = + _ctx.exist(projection_bias_index) && _ctx.at(projection_bias_index).shape().dim(0) != 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; + + const auto batch_size = (_ctx.at(input_index).shape().rank() == 3 && node.param().time_major) + ? _ctx.at(input_index).shape().dim(1) + : _ctx.at(input_index).shape().dim(0); + OP_REQUIRES(batch_size == _ctx.at(output_state_in_index).shape().dim(0) && + batch_size == _ctx.at(cell_state_in_index).shape().dim(0)); + + const auto input_size = _ctx.at(input_index).shape().dim(_ctx.at(input_index).shape().rank() - 1); + OP_REQUIRES(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(input_to_output_weights_index).shape().dim(0); + OP_REQUIRES(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)); + + const auto output_size = + _ctx.at(output_index).shape().dim(_ctx.at(output_index).shape().rank() - 1); + OP_REQUIRES(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)); + + if (has_cifg_param) + { + OP_REQUIRES(input_size == _ctx.at(input_to_input_weights_index).shape().dim(1)); + OP_REQUIRES(num_units == _ctx.at(input_to_input_weights_index).shape().dim(0) && + num_units == _ctx.at(recurrent_to_input_weights_index).shape().dim(0) && + ((_ctx.exist(cell_to_input_weights_index) && + num_units == _ctx.at(cell_to_input_weights_index).shape().dim(0)) || + (!_ctx.exist(cell_to_input_weights_index) || + _ctx.at(cell_to_input_weights_index).shape().dim(0) == 0) /* non-peephole */) && + num_units == _ctx.at(input_gate_bias_index).shape().dim(0)); + OP_REQUIRES(output_size == _ctx.at(recurrent_to_input_weights_index).shape().dim(1)); + OP_REQUIRES(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. + OP_REQUIRES(has_peephole_param); + } + if (_ctx.exist(scratch_buffer_index)) + OP_REQUIRES(_ctx.at(scratch_buffer_index).shape().dim(1) == num_units * 4); + } + else + { + if (_ctx.exist(scratch_buffer_index)) + OP_REQUIRES(_ctx.at(scratch_buffer_index).shape().dim(1) == num_units * 3); + } + + if (has_peephole_param) + { + OP_REQUIRES(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) + { + OP_REQUIRES(num_units == _ctx.at(projection_weights_index).shape().dim(1)); + OP_REQUIRES(output_size == _ctx.at(projection_weights_index).shape().dim(0)); + if (has_projection_bias) + { + OP_REQUIRES(output_size == _ctx.at(projection_bias_index).shape().dim(0)); + } + } + + if (_ctx.exist(scratch_buffer_index)) + { + OP_REQUIRES(_ctx.at(scratch_buffer_index).shape().rank() == 2); + OP_REQUIRES(batch_size == _ctx.at(scratch_buffer_index).shape().dim(0)); + } + + if (_ctx.exist(output_state_out_index)) + { + OP_REQUIRES(_ctx.at(output_state_out_index).shape().rank() == 2); + OP_REQUIRES(batch_size == _ctx.at(output_state_out_index).shape().dim(0)); + OP_REQUIRES(output_size == _ctx.at(output_state_out_index).shape().dim(1)); + } + + if (_ctx.exist(cell_state_out_index)) + { + OP_REQUIRES(_ctx.at(cell_state_out_index).shape().rank() == 2); + OP_REQUIRES(batch_size == _ctx.at(cell_state_out_index).shape().dim(0)); + OP_REQUIRES(num_units == _ctx.at(cell_state_out_index).shape().dim(1)); + } +} + +void ShapeValidator::visit(const ir::operation::L2Normalization &node) +{ + const auto ofm_index{node.getOutputs().at(0)}; + if (_ctx.at(ofm_index).info().isDynamic()) + return; + + const auto ifm_index{node.getInputs().at(ir::operation::L2Normalization::Input::INPUT)}; + + auto ifm_shape = _ctx.at(ifm_index).shape(); + auto ofm_shape = _ctx.at(ofm_index).shape(); + + OP_REQUIRES(ifm_shape.rank() == ofm_shape.rank()); + + for (auto i = 0; i < ifm_shape.rank(); i++) + { + OP_REQUIRES(ifm_shape.dim(i) == ofm_shape.dim(i)); + } +} + +void ShapeValidator::visit(const ir::operation::Unpack &node) +{ + const auto axis{node.param().axis}; + const auto output_index{node.getInputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(ir::operation::Unpack::Input::INPUT)}; + + const auto &input_shape = _ctx.at(input_index).shape(); + const auto input_rank = static_cast<int32_t>(input_shape.rank()); + + OP_REQUIRES(axis >= -input_rank && axis < input_rank); +} + +void ShapeValidator::visit(const ir::operation::Pad &node) +{ + const auto pad_index{node.getInputs().at(ir::operation::Pad::Input::PAD)}; + OP_REQUIRES(_ctx.at(pad_index).typeInfo().type() == ir::DataType::INT32); + + const auto output_index{node.getInputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(ir::operation::Pad::Input::INPUT)}; + + const auto &pad_shape = _ctx.at(pad_index).shape(); + const auto input_rank = static_cast<int32_t>(_ctx.at(input_index).shape().rank()); + + OP_REQUIRES(pad_shape.rank() == 2); + OP_REQUIRES(pad_shape.dim(0) == input_rank); + OP_REQUIRES(pad_shape.dim(1) == 2); + OP_REQUIRES(_ctx.at(input_index).shape().rank() == _ctx.at(output_index).shape().rank()); +} + +void ShapeValidator::visit(const ir::operation::Select &) +{ + // TODO Shape validation of select +} + +void ShapeValidator::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)}; + + if (_ctx.at(output_index).info().isDynamic()) + return; + + OP_REQUIRES(_ctx.at(input_index).shape().rank() <= 4); +} + +void ShapeValidator::visit(const ir::operation::Split &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(ir::operation::Split::Input::INPUT)}; + const auto axis_index{node.getInputs().at(ir::operation::Split::Input::AXIS)}; + + const auto num_splits = node.param().num_splits; + const auto input_rank = _ctx.at(input_index).shape().rank(); + auto axis = *reinterpret_cast<const int32_t *>(_ctx.at(axis_index).data()->base()); + axis = axis < 0 ? axis + input_rank : axis; + + OP_REQUIRES(axis >= 0 && axis < input_rank); + OP_REQUIRES(_ctx.at(input_index).shape().dim(axis) % num_splits == 0); +} + +void ShapeValidator::visit(const ir::operation::Shape &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(0)}; + UNUSED_RELEASE(input_index); + OP_REQUIRES(_ctx.at(output_index).shape().rank() == 1); +} + +void ShapeValidator::visit(const ir::operation::ResizeBilinear &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::ResizeBilinear::Input::INPUT)}; + + if (_ctx.at(output_index).info().isDynamic()) + { + return; + } + OP_REQUIRES(_ctx.at(input_index).shape().rank() == 4); + OP_REQUIRES(_ctx.at(output_index).shape().rank() == 4); +} + +void ShapeValidator::visit(const ir::operation::Reverse &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::Reverse::Input::INPUT)}; + + if (_ctx.at(output_index).info().isDynamic()) + return; + OP_REQUIRES(_ctx.at(output_index).shape() == _ctx.at(input_index).shape()); +} + +void ShapeValidator::visit(const ir::operation::If &) +{ + // TODO Add to validate with subgraphs +} + +void ShapeValidator::visit(const ir::operation::While &) +{ + // This validator does not check shape. So checking isDynamic() is skipped. + // TODO Add to validate with subgraphs +} + +void ShapeValidator::visit(const ir::operation::SquaredDifference &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(ir::operation::SquaredDifference::Input::LHS)}; + const auto rhs_index{node.getInputs().at(ir::operation::SquaredDifference::Input::RHS)}; + + // Check for dimension constraints + if (_ctx.at(output_index).info().isDynamic()) + return; + + auto output_shape = _ctx.at(output_index).shape(); + auto lhs_shape = _ctx.at(lhs_index).shape(); + auto rhs_shape = _ctx.at(rhs_index).shape(); + // Check for output rank + OP_REQUIRES(output_shape.rank() == std::max(lhs_shape.rank(), rhs_shape.rank())); + auto min_rank = std::min(lhs_shape.rank(), rhs_shape.rank()); + + for (int idx = 1; idx <= min_rank; idx++) + { + int l_idx = lhs_shape.rank() - idx; + int r_idx = rhs_shape.rank() - idx; + int out_idx = output_shape.rank() - idx; + + OP_REQUIRES((l_idx >= 0) && (r_idx >= 0) && (out_idx >= 0)); + + auto l_dims = lhs_shape.dim(l_idx); + auto r_dims = rhs_shape.dim(r_idx); + auto out_dims = output_shape.dim(out_idx); + + OP_REQUIRES(((l_dims == r_dims) && (out_dims == l_dims)) || + ((l_dims == 1) && (out_dims == r_dims)) || ((r_dims == 1) && (out_dims == l_dims))); + } + auto &tmp_shape = (lhs_shape.rank() > rhs_shape.rank()) ? lhs_shape : rhs_shape; + for (int idx = min_rank + 1; idx <= output_shape.rank(); idx++) + { + int out_idx = output_shape.rank() - idx; + int tmp_idx = tmp_shape.rank() - idx; + + OP_REQUIRES((out_idx >= 0) && (tmp_idx >= 0) && + (output_shape.dim(out_idx) == tmp_shape.dim(tmp_idx))); + } +} +void ShapeValidator::visit(const ir::operation::Tile &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(0)}; + const auto multiple_index{node.getInputs().at(1)}; + + OP_REQUIRES(_ctx.at(multiple_index).shape().rank() == 1); + OP_REQUIRES(_ctx.at(multiple_index).shape().dim(0) == _ctx.at(input_index).shape().rank()); + OP_REQUIRES(_ctx.at(input_index).shape().rank() == _ctx.at(output_index).shape().rank()); +} + +void ShapeValidator::visit(const ir::operation::Range &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto start_index{node.getInputs().at(ir::operation::Range::Input::START)}; + const auto limit_index{node.getInputs().at(ir::operation::Range::Input::LIMIT)}; + const auto delta_index{node.getInputs().at(ir::operation::Range::Input::DELTA)}; + + // Check for dimension constraints + if (_ctx.at(output_index).info().isDynamic()) + return; + + OP_REQUIRES(_ctx.at(start_index).shape().rank() == 0); + OP_REQUIRES(_ctx.at(limit_index).shape().rank() == 0); + OP_REQUIRES(_ctx.at(delta_index).shape().rank() == 0); +} + +void ShapeValidator::visit(const ir::operation::MatrixBandPart &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(ir::operation::MatrixBandPart::Input::INPUT)}; + const auto num_lower_index{ + node.getInputs().at(ir::operation::MatrixBandPart::Input::NUM_LOWER_DIAG)}; + const auto num_upper_index{ + node.getInputs().at(ir::operation::MatrixBandPart::Input::NUM_UPPER_DIAG)}; + + // Check for dimension constraints + if (_ctx.at(output_index).info().isDynamic()) + return; + + OP_REQUIRES(_ctx.at(input_index).shape().rank() >= 2); // input must be more than 2 dim matrix + OP_REQUIRES(_ctx.at(num_upper_index).shape().rank() == 0); // num_lower must be scalar + OP_REQUIRES(_ctx.at(num_lower_index).shape().rank() == 0); // num_upper must be scalar +} + +void ShapeValidator::visit(const ir::operation::LogSoftmax &node) +{ + const auto output_index{node.getOutputs().at(0)}; + if (_ctx.at(output_index).info().isDynamic()) + return; + + const auto input_index{node.getInputs().at(0)}; + + OP_REQUIRES(_ctx.at(output_index).shape().rank() == _ctx.at(input_index).shape().rank()); +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/ShapeValidator.h b/runtime/onert/core/src/compiler/ShapeValidator.h new file mode 100644 index 000000000..f40c098d5 --- /dev/null +++ b/runtime/onert/core/src/compiler/ShapeValidator.h @@ -0,0 +1,102 @@ +/* + * 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_SHAPE_VALIDATOR_H__ +#define __ONERT_COMPILER_SHAPE_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 ShapeValidator : public ir::OperationVisitor +{ +public: + ShapeValidator(void) = delete; + ShapeValidator(const ir::Graph &graph); + +public: + void operator()(); + +public: + void visit(const ir::operation::BatchMatMul &node) override; + void visit(const ir::operation::BatchToSpaceND &node) override; + void visit(const ir::operation::BCQFullyConnected &node) override; + void visit(const ir::operation::BCQGather &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::Pool2D &node) override; + void visit(const ir::operation::Reduce &node) override; + void visit(const ir::operation::Transpose &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::ElementwiseActivation &node) override; + void visit(const ir::operation::ElementwiseBinary &node) override; + void visit(const ir::operation::ElementwiseUnary &node) override; + void visit(const ir::operation::EmbeddingLookup &node) override; + void visit(const ir::operation::ExpandDims &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::DepthToSpace &node) override; + void visit(const ir::operation::Pack &node) override; + void visit(const ir::operation::LSTM &node) override; + void visit(const ir::operation::L2Normalization &node) override; + void visit(const ir::operation::Unpack &node) override; + void visit(const ir::operation::Pad &node) override; + void visit(const ir::operation::Select &node) override; + void visit(const ir::operation::StridedSlice &node) override; + void visit(const ir::operation::Split &node) override; + void visit(const ir::operation::Shape &node) override; + void visit(const ir::operation::ResizeBilinear &node) override; + void visit(const ir::operation::Reverse &node) override; + void visit(const ir::operation::If &node) override; + void visit(const ir::operation::While &node) override; + void visit(const ir::operation::SquaredDifference &node) override; + void visit(const ir::operation::Tile &node) override; + void visit(const ir::operation::Range &node) override; + void visit(const ir::operation::MatrixBandPart &node) override; + void visit(const ir::operation::LogSoftmax &node) override; + +private: + void checkUnaryOp(const ir::Operation &node); + +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_SHAPE_VALIDATOR_H__ diff --git a/runtime/onert/core/src/compiler/StaticShapeInferer.cc b/runtime/onert/core/src/compiler/StaticShapeInferer.cc new file mode 100644 index 000000000..d3b083b78 --- /dev/null +++ b/runtime/onert/core/src/compiler/StaticShapeInferer.cc @@ -0,0 +1,1302 @@ +/* + * 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 "compiler/StaticShapeInferer.h" +#include "util/ShapeInference.h" +#include "util/logging.h" + +#include <sstream> + +namespace onert +{ +namespace compiler +{ + +bool StaticShapeInferer::infer(const ir::OpSequence &op_seq) +{ + bool has_dynamic_tensor = false; + + for (const auto &operation_idx : op_seq.operations()) + { + auto &op = _operations.at(operation_idx); + auto opcode = op.opcode(); + + _return_has_dynamic_tensor = false; // this is used as a return value inside operation's visit() + + // IF: need shape inference for then, else + // While: need shape inference for condition, body + if (opcode == ir::OpCode::If || opcode == ir::OpCode::While) + { + op.accept(*this); + } + else + { + _return_has_dynamic_tensor = checkDynamicInput(op); + + if (_return_has_dynamic_tensor) + { + setDynamicOutput(op); + } + else + { + op.accept(*this); + } + } + + has_dynamic_tensor = has_dynamic_tensor || _return_has_dynamic_tensor; + } + + return has_dynamic_tensor; +} + +bool StaticShapeInferer::checkDynamicInput(const ir::Operation &op) +{ + for (auto input_idx : op.getInputs() | ir::Remove::UNDEFINED | ir::Remove::DUPLICATED) + { + if (_operands.at(input_idx).info().isDynamic()) + { + return true; + } + } + + return false; +} + +void StaticShapeInferer::setDynamicOutput(const ir::Operation &op) +{ + for (auto output_idx : op.getOutputs() | ir::Remove::UNDEFINED) + { + _operands.at(output_idx).info().setDynamic(); + } +} + +void StaticShapeInferer::handleBinaryArithmeticOp(const ir::Operation &op, + const ir::OperandIndex lhs_idx, + const ir::OperandIndex rhs_idx) +{ + const auto &lhs = _operands.at(lhs_idx); + const auto &rhs = _operands.at(rhs_idx); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferEltwiseShape(lhs.info().shape(), rhs.info().shape()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::handleSimpleUnaryOp(const ir::Operation &op, + const ir::OperandIndex input_idx) +{ + 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); + + // re-sizing output shape + ir::Shape new_shape = input.info().shape(); + output.info().shape(new_shape); +} + +void StaticShapeInferer::dump() +{ + auto get_shape_str = [](const ir::Shape &shape) { + std::stringstream sstream; + sstream << "shape : {"; + for (int i = 0; i < shape.rank(); i++) + { + if (i == 0) + sstream << shape.dim(i); + else + sstream << " " << shape.dim(i); + } + sstream << "}"; + return sstream.str(); + }; + + for (const auto &pair : _lowered_subgs) + { + const auto index = pair.first; + const auto &lowered_subg = pair.second; + VERBOSE(StaticShapeInferer) << "SubGraph #" << index.value() << std::endl; + lowered_subg->graph().operands().iterate( + [&](const ir::OperandIndex &ind, const ir::Operand &operand) { + VERBOSE(StaticShapeInferer) << "Operand #" << ind.value() << ", " + << (operand.info().isDynamic() ? "Dynamic" : "Static") << ", " + << get_shape_str(operand.info().shape()) << std::endl; + }); + } +} + +void StaticShapeInferer::visit(const ir::operation::ArgMax &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::ArgMax::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto axis_idx{op.getInputs().at(ir::operation::ArgMax::Input::AXIS)}; + const auto &axis = _operands.at(axis_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + if (!axis.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + const auto rank = input.info().shape().rank(); + auto axis_value = axis.asScalar<int32_t>(); + axis_value = axis_value < 0 ? axis_value + rank : axis_value; + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferArgMaxShape(input.info().shape(), axis_value, rank); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::BatchMatMul &op) +{ + const auto lhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::LHS); + const auto rhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::RHS); + const auto output_index = op.getOutputs().at(0); + const auto &lhs = _operands.at(lhs_index); + const auto &rhs = _operands.at(rhs_index); + auto &output = _operands.at(output_index); + auto new_shape = shape_inference::inferBatchMatMulShape(lhs.shape(), rhs.shape(), op.param()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::BCQFullyConnected &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::BCQFullyConnected::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto cluster_idx{ + op.getInputs().at(ir::operation::BCQFullyConnected::Input::WEIGHTS_CLUSTERS)}; + const auto &cluster = _operands.at(cluster_idx); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + auto cluster_buf = reinterpret_cast<const int32_t *>(cluster.data()->base()); + assert(cluster_buf); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferBCQFullyConnectedShape( + input.info().shape(), cluster.info().shape(), cluster_buf); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::BCQGather &op) +{ + const auto indices_idx{op.getInputs().at(ir::operation::BCQGather::Input::INDICES)}; + const auto &indices = _operands.at(indices_idx); + + const auto input_binary_idx{op.getInputs().at(ir::operation::BCQGather::Input::INPUT_BINARY)}; + const auto &input_binary = _operands.at(input_binary_idx); + + const auto cluster_idx{op.getInputs().at(ir::operation::BCQGather::Input::INPUT_CLUSTERS)}; + const auto &cluster = _operands.at(cluster_idx); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + auto cluster_buf = reinterpret_cast<const int32_t *>(cluster.data()->base()); + assert(cluster_buf); + + auto rank = input_binary.shape().rank(); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferBCQGatherShape( + indices.info().shape(), cluster.info().shape(), cluster_buf, rank, op.param()); + + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::BinaryArithmetic &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::BinaryArithmetic::Input::LHS), + op.getInputs().at(ir::operation::BinaryArithmetic::Input::RHS)); +} + +void StaticShapeInferer::visit(const ir::operation::BroadcastTo &op) +{ + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + const auto shape_idx{op.getInputs().at(ir::operation::BroadcastTo::Input::SHAPE)}; + const auto &shape = _operands.at(shape_idx); + + if (!shape.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + // assert(shape.typeInfo().type() == ir::DataType::INT32); + auto shape_buffer = reinterpret_cast<const int32_t *>(shape.data()->base()); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferBroadcastToShape(shape.info().shape(), shape_buffer); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Comparison &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Comparison::Input::INPUT0), + op.getInputs().at(ir::operation::Comparison::Input::INPUT1)); +} + +void StaticShapeInferer::visit(const ir::operation::Concat &op) +{ + const auto input_count = op.getInputs().size(); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + shape_inference::Shapes input_shapes; + for (uint32_t i = 0; i < input_count; i++) + { + const auto input_idx{op.getInputs().at(i)}; + const auto &input = _operands.at(input_idx); + input_shapes.emplace_back(input.shape()); + } + + ir::Shape out_shape = shape_inference::inferConcatShape(input_shapes, op.param()); + + // re-sizing output shape + output.info().shape(out_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Conv2D &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Conv2D::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + const auto ker_idx{op.getInputs().at(ir::operation::Conv2D::Input::KERNEL)}; + const auto &ker = _operands.at(ker_idx); + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // re-sizing output shape + ir::Shape new_shape = + shape_inference::inferConv2DShape(input.info().shape(), ker.info().shape(), op.param()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::ElementwiseActivation &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseActivation::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::ElementwiseBinary &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::ElementwiseBinary::Input::LHS), + op.getInputs().at(ir::operation::ElementwiseBinary::Input::RHS)); +} + +void StaticShapeInferer::visit(const ir::operation::ElementwiseUnary &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::ExpandDims &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::ExpandDims::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + const auto axis_idx{op.getInputs().at(ir::operation::ExpandDims::Input::AXIS)}; + const auto &axis = _operands.at(axis_idx); + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + if (!axis.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + // even when axis is constant, output shape should be recalculated since user might call + // nnfw_set_input_tensorinfo(input, some_new_shape) + auto axis_buf = reinterpret_cast<const int32_t *>(axis.data()->base()); + assert(axis_buf); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferExpandDimsShape(input.info().shape(), axis_buf[0]); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Fill &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Fill::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + if (!input.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + assert(input.typeInfo().type() == ir::DataType::INT32); + + auto input_buf = reinterpret_cast<const int32_t *>(input.data()->base()); + assert(input_buf); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferFillShape(input.info().shape(), input_buf); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::FullyConnected &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::FullyConnected::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto ker_idx{op.getInputs().at(ir::operation::FullyConnected::Input::WEIGHT)}; + const auto &ker = _operands.at(ker_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + // re-sizing output shape + ir::Shape new_shape = + shape_inference::inferFullyConnectedShape(input.info().shape(), ker.info().shape()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::FusedBatchNorm &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::FusedBatchNorm::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::Gather &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Gather::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); + + const auto indices_idx{op.getInputs().at(ir::operation::Gather::Input::INDICES)}; + const auto &indices = _operands.at(indices_idx); + const auto rank = input.info().shape().rank(); + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + + assert(0 <= axis && axis < rank); + + // re-sizing output shape + ir::Shape new_shape = + shape_inference::inferGatherShape(input.info().shape(), indices.info().shape(), axis, rank); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::If &op) +{ + auto &then_graph = _lowered_subgs.at(op.param().then_subg_index)->graph(); + auto &else_graph = _lowered_subgs.at(op.param().else_subg_index)->graph(); + const std::vector<ir::OperandIndex> inputs{op.getInputs().begin() + 1, op.getInputs().end()}; + const auto &outputs = op.getOutputs(); + + // re-sizing input shapes of then subgraph + const auto &then_inputs = then_graph.getInputs(); + assert(inputs.size() == then_inputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) + { + auto &then_input = then_graph.operands().at(then_inputs.at(i)); + if (_operands.at(inputs.at(i)).info().isDynamic()) + { + then_input.info().setDynamic(); + } + else + { + auto new_shape = _operands.at(inputs.at(i)).info().shape(); + then_input.info().shape(new_shape); + } + } + + // re-sizing input shapes of else subgraph + const auto &else_inputs = else_graph.getInputs(); + assert(inputs.size() == else_inputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) + { + auto &else_input = else_graph.operands().at(else_inputs.at(i)); + if (_operands.at(inputs.at(i)).info().isDynamic()) + { + else_input.info().setDynamic(); + } + else + { + const auto &new_shape = _operands.at(inputs.at(i)).info().shape(); + else_input.info().shape(new_shape); + } + } + + // re-sizing operands of then subgraph + StaticShapeInferer then_inferer(op.param().then_subg_index, _lowered_subgs); + _lowered_subgs.at(op.param().then_subg_index) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + bool has_dynamic_tensor = then_inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + + // re-sizing operands of else subgraph + StaticShapeInferer else_inferer(op.param().else_subg_index, _lowered_subgs); + _lowered_subgs.at(op.param().else_subg_index) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + bool has_dynamic_tensor = else_inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + + // re-sizing output shapes + const auto &then_outputs = _lowered_subgs.at(op.param().then_subg_index)->graph().getOutputs(); + const auto &else_outputs = _lowered_subgs.at(op.param().else_subg_index)->graph().getOutputs(); + assert(outputs.size() == then_outputs.size()); + assert(outputs.size() == else_outputs.size()); + for (size_t i = 0; i < outputs.size(); ++i) + { + const auto &then_output = then_graph.operands().at(then_outputs.at(i)); + const auto &else_output = else_graph.operands().at(else_outputs.at(i)); + auto &output = _operands.at(outputs.at(i)); + if (!then_output.info().isDynamic() && !else_output.info().isDynamic() && + then_output.shape() == else_output.shape()) + { + output.info().shape(then_output.shape()); + } + else + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + } + } +} + +void StaticShapeInferer::visit(const ir::operation::L2Normalization &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::L2Normalization::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::LSTM &op) +{ + const auto output_index{op.getOutputs().at(ir::operation::LSTM::Output::OUTPUT)}; + auto &output = _operands.at(output_index); + + const auto output_state_out_index{ + op.getOutputs().at(ir::operation::LSTM::Output::OUTPUT_STATE_OUT)}; + + const auto cell_state_out_index{op.getOutputs().at(ir::operation::LSTM::Output::CELL_STATE_OUT)}; + + const auto scratch_buffer_index{op.getOutputs().at(ir::operation::LSTM::Output::SCRATCH_BUFFER)}; + + if (output.info().isDynamic() || (_operands.exist(output_state_out_index) && + _operands.at(output_state_out_index).info().isDynamic()) || + (_operands.exist(cell_state_out_index) && + _operands.at(cell_state_out_index).info().isDynamic()) || + (_operands.exist(scratch_buffer_index) && + _operands.at(scratch_buffer_index).info().isDynamic())) + return; + + const auto input_index{op.getInputs().at(ir::operation::LSTM::Input::INPUT)}; + const auto &input = _operands.at(input_index); + + const auto input_to_output_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_OUTPUT_WEIGHTS)}; + const auto &input_to_output_weights = _operands.at(input_to_output_weights_index); + + const auto recurrent_to_output_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_OUTPUT_WEIGHTS)}; + const auto &recurrent_to_output_weights = _operands.at(recurrent_to_output_weights_index); + + // re-sizing outputs + const int n_batch = (input.shape().rank() == 3 && op.param().time_major) ? input.shape().dim(1) + : input.shape().dim(0); + const int n_cell = input_to_output_weights.shape().dim(0); + const int n_output = recurrent_to_output_weights.shape().dim(1); + if (input.shape().rank() == 3) + { + if (op.param().time_major) + output.info().shape(ir::Shape{input.shape().dim(0), n_batch, n_output}); + else + output.info().shape(ir::Shape{n_batch, input.shape().dim(1), n_output}); + } + else + { + assert(input.shape().rank() == 2); + output.info().shape(ir::Shape{n_batch, n_output}); + } + + if (_operands.exist(output_state_out_index)) + { + auto &output_state_out = _operands.at(output_state_out_index); + output_state_out.info().shape(ir::Shape{n_batch, n_output}); + } + + if (_operands.exist(cell_state_out_index)) + { + auto &cell_state_out = _operands.at(cell_state_out_index); + cell_state_out.info().shape(ir::Shape{n_batch, n_cell}); + } + + if (_operands.exist(scratch_buffer_index)) + { + auto &scratch_buffer = _operands.at(scratch_buffer_index); + + const auto input_to_input_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_INPUT_WEIGHTS)}; + const auto recurrent_to_input_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_INPUT_WEIGHTS)}; + + bool has_input_to_input_weights = + _operands.at(input_to_input_weights_index).shape().dim(0) != 0 && + _operands.at(input_to_input_weights_index).shape().dim(1) != 0; + bool has_recurrent_to_input_weights = + _operands.at(recurrent_to_input_weights_index).shape().dim(0) != 0 && + _operands.at(recurrent_to_input_weights_index).shape().dim(1) != 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; + if (has_cifg_param) + { + scratch_buffer.info().shape(ir::Shape{n_batch, n_cell * 4}); + } + else + { + scratch_buffer.info().shape(ir::Shape{n_batch, n_cell * 3}); + } + } +} + +void StaticShapeInferer::visit(const ir::operation::MatrixBandPart &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::MatrixBandPart::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::OneHot &op) +{ + const auto indice_idx{op.getInputs().at(ir::operation::OneHot::Input::INDICES)}; + const auto &indice = _operands.at(indice_idx); + const auto depth_idx{op.getInputs().at(ir::operation::OneHot::Input::DEPTH)}; + const auto &depth = _operands.at(depth_idx); + + const auto axis = op.param().axis; + + auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + if (!depth.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + const auto *depth_buf = reinterpret_cast<const int32_t *>(depth.data()->base()); + assert(depth_buf); + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferOnehotShape(indice.info().shape(), *depth_buf, axis); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Pack &op) +{ + const auto input_idx{op.getInputs().at(0)}; + 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); + + const auto rank = input.shape().rank() + 1; + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + const auto num = op.param().num; + + assert(0 <= axis && axis < rank); + + // re-sizing output shape + ir::Shape new_shape = shape_inference::inferPackShape(input.info().shape(), axis, rank, num); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Pad &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Pad::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto pad_idx{op.getInputs().at(ir::operation::Pad::Input::PAD)}; + const auto &pad = _operands.at(pad_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // if pad is not constant, output also becomes dynamic + if (!pad.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + // re-sizing output shape + const auto new_shape = shape_inference::inferPadShape( + input.shape(), reinterpret_cast<const int32_t *>(pad.data()->base()), + pad.shape().num_elements()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Permute &op) +{ + const auto input_idx{op.getInputs().at(0)}; + const auto &input = _operands.at(input_idx); + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // re-sizing output shape + // Permute is a special operation that layouts of input/output may be different on backend + // However, it is not applied here, so input/output have the same layout of frontend. Because + // "ExecutorFactory" would convert shape of input/output accoding to the layouts when registering + // operand info to "TensorBuilder" after calling "StaticShapeInferer" + const auto new_shape = input.info().shape(); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Pow &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Pow::Input::LHS), + op.getInputs().at(ir::operation::Pow::Input::RHS)); +} + +void StaticShapeInferer::visit(const ir::operation::Range &op) +{ + const auto start_idx{op.getInputs().at(ir::operation::Range::Input::START)}; + const auto limit_idx{op.getInputs().at(ir::operation::Range::Input::LIMIT)}; + const auto delta_idx{op.getInputs().at(ir::operation::Range::Input::DELTA)}; + const auto &start_op = _operands.at(start_idx); + const auto &limit_op = _operands.at(limit_idx); + const auto &delta_op = _operands.at(delta_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + ir::Shape new_shape; + if (start_op.isConstant() && limit_op.isConstant() && delta_op.isConstant()) + { + assert(start_op.typeInfo().type() == limit_op.typeInfo().type() && + start_op.typeInfo().type() == delta_op.typeInfo().type()); + if (output.typeInfo().type() == ir::DataType::FLOAT32) + { + new_shape = shape_inference::inferRangeShape<float>( + start_op.asScalar<float>(), limit_op.asScalar<float>(), delta_op.asScalar<float>()); + } + else if (output.typeInfo().type() == ir::DataType::INT32) + { + new_shape = shape_inference::inferRangeShape<int32_t>( + start_op.asScalar<int32_t>(), limit_op.asScalar<int32_t>(), delta_op.asScalar<int32_t>()); + } + assert(output.shape() == new_shape); + } + else + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + } +} + +void StaticShapeInferer::visit(const ir::operation::Reduce &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Reduce::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto axes_idx{op.getInputs().at(ir::operation::Reduce::Input::AXES)}; + const auto &axes = _operands.at(axes_idx); + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + std::vector<int32_t> axes_vec; + for (size_t i = 0; i < axes.shape().num_elements(); ++i) + { + switch (axes.typeInfo().type()) + { + case ir::DataType::INT32: + { + axes_vec.emplace_back(reinterpret_cast<const int32_t *>(axes.data()->base())[i]); + break; + } + case ir::DataType::INT64: + { + axes_vec.emplace_back(reinterpret_cast<const int64_t *>(axes.data()->base())[i]); + break; + } + default: + throw std::runtime_error("StaticShapeInferer " + op.name() + ": Not supported data type"); + break; + } + } + const auto keep_dims = op.param().keep_dims; + + // re-sizing output shape + ir::Shape new_shape = + shape_inference::inferReduceShape(input.info().shape(), axes_vec, keep_dims); + output.info().shape(new_shape); +} + +void StaticShapeInferer::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); + + // New shape is given by second input tensor + if (op.getInputs().size() == 2) + { + // 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()) + { + const auto *shape_buf = reinterpret_cast<const int32_t *>(shape.data()->base()); + assert(shape_buf); + + ir::Shape new_shape = shape_inference::inferReshapeShape( + shape_buf, shape.shape().num_elements(), input.shape().num_elements()); + + // if shape is from Const, TFLC put the shape of output into tensor + if (new_shape != output.shape()) + { + // change on output shape + output.info().shape(new_shape); + } + } + else + { + // if shape is NOT Const, set output shape to be dynamic_ + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + } + } + // New shape is given by option + else if (op.param().new_shape.size() != 0) + { + // Let's check the new_shape option + auto shape = op.param().new_shape; + ir::Shape new_shape = shape_inference::inferReshapeShape(shape.data(), shape.size(), + input.shape().num_elements()); + + if (new_shape != output.shape()) + { + // change on output shape + output.info().shape(new_shape); + } + } + else + { + throw std::runtime_error("Reshape: new shape is missing"); + } +} + +void StaticShapeInferer::visit(const ir::operation::ResizeBilinear &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::ResizeBilinear::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); + + int32_t height_out, width_out; + if (op.getInputs().size() == 2) + { + auto &size = _operands.at(op.getInputs().at(ir::operation::ResizeBilinear::Input::SIZE)); + if (!size.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + const auto size_v = size.asVector<std::int32_t>(); + height_out = size_v[0]; + width_out = size_v[1]; + } + else + { + height_out = op.param().height_out; + width_out = op.param().width_out; + } + + // Shape inferencing logic based on Params + ir::Shape new_shape = + shape_inference::inferResizeBilinearShape(input.shape(), height_out, width_out); + + // if size_op is from Const, TFLC put the shape of output into tensor + if (new_shape != output.shape()) + { + // change on output shape + output.info().shape(new_shape); + } +} + +void StaticShapeInferer::visit(const ir::operation::Reverse &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Reverse::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::Select &op) +{ + const auto input_cond_idx{op.getInputs().at(ir::operation::Select::Input::CONDITION)}; + const auto &input_cond = _operands.at(input_cond_idx); + + const auto input_true_idx{op.getInputs().at(ir::operation::Select::Input::INPUT_TRUE)}; + const auto &input_true = _operands.at(input_true_idx); + + const auto input_false_idx{op.getInputs().at(ir::operation::Select::Input::INPUT_FALSE)}; + const auto &input_false = _operands.at(input_false_idx); + + auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // Select output shpae + ir::Shape new_shape = shape_inference::inferSelectShape( + input_cond.info().shape(), input_true.info().shape(), input_false.info().shape()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Shape &op) +{ + const auto input_idx{op.getInputs().at(0)}; + 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); + + // re-sizing output shape + ir::Shape output_shape; + output_shape.append(input.info().shape().rank()); + + output.info().shape(output_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Slice &op) +{ + const auto input_index{op.getInputs().at(ir::operation::Slice::Input::INPUT)}; + const auto &input = _operands.at(input_index); + const auto begins_index{op.getInputs().at(ir::operation::Slice::Input::BEGINS)}; + const auto &begins = _operands.at(begins_index); + const auto sizes_index{op.getInputs().at(ir::operation::Slice::Input::SIZES)}; + const auto &sizes = _operands.at(sizes_index); + const auto output_index = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_index); + + // Whether input is constant or not does not affect whether output is dynamic or not + if (!(begins.isConstant() && sizes.isConstant())) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + auto begins_buf = reinterpret_cast<const int32_t *>(begins.data()->base()); + auto sizes_buf = reinterpret_cast<const int32_t *>(sizes.data()->base()); + + ir::Shape new_shape = + shape_inference::inferSliceShape(input.info().shape(), begins_buf, sizes_buf); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Softmax &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Softmax::Input::INPUT)); +} + +void StaticShapeInferer::visit(const ir::operation::SpaceToBatchND &op) +{ + const auto output_index = op.getOutputs().at(0); + const auto input_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::INPUT)}; + const auto block_shape_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::BLOCK_SIZE)}; + const auto padding_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::PADDINGS)}; + + ir::Operand &output = _operands.at(output_index); + const auto &input = _operands.at(input_idx); + const auto &block_shape = _operands.at(block_shape_idx); + const auto &padding = _operands.at(padding_idx); + + // Whether input is constant or not does not affect whether output is dynamic or not + if (!(block_shape.isConstant() && padding.isConstant())) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + auto input_shape = input.info().shape(); + auto block_shape_shape = block_shape.info().shape(); + auto padding_shape = padding.info().shape(); + + auto block_shape_data = reinterpret_cast<const int32_t *>(block_shape.data()->base()); + auto padding_data = reinterpret_cast<const int32_t *>(padding.data()->base()); + + ir::Shape new_shape = shape_inference::inferSpaceToBatchNDShape( + input_shape, block_shape_shape, padding_shape, block_shape_data, padding_data); + + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Split &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Split::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto axis_idx{op.getInputs().at(ir::operation::Split::Input::AXIS)}; + const auto &axis = _operands.at(axis_idx); + + auto outputs = op.getOutputs(); + if (!axis.isConstant()) + { + for (auto output_idx : outputs) + { + ir::Operand &output = _operands.at(output_idx); + output.info().setDynamic(); + } + _return_has_dynamic_tensor = true; + return; + } + + const auto num_splits = op.param().num_splits; + + const auto rank = input.info().shape().rank(); + auto axis_value = axis.asScalar<int32_t>(); + axis_value = axis_value < 0 ? axis_value + rank : axis_value; + + assert(0 <= axis_value && axis_value < rank); + + ir::Shape new_shape = + shape_inference::inferSplitShape(input.info().shape(), axis_value, num_splits); + for (auto output_idx : outputs) + { + ir::Operand &output = _operands.at(output_idx); + output.info().shape(new_shape); + } +} + +void StaticShapeInferer::visit(const ir::operation::SquaredDifference &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::SquaredDifference::Input::LHS), + op.getInputs().at(ir::operation::SquaredDifference::Input::RHS)); +} + +void StaticShapeInferer::visit(const ir::operation::Squeeze &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Squeeze::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + // Squeeze output shpae + ir::Shape new_shape = shape_inference::inferSqueezeShape(input.info().shape(), op.param()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::StridedSlice &op) +{ + const auto input_index{op.getInputs().at(ir::operation::StridedSlice::Input::INPUT)}; + const auto &input = _operands.at(input_index); + const auto starts_index{op.getInputs().at(ir::operation::StridedSlice::Input::STARTS)}; + const auto &starts = _operands.at(starts_index); + const auto ends_index{op.getInputs().at(ir::operation::StridedSlice::Input::ENDS)}; + const auto &ends = _operands.at(ends_index); + const auto strides_index{op.getInputs().at(ir::operation::StridedSlice::Input::STRIDES)}; + const auto &strides = _operands.at(strides_index); + const auto output_index = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_index); + + if (!(starts.isConstant() && ends.isConstant() && strides.isConstant())) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + const auto begin_mask = op.param().begin_mask; + const auto end_mask = op.param().end_mask; + const auto shrink_axis_mask = op.param().shrink_axis_mask; + const auto rank = input.info().shape().rank(); + + auto starts_buf = reinterpret_cast<const uint32_t *>(starts.data()->base()); + auto ends_buf = reinterpret_cast<const uint32_t *>(ends.data()->base()); + auto strides_buf = reinterpret_cast<const uint32_t *>(strides.data()->base()); + + auto op_params = shape_inference::buildStridedSliceParams( + starts_buf, ends_buf, strides_buf, begin_mask, end_mask, shrink_axis_mask, rank); + + ir::Shape new_shape = + shape_inference::inferStridedSliceShape(input.info().shape(), op_params, rank); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Tile &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Tile::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto multiplier_idx{op.getInputs().at(ir::operation::Tile::Input::MULTIPLES)}; + const auto &multiplier = _operands.at(multiplier_idx); + + const auto output_idx = op.getOutputs().at(0); + ir::Operand &output = _operands.at(output_idx); + + if (!multiplier.isConstant()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + auto multiplier_buffer = reinterpret_cast<const int32_t *>(multiplier.data()->base()); + assert(multiplier_buffer); + + // re-sizing output shape + auto new_shape = shape_inference::inferTileShape(input.info().shape(), multiplier_buffer, + multiplier.shape().num_elements()); + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Transpose &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Transpose::Input::INPUT)}; + const auto &input = _operands.at(input_idx); + + const auto perm_idx{op.getInputs().at(ir::operation::Transpose::Input::PERMUTATION)}; + const auto &perm = _operands.at(perm_idx); + + // perm.shape() != ir::Shape{0} means that perm is (n-1...0) + // TODO This condition changes to perm.num_elements() == 0 + const auto is_regular_transpose = perm.shape() == ir::Shape{0}; + + // get mutable output operand + const auto output_idx = op.getOutputs().at(0); + auto &output = _operands.at(output_idx); + if (!perm.isConstant() && !is_regular_transpose) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + return; + } + + ir::Shape new_shape; + if (is_regular_transpose) + { + // Call by (n-1...0) + new_shape = shape_inference::inferTransposeShape(input.info().shape(), nullptr, 0); + } + else + { + // Check rank + if (input.info().shape().rank() != static_cast<int>(perm.info().shape().num_elements())) + { + throw std::runtime_error("StaticShapeInferer failed, bad rank size: " + + std::to_string(perm.info().shape().num_elements())); + } + + // set output shape, based on input and params + const auto perm_buf = reinterpret_cast<const int32_t *>(perm.data()->base()); + new_shape = shape_inference::inferTransposeShape(input.info().shape(), perm_buf, + perm.shape().num_elements()); + } + output.info().shape(new_shape); +} + +void StaticShapeInferer::visit(const ir::operation::Unpack &op) +{ + const auto input_idx{op.getInputs().at(0)}; + const auto &input = _operands.at(input_idx); + const auto num = op.param().num; + const auto rank = input.shape().rank(); + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + + assert(axis < rank); + if (axis < 0) + { + for (int out_tensor_idx = 0; out_tensor_idx < num; out_tensor_idx++) + { + const auto output_idx = op.getOutputs().at(out_tensor_idx); + ir::Operand &output = _operands.at(output_idx); + output.info().setDynamic(); + } + _return_has_dynamic_tensor = true; + return; + } + + ir::Shape new_shape = shape_inference::inferUnpackShape(input.info().shape(), axis, rank); + + // re-sizing output shape + for (int out_tensor_idx = 0; out_tensor_idx < num; out_tensor_idx++) + { + const auto output_idx = op.getOutputs().at(out_tensor_idx); + ir::Operand &output = _operands.at(output_idx); + output.info().shape(new_shape); + } +} + +void StaticShapeInferer::visit(const ir::operation::While &op) +{ + auto &cond_graph = _lowered_subgs.at(op.param().cond_subg_index)->graph(); + auto &body_graph = _lowered_subgs.at(op.param().body_subg_index)->graph(); + const auto inputs = op.getInputs(); + const auto &outputs = op.getOutputs(); + + // re-sizing input shapes of then subgraph + const auto &cond_inputs = cond_graph.getInputs(); + assert(inputs.size() == cond_inputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) + { + const auto &input = _operands.at(inputs.at(i)); + auto &cond_input = cond_graph.operands().at(cond_inputs.at(i)); + if (input.info().isDynamic()) + { + cond_input.info().setDynamic(); + } + else + { + auto new_shape = input.info().shape(); + cond_input.info().shape(new_shape); + } + } + + // re-sizing input shapes of body subgraph + const auto &body_inputs = body_graph.getInputs(); + assert(cond_inputs.size() == body_inputs.size()); + for (size_t i = 0; i < cond_inputs.size(); ++i) + { + const auto &cond_input = cond_graph.operands().at(cond_inputs.at(i)); + auto &body_input = body_graph.operands().at(body_inputs.at(i)); + if (cond_input.info().isDynamic()) + { + body_input.info().setDynamic(); + } + else + { + const auto &new_shape = cond_input.info().shape(); + body_input.info().shape(new_shape); + } + } + + // re-sizing operands of body subgraph + StaticShapeInferer body_inferer(op.param().body_subg_index, _lowered_subgs); + _lowered_subgs.at(op.param().body_subg_index) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + bool has_dynamic_tensor = body_inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + + // Check whether while operation's shapes are predictable + // If any of shape of body outputs and cond inputs are different, non-constant operands would be + // set to dynamic + bool check_unpredictable_dynamic = false; + const auto &body_outputs = body_graph.getOutputs(); + assert(body_outputs.size() == cond_inputs.size()); + for (size_t i = 0; i < body_outputs.size(); ++i) + { + const auto &body_output = body_graph.operands().at(body_outputs.at(i)); + auto &cond_input = cond_graph.operands().at(cond_inputs.at(i)); + if ((cond_input.info().isDynamic() != body_output.info().isDynamic()) || + (cond_input.shape() != body_output.shape())) + { + check_unpredictable_dynamic = true; + break; + } + } + + if (check_unpredictable_dynamic) + { + // Set inputs of body subgraph + for (const auto &input_index : body_inputs) + { + auto &input = body_graph.operands().at(input_index); + if (!input.isConstant()) + { + input.info().setDynamic(); + } + } + + // Set inputs of cond subgraph + for (const auto &input_index : cond_inputs) + { + auto &input = cond_graph.operands().at(input_index); + if (!input.isConstant()) + { + input.info().setDynamic(); + } + } + + // Set non-constant operands of body subgraph to dynamic + StaticShapeInferer body_inferer(op.param().body_subg_index, _lowered_subgs); + _lowered_subgs.at(op.param().body_subg_index) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + bool has_dynamic_tensor = body_inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + } + + // re-sizing operands of cond subgraph + // If check_unpredictable_dynamic is true, non-constant operands of cond subgraph would be set to + // dynamic + StaticShapeInferer cond_inferer(op.param().cond_subg_index, _lowered_subgs); + _lowered_subgs.at(op.param().cond_subg_index) + ->iterateTopolOpSeqs([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + bool has_dynamic_tensor = cond_inferer.infer(op_seq); + op_seq.has_dynamic_tensor(has_dynamic_tensor); + }); + + // re-sizing outputs of while operation + // If check_unpredictable_dynamic is true, outputs of while operation would be set to dynamic + assert(cond_inputs.size() == outputs.size()); + for (size_t i = 0; i < cond_inputs.size(); ++i) + { + const auto &cond_input = cond_graph.operands().at(cond_inputs.at(i)); + auto &output = _operands.at(outputs.at(i)); + if (cond_input.info().isDynamic()) + { + output.info().setDynamic(); + _return_has_dynamic_tensor = true; + } + else + { + const auto new_shape = cond_input.info().shape(); + output.info().shape(new_shape); + } + } +} + +} // namespace compiler + +} // namespace onert diff --git a/runtime/onert/core/src/compiler/TensorBuilders.h b/runtime/onert/core/src/compiler/TensorBuilders.h new file mode 100644 index 000000000..3b0360b4b --- /dev/null +++ b/runtime/onert/core/src/compiler/TensorBuilders.h @@ -0,0 +1,78 @@ +/* + * 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_TENSOR_BUILDERS_H__ +#define __ONERT_COMPILER_TENSOR_BUILDERS_H__ + +#include <unordered_set> +#include <memory> +#include "backend/BackendContext.h" +#include "backend/Backend.h" +#include "backend/controlflow/Config.h" +#include "backend/controlflow/TensorBuilder.h" +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ + +class TensorBuilders +{ +public: + TensorBuilders() = default; + + TensorBuilders(const onert::backend::BackendContexts &backend_contexts, bool include_controlflow) + { + for (const auto &e : backend_contexts) + { + if (e.first->config()->id() == backend::controlflow::Config::ID) + { + _cf_tensor_builder = std::dynamic_pointer_cast<backend::controlflow::TensorBuilder>( + e.second->tensor_builder); + if (include_controlflow) + _tensor_builders.insert(e.second->tensor_builder); + } + else + { + _tensor_builders.insert(e.second->tensor_builder); + } + } + } + + std::unordered_set<std::shared_ptr<onert::backend::ITensorBuilder>>::const_iterator begin() const + { + return _tensor_builders.cbegin(); + } + std::unordered_set<std::shared_ptr<onert::backend::ITensorBuilder>>::const_iterator end() const + { + return _tensor_builders.cend(); + } + + std::shared_ptr<backend::controlflow::TensorBuilder> getControlflowTensorBuilder() const + { + return _cf_tensor_builder; + } + +private: + std::unordered_set<std::shared_ptr<backend::ITensorBuilder>> _tensor_builders; + std::shared_ptr<backend::controlflow::TensorBuilder> _cf_tensor_builder; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_TENSOR_BUILDERS_H__ diff --git a/runtime/onert/core/src/compiler/TensorRegistries.h b/runtime/onert/core/src/compiler/TensorRegistries.h new file mode 100644 index 000000000..e42225cbf --- /dev/null +++ b/runtime/onert/core/src/compiler/TensorRegistries.h @@ -0,0 +1,91 @@ +/* + * 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_TENSOR_REGISTRIES_H__ +#define __ONERT_COMPILER_TENSOR_REGISTRIES_H__ + +#include <unordered_set> +#include <memory> +#include "backend/BackendContext.h" +#include "backend/Backend.h" +#include "backend/controlflow/Config.h" +#include "backend/controlflow/TensorBuilder.h" +#include "backend/controlflow/TensorRegistry.h" + +namespace onert +{ +namespace compiler +{ + +class TensorRegistries +{ +public: + TensorRegistries() = default; + + TensorRegistries(const onert::backend::BackendContexts &backend_contexts, + bool include_controlflow) + { + for (const auto &e : backend_contexts) + { + auto tensor_reg = e.second->tensor_registry; + if (e.first->config()->id() == backend::controlflow::Config::ID) + { + _cf_tensor_reg = + std::dynamic_pointer_cast<backend::controlflow::TensorRegistry>(tensor_reg); + if (include_controlflow) + _tensor_regs.insert(tensor_reg); + } + else + { + _tensor_regs.insert(tensor_reg); + } + } + } + + std::unordered_set<std::shared_ptr<onert::backend::ITensorRegistry>>::const_iterator begin() const + { + return _tensor_regs.cbegin(); + } + std::unordered_set<std::shared_ptr<onert::backend::ITensorRegistry>>::const_iterator end() const + { + return _tensor_regs.cend(); + } + + std::shared_ptr<backend::controlflow::TensorRegistry> getControlflowTensorRegistry() const + { + return _cf_tensor_reg; + } + + backend::ITensor *getITensor(ir::OperandIndex ind) const + { + for (auto &tensor_reg : _tensor_regs) + { + auto tensor = tensor_reg->getITensor(ind); + if (tensor) + return tensor; + } + return nullptr; + } + +private: + std::unordered_set<std::shared_ptr<backend::ITensorRegistry>> _tensor_regs; + std::shared_ptr<backend::controlflow::TensorRegistry> _cf_tensor_reg; +}; + +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_TENSOR_REGISTRIES_H__ diff --git a/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.cc b/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.cc new file mode 100644 index 000000000..ef6240894 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.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 "ConstantInsertionPass.h" + +#include "backend/Backend.h" +#include <ir/Graph.h> +#include <util/Utils.h> + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +void ConstantInsertionPass::callback(const ir::OperationIndex &node_index, ir::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 = ir::operand::PermuteFactor{backend, layout}; + + for (const auto input : node.getInputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + auto &object = _graph.operands().at(input); + + if (object.isConstant()) + { + const auto key = ReplaceKey{input, factor}; + if (_replace_operands_map.count(key) == 0) + { + ir::Operand new_object(object); + new_object.unsetDef(); + // TODO Remove const_case + const_cast<ir::OperationIndexSet &>(new_object.getUses()).clear(); + const auto new_index = _graph.operands().emplace(new_object); + _replace_operands_map[key] = new_index; + } + + const auto replaced_input = _replace_operands_map[key]; + // Update op_seq + if (_lowered_graph.op_seqs().at(op_sequence_index).getInputs().contains(input)) + { + // All inputs of op_seq have the same PermuteFactor because those inputs are inputs of first + // operation + _lowered_graph.op_seqs().at(op_sequence_index).replaceInputs(input, replaced_input); + } + + // Update the same inputs of a node at once because inputs of an operation have the same + // PermuteFactor + node.replaceInputs(input, replaced_input); + + // Update operand + auto &replaced_object = _graph.operands().at(replaced_input); + replaced_object.insertUse(node_index); + + // Remove this node from uses of origin operand + // Constant operand has no def. + assert(!object.getDef().valid()); + object.removeUse(node_index); + + // Remove origin operand + if (object.getUses().size() == 0) + _graph.removeOperand(input); + } + } + + // Now this runtime does not support the node making output as constant + for (const auto &output : node.getOutputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + UNUSED_RELEASE(output); + assert(!_graph.operands().at(output).isConstant()); + } +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.h b/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.h new file mode 100644 index 000000000..052883c92 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantInsertionPass.h @@ -0,0 +1,76 @@ +/* + * 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_PASS_CONSTANT_INSERTION_PASS_H__ +#define __ONERT_COMPILER_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 compiler +{ +namespace pass +{ + +class ConstantInsertionPass : public LoweredOperationPass +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "ConstantInsertionPass"; } + +public: + void callback(const ir::OperationIndex &index, ir::Operation &node) final; + +private: + struct ReplaceKey + { + ir::OperandIndex index; + ir::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<ir::OperandIndex>()(key.index) ^ + (hash<ir::operand::PermuteFactor>()(key.factor) << 1); + } + }; + + std::unordered_map<ReplaceKey, ir::OperandIndex, KeyHasher> _replace_operands_map; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_CONSTANT_INSERTION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.cc b/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.cc new file mode 100644 index 000000000..1c1dbe0ee --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.cc @@ -0,0 +1,56 @@ +/* + * 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 "ConstantLoweringPass.h" + +#include "backend/Backend.h" +#include <ir/Graph.h> +#include <ir/operand/PermuteFactor.h> +#include <util/Utils.h> + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +void ConstantLoweringPass::callback(const ir::OperationIndex &node_index, ir::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 = ir::operand::PermuteFactor{backend, layout}; + + // Now this runtime does not support the node making output of operation as constant + for (const auto input : node.getInputs() | ir::Remove::DUPLICATED | ir::Remove::UNDEFINED) + { + auto &object = _graph.operands().at(input); + if (object.isConstant()) + { + // All constant operand are already assinged at each backend by ContantInsertionPass. So a + // constant has `def` and `use` as the same PermuteFactor + _lowered_graph.setLowerInfo(input, std::make_unique<ir::operand::LowerInfo>()); + _lowered_graph.getLowerInfo(input)->addDefPermuteFactor(factor); + _lowered_graph.getLowerInfo(input)->addUsePermuteFactor(factor); + } + } +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.h b/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.h new file mode 100644 index 000000000..e17d776d1 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantLoweringPass.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_COMPILER_PASS_CONSTANT_LOWERING_PASS_H__ +#define __ONERT_COMPILER_PASS_CONSTANT_LOWERING_PASS_H__ + +#include <ir/Index.h> +#include "LoweredOperationPass.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class ConstantLoweringPass : public LoweredOperationPass +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "ConstantLoweringPass"; } + +public: + void callback(const ir::OperationIndex &index, ir::Operation &node) final; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_CONSTANT_LOWERING_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/ConstantOutputPass.cc b/runtime/onert/core/src/compiler/pass/ConstantOutputPass.cc new file mode 100644 index 000000000..c176f6ffb --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantOutputPass.cc @@ -0,0 +1,68 @@ +/* + * 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 "ConstantOutputPass.h" + +#include "ir/Graph.h" +#include "ir/operation/Permute.h" +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +void ConstantOutputPass::callback(const ir::OperandIndex &ind, ir::Operand &obj) +{ + if (!_graph.getOutputs().contains(ind) || !obj.isConstant()) + return; + + auto permute_input_ind = _graph.addOperand(obj.shape(), obj.typeInfo()); + auto &permute_input_obj = _graph.operands().at(permute_input_ind); + + // Move the const data + permute_input_obj.data(obj.shareData()); + obj.releaseData(); + obj.info().setAsNonConst(); + + using ir::operation::Permute; + auto permute_obj = std::make_unique<Permute>(permute_input_ind, ind, Permute::Type::COPY); + auto permute_ind = _graph.operations().push(std::move(permute_obj)); + + permute_input_obj.insertUse(permute_ind); + obj.setDef(permute_ind); + + // Make the operations that uses this operand to use the generated operand + auto orig_uses = obj.getUses(); + for (auto use : orig_uses) + { + permute_input_obj.insertUse(use); + obj.removeUse(use); + _graph.operations().at(use).replaceInputs(ind, permute_input_ind); + } + + VERBOSE(ConstantOutputPass) << "Permute Op inserted for a constant ouput, node index : " + << permute_ind << std::endl; + VERBOSE(ConstantOutputPass) << " - Input (inserted) Operand : " << permute_input_ind + << std::endl; + VERBOSE(ConstantOutputPass) << " - Output(original) Operand : " << ind << std::endl; +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/ConstantOutputPass.h b/runtime/onert/core/src/compiler/pass/ConstantOutputPass.h new file mode 100644 index 000000000..193dd3a68 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/ConstantOutputPass.h @@ -0,0 +1,63 @@ +/* + * 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_PASS_CONSTANT_OUTPUT_PASS_H__ +#define __ONERT_COMPILER_PASS_CONSTANT_OUTPUT_PASS_H__ + +#include "OperandPass.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +/** + * @brief Pass to specially handle constant model outputs + * + * As an output buffer is given right before an execution but constant initialization is done at + * prepare phase, the current runtime structure cannot handle when an output is constant. + * To resolve this problem, this pass inserts a Permute layer with a const input and make the model + * output tensor to be its output. + * + * e.g.) + * + * ((Const Output)) + * + * becomes + * + * (Const) -> [Permute] -> ((Output)) + * + * Note that this is a mandatory pass for Graph. + */ +class ConstantOutputPass : public OperandPass +{ +public: + using OperandPass::OperandPass; + +public: + std::string id() final { return "ConstantOutputPass"; } + +public: + void callback(const ir::OperandIndex &i, ir::Operand &o) final; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_CONSTANT_INSERTION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/LoweredOperandPass.h b/runtime/onert/core/src/compiler/pass/LoweredOperandPass.h new file mode 100644 index 000000000..0c5f7d745 --- /dev/null +++ b/runtime/onert/core/src/compiler/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 "compiler/LoweredGraph.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class LoweredOperandPass : public OperandPass +{ +public: + LoweredOperandPass(compiler::LoweredGraph &lowered_graph) + : OperandPass{lowered_graph.graph()}, _lowered_graph{lowered_graph} + { + // DO NOTHING + } + + virtual ~LoweredOperandPass() = default; + + std::string id() override = 0; + void callback(const ir::OperandIndex &i, ir::Operand &o) override = 0; + +protected: + compiler::LoweredGraph &_lowered_graph; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_IR_PASS_LOWERED_OPERAND_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/LoweredOperationPass.h b/runtime/onert/core/src/compiler/pass/LoweredOperationPass.h new file mode 100644 index 000000000..5c8569be2 --- /dev/null +++ b/runtime/onert/core/src/compiler/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 "compiler/LoweredGraph.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class LoweredOperationPass : public OperationPass +{ +public: + LoweredOperationPass(LoweredGraph &lowered_graph) + : OperationPass{lowered_graph.graph()}, _lowered_graph{lowered_graph} + { + // DO NOTHING + } + + virtual ~LoweredOperationPass() = default; + + std::string id() override = 0; + void callback(const ir::OperationIndex &i, ir::Operation &o) override = 0; + +protected: + LoweredGraph &_lowered_graph; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_IR_PASS_LOWERED_OPERATION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/OddOutputPass.cc b/runtime/onert/core/src/compiler/pass/OddOutputPass.cc new file mode 100644 index 000000000..f50fae0d3 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/OddOutputPass.cc @@ -0,0 +1,90 @@ +/* + * 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 "OddOutputPass.h" + +#include "ir/Graph.h" +#include "ir/operation/Permute.h" +#include "util/logging.h" +#include "util/Utils.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +void OddOutputPass::run() +{ + auto &outputs = _graph.getOutputs(); + + VERBOSE(OddOutputPass) << "Case 1 : An operand which is a model output and a model input" + << std::endl; + for (auto &ind : outputs) + { + if (_graph.getInputs().contains(ind)) + { + auto permute_output_ind = insertPermute(ind); + // Update the output to be newly added operand + _graph.getOutputs().replace(ind, permute_output_ind); + } + } + + VERBOSE(OddOutputPass) << "Case 2 : Two or more duplicated outputs" << std::endl; + std::unordered_set<ir::OperandIndex> occurence; + for (auto &ind : outputs) + { + auto &obj = _graph.operands().at(ind); + if (occurence.count(ind) == 0) + { + occurence.insert(ind); + continue; + } + + // Panic when it is const, it must have been handled earlier in another pass + UNUSED_RELEASE(obj); + assert(!obj.isConstant()); + + auto permute_output_ind = insertPermute(ind); + ind = permute_output_ind; // Replace output index to fix output duplication + } +} + +ir::OperandIndex OddOutputPass::insertPermute(ir::OperandIndex ind) +{ + auto &obj = _graph.operands().at(ind); + auto output_ind = _graph.addOperand(obj.shape(), obj.typeInfo()); + auto &output_obj = _graph.operands().at(output_ind); + + using ir::operation::Permute; + auto permute_obj = std::make_unique<Permute>(ind, output_ind, Permute::Type::COPY); + auto permute_ind = _graph.operations().push(std::move(permute_obj)); + + output_obj.setDef(permute_ind); + obj.insertUse(permute_ind); + + VERBOSE(OddOutputPass) << "Permute Op inserted for a constant output, node index : " + << permute_ind << std::endl; + VERBOSE(OddOutputPass) << " - Input (original) Operand : " << ind << std::endl; + VERBOSE(OddOutputPass) << " - Output(inserted) Operand : " << output_ind << std::endl; + + return output_ind; +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/OddOutputPass.h b/runtime/onert/core/src/compiler/pass/OddOutputPass.h new file mode 100644 index 000000000..2accbac60 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/OddOutputPass.h @@ -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 __ONERT_COMPILER_PASS_ODD_OUTPUT_PASS_H__ +#define __ONERT_COMPILER_PASS_ODD_OUTPUT_PASS_H__ + +#include <unordered_set> + +#include "Pass.h" +#include "ir/Index.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +/** + * @brief Pass to specially handle odd outputs in a subgraph + * + * Runtime Graph IR requires every input or output must have distinct tensor index, this is onert's + * restriction. However we allow duplication of indices in the models(or API). So we should + * transform the graph after model-loading. + * + * This is necessary since our API lets users to set different buffers for each input and output so + * it is unavoidable that we must copy the value at runtime. + * + * Note that this is a mandatory pass for Graph. + * + * Case 1 : An operand which is a model output and a model input + * + * Create an operand and insert a Permute(copy) op between them. And change the output to be the + * newly generated operand. + * + * e.g.) + * + * ``` + * ((#0 Input0 and also Output0)) + * becomes + * ((#0 Input0)) -> [#0 Permute] -> ((#1 Output0)) + * ``` + * + * Case 2 : Two or more duplicated outputs + * + * Do the same with Case 1, but between two outputs of the same tensor index. + * + * e.g.) + * + * ``` + * ((#0 Input0)) -> [#0 Some Operation] -> ((#1 Output0 and also Output1)) + * becomes + * ((#0 Input0)) -> [#0 Some Operation] -> ((#1 Output0)) [#1 Permute] -> ((#2 Output1)) + * ``` + * + */ +class OddOutputPass : public Pass +{ +public: + using Pass::Pass; + +public: + std::string id() final { return "OddOutputPass"; } + +public: + void run() override; + +private: + ir::OperandIndex insertPermute(ir::OperandIndex input); +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_ODD_OUTPUT_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/OperandPass.cc b/runtime/onert/core/src/compiler/pass/OperandPass.cc new file mode 100644 index 000000000..50c001c30 --- /dev/null +++ b/runtime/onert/core/src/compiler/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 compiler +{ +namespace pass +{ + +void OperandPass::run() +{ + _graph.operands().iterate( + [&](const ir::OperandIndex &index, ir::Operand &object) { callback(index, object); }); +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/OperandPass.h b/runtime/onert/core/src/compiler/pass/OperandPass.h new file mode 100644 index 000000000..b094879c5 --- /dev/null +++ b/runtime/onert/core/src/compiler/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_COMPILER_PASS_OPERAND_PASS_H__ +#define __ONERT_COMPILER_PASS_OPERAND_PASS_H__ + +#include "Pass.h" +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ +class Operand; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace compiler +{ +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 ir::OperandIndex &i, ir::Operand &o) = 0; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_OPERAND_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/OperationPass.cc b/runtime/onert/core/src/compiler/pass/OperationPass.cc new file mode 100644 index 000000000..d7a55cb22 --- /dev/null +++ b/runtime/onert/core/src/compiler/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 compiler +{ +namespace pass +{ + +void OperationPass::run() +{ + _graph.operations().iterate( + [&](const ir::OperationIndex &index, ir::Operation &node) { callback(index, node); }); +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/OperationPass.h b/runtime/onert/core/src/compiler/pass/OperationPass.h new file mode 100644 index 000000000..ac4d818a2 --- /dev/null +++ b/runtime/onert/core/src/compiler/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_COMPILER_PASS_OPERATION_PASS_H__ +#define __ONERT_COMPILER_PASS_OPERATION_PASS_H__ + +#include "Pass.h" +#include "ir/Index.h" + +namespace onert +{ +namespace ir +{ +class Operation; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace compiler +{ +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 ir::OperationIndex &index, ir::Operation &node) = 0; + + /** + * @brief Run the pass + */ + void run() final; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_OPERATION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/Pass.h b/runtime/onert/core/src/compiler/pass/Pass.h new file mode 100644 index 000000000..3f356c337 --- /dev/null +++ b/runtime/onert/core/src/compiler/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_COMPILER_PASS_PASS_H__ +#define __ONERT_COMPILER_PASS_PASS_H__ + +#include <string> + +namespace onert +{ +namespace ir +{ +class Graph; +} // namespace compiler +} // namespace onert + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class Pass +{ +public: + Pass(ir::Graph &graph) : _graph{graph} {} + virtual ~Pass() = default; + +public: + virtual std::string id() = 0; + virtual void run() = 0; + +protected: + ir::Graph &_graph; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/PassRunner.cc b/runtime/onert/core/src/compiler/pass/PassRunner.cc new file mode 100644 index 000000000..2a058c8ac --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PassRunner.cc @@ -0,0 +1,45 @@ +/* + * 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 "PassRunner.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +PassRunner &PassRunner::append(std::unique_ptr<Pass> pass) +{ + _passes.emplace_back(std::move(pass)); + return *this; +} + +void PassRunner::run() +{ + for (auto &pass : _passes) + { + VERBOSE(PassRunner) << "Start running '" << pass->id() << "'" << std::endl; + pass->run(); + VERBOSE(PassRunner) << "Finished running '" << pass->id() << "'" << std::endl; + // TODO Dump graph(LowerInfo, OpSequence, ...)? + } +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/PassRunner.h b/runtime/onert/core/src/compiler/pass/PassRunner.h new file mode 100644 index 000000000..a43c83f89 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PassRunner.h @@ -0,0 +1,53 @@ +/* + * 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_PASS_PASS_RUNNER_H__ +#define __ONERT_COMPILER_PASS_PASS_RUNNER_H__ + +#include <initializer_list> +#include <memory> +#include <vector> + +#include "Pass.h" +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +/** + * @brief Composite passes with logging + */ +class PassRunner +{ +public: + PassRunner() = default; + PassRunner &append(std::unique_ptr<Pass> pass); + + void run(); + +private: + std::vector<std::unique_ptr<Pass>> _passes; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_PASS_RUNNER_H__ diff --git a/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.cc b/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.cc new file mode 100644 index 000000000..504f1b995 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.cc @@ -0,0 +1,167 @@ +/* + * 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 "PermutationEliminationPass.h" +#include "backend/controlflow/Config.h" + +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +void PermutationEliminationPass::callback(const ir::OperationIndex &ind, ir::Operation &node) +{ + _op_ind = ind; + node.accept(*this); +}; + +void PermutationEliminationPass::visit(const ir::operation::Permute &node) +{ + auto in_operand = node.getInputs().at(0); + auto out_operand = node.getOutputs().at(0); + + // Check if two tensors are both portable if not, we can't eliminate the node + { + auto in_def_factor = _lowered_graph.getLowerInfo(in_operand)->def_factors().getOnlyElement(); + auto out_def_factor = _lowered_graph.getLowerInfo(out_operand)->def_factors().getOnlyElement(); + + auto in_config = in_def_factor.backend()->config(); + auto out_config = out_def_factor.backend()->config(); + + // FIXME Supporting dynamic tensor does not exactly mean those are portable. + // It may need to have another config option for checking if each uses `IPortableTensor`. + if (!(in_config->supportDynamicTensor() && out_config->supportDynamicTensor())) + return; + } + + if (_graph.getOutputs().contains(out_operand)) + { + // If the input is a const, we cannot remove it since we cannot put the constant data in the + // output buffer during prepare phase. + auto permute_input = node.getInputs().at(0); + if (_graph.operands().at(permute_input).isConstant()) + return; + // If the input is a model input, we cannot remove it since our API lets users to set different + // buffers for inputs and outputs even though one tensor is both at the same time. + auto permute_output = node.getOutputs().at(0); + if (_graph.getInputs().contains(permute_input) && _graph.getOutputs().contains(permute_output)) + return; + // Likewise, if copying between outputs to outputs, keep it. + if (_graph.getOutputs().contains(permute_input) && _graph.getOutputs().contains(permute_output)) + return; + + // Exceptional case : When the output operand is a model output + // In this case we keep the output and remove the input + + auto &out_operand_obj = _graph.operands().at(out_operand); + assert(out_operand_obj.getDef() == _op_ind); + out_operand_obj.unsetDef(); + _lowered_graph.op_seqs().iterate([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + if (!op_seq.getOutputs().contains(in_operand)) + return; + + // Update OpSequence/ir::Operation edges and ir::Operand edges + op_seq.replaceOutputs(in_operand, out_operand); + for (auto op : op_seq.operations()) + { + auto &operation_obj = _graph.operations().at(op); + if (operation_obj.getOutputs().contains(in_operand)) + { + operation_obj.replaceOutputs(in_operand, out_operand); + out_operand_obj.setDef(op); + } + } + }); + + // Remove Permute operation, enclosing OpSequence and the operand + { + _graph.removeOperand(in_operand); + + auto op_seq_ind = _lowered_graph.op_seqs().getOperation(_op_ind); + // Assumes enclosing OpSequence contatins just this Permute operation + assert(_lowered_graph.op_seqs().at(op_seq_ind).size() == 1); + _lowered_graph.op_seqs().remove(op_seq_ind); + _graph.operations().remove(_op_ind); + } + + _lowered_graph.op_seqs().iterate([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + if (!op_seq.getInputs().contains(in_operand)) + return; + + op_seq.replaceInputs(in_operand, out_operand); + for (auto op : op_seq.operations()) + { + auto &operation_obj = _graph.operations().at(op); + if (operation_obj.getInputs().contains(in_operand)) + { + operation_obj.replaceInputs(in_operand, out_operand); + out_operand_obj.insertUse(op); + } + } + }); + + VERBOSE(removePermute) << "Permute Op removed, node index : " << _op_ind << std::endl; + VERBOSE(removePermute) << " - Input (removed) ir::Operand : " << in_operand << std::endl; + VERBOSE(removePermute) << " - Output(kept) ir::Operand : " << out_operand << std::endl; + } + else + { + // Otherwise keep the input and remove the output + + auto &in_operand_obj = _graph.operands().at(in_operand); + in_operand_obj.removeUse(_op_ind); + + // Make OpSequences(that use the output) use the input + _lowered_graph.op_seqs().iterate([&](const ir::OpSequenceIndex &, ir::OpSequence &op_seq) { + if (!op_seq.getInputs().contains(out_operand)) + return; + + op_seq.replaceInputs(out_operand, in_operand); + for (auto op : op_seq.operations()) + { + auto &operation_obj = _graph.operations().at(op); + if (operation_obj.getInputs().contains(out_operand)) + { + operation_obj.replaceInputs(out_operand, in_operand); + in_operand_obj.insertUse(op); + } + } + }); + + // Remove Permute operation, enclosing OpSequence and the operand + { + _graph.removeOperand(out_operand); + + auto op_seq_ind = _lowered_graph.op_seqs().getOperation(_op_ind); + // Assumes enclosing OpSequence contatins just this Permute operation + assert(_lowered_graph.op_seqs().at(op_seq_ind).size() == 1); + _lowered_graph.op_seqs().remove(op_seq_ind); + _graph.operations().remove(_op_ind); + } + + VERBOSE(removePermute) << "Permute Op removed, node index : " << _op_ind << std::endl; + VERBOSE(removePermute) << " - Input (kept) ir::Operand : " << in_operand << std::endl; + VERBOSE(removePermute) << " - Output(removed) ir::Operand : " << out_operand << std::endl; + } +} + +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.h b/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.h new file mode 100644 index 000000000..29daf1a82 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationEliminationPass.h @@ -0,0 +1,65 @@ +/* + * 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_PASS_PERMUTATION_ELIMINATION_PASS_H__ +#define __ONERT_COMPILER_PASS_PERMUTATION_ELIMINATION_PASS_H__ + +#include "ir/OperationVisitor.h" +#include "LoweredOperationPass.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +/** + * @brief An optimization pass that removes Permute operations if possible + * + * There may be some Permute operations that are inserted by PermutationInsertionPass or other + * passes. This pass checks all Permute operations and eliminates them if Permute in/out tensors + * are compatible and layouts match. + * + * Permute input tensor is kept and the output is removed for all the cases, except model outputs. + * As all output tensors have to be controlflow backend, so the output is kept. + * + * @note This is an optimization pass which means that everything should work fine even if this pass + * was skipped. + */ +class PermutationEliminationPass : public LoweredOperationPass, public ir::OperationVisitor +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "PermutationEliminationPass"; } + +public: + void callback(const ir::OperationIndex &i, ir::Operation &n) final; + +private: + void visit(const ir::operation::Permute &) final; + +private: + ir::OperationIndex _op_ind; +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_PERMUTATION_ELIMINATION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.cc b/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.cc new file mode 100644 index 000000000..c83a72ada --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.cc @@ -0,0 +1,216 @@ +/* + * 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 "backend/controlflow/Config.h" +#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 compiler +{ +namespace pass +{ + +void PermutationInsertionPass::callback(const ir::OperandIndex &index, ir::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<ir::OperationIndex> permute_indexes; + + // Build a map for all necessary type of operands + std::unordered_map<ir::operand::PermuteFactor, ir::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); + 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<ir::OperationIndex> remove_list; + + auto uses = object.getUses(); + for (auto use : uses) + { + // 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 + // Replace the same inputs of an OpSequence at once for the following reasons: + // 1. An OpSequence's inputs are the same inputs of first operation + // 2. An OpSequence may have inputs as the same operand (2 or more). + // 3. The same inputs of OpSequence have the same PermuteFactor. + _lowered_graph.op_seqs().at(op_seq_index).replaceInputs(index, new_index); + + // Update from operation + // Replace the same inputs of an operation at once for the following reasons: + // No. 2 and 3 above + operation.replaceInputs(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).insertUse(use); + } + } + + for (auto &operation : remove_list) + { + object.removeUse(operation); + } + } +} + +ir::OperationIndex PermutationInsertionPass::insertPermute(const ir::OperandIndex &operand_index, + const ir::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_factor = _lowered_graph.getLowerInfo(operand_index)->def_factors().getOnlyElement(); + auto input_backend = input_factor.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 = ir::Layout::UNKNOWN; + // NOTE If one backend supports several layout, the backend must support Permute operation + const backend::Backend *permute_node_backend = compiler::BackendManager::get().getControlflow(); + if (input_backend == output_backend) + { + permute_node_backend = input_backend; + } + const ir::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<ir::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)); + + // Insert permute operation to the graph + const auto input_layout = input_factor.layout(); + const auto output_layout = factor.layout(); + using Permute = ir::operation::Permute; + const auto permute_type = [&]() { + if (input_layout == ir::Layout::NHWC && output_layout == ir::Layout::NCHW) + { + return Permute::Type::NHWC_TO_NCHW; + } + else if (input_layout == ir::Layout::NCHW && output_layout == ir::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, permute_type); + + auto node_index = _graph.operations().push(std::move(insert_node)); + const auto &node = _graph.operations().at(node_index); + + VERBOSE_F() << "Permute Op inserted, node index : " << node_index << std::endl; + VERBOSE_F() << " - Input (original) Operand : " << operand_index << std::endl; + VERBOSE_F() << " - Output(inserted) Operand : " << out_operand_index << std::endl; + + // OpSequence + { + auto op_seq_index = _lowered_graph.op_seqs().emplace(node_index, 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<ir::operation::LowerInfo>( + permute_node_backend, permute_node_layout)); + } + + // Update Use/Def info + { + _graph.operands().at(operand_index).insertUse(node_index); + _graph.operands().at(out_operand_index).setDef(node_index); + } + return node_index; +} +} // namespace pass +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.h b/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.h new file mode 100644 index 000000000..758515385 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationInsertionPass.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_COMPILER_PASS_PERMUTATION_INSERTION_PASS_H__ +#define __ONERT_COMPILER_PASS_PERMUTATION_INSERTION_PASS_H__ + +#include "LoweredOperandPass.h" +#include "compiler/BackendManager.h" +#include "ir/Operand.h" +#include "ir/operand/PermuteFactor.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class PermutationInsertionPass : public LoweredOperandPass +{ +public: + using LoweredOperandPass::LoweredOperandPass; + +public: + std::string id() override { return "PermutationInsertionPass"; } + void callback(const ir::OperandIndex &index, ir::Operand &object) override; + +private: + /** + * @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 ir::OperationIndex + */ + ir::OperationIndex insertPermute(const ir::OperandIndex &operand_index, + const ir::operand::PermuteFactor &factor); +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_PERMUTATION_INSERTION_PASS_H__ diff --git a/runtime/onert/core/src/compiler/pass/PermutationOperationPass.cc b/runtime/onert/core/src/compiler/pass/PermutationOperationPass.cc new file mode 100644 index 000000000..93d125307 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationOperationPass.cc @@ -0,0 +1,351 @@ +/* + * 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" +#include "util/logging.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +using namespace ir; + +void PermutationOperationPass::callback(const OperationIndex &, Operation &node) +{ + node.accept(*this); +}; + +// TODO Remove this. Expanding ranks of Operand is dangerous +void PermutationOperationPass::applyExpandRanks(const Operation &node) +{ + const auto &output_ind = node.getOutputs().at(0); + const auto &output = _graph.operands().at(output_ind); + + assert(output.getDef().valid()); + const auto node_index = output.getDef(); + 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; + } + + int32_t expanded_rank = 0; + for (const auto &index : + (node.getInputs() + node.getOutputs()) | Remove::DUPLICATED | Remove::UNDEFINED) + { + expanded_rank = std::max(expanded_rank, _graph.operands().at(index).shape().rank()); + } + if (expanded_rank < 4) + return; + + for (const auto &index : + (node.getInputs() + node.getOutputs()) | Remove::DUPLICATED | Remove::UNDEFINED) + { + const auto &operand = _graph.operands().at(index); + if (operand.shape().rank() < expanded_rank) + { + if (operand.getUses().size() > 1) + throw std::runtime_error("PermutationOperationPass: not supported expanding rank of " + "operand used in more than one node"); + // TODO remove const_cast later. For example, _ctx may need to be a non const variable or + // a node to extend shape may be inserted in front of this operation + const_cast<Shape &>(operand.shape()).extendRank(expanded_rank); + } + } +} + +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().valid()); + const auto node_index = output_obj.getDef(); + 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; + } + + // Permutation changing layout beyond 4-D is not supported yet + assert(output_obj.shape().rank() <= 4); + + // Divide op_seq based on target operation + { + auto &prev_op_seq = _lowered_graph.op_seqs().at(op_seq_index); + auto &operations = _lowered_graph.graph().operations(); + + // 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++) != node_index) + ; + if (it != prev_op_seq.end()) + { + const auto &target_op_idx = *it; + const auto &target_node = operations.at(target_op_idx); + const auto &next_op_seq_index = + _lowered_graph.op_seqs().emplace(target_op_idx, prev_op_seq.getLayout()); + auto &next_op_seq = _lowered_graph.op_seqs().at(next_op_seq_index); + next_op_seq.setInputs(target_node.getInputs()); + next_op_seq.setOutputs(target_node.getOutputs()); + + std::vector<OperationIndex> remove_list; + remove_list.emplace_back(target_op_idx); + while (++it != prev_op_seq.end()) + { + next_op_seq.appendOperation(target_op_idx); + next_op_seq.setOutputs(target_node.getOutputs()); + remove_list.emplace_back(target_op_idx); + } + + 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<ir::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_idx = *(--prev_op_seq.end()); + const auto &last_node = _lowered_graph.graph().operations().at(last_node_idx); + 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, 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<ir::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() | Remove::DUPLICATED | Remove::UNDEFINED) + { + bool canRemove = true; + for (const auto &use : _graph.operands().at(input).getUses()) + { + 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().valid() && + (lower_info->def_factors().size() == 1 && + lower_info->def_factors().getOnlyElement() == removed_factor)) + { + 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() | Remove::DUPLICATED | Remove::UNDEFINED) + { + 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 ir::operation::BinaryArithmetic &node) +{ + applyExpandRanks(node); +} + +void PermutationOperationPass::visit(const ir::operation::Concat &node) { applyExpandRanks(node); } + +void PermutationOperationPass::visit(const ir::operation::Comparison &node) +{ + applyExpandRanks(node); +} + +void PermutationOperationPass::visit(const ir::operation::ElementwiseBinary &node) +{ + applyExpandRanks(node); +} + +void PermutationOperationPass::visit(const ir::operation::ElementwiseUnary &node) +{ + applyExpandRanks(node); +} + +void PermutationOperationPass::visit(const ir::operation::FullyConnected &node) +{ + const auto &input_ind = node.getInputs().at(ir::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 ir::operation::Gather &node) +{ + const auto &input_ind = node.getInputs().at(ir::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 ir::operation::OneHot &node) +{ + 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 (output_shape.rank() >= 4) + { + changeToKeepLayout(node); + } +} + +void PermutationOperationPass::visit(const ir::operation::Pack &node) +{ + const auto &input_ind = node.getInputs().at(ir::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); + } +} + +void PermutationOperationPass::visit(const ir::operation::PReLU &node) { applyExpandRanks(node); } + +void PermutationOperationPass::visit(const ir::operation::Reshape &node) +{ + const auto &input_ind = node.getInputs().at(ir::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); + } +} + +void PermutationOperationPass::visit(const ir::operation::SquaredDifference &node) +{ + applyExpandRanks(node); +} + +void PermutationOperationPass::visit(const ir::operation::Unpack &node) +{ + const auto &input_ind = node.getInputs().at(ir::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 compiler +} // namespace onert diff --git a/runtime/onert/core/src/compiler/pass/PermutationOperationPass.h b/runtime/onert/core/src/compiler/pass/PermutationOperationPass.h new file mode 100644 index 000000000..cea5de288 --- /dev/null +++ b/runtime/onert/core/src/compiler/pass/PermutationOperationPass.h @@ -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. + */ + +#ifndef __ONERT_COMPILER_PASS_PERMUTATION_OPERATION_PASS_H__ +#define __ONERT_COMPILER_PASS_PERMUTATION_OPERATION_PASS_H__ + +#include "ir/OperationVisitor.h" +#include "LoweredOperationPass.h" + +namespace onert +{ +namespace compiler +{ +namespace pass +{ + +class PermutationOperationPass : public LoweredOperationPass, public ir::OperationVisitor +{ +public: + using LoweredOperationPass::LoweredOperationPass; + +public: + std::string id() final { return "PermutationOperationPass"; } + +public: + void callback(const ir::OperationIndex &i, ir::Operation &n) final; + +public: + void visit(const ir::operation::BinaryArithmetic &) final; + void visit(const ir::operation::Comparison &) final; + void visit(const ir::operation::Concat &) final; + void visit(const ir::operation::ElementwiseBinary &) final; + void visit(const ir::operation::ElementwiseUnary &) final; + void visit(const ir::operation::OneHot &) final; + void visit(const ir::operation::Pack &) final; + void visit(const ir::operation::PReLU &) final; + void visit(const ir::operation::SquaredDifference &) final; + void visit(const ir::operation::Unpack &) final; + void visit(const ir::operation::FullyConnected &) final; + void visit(const ir::operation::Gather &) final; + void visit(const ir::operation::Reshape &) final; + +private: + void applyExpandRanks(const ir::Operation &); + void changeToKeepLayout(const ir::Operation &); +}; + +} // namespace pass +} // namespace compiler +} // namespace onert + +#endif // __ONERT_COMPILER_PASS_PERMUTATION_OPERATION_PASS_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..38a69696e --- /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.out_edges()) + { + addEdge(node_info, *edge); + } +} + +void DotBuilder::addOpSequence(const DotSubgraphInfo &subgraph_info) +{ + _dot << "subgraph cluster_" << subgraph_info.index().value() << " {\n"; + _dot << " label=\"" << subgraph_info.label() << "\";\n"; + _dot << " style=filled;\n"; + _dot << " color=lightgrey;\n"; + _dot << " "; + for (auto op : subgraph_info.operations()) + { + _dot << "operation" << op.value() << "; "; + } + for (auto op : subgraph_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..681cbbf5d --- /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 "DotSubgraphInfo.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 DotSubgraphInfo &subgraph_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..fdf5c6eaa --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotDumper.cc @@ -0,0 +1,201 @@ +/* + * 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 "DotSubgraphInfo.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; + + 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() || (_graph.getInputs() + _graph.getOutputs()).contains(index); + } + 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); + } + + operand_nodes.emplace(index, std::move(node)); + } + }); + + operations.iterate([&](const ir::OperationIndex &index, const ir::Operation &op) { + auto node = std::make_unique<Operation>(index, op); + + for (auto input : op.getInputs()) + { + using onert::dumper::dot::Operand; + + // Constant input and dump level is ALL_BUT_CONSTANTS + if (operand_nodes.find(input) == operand_nodes.end()) + continue; + + auto &input_node = operand_nodes.at(input); + input_node->addOutEdge(node.get()); + } + + for (auto output : op.getOutputs() | ir::Remove::UNDEFINED) + { + using onert::dumper::dot::Operand; + auto &output_node = operand_nodes.at(output); + node->addOutEdge(output_node.get()); + } + + operation_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() + "]"; + DotSubgraphInfo subgraph_info{index, op_seq, shown_operand_set, _graph.operations()}; + subgraph_info.label(label); + subgraph_info.fillcolor(fillcolor); + dot_builder.addOpSequence(subgraph_info); + + // Set fillcolor of all operations in the op_seq + for (const auto &op_idx : op_seq.operations()) + { + auto found = operation_nodes.find(op_idx); + 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..fdbca1642 --- /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 "compiler/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 compiler::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 compiler::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/DotSubgraphInfo.cc b/runtime/onert/core/src/dumper/dot/DotSubgraphInfo.cc new file mode 100644 index 000000000..52e9c758d --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotSubgraphInfo.cc @@ -0,0 +1,58 @@ +/* + * 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 "DotSubgraphInfo.h" + +#include <sstream> + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +DotSubgraphInfo::DotSubgraphInfo(const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq, + const util::Set<ir::OperandIndex> &shown_operands, + const ir::Operations &operations_ctx) + : _index{index} +{ + for (const auto &op_idx : op_seq.operations()) + { + _operations.insert(op_idx); + const auto &node = operations_ctx.at(op_idx); + for (auto o : 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 : 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/DotSubgraphInfo.h b/runtime/onert/core/src/dumper/dot/DotSubgraphInfo.h new file mode 100644 index 000000000..95ba8953e --- /dev/null +++ b/runtime/onert/core/src/dumper/dot/DotSubgraphInfo.h @@ -0,0 +1,61 @@ +/* + * 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/Operations.h> +#include "ir/OpSequence.h" +#include "util/Set.h" + +namespace onert +{ +namespace dumper +{ +namespace dot +{ + +class DotSubgraphInfo +{ +public: + DotSubgraphInfo(const ir::OpSequenceIndex &index, const ir::OpSequence &op_seq, + const util::Set<ir::OperandIndex> &shown_operands, + const ir::Operations &operations_ctx); + + 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..9b09b92e7 --- /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 addOutEdge(Node *dotinfo) { _out_edges.emplace_back(dotinfo); } + /** + * @brief Return list of out edges + * + * @return Edges + */ + const std::vector<Node *> &out_edges() const { return _out_edges; } + +private: + std::string _id; + std::unordered_map<std::string, std::string> _attributes; + std::vector<Node *> _out_edges; +}; + +} // 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/BackendSet.h b/runtime/onert/core/src/exec/BackendSet.h new file mode 100644 index 000000000..33ec75e4b --- /dev/null +++ b/runtime/onert/core/src/exec/BackendSet.h @@ -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. + */ + +#ifndef __ONERT_EXEC_BACKEND_SET_H__ +#define __ONERT_EXEC_BACKEND_SET_H__ + +#include "util/Set.h" + +namespace onert +{ +namespace backend +{ +class Backend; +} // namespace backend +} // namespace onert + +namespace onert +{ +namespace exec +{ + +using BackendSet = util::Set<const backend::Backend *>; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_BACKEND_SET_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..53bc3c204 --- /dev/null +++ b/runtime/onert/core/src/exec/DataflowExecutor.cc @@ -0,0 +1,183 @@ +/* + * 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::OperationIndex> &operations) +{ + int64_t rank = 0; + if (!_indexed_ranks) + { + return rank; + } + for (const auto &operation_idx : operations) + { + auto it = _indexed_ranks->find(operation_idx); + if (it == _indexed_ranks->end()) + { + assert(_graph.operations().at(operation_idx).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<compiler::LoweredGraph> lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs, + compiler::CodeMap &&code_map) + : ExecutorBase{std::move(lowered_graph), input_tensors, output_tensors, tensor_regs}, + _code_map{std::move(code_map)} +{ + 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()); + + bool dynamic_input_exists = hasDynamicInput(); + + // 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()->op_seq.at(op_seq_index)->backend(); + + _subject.notifyJobBegin(this, op_seq, backend); + + job->fn_seq()->initRunning(); + + // check if FunctionSequence needs to handle dynamic tensor + bool handle_dynamic_tensor = op_seq->has_dynamic_tensor() || dynamic_input_exists; + job->fn_seq()->enableDynamicShapeInferer(handle_dynamic_tensor); + + 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..69dfda15c --- /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<compiler::LoweredGraph> lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs, compiler::CodeMap &&code_map); + + void executeImpl() override; + +protected: + int64_t calculateRank(const std::vector<ir::OperationIndex> &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; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_DATAFLOW_EXECUTOR_H__ diff --git a/runtime/onert/core/src/exec/DynamicShapeInferer.cc b/runtime/onert/core/src/exec/DynamicShapeInferer.cc new file mode 100644 index 000000000..5a40bba78 --- /dev/null +++ b/runtime/onert/core/src/exec/DynamicShapeInferer.cc @@ -0,0 +1,1236 @@ +/* + * 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 "exec/DynamicShapeInferer.h" +#include "util/ShapeInference.h" +#include <assert.h> + +namespace onert +{ +namespace exec +{ + +void DynamicShapeInferer::handleBinaryArithmeticOp(const ir::Operation &op, + const ir::OperandIndex lhs_idx, + const ir::OperandIndex rhs_idx) +{ + auto lhs = _tensor_registry->getITensor(lhs_idx); + auto lhs_shape = lhs->getShape(); + + auto rhs = _tensor_registry->getITensor(rhs_idx); + auto rhs_shape = rhs->getShape(); + + /* + Here, the state after compilation (satic shape inference) could be one of the following: + + lhs rhs output execution-time shape inf required + ------------------------------------------ --------------------------------- + case 1) static static static X + case 2) one or both are dynamic dynamic O + + Then nnfw_apply_tensorinf() could change one or both inputs dynamic. + So, in this method, we have one more state and we have to re-calculate shape for this shape. + + case 3) one or both are dynamic static O + + So, only when all inputs are static, we can skip dynamic shape inference. + */ + if ((!lhs->is_dynamic()) && (!rhs->is_dynamic())) + return; + + auto output_idx = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_idx); + + ir::Shape new_shape = shape_inference::inferEltwiseShape(lhs_shape, rhs_shape); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::handleSimpleUnaryOp(const ir::Operation &op, + const ir::OperandIndex input_ind) +{ + // check if input is not dynamic + auto input = _tensor_registry->getITensor(input_ind); + auto output_shape = input->getShape(); + + /* + Here, the state after compilation (satic shape inference) could be one of the following: + + input output execution-time shape inf required + ------------------------- --------------------------------- + case 1) static static X + case 2) dynamic dynamic O + + Then nnfw_apply_tensorinf() could change input dynamic. + So, in this method, we have one more state and we have to re-calculate shape for this shape. + + case 3) dynamic static O + + So, only when input is static, we can skip dynamic shape inference. + */ + if (!input->is_dynamic()) + return; + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::ArgMax &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::ArgMax::Input::INPUT)}; + const auto input = _tensor_registry->getITensor(input_idx); + + const auto axis_idx{op.getInputs().at(ir::operation::ArgMax::Input::AXIS)}; + const auto axis = _tensor_registry->getITensor(axis_idx); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + if (!input->is_dynamic() && !output->is_dynamic()) + return; + + auto input_shape = input->getShape(); + auto axis_value = *reinterpret_cast<const int32_t *>(axis->buffer()); + const auto rank = input_shape.rank(); + axis_value = axis_value < 0 ? axis_value + rank : axis_value; + + ir::Shape new_shape = shape_inference::inferArgMaxShape(input_shape, axis_value, rank); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::BatchMatMul &op) +{ + const auto lhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::LHS); + const auto rhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::RHS); + auto lhs = _tensor_registry->getITensor(lhs_index); + auto rhs = _tensor_registry->getITensor(rhs_index); + + if (!lhs->is_dynamic() && !rhs->is_dynamic()) + return; + + const auto output_index = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_index); + + auto lhs_shape = lhs->getShape(); + auto rhs_shape = rhs->getShape(); + // TODO + + auto new_shape = shape_inference::inferBatchMatMulShape(lhs_shape, rhs_shape, op.param()); + output->applyShape(new_shape); +} + +void DynamicShapeInferer::visit(const ir::operation::BCQFullyConnected &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::BCQFullyConnected::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + + const auto cluster_idx{ + op.getInputs().at(ir::operation::BCQFullyConnected::Input::WEIGHTS_CLUSTERS)}; + const auto &cluster = _tensor_registry->getITensor(cluster_idx); + assert(cluster->is_constant()); + + if (!input->is_dynamic()) + return; + + auto input_shape = input->getShape(); + auto cluster_shape = cluster->getShape(); + + auto cluster_buf = reinterpret_cast<const int32_t *>(cluster->buffer()); + assert(cluster_buf); + + ir::Shape new_shape = + shape_inference::inferBCQFullyConnectedShape(input_shape, cluster_shape, cluster_buf); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::BCQGather &op) +{ + const auto indices_idx{op.getInputs().at(ir::operation::BCQGather::Input::INDICES)}; + const auto &indices = _tensor_registry->getITensor(indices_idx); + + const auto input_binary_idx{op.getInputs().at(ir::operation::BCQGather::Input::INDICES)}; + const auto &input_binary = _tensor_registry->getITensor(input_binary_idx); + + const auto cluster_idx{op.getInputs().at(ir::operation::BCQGather::Input::INPUT_CLUSTERS)}; + const auto &cluster = _tensor_registry->getITensor(cluster_idx); + assert(cluster->is_constant()); + + if (!indices->is_dynamic()) + return; + + auto indices_shape = indices->getShape(); + auto cluster_shape = cluster->getShape(); + auto rank = input_binary->getShape().rank(); + + auto cluster_buf = reinterpret_cast<const int32_t *>(cluster->buffer()); + assert(cluster_buf); + + ir::Shape new_shape = shape_inference::inferBCQGatherShape(indices_shape, cluster_shape, + cluster_buf, rank, op.param()); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::BinaryArithmetic &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::BinaryArithmetic::Input::LHS), + op.getInputs().at(ir::operation::BinaryArithmetic::Input::RHS)); +} + +void DynamicShapeInferer::visit(const ir::operation::BroadcastTo &op) +{ + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto input_idx = op.getInputs().at(ir::operation::BroadcastTo::INPUT); + auto input = _tensor_registry->getITensor(input_idx); + + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + auto shape_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES); + const auto &shape = _tensor_registry->getITensor(shape_idx); + + assert(shape); // It shouldn't be 0. + + auto output_shape = shape_inference::inferBroadcastToShape( + shape->getShape(), reinterpret_cast<const int32_t *>(shape->buffer())); + + // set output shape and output buffer + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Comparison &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Comparison::Input::INPUT0), + op.getInputs().at(ir::operation::Comparison::Input::INPUT1)); +} + +void DynamicShapeInferer::visit(const ir::operation::Concat &op) +{ + /* + The state after compilation (satic shape inference) could be one of the following: + + inputs output execution-time shape inf required + ------------------------------------------ --------------------------------- + case 1) all static static X + case 2) at least on is dynamic dynamic O + + Then nnfw_apply_tensorinf() could change one or both inputs dynamic. + So, in this method, we have one more state and we have to re-calculate shape for this shape. + + case 3) at least on is dynamic static O + + So, only when all inputs are static, we can skip dynamic shape inference. + */ + bool all_static = true; + for (auto input_ind : op.getInputs()) + { + auto input = _tensor_registry->getITensor(input_ind); + if (input->is_dynamic()) + { + all_static = false; + break; + } + } + + if (all_static) + return; + + // sanity check + { + auto isConcatible = [](const backend::ITensor *input1, const backend::ITensor *input2, + int32_t axis) { + if (input1->num_dimensions() != input2->num_dimensions()) + return false; + + for (size_t i = 0; i < input1->num_dimensions(); i++) + { + auto positive_axis = (axis >= 0) ? axis : axis + input1->num_dimensions(); + + if (i != positive_axis) + if (input1->dimension(i) != input2->dimension(i)) + return false; + } + + return true; + }; + + auto first_input_ind = op.getInputs().at(0); + auto first_input = _tensor_registry->getITensor(first_input_ind); + + for (auto input_ind : op.getInputs()) + { + auto input = _tensor_registry->getITensor(input_ind); + if (input != first_input && !isConcatible(first_input, input, op.param().axis)) + throw std::runtime_error("input shapes does not matched for concat"); + } + } + + // getting output shape + onert::shape_inference::Shapes in_shapes; + for (auto input_ind : op.getInputs()) + { + auto input = _tensor_registry->getITensor(input_ind); + ir::Shape shape = input->getShape(); + + in_shapes.emplace_back(shape); + } + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + auto output_shape = shape_inference::inferConcatShape(in_shapes, op.param()); + + output->applyShape(output_shape); +} + +void DynamicShapeInferer::visit(const ir::operation::Conv2D &op) +{ + // check if input is not dynamic + auto input_ind = op.getInputs().at(ir::operation::Conv2D::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + + auto ker_ind = op.getInputs().at(ir::operation::Conv2D::KERNEL); + auto ker = _tensor_registry->getITensor(ker_ind); + + if ((!input->is_dynamic()) && (!ker->is_dynamic())) + return; + + ir::Shape input_shape = input->getShape(); + ir::Shape ker_shape = ker->getShape(); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + ir::Shape output_shape = shape_inference::inferConv2DShape(input_shape, ker_shape, op.param()); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::ElementwiseActivation &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseActivation::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::ElementwiseBinary &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::ElementwiseBinary::Input::LHS), + op.getInputs().at(ir::operation::ElementwiseBinary::Input::RHS)); +} + +void DynamicShapeInferer::visit(const ir::operation::ElementwiseUnary &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::ExpandDims &op) +{ + // check if input is not dynamic + auto input_ind = op.getInputs().at(ir::operation::ExpandDims::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + + // check if output is not dynamic, meaning when 1st input is static and 2nd input is const + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + /* + Here, the state after compilation (satic shape inference) could be one of the following: + + input1 input2 output execution-time shape inf required + ----------------------------- -------------------------------- + case 1) static const static X + case 2) static placeholder dynamic O + case 3) dynamic const dynamic O + case 4) dynamic placeholder dynamic O + + Then nnfw_apply_tensorinf() could change input dynamic. + So, in this method, we could have one more state and we have to re-calculate shape + for this shape. + + case 5) dynamic const static O + + So, only when input1 and ouput are static, we can skip dynamic shape inference. + */ + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + ir::Shape input_shape = input->getShape(); + + auto axis_ind = op.getInputs().at(ir::operation::ExpandDims::AXIS); + auto axis = _tensor_registry->getITensor(axis_ind); + auto axis_buf = reinterpret_cast<const int32_t *>(axis->buffer()); + assert(axis_buf); + + auto output_shape = shape_inference::inferExpandDimsShape(input_shape, axis_buf[0]); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Fill &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + auto input_ind = op.getInputs().at(ir::operation::Fill::Input::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + ir::Shape input_shape = input->getShape(); + + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + assert(input->data_type() == ir::DataType::INT32); + + auto input_buf = reinterpret_cast<const int32_t *>(input->buffer()); + assert(input_buf); + + auto output_shape = shape_inference::inferFillShape(input_shape, input_buf); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::FullyConnected &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::FullyConnected::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + + const auto ker_idx{op.getInputs().at(ir::operation::FullyConnected::Input::WEIGHT)}; + const auto &ker = _tensor_registry->getITensor(ker_idx); + + if (!input->is_dynamic() && !ker->is_dynamic()) + return; + + auto input_shape = input->getShape(); + auto ker_shape = ker->getShape(); + + ir::Shape new_shape = shape_inference::inferFullyConnectedShape(input_shape, ker_shape); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::FusedBatchNorm &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::FusedBatchNorm::Input::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::Gather &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Gather::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + auto input_shape = input->getShape(); + + const auto indices_idx{op.getInputs().at(ir::operation::Gather::Input::INDICES)}; + const auto &indices = _tensor_registry->getITensor(indices_idx); + auto indices_shape = indices->getShape(); + + if (!(input->is_dynamic()) && !(indices->is_dynamic())) + return; + + const auto rank = input_shape.rank(); + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + + assert(0 <= axis && axis < rank); + + ir::Shape new_shape = shape_inference::inferGatherShape(input_shape, indices_shape, axis, rank); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::L2Normalization &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::L2Normalization::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::LSTM &op) +{ + const auto output_index{op.getOutputs().at(ir::operation::LSTM::Output::OUTPUT)}; + auto output = _tensor_registry->getITensor(output_index); + + const auto output_state_out_index{ + op.getOutputs().at(ir::operation::LSTM::Output::OUTPUT_STATE_OUT)}; + + const auto cell_state_out_index{op.getOutputs().at(ir::operation::LSTM::Output::CELL_STATE_OUT)}; + + const auto scratch_buffer_index{op.getOutputs().at(ir::operation::LSTM::Output::SCRATCH_BUFFER)}; + + if (!output->is_dynamic() && + !(_tensor_registry->getITensor(output_state_out_index) != nullptr && + _tensor_registry->getITensor(output_state_out_index)->is_dynamic()) && + !(_tensor_registry->getITensor(cell_state_out_index) != nullptr && + _tensor_registry->getITensor(cell_state_out_index)->is_dynamic()) && + !(_tensor_registry->getITensor(scratch_buffer_index) != nullptr && + _tensor_registry->getITensor(cell_state_out_index)->is_dynamic())) + return; + + const auto input_index{op.getInputs().at(ir::operation::LSTM::Input::INPUT)}; + const auto input = _tensor_registry->getITensor(input_index); + const auto input_shape = input->getShape(); + + const auto input_to_output_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_OUTPUT_WEIGHTS)}; + const auto input_to_output_weights = _tensor_registry->getITensor(input_to_output_weights_index); + const auto input_to_output_weights_shape = input_to_output_weights->getShape(); + + const auto recurrent_to_output_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_OUTPUT_WEIGHTS)}; + const auto recurrent_to_output_weights = + _tensor_registry->getITensor(recurrent_to_output_weights_index); + const auto recurrent_to_output_weights_shape = recurrent_to_output_weights->getShape(); + + // re-sizing outputs + const int n_batch = + (input_shape.rank() == 3 && op.param().time_major) ? input_shape.dim(1) : input_shape.dim(0); + const int n_cell = input_to_output_weights_shape.dim(0); + const int n_output = recurrent_to_output_weights_shape.dim(1); + if (input_shape.rank() == 3) + { + if (op.param().time_major) + output->applyShape(ir::Shape{input_shape.dim(0), n_batch, n_output}); + else + output->applyShape(ir::Shape{n_batch, input_shape.dim(1), n_output}); + } + else + { + assert(input_shape.rank() == 2); + output->applyShape(ir::Shape{n_batch, n_output}); + } + assert(output->buffer() != nullptr); + + auto output_state_out = _tensor_registry->getITensor(output_state_out_index); + if (output_state_out != nullptr) + { + output_state_out->applyShape(ir::Shape{n_batch, n_output}); + assert(output_state_out->buffer() != nullptr); + } + + auto cell_state_out = _tensor_registry->getITensor(cell_state_out_index); + if (cell_state_out != nullptr) + { + cell_state_out->applyShape(ir::Shape{n_batch, n_cell}); + assert(cell_state_out->buffer() != nullptr); + } + + auto scratch_buffer = _tensor_registry->getITensor(scratch_buffer_index); + if (scratch_buffer != nullptr) + { + const auto input_to_input_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::INPUT_TO_INPUT_WEIGHTS)}; + const auto recurrent_to_input_weights_index{ + op.getInputs().at(ir::operation::LSTM::Input::RECURRENT_TO_INPUT_WEIGHTS)}; + + const auto input_to_input_weights_shape = + _tensor_registry->getITensor(input_to_input_weights_index)->getShape(); + bool has_input_to_input_weights = + input_to_input_weights_shape.dim(0) != 0 && input_to_input_weights_shape.dim(1) != 0; + + const auto recurrent_to_input_weights_shape = + _tensor_registry->getITensor(recurrent_to_input_weights_index)->getShape(); + bool has_recurrent_to_input_weights = recurrent_to_input_weights_shape.dim(0) != 0 && + recurrent_to_input_weights_shape.dim(1) != 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; + if (has_cifg_param) + { + scratch_buffer->applyShape(ir::Shape{n_batch, n_cell * 4}); + } + else + { + scratch_buffer->applyShape(ir::Shape{n_batch, n_cell * 3}); + } + assert(scratch_buffer->buffer() != nullptr); + } +} + +void DynamicShapeInferer::visit(const ir::operation::MatrixBandPart &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::MatrixBandPart::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::OneHot &op) +{ + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto indices_ind = op.getInputs().at(ir::operation::OneHot::INDICES); + const auto &indices = _tensor_registry->getITensor(indices_ind); + auto indices_shape = indices->getShape(); + + auto depth_ind = op.getInputs().at(ir::operation::OneHot::DEPTH); + const auto &depth = _tensor_registry->getITensor(depth_ind); + + if (!indices->is_dynamic() && !depth->is_dynamic()) + { + return; + } + + int32_t *depth_buf = reinterpret_cast<int32_t *>(depth->buffer()); + assert(depth_buf); + const auto axis_val = op.param().axis; + + ir::Shape new_shape = shape_inference::inferOnehotShape(indices_shape, *depth_buf, axis_val); + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Pack &op) +{ + bool is_any_of_inputs_dynamic = [&]() -> bool { + for (uint32_t i = 0; i < op.getInputs().size(); ++i) + { + const auto &input = _tensor_registry->getITensor(op.getInputs().at(i)); + if (input->is_dynamic()) + { + return true; + } + } + return false; + }(); + + const auto input_idx{op.getInputs().at(0)}; + const auto &input = _tensor_registry->getITensor(input_idx); + auto input_shape = input->getShape(); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + if (!is_any_of_inputs_dynamic && !output->is_dynamic()) + return; + + const auto rank = input_shape.rank() + 1; + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + const auto num = op.param().num; + + assert(0 <= axis && axis < rank); + + ir::Shape new_shape = shape_inference::inferPackShape(input_shape, axis, rank, num); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Pad &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto input_ind = op.getInputs().at(ir::operation::Pad::Input::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + + auto pad_ind = op.getInputs().at(ir::operation::Pad::Input::PAD); + auto pad = _tensor_registry->getITensor(pad_ind); + + // check if input and output are not dynamic + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + int32_t *pad_buf = reinterpret_cast<int32_t *>(pad->buffer()); + assert(pad_buf); + + auto output_shape = + shape_inference::inferPadShape(input->getShape(), pad_buf, pad->getShape().num_elements()); + + // change output shape and reallocate output tensor memory + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Permute & /* op */) +{ + // NOTE Permute is a special operation which does not do shape inference before the actual + // function(kernel) execution. Shape inference and output allocation will be done in the kernel + // on-the-fly, as it must support inter-backend inference/allocation. +} + +void DynamicShapeInferer::visit(const ir::operation::Pow &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Pow::Input::LHS), + op.getInputs().at(ir::operation::Pow::Input::RHS)); +} + +void DynamicShapeInferer::visit(const ir::operation::Range &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + // from op, access the buffer of second input to read new shape + auto start_idx = op.getInputs().at(ir::operation::Range::Input::START); + auto start_tensor = _tensor_registry->getITensor(start_idx); + + auto limit_idx = op.getInputs().at(ir::operation::Range::Input::LIMIT); + auto limit_tensor = _tensor_registry->getITensor(limit_idx); + + auto delta_idx = op.getInputs().at(ir::operation::Range::Input::DELTA); + auto delta_tensor = _tensor_registry->getITensor(delta_idx); + + if (!start_tensor->is_dynamic() && !limit_tensor->is_dynamic() && !delta_tensor->is_dynamic() && + !output->is_dynamic()) + return; + + ir::Shape new_shape; + if (output->data_type() == ir::DataType::FLOAT32) + { + new_shape = + shape_inference::inferRangeShape<float>(*reinterpret_cast<float *>(start_tensor->buffer()), + *reinterpret_cast<float *>(limit_tensor->buffer()), + *reinterpret_cast<float *>(delta_tensor->buffer())); + } + else if (output->data_type() == ir::DataType::INT32) + { + new_shape = shape_inference::inferRangeShape<int32_t>( + *reinterpret_cast<int32_t *>(start_tensor->buffer()), + *reinterpret_cast<int32_t *>(limit_tensor->buffer()), + *reinterpret_cast<int32_t *>(delta_tensor->buffer())); + } + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Reduce &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Reduce::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + auto input_shape = input->getShape(); + + const auto axes_idx{op.getInputs().at(ir::operation::Reduce::Input::AXES)}; + const auto &axes = _tensor_registry->getITensor(axes_idx); + + if (!input->is_dynamic()) + return; + + std::vector<int32_t> axes_vec; + for (uint32_t i = 0; i < axes->getShape().num_elements(); ++i) + { + const auto buffer = axes->buffer() + axes->calcOffset({i}); + switch (axes->data_type()) + { + case ir::DataType::INT32: + { + axes_vec.emplace_back(*reinterpret_cast<const int32_t *>(buffer)); + break; + } + case ir::DataType::INT64: + { + axes_vec.emplace_back(*reinterpret_cast<const int64_t *>(buffer)); + break; + } + default: + throw std::runtime_error("DynamicShapeInferer " + op.name() + ": Not supported data type"); + break; + } + } + const auto keep_dims = op.param().keep_dims; + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + ir::Shape new_shape = shape_inference::inferReduceShape(input_shape, axes_vec, keep_dims); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Reshape &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + + /* + Here, the state after compilation (satic shape inference) could be one of the following: + + input1 input2 (or option) output execution-time shape inf required + ------------------------------------ -------------------------------- + case 1) static const static X + case 2) static placeholder dynamic O + case 3) dynamic const dynamic O + case 4) dynamic placeholder dynamic O + + Then nnfw_apply_tensorinf() could change input dynamic. + So, in this method, we could have one more state and we have to re-calculate shape + for this shape. + + case 5) dynamic const static O + + So, only when both input1 and ouput are static, we can skip dynamic shape inference. + */ + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + // New shape is given by second input tensor + if (op.getInputs().size() == 2) + { + // from op, access the buffer of second input to read new shape + auto new_shape_ind = op.getInputs().at(ir::operation::Reshape::Input::SHAPE); + + // getting output shape by reading new_shape tensor buffer + auto new_shape = _tensor_registry->getITensor(new_shape_ind); + assert(new_shape); + + int32_t *new_shape_buf = reinterpret_cast<int32_t *>(new_shape->buffer()); + assert(new_shape_buf); + + auto output_shape = shape_inference::inferReshapeShape( + new_shape_buf, new_shape->getShape().num_elements(), input->getShape().num_elements()); + + // if shape is changed, change output shape and reallocate output tensor memory + if (output_shape != output->getShape() || output->buffer() == nullptr) + { + // change on output shape + output->applyShape(output_shape); + } + assert(output->buffer() != nullptr); + } + // New shape is given by option + else if (op.param().new_shape.size() != 0) + { + // Let's check the new_shape option + auto shape = op.param().new_shape; + auto output_shape = shape_inference::inferReshapeShape(shape.data(), shape.size(), + input->getShape().num_elements()); + + // if shape is changed, change output shape and reallocate output tensor memory + if (output_shape != output->getShape() || output->buffer() == nullptr) + { + // change on output shape + output->applyShape(output_shape); + } + assert(output->buffer() != nullptr); + } + else + { + throw std::runtime_error("Reshape: new shape is missing"); + return; + } +} + +void DynamicShapeInferer::visit(const ir::operation::ResizeBilinear &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + // getting output shape from input shape and Params + int32_t height_out, width_out; + if (op.getInputs().size() == 2) + { + auto size_ind = op.getInputs().at(ir::operation::ResizeBilinear::Input::SIZE); + auto size = _tensor_registry->getITensor(size_ind); + if (size->data_type() == ir::DataType::INT32) + { + auto size_buf = reinterpret_cast<const int32_t *>(size->buffer()); + height_out = size_buf[0]; + width_out = size_buf[1]; + } + else + { + throw std::runtime_error("DynamicShapeInferer ResizeBilinear : Unsupported data type"); + } + } + else + { + height_out = op.param().height_out; + width_out = op.param().width_out; + } + auto output_shape = + shape_inference::inferResizeBilinearShape(input->getShape(), height_out, width_out); + + // if shape is changed, change output shape and reallocate output tensor memory + if (output_shape != output->getShape() || output->buffer() == nullptr) + { + // change on output shape + output->applyShape(output_shape); + } + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Reverse &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Reverse::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::Select &op) +{ + const auto input_cond_idx = op.getInputs().at(ir::operation::Select::Input::CONDITION); + const auto &input_cond = _tensor_registry->getITensor(input_cond_idx); + + const auto input_true_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_TRUE); + const auto &input_true = _tensor_registry->getITensor(input_true_idx); + + const auto input_false_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_FALSE); + const auto &input_false = _tensor_registry->getITensor(input_false_idx); + + if ((!input_cond->is_dynamic()) && (!input_true->is_dynamic()) && (!input_false->is_dynamic())) + { + return; + } + + auto input_cond_shape = input_cond->getShape(); + auto input_true_shape = input_true->getShape(); + auto input_false_shape = input_false->getShape(); + + // Select output shpae + ir::Shape new_shape = + shape_inference::inferSelectShape(input_cond_shape, input_true_shape, input_false_shape); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Shape &op) +{ + const auto input_idx{op.getInputs().at(0)}; + const auto &input = _tensor_registry->getITensor(input_idx); + auto input_shape = input->getShape(); + + if (!input->is_dynamic()) + return; + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + ir::Shape output_shape; + output_shape.append(input_shape.rank()); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Slice &op) +{ + const auto input_index{op.getInputs().at(ir::operation::Slice::Input::INPUT)}; + const auto input = _tensor_registry->getITensor(input_index); + const auto begins_index{op.getInputs().at(ir::operation::Slice::Input::BEGINS)}; + const auto begins = _tensor_registry->getITensor(begins_index); + const auto sizes_index{op.getInputs().at(ir::operation::Slice::Input::SIZES)}; + const auto sizes = _tensor_registry->getITensor(sizes_index); + auto output_index = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_index); + + if (!(input->is_dynamic() || begins->is_dynamic() || sizes->is_dynamic() || output->is_dynamic())) + { + return; + } + + ir::Shape input_shape = input->getShape(); + auto begins_buf = reinterpret_cast<const int32_t *>(begins->buffer()); + auto sizes_buf = reinterpret_cast<const int32_t *>(sizes->buffer()); + + ir::Shape new_shape = shape_inference::inferSliceShape(input_shape, begins_buf, sizes_buf); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Softmax &op) +{ + handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Softmax::INPUT)); +} + +void DynamicShapeInferer::visit(const ir::operation::SpaceToBatchND &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::INPUT)}; + const auto block_shape_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::BLOCK_SIZE)}; + const auto padding_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::PADDINGS)}; + auto output_idx{op.getOutputs().at(0)}; + + const auto &input = _tensor_registry->getITensor(input_idx); + const auto &block_shape = _tensor_registry->getITensor(block_shape_idx); + const auto &padding = _tensor_registry->getITensor(padding_idx); + auto output = _tensor_registry->getITensor(output_idx); + + if (!(input->is_dynamic() || block_shape->is_dynamic() || padding->is_dynamic() || + output->is_dynamic())) + { + return; + } + + auto input_shape = input->getShape(); + auto block_shape_shape = block_shape->getShape(); + auto padding_shape = padding->getShape(); + + auto block_shape_data = reinterpret_cast<int32_t *>(block_shape->buffer()); + auto padding_data = reinterpret_cast<int32_t *>(padding->buffer()); + + ir::Shape new_shape = shape_inference::inferSpaceToBatchNDShape( + input_shape, block_shape_shape, padding_shape, block_shape_data, padding_data); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Split &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Split::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + + // Return if all tensors are not dynamic + bool has_dynamic = false; + for (const auto output_idx : op.getOutputs()) + { + auto output = _tensor_registry->getITensor(output_idx); + has_dynamic |= output->is_dynamic(); + } + if (!input->is_dynamic() && !has_dynamic) + { + return; + } + + auto input_shape = input->getShape(); + + const auto axis_idx{op.getInputs().at(ir::operation::Split::Input::AXIS)}; + const auto &axis = _tensor_registry->getITensor(axis_idx); + + auto axis_value = *reinterpret_cast<const int32_t *>(axis->buffer()); + const auto num_splits = op.param().num_splits; + const auto rank = input_shape.rank(); + axis_value = axis_value < 0 ? axis_value + rank : axis_value; + + assert(0 <= axis_value && axis_value < rank); + + ir::Shape new_shape = shape_inference::inferSplitShape(input_shape, axis_value, num_splits); + for (int out_tensor_idx = 0; out_tensor_idx < num_splits; out_tensor_idx++) + { + auto output_ind = op.getOutputs().at(out_tensor_idx); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); + } +} + +void DynamicShapeInferer::visit(const ir::operation::SquaredDifference &op) +{ + handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::SquaredDifference::Input::LHS), + op.getInputs().at(ir::operation::SquaredDifference::Input::RHS)); +} + +void DynamicShapeInferer::visit(const ir::operation::Squeeze &op) +{ + const auto input_idx{op.getInputs().at(ir::operation::Squeeze::Input::INPUT)}; + const auto &input = _tensor_registry->getITensor(input_idx); + + if (!input->is_dynamic()) + { + return; + } + + auto input_shape = input->getShape(); + + // Squeeze output shpae + ir::Shape new_shape = shape_inference::inferSqueezeShape(input_shape, op.param()); + + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::StridedSlice &op) +{ + + const auto input_index{op.getInputs().at(ir::operation::StridedSlice::Input::INPUT)}; + auto input = _tensor_registry->getITensor(input_index); + ir::Shape input_shape = input->getShape(); + + const auto starts_index{op.getInputs().at(ir::operation::StridedSlice::Input::STARTS)}; + auto starts = _tensor_registry->getITensor(starts_index); + + const auto ends_index{op.getInputs().at(ir::operation::StridedSlice::Input::ENDS)}; + auto ends = _tensor_registry->getITensor(ends_index); + + const auto strides_index{op.getInputs().at(ir::operation::StridedSlice::Input::STRIDES)}; + auto strides = _tensor_registry->getITensor(strides_index); + + if (!(input->is_dynamic() || starts->is_dynamic() || ends->is_dynamic() || strides->is_dynamic())) + { + return; + } + + const auto begin_mask = op.param().begin_mask; + const auto end_mask = op.param().end_mask; + const auto shrink_axis_mask = op.param().shrink_axis_mask; + const auto rank = input_shape.rank(); + + auto op_params = shape_inference::buildStridedSliceParams( + reinterpret_cast<uint32_t *>(starts->buffer()), reinterpret_cast<uint32_t *>(ends->buffer()), + reinterpret_cast<uint32_t *>(strides->buffer()), begin_mask, end_mask, shrink_axis_mask, + rank); + + auto output_index = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_index); + + ir::Shape output_shape = + onert::shape_inference::inferStridedSliceShape(input_shape, op_params, rank); + + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Tile &op) +{ + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + auto input_idx = op.getInputs().at(ir::operation::Tile::Input::INPUT); + auto input = _tensor_registry->getITensor(input_idx); + + auto multiplier_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES); + auto multiplier = _tensor_registry->getITensor(multiplier_idx); + + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + auto input_shape = input->getShape(); + auto multiplier_buffer = reinterpret_cast<const int32_t *>(multiplier->buffer()); + assert(multiplier_buffer); + + auto output_shape = + shape_inference::inferTileShape(input_shape, multiplier_buffer, multiplier->dimension(0)); + + // set output shape and output buffer + output->applyShape(output_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Transpose &op) +{ + // check if output is not dynamic + auto output_ind = op.getOutputs().at(0); + auto output = _tensor_registry->getITensor(output_ind); + + // from op, access the buffer of second input to read new shape + auto input_ind = op.getInputs().at(ir::operation::Transpose::Input::INPUT); + auto input = _tensor_registry->getITensor(input_ind); + auto input_shape = input->getShape(); + + /* + Here, the state after compilation (static shape inference) could be one of the following: + + input perms output execution-time shape inf required + ------------------------------------ -------------------------------- + case 1) static const static X + case 2) static non-const dynamic O + case 3) dynamic const dynamic O + case 4) dynamic non-const dynamic O + + So, only when both input1 and ouput are static, we can skip dynamic shape inference. + */ + if ((!input->is_dynamic()) && (!output->is_dynamic())) + return; + + auto perm_ind = op.getInputs().at(ir::operation::Transpose::Input::PERMUTATION); + auto perm = _tensor_registry->getITensor(perm_ind); + + ir::Shape new_shape; + // TODO Change perm->dimension(0) == 0 to perm->num_elements() == 0 + if (perm->dimension(0) == 0) // This condition means that perm is (n-1...0) + { + // Call by (n-1...0) + new_shape = shape_inference::inferTransposeShape(input_shape, nullptr, 0); + } + else + { + // Check rank + if (input->num_dimensions() != perm->getShape().num_elements()) + { + throw std::runtime_error("DynamicShapeInferer failed, bad rank size: " + + std::to_string(perm->getShape().num_elements())); + } + + // set output shape, based on input and params + const auto perm_buffer = reinterpret_cast<const int32_t *>(perm->buffer()); + new_shape = shape_inference::inferTransposeShape(input_shape, perm_buffer, perm->dimension(0)); + } + output->applyShape(new_shape); + assert(output->buffer() != nullptr); +} + +void DynamicShapeInferer::visit(const ir::operation::Unpack &op) +{ + // check if output is not dynamic + const auto input_idx{op.getInputs().at(0)}; + const auto &input = _tensor_registry->getITensor(input_idx); + + if (!input->is_dynamic()) + return; + + auto input_shape = input->getShape(); + + const auto rank = input_shape.rank(); + const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis); + const auto num = op.param().num; + + assert(0 <= axis && axis < rank); + + ir::Shape new_shape = shape_inference::inferUnpackShape(input_shape, axis, rank); + + for (int out_tensor_idx = 0; out_tensor_idx < num; out_tensor_idx++) + { + auto output_ind = op.getOutputs().at(out_tensor_idx); + auto output = _tensor_registry->getITensor(output_ind); + + output->applyShape(new_shape); + + assert(output->buffer() != nullptr); + } +} + +} // namespace exec +} // namespace onert 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/ExecTime.h b/runtime/onert/core/src/exec/ExecTime.h new file mode 100644 index 000000000..846d0930b --- /dev/null +++ b/runtime/onert/core/src/exec/ExecTime.h @@ -0,0 +1,112 @@ +/* + * 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_EXEC_TIME_H__ +#define __ONERT_EXEC_EXEC_TIME_H__ + +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "JSONExecTime.h" +#include <memory> +#include <limits> +#include <map> +#include <unordered_map> +#include <vector> + +namespace onert +{ +namespace exec +{ +class ExecTime +{ +public: + explicit ExecTime(const std::vector<const backend::Backend *> &backends) + : _json(backends, _measurements) + { + } + +public: + /** + * @brief Get exec time of an operation with input size + * or linearly interpolated value based on size if there is no record for given size + * + * @param[in] backend id of a backend + * @param[in] operation name of an operation + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @return execution time for given input sizes + * -1 if there are no records for given parameters (backend, op, quantization). + */ + int64_t getOperationExecTime(const backend::Backend *backend, const std::string &operation, + bool quant, uint32_t op_size) const; + /** + * @brief Update exec time of the operation on a backend with given input size or + * add new entity if there is no one. + * + * @param[in] backend id of a backend + * @param[in] operation name of an operation + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @param[in] time real measured value + */ + void updateOperationExecTime(const backend::Backend *backend, const std::string &operation, + bool quant, uint32_t op_size, int64_t time); + /** + * @brief Get the permute time from one backend to another + * + * @param[in] from_backend + * @param[in] to_backend + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @return permutation time for operation size + */ + int64_t getPermuteTime(const backend::Backend *from_backend, const backend::Backend *to_backend, + bool quant, uint32_t op_size) const; + /** + * @brief Update permute time from one backend to another + * + * @param[in] from_backend + * @param[in] to_backend + * @param[in] quant if input type quantized + * @param[in] time measured permutation time + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + */ + void updatePermuteTime(const backend::Backend *from_backend, const backend::Backend *to_backend, + bool quant, uint32_t op_size, int64_t time); + /** + * @brief Get the max value of int32_t in int64_t + * @return max value + */ + static int64_t getMax() { return _MAX; } + /** + * @brief Update metrics file with new data. + */ + void uploadOperationsExecTime() const { _json.uploadOperationsExecTime(); } + static const int64_t NOT_FOUND = -1; + +private: + /// @brief Measurement data, which is shared with serializer + MeasurementData _measurements; + // int64_t::max may cause integer overflow + static const int64_t _MAX = std::numeric_limits<int32_t>::max(); + /// @brief Serializer + JSON _json; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_EXEC_TIME_H__ diff --git a/runtime/onert/core/src/exec/Execution.cc b/runtime/onert/core/src/exec/Execution.cc new file mode 100644 index 000000000..21fdd9c05 --- /dev/null +++ b/runtime/onert/core/src/exec/Execution.cc @@ -0,0 +1,182 @@ +/* + * 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<ExecutorMap> &executors) : _executors{executors} +{ + assert(executors != nullptr); + assert(executors->at(ir::SubgraphIndex{0}) != nullptr); + const auto &primary_subg = primary_subgraph(); + _io_desc.inputs.resize(primary_subg.getInputs().size()); + _io_desc.outputs.resize(primary_subg.getOutputs().size()); +} + +void Execution::changeInputShape(const ir::IOIndex &index, const ir::Shape &new_shape) +{ + // This will be used later to set input tensor dynamic + // Note that 'compiled' model will not be updated with new_shape + // but new_shape will change model input shape while 'running' the model + _io_desc.dynamic_input_shapes[index] = new_shape; + + VERBOSE(Execution) << "Model input shape will be changed at the start of execute()" + << "(index: " << index.value() << ")" << std::endl; +} + +// TODO Remove default parameter +void Execution::setInput(const ir::IOIndex &index, const void *buffer, size_t length, + ir::Layout layout) +{ + const auto input_index = primary_subgraph().getInputs().at(index); + const auto info = primary_subgraph().operands().at(input_index).info(); + + // TODO handle when (!buffer && length != 0) : setting the input as an optional tensor + + // check if size enough for input is passed + // if input_shape_sig is set, input_shape_sig overrides shape in info + // note: input_shape_sig contains shape passed by nnfw_set_input_tensorinfo() + { + auto input_shape_sig = _io_desc.dynamic_input_shapes.find(index); + auto size_required = (input_shape_sig != _io_desc.dynamic_input_shapes.end()) + ? input_shape_sig->second.num_elements() * + onert::ir::sizeOfDataType(info.typeInfo().type()) + : info.total_size(); + + if (length < size_required) + { + 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) +{ + auto info = ir::OperandInfo::createStaticInfo(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 = primary_subgraph().getOutputs().at(index); + const auto info = primary_subgraph().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) +{ + auto info = ir::OperandInfo::createStaticInfo(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; + + primary_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; } + +ir::Shape Execution::getInputShape(ir::IOIndex ind) const +{ + auto itr = _io_desc.dynamic_input_shapes.find(ind); + if (itr == _io_desc.dynamic_input_shapes.end()) + { + auto operand_idx = primary_subgraph().getInputs().at(ind.value()); + return primary_subgraph().operands().at(operand_idx).shape(); + } + else + { + return itr->second; + } +} + +ir::Shape Execution::getOutputShape(ir::IOIndex ind) const +{ + if (!isFinished()) + throw std::runtime_error("Cannot get output shape before execution is finished"); + + const auto &output_desc = _io_desc.outputs.at(ind.value()); + + return output_desc->info.shape(); +} + +} // 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..066b52ee1 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutionObservers.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 "exec/ExecutionObservers.h" + +#include <string> + +#include "util/logging.h" +#include "exec/IExecutor.h" +#include "misc/polymorphic_downcast.h" +#include "ir/OpSequence.h" +#include "util/EventWriter.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 + const auto &node = _graph.operations().at(op_seq->operations().at(0)); + 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::QUANT_UINT8_ASYMM; + + uint32_t size = 0; + for (const auto &ind : (node.getInputs() + node.getOutputs()) | ir::Remove::UNDEFINED) + { + size += exec->graph().operands().at(ind).info().total_size(); + } + if (node_name == "Permute") + { + // TODO Change it to updateOperationExecTime() + _et->updatePermuteTime(backend, backend, is_quantized, size, timer_res); + } + else + { + _et->updateOperationExecTime(backend, node_name, is_quantized, size, timer_res); + } +}; + +ChromeTracingObserver::ChromeTracingObserver(const std::string &filepath, const ir::Graph &graph) + : _base_filepath(filepath), _recorder{}, _collector{&_recorder}, _graph{graph} +{ +} + +ChromeTracingObserver::~ChromeTracingObserver() +{ + try + { + EventWriter{_recorder}.writeToFiles(_base_filepath); + } + catch (const std::exception &e) + { + std::cerr << "E: Fail to record event in ChromeTracingObserver: " << e.what() << std::endl; + } +} + +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, _graph.operations())}); +} + +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, _graph.operations())}); +} + +void ChromeTracingObserver::handleEnd(IExecutor *) +{ + _collector.onEvent(EventCollector::Event{EventCollector::Edge::END, "runtime", "Graph"}); +} + +std::string ChromeTracingObserver::opSequenceTag(const ir::OpSequence *op_seq, + const ir::Operations &operations) +{ + if (op_seq->size() == 0) + return "Empty OpSequence"; + + const auto &first_op_idx = op_seq->operations().at(0); + const auto &first_op_node = operations.at(first_op_idx); + std::string tag = "$" + std::to_string(first_op_idx.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/ExecutionObservers.h b/runtime/onert/core/src/exec/ExecutionObservers.h new file mode 100644 index 000000000..f8c2acca5 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutionObservers.h @@ -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. + */ + +#ifndef __ONERT_EXEC_OBSREVERS_H__ +#define __ONERT_EXEC_OBSREVERS_H__ + +#include "exec/IFunction.h" +#include "ir/OpSequence.h" +#include "ExecTime.h" +#include "util/ITimer.h" +#include "exec/IExecutor.h" +#include "util/EventCollector.h" +#include "util/EventRecorder.h" + +namespace onert +{ +namespace exec +{ +class IExecutionObserver +{ +public: + /// @brief Invoked just before model (not individual operation) execution begins + virtual void handleBegin(IExecutor *) { return; } + + virtual void handleBegin(IExecutor *, const ir::OpSequence *, const backend::Backend *) = 0; + virtual void handleEnd(IExecutor *, const ir::OpSequence *, const backend::Backend *) = 0; + + /// @brief Invoked just after model (not individual operation) execution ends + virtual void handleEnd(IExecutor *) { return; } + + virtual ~IExecutionObserver() = default; +}; + +class ProfileObserver : public IExecutionObserver +{ +public: + explicit ProfileObserver(std::shared_ptr<ExecTime> et, const ir::Graph &graph) + : _et(std::move(et)), _graph(graph) + { + } + void handleBegin(IExecutor *, const ir::OpSequence *, const backend::Backend *) override; + void handleEnd(IExecutor *, const ir::OpSequence *, const backend::Backend *) override; + + void handleEnd(IExecutor *) override { _et->uploadOperationsExecTime(); } + +private: + std::unique_ptr<util::ITimer> _timer; + std::shared_ptr<ExecTime> _et; + const ir::Graph &_graph; +}; + +class ChromeTracingObserver : public IExecutionObserver +{ +public: + ChromeTracingObserver(const std::string &filepath, const ir::Graph &graph); + ~ChromeTracingObserver(); + void handleBegin(IExecutor *) override; + void handleBegin(IExecutor *, const ir::OpSequence *, const backend::Backend *) override; + void handleEnd(IExecutor *, const ir::OpSequence *, const backend::Backend *) override; + void handleEnd(IExecutor *) override; + +private: + static std::string opSequenceTag(const ir::OpSequence *op_seq, const ir::Operations &operations); + +private: + const std::string &_base_filepath; + EventRecorder _recorder; + EventCollector _collector; + const ir::Graph &_graph; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_OBSREVERS_H__ diff --git a/runtime/onert/core/src/exec/ExecutorBase.cc b/runtime/onert/core/src/exec/ExecutorBase.cc new file mode 100644 index 000000000..018a0bba0 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutorBase.cc @@ -0,0 +1,203 @@ +/* + * 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 "backend/ITensor.h" +#include "backend/controlflow/UserTensor.h" +#include "backend/cpu_common/Tensor.h" +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +ExecutorBase::ExecutorBase(std::unique_ptr<compiler::LoweredGraph> &&lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs) + : _lowered_graph{std::move(lowered_graph)}, _graph{_lowered_graph->graph()}, + _input_tensors{input_tensors}, _output_tensors{output_tensors}, _mutex() +{ + // TODO Fix the way of knowing whether it is primary or not + bool primary_executor = !(_input_tensors.empty() && _output_tensors.empty()); + if (!primary_executor) + { + auto build_input_tensor_list = [&](const onert::ir::OperandIndexSequence &ind_seq) { + std::vector<backend::ITensor *> list; + for (auto ind : ind_seq) + { + backend::ITensor *tensor = tensor_regs.getITensor(ind); + assert(tensor != nullptr); + list.push_back(tensor); + } + return list; + }; + auto build_output_tensor_list = [&](const onert::ir::OperandIndexSequence &ind_seq) { + std::vector<backend::ITensor *> list; + for (auto ind : ind_seq) + { + backend::ITensor *tensor = tensor_regs.getITensor(ind); + assert(tensor != nullptr); + list.push_back(tensor); + } + return list; + }; + _input_tensors = build_input_tensor_list(_graph.getInputs()); + _output_tensors = build_output_tensor_list(_graph.getOutputs()); + } +} + +void ExecutorBase::execute(const std::vector<backend::ITensor *> &src_tensors, + const std::shared_ptr<IPermuteFunction> &pre_fn) +{ + // 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) + // Deadlock occurs when an Executor is called recursively. + std::lock_guard<std::mutex> lock(_mutex); + + assert(src_tensors.size() == _graph.getInputs().size()); + assert(src_tensors.size() == _input_tensors.size()); + for (uint32_t n = 0; n < _graph.getInputs().size(); ++n) + { + // when user changes input shape, the input tensor is dynamic and its memory is not allocated. + // This code find the info to allocate dynamic tensor, and allocate memory based on the source + // tensor's shape set by caller. + const auto src_tensor = src_tensors[n]; + auto input_tensor = _input_tensors[n]; + // If src_tensor or input_tensor is nullptr, pre_fn does not copy the tensors + if (src_tensor != nullptr && input_tensor != nullptr) + { + const auto orig_input_shape = input_tensor->getShape(); + const auto changed_input_shape = + convertShape(src_tensor->getShape(), src_tensor->layout(), input_tensor->layout()); + if (orig_input_shape != changed_input_shape) + { + input_tensor->set_dynamic(); + } + } + } + + // TODO Move calling permute_fn.run() into executeImpl() + assert(pre_fn); + pre_fn->run(); + + executeImpl(); +} + +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); + + // Set input(s) + assert(_input_tensors.size() == desc.inputs.size()); + for (uint32_t i = 0; i < _input_tensors.size(); ++i) + { + // TODO Remove dynamic_cast + auto *tensor = dynamic_cast<backend::controlflow::UserTensor *>(_input_tensors[i]); + assert(tensor); + auto input_shape = desc.dynamic_input_shapes.find(ir::IOIndex{i}); + if (input_shape != desc.dynamic_input_shapes.end()) + { + tensor->set_dynamic(); + tensor->setShape(input_shape->second); + } + // TODO Check if (desc.inputs[i] == nullptr) + // TODO Better design for ITensor? (we need const_cast as ITensor is writable) + tensor->setBuffer(static_cast<uint8_t *>(const_cast<void *>(desc.inputs[i]->buffer)), + desc.inputs[i]->size); + + handleDynamicInputTensor(ir::IOIndex{i}, desc); + } + + assert(_output_tensors.size() == desc.outputs.size()); + for (uint32_t i = 0; i < _output_tensors.size(); ++i) + { + // TODO Remove dynamic_cast + auto *tensor = dynamic_cast<backend::controlflow::UserTensor *>(_output_tensors[i]); + assert(tensor); + tensor->set_dynamic(); // It can't be resized but shape could change + if (desc.outputs[i] == nullptr) + throw std::runtime_error{"Output " + std::to_string(i) + "'s buffer is not set."}; + tensor->setBuffer(static_cast<uint8_t *>(desc.outputs[i]->buffer), desc.outputs[i]->size); + } + + executeImpl(); + + // Update output(s) desc + for (uint32_t n = 0; n < _graph.getOutputs().size(); ++n) + { + ir::IOIndex output_index{n}; + // Optional output + if (desc.outputs.at(n) == nullptr) + { + continue; + } + auto &output = *desc.outputs.at(n); + + // set shape of outputDesc to tensor shape since tensor can be dynamic + const auto output_tensor_shape = _output_tensors[n]->getShape(); + output.info.shape( + convertShape(output_tensor_shape, _output_tensors[n]->layout(), output.layout)); + } +} + +/** + * @brief Changes tensor shape and allocate memory + * if input shape was changed by nnfw_set_input_tensorinfo() + * + * @note Cases are: + * 1) static operand -> nnfw_set_input_tensorinfo() -> execute() -> execute() + * (a) (b) + * + * at (a), operand is static, tensor is static - memory dealloc is not needed + * (DynamicTensorManager cannot dealloc memory allocated by StaticTensorManager) + * at (b), operand is static, tensor is dynamic - memory dealloc is needed + * + * 2) dynamic operand -> nnfw_set_input_tensorinfo() -> execute() -> execute() + * (a) (b) + * + * at (a), operand is dynamic, tensor is dynamic - memory dealloc is not needed + * since it has not been allocated yet + * at (b), operand is dynamic, tensor is dynamic - memory dealloc is needed + */ +void ExecutorBase::handleDynamicInputTensor(ir::IOIndex io_ind, const IODescription &desc) +{ + auto shape_sig_found = desc.dynamic_input_shapes.find(io_ind); + if (shape_sig_found != desc.dynamic_input_shapes.end()) + { + auto changed_input_shape = shape_sig_found->second; + _input_tensors[io_ind.value()]->applyShape(changed_input_shape); + } +} + +bool ExecutorBase::hasDynamicInput() +{ + for (auto &tensor : _input_tensors) + { + if (tensor->is_dynamic()) + return true; + } + return false; +} + +} // 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..8a6ec9174 --- /dev/null +++ b/runtime/onert/core/src/exec/ExecutorBase.h @@ -0,0 +1,107 @@ +/* + * 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 "IPermuteFunction.h" +#include "exec/ExecutionObservers.h" +#include "ShapeConverter.h" +#include "exec/IExecutor.h" +#include "compiler/LoweredGraph.h" +#include "ir/LowerInfoMap.h" +#include "backend/IConfig.h" +#include "backend/Backend.h" +#include "exec/ExecTime.h" +#include "exec/IFunction.h" +#include "backend/IDynamicTensorManager.h" +#include "backend/ITensorManager.h" +#include "exec/ExecutionObservee.h" +#include "compiler/TensorRegistries.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<compiler::LoweredGraph> &&lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs); + + virtual ~ExecutorBase() = default; + + const ir::Graph &graph() final { return _graph; } + + /** + * @brief Execute without IODescription + * + * @param src_tensor Tensor list that will be copied to input tensors of this + * @param pre_fn The permutation function that copy from src_tensor to input tensors of this + */ + void execute(const std::vector<backend::ITensor *> &src_tensors, + const std::shared_ptr<IPermuteFunction> &pre_fn); + + 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)); }; + + const std::vector<backend::ITensor *> &getInputTensors() const { return _input_tensors; } + + const std::vector<backend::ITensor *> &getOutputTensors() const { return _output_tensors; } + +protected: + /** + * @brief Returns @c true if any input tensor is dynamic; @c false if all are static tensors + */ + bool hasDynamicInput(); + +protected: + ExecutionObservee _subject; + std::shared_ptr<ir::OperationIndexMap<int64_t>> _indexed_ranks; + std::unique_ptr<compiler::LoweredGraph> _lowered_graph; + const ir::Graph &_graph; + std::vector<backend::ITensor *> _input_tensors; + std::vector<backend::ITensor *> _output_tensors; + std::mutex _mutex; + +private: + void handleDynamicInputTensor(ir::IOIndex input_index, const IODescription &desc); +}; + +} // 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..8aefa5eeb --- /dev/null +++ b/runtime/onert/core/src/exec/FunctionSequence.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 "exec/FunctionSequence.h" + +#include "ir/Operation.h" +#include "backend/IDynamicTensorManager.h" +#include "backend/ITensorRegistry.h" +#include "util/logging.h" + +namespace onert +{ +namespace exec +{ + +void FunctionSequence::run() +{ + if (_enable_dynamic_shape_inferer && _dynamic_tensor_ctx) + { + // acl_cl and acl_neon backend don't support dynamic shape. + // _dynamic_tensor_ctx is always nullptr for acl_cl and acl_neon + // Thus, those two bakends cannot reach here. + if (_dynamic_tensor_ctx->op_seq->size() != _functions.size()) + throw std::runtime_error("operation and functions should be mapped one by one"); + + auto op_seq_iter = _dynamic_tensor_ctx->op_seq->begin(); + for (const auto &function : _functions) + { + // set shape of output and allocate memory when needed + auto &op = _dynamic_tensor_ctx->operations->at(*op_seq_iter); + op.accept(*_dynamic_tensor_ctx->dynamic_shape_inferer); + + auto *sub_func_seq = dynamic_cast<FunctionSequence *>(function.get()); + if (sub_func_seq != nullptr) + { + sub_func_seq->enableDynamicShapeInferer(true); + sub_func_seq->dynamic_tensor_ctx(dynamic_tensor_ctx()); + } + + // run kernel + function->run(); + + // deallocate input tensors which is no longer used + _dynamic_tensor_ctx->dynamic_tensor_manager->deallocInput(*op_seq_iter); + + op_seq_iter++; + } + } + else + { + for (const auto &function : _functions) + { + function->run(); + } + } +} + +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/IPermuteFunction.h b/runtime/onert/core/src/exec/IPermuteFunction.h new file mode 100644 index 000000000..11017edc9 --- /dev/null +++ b/runtime/onert/core/src/exec/IPermuteFunction.h @@ -0,0 +1,354 @@ +/* + * 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_EXEC_I_PERMUTE_FUNCTION_H__ +#define __ONERT_EXEC_I_PERMUTE_FUNCTION_H__ + +#include "feature/IndexIterator.h" +#include "feature/nchw/Reader.h" +#include "feature/nchw/View.h" +#include "feature/nhwc/Reader.h" +#include "feature/nhwc/View.h" + +#include "backend/ITensor.h" +#include "exec/IFunction.h" +#include "ir/Index.h" +#include "ir/Shape.h" +#include <memory> +#include <typeinfo> +#include "util/Utils.h" +#include <vector> +#include <unordered_map> + +namespace onert +{ +namespace exec +{ + +inline void UpdateOffsets(::onert::backend::ITensor *src, ::onert::backend::ITensor *dst, + const ::onert::ir::Shape &loop_shape, std::vector<size_t> &src_offsets, + std::vector<size_t> &dst_offsets) +{ + ShapeLoop(loop_shape, [&](const onert::ir::Coordinates &coords) { + src_offsets.emplace_back(src->calcOffset(coords)); + dst_offsets.emplace_back(dst->calcOffset(coords)); + }); +} + +inline void CopyStatic(const uint8_t *src_buffer, uint8_t *dst_buffer, + const std::vector<size_t> &src_offsets, + const std::vector<size_t> &dst_offsets, size_t copy_len) +{ + assert(src_offsets.size() == dst_offsets.size()); + for (size_t i = 0; i < src_offsets.size(); ++i) + { + memcpy(dst_buffer + dst_offsets.at(i), src_buffer + src_offsets.at(i), copy_len); + } +} + +inline void CopyDynamic(const ::onert::backend::ITensor *src, const ::onert::backend::ITensor *dst, + uint8_t *dst_buffer, const ::onert::ir::Shape &loop_shape, size_t copy_len) +{ + ShapeLoop(loop_shape, [&](const onert::ir::Coordinates &coords) { + // Copy src tensor's data to dst_buffer with calculated offset of dst tensor + memcpy(dst_buffer + dst->calcOffset(coords), src->buffer() + src->calcOffset(coords), copy_len); + }); +} + +class IPermuteFunction : public IFunction +{ +protected: + enum class PermuteType + { + NHWC_TO_NCHW, + NCHW_TO_NHWC, + COPY + }; + +public: + virtual void run() override + { + // TODO Optimization : Make control does not reach here? when (_src_tensors.size() == 0) + assert(_src_tensors.size() == _dst_tensors.size()); + if (_src_tensors_offsets.size() == 0) + { + _src_tensors_offsets.resize(_src_tensors.size()); + _dst_tensors_offsets.resize(_dst_tensors.size()); + } + assert(_src_tensors.size() == _src_tensors_offsets.size()); + assert(_src_tensors_offsets.size() == _dst_tensors_offsets.size()); + + for (size_t i = 0; i < _src_tensors.size(); ++i) + { + auto src_tensor = _src_tensors.at(i); + auto dst_tensor = _dst_tensors.at(i); + auto &src_offsets = _src_tensors_offsets.at(i); + auto &dst_offsets = _dst_tensors_offsets.at(i); + if (src_tensor != dst_tensor) + { + const auto rank = src_tensor->num_dimensions(); + permute(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + } + } + } + + virtual void prepare() override { optimize(); } + + virtual void optimize() = 0; + +protected: + void permute(backend::ITensor *src_tensor, backend::ITensor *dst_tensor, size_t rank, + std::vector<size_t> &src_offsets, std::vector<size_t> &dst_offsets) + { + if (src_tensor->total_size() == 0) + { + assert(dst_tensor->total_size() == 0); + return; + } + + assert(src_tensor != dst_tensor); + assert(underlying_type(src_tensor->data_type()) == underlying_type(dst_tensor->data_type())); + switch (src_tensor->data_type()) + { + case ir::DataType::FLOAT32: + permute<float>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + case ir::DataType::INT32: + permute<int32_t>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + case ir::DataType::UINT32: + permute<uint32_t>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + case ir::DataType::BOOL8: + case ir::DataType::QUANT_UINT8_ASYMM: + case ir::DataType::UINT8: + permute<uint8_t>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + case ir::DataType::QUANT_INT8_ASYMM: + case ir::DataType::QUANT_INT8_SYMM: + permute<int8_t>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + case ir::DataType::INT64: + permute<int64_t>(src_tensor, dst_tensor, rank, src_offsets, dst_offsets); + break; + default: + throw std::runtime_error("IPermuteFunction: Not supported data type"); + break; + } + } + +private: + // TODO make src const by proving const access() + template <class T> + void permute(backend::ITensor *src, backend::ITensor *dst, size_t rank, + std::vector<size_t> &src_offsets, std::vector<size_t> &dst_offsets) + { + assert(src->total_size() != 0 && dst->total_size() != 0); + // If dst is subtensor, we have to use clEnqueueMapBuffer instead of clEnqueueWirteBuffer + if (dst->needMemoryMap() && !dst->is_subtensor()) + { + // A assertion to check mapping without calling map() + // Now there is no case where both src and dst have cl buffer. + assert(!src->needMemoryMap()); + + if (!src->has_padding() && !dst->has_padding() && src->layout() == dst->layout()) + { + src->access([&](backend::ITensor &) { dst->enqueueWriteBuffer(src->buffer(), false); }); + } + else + { + // TODO Optimize this block in case of that padding size of dst is big. + _buffers_map[dst].reserve(dst->total_size()); + auto dst_buffer = _buffers_map[dst].data(); + src->access([&](backend::ITensor &) { + permute<T>(src, dst, rank, dst_buffer, dst->total_size(), src_offsets, dst_offsets); + }); + dst->enqueueWriteBuffer(dst_buffer, false); + } + } + else if (src->needMemoryMap() && !src->is_subtensor() && !src->has_padding() && + !dst->has_padding() && src->layout() == dst->layout()) + { + assert(!dst->needMemoryMap()); + dst->access([&](backend::ITensor &) { src->enqueueReadBuffer(dst->buffer(), true); }); + } + else + { + auto fn = [&](backend::ITensor &) { + dst->access([&](backend::ITensor &) { + permute<T>(src, dst, rank, dst->buffer(), dst->total_size(), src_offsets, dst_offsets); + }); + }; + src->access(fn); + } + } + + template <class T> + void permute(backend::ITensor *src, backend::ITensor *dst, size_t rank, uint8_t *dst_buffer, + size_t dst_size, std::vector<size_t> &src_offsets, std::vector<size_t> &dst_offsets) + { + assert(dst_buffer != nullptr); + assert(dst_size == dst->total_size()); + + const auto permute_type = [&]() -> PermuteType { + if (src->layout() == ir::Layout::NHWC && dst->layout() == ir::Layout::NCHW) + { + return PermuteType::NHWC_TO_NCHW; + } + else if (src->layout() == ir::Layout::NCHW && dst->layout() == ir::Layout::NHWC) + { + return PermuteType::NCHW_TO_NHWC; + } + else + { + return PermuteType::COPY; + } + }(); + if (rank == 4 && permute_type != PermuteType::COPY) + { + switch (permute_type) + { + case PermuteType::NHWC_TO_NCHW: + { + ir::FeatureShape shape; + shape.N = dst->dimension(0); + shape.C = dst->dimension(1); + shape.H = dst->dimension(2); + shape.W = dst->dimension(3); + + typename feature::nchw::View<T>::Strides strides; + const auto start_offset = dst->calcOffset({0, 0, 0, 0}); + strides.W = dst->dimension(3) == 1 ? 0 : dst->calcOffset({0, 0, 0, 1}) - start_offset; + strides.H = dst->dimension(2) == 1 ? 0 : dst->calcOffset({0, 0, 1, 0}) - start_offset; + strides.C = dst->dimension(1) == 1 ? 0 : dst->calcOffset({0, 1, 0, 0}) - start_offset; + strides.N = dst->dimension(0) == 1 ? 0 : dst->calcOffset({1, 0, 0, 0}) - start_offset; + + const feature::nhwc::Reader<T> from(src); + feature::nchw::View<T> into(shape, strides, + reinterpret_cast<T *>(dst_buffer + start_offset), dst_size); + 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; + }; + break; + } + case PermuteType::NCHW_TO_NHWC: + { + ir::FeatureShape shape; + shape.N = dst->dimension(0); + shape.H = dst->dimension(1); + shape.W = dst->dimension(2); + shape.C = dst->dimension(3); + + typename feature::nhwc::View<T>::Strides strides; + const auto start_offset = dst->calcOffset({0, 0, 0, 0}); + strides.C = dst->dimension(3) == 1 ? 0 : dst->calcOffset({0, 0, 0, 1}) - start_offset; + strides.W = dst->dimension(2) == 1 ? 0 : dst->calcOffset({0, 0, 1, 0}) - start_offset; + strides.H = dst->dimension(1) == 1 ? 0 : dst->calcOffset({0, 1, 0, 0}) - start_offset; + strides.N = dst->dimension(0) == 1 ? 0 : dst->calcOffset({1, 0, 0, 0}) - start_offset; + + const feature::nchw::Reader<T> from(src); + feature::nhwc::View<T> into(shape, strides, + reinterpret_cast<T *>(dst_buffer + start_offset), dst_size); + 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; + }; + break; + } + default: + { + throw std::runtime_error("Unsupported Permutation"); + break; + } + } + } + else if (!src->has_padding() && !dst->has_padding()) + { + auto src_size = src->total_size(); + assert(src_size <= dst->total_size()); + memcpy(dst_buffer, src->buffer(), src_size); + } + else + { + auto loop_shape = src->getShape(); + const auto copy_axis = loop_shape.rank() - 1; + const auto copy_len = loop_shape.dim(copy_axis) * sizeof(T); + loop_shape.dim(copy_axis) = 1; + + if (src->is_dynamic()) + { + assert(dst->is_dynamic()); + CopyDynamic(src, dst, dst_buffer, loop_shape, copy_len); + } + else + { + // TODO Uncomment the assertion below + // assert(!dst->is_dynamic() || dst is output of graph); + if (src_offsets.size() == 0) + { + assert(dst_offsets.size() == 0); + + auto loop_shape = src->getShape(); + const auto copy_axis = loop_shape.rank() - 1; + loop_shape.dim(copy_axis) = 1; + UpdateOffsets(src, dst, loop_shape, src_offsets, dst_offsets); + } + CopyStatic(src->buffer(), dst_buffer, src_offsets, dst_offsets, copy_len); + } + } + } + +protected: + // NOTE The typeid expression is lvalue expression which refers to an object with static storage + // duration, of the polymorphic type const std::type_info or of some type derived from it. + // So std::type_info is non-copyable + const std::type_info &underlying_type(ir::DataType type) const + { + switch (type) + { + case ir::DataType::FLOAT32: + return typeid(float); + case ir::DataType::INT32: + return typeid(int32_t); + case ir::DataType::UINT32: + return typeid(uint32_t); + case ir::DataType::INT64: + return typeid(int64_t); + case ir::DataType::BOOL8: + case ir::DataType::QUANT_UINT8_ASYMM: + case ir::DataType::UINT8: + return typeid(uint8_t); + case ir::DataType::QUANT_INT8_ASYMM: + case ir::DataType::QUANT_INT8_SYMM: + return typeid(int8_t); + default: + throw std::runtime_error("IPermuteFunction: Not supported data type"); + } + } + +protected: + std::vector<backend::ITensor *> _src_tensors; + std::vector<backend::ITensor *> _dst_tensors; + std::vector<std::vector<size_t>> _src_tensors_offsets; + std::vector<std::vector<size_t>> _dst_tensors_offsets; + std::unordered_map<const backend::ITensor *, std::vector<uint8_t>> _buffers_map; +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_I_PERMUTE_FUNCTION_H__ 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/JSONExecTime.h b/runtime/onert/core/src/exec/JSONExecTime.h new file mode 100644 index 000000000..a64cb3133 --- /dev/null +++ b/runtime/onert/core/src/exec/JSONExecTime.h @@ -0,0 +1,97 @@ +/* + * 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_JSON_EXEC_TIME_H__ +#define __ONERT_EXEC_JSON_EXEC_TIME_H__ + +#include <fstream> +#include <unordered_map> +#include <map> +#include <vector> +#include "backend/Backend.h" +#include "backend/IConfig.h" + +namespace onert +{ +namespace exec +{ + +/** + * @brief table, that contains execution time of an operation on some backend for different input + * sizes and transfer time from one backend to another for various input sizes (permutation time) + * + * backend -> op -> quant-> size --> time + * _measurements[Backend*]["string"][bool][uint32_t] = int64_t + */ +using MeasurementData = std::unordered_map< + const backend::Backend *, + std::unordered_map<std::string, std::unordered_map<bool, std::map<uint32_t, int64_t>>>>; + +class JSON +{ +public: + explicit JSON(const std::vector<const backend::Backend *> &backends, + MeasurementData &measurements) + : _measurement_file("exec_time.json"), _backends(), _measurements(measurements) + { + for (const auto b : backends) + { + _backends.emplace(b->config()->id(), b); + } + loadOperationsExecTime(); + }; + /** + * @brief Update _operations_exec_time_file with new data. + */ + void uploadOperationsExecTime() const; + +private: + ///@brief file containing measurements + std::string _measurement_file; + std::unordered_map<std::string, const backend::Backend *> _backends; + std::unordered_map< + const backend::Backend *, + std::unordered_map<std::string, std::unordered_map<bool, std::map<uint32_t, int64_t>>>> + &_measurements; + /** + * @brief Helper function for inserting data to OperationExecTimes + * + * @param backend String name of backend + * @param operation String name of operation + * @param quant if input type quantized + * @param stream File stream + */ + void readOperation(const std::string &backend, const std::string &operation, bool quant, + std::ifstream &stream); + + /** + * @brief Helper function for writing OperationExecTimes to stream + * + * @param operation_info Map of operations execution information + * @param stream File stream + */ + void printOperation(const std::map<uint32_t, int64_t> &operation_info, + std::ofstream &stream) const; + /** + * @brief Parse and load operations_exec_time from _operations_exec_time_file. + */ + void loadOperationsExecTime(); +}; + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_JSON_EXEC_TIME_H__ diff --git a/runtime/onert/core/src/exec/Job.cc b/runtime/onert/core/src/exec/Job.cc new file mode 100644 index 000000000..27925a93c --- /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, FunctionSequence *fn_seq) : _index{index}, _fn_seq{fn_seq} {} + +void Job::run() { _fn_seq->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..6de9c31a0 --- /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/FunctionSequence.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_seq compiled code to run this job + * @param inputs Input operand list + * @param outputs Output operand list + */ + Job(uint32_t index, FunctionSequence *fn_seq); + /** + * @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 + */ + FunctionSequence *fn_seq() { return _fn_seq; } + +private: + uint32_t _index; + FunctionSequence *_fn_seq; +}; + +} // 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..6e6ca110f --- /dev/null +++ b/runtime/onert/core/src/exec/LinearExecutor.cc @@ -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. + */ + +#include "LinearExecutor.h" +#ifdef RUY_PROFILER +#include "ruy/profiler/instrumentation.h" +#endif + +namespace onert +{ +namespace exec +{ + +#ifdef RUY_PROFILER +namespace +{ +char *seq_to_label(const onert::ir::OpSequence *op_seq, const onert::ir::Operations &operations) +{ + auto node_name = operations.at(*op_seq->begin()).name(); + char *cstr = new char[node_name.length() + 1]; + std::strcpy(cstr, node_name.c_str()); + return cstr; +} +} // namespace +#endif + +void LinearExecutor::executeImpl() +{ + _subject.notifyModelBegin(this); + for (auto &&code : _code) + { + const auto op_seq = code.op_seq; + const auto backend = code.lower_info->backend(); +// TODO : Move ruy profiler into ExecutionObserver +#ifdef RUY_PROFILER + ruy::profiler::ScopeLabel label(seq_to_label(op_seq, _graph.operations())); +#endif + _subject.notifyJobBegin(this, op_seq, backend); + + auto &fn_seq = code.fn_seq; + + fn_seq->initRunning(); + + bool handle_dynamic_tensor = op_seq->has_dynamic_tensor() || hasDynamicInput(); + fn_seq->enableDynamicShapeInferer(handle_dynamic_tensor); + 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..22d00ec30 --- /dev/null +++ b/runtime/onert/core/src/exec/LinearExecutor.h @@ -0,0 +1,72 @@ +/* + * 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<compiler::LoweredGraph> lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs, compiler::CodeMap &&code_map, + const std::vector<ir::OpSequenceIndex> &order) + : ExecutorBase{std::move(lowered_graph), input_tensors, output_tensors, tensor_regs} + { + 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..676bdb5fa --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelExecutor.cc @@ -0,0 +1,156 @@ +/* + * 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(); + } + +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<compiler::LoweredGraph> lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs, + compiler::CodeMap &&code_map) + : DataflowExecutor{std::move(lowered_graph), input_tensors, output_tensors, tensor_regs, + std::move(code_map)} +{ + VERBOSE(ParallelExecutor) << "Constructing Parallel Executor" << std::endl; +} + +void ParallelExecutor::executeImpl() +{ + bool dynamic_input_exists = hasDynamicInput(); + + // Init scheduler + // TODO Consider to have distinct backend set in LowerInfoMap + BackendSet backends; + for (auto &itr : _lowered_graph->getLowerInfo()->op_seq) + { + 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()->op_seq.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); + }; + + job->fn_seq()->initRunning(); + + // dynamic tensor setting + bool handle_dynamic_tensor = op_seq->has_dynamic_tensor() || dynamic_input_exists; + job->fn_seq()->enableDynamicShapeInferer(handle_dynamic_tensor); + + _scheduler->assign(std::make_unique<HookFunction>(job->fn_seq(), 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..111c20c0c --- /dev/null +++ b/runtime/onert/core/src/exec/ParallelExecutor.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_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<compiler::LoweredGraph> lowered_graph, + const std::vector<backend::ITensor *> &input_tensors, + const std::vector<backend::ITensor *> &output_tensors, + const compiler::TensorRegistries &tensor_regs, 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..70c9c3dd6 --- /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 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..6802c9e43 --- /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 "BackendSet.h" +#include "ThreadPool.h" + +namespace onert +{ +namespace exec +{ + +class ParallelScheduler +{ +public: + /** + * @brief Constructs ParallelScheduler object + * + * @param backends Backend set + */ + ParallelScheduler(const 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/ShapeConverter.cc b/runtime/onert/core/src/exec/ShapeConverter.cc new file mode 100644 index 000000000..707aef29b --- /dev/null +++ b/runtime/onert/core/src/exec/ShapeConverter.cc @@ -0,0 +1,60 @@ +/* + * 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 "ShapeConverter.h" + +namespace onert +{ +namespace exec +{ + +ir::Shape convertShape(const ir::Shape &shape, ir::Layout src_layout, ir::Layout dst_layout) +{ + if (shape.rank() != 4) + return shape; + + if (src_layout == dst_layout) + return shape; + + if (src_layout == ir::Layout::NCHW && dst_layout == ir::Layout::NHWC) + { + const ir::Shape &src_NCHW = shape; + ir::Shape dst_NHWC(4); + dst_NHWC.dim(0) = src_NCHW.dim(0); // N + dst_NHWC.dim(1) = src_NCHW.dim(2); // H + dst_NHWC.dim(2) = src_NCHW.dim(3); // W + dst_NHWC.dim(3) = src_NCHW.dim(1); // C + + return dst_NHWC; + } + + if (src_layout == ir::Layout::NHWC && dst_layout == ir::Layout::NCHW) + { + const ir::Shape &src_NHWC = shape; + ir::Shape dst_NCHW(4); + dst_NCHW.dim(0) = src_NHWC.dim(0); // N + dst_NCHW.dim(1) = src_NHWC.dim(3); // C + dst_NCHW.dim(2) = src_NHWC.dim(1); // H + dst_NCHW.dim(3) = src_NHWC.dim(2); // W + + return dst_NCHW; + } + + throw std::runtime_error("Should not reach here"); +} + +} // namespace exec +} // namespace onert diff --git a/runtime/onert/core/src/exec/ShapeConverter.h b/runtime/onert/core/src/exec/ShapeConverter.h new file mode 100644 index 000000000..7dc7e7536 --- /dev/null +++ b/runtime/onert/core/src/exec/ShapeConverter.h @@ -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. + */ + +#ifndef __ONERT_EXEC_SHAPE_CONVERTER_H__ +#define __ONERT_EXEC_SHAPE_CONVERTER_H__ + +#include <ir/Layout.h> +#include <ir/Shape.h> + +namespace onert +{ +namespace exec +{ + +/** + * @brief Converts shape when its rank is 4 + * + * @return ir::Shape Return a shape based on dst_layout. If rank is not 4, input shape is + * returned without conversion. + */ +ir::Shape convertShape(const ir::Shape &shape, ir::Layout src_layout, ir::Layout dst_layout); + +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_SHAPE_CONVERTER_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/exec/feature/IndexIterator.h b/runtime/onert/core/src/exec/feature/IndexIterator.h new file mode 100644 index 000000000..9613f5a30 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/IndexIterator.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +/** + * @file IndexIterator.h + * @brief This file contains IndexIterator class + */ + +#ifndef __ONERT_EXEC_FEATURE_INDEX_ITERATOR_H__ +#define __ONERT_EXEC_FEATURE_INDEX_ITERATOR_H__ + +#include "ir/Shape.h" + +namespace onert +{ +namespace exec +{ +namespace feature +{ + +/** + * @brief Class to iterate Callable with Index of feature + */ +class IndexIterator +{ +public: + /** + * @brief Construct IndexIterator object with Shape of feature + * @param[in] shape Shape reference of feature + */ + IndexIterator(const ir::FeatureShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + /** + * @brief Call a function iterated + * @param[in] cb A callback function + * @return Current IndexIterator object + */ + template <typename Callable> IndexIterator &iter(Callable cb) + { + for (int32_t batch = 0; batch < _shape.N; ++batch) + { + for (int32_t ch = 0; ch < _shape.C; ++ch) + { + for (int32_t row = 0; row < _shape.H; ++row) + { + for (int32_t col = 0; col < _shape.W; ++col) + { + cb(batch, ch, row, col); + } + } + } + } + + return (*this); + } + +private: + /** + * @brief Shape for feature + */ + const ir::FeatureShape _shape; +}; + +/** + * @brief Create an object of IndexIterator for feature + * @param[in] Shape reference of feature + * @return Created IndexIterator object + */ +static inline IndexIterator iterate(const ir::FeatureShape &shape) { return IndexIterator{shape}; } + +/** + * @brief Call a function iterated using IndexIterator of feature + * Overloaded operator<< + * @param[in] it An IndexIterator reference + * @param[in] cb A callback function + * @return created IndexIterator object + */ +template <typename Callable> IndexIterator &operator<<(IndexIterator &&it, Callable cb) +{ + return it.iter(cb); +} + +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_INDEX_ITERATOR_H__ diff --git a/runtime/onert/core/src/exec/feature/Reader.h b/runtime/onert/core/src/exec/feature/Reader.h new file mode 100644 index 000000000..ed87bb990 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/Reader.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +/** + * @file Reader.h + * @brief This file contains Reader class + */ + +#ifndef __ONERT_EXEC_FEATURE_READER_H__ +#define __ONERT_EXEC_FEATURE_READER_H__ + +#include <cstdint> + +namespace onert +{ +namespace exec +{ +namespace feature +{ + +/** + * @brief Class reads values of feature + * The interface class + */ +template <typename T> struct Reader +{ + /** + * @brief Destruct Reader object using default destructor + */ + virtual ~Reader() = default; + + /** + * @brief Get the value used by three indexes + * @param[in] ch The depth index + * @param[in] row The height index + * @param[in] col The width index + * @return The value at the offset + */ + virtual T at(uint32_t ch, uint32_t row, uint32_t col) const = 0; + /** + * @brief Get the value used by four indexes + * @param[in] batch The batch index + * @param[in] ch The depth index + * @param[in] row The height index + * @param[in] col The width index + * @return The value at the offset + */ + virtual T at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const = 0; +}; + +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_READER_H__ diff --git a/runtime/onert/core/src/exec/feature/nchw/Reader.h b/runtime/onert/core/src/exec/feature/nchw/Reader.h new file mode 100644 index 000000000..aebedd853 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/nchw/Reader.h @@ -0,0 +1,117 @@ +/* + * 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_FEATURE_NCHW_READER_H__ +#define __ONERT_EXEC_FEATURE_NCHW_READER_H__ + +#include "../Reader.h" + +#include <cassert> + +#include "backend/ITensor.h" +#include "ir/Shape.h" + +namespace onert +{ +namespace exec +{ +namespace feature +{ +namespace nchw +{ + +template <typename T> class Reader : public feature::Reader<T> +{ +public: + using Strides = ir::FeatureShape; + // Construct for buffer and strides + Reader(const ir::FeatureShape &shape, const Strides &strides, const T *ptr, size_t len) + : _shape{shape}, _strides{strides}, _ptr{reinterpret_cast<const uint8_t *>(ptr)}, _len{len} + { + UNUSED_RELEASE(len); // Workaround for unused variable in release mode + assert(len == static_cast<size_t>(strides.N != 0 + ? shape.N * strides.N + : strides.C != 0 ? shape.C * strides.C + : strides.H != 0 ? shape.H * strides.H + : shape.W * strides.W)); + } + + // Construct for backend tensor + Reader(backend::ITensor *tensor) + : _ptr{tensor->buffer() + tensor->calcOffset({0, 0, 0, 0})}, _len{tensor->total_size()} + { + assert(tensor->layout() == ir::Layout::NCHW); + + const auto start_offset = tensor->calcOffset({0, 0, 0, 0}); + _strides.W = tensor->dimension(3) == 1 ? 0 : tensor->calcOffset({0, 0, 0, 1}) - start_offset; + _strides.H = tensor->dimension(2) == 1 ? 0 : tensor->calcOffset({0, 0, 1, 0}) - start_offset; + _strides.C = tensor->dimension(1) == 1 ? 0 : tensor->calcOffset({0, 1, 0, 0}) - start_offset; + _strides.N = tensor->dimension(0) == 1 ? 0 : tensor->calcOffset({1, 0, 0, 0}) - start_offset; + + _shape.W = tensor->dimension(3); + _shape.H = tensor->dimension(2); + _shape.C = tensor->dimension(1); + _shape.N = tensor->dimension(0); + } + +public: + T at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const final + { + return getRef(batch, ch, row, col); + } + T at(uint32_t ch, uint32_t row, uint32_t col) const final { return getRef(0, ch, row, col); } + +protected: + const T &getRef(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const + { + const auto offset = feature_index_to_byte_offset(batch, ch, row, col); + + const T *ptr = reinterpret_cast<const T *>(_ptr + offset); + + return *ptr; + } + +private: + size_t feature_index_to_byte_offset(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const + { + assert(1u * _shape.N > batch); // shape.N > batch + assert(1u * _shape.C > ch); // shape.C > ch + assert(1u * _shape.H > row); // shape.H > row + assert(1u * _shape.W > col); // shape.W > col + + uint32_t res = 0; + res += batch * _strides.N; + res += ch * _strides.C; + res += row * _strides.H; + res += col * _strides.W; + + return res; + } + +private: + // TODO Remove _shape + ir::FeatureShape _shape; + Strides _strides; + const uint8_t *_ptr; + size_t _len; +}; + +} // namespace nchw +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_NCHW_READER_H__ diff --git a/runtime/onert/core/src/exec/feature/nchw/View.h b/runtime/onert/core/src/exec/feature/nchw/View.h new file mode 100644 index 000000000..df3576264 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/nchw/View.h @@ -0,0 +1,71 @@ +/* + * 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_FEATURE_NCHW_VIEW_H__ +#define __ONERT_EXEC_FEATURE_NCHW_VIEW_H__ + +#include "Reader.h" + +#include "backend/ITensor.h" +#include "ir/Shape.h" +#include "util/logging.h" + +#include <cassert> + +namespace onert +{ +namespace exec +{ +namespace feature +{ +namespace nchw +{ + +template <typename T> class View final : public Reader<T> +{ +public: + using Strides = typename Reader<T>::Strides; + // Construct for buffer of model inputs + View(const ir::FeatureShape &shape, const Strides &strides, T *ptr, size_t len) + : Reader<T>{shape, strides, ptr, len} + { + // DO NOTHING + } + + // Construct for backend tensor + View(::onert::backend::ITensor *tensor) : Reader<T>{tensor} + { + // DO NOTHING + } + +public: + using Reader<T>::at; + T &at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) + { + return const_cast<T &>(Reader<T>::getRef(batch, ch, row, col)); + } + T &at(uint32_t ch, uint32_t row, uint32_t col) + { + return const_cast<T &>(Reader<T>::getRef(0, ch, row, col)); + } +}; + +} // namespace nchw +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_NCHW_VIEW_H__ diff --git a/runtime/onert/core/src/exec/feature/nhwc/Reader.h b/runtime/onert/core/src/exec/feature/nhwc/Reader.h new file mode 100644 index 000000000..da6a5f6a9 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/nhwc/Reader.h @@ -0,0 +1,118 @@ +/* + * 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_FEATURE_NHWC_READER_H__ +#define __ONERT_EXEC_FEATURE_NHWC_READER_H__ + +#include "../Reader.h" + +#include <cassert> + +#include "backend/ITensor.h" +#include "ir/Shape.h" +#include "util/Utils.h" + +namespace onert +{ +namespace exec +{ +namespace feature +{ +namespace nhwc +{ + +template <typename T> class Reader : public feature::Reader<T> +{ +public: + using Strides = ir::FeatureShape; + // Construct for buffer and strides + Reader(const ir::FeatureShape &shape, const Strides &strides, const T *ptr, size_t len) + : _shape{shape}, _strides{strides}, _ptr{reinterpret_cast<const uint8_t *>(ptr)}, _len{len} + { + UNUSED_RELEASE(len); // Workaround for unused variable in release mode + assert(len == static_cast<size_t>(strides.N != 0 + ? shape.N * strides.N + : strides.H != 0 ? shape.H * strides.H + : strides.W != 0 ? shape.W * strides.W + : shape.C * strides.C)); + } + + // Construct for backend tensor + Reader(const backend::ITensor *tensor) + : _ptr{tensor->buffer() + tensor->calcOffset({0, 0, 0, 0})}, _len{tensor->total_size()} + { + assert(tensor->layout() == ir::Layout::NHWC); + + const auto start_offset = tensor->calcOffset({0, 0, 0, 0}); + _strides.C = tensor->dimension(3) == 1 ? 0 : tensor->calcOffset({0, 0, 0, 1}) - start_offset; + _strides.W = tensor->dimension(2) == 1 ? 0 : tensor->calcOffset({0, 0, 1, 0}) - start_offset; + _strides.H = tensor->dimension(1) == 1 ? 0 : tensor->calcOffset({0, 1, 0, 0}) - start_offset; + _strides.N = tensor->dimension(0) == 1 ? 0 : tensor->calcOffset({1, 0, 0, 0}) - start_offset; + + _shape.C = tensor->dimension(3); + _shape.W = tensor->dimension(2); + _shape.H = tensor->dimension(1); + _shape.N = tensor->dimension(0); + } + +public: + T at(uint32_t batch, uint32_t row, uint32_t col, uint32_t ch) const final + { + return getRef(batch, row, col, ch); + } + T at(uint32_t row, uint32_t col, uint32_t ch) const final { return getRef(0, row, col, ch); } + +protected: + const T &getRef(uint32_t batch, uint32_t row, uint32_t col, uint32_t ch) const + { + const auto offset = feature_index_to_byte_offset(batch, row, col, ch); + + const T *ptr = reinterpret_cast<const T *>(_ptr + offset); + + return *ptr; + } + +private: + size_t feature_index_to_byte_offset(uint32_t batch, uint32_t row, uint32_t col, uint32_t ch) const + { + assert(1u * _shape.N > batch); // shape.N > batch + assert(1u * _shape.H > row); // shape.H > row + assert(1u * _shape.W > col); // shape.W > col + assert(1u * _shape.C > ch); // shape.C > ch + + uint32_t res = 0; + res += batch * _strides.N; + res += row * _strides.H; + res += col * _strides.W; + res += ch * _strides.C; + + return res; + } + +private: + // TODO Remove _shape + ir::FeatureShape _shape; + Strides _strides; + const uint8_t *_ptr; + size_t _len; +}; + +} // namespace nhwc +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_NHWC_READER_H__ diff --git a/runtime/onert/core/src/exec/feature/nhwc/View.h b/runtime/onert/core/src/exec/feature/nhwc/View.h new file mode 100644 index 000000000..a77f68024 --- /dev/null +++ b/runtime/onert/core/src/exec/feature/nhwc/View.h @@ -0,0 +1,72 @@ +/* + * 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_FEATURE_NHWC_VIEW_H__ +#define __ONERT_EXEC_FEATURE_NHWC_VIEW_H__ + +#include "../Reader.h" + +#include <cassert> +#include <cstddef> + +#include "backend/ITensor.h" +#include "ir/Shape.h" +#include "util/Utils.h" + +namespace onert +{ +namespace exec +{ +namespace feature +{ +namespace nhwc +{ + +template <typename T> class View final : public Reader<T> +{ +public: + using Strides = typename Reader<T>::Strides; + // Construct for buffer and strides + View(const ir::FeatureShape &shape, const Strides &strides, T *ptr, size_t len) + : Reader<T>{shape, strides, ptr, len} + { + // DO NOTHING + } + + // Construct for backend tensor + View(backend::ITensor *tensor) : Reader<T>{tensor} + { + // DO NOTHING + } + +public: + using Reader<T>::at; + T &at(uint32_t batch, uint32_t row, uint32_t col, uint32_t ch) + { + return const_cast<T &>(Reader<T>::getRef(batch, row, col, ch)); + } + T &at(uint32_t row, uint32_t col, uint32_t ch) + { + return const_cast<T &>(Reader<T>::getRef(0, row, col, ch)); + } +}; + +} // namespace nhwc +} // namespace feature +} // namespace exec +} // namespace onert + +#endif // __ONERT_EXEC_FEATURE_NHWC_VIEW_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..7f577ea6e --- /dev/null +++ b/runtime/onert/core/src/interp/ExecEnv.h @@ -0,0 +1,212 @@ +/* + * 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 + * can_optional @c True if tensor can be optional input, otherwise @c false + * @return Tensor pointer + */ + const ITensor *tensorAt(const ir::OperandIndex index, bool can_optional = false) const + { + if (_tensors.find(index) == _tensors.end()) + { + // It may optional input, + // otherwise input is not set by runtime user + if (can_optional) + { + return nullptr; + } + + throw std::runtime_error{"ExecEnv: Input is not set"}; + } + + 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; + } + + // Buffer from external (ex. model output) + auto tensor = std::make_shared<Tensor>(info); + if (isExtBuffer(index)) + { + tensor->setBuffer(_external_buffers.at(index)); + assignTensor(index, tensor); + + return; + } + + 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; + } + + if (isExtBuffer(index)) + { + auto tensor = std::make_shared<Tensor>(info); + tensor->setBuffer(_external_buffers.at(index)); + assignTensor(index, tensor); + } + 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(); + } + } + + /** + * @brief Assign ExternalBuffer into external buffer map + * @param[in] index Tensor index + * @param[in] buffer External buffer + */ + void assignExternalBuffer(const ir::OperandIndex index, std::shared_ptr<ExternalBuffer> buffer) + { + _external_buffers.emplace(index, buffer); + } + +private: + bool isExtBuffer(const ir::OperandIndex index) + { + return (_external_buffers.find(index) != _external_buffers.end()); + } + +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; + // Tensor buffer from external + std::unordered_map<ir::OperandIndex, std::shared_ptr<ExternalBuffer>> _external_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..cd31a4dca --- /dev/null +++ b/runtime/onert/core/src/interp/InterpExecutor.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 "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).get(); + if (input == nullptr) + { + // Optional input + continue; + } + + 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; + } + + /************************************************************************ + * 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()) + { + if (tensor_map.find(index) != tensor_map.end()) + { + VERBOSE(INTERPRETER) << "Assign input tensor. operand index:" << index.value() << std::endl; + interp_env->assignTensor(index, tensor_map.at(index)); + } + } + + 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).get(); + if (output == nullptr) + { + // Optional output + continue; + } + + VERBOSE(INTERPRETER) << "Set out buffer to ExecEnv. operand index:" << output_index.value() + << std::endl; + + interp_env->assignExternalBuffer( + output_index, std::make_shared<ExternalBuffer>(reinterpret_cast<uint8_t *>(output->buffer), + output->size)); + } + + // 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/InterpExecutor.h b/runtime/onert/core/src/interp/InterpExecutor.h new file mode 100644 index 000000000..2e3f3ca54 --- /dev/null +++ b/runtime/onert/core/src/interp/InterpExecutor.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 InterpExecutor.h + * @brief This file contains InterpExecutor class\n + * to manage interpreter execution and environment + */ +#ifndef __ONERT_INTERP_INTERP_EXECUTOR_H__ +#define __ONERT_INTERP_INTERP_EXECUTOR_H__ + +#include "ir/OperandIndexMap.h" +#include "ir/Graph.h" +#include "exec/IExecutor.h" + +namespace onert +{ +namespace interp +{ + +class ITensor; + +/** + * @brief Class to execute model using interpreter + */ +class InterpExecutor final : public exec::IExecutor +{ +public: + explicit InterpExecutor(const ir::Graph &graph) : _graph(graph) + { + // DO NOTHING + } + +public: + /** + * @brief Return graph object + * @return Graph object + */ + const ir::Graph &graph() final { return _graph; } + void setIndexedRanks(std::shared_ptr<ir::OperationIndexMap<int64_t>>) override{ + // Not implemented + }; + /** + * @brief Start execution + * @note It should be called after setting input and output buffer + */ + void execute(const exec::IODescription &desc) final; + +private: + const ir::Graph &_graph; + ir::OperandIndexMap<std::shared_ptr<ITensor>> _tensor_map; +}; + +} // namespace interp +} // namespace onert + +#endif // __ONERT_INTERP_INTERP_EXECUTOR_H__ diff --git a/runtime/onert/core/src/interp/InterpOps.lst b/runtime/onert/core/src/interp/InterpOps.lst new file mode 100644 index 000000000..0714df38a --- /dev/null +++ b/runtime/onert/core/src/interp/InterpOps.lst @@ -0,0 +1,73 @@ +/* + * 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(BinaryArithmetic) +//INTERP_OP(BatchToSpaceND) +//INTERP_OP(Cast) +INTERP_OP(Conv2D) +INTERP_OP(DepthwiseConv2D) +INTERP_OP(Pool2D) +INTERP_OP(Concat) +INTERP_OP(FullyConnected) +//INTERP_OP(Reduce) +INTERP_OP(Reshape) +INTERP_OP(Softmax) +//INTERP_OP(Squeeze) +//INTERP_OP(Slice) +//INTERP_OP(StridedSlice) +INTERP_OP(ElementwiseActivation) +//INTERP_OP(Transpose) +//INTERP_OP(Exp) +//INTERP_OP(Comparison) +//INTERP_OP(LogicalNot) +//INTERP_OP(LSTM) +//INTERP_OP(RSQRT) +//INTERP_OP(ResizeBilinear) +//INTERP_OP(RNN) +//INTERP_OP(Floor) +//INTERP_OP(SpaceToBatchND) +//INTERP_OP(SpaceToDepth) +//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(LocalResponseNormalization) +//INTERP_OP(DepthToSpace) +//INTERP_OP(Pack) +//INTERP_OP(Split) +//INTERP_OP(Unpack) +INTERP_OP(Pad) +//INTERP_OP(Custom) +//INTERP_OP(Permute) +//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..b92afbe73 --- /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.find(nodeOpCode) == _kernels.end()) + { + throw std::runtime_error{"Interpreter: Operation " + nodeName + " is not yet implemented"}; + } + + 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; + const auto use_operators = _env->graph().operands().at(current_operand_index).getUses(); + for (const 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 (const auto &use_operator : use_operators) + { + 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..8b72d537d --- /dev/null +++ b/runtime/onert/core/src/interp/Tensor.h @@ -0,0 +1,183 @@ +/* + * 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 is_dynamic() const override { return false; } + bool has_padding() const override { return false; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + float data_scale() const override { return _info.typeInfo().scale(); } + int32_t data_offset() const override { return _info.typeInfo().offset(); } + 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 is_dynamic() const override { return false; } + bool has_padding() const override { return false; } + ir::DataType data_type() const override { return _info.typeInfo().type(); } + float data_scale() const override { return _info.typeInfo().scale(); } + int32_t data_offset() const override { return _info.typeInfo().offset(); } + 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/BinaryArithmeticOps.cc b/runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc new file mode 100644 index 000000000..86e883524 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/BinaryArithmeticOps.cc @@ -0,0 +1,205 @@ +/* + * 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/BinaryArithmetic.h" +#include "misc/polymorphic_downcast.h" +#include "cker/Types.h" + +namespace onert +{ +namespace interp +{ +namespace +{ + +enum class OpType +{ + ADD, + SUB, + MUL +}; + +void prepare(ExecEnv *env, const ir::Operation &node) +{ + const auto &arithmetic_node = + nnfw::misc::polymorphic_downcast<const ir::operation::BinaryArithmetic &>(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); + + // 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(" + arithmetic_node.name() + "): 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(" + arithmetic_node.name() + "): Fail to brodcasting"}; + } + + auto output_info = + ir::OperandInfo::createStaticInfo(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(" + arithmetic_node.name() + "): 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, OpType op_type> +void invoke(const ITensor *lhs_tensor, const ITensor *rhs_tensor, const ITensor *out_tensor, + const ir::operation::BinaryArithmetic::Param ¶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); + + const auto cker_op_type = + (op_type == OpType::ADD) + ? nnfw::cker::BinaryArithmeticOpType::ADD + : ((op_type == OpType::SUB) ? nnfw::cker::BinaryArithmeticOpType::SUB + : nnfw::cker::BinaryArithmeticOpType::MUL); + + const bool need_broadcast = nnfw::cker::ProcessBroadcastShapes( + convertShape(lhs_tensor->tensorInfo().shape()), + convertShape(rhs_tensor->tensorInfo().shape()), &cker_param); + + if (need_broadcast) + { + 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::BroadcastBinaryArithmeticOp<cker_op_type>(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_op_type>(cker_param, lhs_shape, lhs_ptr, rhs_shape, rhs_ptr, + out_shape, out_ptr); +} + +template <OpType op_type> +void invokeBinaryArithmetic(const ExecEnv *env, const ir::operation::BinaryArithmetic &node) +{ + const auto lhs_index = node.getInputs().at(node.LHS); + const auto rhs_index = node.getInputs().at(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, op_type>(lhs_tensor, rhs_tensor, out_tensor, node.param()); + } + else if (data_type == ir::DataType::FLOAT32) + { + invoke<float, op_type>(lhs_tensor, rhs_tensor, out_tensor, node.param()); + } + else + { + throw std::runtime_error{"NYI: Unsupported data type"}; + } +} + +void invokeBinaryArithmeticOps(const ExecEnv *env, const ir::Operation &node) +{ + const auto &arithmetic_node = + nnfw::misc::polymorphic_downcast<const ir::operation::BinaryArithmetic &>(node); + + switch (arithmetic_node.param().arithmetic_type) + { + case ir::operation::BinaryArithmetic::ArithmeticType::ADD: + invokeBinaryArithmetic<OpType::ADD>(env, arithmetic_node); + break; + case ir::operation::BinaryArithmetic::ArithmeticType::SUB: + invokeBinaryArithmetic<OpType::SUB>(env, arithmetic_node); + break; + case ir::operation::BinaryArithmetic::ArithmeticType::MUL: + invokeBinaryArithmetic<OpType::MUL>(env, arithmetic_node); + break; + default: + throw std::runtime_error{"Interp(BinaryArithmetic): NYI unsupported operation " + + arithmetic_node.name()}; + break; + } +} + +} // namespace + +OpKernel *getBinaryArithmetic() +{ + static OpKernel kernel = {prepare, invokeBinaryArithmeticOps}; + 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..efc46c66b --- /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::createStaticInfo( + 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..bb00b828c --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Conv2D.cc @@ -0,0 +1,151 @@ +/* + * 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_shape = shape_inference::inferConv2DShape( + in_tensor->tensorInfo().shape(), kernel_tensor->tensorInfo().shape(), conv_node.param()); + env->allocateIfNeeded( + out_index, ir::OperandInfo::createStaticInfo(infered_output_shape, 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..0473855d9 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/DepthwiseConv2D.cc @@ -0,0 +1,156 @@ +/* + * 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_shape = shape_inference::inferDepthwiseConv2DShape( + in_tensor->tensorInfo().shape(), kernel_tensor->tensorInfo().shape(), + depth_conv_node.param()); + env->allocateIfNeeded( + out_index, ir::OperandInfo::createStaticInfo(infered_output_shape, 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/ElementwiseActivations.cc b/runtime/onert/core/src/interp/operations/ElementwiseActivations.cc new file mode 100644 index 000000000..c8773bef4 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/ElementwiseActivations.cc @@ -0,0 +1,161 @@ +/* + * 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/ElementwiseActivation.h" + +#include <misc/polymorphic_downcast.h> +#include <cker/operation/Logistic.h> +#include <cker/operation/Tanh.h> + +namespace onert +{ +namespace interp +{ +namespace +{ + +enum class ActivationType +{ + Logistic, + ReLU, + 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(ElementwiseActivation): Invalid output type"}; + } +} + +template <ActivationType act_type> +void evalFloat(const float *input_ptr, float *output_ptr, uint64_t num_elements, float alpha, + float beta) +{ + std::function<float(const float &)> fn = [](const float &) { return std::nanf(""); }; + switch (act_type) + { + case ActivationType::ReLU: + fn = [alpha, beta](const float &in) { return std::min(std::max(beta, in), alpha); }; + break; + case ActivationType::Tanh: + fn = [](const float &in) { return std::tanh(in); }; + break; + default: + throw std::runtime_error{"Interp(ElementwiseActivation): 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()); + if (act_type == ActivationType::Logistic) + { + const auto cker_input_shape = convertShape(input_tensor->tensorInfo().shape()); + const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape()); + nnfw::cker::Logistic(cker_input_shape, input_start, cker_output_shape, out); + } + else + { + const auto &act_node = + nnfw::misc::polymorphic_downcast<const ir::operation::ElementwiseActivation &>(node); + evalFloat<act_type>(input_start, out, elements, act_node.param().alpha, + act_node.param().beta); + } + } + else + { + throw std::runtime_error{"Interp(" + node.name() + "): NYI - Support float only"}; + } +} + +void invokeElementwiseActivation(const ExecEnv *env, const ir::Operation &node) +{ + const auto &act_node = + nnfw::misc::polymorphic_downcast<const ir::operation::ElementwiseActivation &>(node); + switch (act_node.param().op_type) + { + case ir::operation::ElementwiseActivation::Type::LOGISTIC: + invoke<ActivationType::Logistic>(env, node); + break; + case ir::operation::ElementwiseActivation::Type::RELU: + invoke<ActivationType::ReLU>(env, node); + break; + case ir::operation::ElementwiseActivation::Type::TANH: + invoke<ActivationType::Tanh>(env, node); + break; + default: + throw std::runtime_error("Interp(" + node.name() + "): NYI - Unsupported activation"); + } +} + +} // namespace + +OpKernel *getElementwiseActivation() +{ + static OpKernel kernel = {prepare, invokeElementwiseActivation}; + 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..4f97632b2 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/FullyConnected.cc @@ -0,0 +1,134 @@ +/* + * 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 auto out_info = + ir::OperandInfo::createStaticInfo(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); + 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..9e82def5f --- /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::QUANT_UINT8_ASYMM && + input_tensor->tensorInfo().typeInfo() != output_tensor->tensorInfo().typeInfo()) + { + throw std::runtime_error{ + "Interp(Gather): Cannot handle different I/O QUANT_UINT8_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::QUANT_UINT8_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/OperationUtil.h b/runtime/onert/core/src/interp/operations/OperationUtil.h new file mode 100644 index 000000000..2fdf098f0 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/OperationUtil.h @@ -0,0 +1,203 @@ +/* + * 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(dimensions.size()); + + for (uint32_t i = 0; i < dimensions.size(); ++i) + { + 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()); + + const int32_t extended_rank = 4; + int32_t raw_shape[extended_rank]; + uint32_t start = extended_rank - dimensions.size(); + + for (uint32_t i = 0; i < extended_rank; ++i) + { + if (i < start) + { + raw_shape[i] = 1; + } + else + { + raw_shape[i] = dimensions[i - start]; + } + } + + return nnfw::cker::Shape(extended_rank, 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..c8dce698d --- /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<float>(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/Pool2D.cc b/runtime/onert/core/src/interp/operations/Pool2D.cc new file mode 100644 index 000000000..92f9d70b2 --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Pool2D.cc @@ -0,0 +1,140 @@ +/* + * 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 <cker/operation/MaxPool.h> + +#include "OperationUtil.h" + +#include "interp/Registration.h" +#include "ir/operation/Pool2D.h" +#include "util/Utils.h" +#include "util/ShapeInference.h" +#include "misc/polymorphic_downcast.h" + +namespace onert +{ +namespace interp +{ +namespace pool2d +{ + +void preparePool2D(ExecEnv *env, const ir::Operation &node) +{ + const auto &pool_node = nnfw::misc::polymorphic_downcast<const ir::operation::Pool2D &>(node); + const auto in_index = node.getInputs().at(pool_node.INPUT); + 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 infered_output_shape = + shape_inference::inferPoolShape(in_tensor->tensorInfo().shape(), pool_node.param()); + env->allocateIfNeeded( + out_index, ir::OperandInfo::createStaticInfo(infered_output_shape, 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); +} + +template <typename T> +void invoke(const nnfw::cker::PoolParams ¶ms, const nnfw::cker::Shape &in_shape, + const T *in_ptr, const nnfw::cker::Shape &out_shape, T *out_ptr, + ir::operation::Pool2D::PoolType op_type) +{ + switch (op_type) + { + case ir::operation::Pool2D::PoolType::AVG: + nnfw::cker::AveragePool<T>(params, in_shape, in_ptr, out_shape, out_ptr); + break; + case ir::operation::Pool2D::PoolType::MAX: + nnfw::cker::MaxPool<T>(params, in_shape, in_ptr, out_shape, out_ptr); + break; + default: + throw std::runtime_error{"Interp(Pool2D): NYI unsupported operation"}; + break; + } +} + +void invokePool2DOps(const ExecEnv *env, const ir::Operation &node) +{ + const auto &pool_node = nnfw::misc::polymorphic_downcast<const ir::operation::Pool2D &>(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); + + // 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 param = pool_node.param(); + const auto padding = + ir::calculatePadding(param.padding, ifm_shape, ofm_shape, param.stride, param.kw, param.kh); + // Calculate + nnfw::cker::PoolParams cker_param; + 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 data_type = in_tensor->data_type(); + if (data_type == ir::DataType::FLOAT32) + { + calculateActivationRange(param.activation, &cker_param.float_activation_min, + &cker_param.float_activation_max); + + 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()); + // Now, invoke() supports only Pool2D in float + invoke<float>(cker_param, in_shape, in_ptr, out_shape, out_ptr, param.op_type); + } + else + { + throw std::runtime_error{"NYI: Support float only"}; + } +} +} // namespace pool2d + +OpKernel *getPool2D() +{ + static OpKernel kernel = {pool2d::preparePool2D, pool2d::invokePool2DOps}; + 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..d30f78deb --- /dev/null +++ b/runtime/onert/core/src/interp/operations/Softmax.cc @@ -0,0 +1,123 @@ +/* + * 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 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 auto output_info = ir::OperandInfo::createStaticInfo(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); + + nnfw::cker::Softmax(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/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/DataType.cc b/runtime/onert/core/src/ir/DataType.cc new file mode 100644 index 000000000..9eedcd21a --- /dev/null +++ b/runtime/onert/core/src/ir/DataType.cc @@ -0,0 +1,58 @@ +/* + * 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/DataType.h" + +#include <stdexcept> +#include <Half.h> + +using float16 = Half; + +namespace onert +{ +namespace ir +{ + +size_t sizeOfDataType(DataType data_type) +{ + switch (data_type) + { + case DataType::FLOAT32: + return sizeof(float); + case DataType::INT32: + return sizeof(int32_t); + case DataType::UINT32: + return sizeof(uint32_t); + case DataType::BOOL8: + case DataType::QUANT_UINT8_ASYMM: + case DataType::UINT8: + return sizeof(uint8_t); + case DataType::QUANT_INT8_SYMM: + case DataType::QUANT_INT8_ASYMM: + return sizeof(int8_t); + case DataType::FLOAT16: + return sizeof(float16); + case DataType::INT64: + return sizeof(int64_t); + case DataType::QUANT_INT16_ASYMM: + return sizeof(int16_t); + default: + throw std::runtime_error{"Unsupported type size"}; + } +} + +} // 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..1b8300f40 --- /dev/null +++ b/runtime/onert/core/src/ir/Graph.cc @@ -0,0 +1,158 @@ +/* + * 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 "OperationValidator.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/OperandIndexMap.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, const std::string &name) +{ + assert(isBuildingPhase()); + if (!name.empty()) + _name_to_input.emplace(name, IOIndex{_inputs.size()}); + _inputs.append(ind); +} + +void Graph::addOutput(const OperandIndex &ind, const std::string &name) +{ + assert(isBuildingPhase()); + if (!name.empty()) + _name_to_output.emplace(name, IOIndex{_outputs.size()}); + _outputs.append(ind); +} + +IOIndex Graph::getInputIndex(const std::string &name) const +{ + auto itr = _name_to_input.find(name); + return (itr == _name_to_input.end()) ? IOIndex{} : itr->second; +} + +IOIndex Graph::getOutputIndex(const std::string &name) const +{ + auto itr = _name_to_output.find(name); + return (itr == _name_to_output.end()) ? IOIndex{} : itr->second; +} + +void Graph::finishBuilding(void) +{ + assert(isBuildingPhase()); + _phase = Phase::MODEL; + + initializeUseDef(); + sweepGarbageOperands(); + + // Call graph verifications for the MODEL phase + { + // Except for edge consistency, the user might have been given a bad model + // so here it throws an execption rather than assertion. + if (!verifier::InputOutputChecker().verify(*this)) + throw std::runtime_error{"One of model input and output operands does not exist."}; + if (!verifier::DAGChecker().verify(*this)) + throw std::runtime_error{"The graph is cyclic."}; + assert(verifier::EdgeConsistencyChecker().verify(*this)); + } + + // Check shape independent operation feature + // - Operand type + // - Shape independent parameter + OperationValidator{*this}(); +} + +void Graph::initializeUseDef() +{ + operations().iterate([&](const OperationIndex &index, const Operation &node) -> void { + auto outputs = node.getOutputs(); + for (auto output : outputs | ir::Remove::UNDEFINED) + { + operands().at(output).setDef(index); + } + + for (auto input : node.getInputs() | ir::Remove::UNDEFINED) + { + operands().at(input).insertUse(index); + } + }); +} + +void Graph::sweepGarbageOperands() +{ + // Remove operands that are not used by any operations, except Graph inputs/outputs + ir::OperandIndexMap<bool> visited; + + operations().iterate([&](const OperationIndex &, const Operation &node) { + for (auto ind : node.getInputs() + node.getOutputs()) + { + visited[ind] = true; + } + }); + + // Graph's inputs/outputs are always reachable + for (auto ind : getInputs() + getOutputs()) + { + visited[ind] = true; + } + + operands().iterate([&](const OperandIndex &ind, const Operand &) { + if (!visited[ind]) + { + VERBOSE(Graph::sweepGarbageOperands) << "Sweep garbage operand " << ind.value() << std::endl; + operands().remove(ind); + } + }); +} + +} // 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..ac67771c4 --- /dev/null +++ b/runtime/onert/core/src/ir/GraphIterator.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 "GraphIterator.h" + +#include "ir/OperationIndexMap.h" +#include "compiler/LoweredGraph.h" + +namespace onert +{ +namespace ir +{ + +// +// 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 (const auto output : node.getOutputs() | Remove::DUPLICATED | Remove::UNDEFINED) + { + const auto &operand = graph.operands().at(output); + for (const auto &use : operand.getUses()) + { + 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; })); +} + +template <bool is_const> +void PostDfsIterator<is_const>::iterateOpSeqs(LoweredGraphRef lowered_graph, + const OpSeqIterFn &fn) const +{ + std::unordered_map<OpSequenceIndex, bool> visited; + lowered_graph.op_seqs().iterate( + [&](const OpSequenceIndex &index, OpSequenceRef) { visited[index] = false; }); + + std::function<void(const OpSequenceIndex &, OpSequenceRef)> dfs_recursive = + [&](const OpSequenceIndex &index, OpSequenceRef op_seq) -> void { + if (visited[index]) + return; + visited[index] = true; + + for (const auto output : op_seq.getOutputs() | Remove::DUPLICATED | Remove::UNDEFINED) + { + const auto &operand = lowered_graph.graph().operands().at(output); + for (const auto &use : operand.getUses()) + { + const auto use_op_seq_index = lowered_graph.op_seqs().getOperation(use); + dfs_recursive(use_op_seq_index, lowered_graph.op_seqs().at(use_op_seq_index)); + } + } + + fn(index, op_seq); + }; + + lowered_graph.op_seqs().iterate(dfs_recursive); + + // All of the operations(nodes) must have been visited. + assert(std::all_of(visited.begin(), visited.end(), + [](const std::pair<const OpSequenceIndex, bool> &v) { return v.second; })); +} + +// Explicit instantiations to have implementation in the source file. +// NOTE If these instatiations were in the top of this file, `iterate` is compiled and saved in +// `GraphIterator.cc.o` but `iterateOpSeqs`. This happens only when cross-building for Android. +// (Maybe a bug of NDK toolchain(clang)?) + +template class DefaultIterator<true>; +template class DefaultIterator<false>; + +template class PostDfsIterator<true>; +template class PostDfsIterator<false>; + +} // 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..b54314e0e --- /dev/null +++ b/runtime/onert/core/src/ir/GraphIterator.h @@ -0,0 +1,90 @@ +/* + * 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 compiler +{ +class LoweredGraph; +} // namespace compiler +} // namespace onert + +namespace onert +{ +namespace ir +{ + +class Graph; +class Operation; +class OpSequence; + +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; + using LoweredGraphRef = + typename std::conditional<is_const, const typename compiler::LoweredGraph &, + typename compiler::LoweredGraph &>::type; + using OpSequenceRef = typename std::conditional<is_const, const OpSequence &, OpSequence &>::type; + using OpSeqIndexRef = const OpSequenceIndex &; + using OpSeqIterFn = std::function<void(OpSeqIndexRef, OpSequenceRef)>; + +public: + void iterate(GraphRef graph, const IterFn &fn) const; + void iterateOpSeqs(LoweredGraphRef lowered_graph, const OpSeqIterFn &f) 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/OpCode.cc b/runtime/onert/core/src/ir/OpCode.cc new file mode 100644 index 000000000..ef3411f6d --- /dev/null +++ b/runtime/onert/core/src/ir/OpCode.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/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); +} + +OpCode toOpCode(const std::string str) +{ + static const std::unordered_map<std::string, OpCode> map{ +#define OP(Name) {#Name, OpCode::Name}, +#include "ir/Operations.lst" +#undef OP + }; + return map.at(str); +} + +} // 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..e2b989d8c --- /dev/null +++ b/runtime/onert/core/src/ir/OpSequence.cc @@ -0,0 +1,95 @@ +/* + * 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/Operations.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}, _has_dynamic_tensor{false} +{ + // DO NOTHING +} + +void OpSequence::accept(OperationVisitor &v) const { v.visit(*this); } + +// TODO: Impl Dumper instead of this method +std::string getStrFromOpSeq(const OpSequence &op_seq, const Operations &operations) +{ + // " 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(op_seq.getInputs()) << ") -> {"; + for (const auto &op_idx : op_seq) + { + ss << " " << op_idx.value() << "(" << operations.at(op_idx).name() << ":" + << getStrFromIndice(operations.at(op_idx).getInputs()) << ":" + << getStrFromIndice(operations.at(op_idx).getOutputs()) << ")"; + } + ss << " } -> OUT(" << getStrFromIndice(op_seq.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) + { + _operations.erase(it); + break; + } + } +} + +bool OpSequence::exist(const OperationIndex &index) const +{ + for (const auto &inner_op_idx : _operations) + { + if (inner_op_idx == 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..68884783e --- /dev/null +++ b/runtime/onert/core/src/ir/OpSequences.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 "ir/OpSequences.h" +#include "util/logging.h" +#include <memory> + +#include <cassert> +#include <string> + +namespace onert +{ +namespace ir +{ + +OpSequenceIndex OpSequences::emplace(const OperationIndex &index, Layout layout) +{ + std::unique_ptr<OpSequence> op_seq = std::make_unique<OpSequence>(layout); + op_seq->appendOperation(index); + const OpSequenceIndex &seq_index = push(std::move(op_seq)); + cacheSequenceIndex(seq_index, index); + return seq_index; +} + +OpSequenceIndex OpSequences::emplace(std::unique_ptr<OpSequence> &&op_seq) +{ + auto &operations = op_seq->operations(); + const OpSequenceIndex &seq_index = push(std::move(op_seq)); + for (const auto &op_idx : operations) + { + cacheSequenceIndex(seq_index, op_idx); + } + return seq_index; +} + +void OpSequences::cacheSequenceIndex(const OpSequenceIndex &seq_index, + const OperationIndex &op_index) const +{ + _seq_indexes.emplace(op_index, seq_index); +} + +OpSequenceIndex *OpSequences::findSequenceIndex(const OperationIndex &operation_index) const +{ + // If opration_index is cached, return sequence_index from cache + if (_seq_indexes.count(operation_index)) + { + auto &op_seq_index = _seq_indexes.at(operation_index); + if (_objects.count(op_seq_index) && _objects.at(op_seq_index)->exist(operation_index)) + { + return &op_seq_index; + } + else + { + _seq_indexes.erase(operation_index); + return nullptr; + } + } + return nullptr; +} + +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; +} + +void OpSequences::removeFromOpSequence(const OperationIndex &operation_index) +{ + const auto op_seq_index = findOperation(operation_index); + auto &op_seq = at(op_seq_index); + _seq_indexes.erase(operation_index); + op_seq.remove(operation_index); + if (op_seq.size() == 0) + { + remove(op_seq_index); + } +} + +OpSequenceIndex OpSequences::findOperation(const OperationIndex &operation_index) const +{ + if (OpSequenceIndex *op_seq_index = findSequenceIndex(operation_index)) + return *op_seq_index; + + for (auto &e : _objects) + { + OpSequence &object = *e.second; + auto it = find(object.operations().begin(), object.operations().end(), operation_index); + if (it != object.operations().end()) + { + cacheSequenceIndex(e.first, operation_index); + return e.first; + } + } + throw std::runtime_error("Operation not found"); +} + +void dumpOpSequences(const OpSequences &op_seqs, const Operations &operations) +{ + op_seqs.iterate([&](const OpSequenceIndex &idx, const OpSequence &op_seq) { + VERBOSE(OpSequences) << idx.value() << "] " << getStrFromOpSeq(op_seq, operations) << std::endl; + }); +} + +} // 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..e29c7a6ec --- /dev/null +++ b/runtime/onert/core/src/ir/Operand.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/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::insertUse(const OperationIndex &idx) { _uses.insert(idx); } + +void Operand::removeUse(const OperationIndex &idx) { _uses.remove(idx); } + +void Operand::setDef(const OperationIndex &idx) { _def = idx; } + +void Operand::unsetDef() { _def = OperationIndex{}; } + +} // 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..73f928280 --- /dev/null +++ b/runtime/onert/core/src/ir/OperandIndexSequence.cc @@ -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. + */ + +#include "ir/OperandIndexSequence.h" + +#include <algorithm> +#include <sstream> + +namespace onert +{ +namespace ir +{ + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<OperandIndex> list) : _vec(list) +{ + // DO NOTHING +} + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<int32_t> list) +{ + for (auto val : list) + { + _vec.emplace_back(static_cast<uint32_t>(val)); + } +} + +OperandIndexSequence::OperandIndexSequence(std::initializer_list<uint32_t> list) +{ + for (auto val : list) + { + _vec.emplace_back(val); + } +} + +bool OperandIndexSequence::contains(const OperandIndex &index) const +{ + return std::find(_vec.begin(), _vec.end(), index) != _vec.end(); +} + +void OperandIndexSequence::replace(const OperandIndex &from, const OperandIndex &to) +{ + std::replace(_vec.begin(), _vec.end(), from, to); +} + +OperandIndexSequence OperandIndexSequence::operator+(const OperandIndexSequence &other) const +{ + OperandIndexSequence ret = *this; + ret.append(other); + return ret; +} + +std::ostream &operator<<(std::ostream &o, const OperandIndexSequence &op_seq) +{ + std::string delimeter; + for (const auto &ind : op_seq._vec) + { + o << delimeter << ind; + delimeter = ','; + } + return o; +} + +} // 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..4af878541 --- /dev/null +++ b/runtime/onert/core/src/ir/Operation.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 "ir/Operation.h" + +#include <cassert> + +namespace onert +{ +namespace ir +{ + +Operation::Operation(OperandConstraint input_constr, const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, OperandConstraint output_constr) + : _input_constr{input_constr}, _output_constr{output_constr} +{ + setInputs(inputs); + setOutputs(outputs); +} + +Operation::Operation(OperandConstraint input_constr, OperandConstraint output_constr) + : _input_constr{input_constr}, _output_constr{output_constr} +{ +} + +Operation::~Operation() = default; + +void Operation::setInputs(const OperandIndexSequence &indexes) +{ + if (!_input_constr.check(indexes.size())) + throw std::runtime_error{"Invalid number of input tensors for this operation."}; + _inputs = indexes; +} + +void Operation::setOutputs(const OperandIndexSequence &indexes) +{ + if (!_output_constr.check(indexes.size())) + throw std::runtime_error{"Invalid number of output tensors for this operation."}; + _outputs = indexes; +} + +void Operation::replaceInputs(const OperandIndex &from, const OperandIndex &to) +{ + _inputs.replace(from, to); +} + +void Operation::replaceOutputs(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..eecfe81cc --- /dev/null +++ b/runtime/onert/core/src/ir/OperationDumper.cc @@ -0,0 +1,521 @@ +/* + * 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; + +namespace +{ +void dumpUnaryInputOp(const Operation &node, const std::string &adding_input = "") +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(0) << ") " << adding_input + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void dumpBinaryInputOp(const Operation &node, const std::string &adding_input = "") +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(0) << ", " << node.getInputs().at(1) + << ") " << adding_input << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void dumpConvOp(const Operation &node, const std::string &padding_type) +{ + VERBOSE(LIR) << "* " << node.name() << "(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(Conv2D::Input::INPUT) << ") Kernel(" + << node.getInputs().at(Conv2D::Input::KERNEL) << ") Bias(" + << node.getInputs().at(Conv2D::Input::BIAS) << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void dumpPackingOp(const Operation &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + std::string inputs; + for (auto i : node.getInputs()) + { + inputs += std::to_string(i.value()) + ","; + } + VERBOSE(LIR) << " - Inputs : Inputs(" << inputs << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} +} // namespace + +OperationDumper::OperationDumper(const std::string &start_msg) +{ + VERBOSE(LIR) << start_msg << std::endl; +} + +void OperationDumper::visit(const ArgMax &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const BatchToSpaceND &node) +{ + std::string block_size = + "BlockSize(" + + std::to_string(node.getInputs().at(BatchToSpaceND::Input::BLOCK_SIZE).value()) + ")"; + dumpUnaryInputOp(node, block_size); +} + +void OperationDumper::visit(const BCQFullyConnected &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(BCQFullyConnected::Input::INPUT) + << ") WeightsBinary(" + << node.getInputs().at(BCQFullyConnected::Input::WEIGHTS_BINARY) + << ") WeightsScales(" + << node.getInputs().at(BCQFullyConnected::Input::WEIGHTS_SCALES) + << ") WeightsClusters(" + << node.getInputs().at(BCQFullyConnected::Input::WEIGHTS_CLUSTERS) << ") Bias(" + << node.getInputs().at(BCQFullyConnected::Input::BIAS) << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const BinaryArithmetic &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const operation::BroadcastTo &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const Comparison &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const Concat &node) { dumpPackingOp(node); } + +void OperationDumper::visit(const Conv2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + dumpConvOp(node, padding_type); +} + +void OperationDumper::visit(const ConvertFp16ToFp32 &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const ConvertFp32ToFp16 &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const DepthToSpace &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const DepthwiseConv2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + dumpConvOp(node, padding_type); +} + +void OperationDumper::visit(const ElementwiseActivation &node) +{ + std::string params; + if (node.param().op_type == ElementwiseActivation::Type::RELU) + { + params = " lower value(" + std::to_string(node.param().alpha) + ") upper value(" + + std::to_string(node.param().beta) + ")"; + } + else if (node.param().op_type == ElementwiseActivation::Type::LEAKY_RELU) + { + params = " alpha value(" + std::to_string(node.param().alpha) + ")"; + } + dumpUnaryInputOp(node, params); +} + +void OperationDumper::visit(const ElementwiseBinary &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const ElementwiseUnary &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const EmbeddingLookup &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : Lookups(" << node.getInputs().at(EmbeddingLookup::Input::LOOKUPS) + << ") VALUES(" << node.getInputs().at(EmbeddingLookup::Input::VALUES) << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const ExpandDims &node) +{ + std::string axis = + "AXIS(" + std::to_string(node.getInputs().at(ExpandDims::Input::AXIS).value()) + ")"; + dumpUnaryInputOp(node, axis); +} + +void OperationDumper::visit(const FullyConnected &node) +{ + std::string inputs = + "Weight(" + std::to_string(node.getInputs().at(FullyConnected::Input::WEIGHT).value()) + + ") Bias(" + std::to_string(node.getInputs().at(FullyConnected::Input::BIAS).value()) + ")"; + dumpUnaryInputOp(node, inputs); +} + +void OperationDumper::visit(const Gather &node) +{ + std::string indices = + "Indices(" + std::to_string(node.getInputs().at(Gather::Input::INDICES).value()) + ")"; + dumpUnaryInputOp(node, indices); +} + +void OperationDumper::visit(const HashtableLookup &node) +{ + VERBOSE(LIR) << "* HashTableLookup" << std::endl; + VERBOSE(LIR) << " - Inputs : Lookups(" << node.getInputs().at(HashtableLookup::Input::LOOKUPS) + << ") Keys(" << node.getInputs().at(HashtableLookup::Input::KEYS) << ") Values(" + << node.getInputs().at(HashtableLookup::Input::VALUES) << ")" << std::endl; + VERBOSE(LIR) << " - Outputs : Output(" << node.getInputs().at(HashtableLookup::Output::OUTPUT) + << ") Hits(" << node.getInputs().at(HashtableLookup::Output::HITS) << ")" + << std::endl; +} + +void OperationDumper::visit(const InstanceNorm &node) +{ + std::string inputs = + "Gamma(" + std::to_string(node.getInputs().at(InstanceNorm::Input::GAMMA).value()) + + ") Beta(" + std::to_string(node.getInputs().at(InstanceNorm::Input::BETA).value()) + ")"; + dumpUnaryInputOp(node, inputs); +} + +void OperationDumper::visit(const L2Normalization &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const LocalResponseNormalization &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const LSTM &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) + << " - Inputs : Input(" << node.getInputs().at(LSTM::Input::INPUT) + << ") Input To Input Weights(" << node.getInputs().at(LSTM::Input::INPUT_TO_INPUT_WEIGHTS) + << ") Input To Forget Weights(" << node.getInputs().at(LSTM::Input::INPUT_TO_FORGET_WEIGHTS) + << ") Input To Cell Weights(" << node.getInputs().at(LSTM::Input::INPUT_TO_CELL_WEIGHTS) + << ") Input To Output Weights(" << node.getInputs().at(LSTM::Input::INPUT_TO_OUTPUT_WEIGHTS) + << ") Recurrent To Input Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_INPUT_WEIGHTS) + << ") Recurrent To Forget Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_FORGET_WEIGHTS) + << ") Recurrent To Cell Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_CELL_WEIGHTS) + << ") Recurrent To Output Weights(" + << node.getInputs().at(LSTM::Input::RECURRENT_TO_OUTPUT_WEIGHTS) << ") Cell To Input Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_INPUT_WEIGHTS) << ") Cell To Forget Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_FORGET_WEIGHTS) << ") Cell To OUTPUT Weights(" + << node.getInputs().at(LSTM::Input::CELL_TO_OUTPUT_WEIGHTS) << ") Input Gate Bias(" + << node.getInputs().at(LSTM::Input::INPUT_GATE_BIAS) << ") Forget Gate Bias(" + << node.getInputs().at(LSTM::Input::FORGET_GATE_BIAS) << ") Cell Bias(" + << node.getInputs().at(LSTM::Input::CELL_BIAS) << ") Output Gate Bias(" + << node.getInputs().at(LSTM::Input::OUTPUT_GATE_BIAS) << ") Projection Weights(" + << node.getInputs().at(LSTM::Input::PROJECTION_WEIGHTS) << ") Projection Bias(" + << node.getInputs().at(LSTM::Input::PROJECTION_BIAS) << ") Output State In(" + << node.getInputs().at(LSTM::Input::OUTPUT_STATE_IN) << ") Cell State In(" + << node.getInputs().at(LSTM::Input::CELL_STATE_IN); + if (node.getInputs().size() == 24) + { + VERBOSE(LIR) << ") Input Layer Normalization Weights(" + << node.getInputs().at(LSTM::Input::INPUT_LAYER_NORMALIZATION_WEIGHTS) + << ") Forget Layer Normalization Weights(" + << node.getInputs().at(LSTM::Input::FORGET_LAYER_NORMALIZATION_WEIGHTS) + << ") Cell Layer Normalization Weights(" + << node.getInputs().at(LSTM::Input::CELL_LAYER_NORMALIZATION_WEIGHTS) + << ") Ouput Layer Normalization Weights(" + << node.getInputs().at(LSTM::Input::OUTPUT_LAYER_NORMALIZATION_WEIGHTS); + } + VERBOSE(LIR) << ")" << std::endl; + VERBOSE(LIR) << " - Output : Scratch Buffer(" + << node.getOutputs().at(LSTM::Output::SCRATCH_BUFFER) << ") Output State Out(" + << node.getOutputs().at(LSTM::Output::OUTPUT_STATE_OUT) << ") Cell State Out(" + << node.getOutputs().at(LSTM::Output::CELL_STATE_OUT) << ") Output(" + << node.getOutputs().at(LSTM::Output::OUTPUT) << ")" << std::endl; +} + +void OperationDumper::visit(const Pack &node) { dumpPackingOp(node); } + +void OperationDumper::visit(const Pad &node) +{ + std::string pad = "Pad(" + std::to_string(node.getInputs().at(Pad::Input::PAD).value()) + ")"; + dumpUnaryInputOp(node, pad); +} + +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) << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const Pool2D &node) +{ + std::string padding_type = + node.param().padding.type == PaddingType::EXPLICIT ? "Explicit" : "Implicit"; + VERBOSE(LIR) << "* " << node.name() << "(" << padding_type << ")" << std::endl; + VERBOSE(LIR) << " - Inputs : IFM(" << node.getInputs().at(Pool2D::Input::INPUT) << ")" + << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const Pow &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const PReLU &node) +{ + std::string alpha = + "Alpha(" + std::to_string(node.getInputs().at(PReLU::Input::ALPHA).value()) + ")"; + dumpUnaryInputOp(node, alpha); +} + +void OperationDumper::visit(const Rank &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Reduce &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Reshape &node) +{ + // optional param + std::string shape = + node.getInputs().size() == 2 + ? "Shape(" + std::to_string(node.getInputs().at(Reshape::Input::SHAPE).value()) + ")" + : "Shape(not provided)"; + dumpUnaryInputOp(node, shape); +} + +void OperationDumper::visit(const ResizeBilinear &node) +{ + if (node.getInputs().size() == 1) + { + dumpUnaryInputOp(node); + } + else if (node.getInputs().size() == 2) + { + dumpBinaryInputOp(node); + } + else + { + VERBOSE(LIR) << "* " << node.name() << " is set wrong" << std::endl; + } +} + +void OperationDumper::visit(const ResizeNearestNeighbor &node) +{ + if (node.getInputs().size() == 1) + { + dumpUnaryInputOp(node); + } + else if (node.getInputs().size() == 2) + { + dumpBinaryInputOp(node); + } + else + { + VERBOSE(LIR) << "* " << node.name() << " is set wrong" << std::endl; + } +} + +void OperationDumper::visit(const Reverse &node) +{ + std::string axis = + "Axis(" + std::to_string(node.getInputs().at(Reverse::Input::AXIS).value()) + ")"; + dumpUnaryInputOp(node, axis); +} + +void OperationDumper::visit(const RNN &node) +{ + VERBOSE(LIR) << "* RNN" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(RNN::Input::INPUT) << ") Weights(" + << node.getInputs().at(RNN::Input::WEIGHTS) << ") Recurrent Weights(" + << node.getInputs().at(RNN::Input::RECURRENT_WEIGHTS) << ") Bias(" + << node.getInputs().at(RNN::Input::BIAS) << ") Hidden State(" + << node.getInputs().at(RNN::Input::HIDDEN_STATE_IN) << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(RNN::Output::OUTPUT) + << ") Hidden State(" << node.getInputs().at(RNN::Output::HIDDEN_STATE_OUT) << ")" + << std::endl; +} + +void OperationDumper::visit(const Range &node) +{ + VERBOSE(LIR) << "* Range" << std::endl; + VERBOSE(LIR) << " - Inputs : Start(" << node.getInputs().at(Range::Input::START) << ")" + << " Limit(" << node.getInputs().at(Range::Input::LIMIT) << ")" + << " Delta(" << node.getInputs().at(Range::Input::DELTA) << ")" << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const Select &node) +{ + VERBOSE(LIR) << "* Select" << std::endl; + VERBOSE(LIR) << " - Inputs : Condition(" << node.getInputs().at(Select::Input::CONDITION) << ")" + << " Input_X(" << node.getInputs().at(Select::Input::INPUT_TRUE) << ")" + << " Input_Y(" << node.getInputs().at(Select::Input::INPUT_FALSE) << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const ir::operation::Shape &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Softmax &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const SpaceToBatchND &node) +{ + std::string inputs = + "BlockSize(" + + std::to_string(node.getInputs().at(SpaceToBatchND::Input::BLOCK_SIZE).value()) + + ") Paddings(" + std::to_string(node.getInputs().at(SpaceToBatchND::Input::PADDINGS).value()) + + ")"; + dumpUnaryInputOp(node, inputs); +} + +void OperationDumper::visit(const SpaceToDepth &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Split &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const SquaredDifference &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const StatelessRandomUniform &node) +{ + VERBOSE(LIR) << "* StatelessRandomUniform" << std::endl; + VERBOSE(LIR) << " - Inputs : Shape(" << node.getInputs().at(StatelessRandomUniform::Input::SHAPE) + << " Seed(" << node.getInputs().at(StatelessRandomUniform::Input::SEED) << ")" + << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const Squeeze &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Slice &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const StridedSlice &node) { dumpUnaryInputOp(node); } + +void OperationDumper::visit(const Tile &node) +{ + std::string multiples = + "Multiples(" + std::to_string(node.getInputs().at(Tile::Input::MULTIPLES).value()) + ")"; + dumpUnaryInputOp(node, multiples); +} + +void OperationDumper::visit(const TopKV2 &node) +{ + VERBOSE(LIR) << "* TopKV2" << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(TopKV2::Input::INPUT) << ")" + << std::endl; + VERBOSE(LIR) << " - Outputs : Values(" << node.getOutputs().at(TopKV2::Output::OUTPUT_VALUES) + << ") Indices(" << node.getOutputs().at(TopKV2::Output::OUTPUT_INDICES) << ")" + << 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) << ") KERNEL(" + << node.getInputs().at(TransposeConv::Input::KERNEL) << ") IFM(" + << node.getInputs().at(TransposeConv::Input::INPUT) << ")" << std::endl; + VERBOSE(LIR) << " - Output : OFM(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const Transpose &node) { dumpBinaryInputOp(node); } + +void OperationDumper::visit(const Unpack &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : Input(" << node.getInputs().at(Unpack::Input::INPUT) << ")" + << 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 OneHot &node) +{ + VERBOSE(LIR) << "* " << node.name() << std::endl; + VERBOSE(LIR) << " - Inputs : " + << "Indices(" << node.getInputs().at(OneHot::Input::INDICES) << ") " << std::endl; + VERBOSE(LIR) << " - Output : Output(" << node.getOutputs().at(0) << ")" << std::endl; +} + +void OperationDumper::visit(const If &node) +{ + VERBOSE(LIR) << "* " << node.name() << 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 : " + << "Then subgraph (" << node.param().then_subg_index << ") Else subgraph (" + << node.param().else_subg_index << ") Inputs(" << inputs << ")" << 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) << " - Output : Outputs(" << outputs << ")" << std::endl; +} + +void OperationDumper::visit(const While &node) +{ + VERBOSE(LIR) << "* " << node.name() << 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 : " + << "Cond subgraph (" << node.param().cond_subg_index << ") Body subgraph (" + << node.param().cond_subg_index << ") Inputs(" << inputs << ")" << 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) << " - Output : Outputs(" << outputs << ")" << 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..91642ab13 --- /dev/null +++ b/runtime/onert/core/src/ir/OperationDumper.h @@ -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. + */ + +#ifndef __ONERT_OPERATION_DUMPER_H__ +#define __ONERT_OPERATION_DUMPER_H__ + +#include "ir/OperationVisitor.h" +#include <string> + +namespace onert +{ +namespace ir +{ + +class OperationDumper : public OperationVisitor +{ +public: + OperationDumper(const std::string &start_msg); + +public: + void visit(const operation::ArgMax &) override; + void visit(const operation::BatchToSpaceND &node) override; + void visit(const operation::BCQFullyConnected &node) override; + void visit(const operation::BinaryArithmetic &node) override; + void visit(const operation::BroadcastTo &) 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::ConvertFp16ToFp32 &node) override; + void visit(const operation::ConvertFp32ToFp16 &node) override; + void visit(const operation::DepthToSpace &) override; + void visit(const operation::DepthwiseConv2D &node) override; + void visit(const operation::ElementwiseActivation &) override; + void visit(const operation::ElementwiseBinary &) override; + void visit(const operation::ElementwiseUnary &) override; + void visit(const operation::EmbeddingLookup &) override; + void visit(const operation::ExpandDims &) 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::LocalResponseNormalization &) override; + void visit(const operation::LSTM &) override; + void visit(const operation::Pack &) override; + void visit(const operation::Pad &) override; + void visit(const operation::Permute &node) override; + void visit(const operation::Pool2D &node) override; + void visit(const operation::Pow &node) override; + void visit(const operation::PReLU &) override; + void visit(const operation::Range &) override; + void visit(const operation::Rank &) override; + void visit(const operation::Reduce &) override; + void visit(const operation::Reshape &node) override; + void visit(const operation::ResizeBilinear &) override; + void visit(const operation::ResizeNearestNeighbor &) override; + void visit(const operation::Reverse &) override; + void visit(const operation::RNN &) override; + void visit(const operation::Select &node) override; + void visit(const operation::Shape &node) override; + void visit(const operation::Softmax &node) override; + void visit(const operation::SpaceToBatchND &) override; + void visit(const operation::SpaceToDepth &) override; + void visit(const operation::Split &) 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::StatelessRandomUniform &) override; + void visit(const operation::Tile &) 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::OneHot &) override; + void visit(const operation::If &) override; + void visit(const operation::While &) override; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_OPERATION_DUMPER_H__ diff --git a/runtime/onert/core/src/ir/OperationIndexSet.cc b/runtime/onert/core/src/ir/OperationIndexSet.cc new file mode 100644 index 000000000..750ffffa6 --- /dev/null +++ b/runtime/onert/core/src/ir/OperationIndexSet.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/OperationIndexSet.h" + +#include <algorithm> + +namespace onert +{ +namespace ir +{ + +OperationIndexSet::OperationIndexSet(std::initializer_list<OperationIndex> list) : _set(list) +{ + // DO NOTHING +} + +bool OperationIndexSet::contains(const OperationIndex &index) const +{ + return std::find(_set.begin(), _set.end(), index) != _set.end(); +} + +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperationValidator.cc b/runtime/onert/core/src/ir/OperationValidator.cc new file mode 100644 index 000000000..da08e81fc --- /dev/null +++ b/runtime/onert/core/src/ir/OperationValidator.cc @@ -0,0 +1,339 @@ +/* + * 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 "OperationValidator.h" + +#include "ir/Graph.h" + +#define OP_REQUIRES(EXP) \ + do \ + { \ + if (!(EXP)) \ + throw std::runtime_error("OperationValidator failed at line " + std::to_string(__LINE__)); \ + } while (0) + +namespace onert +{ +namespace ir +{ + +OperationValidator::OperationValidator(const Graph &graph) + : _operations{graph.operations()}, _operands{graph.operands()} +{ +} + +void OperationValidator::operator()() +{ + _operations.iterate([&](const OperationIndex &, const Operation &node) { node.accept(*this); }); +} + +DataType OperationValidator::operandType(const OperandIndex &idx) +{ + return _operands.at(idx).typeInfo().type(); +} + +bool OperationValidator::isConstant(const OperandIndex &idx) +{ + return _operands.at(idx).isConstant(); +} + +bool OperationValidator::isSameType(const OperandIndex &idx1, const OperandIndex &idx2) +{ + return operandType(idx1) == operandType(idx2); +} + +bool OperationValidator::isValidType(const OperandIndex &idx, const DataType &type) +{ + return operandType(idx) == type; +} + +bool OperationValidator::isValidType(const OperandIndex &idx, + std::initializer_list<DataType> valid_types) +{ + for (auto type_to_check : valid_types) + { + if (isValidType(idx, type_to_check)) + { + return true; + } + } + + return false; +} + +void OperationValidator::visit(const operation::AddN &node) +{ + int size = node.getInputs().size(); + for (int i = 0; i < size; i++) + { + const auto input_index(node.getInputs().at(i)); + OP_REQUIRES(isValidType(input_index, {DataType::FLOAT32, DataType::INT32})); + } +} + +void OperationValidator::visit(const operation::BatchMatMul &node) +{ + const auto lhs_index(node.getInputs().at(operation::BatchMatMul::Input::LHS)); + const auto rhs_index(node.getInputs().at(operation::BatchMatMul::Input::RHS)); + + // Constant lhs and rhs is not implemented yet + OP_REQUIRES(!isConstant(lhs_index) && !isConstant(rhs_index)); +} + +void OperationValidator::visit(const operation::BatchToSpaceND &node) +{ + const auto block_size_index{node.getInputs().at(operation::BatchToSpaceND::Input::BLOCK_SIZE)}; + + // Non-constant block_size is not implemented yet + OP_REQUIRES(isConstant(block_size_index)); +} + +void OperationValidator::visit(const operation::BinaryArithmetic &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(operation::BinaryArithmetic::Input::LHS)}; + const auto rhs_index{node.getInputs().at(operation::BinaryArithmetic::Input::RHS)}; + + OP_REQUIRES(isSameType(lhs_index, rhs_index)); + OP_REQUIRES(isSameType(lhs_index, output_index)); +} + +void OperationValidator::visit(const operation::Comparison &node) +{ + const auto output_index{node.getOutputs().at(0)}; + + const auto lhs_index{node.getInputs().at(operation::Comparison::Input::INPUT0)}; + const auto rhs_index{node.getInputs().at(operation::Comparison::Input::INPUT1)}; + + OP_REQUIRES(isSameType(lhs_index, rhs_index)); + OP_REQUIRES(isValidType(output_index, DataType::BOOL8)); +} + +void OperationValidator::visit(const operation::DepthToSpace &node) +{ + int32_t block_size = node.param().block_size; + + OP_REQUIRES(block_size > 0); +} + +void OperationValidator::visit(const operation::DepthwiseConv2D &node) +{ + const auto input_index{node.getInputs().at(operation::DepthwiseConv2D::Input::INPUT)}; + const auto output_index{node.getOutputs().at(0)}; + + uint32_t stride_horizontal = node.param().stride.horizontal; + uint32_t stride_vertical = node.param().stride.vertical; + uint32_t dilation_width = node.param().dilation.width_factor; + uint32_t dilation_height = node.param().dilation.height_factor; + + OP_REQUIRES((stride_horizontal > 0) && (stride_vertical > 0)); + OP_REQUIRES((dilation_width > 0) && (dilation_height > 0)); + OP_REQUIRES(isSameType(input_index, output_index)); +} + +void OperationValidator::visit(const operation::ElementwiseActivation &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(0)}; + + // Check if I/O types match + OP_REQUIRES(isSameType(output_index, input_index)); +} + +void OperationValidator::visit(const operation::ElementwiseBinary &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(operation::ElementwiseBinary::Input::LHS)}; + const auto rhs_index{node.getInputs().at(operation::ElementwiseBinary::Input::RHS)}; + + OP_REQUIRES(isSameType(lhs_index, rhs_index)); + OP_REQUIRES(isSameType(lhs_index, output_index)); +} + +void OperationValidator::visit(const operation::ElementwiseUnary &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(operation::ElementwiseUnary::Input::INPUT)}; + + // Check if I/O types match + if (node.param().op_type == operation::ElementwiseUnary::Type::DEQUANTIZE) + { + // NNAPI allow QUANT_INT8_SYMM type input + OP_REQUIRES(isValidType(input_index, {DataType::QUANT_UINT8_ASYMM, DataType::QUANT_INT8_SYMM, + DataType::QUANT_INT8_ASYMM})); + OP_REQUIRES(isValidType(output_index, DataType::FLOAT32)); + } + else if (node.param().op_type == operation::ElementwiseUnary::Type::QUANTIZE) + { + OP_REQUIRES(isValidType(input_index, DataType::FLOAT32)); + OP_REQUIRES(isValidType(output_index, DataType::QUANT_UINT8_ASYMM)); + } + else if (node.param().op_type == operation::ElementwiseUnary::Type::FLOOR) + { + OP_REQUIRES(isValidType(input_index, DataType::FLOAT32)); + OP_REQUIRES(isSameType(output_index, input_index)); + } + else if (node.param().op_type != operation::ElementwiseUnary::Type::CAST) + { + OP_REQUIRES(isSameType(output_index, input_index)); + } +} + +void OperationValidator::visit(const operation::EmbeddingLookup &node) +{ + const auto lookups_index{node.getInputs().at(operation::EmbeddingLookup::Input::LOOKUPS)}; + + OP_REQUIRES(isValidType(lookups_index, DataType::INT32)); +} + +void OperationValidator::visit(const operation::ExpandDims &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(operation::ExpandDims::Input::INPUT)}; + const auto axis_index{node.getInputs().at(operation::ExpandDims::Input::AXIS)}; + + OP_REQUIRES(isSameType(output_index, input_index)); + OP_REQUIRES(isValidType(axis_index, DataType::INT32)); +} + +void OperationValidator::visit(const operation::HashtableLookup &node) +{ + const auto hits_index{node.getOutputs().at(operation::HashtableLookup::Output::HITS)}; + const auto lookups_index{node.getInputs().at(operation::HashtableLookup::Input::LOOKUPS)}; + const auto keys_index{node.getInputs().at(operation::HashtableLookup::Input::KEYS)}; + + OP_REQUIRES(isValidType(lookups_index, DataType::INT32)); + OP_REQUIRES(isValidType(keys_index, DataType::INT32)); + OP_REQUIRES(isValidType(hits_index, DataType::QUANT_UINT8_ASYMM)); +} + +void OperationValidator::visit(const operation::Pack &node) +{ + const auto num{node.param().num}; + + OP_REQUIRES(num == static_cast<int32_t>(node.getInputs().size())); +} + +void OperationValidator::visit(const operation::Pad &node) +{ + const auto pad_index{node.getInputs().at(operation::Pad::Input::PAD)}; + + OP_REQUIRES(isValidType(pad_index, DataType::INT32)); +} + +void OperationValidator::visit(const operation::Rank &node) +{ + const auto output_index{node.getOutputs().at(0)}; + + OP_REQUIRES(isValidType(output_index, DataType::INT32)); +} + +void OperationValidator::visit(const operation::ResizeBilinear &node) +{ + auto align_corners = node.param().align_corners; + auto half_pixel_centers = node.param().half_pixel_centers; + + OP_REQUIRES(!align_corners || !half_pixel_centers); +} + +void OperationValidator::visit(const operation::Reverse &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(operation::Reverse::Input::INPUT)}; + const auto axis_index{node.getInputs().at(operation::Reverse::Input::AXIS)}; + + OP_REQUIRES(isValidType(axis_index, DataType::INT32)); + OP_REQUIRES(isSameType(output_index, input_index)); +} + +void OperationValidator::visit(const operation::Select &node) +{ + const auto condition_index{node.getInputs().at(operation::Select::Input::CONDITION)}; + const auto input_true_index{node.getInputs().at(operation::Select::Input::INPUT_TRUE)}; + const auto input_false_index{node.getInputs().at(operation::Select::Input::INPUT_FALSE)}; + + OP_REQUIRES(isValidType(condition_index, DataType::BOOL8)); + OP_REQUIRES(isSameType(input_true_index, input_false_index)); +} + +void OperationValidator::visit(const operation::Shape &node) +{ + const auto output_index{node.getOutputs().at(0)}; + + OP_REQUIRES(isValidType(output_index, {DataType::UINT32, DataType::INT32, DataType::INT64})); +} + +void OperationValidator::visit(const operation::SpaceToBatchND &node) +{ + const auto block_size_index{node.getInputs().at(operation::SpaceToBatchND::Input::BLOCK_SIZE)}; + const auto paddings_index{node.getInputs().at(operation::SpaceToBatchND::Input::PADDINGS)}; + + // Non-constant block_size and padding is not implemented yet + OP_REQUIRES(isConstant(block_size_index)); + OP_REQUIRES(isConstant(paddings_index)); +} + +void OperationValidator::visit(const operation::SpaceToDepth &node) +{ + const auto block_size = node.param().block_size; + OP_REQUIRES(block_size >= 1); +} + +void OperationValidator::visit(const operation::Split &node) +{ + const auto num_splits = node.param().num_splits; + + OP_REQUIRES(num_splits > 0 && num_splits <= 0xFFFF); + OP_REQUIRES(node.getOutputs().size() == static_cast<uint32_t>(num_splits)); +} + +void OperationValidator::visit(const operation::SquaredDifference &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto lhs_index{node.getInputs().at(operation::SquaredDifference::Input::LHS)}; + const auto rhs_index{node.getInputs().at(operation::SquaredDifference::Input::RHS)}; + + OP_REQUIRES(isSameType(output_index, lhs_index)); + OP_REQUIRES(isSameType(lhs_index, rhs_index)); +} + +void OperationValidator::visit(const operation::StridedSlice &node) +{ + const auto output_index{node.getOutputs().at(0)}; + const auto input_index{node.getInputs().at(operation::StridedSlice::Input::INPUT)}; + + OP_REQUIRES(isSameType(output_index, input_index)); +} + +void OperationValidator::visit(const operation::TransposeConv &node) +{ + OP_REQUIRES((node.param().padding.type == PaddingType::SAME) || + (node.param().padding.type == PaddingType::VALID)); +} + +void OperationValidator::visit(const operation::Unpack &node) +{ + const auto num{node.param().num}; + OP_REQUIRES(num == static_cast<int32_t>(node.getOutputs().size())); +} + +void OperationValidator::visit(const operation::While &node) +{ + OP_REQUIRES(node.getInputs().size() == node.getOutputs().size()); +} + +} // namespace compiler +} // namespace onert diff --git a/runtime/onert/core/src/ir/OperationValidator.h b/runtime/onert/core/src/ir/OperationValidator.h new file mode 100644 index 000000000..2ea8000e5 --- /dev/null +++ b/runtime/onert/core/src/ir/OperationValidator.h @@ -0,0 +1,90 @@ +/* + * 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_VALIDATOR_H__ +#define __ONERT_IR_OPERATION_VALIDATOR_H__ + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +class Graph; +class Operands; +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ + +class OperationValidator : public OperationVisitor +{ +public: + OperationValidator(void) = delete; + OperationValidator(const Graph &graph); + +public: + void operator()(); + +public: + void visit(const operation::AddN &node) override; + void visit(const operation::BatchMatMul &node) override; + void visit(const operation::BatchToSpaceND &node) override; + void visit(const operation::BinaryArithmetic &node) override; + void visit(const operation::Comparison &node) override; + void visit(const operation::DepthToSpace &node) override; + void visit(const operation::DepthwiseConv2D &node) override; + void visit(const operation::ElementwiseActivation &node) override; + void visit(const operation::ElementwiseBinary &node) override; + void visit(const operation::ElementwiseUnary &node) override; + void visit(const operation::EmbeddingLookup &node) override; + void visit(const operation::ExpandDims &node) override; + void visit(const operation::HashtableLookup &node) override; + void visit(const operation::Pack &node) override; + void visit(const operation::Pad &node) override; + void visit(const operation::Rank &node) override; + void visit(const operation::ResizeBilinear &node) override; + void visit(const operation::Reverse &node) override; + void visit(const operation::Select &node) override; + void visit(const operation::Shape &node) override; + void visit(const operation::SpaceToBatchND &node) override; + void visit(const operation::SpaceToDepth &node) override; + void visit(const operation::Split &node) override; + void visit(const operation::SquaredDifference &node) override; + void visit(const operation::StridedSlice &node) override; + void visit(const operation::TransposeConv &node) override; + void visit(const operation::Unpack &node) override; + void visit(const operation::While &node) override; + +private: + DataType operandType(const OperandIndex &idx); + bool isConstant(const OperandIndex &idx); + bool isSameType(const OperandIndex &idx1, const OperandIndex &idx2); + bool isValidType(const OperandIndex &idx, const DataType &type); + bool isValidType(const OperandIndex &idx, std::initializer_list<DataType> valid_types); + +private: + const Operations &_operations; + const Operands &_operands; +}; + +} // namespace ir +} // namespace onert + +#endif // __ONERT_IR_OPERATION_VALIDATOR_H__ 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..d74f80217 --- /dev/null +++ b/runtime/onert/core/src/ir/Padding.cc @@ -0,0 +1,160 @@ +/* + * 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, uint32_t dwf, uint32_t dhf) +{ + 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 effective_filter_h_size = (kh - 1) * dhf + 1; + const int32_t effective_filter_w_size = (kw - 1) * dwf + 1; + + 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 + effective_filter_h_size; + 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 + effective_filter_w_size; + 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, uint32_t dwf, + uint32_t dhf) +{ + 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, dwf, dhf); +} + +} // 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, uint32_t dwf, uint32_t dhf) +{ + if (padding.type == PaddingType::EXPLICIT) + { + return padding.param; + } + else if (padding.type == PaddingType::SAME) + { + return samePadding(ifm_shape, ofm_shape, stride, kw, kh, dwf, dhf); + } + 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..322df7b4c --- /dev/null +++ b/runtime/onert/core/src/ir/Shape.cc @@ -0,0 +1,114 @@ +/* + * 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 +{ + +int32_t const Shape::UNSPECIFIED_DIM = -1; + +// NNFW_MAX_RANK is 6 +int32_t const Shape::MAX_RANK = 6; + +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 +{ + // if dimension is 0, it means unspecified and cannot calculate the total number of elements + if (std::any_of(_dimensions.begin(), _dimensions.end(), + [](const int32_t &v) { return v == UNSPECIFIED_DIM; })) + throw std::runtime_error("num_elements() cannot calculate when any dimension is unspecified"); + + 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() <= Shape::MAX_RANK); + Shape backend_shape{shape}; + if (shape.rank() >= 4 && frontend_layout == Layout::NHWC && backend_layout == Layout::NCHW) + { + // Permutation changing layout beyond 4-D is not supported yet + assert(shape.rank() <= 4); + 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) + { + // Permutation changing layout beyond 4-D is not supported yet + assert(shape.rank() <= 4); + 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/AddN.cc b/runtime/onert/core/src/ir/operation/AddN.cc new file mode 100644 index 000000000..ce471252d --- /dev/null +++ b/runtime/onert/core/src/ir/operation/AddN.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/AddN.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void AddN::accept(OperationVisitor &v) const { v.visit(*this); } + +AddN::AddN(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createExact(inputs.size()), inputs, outputs} +{ +} + +} // 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..f3bd8fd73 --- /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(2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/BCQFullyConnected.cc b/runtime/onert/core/src/ir/operation/BCQFullyConnected.cc new file mode 100644 index 000000000..9dc54e6e9 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BCQFullyConnected.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/BCQFullyConnected.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void BCQFullyConnected::accept(OperationVisitor &v) const { v.visit(*this); } + +BCQFullyConnected::BCQFullyConnected(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/BCQGather.cc b/runtime/onert/core/src/ir/operation/BCQGather.cc new file mode 100644 index 000000000..80efa6460 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BCQGather.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/BCQGather.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void BCQGather::accept(OperationVisitor &v) const { v.visit(*this); } + +BCQGather::BCQGather(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/BatchMatMul.cc b/runtime/onert/core/src/ir/operation/BatchMatMul.cc new file mode 100644 index 000000000..b9616158d --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BatchMatMul.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/operation/BatchMatMul.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void BatchMatMul::accept(OperationVisitor &v) const { v.visit(*this); } + +BatchMatMul::BatchMatMul(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/BatchToSpaceND.cc b/runtime/onert/core/src/ir/operation/BatchToSpaceND.cc new file mode 100644 index 000000000..34be79dd2 --- /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::createInRange(2u, 3u), inputs, outputs} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/BinaryArithmetic.cc b/runtime/onert/core/src/ir/operation/BinaryArithmetic.cc new file mode 100644 index 000000000..2b1422c73 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BinaryArithmetic.cc @@ -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. + */ + +#include "ir/operation/BinaryArithmetic.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void BinaryArithmetic::accept(OperationVisitor &v) const { v.visit(*this); } + +BinaryArithmetic::BinaryArithmetic(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +std::string BinaryArithmetic::name() const +{ + using ArithmeticType = onert::ir::operation::BinaryArithmetic::ArithmeticType; + static const std::unordered_map<ArithmeticType, std::string> name_map{ + {ArithmeticType::ADD, std::string{"Add"}}, + {ArithmeticType::SUB, std::string{"Sub"}}, + {ArithmeticType::MUL, std::string{"Mul"}}, + {ArithmeticType::DIV, std::string{"Div"}}}; + return name_map.at(_param.arithmetic_type); +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/BroadcastTo.cc b/runtime/onert/core/src/ir/operation/BroadcastTo.cc new file mode 100644 index 000000000..a8f5e59cf --- /dev/null +++ b/runtime/onert/core/src/ir/operation/BroadcastTo.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/BroadcastTo.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void BroadcastTo::accept(OperationVisitor &v) const { v.visit(*this); } + +BroadcastTo::BroadcastTo(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/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/Einsum.cc b/runtime/onert/core/src/ir/operation/Einsum.cc new file mode 100644 index 000000000..3c1473aaa --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Einsum.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/operation/Einsum.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Einsum::accept(OperationVisitor &v) const { v.visit(*this); } + +Einsum::Einsum(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/ElementwiseActivation.cc b/runtime/onert/core/src/ir/operation/ElementwiseActivation.cc new file mode 100644 index 000000000..f6718b656 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ElementwiseActivation.cc @@ -0,0 +1,72 @@ +/* + * 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/ElementwiseActivation.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ElementwiseActivation::accept(OperationVisitor &v) const { v.visit(*this); } + +ElementwiseActivation::ElementwiseActivation(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ + if (param.op_type == Type::LOGISTIC) + { + assert(param.alpha == 0.0f && param.beta == 0.0f && "Logistic will be supported only as " + "sigmoid function(L=1, k=1, x0=0). So, do " + "not use alpha and beta"); + } + else if (param.op_type == Type::RELU) + { + assert(param.alpha >= param.beta && "ReLU's alpha must be equal or greater than beta"); + } + else if (param.op_type == Type::TANH) + { + assert(param.alpha == 1.0f && param.beta == 1.0f && "f(x) = alpha * tanh(beta * x), Tanh is " + "supported only the values of alpha and " + "beta are 1.f"); + } +} + +std::string ElementwiseActivation::name() const +{ + using ElementwiseActivationType = onert::ir::operation::ElementwiseActivation::Type; + static const std::unordered_map<Type, std::string> name_map{ + {ElementwiseActivationType::ELU, "ELU"}, + {ElementwiseActivationType::LOGISTIC, "Logistic"}, + {ElementwiseActivationType::RELU, "ReLU"}, + {ElementwiseActivationType::TANH, "Tanh"}, + {ElementwiseActivationType::LEAKY_RELU, "LeakyRelu"}}; + return name_map.at(_param.op_type); +} + +float ElementwiseActivation::infinity = std::numeric_limits<float>::infinity(); + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ElementwiseBinary.cc b/runtime/onert/core/src/ir/operation/ElementwiseBinary.cc new file mode 100644 index 000000000..3287fc0a3 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ElementwiseBinary.cc @@ -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. + */ + +#include "ir/operation/ElementwiseBinary.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ElementwiseBinary::accept(OperationVisitor &v) const { v.visit(*this); } + +ElementwiseBinary::ElementwiseBinary(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +std::string ElementwiseBinary::name() const +{ + using ElementwiseBinaryType = onert::ir::operation::ElementwiseBinary::ElementwiseBinaryType; + static const std::unordered_map<ElementwiseBinaryType, std::string> name_map{ + {ElementwiseBinaryType::LOGICAL_AND, std::string{"LogicalAnd"}}, + {ElementwiseBinaryType::LOGICAL_OR, std::string{"LogicalOr"}}, + {ElementwiseBinaryType::MAX, std::string{"Max"}}, + {ElementwiseBinaryType::MIN, std::string{"Min"}}}; + return name_map.at(_param.op_type); +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ElementwiseUnary.cc b/runtime/onert/core/src/ir/operation/ElementwiseUnary.cc new file mode 100644 index 000000000..6a0be7eb8 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ElementwiseUnary.cc @@ -0,0 +1,67 @@ +/* + * 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/ElementwiseUnary.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ElementwiseUnary::accept(OperationVisitor &v) const { v.visit(*this); } + +ElementwiseUnary::ElementwiseUnary(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs, + OperandConstraint::createExact(1u)}, + _param{param} +{ +} + +std::string ElementwiseUnary::name() const +{ + using ElementwiseUnaryType = onert::ir::operation::ElementwiseUnary::Type; + static const std::unordered_map<ElementwiseUnaryType, std::string> name_map{ + {ElementwiseUnaryType::ABS, std::string{"Abs"}}, + {ElementwiseUnaryType::CAST, std::string{"Cast"}}, + {ElementwiseUnaryType::COS, std::string{"Cos"}}, + {ElementwiseUnaryType::DEQUANTIZE, std::string{"Dequantize"}}, + {ElementwiseUnaryType::ERF, std::string{"Erf"}}, + {ElementwiseUnaryType::EXP, std::string{"Exp"}}, + {ElementwiseUnaryType::FLOOR, std::string{"Floor"}}, + {ElementwiseUnaryType::LOG, std::string{"Log"}}, + {ElementwiseUnaryType::LOGICAL_NOT, std::string{"LogicalNot"}}, + {ElementwiseUnaryType::NEG, std::string{"Neg"}}, + {ElementwiseUnaryType::QUANTIZE, std::string{"Quantize"}}, + {ElementwiseUnaryType::ROUND, std::string{"Round"}}, + {ElementwiseUnaryType::RSQRT, std::string{"RSqrt"}}, + {ElementwiseUnaryType::SIN, std::string{"Sin"}}, + {ElementwiseUnaryType::SQRT, std::string{"Sqrt"}}, + {ElementwiseUnaryType::SQURE, std::string{"Squre"}}, + {ElementwiseUnaryType::ZEROS_LIKE, std::string{"ZerosLike"}}}; + return name_map.at(_param.op_type); +} + +} // 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/ExpandDims.cc b/runtime/onert/core/src/ir/operation/ExpandDims.cc new file mode 100644 index 000000000..3f555bd23 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ExpandDims.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/ExpandDims.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ExpandDims::accept(OperationVisitor &v) const { v.visit(*this); } + +ExpandDims::ExpandDims(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/Fill.cc b/runtime/onert/core/src/ir/operation/Fill.cc new file mode 100644 index 000000000..b8b97d1c0 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Fill.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/Fill.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Fill::accept(OperationVisitor &v) const { v.visit(*this); } + +Fill::Fill(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/FullyConnected.cc b/runtime/onert/core/src/ir/operation/FullyConnected.cc new file mode 100644 index 000000000..9837a3137 --- /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::createInRange(2u, 3u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/FusedBatchNorm.cc b/runtime/onert/core/src/ir/operation/FusedBatchNorm.cc new file mode 100644 index 000000000..7b9301ea6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/FusedBatchNorm.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/operation/FusedBatchNorm.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void FusedBatchNorm::accept(OperationVisitor &v) const { v.visit(*this); } + +FusedBatchNorm::FusedBatchNorm(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createAtLeast(5u), 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/If.cc b/runtime/onert/core/src/ir/operation/If.cc new file mode 100644 index 000000000..599751dfd --- /dev/null +++ b/runtime/onert/core/src/ir/operation/If.cc @@ -0,0 +1,32 @@ +/* + * 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/If.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void If::accept(OperationVisitor &v) const { v.visit(*this); } +If::If(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, const Param ¶m) + : Operation{OperandConstraint::createAny(), inputs, outputs}, _param{param} +{ +} +} // 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..9a7d3eb61 --- /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) + : Operation{OperandConstraint::createExact(1u), inputs, outputs} +{ +} + +} // 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..5cd7c793a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LSTM.cc @@ -0,0 +1,48 @@ +/* + * 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::createInRange(20u, 24u), inputs, outputs}, _param{param} +{ +} + +std::string LSTM::name() const +{ + if (getOutputs().at(Output::SCRATCH_BUFFER).undefined()) + return std::string{"UnidirectionalSequenceLSTM"}; + else + return Operation::name(); +} + +} // 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/LogSoftmax.cc b/runtime/onert/core/src/ir/operation/LogSoftmax.cc new file mode 100644 index 000000000..73c6580ec --- /dev/null +++ b/runtime/onert/core/src/ir/operation/LogSoftmax.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/LogSoftmax.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void LogSoftmax::accept(OperationVisitor &v) const { v.visit(*this); } + +LogSoftmax::LogSoftmax(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/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/MatrixBandPart.cc b/runtime/onert/core/src/ir/operation/MatrixBandPart.cc new file mode 100644 index 000000000..bac31f13e --- /dev/null +++ b/runtime/onert/core/src/ir/operation/MatrixBandPart.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/MatrixBandPart.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void MatrixBandPart::accept(OperationVisitor &v) const { v.visit(*this); } + +MatrixBandPart::MatrixBandPart(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/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..784d4162a --- /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(1u), 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..0c56e92e3 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Pad.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/Pad.h" + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Pad::accept(OperationVisitor &v) const { v.visit(*this); } + +// PAD: 2 inputs +// PADV2: 3 inputs +Pad::Pad(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs) + : Operation{OperandConstraint::createInRange(2u, 3u), inputs, outputs} +{ +} + +} // 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..eefb6c542 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Permute.cc @@ -0,0 +1,41 @@ +/* + * 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, Type type) + : Operation{OperandConstraint::createExact(1u)}, _type{type} +{ + setInputs({input}); + setOutputs({output}); +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Pool2D.cc b/runtime/onert/core/src/ir/operation/Pool2D.cc new file mode 100644 index 000000000..761d14c3d --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Pool2D.cc @@ -0,0 +1,51 @@ +/* + * 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/Pool2D.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Pool2D::accept(OperationVisitor &v) const { v.visit(*this); } + +Pool2D::Pool2D(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(1u), inputs, outputs}, _param{param} +{ +} + +std::string Pool2D::name() const +{ + using PoolType = onert::ir::operation::Pool2D::PoolType; + static const std::unordered_map<PoolType, std::string> name_map{ + {PoolType::AVG, "Avg" + std::string{toString(opcode())}}, + {PoolType::L2, "L2" + std::string{toString(opcode())}}, + {PoolType::MAX, "Max" + std::string{toString(opcode())}}}; + return name_map.at(_param.op_type); +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Pow.cc b/runtime/onert/core/src/ir/operation/Pow.cc new file mode 100644 index 000000000..940b1391a --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Pow.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/Pow.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Pow::accept(OperationVisitor &v) const { v.visit(*this); } + +Pow::Pow(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/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/Range.cc b/runtime/onert/core/src/ir/operation/Range.cc new file mode 100644 index 000000000..96ab04c1b --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Range.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/Range.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Range::accept(OperationVisitor &v) const { v.visit(*this); } + +Range::Range(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/Rank.cc b/runtime/onert/core/src/ir/operation/Rank.cc new file mode 100644 index 000000000..c357e9018 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Rank.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/Rank.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Rank::accept(OperationVisitor &v) const { v.visit(*this); } + +Rank::Rank(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/Reduce.cc b/runtime/onert/core/src/ir/operation/Reduce.cc new file mode 100644 index 000000000..d6a1d953c --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Reduce.cc @@ -0,0 +1,56 @@ +/* + * 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/Reduce.h" + +#include <cassert> +#include <unordered_map> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Reduce::accept(OperationVisitor &v) const { v.visit(*this); } + +Reduce::Reduce(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createExact(2u), inputs, outputs}, _param{param} +{ +} + +std::string Reduce::name() const +{ + using ReduceType = onert::ir::operation::Reduce::ReduceType; + static const std::unordered_map<ReduceType, std::string> name_map{ + {ReduceType::ALL, std::string{toString(opcode())} + "All"}, + {ReduceType::ANY, std::string{toString(opcode())} + "Any"}, + {ReduceType::MAX, std::string{toString(opcode())} + "Max"}, + {ReduceType::MEAN, std::string{toString(opcode())} + "Mean"}, + {ReduceType::MIN, std::string{toString(opcode())} + "Min"}, + {ReduceType::PROD, std::string{toString(opcode())} + "Prod"}, + {ReduceType::SUM, std::string{toString(opcode())} + "SUM"}}; + return name_map.at(_param.reduce_type); + // return std::string(toString(opcode())) + reduce_type_str_map.at(_param.reduce_type); +} + +} // 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..92aa89ac6 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Reshape.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/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, + 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/ResizeBilinear.cc b/runtime/onert/core/src/ir/operation/ResizeBilinear.cc new file mode 100644 index 000000000..71925bb44 --- /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::createInRange(1u, 2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/ResizeNearestNeighbor.cc b/runtime/onert/core/src/ir/operation/ResizeNearestNeighbor.cc new file mode 100644 index 000000000..98d0b5f26 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/ResizeNearestNeighbor.cc @@ -0,0 +1,41 @@ +/* + * 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/ResizeNearestNeighbor.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void ResizeNearestNeighbor::accept(OperationVisitor &v) const { v.visit(*this); } + +ResizeNearestNeighbor::ResizeNearestNeighbor(const OperandIndexSequence &inputs, + const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createInRange(1u, 2u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Reverse.cc b/runtime/onert/core/src/ir/operation/Reverse.cc new file mode 100644 index 000000000..4b3c1e1af --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Reverse.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/Reverse.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Reverse::accept(OperationVisitor &v) const { v.visit(*this); } + +Reverse::Reverse(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/Select.cc b/runtime/onert/core/src/ir/operation/Select.cc new file mode 100644 index 000000000..1f22b5234 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Select.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/operation/Select.h" + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Select::accept(OperationVisitor &v) const { v.visit(*this); } + +Select::Select(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/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/Slice.cc b/runtime/onert/core/src/ir/operation/Slice.cc new file mode 100644 index 000000000..888b563fb --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Slice.cc @@ -0,0 +1,36 @@ +/* + * 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) + : Operation{OperandConstraint::createExact(3u), inputs, outputs} +{ +} + +} // 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..b538e9206 --- /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(2u), inputs, outputs}, _param{param} +{ +} +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/SplitV.cc b/runtime/onert/core/src/ir/operation/SplitV.cc new file mode 100644 index 000000000..e638c9ac9 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/SplitV.cc @@ -0,0 +1,33 @@ +/* + * 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/SplitV.h" +#include <cassert> +#include "ir/OperationVisitor.h" +namespace onert +{ +namespace ir +{ +namespace operation +{ +void SplitV::accept(OperationVisitor &v) const { v.visit(*this); } +SplitV::SplitV(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/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/StatelessRandomUniform.cc b/runtime/onert/core/src/ir/operation/StatelessRandomUniform.cc new file mode 100644 index 000000000..cbb0ff251 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/StatelessRandomUniform.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/StatelessRandomUniform.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void StatelessRandomUniform::accept(OperationVisitor &v) const { v.visit(*this); } + +StatelessRandomUniform::StatelessRandomUniform(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/StridedSlice.cc b/runtime/onert/core/src/ir/operation/StridedSlice.cc new file mode 100644 index 000000000..2a7905995 --- /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(4u), inputs, outputs}, _param{param} +{ +} + +} // namespace operation +} // namespace ir +} // namespace onert diff --git a/runtime/onert/core/src/ir/operation/Tile.cc b/runtime/onert/core/src/ir/operation/Tile.cc new file mode 100644 index 000000000..5ba3df2ad --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Tile.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/Tile.h" + +#include <cassert> + +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ + +void Tile::accept(OperationVisitor &v) const { v.visit(*this); } + +Tile::Tile(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/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..997f98ab0 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/Transpose.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/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) + : Operation{OperandConstraint::createExact(2u), inputs, outputs} +{ +} + +} // 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/operation/While.cc b/runtime/onert/core/src/ir/operation/While.cc new file mode 100644 index 000000000..2505c60e3 --- /dev/null +++ b/runtime/onert/core/src/ir/operation/While.cc @@ -0,0 +1,33 @@ +/* + * 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/While.h" +#include "ir/OperationVisitor.h" + +namespace onert +{ +namespace ir +{ +namespace operation +{ +void While::accept(OperationVisitor &v) const { v.visit(*this); } +While::While(const OperandIndexSequence &inputs, const OperandIndexSequence &outputs, + const Param ¶m) + : Operation{OperandConstraint::createAny(), inputs, outputs}, _param{param} +{ +} +} // namespace operation +} // namespace ir +} // namespace onert 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..7d05acb28 --- /dev/null +++ b/runtime/onert/core/src/ir/verifier/Verifier.cc @@ -0,0 +1,146 @@ +/* + * 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 noexcept +{ + 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() | Remove::DUPLICATED | Remove::UNDEFINED) + { + const auto &operand = graph.operands().at(output); + for (const auto &use : operand.getUses()) + { + 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 noexcept +{ + auto &operations = graph.operations(); + uint32_t errors = 0; + operations.iterate([&](const OperationIndex &index, const Operation &node) { + for (auto operand_index : node.getInputs() | ir::Remove::UNDEFINED) + { + try + { + auto &operand = graph.operands().at(operand_index); + bool operand_has_use = operand.getUses().contains(index); + if (!operand_has_use) + { + VERBOSE(EdgeConsistencyChecker) << "[ERROR] EDGE MISMATCH : Missing USE edge - Operand " + << operand_index << " to Operation " << index + << std::endl; + errors += 1; + } + } + catch (const std::out_of_range &e) + { + VERBOSE(EdgeConsistencyChecker) + << "[ERROR] OPEARAND NOT FOUND : Operation " << index << " has Operand " + << operand_index << ", but the operand object is not present in the graph" << std::endl; + errors += 1; + } + } + for (auto operand_index : node.getOutputs() | ir::Remove::UNDEFINED) + { + try + { + auto &operand = graph.operands().at(operand_index); + if (operand.getDef() != index) + { + VERBOSE(EdgeConsistencyChecker) << "[ERROR] EDGE MISMATCH : Missing DEF edge - Operand" + << operand_index << " to Operation " << index + << std::endl; + errors += 1; + } + } + catch (const std::out_of_range &e) + { + VERBOSE(EdgeConsistencyChecker) + << "[ERROR] OPEARAND NOT FOUND : Operation " << index << " has Operand " + << operand_index << ", but the operand object is not present in the graph" << std::endl; + errors += 1; + } + } + }); + + VERBOSE(EdgeConsistencyChecker) << "Total Number of errors : " << errors << std::endl; + + return errors == 0; +} + +bool InputOutputChecker::verify(const Graph &graph) const noexcept +{ + for (auto operand_ind : + (graph.getInputs() + graph.getOutputs()) | Remove::DUPLICATED | Remove::UNDEFINED) + { + if (!graph.operands().exist(operand_ind)) + { + VERBOSE(InputOutputChecker) << "Input or Output tensor " << operand_ind << " does not exist."; + return false; + } + } + return true; +} + +} // 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..143db343a --- /dev/null +++ b/runtime/onert/core/src/ir/verifier/Verifier.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. + */ + +#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 noexcept = 0; +}; + +} // namespace verifier +} // namespace ir +} // namespace onert + +namespace onert +{ +namespace ir +{ +namespace verifier +{ + +class DAGChecker : public IVerifier +{ +public: + bool verify(const Graph &graph) const noexcept override; +}; + +class EdgeConsistencyChecker : public IVerifier +{ +public: + bool verify(const Graph &graph) const noexcept override; +}; + +/** + * @brief Check model input and output operands are really exist in the graph + */ +class InputOutputChecker : public IVerifier +{ +public: + bool verify(const Graph &graph) const noexcept 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/EventCollector.cc b/runtime/onert/core/src/util/EventCollector.cc new file mode 100644 index 000000000..de37276bf --- /dev/null +++ b/runtime/onert/core/src/util/EventCollector.cc @@ -0,0 +1,109 @@ +/* + * 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/EventCollector.h" + +// C++ standard libraries +#include <chrono> + +// POSIX standard libraries +#include <sys/time.h> +#include <sys/resource.h> + +namespace +{ + +std::string timestamp(void) +{ + auto now = std::chrono::steady_clock::now(); + return std::to_string( + std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count()); +} + +class DurationEventBuilder +{ +public: + DurationEventBuilder(const std::string &ts) : _ts{ts} {} + + DurationEvent build(const std::string &tid, const std::string &name, const std::string &ph) const + { + DurationEvent evt; + + evt.name = name; + evt.tid = tid; + evt.ph = ph; + evt.ts = _ts; + + return evt; + } + +private: + std::string _ts; +}; + +#ifdef DEBUG +inline void emit_rusage(EventRecorder *rec, const std::string &ts) +{ + struct rusage ru; + + getrusage(RUSAGE_SELF, &ru); + { + CounterEvent evt; + + evt.name = "maxrss"; + evt.ph = "C"; + evt.ts = ts; + evt.values["value"] = std::to_string(ru.ru_maxrss); + + rec->emit(evt); + } + + { + CounterEvent evt; + + evt.name = "minflt"; + evt.ph = "C"; + evt.ts = ts; + evt.values["value"] = std::to_string(ru.ru_minflt); + + rec->emit(evt); + } +} +#endif + +} // namespace + +void EventCollector::onEvent(const Event &event) +{ + auto ts = timestamp(); + + switch (event.edge) + { + case Edge::BEGIN: + _rec->emit(DurationEventBuilder(ts).build(event.backend, event.label, "B")); + break; + + case Edge::END: + _rec->emit(DurationEventBuilder(ts).build(event.backend, event.label, "E")); + break; + } + +// TODO: Add resurece measurement(e.g. RSS) +// when ready with low overhead in release build +#ifdef DEBUG + emit_rusage(_rec, ts); +#endif +} diff --git a/runtime/onert/core/src/util/EventCollector.h b/runtime/onert/core/src/util/EventCollector.h new file mode 100644 index 000000000..8154be592 --- /dev/null +++ b/runtime/onert/core/src/util/EventCollector.h @@ -0,0 +1,51 @@ +/* + * 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_UTIL_EVENT_COLLECTOR_H__ +#define __ONERT_UTIL_EVENT_COLLECTOR_H__ + +#include "util/EventRecorder.h" + +class EventCollector +{ +public: + enum class Edge + { + BEGIN, + END + }; + + struct Event + { + Edge edge; + std::string backend; + std::string label; + }; + +public: + EventCollector(EventRecorder *rec) : _rec{rec} + { + // DO NOTHING + } + +public: + void onEvent(const Event &event); + +protected: + EventRecorder *_rec; +}; + +#endif // __ONERT_UTIL_EVENT_COLLECTOR_H__ diff --git a/runtime/onert/core/src/util/EventCollectorGlobal.cc b/runtime/onert/core/src/util/EventCollectorGlobal.cc new file mode 100644 index 000000000..6c03a5b9a --- /dev/null +++ b/runtime/onert/core/src/util/EventCollectorGlobal.cc @@ -0,0 +1,94 @@ +/* + * 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 <iostream> + +#include "util/ConfigSource.h" +#include "util/EventWriter.h" + +namespace onert +{ +namespace util +{ + +EventCollectorGlobal::EventCollectorGlobal() : _recorder{}, _collector{&_recorder} +{ + // DO NOTHING +} + +EventCollectorGlobal::~EventCollectorGlobal() +{ + if (!_recorder.empty()) + { + try + { + // TODO Need better way for saved file path than the hardcoded path + EventWriter{_recorder}.writeToFile("trace.global.json", + EventWriter::WriteFormat::CHROME_TRACING); + } + catch (const std::exception &e) + { + std::cerr << "E: Fail to record event in EventCollectorGlobal: " << e.what() << std::endl; + } + } +} + +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/EventCollectorGlobal.h b/runtime/onert/core/src/util/EventCollectorGlobal.h new file mode 100644 index 000000000..1027ec84d --- /dev/null +++ b/runtime/onert/core/src/util/EventCollectorGlobal.h @@ -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. + */ + +#ifndef __ONERT_UTIL_EVENT_COLLECTOR_GLOBAL_H__ +#define __ONERT_UTIL_EVENT_COLLECTOR_GLOBAL_H__ + +#include "util/EventRecorder.h" +#include "util/EventCollector.h" + +namespace onert +{ +namespace util +{ + +/** + * @brief Singleton class for event collection from anywhere in code + * + */ +class EventCollectorGlobal +{ +public: + /** + * @brief Get the singleton object of this class + * + * @return EventCollectorGlobal& Singleton object + */ + static EventCollectorGlobal &get(); + +public: + /** + * @brief Getter for event collector object + * + * @return EventCollector& Collector object + */ + EventCollector &collector() { return _collector; } + +private: + EventCollectorGlobal(); + ~EventCollectorGlobal(); + +private: + EventRecorder _recorder; + EventCollector _collector; +}; + +/** + * @brief Helper class for emitting duration event which is handled automatically with ctor/dtor + * + */ +class EventDurationBlock +{ +public: + /** + * @brief Raise a duration event with type of BEGIN + * + * @param tag A label for the duration event + */ + EventDurationBlock(const std::string &tag); + /** + * @brief Raise a duration event with type of END + * + */ + ~EventDurationBlock(); + +private: + std::string _tag; +}; + +/** + * @brief Helper class for emitting duration event which is handled manually + * + * Usage: + * { + * ... + * EventDurationManual duration("some tag"); + * duration.begin(); + * ... + * ... // Code for duration + * ... + * duration.end(); + * } + * + */ +class EventDurationManual +{ +public: + /** + * @brief Construct a new Event Duration Manual object + * + * @param tag A label for the duration object + */ + EventDurationManual(const std::string &tag); + /** + * @brief Destroy the Event Duration Manual object + * + */ + ~EventDurationManual(); + + /** + * @brief Raise a duration event with type of BEGIN + * + */ + void begin(); + /** + * @brief Raise a duration event with type of END + * + */ + void end(); + +private: + std::string _tag; + bool _pair; +}; + +} // namespace util +} // namespace onert + +/** + * Helper Macro Definitions + * + * HOW TO USE + * + * void f(args) + * { + * EVENT_DURATION_FUNCTION(); + * ... + * if(cond) + * { + * EVENT_DURATION_REGION("if branch"); + * ... + * } + * ... + * } + */ + +#define EVENT_DURATION_FUNCTION() \ + ::onert::util::EventDurationBlock __event_duration__##__LINE__ { __FUNCTION__ } + +#define EVENT_DURATION_REGION(tag) \ + ::onert::util::EventDurationBlock __event_duration__##__LINE__ { tag } + +#endif // __ONERT_UTIL_EVENT_COLLECTOR_GLOBAL_H__ diff --git a/runtime/onert/core/src/util/EventRecorder.cc b/runtime/onert/core/src/util/EventRecorder.cc new file mode 100644 index 000000000..3714e4f02 --- /dev/null +++ b/runtime/onert/core/src/util/EventRecorder.cc @@ -0,0 +1,31 @@ +/* + * 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/EventRecorder.h" + +void EventRecorder::emit(const DurationEvent &evt) +{ + std::lock_guard<std::mutex> lock{_mu}; + + _duration_events.push_back(evt); +} + +void EventRecorder::emit(const CounterEvent &evt) +{ + std::lock_guard<std::mutex> lock{_mu}; + + _counter_events.push_back(evt); +} diff --git a/runtime/onert/core/src/util/EventRecorder.h b/runtime/onert/core/src/util/EventRecorder.h new file mode 100644 index 000000000..7af4c7ddb --- /dev/null +++ b/runtime/onert/core/src/util/EventRecorder.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_UTIL_EVENT_RECORDER_H__ +#define __ONERT_UTIL_EVENT_RECORDER_H__ + +#include <map> +#include <memory> +#include <mutex> + +#include <vector> + +struct Event +{ + std::string name; + std::string tid; + std::string ph; /* REQUIRED */ + std::string ts; /* REQUIRED */ +}; + +struct DurationEvent : public Event +{ + // TO BE FILLED +}; + +struct CounterEvent : public Event +{ + std::map<std::string, std::string> values; +}; + +// +// Record Event as Chrome Trace Event File Format +// +// Refrence: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit +// +class EventRecorder +{ +public: + EventRecorder() = default; + +public: + void emit(const DurationEvent &evt); + void emit(const CounterEvent &evt); + +public: + bool empty() { return _duration_events.empty() && _counter_events.empty(); } + const std::vector<DurationEvent> &duration_events() const { return _duration_events; } + const std::vector<CounterEvent> &counter_events() const { return _counter_events; } + +private: + std::mutex _mu; + std::vector<DurationEvent> _duration_events; + std::vector<CounterEvent> _counter_events; +}; + +#endif // __ONERT_UTIL_EVENT_RECORDER_H__ diff --git a/runtime/onert/core/src/util/EventWriter.cc b/runtime/onert/core/src/util/EventWriter.cc new file mode 100644 index 000000000..dacb40e64 --- /dev/null +++ b/runtime/onert/core/src/util/EventWriter.cc @@ -0,0 +1,574 @@ +/* + * 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 "util/EventWriter.h" + +#include <sstream> +#include <vector> +#include <unordered_map> +#include <json/json.h> +#include <assert.h> +#include <utility> +#include <map> +#include <set> +#include <stdint.h> +#include <fstream> + +// json type for Chrome Event Trace +namespace +{ + +std::string quote(const std::string &value) +{ + std::stringstream ss; + ss << '"' << value << '"'; + return ss.str(); +} + +std::string field(const std::string &k, const std::string &v) +{ + std::stringstream ss; + ss << quote(k) << " : " << quote(v); + return ss.str(); +} + +struct Content // One Entry in Chrome Event Trace +{ + std::vector<std::pair<std::string, std::string>> flds; + std::vector<std::pair<std::string, std::string>> args; +}; + +std::string object(const Content &content) +{ + std::stringstream ss; + + ss << "{ "; + + ss << field(content.flds[0].first, content.flds[0].second); + + for (uint32_t n = 1; n < content.flds.size(); ++n) + { + ss << ", " << field(content.flds.at(n).first, content.flds.at(n).second); + } + + if (content.args.size() > 0) + { + ss << ", " << quote("args") << " : { "; + ss << field(content.args.at(0).first, content.args.at(0).second); + + for (uint32_t n = 1; n < content.args.size(); ++n) + { + ss << ", " << field(content.args.at(n).first, content.args.at(n).second); + } + + ss << "}"; + } + + ss << " }"; + + return ss.str(); +} + +void fill(Content &content, const Event &evt) +{ + content.flds.emplace_back("name", evt.name); + content.flds.emplace_back("pid", "0"); + content.flds.emplace_back("tid", evt.tid); + content.flds.emplace_back("ph", evt.ph); + content.flds.emplace_back("ts", evt.ts); +} + +std::string object(const DurationEvent &evt) +{ + Content content; + + fill(content, evt); + + return ::object(content); +} + +std::string object(const CounterEvent &evt) +{ + Content content; + + fill(content, evt); + + for (auto it = evt.values.begin(); it != evt.values.end(); ++it) + { + content.args.emplace_back(it->first, it->second); + } + + return ::object(content); +} + +} // namespace + +// md table type +namespace +{ + +void writeMDTableRow(std::ostream &os, const std::vector<std::string> &list) +{ + os << "| "; + for (auto &key : list) + { + os << key << " | "; + } + os << "\n"; +} + +struct MDContent +{ + std::string name; + uint64_t begin_ts; + uint64_t end_ts; + uint32_t min_rss; + uint32_t max_rss; + uint32_t min_page_reclaims; + uint32_t max_page_reclaims; + + MDContent() + : begin_ts(0), end_ts(0), min_rss(UINT32_MAX), max_rss(0), min_page_reclaims(UINT32_MAX), + max_page_reclaims(0) + { + // DO NOTHING + } + + virtual ~MDContent() = default; + + void updateRss(uint32_t rss) + { + if (min_rss == UINT32_MAX) + min_rss = rss; + if (max_rss == 0) + max_rss = rss; + + if (min_rss > rss) + min_rss = rss; + else if (max_rss < rss) + max_rss = rss; + } + + void updateMinflt(uint32_t minflt) + { + if (min_page_reclaims == UINT32_MAX) + min_page_reclaims = minflt; + if (max_page_reclaims == 0) + max_page_reclaims = minflt; + + if (min_page_reclaims > minflt) + min_page_reclaims = minflt; + else if (max_page_reclaims < minflt) + max_page_reclaims = minflt; + } + + virtual void write(std::ostream &os) const = 0; +}; + +struct OpSeq : public MDContent +{ + std::string backend; + uint64_t graph_latency; + + struct OpSeqCmp + { + bool operator()(const OpSeq &lhs, const OpSeq &rhs) const + { + return lhs.begin_ts < rhs.begin_ts; + } + bool operator()(const OpSeq &lhs, const OpSeq &rhs) { return lhs.begin_ts < rhs.begin_ts; } + bool operator()(OpSeq &lhs, OpSeq &rhs) { return lhs.begin_ts < rhs.begin_ts; } + }; + + void write(std::ostream &os) const override + { + uint64_t opseq_latency = end_ts - begin_ts; + double opseq_per = static_cast<double>(opseq_latency) / graph_latency * 100.0; + writeMDTableRow(os, {name, backend, std::to_string(opseq_latency), std::to_string(opseq_per), + std::to_string(min_rss), std::to_string(max_rss), + std::to_string(min_page_reclaims), std::to_string(max_page_reclaims)}); + } +}; + +struct Graph : public MDContent +{ + std::set<OpSeq, OpSeq::OpSeqCmp> opseqs; + + void setOpSeqs(const std::map<std::string, OpSeq> &name_to_opseq) + { + uint64_t graph_latency = end_ts - begin_ts; + for (auto it : name_to_opseq) + { + auto opseq = it.second; + opseq.graph_latency = graph_latency; + + opseqs.insert(opseq); + + updateRss(opseq.min_rss); + updateRss(opseq.max_rss); + updateMinflt(opseq.min_page_reclaims); + updateMinflt(opseq.max_page_reclaims); + } + } + + void write(std::ostream &os) const override + { + static std::vector<std::string> graph_headers{"latency(us)", "rss_min(kb)", "rss_max(kb)", + "page_reclaims_min", "page_reclaims_max"}; + + static std::vector<std::string> graph_headers_line{"-----------", "-------", "-------", + "-----------------", "-----------------"}; + + // Graph's Header + writeMDTableRow(os, graph_headers); + writeMDTableRow(os, graph_headers_line); + + // Graph's contents + writeMDTableRow(os, {std::to_string(end_ts - begin_ts), std::to_string(min_rss), + std::to_string(max_rss), std::to_string(min_page_reclaims), + std::to_string(max_page_reclaims)}); + + os << "\n"; + + static std::vector<std::string> opseq_headers{ + "OpSeq name", "backend", "latency(us)", "latency(%)", + "rss_min(kb)", "rss_max(kb)", "page_reclaims_min", "page_reclaims_max"}; + + static std::vector<std::string> opseq_headers_line{ + "----------", "-------", "-----------", "-----------", + "-------", "-------", "-----------------", "-----------------"}; + + os << "## OpSequences \n"; + + // OpSeq's Header + writeMDTableRow(os, opseq_headers); + writeMDTableRow(os, opseq_headers_line); + + // OpSeq's contents + for (auto opseq : opseqs) + { + opseq.write(os); + } + + os << "\n"; + } +}; + +struct MDTableBuilder +{ + MDTableBuilder(const std::vector<DurationEvent> &duration_events, + const std::vector<CounterEvent> &counter_events) + : _duration_events(duration_events), _counter_events(counter_events) + { +// when ready with low overhead in release build +#ifdef DEBUG + for (const auto &evt : _counter_events) + { + uint64_t ts = std::stoull(evt.ts); + auto &name = evt.name; + assert(name.compare("maxrss") == 0 || name.compare("minflt") == 0); + assert(evt.values.size() == 1); + auto &val = evt.values.begin()->second; + if (_ts_to_values.find(ts) == _ts_to_values.end()) + { + std::pair<uint32_t, uint32_t> values; + if (name.compare("maxrss") == 0) + values.first = std::stoul(val); + else + values.second = std::stoul(val); + _ts_to_values.insert({ts, values}); + } + else + { + auto &values = _ts_to_values.at(ts); + if (name.compare("maxrss") == 0) + values.first = std::stoul(val); + else + values.second = std::stoul(val); + } + } +#endif + } + + MDTableBuilder &build() + { + for (auto &it : divideGraph()) + { + size_t begin_idx = it.first; + size_t end_idx = it.second; + std::map<std::string, OpSeq> name_to_opseq; + for (size_t i = begin_idx + 1; i < end_idx; ++i) + { + const auto &evt = _duration_events[i]; + assert(evt.name.compare("Graph") != 0); + assert(evt.ph.compare("B") == 0 || evt.ph.compare("E") == 0); + if (evt.ph.compare("B") == 0) + { + assert(name_to_opseq.find(evt.name) == name_to_opseq.end()); + name_to_opseq.insert({evt.name, makeOpSeq(evt)}); + } + else + { + assert(name_to_opseq.find(evt.name) != name_to_opseq.end()); + auto &opseq = name_to_opseq.at(evt.name); + updateOpSeq(opseq, evt); + } + } + + _graphs.emplace_back(makeGraph(begin_idx, end_idx, name_to_opseq)); + } + + return *this; + } + + std::vector<std::pair<size_t, size_t>> divideGraph() + { + std::vector<std::pair<size_t, size_t>> graph_idx_list; // pair<begin_idx, end_idx> + for (size_t i = 0, begin_idx = 0; i < _duration_events.size(); ++i) + { + const auto &evt = _duration_events.at(i); + if (evt.name.compare("Graph") == 0) + { + if (evt.ph.compare("B") == 0) + begin_idx = i; + else + graph_idx_list.emplace_back(begin_idx, i); + } + } + return graph_idx_list; + } + + OpSeq makeOpSeq(const DurationEvent &evt) + { + OpSeq opseq; + opseq.name = evt.name; + opseq.begin_ts = std::stoull(evt.ts); + opseq.backend = evt.tid; +#ifdef DEBUG + opseq.updateRss(_ts_to_values.at(opseq.begin_ts).first); + opseq.updateMinflt(_ts_to_values.at(opseq.begin_ts).second); +#else + opseq.updateRss(0); + opseq.updateMinflt(0); +#endif + return opseq; + } + + void updateOpSeq(OpSeq &opseq, const DurationEvent &evt) + { + opseq.end_ts = std::stoull(evt.ts); +#ifdef DEBUG + opseq.updateRss(_ts_to_values.at(opseq.end_ts).first); + opseq.updateMinflt(_ts_to_values.at(opseq.end_ts).second); +#else + opseq.updateRss(0); + opseq.updateMinflt(0); +#endif + } + + Graph makeGraph(size_t begin_idx, size_t end_idx, + const std::map<std::string, OpSeq> &name_to_opseq) + { + Graph graph; + graph.name = "Graph"; + graph.begin_ts = std::stoull(_duration_events[begin_idx].ts); + graph.end_ts = std::stoull(_duration_events[end_idx].ts); + graph.setOpSeqs(name_to_opseq); +#ifdef DEBUG + graph.updateRss(_ts_to_values.at(graph.begin_ts).first); + graph.updateMinflt(_ts_to_values.at(graph.begin_ts).second); + graph.updateRss(_ts_to_values.at(graph.end_ts).first); + graph.updateMinflt(_ts_to_values.at(graph.end_ts).second); +#else + graph.updateRss(0); + graph.updateMinflt(0); +#endif + return graph; + } + + void write(std::ostream &os) + { + // Write contents + for (size_t i = 0; i < _graphs.size(); ++i) + { + os << "# Graph " << i << "\n"; + _graphs.at(i).write(os); + } + } + + const std::vector<DurationEvent> &_duration_events; + const std::vector<CounterEvent> &_counter_events; + // timestamp to std::pair<maxrss, minflt> + std::unordered_map<uint64_t, std::pair<uint32_t, uint32_t>> _ts_to_values; + std::vector<Graph> _graphs; +}; + +} // namespace + +EventWriter::EventWriter(const EventRecorder &recorder) : _recorder(recorder) +{ + // DO NOTHING +} + +void EventWriter::writeToFiles(const std::string &base_filepath) +{ + // Note. According to an internal issue, let snpe json as just file name not '.snpe.json' + writeToFile(base_filepath, WriteFormat::SNPE_BENCHMARK); + writeToFile(base_filepath + ".chrome.json", WriteFormat::CHROME_TRACING); + writeToFile(base_filepath + ".table.md", WriteFormat::MD_TABLE); +} + +void EventWriter::writeToFile(const std::string &filepath, WriteFormat write_format) +{ + std::ofstream os{filepath, std::ofstream::out}; + switch (write_format) + { + case WriteFormat::CHROME_TRACING: + writeChromeTrace(os); + break; + case WriteFormat::SNPE_BENCHMARK: + writeSNPEBenchmark(os); + break; + case WriteFormat::MD_TABLE: + writeMDTable(os); + break; + default: + assert(!"Invalid value"); + break; + } +} + +void EventWriter::writeSNPEBenchmark(std::ostream &os) +{ + Json::Value root; + auto &exec_data = root["Execution_Data"] = Json::Value{Json::objectValue}; + + struct Stat + { + uint64_t sum = 0; + uint64_t count = 0; + uint64_t max = 0; + uint64_t min = std::numeric_limits<uint64_t>::max(); + + void accumulate(uint64_t val) + { + sum += val; + count++; + max = std::max(max, val); + min = std::min(min, val); + } + }; + + // Memory + { + std::unordered_map<std::string, Stat> mem_stats; + for (auto &evt : _recorder.counter_events()) + { + auto &mem_stat = mem_stats[evt.name]; + uint64_t val = std::stoull(evt.values.at("value")); + mem_stat.accumulate(val); + } + + auto &mem = exec_data["memory"] = Json::Value{Json::objectValue}; + for (auto &kv : mem_stats) + { + auto &key = kv.first; + auto &val = kv.second; + mem[key]["Avg_Size"] = val.sum / val.count; + mem[key]["Max_Size"] = val.max; + mem[key]["Min_Size"] = val.min; + mem[key]["Runtime"] = "NA"; + } + } + + // Operation Execution Time + { + // NOTE This assumes _duration_events is sorted by "ts" ascending + + // 2D keys : stats[tid][name] + std::unordered_map<std::string, std::unordered_map<std::string, Stat>> stats; + std::unordered_map<std::string, std::unordered_map<std::string, uint64_t>> begin_timestamps; + for (auto &evt : _recorder.duration_events()) + { + auto &stat = stats[evt.tid][evt.name]; + auto &begin_ts = begin_timestamps[evt.tid][evt.name]; + uint64_t timestamp = std::stoull(evt.ts); + if (evt.ph == "B") + { + if (begin_ts != 0) + throw std::runtime_error{"Invalid Data"}; + begin_ts = timestamp; + } + else if (evt.ph == "E") + { + if (begin_ts == 0 || timestamp < begin_ts) + throw std::runtime_error{"Invalid Data"}; + stat.accumulate(timestamp - begin_ts); + begin_ts = 0; + } + else + throw std::runtime_error{"Invalid Data - invalid value for \"ph\" : \"" + evt.ph + "\""}; + } + + for (auto &kv : begin_timestamps) + for (auto &kv2 : kv.second) + if (kv2.second != 0) + throw std::runtime_error{"Invalid Data - B and E pair does not match."}; + + for (auto &kv : stats) + { + auto &tid = kv.first; + auto &map = kv.second; + auto &json_tid = exec_data[tid] = Json::Value{Json::objectValue}; + for (auto &kv : map) + { + auto &name = kv.first; + auto &val = kv.second; + json_tid[name]["Avg_Time"] = val.sum / val.count; + json_tid[name]["Max_Time"] = val.max; + json_tid[name]["Min_Time"] = val.min; + json_tid[name]["Runtime"] = tid; + } + } + } + + os << root; +} + +void EventWriter::writeChromeTrace(std::ostream &os) +{ + os << "{\n"; + os << " " << quote("traceEvents") << ": [\n"; + + for (auto &evt : _recorder.duration_events()) + { + os << " " << object(evt) << ",\n"; + } + + for (auto &evt : _recorder.counter_events()) + { + os << " " << object(evt) << ",\n"; + } + + os << " { }\n"; + os << " ]\n"; + os << "}\n"; +} + +void EventWriter::writeMDTable(std::ostream &os) +{ + MDTableBuilder(_recorder.duration_events(), _recorder.counter_events()).build().write(os); +} diff --git a/runtime/onert/core/src/util/EventWriter.h b/runtime/onert/core/src/util/EventWriter.h new file mode 100644 index 000000000..7e838ca82 --- /dev/null +++ b/runtime/onert/core/src/util/EventWriter.h @@ -0,0 +1,51 @@ +/* + * 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_UTIL_EVENT_WRITER_H__ +#define __ONERT_UTIL_EVENT_WRITER_H__ + +#include "EventRecorder.h" + +#include <string> +#include <ostream> + +class EventWriter +{ +public: + enum class WriteFormat + { + CHROME_TRACING, + SNPE_BENCHMARK, + MD_TABLE, + }; + +public: + EventWriter(const EventRecorder &recorder); + +public: + void writeToFiles(const std::string &base_filepath); + void writeToFile(const std::string &filepath, WriteFormat write_format); + +private: + void writeSNPEBenchmark(std::ostream &os); + void writeChromeTrace(std::ostream &os); + void writeMDTable(std::ostream &os); + +private: + const EventRecorder &_recorder; +}; + +#endif // __ONERT_UTIL_EVENT_WRITER_H__ 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..1f468a8b5 --- /dev/null +++ b/runtime/onert/core/src/util/ShapeInference.cc @@ -0,0 +1,1131 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. 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 "util/ShapeInference.h" +#include "util/logging.h" + +#include <cassert> +#include <numeric> +#include <sstream> +#include <cmath> + +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; +} + +} // namespace + +namespace bcq +{ +inline int getOutputSize(const ir::Shape &cluster_shape, const int32_t *cluster_buf) +{ + int size = 0; + for (int idx = 0; idx < cluster_shape.dim(0); idx++) + { + size += cluster_buf[idx * 2 + 1]; + } + return size; +} +} // namespace bcq + +// +// Shape inference +// + +// 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, + const ir::Dilation dilation = {1, 1}) +{ + int32_t out_h = 0, out_w = 0; + int32_t effective_filter_w_size = (ker_w - 1) * dilation.width_factor + 1; + int32_t effective_filter_h_size = (ker_h - 1) * dilation.height_factor + 1; + 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 - effective_filter_h_size + 1, stride.vertical); + out_w = ceil_div(in_w - effective_filter_w_size + 1, stride.horizontal); + break; + case ir::PaddingType::EXPLICIT: + out_h = + (in_h + pad.param.top + pad.param.bottom - effective_filter_h_size) / stride.vertical + 1; + out_w = + (in_w + pad.param.left + pad.param.right - effective_filter_w_size) / stride.horizontal + + 1; + break; + default: + assert(false); + } + + return {out_h, out_w}; +} + +ir::Shape inferEltwiseShape(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape) +{ + return broadcastShapes(lhs_shape, rhs_shape); +} + +ir::Shape inferArgMaxShape(const ir::Shape &input_shape, int axis, int rank) +{ + if (axis < 0 || axis >= rank) + { + throw std::runtime_error("ArgMax shape inference: Wrong axis value " + std::to_string(axis)); + } + + ir::Shape out_shape; + for (int idx = 0; idx < rank; ++idx) + { + if (idx != axis) + { + int32_t input_dim = input_shape.dim(idx); + out_shape.append(input_dim); + } + } + + return out_shape; +} + +ir::Shape inferReduceShape(const ir::Shape &input_shape, const std::vector<int> &axes, + bool keep_dims) +{ + int num_axis = axes.size(); + int input_num_dims = input_shape.rank(); + if (input_num_dims == 0) + { + ir::Shape out_shape(0); + return out_shape; + } + if (keep_dims) + { + ir::Shape out_shape; + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axis; ++axis_idx) + { + if (axes[axis_idx] == idx || axes[axis_idx] + input_num_dims == idx) + { + is_axis = true; + break; + } + } + if (is_axis) + { + out_shape.append(1); + } + else + { + out_shape.append(input_shape.dim(idx)); + } + } + return out_shape; + } + else + { + // Calculates size of reducing axis. + int num_reduce_axis = num_axis; + for (int i = 0; i < num_axis; ++i) + { + int current = axes[i]; + if (!(-input_num_dims <= current && current < input_num_dims)) + throw std::runtime_error{"Invalid dim value " + std::to_string(current)}; + if (current < 0) + { + current += input_num_dims; + } + for (int j = 0; j < i; ++j) + { + int previous = axes[j]; + if (previous < 0) + { + previous += input_num_dims; + } + if (current == previous) + { + --num_reduce_axis; + break; + } + } + } + // Determines output dimensions. + ir::Shape out_shape; + int num_skip_axis = 0; + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axis; ++axis_idx) + { + if (axes[axis_idx] == idx || axes[axis_idx] + input_num_dims == idx) + { + ++num_skip_axis; + is_axis = true; + break; + } + } + if (!is_axis) + { + out_shape.append(input_shape.dim(idx)); + } + } + return out_shape; + } +} + +ir::Shape inferBatchMatMulShape(const ir::Shape &lhs_shape, const ir::Shape &rhs_shape, + const ir::operation::BatchMatMul::Param ¶m) +{ + bool adj_x = param.adj_x; + bool adj_y = param.adj_y; + ir::Shape output_shape; + + int output_rank = std::max(lhs_shape.rank(), rhs_shape.rank()); + + // Extend lhs and rhs shape + ir::Shape extended_lhs_shape(lhs_shape); + ir::Shape extended_rhs_shape(rhs_shape); + extended_lhs_shape.extendRank(output_rank); + extended_rhs_shape.extendRank(output_rank); + + for (int i = 0; i < output_rank - 2; i++) + { + const int lhs_dim = extended_lhs_shape.dim(i); + const int rhs_dim = extended_rhs_shape.dim(i); + int broadcast_dim = lhs_dim; + if (lhs_dim != rhs_dim) + { + if (lhs_dim == 1) + { + broadcast_dim = rhs_dim; + } + else if (rhs_dim != 1) + { + throw std::runtime_error{"BatchMatMul shape inference: invalid brodcasting input shape"}; + } + } + + output_shape.append(broadcast_dim); + } + + // Fill in the matmul dimensions. + int lhs_rows_index = adj_x ? output_rank - 1 : output_rank - 2; + int rhs_cols_index = adj_y ? output_rank - 2 : output_rank - 1; + + output_shape.append(extended_lhs_shape.dim(lhs_rows_index)); + output_shape.append(extended_rhs_shape.dim(rhs_cols_index)); + + return output_shape; +} + +/* + * shp_shape : SHAPE input tensor's shape + * shp_buf : SHAPE input tensor's buffer + */ +ir::Shape inferBroadcastToShape(const ir::Shape shp_shape, const int32_t *shp_buf) +{ + + const int num_elements = shp_shape.num_elements(); + + assert(num_elements != 0); + assert(shp_buf); + + ir::Shape new_shape(num_elements); + + for (int i = 0; i < num_elements; ++i) + { + assert(shp_buf[i] != 0); // It shouldn't be 0. + new_shape.dim(i) = shp_buf[i]; + } + + return new_shape; +} + +ir::Shape inferConcatShape(const Shapes &in_shapes, const ir::operation::Concat::Param ¶m) +{ + const int32_t concat_axis = param.axis >= 0 ? param.axis : in_shapes[0].rank() + 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) + { + if (in_shape.rank() != first_in_shape.rank()) + throw std::runtime_error("Rank in all input tensors should be same"); + + for (int64_t dim_idx = 0; dim_idx < in_shape.rank(); ++dim_idx) + if (!(dim_idx == concat_axis || in_shape.dim(dim_idx) == first_in_shape.dim(dim_idx))) + throw std::runtime_error("All tensor should have same dimension " + "except dimension on passed axis"); + } + + // 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; +} + +ir::Shape inferConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::Conv2D::Param ¶m, ir::Layout layout) +{ + if (param.stride.horizontal == 0 || param.stride.vertical == 0) + throw std::runtime_error{"Conv2D: stride values must be positive"}; + + 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, param.dilation); + + return ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.N}; +} + +ir::Shape inferDepthwiseConv2DShape(const ir::Shape &in_shape, const ir::Shape &ker_shape, + const ir::operation::DepthwiseConv2D::Param ¶m, + ir::Layout layout) +{ + if (param.stride.horizontal == 0 || param.stride.vertical == 0) + throw std::runtime_error{"DepthwiseConv2D: stride values must be positive"}; + + 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, param.dilation); + + return ir::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.C}; +} + +ir::Shape inferExpandDimsShape(const ir::Shape &in_shape, int32_t axis) +{ + ir::Shape out_shape(in_shape.rank() + 1); + + axis = ((axis >= 0) ? axis : /* when axis < 0 */ (out_shape.rank() + axis)); + if (!(0 <= axis && axis <= in_shape.rank())) + throw std::runtime_error("axis of dim is out of range"); + + for (int x = 0, out_x = 0; out_x < out_shape.rank(); ++out_x) + { + if (out_x == axis) + out_shape.dim(out_x) = 1; + else + out_shape.dim(out_x) = in_shape.dim(x++); + } + + return out_shape; +} + +ir::Shape inferFillShape(const ir::Shape &in_shape, const int32_t *in_buf) +{ + ir::Shape out_shape(in_shape.dim(0)); + + for (int out_x = 0; out_x < out_shape.rank(); ++out_x) + { + out_shape.dim(out_x) = in_buf[out_x]; + } + + return out_shape; +} + +ir::Shape 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})}; +} + +ir::Shape inferBCQFullyConnectedShape(const ir::Shape &in_shape, const ir::Shape &cluster_shape, + const int32_t *cluster_buf) +{ + assert(cluster_shape.rank() == 2); + assert(cluster_shape.dim(1) == 2); + + const auto input_size = in_shape.dim(1); + const auto output_size = bcq::getOutputSize(cluster_shape, cluster_buf); + + return {ir::Shape({output_size, input_size})}; +} + +ir::Shape inferBCQGatherShape(const ir::Shape &indices_shape, const ir::Shape &cluster_shape, + const int32_t *cluster_buf, int rank, + const ir::operation::BCQGather::Param ¶m) +{ + ir::Shape out_shape; + ir::Shape in_original_shape; + + assert(cluster_shape.rank() == 2); + assert(cluster_shape.dim(1) == 2); + + auto hidden_size = param.input_hidden_size; + auto axis = param.axis; + + in_original_shape.append(bcq::getOutputSize(cluster_shape, cluster_buf)); + in_original_shape.append(hidden_size); + + const int indices_rank = indices_shape.rank(); + for (int idx = 0; idx < rank; ++idx) + { + if (idx == (int)axis) + { + for (int indices_idx = 0; indices_idx < indices_rank; indices_idx++) + { + out_shape.append(indices_shape.dim(indices_idx)); + } + } + else + { + out_shape.append(in_original_shape.dim(idx)); + } + } + + return out_shape; +} + +ir::Shape inferGatherShape(const ir::Shape &input_shape, const ir::Shape &indices_shape, int axis, + int rank) +{ + ir::Shape out_shape; + + const int indices_rank = indices_shape.rank(); + + for (int idx = 0; idx < rank; ++idx) + { + if (idx == axis) + { + for (int indices_idx = 0; indices_idx < indices_rank; indices_idx++) + { + out_shape.append(indices_shape.dim(indices_idx)); + } + } + else + { + out_shape.append(input_shape.dim(idx)); + } + } + + return out_shape; +} + +ir::Shape inferOnehotShape(const ir::Shape &input_shape, const int depth, int axis) +{ + assert(depth >= 0); + const auto rank = input_shape.rank() + 1; + ir::Shape newShape(rank); + + axis = (axis == -1) ? (rank - 1) : axis; + + for (int i = 0; i < rank; ++i) + { + if (i < axis) + { + newShape.dim(i) = input_shape.dim(i); + } + else if (i == axis) + { + newShape.dim(i) = depth; + } + else + { + newShape.dim(i) = input_shape.dim(i - 1); + } + } + + return newShape; +} + +ir::Shape inferPackShape(const ir::Shape &input_shape, int axis, int rank, int num) +{ + ir::Shape out_shape; + int in_idx = 0; + + for (int out_idx = 0; out_idx < rank; ++out_idx) + { + if (out_idx == axis) + { + out_shape.append(num); + } + else + { + out_shape.append(input_shape.dim(in_idx++)); + } + } + + return out_shape; +} + +ir::Shape inferPadShape(const ir::Shape &in_shape, const int32_t *pad_buf, const size_t num_pads) +{ + assert(num_pads % 2 == 0); + const int32_t rank = num_pads / 2; + + ir::Shape ret(rank); + for (int32_t i = 0; i < rank; ++i) + { + const auto before_padding = pad_buf[i * 2]; + const auto after_padding = pad_buf[i * 2 + 1]; + + ret.dim(i) = in_shape.dim(i) + before_padding + after_padding; + } + + return ret; +} + +ir::Shape inferPoolShape(const ir::Shape &in_shape, const ir::operation::Pool2D::Param ¶m, + const ir::Layout layout) +{ + if (param.stride.horizontal == 0 || param.stride.vertical == 0) + throw std::runtime_error{"Pool2D: stride values must be positive"}; + + 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}; +} + +ir::Shape inferResizeBilinearShape(const ir::Shape &in_shape, const int32_t output_height, + const int32_t output_width) +{ + assert(in_shape.rank() == 4); + if (output_height < 0) + { + throw std::runtime_error{"ResizeBilinear: size value must be positive value, output_height = " + + std::to_string(output_height)}; + } + if (output_width < 0) + { + throw std::runtime_error{"ResizeBilinear: size value must be positive value, output_width = " + + std::to_string(output_width)}; + } + + ir::Shape ret(in_shape.rank()); + + ret.dim(0) = in_shape.dim(0); + ret.dim(1) = output_height; + ret.dim(2) = output_width; + ret.dim(3) = in_shape.dim(3); + + return ret; +} + +template <typename T> ir::Shape inferRangeShape(T start_val, T limit_val, T delta_val) +{ + ir::Shape out_shape(static_cast<int>(1)); + + out_shape.dim(0) = + (std::is_integral<T>::value + ? ((std::abs(start_val - limit_val) + std::abs(delta_val) - 1) / std::abs(delta_val)) + : std::ceil(std::abs((start_val - limit_val) / delta_val))); + return out_shape; +} + +// template instantiation +template ir::Shape inferRangeShape(int start_val, int limit_val, int delta_val); +template ir::Shape inferRangeShape(float start_val, float limit_val, float delta_val); + +ir::Shape inferReshapeShape(const int32_t *shape_buf, const int32_t shape_num_elements, + const size_t total_num_elements) +{ + ir::Shape ret(shape_num_elements); + int32_t flatten_dim = ir::Shape::UNSPECIFIED_DIM; + for (int32_t i = 0; i < shape_num_elements; ++i) + { + if (shape_buf[i] < 0) + { + if (flatten_dim != ir::Shape::UNSPECIFIED_DIM) + throw std::runtime_error("Reshape: 2nd param has special dim(for flatten) more than twice"); + flatten_dim = i; + ret.dim(i) = 1; + } + else + { + ret.dim(i) = shape_buf[i]; + } + } + if (flatten_dim != ir::Shape::UNSPECIFIED_DIM) + ret.dim(flatten_dim) = total_num_elements / ret.num_elements(); + + // Check reshapable + if (total_num_elements != static_cast<size_t>(ret.num_elements())) + throw std::runtime_error("Reshape: 2nd param is not compatible with the shape of input"); + + return ret; +} + +ir::Shape inferSelectShape(const ir::Shape &input_cond_shape, const ir::Shape &input_true_shape, + const ir::Shape &input_false_shape) +{ + auto haveSameShapes = [](const ir::Shape &input_cond_shape, const ir::Shape &input_true_shape, + const ir::Shape &input_false_shape) { + if ((input_cond_shape.rank() != input_true_shape.rank()) || + input_cond_shape.rank() != input_false_shape.rank()) + { + return false; + } + + int rank = input_cond_shape.rank(); + for (int i = 0; i < rank; ++i) + { + if (input_cond_shape.dim(i) != input_true_shape.dim(i) || + input_cond_shape.dim(i) != input_false_shape.dim(i)) + { + return false; + } + } + + return true; + }; + + auto calculateShape = [](const ir::Shape &input_cond_shape, const ir::Shape &input_true_shape, + const ir::Shape &input_false_shape, ir::Shape &new_shape) { + ir::Shape cond_shape = input_cond_shape; + ir::Shape true_shape = input_true_shape; + ir::Shape false_shape = input_false_shape; + int most_rank = + (cond_shape.rank() >= true_shape.rank()) && (cond_shape.rank() >= false_shape.rank()) + ? cond_shape.rank() + : (false_shape.rank() >= true_shape.rank() ? false_shape.rank() : true_shape.rank()); + + ir::Shape calculate_shape(most_rank); + + cond_shape.extendRank(most_rank); + true_shape.extendRank(most_rank); + false_shape.extendRank(most_rank); + + for (int i = 0; i < most_rank; ++i) + { + calculate_shape.dim(i) = + (cond_shape.dim(i) >= true_shape.dim(i)) && (cond_shape.dim(i) >= false_shape.dim(i)) + ? cond_shape.dim(i) + : (false_shape.dim(i) >= true_shape.dim(i) ? false_shape.dim(i) : true_shape.dim(i)); + + if ((cond_shape.dim(i) != calculate_shape.dim(i) && cond_shape.dim(i) != 1) || + (true_shape.dim(i) != calculate_shape.dim(i) && true_shape.dim(i) != 1) || + (false_shape.dim(i) != calculate_shape.dim(i) && false_shape.dim(i) != 1)) + { + return false; + } + } + + new_shape = calculate_shape; + + return true; + }; + + bool havesame = haveSameShapes(input_cond_shape, input_true_shape, input_false_shape); + if (havesame) + { + return input_cond_shape; + } + + ir::Shape new_shape; + bool possible = calculateShape(input_cond_shape, input_true_shape, input_false_shape, new_shape); + + if (!possible) + { + throw std::runtime_error("Broadcasting is not possible."); + } + + return new_shape; +} + +ir::Shape inferSliceShape(const ir::Shape &input_shape, const int32_t *begins_buf, + const int32_t *sizes_buf) +{ + const uint32_t rank = input_shape.rank(); + ir::Shape out_shape(rank); + + for (uint32_t idx = 0; idx < rank; ++idx) + { + const auto input_dim = input_shape.dim(idx); + + // begin is zero-based + auto begin = begins_buf[idx]; + if (begin < 0) + throw std::runtime_error("shape inference Slice: Invalid begin."); + + // size is one-based + auto size = sizes_buf[idx]; + if (size < -1) + throw std::runtime_error("shape inference Slice: Invalid size."); + + if (size == -1) + { + size = input_dim - begin; + } + else + { + if (input_dim < begin + size) + throw std::runtime_error("shape inference Slice: Invalid begin and size."); + } + out_shape.dim(idx) = size; + } + + return out_shape; +} + +ir::Shape inferSpaceToBatchNDShape(const ir::Shape &input_shape, const ir::Shape &block_shape_shape, + const ir::Shape &padding_shape, const int32_t *block_shape_buf, + const int32_t *padding_buf) +{ + const uint32_t rank = input_shape.rank(); + ir::Shape out_shape(rank); + + // Currently, only 4D NHWC input/output op_context are supported. + // The 4D array need to have exactly 2 spatial dimensions. + // TODO(nupurgarg): Support arbitrary dimension in SpaceToBatchND. + const int32_t kInputDimensionNum = 4; + const int32_t kBlockSizeDimensionNum = 1; + const int32_t kSpatialDimensionNum = 2; + + UNUSED_RELEASE(kInputDimensionNum); + UNUSED_RELEASE(kBlockSizeDimensionNum); + UNUSED_RELEASE(block_shape_shape); + UNUSED_RELEASE(padding_shape); + + assert(block_shape_shape.rank() == kBlockSizeDimensionNum); + assert(block_shape_shape.dim(0) == kSpatialDimensionNum); + assert(padding_shape.dim(0) == kSpatialDimensionNum); + assert(padding_shape.dim(1) == 2); // fixed, meaning left/right padding for each element + assert(padding_shape.rank() == 2); // fixed, meaning dimension(dim 0) and padding length(dim 1) + + // Ensures the input height and width (with padding) is a multiple of block + // shape height and width. + for (int dim = 0; dim < kSpatialDimensionNum; ++dim) + { + int final_dim_size = + (input_shape.dim(dim + 1) + padding_buf[dim * 2] + padding_buf[dim * 2 + 1]); + + assert(final_dim_size % block_shape_buf[dim] == 0); + + out_shape.dim(dim + 1) = final_dim_size / block_shape_buf[dim]; + } + + const int output_batch_size = input_shape.dim(0) * block_shape_buf[0] * block_shape_buf[1]; + const int output_channel_size = input_shape.dim(3); + + out_shape.dim(0) = output_batch_size; + out_shape.dim(3) = output_channel_size; + + return out_shape; +} + +ir::Shape inferSplitShape(const ir::Shape input_shape, int axis_value, int num_splits) +{ + ir::Shape newShape(input_shape); + + assert(axis_value >= 0); + assert(axis_value < input_shape.rank()); + + const int input_size = input_shape.dim(axis_value); + assert(input_size % num_splits == 0); + const int slice_size = input_size / num_splits; + + newShape.dim(axis_value) = slice_size; + + return newShape; +} + +ir::Shape inferSqueezeShape(const ir::Shape &in_shape, const ir::operation::Squeeze::Param ¶m) +{ + const int ndims = param.ndim; + const int *squeeze_dims = param.dims; + bool should_squeeze[8] = {false}; + int num_squeezed_dims = 0; + int shape_rank = in_shape.rank(); + if (ndims == 0) + { + for (int idx = 0; idx < shape_rank; ++idx) + { + if (in_shape.dim(idx) == 1) + { + should_squeeze[idx] = true; + ++num_squeezed_dims; + } + } + } + else + { + for (int idx = 0; idx < ndims; ++idx) + { + int current = squeeze_dims[idx]; + if (current < 0) + { + current += shape_rank; + } + + if (!(current >= 0 && current < shape_rank && in_shape.dim(current) == 1)) + { + throw std::runtime_error( + "The following conditions must be met: 0 <= dim < Shape rank, dim == 1"); + } + + if (!should_squeeze[current]) + { + ++num_squeezed_dims; + } + should_squeeze[current] = true; + } + } + + // Set output shape. + ir::Shape out_shape(shape_rank - num_squeezed_dims); + for (int in_idx = 0, out_idx = 0; in_idx < shape_rank; ++in_idx) + { + if (!should_squeeze[in_idx]) + { + out_shape.dim(out_idx++) = in_shape.dim(in_idx); + } + } + + return out_shape; +} + +// helper for for StridedSlice +template <typename T> +StridedSliceParams buildStridedSliceParams(const T *begin, const T *end, const T *strides, + const uint32_t begin_mask, const uint32_t end_mask, + const uint32_t shrink_axis_mask, const uint8_t rank) +{ + StridedSliceParams op_params; + op_params.start_indices_count = rank; + op_params.stop_indices_count = rank; + op_params.strides_count = rank; + + for (int i = 0; i < op_params.strides_count; ++i) + { + op_params.start_indices[i] = begin[i]; + op_params.stop_indices[i] = end[i]; + op_params.strides[i] = strides[i]; + + assert(op_params.strides[i] != 0); + } + + op_params.begin_mask = begin_mask; + op_params.ellipsis_mask = 0; // NYI + op_params.end_mask = end_mask; + op_params.new_axis_mask = 0; // NYI + op_params.shrink_axis_mask = shrink_axis_mask; + + assert(sizeof(op_params.begin_mask) * 4 >= rank); + + return op_params; +} + +// template instantiation +template StridedSliceParams +buildStridedSliceParams(const uint32_t *begin, const uint32_t *end, const uint32_t *strides, + const uint32_t begin_mask, const uint32_t end_mask, + const uint32_t shrink_axis_mask, const uint8_t rank); + +int Clamp(const int v, const int lo, const int hi) +{ + assert(!(hi < lo)); + if (hi < v) + return hi; + if (v < lo) + return lo; + return v; +} + +int StartForAxis(const StridedSliceParams ¶ms, const ir::Shape &input_shape, int axis) +{ + const auto begin_mask = params.begin_mask; + const auto *start_indices = params.start_indices; + const auto *strides = params.strides; + // Begin with the specified index. + int start = start_indices[axis]; + + // begin_mask override + if (begin_mask & 1 << axis) + { + if (strides[axis] > 0) + { + // Forward iteration - use the first element. These values will get + // clamped below (Note: We could have set them to 0 and axis_size-1, but + // use lowest() and max() to maintain symmetry with StopForAxis()) + start = std::numeric_limits<int>::lowest(); + } + else + { + // Backward iteration - use the last element. + start = std::numeric_limits<int>::max(); + } + } + + // Handle negative indices + int axis_size = input_shape.dim(axis); + if (start < 0) + { + start += axis_size; + } + + // Clamping + start = Clamp(start, 0, axis_size - 1); + + return start; +} + +// Return the "real" index for the end of iteration along that axis. This is an +// "end" in the traditional C sense, in that it points to one past the last +// element. ie. So if you were iterating through all elements of a 1D array of +// size 4, this function would return 4 as the stop, because it is one past the +// "real" indices of 0, 1, 2 & 3. +int StopForAxis(const StridedSliceParams ¶ms, const ir::Shape &input_shape, int axis, + int start_for_axis) +{ + const auto end_mask = params.end_mask; + const auto shrink_axis_mask = params.shrink_axis_mask; + const auto *stop_indices = params.stop_indices; + const auto *strides = params.strides; + + // Begin with the specified index + const bool shrink_axis = shrink_axis_mask & (1 << axis); + int stop = stop_indices[axis]; + + // When shrinking an axis, the end position does not matter (and can be + // incorrect when negative indexing is used, see Issue #19260). Always use + // start_for_axis + 1 to generate a length 1 slice, since start_for_axis has + // already been adjusted for negative indices. + if (shrink_axis) + { + stop = start_for_axis + 1; + } + + // end_mask override + if (end_mask & (1 << axis)) + { + if (strides[axis] > 0) + { + // Forward iteration - use the last element. These values will get + // clamped below + stop = std::numeric_limits<int>::max(); + } + else + { + // Backward iteration - use the first element. + stop = std::numeric_limits<int>::lowest(); + } + } + + // Handle negative indices + + const int axis_size = input_shape.dim(axis); + if (stop < 0) + { + stop += axis_size; + } + + // Clamping + // Because the end index points one past the last element, we need slightly + // different clamping ranges depending on the direction. + if (strides[axis] > 0) + { + // Forward iteration + stop = Clamp(stop, 0, axis_size); + } + else + { + // Backward iteration + stop = Clamp(stop, -1, axis_size - 1); + } + + return stop; +} + +ir::Shape inferStridedSliceShape(const ir::Shape &input_shape, const StridedSliceParams &op_params, + uint32_t rank) +{ + ir::Shape out_shape; + + for (uint32_t idx = 0; idx < rank; ++idx) + { + int32_t stride = op_params.strides[idx]; + int32_t begin = StartForAxis(op_params, input_shape, idx); + int32_t end = StopForAxis(op_params, input_shape, idx, begin); + + // When shrinking an axis, the end position does not matter (and can be + // incorrect when negative indexing is used, see Issue #19260). Always use + // begin + 1 to generate a length 1 slice, since begin has + // already been adjusted for negative indices by StartForAxis. + const bool shrink_axis = op_params.shrink_axis_mask & (1 << idx); + if (shrink_axis) + { + end = begin + 1; + } + + int32_t dim_shape = std::ceil((end - begin) / static_cast<float>(stride)); + dim_shape = dim_shape < 0 ? 0 : dim_shape; + if (!shrink_axis) + { + out_shape.append(dim_shape); + } + } + + return out_shape; +} + +ir::Shape inferTileShape(const ir::Shape &in_shape, const int32_t *multiplier_buf, + const int32_t multiplier_size) +{ + if (multiplier_size != in_shape.rank()) + { + throw std::runtime_error("inferTileShape failed, input rank: " + + std::to_string(in_shape.rank()) + ", bad multipliers size: " + + std::to_string(multiplier_size) + ""); + } + ir::Shape new_Shape(in_shape.rank()); + + for (int i = 0; i < in_shape.rank(); ++i) + { + assert(multiplier_buf[i]); // multiplier_buf[i] shuld not be 0. + new_Shape.dim(i) = in_shape.dim(i) * multiplier_buf[i]; + } + return new_Shape; +} + +ir::Shape inferTransposeShape(const ir::Shape &in_shape, const int32_t *perm_buf, + const int32_t perm_size) +{ + const auto rank = in_shape.rank(); + if (perm_size > rank) + { + throw std::runtime_error("inferTransposeShape failed, bad permutation size: " + + std::to_string(perm_size)); + } + + const int32_t *perm_data = perm_buf; + std::vector<int32_t> regular_perm_vec; + if (perm_size == 0) + { + // perm_data will be set to (n-1...0) + regular_perm_vec.resize(rank); + std::iota(regular_perm_vec.begin(), regular_perm_vec.end(), 0); + std::reverse(regular_perm_vec.begin(), regular_perm_vec.end()); + perm_data = regular_perm_vec.data(); + } + else + { + assert(rank == perm_size); + } + + ir::Shape out_shape(rank); + std::vector<bool> visit_perms(rank, false); + for (int idx = 0; idx < rank; idx++) + { + const auto perm_val = perm_data[idx]; + // Check invalid permutation value + if (perm_val < 0 || perm_val >= rank) + { + throw std::runtime_error("inferTransposeShape failed, bad permutation value: " + + std::to_string(perm_val)); + } + + // Check duplicated permutation value + if (visit_perms.at(perm_val)) + { + throw std::runtime_error("inferTransposeShape failed, duplicated permutation value: " + + std::to_string(perm_val)); + } + visit_perms.at(perm_val) = true; + + out_shape.dim(idx) = in_shape.dim(perm_val); + } + return out_shape; +} + +ir::Shape inferUnpackShape(const ir::Shape &input_shape, int axis, int rank) +{ + ir::Shape out_shape; + + for (int out_idx = 0; out_idx < rank; out_idx++) + { + if (out_idx != axis) + { + out_shape.append(input_shape.dim(out_idx)); + } + } + + return out_shape; +} + +} // 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; +} |