diff --git a/cmake/features.cmake b/cmake/features.cmake index 95db08a1393..3bc43005abd 100644 --- a/cmake/features.cmake +++ b/cmake/features.cmake @@ -54,3 +54,9 @@ ie_dependent_option (ENABLE_FASTER_BUILD "Enable build features (PCH, UNITY) to # Type of build, we add this as an explicit option to default it to ON # FIXME: Ah this moment setting this to OFF will only build ngraph a static library ie_option (BUILD_SHARED_LIBS "Build as a shared library" ON) + +ie_option_enum(SELECTIVE_BUILD "Enable OpenVINO conditional compilation or statistics collection. \ +In case SELECTIVE_BUILD is enabled, the SELECTIVE_BUILD_STAT variable should contain the path to the collected InelSEAPI statistics. \ +Usage: -DSELECTIVE_BUILD=ON -DSELECTIVE_BUILD_STAT=/path/*.csv" OFF + ALLOWED_VALUES ON OFF COLLECT) + diff --git a/cmake/options.cmake b/cmake/options.cmake index d8b8a54cdb9..b4bb86b1d6e 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -16,6 +16,21 @@ macro (ie_dependent_option variable description def_value condition fallback_val list(APPEND IE_OPTIONS ${variable}) endmacro() +macro (ie_option_enum variable description value) + set(OPTIONS) + set(ONE_VALUE_ARGS) + set(MULTI_VALUE_ARGS ALLOWED_VALUES) + cmake_parse_arguments(IE_OPTION_ENUM "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) + + if(NOT ${value} IN_LIST IE_OPTION_ENUM_ALLOWED_VALUES) + message(FATAL_ERROR "variable must be one of ${IE_OPTION_ENUM_ALLOWED_VALUES}") + endif() + + list(APPEND IE_OPTIONS ${variable}) + + set(${variable} ${value} CACHE STRING "${description}") +endmacro() + function (print_enabled_features) message(STATUS "Inference Engine enabled features: ") message(STATUS "") diff --git a/cmake/os_flags.cmake b/cmake/os_flags.cmake index 49f0c8b2076..0ed6e258298 100644 --- a/cmake/os_flags.cmake +++ b/cmake/os_flags.cmake @@ -167,6 +167,17 @@ macro(ie_add_compiler_flags) endforeach() endmacro() +# +# Forced includes certain header file to all target source files +# +function(ov_force_include target scope header_file) + if(MSVC) + target_compile_options(${target} ${scope} /FI"${header_file}") + else() + target_compile_options(${target} ${scope} -include "${header_file}") + endif() +endfunction() + # # Compilation and linker flags # diff --git a/inference-engine/src/inference_engine/CMakeLists.txt b/inference-engine/src/inference_engine/CMakeLists.txt index 626ce2a88a2..c877a7e86d8 100644 --- a/inference-engine/src/inference_engine/CMakeLists.txt +++ b/inference-engine/src/inference_engine/CMakeLists.txt @@ -83,7 +83,7 @@ target_include_directories(${TARGET_NAME}_plugin_api INTERFACE $ ${PUBLIC_HEADERS_DIR}) -target_link_libraries(${TARGET_NAME}_plugin_api INTERFACE openvino::itt) +target_link_libraries(${TARGET_NAME}_plugin_api INTERFACE openvino::itt openvino::conditional_compilation) set_ie_threading_interface_for(${TARGET_NAME}_plugin_api) @@ -138,7 +138,7 @@ ie_add_vs_version_file(NAME ${TARGET_NAME} set_ie_threading_interface_for(${TARGET_NAME}) -target_link_libraries(${TARGET_NAME} PRIVATE pugixml openvino::itt ${CMAKE_DL_LIBS} Threads::Threads +target_link_libraries(${TARGET_NAME} PRIVATE pugixml openvino::itt openvino::conditional_compilation ${CMAKE_DL_LIBS} Threads::Threads ${NGRAPH_LIBRARIES} inference_engine_transformations) target_include_directories(${TARGET_NAME} INTERFACE ${PUBLIC_HEADERS_DIR} @@ -173,7 +173,7 @@ if(WIN32) set_target_properties(${TARGET_NAME}_s PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}_s) endif() -target_link_libraries(${TARGET_NAME}_s PRIVATE openvino::itt ${CMAKE_DL_LIBS} ${NGRAPH_LIBRARIES} +target_link_libraries(${TARGET_NAME}_s PRIVATE openvino::itt openvino::conditional_compilation ${CMAKE_DL_LIBS} ${NGRAPH_LIBRARIES} inference_engine_transformations PUBLIC pugixml) diff --git a/inference-engine/tests/functional/inference_engine/CMakeLists.txt b/inference-engine/tests/functional/inference_engine/CMakeLists.txt index 414fb9c1067..553992bd1be 100644 --- a/inference-engine/tests/functional/inference_engine/CMakeLists.txt +++ b/inference-engine/tests/functional/inference_engine/CMakeLists.txt @@ -12,6 +12,7 @@ set(LINK_LIBRARIES ngraphFunctions inference_engine_transformations openvino::itt + openvino::conditional_compilation ) set(DEPENDENCIES mock_engine diff --git a/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build.cpp b/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build.cpp new file mode 100644 index 00000000000..386d7479481 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2018-2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#ifdef SELECTIVE_BUILD_ANALYZER +# define SELECTIVE_BUILD_ANALYZER_ON +# undef SELECTIVE_BUILD_ANALYZER +#elif defined(SELECTIVE_BUILD) +# define SELECTIVE_BUILD_ON +# undef SELECTIVE_BUILD +#endif + +#define SELECTIVE_BUILD + +#include + +namespace { +OV_CC_DOMAINS(CCTests); + +template +struct TestTemplateClass; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 42; + } +}; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 43; + } +}; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 44; + } +}; + +struct TestNodeBase { + TestNodeBase(int k, int v) + : key(k) + , value(v) {} + virtual ~TestNodeBase() = default; + int key; + int value; +}; + +template +struct TestNode : public TestNodeBase { + TestNode(int value) + : TestNodeBase(N, value) {} +}; + +} // namespace + +TEST(ConditionalCompilationTests, SimpleScope) { +#define CCTests_Scope0 1 + + int n = 0; + + // Simple scope is enabled + OV_SCOPE(CCTests, Scope0, n = 42;); + EXPECT_EQ(n, 42); + + // Simple scope is disabled + OV_SCOPE(CCTests, Scope1, n = 0;); + EXPECT_EQ(n, 42); + +#undef CCTests_Scope0 +} + +TEST(ConditionalCompilationTests, SwitchCase) { + // Cases 0 and 2 are enabled +#define CCTests_TestTemplateClass 1 +#define CCTests_TestTemplateClass_cases OV_CASE(0, int), OV_CASE(2, float) + + int n = 0; + + OV_SWITCH(CCTests, TestTemplateClass, n, 0, + OV_CASE(0, int), + OV_CASE(1, bool), + OV_CASE(2, float)); + EXPECT_EQ(n, 42); + + OV_SWITCH(CCTests, TestTemplateClass, n, 1, + OV_CASE(0, int), + OV_CASE(1, bool), + OV_CASE(2, float)); + EXPECT_EQ(n, 42); + + OV_SWITCH(CCTests, TestTemplateClass, n, 2, + OV_CASE(0, int), + OV_CASE(1, bool), + OV_CASE(2, float)); + EXPECT_EQ(n, 44); + +#undef CCTests_TestTemplateClass +#undef CCTests_TestTemplateClass_cases +} + +TEST(ConditionalCompilationTests, Factory) { +#define CCTests_TestNode0 1 +#define CCTests_TestNode1 0 +#define CCTests_TestNode2 1 + + openvino::cc::Factory testFactory("TestFactory"); + testFactory.registerNodeIfRequired(CCTests, TestNode0, 0, TestNode<0>); + testFactory.registerNodeIfRequired(CCTests, TestNode1, 1, TestNode<1>); + testFactory.registerNodeIfRequired(CCTests, TestNode2, 2, TestNode<2>); + + TestNodeBase *node0 = testFactory.createNodeIfRegistered(CCTests, 0, 42); + TestNodeBase *node1 = testFactory.createNodeIfRegistered(CCTests, 1, 43); + TestNodeBase *node2 = testFactory.createNodeIfRegistered(CCTests, 2, 44); + + EXPECT_TRUE(node0 && node0->key == 0 && node0->value == 42); + EXPECT_TRUE(!node1); + EXPECT_TRUE(node2 && node2->key == 2 && node2->value == 44); + + delete node0; + delete node1; + delete node2; + +#undef CCTests_TestNode0 +#undef CCTests_TestNode1 +#undef CCTests_TestNode2 +} + +#undef SELECTIVE_BUILD + +#ifdef SELECTIVE_BUILD_ANALYZER_ON +# define SELECTIVE_BUILD_ANALYZER +#elif defined(SELECTIVE_BUILD_ON) +# define SELECTIVE_BUILD +#endif diff --git a/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build_analyzer.cpp b/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build_analyzer.cpp new file mode 100644 index 00000000000..888978c8442 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/conditional_compilation/selective_build_analyzer.cpp @@ -0,0 +1,108 @@ +// Copyright (C) 2018-2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#ifdef SELECTIVE_BUILD_ANALYZER +# define SELECTIVE_BUILD_ANALYZER_ON +# undef SELECTIVE_BUILD_ANALYZER +#elif defined(SELECTIVE_BUILD) +# define SELECTIVE_BUILD_ON +# undef SELECTIVE_BUILD +#endif + +#define SELECTIVE_BUILD_ANALYZER + +#include + +namespace { +OV_CC_DOMAINS(CCTests); + +template +struct TestTemplateClass; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 42; + } +}; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 43; + } +}; + +template<> +struct TestTemplateClass { + void operator()(int &v) { + v = 44; + } +}; + +struct TestNodeBase { + TestNodeBase(int k, int v) + : key(k) + , value(v) {} + virtual ~TestNodeBase() = default; + int key; + int value; +}; + +template +struct TestNode : public TestNodeBase { + TestNode(int value) + : TestNodeBase(N, value) {} +}; + +} // namespace + +TEST(ConditionalCompilationTests, SimpleScopeAnalysys) { + int n = 0; + + OV_SCOPE(CCTests, Scope0, n = 42;); + EXPECT_EQ(n, 42); + + OV_SCOPE(CCTests, Scope1, n = 43;); + EXPECT_EQ(n, 43); +} + +TEST(ConditionalCompilationTests, SwitchCaseAnalysys) { + int n = 0; + + OV_SWITCH(CCTests, TestTemplateClass, n, 0, + OV_CASE(0, int), + OV_CASE(1, bool), + OV_CASE(2, float)); + EXPECT_EQ(n, 42); +} + +TEST(ConditionalCompilationTests, FactoryAnalysys) { + openvino::cc::Factory testFactory("TestFactory"); + testFactory.registerNodeIfRequired(CCTests, TestNode0, 0, TestNode<0>); + testFactory.registerNodeIfRequired(CCTests, TestNode1, 1, TestNode<1>); + testFactory.registerNodeIfRequired(CCTests, TestNode2, 2, TestNode<2>); + + TestNodeBase *node0 = testFactory.createNodeIfRegistered(CCTests, 0, 42); + TestNodeBase *node1 = testFactory.createNodeIfRegistered(CCTests, 1, 43); + TestNodeBase *node2 = testFactory.createNodeIfRegistered(CCTests, 2, 44); + + EXPECT_TRUE(node0 && node0->key == 0 && node0->value == 42); + EXPECT_TRUE(node1 && node1->key == 1 && node1->value == 43); + EXPECT_TRUE(node2 && node2->key == 2 && node2->value == 44); + + delete node0; + delete node1; + delete node2; +} + +#undef SELECTIVE_BUILD_ANALYZER + +#ifdef SELECTIVE_BUILD_ANALYZER_ON +# define SELECTIVE_BUILD_ANALYZER +#elif defined(SELECTIVE_BUILD_ON) +# define SELECTIVE_BUILD +#endif diff --git a/openvino/CMakeLists.txt b/openvino/CMakeLists.txt index c0431de6f90..2d6ebf915f2 100644 --- a/openvino/CMakeLists.txt +++ b/openvino/CMakeLists.txt @@ -15,5 +15,6 @@ # ****************************************************************************** add_subdirectory(itt) +add_subdirectory(conditional_compilation) -openvino_developer_export_targets(openvino::itt) +openvino_developer_export_targets(openvino::itt openvino::conditional_compilation) diff --git a/openvino/conditional_compilation/CMakeLists.txt b/openvino/conditional_compilation/CMakeLists.txt new file mode 100644 index 00000000000..c6798daff2a --- /dev/null +++ b/openvino/conditional_compilation/CMakeLists.txt @@ -0,0 +1,46 @@ +# ****************************************************************************** +# Copyright 2017-2020 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ****************************************************************************** + +set(TARGET_NAME conditional_compilation) + +add_library(${TARGET_NAME} INTERFACE) + +add_library(openvino::conditional_compilation ALIAS ${TARGET_NAME}) + +target_link_libraries(${TARGET_NAME} INTERFACE openvino::itt) + +target_include_directories(${TARGET_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +if(SELECTIVE_BUILD STREQUAL "COLLECT") + target_compile_definitions(${TARGET_NAME} INTERFACE SELECTIVE_BUILD_ANALYZER) +elseif(SELECTIVE_BUILD STREQUAL "ON") + if(NOT DEFINED SELECTIVE_BUILD_STAT) + message(FATAL_ERROR "In case SELECTIVE_BUILD is enabled, the SELECTIVE_BUILD_STAT variable should contain the path to the collected InelSEAPI statistics.\ + Usage: -DSELECTIVE_BUILD=ON -DSELECTIVE_BUILD_STAT=/path/*.csv") + endif() + + target_compile_definitions(${TARGET_NAME} INTERFACE SELECTIVE_BUILD) + + set(GENERATED_HEADER ${CMAKE_CURRENT_BINARY_DIR}/conditional_compilation_gen.h) + set(GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/scripts/ccheader.py) + + add_custom_command(OUTPUT ${GENERATED_HEADER} + COMMAND python3 ${GENERATOR} --stat ${SELECTIVE_BUILD_STAT} --out ${GENERATED_HEADER}) + add_custom_target(conditional_compilation_gen DEPENDS ${GENERATED_HEADER}) + add_dependencies(${TARGET_NAME} conditional_compilation_gen) + + ov_force_include(${TARGET_NAME} INTERFACE ${GENERATED_HEADER}) +endif() diff --git a/openvino/conditional_compilation/include/openvino/cc/factory.h b/openvino/conditional_compilation/include/openvino/cc/factory.h new file mode 100644 index 00000000000..f971e0fc028 --- /dev/null +++ b/openvino/conditional_compilation/include/openvino/cc/factory.h @@ -0,0 +1,144 @@ +//***************************************************************************** +// Copyright 2017-2020 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//***************************************************************************** + +#pragma once +#include "selective_build.h" +#include +#include +#include +#include + +namespace openvino +{ + namespace cc + { + template + class Factory; + + template + class Factory { + Factory(Factory const&) = delete; + Factory& operator=(Factory const&) = delete; + + public: + using builder_t = std::function; + + Factory(const std::string & name) + : name(name) {} + + #ifdef SELECTIVE_BUILD + #define registerNodeIfRequired(Module, Name, key, Impl) \ + OV_CC_EXPAND(OV_CC_CAT(registerImpl, OV_CC_SCOPE_IS_ENABLED(OV_CC_CAT3(Module, _, Name)))(key)) + #define createNodeIfRegistered(Module, key, ...) createImpl(key, __VA_ARGS__) + + template + void registerImpl0(const Key &) { + } + + template + void registerImpl1(const Key & key) { + builders[key] = [](Args... args) -> T { + Impl *impl = new Impl(args...); + return static_cast(impl); + }; + } + + T createImpl(const Key & key, Args... args) { + auto builder = builders.find(key); + if (builder != builders.end()) { + return builder->second(args...); + } + return nullptr; + } + #elif defined(SELECTIVE_BUILD_ANALYZER) + #define registerNodeIfRequired(Module, Name, key, Impl) registerImpl(key, OV_CC_TOSTRING(Name)) + #define createNodeIfRegistered(Module, key, ...) createImpl(key, __VA_ARGS__) + + template + void registerImpl(const Key & key, const char *typeName) { + const std::string task_name = "REG$" + name + "$" + to_string(key) + "$" + typeName; + openvino::itt::ScopedTask task(openvino::itt::handle(task_name)); + builders[key] = [](Args... args) -> T { + Impl *impl = new Impl(args...); + return static_cast(impl); + }; + } + + template + T createImpl(const Key & key, Args... args) { + auto builder = builders.find(key); + if (builder != builders.end()) { + const std::string task_name = "CREATE$" + name + "$" + to_string(key); + openvino::itt::ScopedTask task(openvino::itt::handle(task_name)); + return builder->second(args...); + } + return nullptr; + } + #else + #define registerNodeIfRequired(Module, Name, key, Impl) registerImpl(key) + #define createNodeIfRegistered(Module, key, ...) createImpl(key, __VA_ARGS__) + + template + void registerImpl(const Key & key) { + builders[key] = [](Args... args) -> T { + Impl *impl = new Impl(args...); + return static_cast(impl); + }; + } + + T createImpl(const Key & key, Args... args) { + auto builder = builders.find(key); + if (builder != builders.end()) { + return builder->second(args...); + } + return nullptr; + } + #endif + + template + void foreach(Fn fn) const { + for (auto itm : builders) + fn(itm); + } + + size_t size() const noexcept { + return builders.size(); + } + + private: + const std::string & to_string(const std::string & str) const noexcept { + return str; + } + + template::value, bool>::type = true> + std::string to_string(V val) const { + return std::to_string(static_cast(val)); + } + + template::value, bool>::type = true> + std::string to_string(V val) const { + return std::to_string(val); + } + + using map_t = std::unordered_map; + + const std::string name; + map_t builders; + }; + } +} diff --git a/openvino/conditional_compilation/include/openvino/cc/selective_build.h b/openvino/conditional_compilation/include/openvino/cc/selective_build.h new file mode 100644 index 00000000000..3b9fe9dd66f --- /dev/null +++ b/openvino/conditional_compilation/include/openvino/cc/selective_build.h @@ -0,0 +1,265 @@ +//***************************************************************************** +// Copyright 2017-2020 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//***************************************************************************** + +#pragma once + +/* + This file contains a useful API for analyzing OpenVINO sources + and enabling or disabling some regions. + Three working modes are currently supported. + * SELECTIVE_BUILD_ANALYZER This macro enables analysis mode for annotated code regions. + * When the process completes, a new C++ header file is created + * that contains macros for enabling active regions. This file + * should be included in all analysed C++ files. + * + * SELECTIVE_BUILD This mode disables inactive areas of the code using the result + * of the analysis step. + * + * No definitions The default behavior is keept if no SELECTIVE_BUILD* macros are defined, + * i.e all features of the OpenVINO are enabled. + * + * Prerequisites: + * Before using macros for code annotation,domains for conditional + * compilation should be defined in module namespace. + * + * OV_CC_DOMAINS(MyModule); + * + * An example of using annotation: + * + * I. Any C++ code block: + * OV_SCOPE(MyModule, ScopeName, + * // Any C++ code. + * cout << "Hello world!"; + * ); + * + * II. Template class instantiation using switch-case: + * + * struct Context { ... }; + * + * template + * struct SomeTemplateClass { + * void operator()(Context &context) { + * // Any C++ code. + * cout << "Hello world!"; + * } + * }; + * + * auto key = Precision::U8; + * Context context; + * + * OV_SWITCH(MyModule, SomeTemplateClass, context, key, + * OV_CASE(Precision::U8, uint8_t), + * OV_CASE(Precision::I8, int8_t), + * OV_CASE(Precision::FP32, float)); + * +*/ + +#include + +#ifdef SELECTIVE_BUILD_ANALYZER +#include +#endif + +#include +#include + +namespace openvino +{ + namespace cc + { +#ifndef SELECTIVE_BUILD_ANALYZER + namespace internal + { + template + struct case_wrapper { + using type = T; + const C value {}; + + case_wrapper(C && val) + : value(std::forward(val)) + {} + }; + + template + case_wrapper make_case_wrapper(C && val) { + return case_wrapper(std::forward(val)); + } + + template class Fn, typename Ctx, typename T, typename Case> + bool match(Ctx && ctx, T && val, Case && cs) { + const bool is_matched = val == cs.value; + if (is_matched) + Fn()(std::forward(ctx)); + return is_matched; + } + + template class Fn, typename Ctx, typename T, typename Case, typename ...Cases> + bool match(Ctx && ctx, T && val, Case && cs, Cases&&... cases) { + if (match(std::forward(ctx), std::forward(val), std::forward(cs))) + return true; + return match(std::forward(ctx), std::forward(val), std::forward(cases)...); + } + } // namespace internal +#endif + +// Macros for names concatenation +#define OV_CC_CAT_(x, y) x ## y +#define OV_CC_CAT(x, y) OV_CC_CAT_(x, y) +#define OV_CC_CAT3_(x, y, z) x ## y ## z +#define OV_CC_CAT3(x, y, z) OV_CC_CAT3_(x, y, z) +#define OV_CC_CAT4_(x, y, z, w) x ## y ## z ## w +#define OV_CC_CAT4(x, y, z, w) OV_CC_CAT4_(x, y, z, w) + +// Expand macro argument +#define OV_CC_EXPAND(x) x + +// Macros for string conversion +#define OV_CC_TOSTRING(...) OV_CC_TOSTRING_(__VA_ARGS__) +#define OV_CC_TOSTRING_(...) #__VA_ARGS__ + +#ifdef SELECTIVE_BUILD_ANALYZER // OpenVINO analysis + +#define OV_CC_DOMAINS(Module) \ + OV_ITT_DOMAIN(OV_CC_CAT(SIMPLE_, Module)); /* Domain for simple scope surrounded by ifdefs */ \ + OV_ITT_DOMAIN(OV_CC_CAT(SWITCH_, Module)); /* Domain for switch/cases */ \ + OV_ITT_DOMAIN(OV_CC_CAT(FACTORY_, Module)); /* Domain for factories */ + +namespace internal +{ + template + struct case_wrapper { + using type = T; + const C value {}; + const char *name = nullptr; + + case_wrapper(C && val, const char *name) + : value(std::forward(val)) + , name(name) + {} + }; + + template + case_wrapper make_case_wrapper(C && val, const char *name) { + return case_wrapper(std::forward(val), name); + } + + template class Fn, + typename Ctx, + typename T, + typename Case> + bool match(char const *region, Ctx && ctx, T && val, Case && cs) { + const bool is_matched = val == cs.value; + if (is_matched) { + openvino::itt::ScopedTask task( + openvino::itt::handle( + std::string(region) + "$" + cs.name)); + Fn()(std::forward(ctx)); + } + return is_matched; + } + + template class Fn, + typename Ctx, + typename T, + typename Case, typename ...Cases> + bool match(char const *region, Ctx && ctx, T && val, Case && cs, Cases&&... cases) { + if (match(region, std::forward(ctx), std::forward(val), std::forward(cs))) + return true; + return match(region, std::forward(ctx), std::forward(val), std::forward(cases)...); + } +} // namespace internal + +#define OV_SCOPE(Module, region, ...) \ + OV_ITT_SCOPED_TASK(OV_CC_CAT(SIMPLE_, Module), OV_CC_TOSTRING(region)); \ + __VA_ARGS__ + +#define OV_SWITCH(Module, fn, ctx, val, ...) \ + openvino::cc::internal::match \ + (OV_CC_TOSTRING(fn), ctx, val, __VA_ARGS__); + +#define OV_CC_LBR ( +#define OV_CC_RBR ) + +#define OV_CASE(Case, Type) \ + openvino::cc::internal::make_case_wrapper(Case, OV_CC_TOSTRING(OV_CASE OV_CC_LBR Case, Type OV_CC_RBR)) + +#define OV_CASE2(Case1, Case2, Type1, Type2) \ + openvino::cc::internal::make_case_wrapper>( \ + std::make_tuple(Case1, Case2), \ + OV_CC_TOSTRING(OV_CASE2 OV_CC_LBR Case1, Case2, Type1, Type2 OV_CC_RBR)) + +#elif defined(SELECTIVE_BUILD) // OpenVINO selective build is enabled + +#define OV_CC_DOMAINS(Module) + +// Placeholder for first macro argument +#define OV_CC_SCOPE_ARG_PLACEHOLDER_1 0, + +// This macro returns second argument, first argument is ignored +#define OV_CC_SCOPE_SECOND_ARG(...) OV_CC_EXPAND(OV_CC_SCOPE_SECOND_ARG_(__VA_ARGS__, 0)) +#define OV_CC_SCOPE_SECOND_ARG_(...) OV_CC_EXPAND(OV_CC_SCOPE_SECOND_ARG_GET(__VA_ARGS__)) +#define OV_CC_SCOPE_SECOND_ARG_GET(ignored, val, ...) val + +// Return macro argument value +#define OV_CC_SCOPE_IS_ENABLED(x) OV_CC_SCOPE_IS_ENABLED1(x) + +// Generate junk macro or {0, } sequence if val is 1 +#define OV_CC_SCOPE_IS_ENABLED1(val) OV_CC_SCOPE_IS_ENABLED2(OV_CC_CAT(OV_CC_SCOPE_ARG_PLACEHOLDER_, val)) + +// Return second argument from possible sequences {1, 0}, {0, 1, 0} +#define OV_CC_SCOPE_IS_ENABLED2(arg1_or_junk) OV_CC_SCOPE_SECOND_ARG(arg1_or_junk 1, 0) + +// Scope is disabled +#define OV_CC_SCOPE_0(...) + +// Scope is enabled +#define OV_CC_SCOPE_1(...) __VA_ARGS__ + +#define OV_SCOPE(Module, region, ...) \ + OV_CC_EXPAND(OV_CC_CAT(OV_CC_SCOPE_, OV_CC_SCOPE_IS_ENABLED(OV_CC_CAT3(Module, _, region)))(__VA_ARGS__)) + +// Switch is disabled +#define OV_CC_SWITCH_0(Module, fn, ctx, val) + +// Switch is enabled +#define OV_CC_SWITCH_1(Module, fn, ctx, val) openvino::cc::internal::match(ctx, val, OV_CC_CAT4(Module, _, fn, _cases)); + +#define OV_SWITCH(Module, fn, ctx, val, ...) \ + OV_CC_EXPAND(OV_CC_CAT(OV_CC_SWITCH_, OV_CC_SCOPE_IS_ENABLED(OV_CC_CAT3(Module, _, fn)))(Module, fn, ctx, val)) + +#define OV_CASE(Case, Type) openvino::cc::internal::make_case_wrapper(Case) + +#define OV_CASE2(Case1, Case2, Type1, Type2) openvino::cc::internal::make_case_wrapper>(std::make_tuple(Case1, Case2)) + +#else + +#define OV_CC_DOMAINS(Module) + +#define OV_SCOPE(Module, region, ...) __VA_ARGS__ + +#define OV_SWITCH(Module, fn, ctx, val, ...) \ + openvino::cc::internal::match(ctx, val, __VA_ARGS__); + +#define OV_CASE(Case, Type) openvino::cc::internal::make_case_wrapper(Case) + +#define OV_CASE2(Case1, Case2, Type1, Type2) openvino::cc::internal::make_case_wrapper>(std::make_tuple(Case1, Case2)) + +#endif + + } +} diff --git a/openvino/conditional_compilation/scripts/ccheader.py b/openvino/conditional_compilation/scripts/ccheader.py new file mode 100755 index 00000000000..2789e5e4738 --- /dev/null +++ b/openvino/conditional_compilation/scripts/ccheader.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2020 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The main purpose of this script is code generation for conditional compilation. +# After collecting statistics using IntelSEAPI, several CSV files are generated. +# This script can read these files and can produce header file which will contain +# definitions for enabled OpenVINO parts. +# +# Usage: ccheader.py [-h] --stat PATH[PATH...] [PATH[PATH...] ...] --out cc.h +# +# Mandatory arguments: +# --stat PATH[ PATH...] [PATH[ PATH...] ...] +# IntelSEAPI statistics files in CSV format +# --out cc.h C++ header file to be generated + +import argparse, csv +from pathlib import Path +from abc import ABC, abstractmethod + +Domain = ['SIMPLE_', + 'SWITCH_', + 'FACTORY_'] + +FILE_HEADER = "#pragma once\n\n" +FILE_FOOTER = "\n" + +ENABLED_SCOPE_FMT = "#define %s_%s 1\n" +ENABLED_SWITCH_FMT = "#define %s_%s 1\n#define %s_%s_cases %s\n" +ENABLED_FACTORY_INSTANCE_FMT = "#define %s_%s 1\n" + +class IScope(ABC): + @abstractmethod + def generate(self, f, module): + pass + +class Scope(IScope): + def __init__(self, name): + self.name = name + + def generate(self, f, module): + f.write(ENABLED_SCOPE_FMT % (module, self.name)) + +class Switch(IScope): + def __init__(self, name): + self.name = name + self.cases = set() + + def case(self, val): + self.cases.add(val) + + def generate(self, f, module): + f.write(ENABLED_SWITCH_FMT % (module, self.name, module, self.name, ', '.join(self.cases))) + +class Factory(IScope): + def __init__(self, name): + self.name = name + self.registered = {} + self.created = set() + + def register(self, id, name): + self.registered[id] = name + + def create(self, id): + self.created.add(id) + + def generate(self, f, module): + for id in self.created: + r = self.registered.get(id) + if r: + f.write(ENABLED_FACTORY_INSTANCE_FMT % (module, r)) + +class Module: + def __init__(self, name): + self.name = name + self.scopes = {} + + def scope(self, name): + if name not in self.scopes: + self.scopes[name] = Scope(name) + return self.scopes.get(name) + + def factory(self, name): + if name not in self.scopes: + self.scopes[name] = Factory(name) + return self.scopes.get(name) + + def switch(self, name): + if name not in self.scopes: + self.scopes[name] = Switch(name) + return self.scopes.get(name) + + def generate(self, f): + for _, scope in self.scopes.items(): + scope.generate(f, self.name) + if self.scopes: + f.write("\n") + +class Stat: + def __init__(self, files): + self.modules = {} + self.read(files) + + def module(self, name): + if name not in self.modules: + self.modules[name] = Module(name) + return self.modules.get(name) + + def read(self, files): + for stat in files: + with open(stat) as f: + reader = csv.reader(f) + rows = list(reader) + if rows: + # Scopes + scopes = list(filter(lambda row: row[0].startswith(Domain[0]), rows)) + for row in scopes: + moduleName = row[0][len(Domain[0]):] + self.module(moduleName).scope(row[1]) + + # Switches + switches = list(map(lambda row: [row[0][len(Domain[1]):]] + row[1].strip().split('$'), + filter(lambda row: row[0].startswith(Domain[1]), rows))) + for switch in switches: + self.module(switch[0]).switch(switch[1]).case(switch[2]) + + # Factories + factories = list(map(lambda row: [row[0][len(Domain[2]):]] + row[1].strip().split('$'), + filter(lambda row: row[0].startswith(Domain[2]), rows))) + for reg in list(filter(lambda row: row[1] == 'REG', factories)): + self.module(reg[0]).factory(reg[2]).register(reg[3], reg[4]) + for cre in list(filter(lambda row: row[1] == 'CREATE', factories)): + self.module(cre[0]).factory(cre[2]).create(cre[3]) + + def generate(self, out): + with open(out, 'w') as f: + f.write(FILE_HEADER) + + for _, module in self.modules.items(): + module.generate(f) + + f.write(FILE_FOOTER) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--stat', type=Path, nargs='+', metavar='PATH[ PATH...]', + help='IntelSEAPI statistics files in CSV format', required=True) + parser.add_argument('--out', type=Path, metavar='cc.h', + help='C++ header file to be generated', required=True) + args = parser.parse_args() + + stat = Stat(args.stat) + stat.generate(args.out) + +if __name__ == '__main__': + main()