Tools for conditional compilation (#3169)
* Tools for conditional compilation * Unit tests for conditional compilation & Refactoring * Fix for MSVC selective build macros * Unit tests for conditional compilation in analysys mode * Code generation for selective build is hidden inside CMake * Comments for conditional compilation options
This commit is contained in:
parent
5ce8946864
commit
1f96ddfbf9
@ -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)
|
||||
|
||||
|
@ -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 "")
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -83,7 +83,7 @@ target_include_directories(${TARGET_NAME}_plugin_api INTERFACE
|
||||
$<TARGET_PROPERTY:${TARGET_NAME}_preproc,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
${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)
|
||||
|
||||
|
@ -12,6 +12,7 @@ set(LINK_LIBRARIES
|
||||
ngraphFunctions
|
||||
inference_engine_transformations
|
||||
openvino::itt
|
||||
openvino::conditional_compilation
|
||||
)
|
||||
set(DEPENDENCIES
|
||||
mock_engine
|
||||
|
@ -0,0 +1,141 @@
|
||||
// Copyright (C) 2018-2020 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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 <openvino/cc/factory.h>
|
||||
|
||||
namespace {
|
||||
OV_CC_DOMAINS(CCTests);
|
||||
|
||||
template<typename T>
|
||||
struct TestTemplateClass;
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<int> {
|
||||
void operator()(int &v) {
|
||||
v = 42;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<bool> {
|
||||
void operator()(int &v) {
|
||||
v = 43;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<float> {
|
||||
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<int N>
|
||||
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<int, TestNodeBase*(int)> 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
|
@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2018-2020 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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 <openvino/cc/factory.h>
|
||||
|
||||
namespace {
|
||||
OV_CC_DOMAINS(CCTests);
|
||||
|
||||
template<typename T>
|
||||
struct TestTemplateClass;
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<int> {
|
||||
void operator()(int &v) {
|
||||
v = 42;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<bool> {
|
||||
void operator()(int &v) {
|
||||
v = 43;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TestTemplateClass<float> {
|
||||
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<int N>
|
||||
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<int, TestNodeBase*(int)> 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
|
@ -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)
|
||||
|
46
openvino/conditional_compilation/CMakeLists.txt
Normal file
46
openvino/conditional_compilation/CMakeLists.txt
Normal file
@ -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()
|
144
openvino/conditional_compilation/include/openvino/cc/factory.h
Normal file
144
openvino/conditional_compilation/include/openvino/cc/factory.h
Normal file
@ -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 <string>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace openvino
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
template<typename Key, typename T>
|
||||
class Factory;
|
||||
|
||||
template<typename Key, typename T, typename ...Args>
|
||||
class Factory<Key, T(Args...)> {
|
||||
Factory(Factory const&) = delete;
|
||||
Factory& operator=(Factory const&) = delete;
|
||||
|
||||
public:
|
||||
using builder_t = std::function<T(Args...)>;
|
||||
|
||||
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)))<Impl>(key))
|
||||
#define createNodeIfRegistered(Module, key, ...) createImpl(key, __VA_ARGS__)
|
||||
|
||||
template<typename Impl>
|
||||
void registerImpl0(const Key &) {
|
||||
}
|
||||
|
||||
template<typename Impl>
|
||||
void registerImpl1(const Key & key) {
|
||||
builders[key] = [](Args... args) -> T {
|
||||
Impl *impl = new Impl(args...);
|
||||
return static_cast<T>(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<OV_CC_CAT(FACTORY_, Module), Impl>(key, OV_CC_TOSTRING(Name))
|
||||
#define createNodeIfRegistered(Module, key, ...) createImpl<OV_CC_CAT(FACTORY_, Module)>(key, __VA_ARGS__)
|
||||
|
||||
template<openvino::itt::domain_t(*domain)(), typename Impl>
|
||||
void registerImpl(const Key & key, const char *typeName) {
|
||||
const std::string task_name = "REG$" + name + "$" + to_string(key) + "$" + typeName;
|
||||
openvino::itt::ScopedTask<domain> task(openvino::itt::handle(task_name));
|
||||
builders[key] = [](Args... args) -> T {
|
||||
Impl *impl = new Impl(args...);
|
||||
return static_cast<T>(impl);
|
||||
};
|
||||
}
|
||||
|
||||
template<openvino::itt::domain_t(*domain)()>
|
||||
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<domain> task(openvino::itt::handle(task_name));
|
||||
return builder->second(args...);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
#define registerNodeIfRequired(Module, Name, key, Impl) registerImpl<Impl>(key)
|
||||
#define createNodeIfRegistered(Module, key, ...) createImpl(key, __VA_ARGS__)
|
||||
|
||||
template<typename Impl>
|
||||
void registerImpl(const Key & key) {
|
||||
builders[key] = [](Args... args) -> T {
|
||||
Impl *impl = new Impl(args...);
|
||||
return static_cast<T>(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<typename Fn>
|
||||
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<typename V,
|
||||
typename std::enable_if<std::is_enum<V>::value, bool>::type = true>
|
||||
std::string to_string(V val) const {
|
||||
return std::to_string(static_cast<int>(val));
|
||||
}
|
||||
|
||||
template<typename V,
|
||||
typename std::enable_if<!std::is_enum<V>::value, bool>::type = true>
|
||||
std::string to_string(V val) const {
|
||||
return std::to_string(val);
|
||||
}
|
||||
|
||||
using map_t = std::unordered_map<Key, builder_t>;
|
||||
|
||||
const std::string name;
|
||||
map_t builders;
|
||||
};
|
||||
}
|
||||
}
|
@ -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<typename T>
|
||||
* 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 <openvino/itt.hpp>
|
||||
|
||||
#ifdef SELECTIVE_BUILD_ANALYZER
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
namespace openvino
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
#ifndef SELECTIVE_BUILD_ANALYZER
|
||||
namespace internal
|
||||
{
|
||||
template<typename C, typename T>
|
||||
struct case_wrapper {
|
||||
using type = T;
|
||||
const C value {};
|
||||
|
||||
case_wrapper(C && val)
|
||||
: value(std::forward<C>(val))
|
||||
{}
|
||||
};
|
||||
|
||||
template<typename T, typename C>
|
||||
case_wrapper<C, T> make_case_wrapper(C && val) {
|
||||
return case_wrapper<C, T>(std::forward<C>(val));
|
||||
}
|
||||
|
||||
template<template<typename...> 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<typename Case::type>()(std::forward<Ctx>(ctx));
|
||||
return is_matched;
|
||||
}
|
||||
|
||||
template<template<typename...> class Fn, typename Ctx, typename T, typename Case, typename ...Cases>
|
||||
bool match(Ctx && ctx, T && val, Case && cs, Cases&&... cases) {
|
||||
if (match<Fn>(std::forward<Ctx>(ctx), std::forward<T>(val), std::forward<Case>(cs)))
|
||||
return true;
|
||||
return match<Fn>(std::forward<Ctx>(ctx), std::forward<T>(val), std::forward<Cases>(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<typename C, typename T>
|
||||
struct case_wrapper {
|
||||
using type = T;
|
||||
const C value {};
|
||||
const char *name = nullptr;
|
||||
|
||||
case_wrapper(C && val, const char *name)
|
||||
: value(std::forward<C>(val))
|
||||
, name(name)
|
||||
{}
|
||||
};
|
||||
|
||||
template<typename T, typename C>
|
||||
case_wrapper<C, T> make_case_wrapper(C && val, const char *name) {
|
||||
return case_wrapper<C, T>(std::forward<C>(val), name);
|
||||
}
|
||||
|
||||
template<openvino::itt::domain_t(*domain)(),
|
||||
template<typename...> 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<domain> task(
|
||||
openvino::itt::handle<struct OV_CC_CAT(Task_, __LINE__)>(
|
||||
std::string(region) + "$" + cs.name));
|
||||
Fn<typename Case::type>()(std::forward<Ctx>(ctx));
|
||||
}
|
||||
return is_matched;
|
||||
}
|
||||
|
||||
template<openvino::itt::domain_t(*domain)(),
|
||||
template<typename...> 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<domain, Fn>(region, std::forward<Ctx>(ctx), std::forward<T>(val), std::forward<Case>(cs)))
|
||||
return true;
|
||||
return match<domain, Fn>(region, std::forward<Ctx>(ctx), std::forward<T>(val), std::forward<Cases>(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_CAT(SWITCH_, Module), fn> \
|
||||
(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<Type>(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::tuple<Type1, Type2>>( \
|
||||
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<fn>(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<Type>(Case)
|
||||
|
||||
#define OV_CASE2(Case1, Case2, Type1, Type2) openvino::cc::internal::make_case_wrapper<std::tuple<Type1, Type2>>(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<fn>(ctx, val, __VA_ARGS__);
|
||||
|
||||
#define OV_CASE(Case, Type) openvino::cc::internal::make_case_wrapper<Type>(Case)
|
||||
|
||||
#define OV_CASE2(Case1, Case2, Type1, Type2) openvino::cc::internal::make_case_wrapper<std::tuple<Type1, Type2>>(std::make_tuple(Case1, Case2))
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
168
openvino/conditional_compilation/scripts/ccheader.py
Executable file
168
openvino/conditional_compilation/scripts/ccheader.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user