From bc2ea2544647b5fb0de04093ec25e61bcb1f890f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Tue, 20 Jun 2023 13:25:15 +0200 Subject: [PATCH 01/29] Added googletest dependency --- cmake/Dependencies.cmake | 3 +++ cmake/gtest.cmake | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 cmake/gtest.cmake diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index f76d7422..5f249c30 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -1 +1,4 @@ +if (LAYERS_BUILD_TESTS) + include (gtest) +endif() include (RapidXml) diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake new file mode 100644 index 00000000..65f76197 --- /dev/null +++ b/cmake/gtest.cmake @@ -0,0 +1,14 @@ +if (NOT DEPENDENCIES_FORCE_DOWNLOAD) + find_package(GTest) +endif () + +if (NOT GTest_FOUND) + cmake_minimum_required(VERSION 3.11) + include (FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.12.0 + ) + FetchContent_MakeAvailable(googletest) +endif () From 46f4ee6c929883b0376ba727e9d797330dcbb43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Tue, 20 Jun 2023 13:25:32 +0200 Subject: [PATCH 02/29] Implemented program cache lib with tests --- .clang-format | 124 +++++++ .gitignore | 3 + CMakeLists.txt | 1 + program-cache/CMakeLists.txt | 1 + program-cache/lib/CMakeLists.txt | 14 + .../inc/ocl_program_cache/program_cache.hpp | 201 +++++++++++ program-cache/lib/src/program_cache.cpp | 321 ++++++++++++++++++ program-cache/lib/test/CMakeLists.txt | 3 + program-cache/lib/test/test_program_cache.cpp | 194 +++++++++++ 9 files changed, 862 insertions(+) create mode 100644 .clang-format create mode 100644 program-cache/CMakeLists.txt create mode 100644 program-cache/lib/CMakeLists.txt create mode 100644 program-cache/lib/inc/ocl_program_cache/program_cache.hpp create mode 100644 program-cache/lib/src/program_cache.cpp create mode 100644 program-cache/lib/test/CMakeLists.txt create mode 100644 program-cache/lib/test/test_program_cache.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..848366ea --- /dev/null +++ b/.clang-format @@ -0,0 +1,124 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: Never +... diff --git a/.gitignore b/.gitignore index 2304f789..8cf55a30 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ Mkfile.old dkms.conf build + +# VSCode files +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index a2a0587d..0115877a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,4 +106,5 @@ add_subdirectory (simple-print) add_subdirectory (ocl-icd-compat) add_subdirectory (object-lifetime) add_subdirectory (param-verification) +add_subdirectory (program-cache) add_subdirectory (utils) diff --git a/program-cache/CMakeLists.txt b/program-cache/CMakeLists.txt new file mode 100644 index 00000000..3ea7a419 --- /dev/null +++ b/program-cache/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(lib) diff --git a/program-cache/lib/CMakeLists.txt b/program-cache/lib/CMakeLists.txt new file mode 100644 index 00000000..d86ea063 --- /dev/null +++ b/program-cache/lib/CMakeLists.txt @@ -0,0 +1,14 @@ +if (NOT TARGET OpenCL::HeadersCpp) + find_package(OpenCLHeadersCpp REQUIRED) +endif() + +add_library(ProgramCache + src/program_cache.cpp) +target_include_directories(ProgramCache PUBLIC inc) +target_link_libraries(ProgramCache PUBLIC LayersCommon OpenCL::HeadersCpp) +target_compile_features(ProgramCache PUBLIC cxx_std_17) +target_compile_definitions(ProgramCache PUBLIC CL_HPP_TARGET_OPENCL_VERSION=300) + +if (LAYERS_BUILD_TESTS) + add_subdirectory(test) +endif() diff --git a/program-cache/lib/inc/ocl_program_cache/program_cache.hpp b/program-cache/lib/inc/ocl_program_cache/program_cache.hpp new file mode 100644 index 00000000..4d03fa17 --- /dev/null +++ b/program-cache/lib/inc/ocl_program_cache/program_cache.hpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * OpenCL is a trademark of Apple Inc. used under license by Khronos. + */ + +#ifndef OCL_PROGRAM_CACHE_LIB_INC_OCL_PROGRAM_CACHE_PROGRAM_CACHE_HPP_ +#define OCL_PROGRAM_CACHE_LIB_INC_OCL_PROGRAM_CACHE_PROGRAM_CACHE_HPP_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace ocl::program_cache { + +/// @brief Error thrown when the path for the cache cannot be accessed. +struct cache_access_error : public std::runtime_error +{ + cache_access_error(const std::string& what_arg) + : std::runtime_error(what_arg) + {} +}; + +/// @brief Error thrown when the passed OpenCL program could not be built. +struct opencl_build_error : public std::runtime_error +{ + opencl_build_error(cl_int error) + : std::runtime_error("An OpenCL kernel build error occured: " + + std::to_string(error)) + {} +}; + +/// @brief Error thrown when a cl::Program is passed which should have been built +/// previously. +struct unbuilt_program_error : public std::runtime_error +{ + unbuilt_program_error() + : std::runtime_error("The passed program has not been built") + {} +}; + +/// @brief Store and fetch OpenCL program binaries to/from the filesystem. +class program_cache { +public: + /// @brief Creates a new instance of the \c program_cache. + /// @param context OpenCL context to build the programs for. If \c nullptr is passed, + /// then the default context is used. + /// @param cache_root Path to the program cache root on the filesystem. If \c nullopt + /// is passed, the platform dependent default location is used. + program_cache( + std::shared_ptr context = nullptr, + const std::optional& cache_root = std::nullopt); + + /// @brief Loads cached binaries for all devices associated with the \c cl::Context + /// passed in the constructor and builds a \c cl::Program. + /// @param key Key to the cache entries. The key must be equal to the key passed to a + /// previous \c store call to retrieve the same binaries. + /// @return The built \c cl::Program if a cache entry was found for all devices, + /// \c std::nullopt otherwise. + std::optional fetch(std::string_view key) const; + + /// @brief Loads cached binaries for all devices passed and returns a built \c cl::Program. + /// @param key Key to the cache entries. The key must be equal to the key passed to a + /// previous \c store call to retrieve the same binaries. + /// @param devices The devices to load the programs for. + /// @return The built \c cl::Program if a cache entry was found for all devices, + /// \c std::nullopt otherwise. + std::optional + fetch(std::string_view key, const std::vector& devices) const; + + /// @brief Stores the binary representation of a \c cl::Program in the cache. + /// @param program The program to store. It must be built previously, otherwise + /// \c unbuilt_program_error is thrown. + /// @param key The key to the cache entry, which can be used to retrieve the cache + /// entry later via \c fetch. + void store(const cl::Program& program, std::string_view key) const; + + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param source Source code of the program. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed source code, the passed options, + /// the platform's version and the devices to which the code is compiled. + /// In this overload, the code is compiled to all devices associated with the + /// \c cl::Context passed in the constructor. + cl::Program fetch_or_build_source(std::string_view source, + std::string_view options = {}) const; + + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param source Source code of the program. + /// @param context The OpenCL context to compile for. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed source code, the passed options, + /// the platform's version and the devices to which the code is compiled. + /// In this overload, the code is compiled to all devices associated with the + /// \c cl::Context passed. + cl::Program fetch_or_build_source(std::string_view source, + const cl::Context& context, + std::string_view options = {}) const; + + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param source Source code of the program. + /// @param context The OpenCL context to compile for. + /// @param devices The OpenCL devices associated with the \c context to compile for. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed source code, the passed options, + /// the platform's version and the devices to which the code is compiled. + cl::Program fetch_or_build_source(std::string_view source, + const cl::Context& context, + const std::vector& devices, + std::string_view options = {}) const; + + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param il IL code of the program. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed IL code, the passed options, + /// the platform's version and the devices to which the code is compiled. + /// In this overload, the code is compiled to all devices associated with the + /// \c cl::Context passed in the constructor. + cl::Program fetch_or_build_il(const std::vector& il, + std::string_view options = {}) const; + + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param il IL code of the program. + /// @param context The OpenCL context to compile for. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed IL code, the passed options, + /// the platform's version and the devices to which the code is compiled. + /// In this overload, the code is compiled to all devices associated with the + /// \c cl::Context passed. + cl::Program fetch_or_build_il(const std::vector& il, + const cl::Context& context, + std::string_view options = {}) const; + + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. + /// If the program existed in the cache previously, loads it back from the cache + /// without building. + /// @param il IL code of the program. + /// @param context The OpenCL context to compile for. + /// @param devices The OpenCL devices associated with the \c context to compile for. + /// @param options Build options that are passed to the OpenCL compiler. + /// @return The built OpenCL program. + /// @note The cache lookup considers the passed IL code, the passed options, + /// the platform's version and the devices to which the code is compiled. + cl::Program fetch_or_build_il(const std::vector& il, + const cl::Context& context, + const std::vector& devices, + std::string_view options = {}) const; + +private: + template + cl::Program fetch_or_build_impl(const T& input, + const cl::Context& context, + const std::vector& devices, + std::string_view options) const; + + std::filesystem::path + get_path_for_device_binary(const cl::Device& device, + std::string_view key_hash) const; + + std::shared_ptr context_; + std::filesystem::path cache_root_; +}; + +} + +#endif // OCL_PROGRAM_CACHE_LIB_INC_OCL_PROGRAM_CACHE_PROGRAM_CACHE_HPP_ diff --git a/program-cache/lib/src/program_cache.cpp b/program-cache/lib/src/program_cache.cpp new file mode 100644 index 00000000..db9f2a48 --- /dev/null +++ b/program-cache/lib/src/program_cache.cpp @@ -0,0 +1,321 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK_CL_BUILD_ERROR(expression) \ + { \ + if (const cl_int error = (expression); error != CL_SUCCESS) \ + throw ::ocl::program_cache::opencl_build_error(error); \ + } + +namespace ocl::program_cache { +namespace { + +#ifdef _WIN32 + +std::filesystem::path get_default_cache_root() +{ + std::size_t buffer_length{}; + std::wstring appdata_local; + constexpr std::size_t max_size = 512; + appdata_local.resize(max_size); + if (_wgetenv_s(&buffer_length, appdata_local.data(), max_size, + L"LOCALAPPDATA")) + { + throw cache_access_error("Could not get default cache root directory"); + } + else + { + appdata_local.resize(buffer_length); + return std::filesystem::path(appdata_local) / "Khronos" / "OpenCL" + / "cache"; + } +} + +#elif defined(__APPLE__) + +std::filesystem::path get_default_cache_root() +{ + if (char* home_path = std::getenv("HOME"); home_path == nullptr) + { + throw cache_access_error("Could not get default cache root directory"); + } + else + { + return std::filesystem::path(home_path) / "Library" / "Caches" + / "Khronos" / "OpenCL"; + } +} + +#else + +std::filesystem::path get_default_cache_root() +{ + const auto cache_home = []() -> std::filesystem::path { + if (char* cache_home = std::getenv("XDG_CACHE_HOME"); + cache_home == nullptr) + { + if (char* home_path = std::getenv("HOME"); home_path == nullptr) + { + throw cache_access_error( + "Could not get default cache root directory"); + } + else + { + return std::filesystem::path(home_path) / ".cache"; + } + } + else + { + return { cache_home }; + } + }(); + return cache_home / "Khronos" / "OpenCL" / "cache"; +} + +#endif + +std::string get_device_identifier(const cl::Device& device) +{ + const auto device_name = device.getInfo(); + const auto platform = cl::Platform(device.getInfo()); + const auto platform_version = platform.getInfo(); + return platform_version + "/" + device_name; +} + +std::vector build_program_to_binary(const cl::Context& context, + const cl::Device& device, + std::string_view source, + std::string_view options) +{ + cl::Program program(context, std::string(source)); + CHECK_CL_BUILD_ERROR(program.build(device, options.data())); + return program.getInfo().front(); +} + +std::vector build_program_to_binary(const cl::Context& context, + const cl::Device& device, + const std::vector& il, + std::string_view options) +{ + cl::Program program(context, il); + CHECK_CL_BUILD_ERROR(program.build(device, options.data())); + return program.getInfo().front(); +} + +void save_binary_to_cache(const std::filesystem::path& cache_path, + const std::vector& binary_data) +{ + std::filesystem::create_directory(cache_path.parent_path()); + std::default_random_engine prng(std::random_device{}()); + std::stringstream sstream; + sstream << std::setw(8) << std::setfill('0') << std::hex + << std::uniform_int_distribution{}(prng); + const auto tmp_file_path = + cache_path.parent_path() / (sstream.str() + ".tmp"); + { + std::ofstream ofs(tmp_file_path, std::ios::binary); + std::copy(binary_data.begin(), binary_data.end(), + std::ostreambuf_iterator(ofs)); + } + try + { + std::filesystem::rename(tmp_file_path, cache_path); + } catch (const std::filesystem::filesystem_error&) + { + // If the rename fails due to e.g. file being locked by another process + // on Windows, silently return + } +} + +std::string hash_str(std::string_view data, std::string_view options = "") +{ + std::stringstream sstream; + sstream << std::setfill('0') << std::setw(16) << std::hex + << std::hash{}(data) + + std::hash{}(options); + return sstream.str(); +} + +std::string hash_str(const std::vector& data, + std::string_view options = "") +{ + return hash_str(std::string(data.begin(), data.end()), options); +} + +} // namespace + +program_cache::program_cache( + std::shared_ptr context, + const std::optional& cache_root) + : context_(context), + cache_root_(cache_root.value_or(get_default_cache_root())) +{ + std::filesystem::create_directories(cache_root_); +} + +std::optional program_cache::fetch(std::string_view key) const +{ + return fetch(key, + (context_ == nullptr ? cl::Context::getDefault() : *context_) + .getInfo()); +} + +std::optional +program_cache::fetch(std::string_view key, + const std::vector& devices) const +{ + std::vector> device_binaries; + for (const auto& device : devices) + { + const auto cache_path = + get_path_for_device_binary(device, hash_str(key)); + std::ifstream ifs(cache_path, std::ios::binary); + if (!ifs.good()) + { + return std::nullopt; + } + device_binaries.emplace_back(std::istreambuf_iterator(ifs), + std::istreambuf_iterator()); + } + cl::Program program(context_ == nullptr ? cl::Context::getDefault() + : *context_, + devices, device_binaries); + CHECK_CL_BUILD_ERROR(program.build()); + return program; +} + +void program_cache::store(const cl::Program& program, + std::string_view key) const +{ + const auto build_statuses = program.getBuildInfo(); + const bool unsuccessful_build = + std::any_of(build_statuses.begin(), build_statuses.end(), + [](const auto& device_status) { + return device_status.second != CL_BUILD_SUCCESS; + }); + if (unsuccessful_build) + { + throw unbuilt_program_error(); + } + const auto devices = program.getInfo(); + const auto binaries = program.getInfo(); + assert(devices.size() == binaries.size()); + auto device_it = devices.begin(); + for (auto binary_it = binaries.begin(), end = binaries.end(); + binary_it != end; ++binary_it, ++device_it) + { + const auto cache_path = + get_path_for_device_binary(*device_it, hash_str(key)); + save_binary_to_cache(cache_path, *binary_it); + } +} + +cl::Program program_cache::fetch_or_build_source(std::string_view source, + std::string_view options) const +{ + return fetch_or_build_source( + source, context_ == nullptr ? cl::Context::getDefault() : *context_, + options); +} + +cl::Program program_cache::fetch_or_build_source(std::string_view source, + const cl::Context& context, + std::string_view options) const +{ + return fetch_or_build_source( + source, context, context.getInfo(), options); +} + +cl::Program +program_cache::fetch_or_build_source(std::string_view source, + const cl::Context& context, + const std::vector& devices, + std::string_view options) const +{ + // ToDo preprocess source + return fetch_or_build_impl(source, context, devices, options); +} + +cl::Program program_cache::fetch_or_build_il(const std::vector& il, + std::string_view options) const +{ + return fetch_or_build_il( + il, context_ == nullptr ? cl::Context::getDefault() : *context_, + options); +} + +cl::Program program_cache::fetch_or_build_il(const std::vector& il, + const cl::Context& context, + std::string_view options) const +{ + return fetch_or_build_il(il, context, context.getInfo(), + options); +} + +cl::Program +program_cache::fetch_or_build_il(const std::vector& il, + const cl::Context& context, + const std::vector& devices, + std::string_view options) const +{ + return fetch_or_build_impl(il, context, devices, options); +} + +template +cl::Program +program_cache::fetch_or_build_impl(const T& input, + const cl::Context& context, + const std::vector& devices, + std::string_view options) const +{ + const auto key_hash = hash_str(input, options); + std::vector> program_binaries; + std::transform( + devices.begin(), devices.end(), std::back_inserter(program_binaries), + [&](const auto& device) { + auto cache_path = get_path_for_device_binary(device, key_hash); + + if (std::ifstream ifs(cache_path, std::ios::binary); ifs.good()) + { + return std::vector( + std::istreambuf_iterator(ifs), {}); + } + const auto program_binary = + build_program_to_binary(context, device, input, options); + save_binary_to_cache(cache_path, program_binary); + return program_binary; + }); + + const auto program = cl::Program(context, devices, program_binaries); + CHECK_CL_BUILD_ERROR(program.build()); + return program; +} + +std::filesystem::path +program_cache::get_path_for_device_binary(const cl::Device& device, + std::string_view key_hash) const +{ + const auto device_hash = hash_str(get_device_identifier(device)); + assert(key_hash.size() == 16); + auto path = + cache_root_ / std::string(key_hash.begin(), key_hash.begin() + 2); + path /= + std::string(key_hash.begin() + 2, key_hash.end()) + "_" + device_hash; + return path; +} + +} // namespace ocl::program_cache diff --git a/program-cache/lib/test/CMakeLists.txt b/program-cache/lib/test/CMakeLists.txt new file mode 100644 index 00000000..7fd01c6a --- /dev/null +++ b/program-cache/lib/test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(ProgramCacheTest test_program_cache.cpp) +target_link_libraries(ProgramCacheTest PUBLIC ProgramCache OpenCL::OpenCL gtest gtest_main) +add_test(NAME ProgramCacheTest COMMAND ProgramCacheTest) diff --git a/program-cache/lib/test/test_program_cache.cpp b/program-cache/lib/test/test_program_cache.cpp new file mode 100644 index 00000000..5df0b4e0 --- /dev/null +++ b/program-cache/lib/test/test_program_cache.cpp @@ -0,0 +1,194 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ocl::program_cache::program_cache; + +#ifdef _WIN32 +namespace testing::internal { +// Passing std::filesystem::path containing non-ASCII characters to ostream +// crashes on Windows +template <> void PrintTo(const std::filesystem::path&, std::ostream*) {} +} +#endif + +class ProgramCacheTest : public testing::TestWithParam { +protected: + const cl::Context& context() const { return *context_; } + const program_cache& cache() const { return *cache_; } + + void SetUp() override + { + const auto cache_path = std::filesystem::current_path() / GetParam(); + if (std::filesystem::exists(cache_path)) + { + std::filesystem::remove_all(cache_path); + } + context_ = std::make_shared(cl::Context::getDefault()); + cache_ = std::make_unique(context_, cache_path); + } + + std::string get_program_source(int i = 100) + { + return "kernel void foo(global int* i) { *i = " + std::to_string(i) + + "; }"; + } + + std::vector get_program_il() + { + // $ echo "kernel void foo(global int* i) { *i = 100; }" > kernel.cl + // $ clang -c --target=spirv64 -o kernel.spv64 kernel.cl + return { 3, 2, 35, 7, 0, 0, 1, 0, 14, 0, 6, 0, 14, + 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 4, 0, + 0, 0, 17, 0, 2, 0, 5, 0, 0, 0, 17, 0, 2, + 0, 6, 0, 0, 0, 11, 0, 5, 0, 1, 0, 0, 0, + 79, 112, 101, 110, 67, 76, 46, 115, 116, 100, 0, 0, 14, + 0, 3, 0, 2, 0, 0, 0, 2, 0, 0, 0, 15, 0, + 4, 0, 6, 0, 0, 0, 10, 0, 0, 0, 102, 111, 111, + 0, 3, 0, 3, 0, 3, 0, 0, 0, 112, -114, 1, 0, + 5, 0, 3, 0, 6, 0, 0, 0, 102, 111, 111, 0, 71, + 0, 5, 0, 6, 0, 0, 0, 41, 0, 0, 0, 102, 111, + 111, 0, 0, 0, 0, 0, 71, 0, 4, 0, 7, 0, 0, + 0, 38, 0, 0, 0, 5, 0, 0, 0, 71, 0, 4, 0, + 7, 0, 0, 0, 44, 0, 0, 0, 4, 0, 0, 0, 71, + 0, 4, 0, 11, 0, 0, 0, 38, 0, 0, 0, 5, 0, + 0, 0, 71, 0, 4, 0, 11, 0, 0, 0, 44, 0, 0, + 0, 4, 0, 0, 0, 21, 0, 4, 0, 3, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 3, + 0, 0, 0, 9, 0, 0, 0, 100, 0, 0, 0, 19, 0, + 2, 0, 2, 0, 0, 0, 32, 0, 4, 0, 4, 0, 0, + 0, 5, 0, 0, 0, 3, 0, 0, 0, 33, 0, 4, 0, + 5, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 54, + 0, 5, 0, 2, 0, 0, 0, 6, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 55, 0, 3, 0, 4, 0, 0, + 0, 7, 0, 0, 0, -8, 0, 2, 0, 8, 0, 0, 0, + 62, 0, 5, 0, 7, 0, 0, 0, 9, 0, 0, 0, 2, + 0, 0, 0, 4, 0, 0, 0, -3, 0, 1, 0, 56, 0, + 1, 0, 54, 0, 5, 0, 2, 0, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, 0, 55, 0, 3, 0, + 4, 0, 0, 0, 11, 0, 0, 0, -8, 0, 2, 0, 12, + 0, 0, 0, 57, 0, 5, 0, 2, 0, 0, 0, 13, 0, + 0, 0, 6, 0, 0, 0, 11, 0, 0, 0, -3, 0, 1, + 0, 56, 0, 1, 0 }; + } + + void check_program(const cl::Program& program, int i = 100) + { + cl::Buffer output(context(), CL_MEM_WRITE_ONLY, sizeof(int)); + cl::KernelFunctor kernel_functor(program, "foo"); + kernel_functor(cl::EnqueueArgs(cl::NDRange(1)), output); + int h_out{}; + cl::enqueueReadBuffer(output, true, 0, sizeof(h_out), &h_out); + ASSERT_EQ(i, h_out); + } + +private: + std::shared_ptr context_; + std::unique_ptr cache_; +}; + +INSTANTIATE_TEST_SUITE_P( + ProgramCacheTest, + ProgramCacheTest, + testing::Values(std::filesystem::path("test-cache"), + // "Each person must strive for the completion and + // perfection of one's [UTF-8] character" + std::filesystem::u8path("人格 完成に努める こと"))); + +TEST(ProgramCacheBasicTest, InstantiateDefaultCache) { program_cache cache; } + +TEST_P(ProgramCacheTest, UnbuiltProgramThrows) +{ + cl::Program program(context(), get_program_source()); + const std::string key = "abcdef"; + ASSERT_THROW(cache().store(program, key), + ocl::program_cache::unbuilt_program_error); +} + +TEST_P(ProgramCacheTest, StoreAndFetch) +{ + cl::Program program(context(), get_program_source()); + ASSERT_EQ(CL_SUCCESS, program.build()); + + const std::string key = "abcdef"; + cache().store(program, key); + + program = *cache().fetch(key); + check_program(program); +} + +TEST_P(ProgramCacheTest, CacheSimpleTextProgram) +{ + const auto source_100 = get_program_source(100); + const auto source_5 = get_program_source(5); + + auto program_100 = cache().fetch_or_build_source(source_100); + auto program_5 = cache().fetch_or_build_source(source_5); + check_program(program_5, 5); + check_program(program_100, 100); + + // The subsequent lookups should be cache hits + program_5 = cache().fetch_or_build_source(source_5); + program_100 = cache().fetch_or_build_source(source_100); + check_program(program_5, 5); + check_program(program_100, 100); +} + +TEST_P(ProgramCacheTest, CacheSimpleILProgram) +{ + const auto devices = context().getInfo(); + const bool no_il_version = std::any_of( + devices.begin(), devices.end(), [](const cl::Device& device) { + const auto il_version = device.getInfo(); + return il_version.empty(); + }); + if (no_il_version) + { + GTEST_SKIP(); + } + + const auto program_100 = cache().fetch_or_build_il(get_program_il()); + check_program(program_100, 100); +} + +TEST_P(ProgramCacheTest, ParallelAccessToCache) +{ + using namespace std::chrono_literals; + + constexpr std::size_t num_futures = 16; + constexpr std::size_t num_programs_per_thread = 20; + + std::vector> futures; + for (std::size_t i = 0; i < num_futures; ++i) + { + futures.push_back(std::async(std::launch::async, [&] { + std::vector program_indices(num_programs_per_thread); + std::iota(program_indices.begin(), program_indices.end(), 1); + std::default_random_engine prng(std::random_device{}()); + std::shuffle(program_indices.begin(), program_indices.end(), prng); + for (auto idx : program_indices) + { + const auto program = + cache().fetch_or_build_source(get_program_source(idx)); + check_program(program, idx); + } + })); + } + + for (auto& future : futures) + { + future.get(); + } +} From f48313e8dd116ed151d8c07e0c79113e8592e770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Tue, 20 Jun 2023 13:43:45 +0200 Subject: [PATCH 03/29] Update CI --- .github/workflows/linux.yml | 170 ++++++++++++++++++++-------------- .github/workflows/macos.yml | 31 ++++++- .github/workflows/windows.yml | 81 +++++++++++++++- 3 files changed, 209 insertions(+), 73 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f41c108d..f6a33470 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: compatibility: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest container: streamhpc/opencl-sdk-intelcpu:ubuntu-18.04-20220127 strategy: matrix: @@ -28,18 +28,18 @@ jobs: GEN: Unix Makefiles CONFIG: Release BIN: 64 - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Debug - BIN: 32 - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Release - BIN: 32 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Debug + # BIN: 32 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Release + # BIN: 32 - C_COMPILER: gcc-11 CXX_COMPILER: g++-11 CMAKE: 3.22.1 @@ -52,18 +52,18 @@ jobs: GEN: Unix Makefiles CONFIG: Release BIN: 64 - - C_COMPILER: gcc-11 - CXX_COMPILER: g++-11 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Debug - BIN: 32 - - C_COMPILER: gcc-11 - CXX_COMPILER: g++-11 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Release - BIN: 32 + # - C_COMPILER: gcc-11 + # CXX_COMPILER: g++-11 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Debug + # BIN: 32 + # - C_COMPILER: gcc-11 + # CXX_COMPILER: g++-11 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Release + # BIN: 32 - C_COMPILER: clang-8 CXX_COMPILER: clang++-8 CMAKE: 3.22.1 @@ -76,18 +76,18 @@ jobs: GEN: Unix Makefiles CONFIG: Release BIN: 64 - - C_COMPILER: clang-8 - CXX_COMPILER: clang++-8 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Debug - BIN: 32 - - C_COMPILER: clang-8 - CXX_COMPILER: clang++-8 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Release - BIN: 32 + # - C_COMPILER: clang-8 + # CXX_COMPILER: clang++-8 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Debug + # BIN: 32 + # - C_COMPILER: clang-8 + # CXX_COMPILER: clang++-8 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Release + # BIN: 32 - C_COMPILER: clang-13 CXX_COMPILER: clang++-13 CMAKE: 3.22.1 @@ -100,18 +100,18 @@ jobs: GEN: Unix Makefiles CONFIG: Release BIN: 64 - - C_COMPILER: clang-13 - CXX_COMPILER: clang++-13 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Debug - BIN: 32 - - C_COMPILER: clang-13 - CXX_COMPILER: clang++-13 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Release - BIN: 32 + # - C_COMPILER: clang-13 + # CXX_COMPILER: clang++-13 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Debug + # BIN: 32 + # - C_COMPILER: clang-13 + # CXX_COMPILER: clang++-13 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Release + # BIN: 32 # Multi-config generators # One CMake version # For all compilers @@ -121,41 +121,41 @@ jobs: CMAKE: 3.22.1 GEN: Ninja Multi-Config BIN: 64 - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Ninja Multi-Config - BIN: 32 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Ninja Multi-Config + # BIN: 32 - C_COMPILER: gcc-11 CXX_COMPILER: g++-11 CMAKE: 3.22.1 GEN: Ninja Multi-Config BIN: 64 - - C_COMPILER: gcc-11 - CXX_COMPILER: g++-11 - CMAKE: 3.22.1 - GEN: Ninja Multi-Config - BIN: 32 + # - C_COMPILER: gcc-11 + # CXX_COMPILER: g++-11 + # CMAKE: 3.22.1 + # GEN: Ninja Multi-Config + # BIN: 32 - C_COMPILER: clang-8 CXX_COMPILER: clang++-8 CMAKE: 3.22.1 GEN: Ninja Multi-Config BIN: 64 - - C_COMPILER: clang-8 - CXX_COMPILER: clang++-8 - CMAKE: 3.22.1 - GEN: Ninja Multi-Config - BIN: 32 + # - C_COMPILER: clang-8 + # CXX_COMPILER: clang++-8 + # CMAKE: 3.22.1 + # GEN: Ninja Multi-Config + # BIN: 32 - C_COMPILER: clang-13 CXX_COMPILER: clang++-13 CMAKE: 3.22.1 GEN: Ninja Multi-Config BIN: 64 - - C_COMPILER: clang-13 - CXX_COMPILER: clang++-13 - CMAKE: 3.22.1 - GEN: Ninja Multi-Config - BIN: 32 + # - C_COMPILER: clang-13 + # CXX_COMPILER: clang++-13 + # CMAKE: 3.22.1 + # GEN: Ninja Multi-Config + # BIN: 32 env: CMAKE_EXE: /opt/Kitware/CMake/${{ matrix.CMAKE }}/bin/cmake CTEST_EXE: /opt/Kitware/CMake/${{ matrix.CMAKE }}/bin/ctest @@ -173,6 +173,12 @@ jobs: repository: KhronosGroup/OpenCL-Headers path: external/OpenCL-Headers + - name: Checkout OpenCL-CLHPP + uses: actions/checkout@v2 + with: + repository: KhronosGroup/OpenCL-CLHPP + path: external/OpenCL-CLHPP + - name: Checkout OpenCL-ICD-Loader uses: actions/checkout@v2 with: @@ -199,6 +205,30 @@ jobs: -- \ -j`nproc` + - name: Build & install OpenCL-CLHPP + shell: bash + run: | + $CMAKE_EXE \ + -G "${{matrix.GEN}}" \ + `if [[ "${{matrix.GEN}}" == "Unix Makefiles" ]]; then echo -D CMAKE_BUILD_TYPE=${{matrix.CONFIG}}; fi;` \ + -D BUILD_TESTING=OFF \ + -D BUILD_DOCS=OFF \ + -D BUILD_EXAMPLES=OFF \ + -D OPENCL_CLHPP_BUILD_TESTING=OFF \ + -D CMAKE_C_FLAGS="-w -m${{matrix.BIN}}" \ + -D CMAKE_C_COMPILER=${{matrix.C_COMPILER}} \ + -D CMAKE_C_EXTENSIONS=OFF \ + -D CMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/external/OpenCL-CLHPP/install \ + -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install" \ + -B$GITHUB_WORKSPACE/external/OpenCL-CLHPP/build \ + -H$GITHUB_WORKSPACE/external/OpenCL-CLHPP ; + $CMAKE_EXE \ + --build $GITHUB_WORKSPACE/external/OpenCL-CLHPP/build \ + `if [[ "${{matrix.GEN}}" == "Ninja Multi-Config" ]]; then echo --config Release; fi;` \ + --target install \ + -- \ + -j`nproc` + - name: Build & install OpenCL-ICD-Loader shell: bash run: | @@ -234,7 +264,7 @@ jobs: -D CMAKE_CXX_COMPILER=${{matrix.CXX_COMPILER}} -D CMAKE_CXX_EXTENSIONS=OFF -D CMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install - -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install;$GITHUB_WORKSPACE/external/OpenCL-ICD-Loader/install" + -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install;$GITHUB_WORKSPACE/external/OpenCL-ICD-Loader/install;$GITHUB_WORKSPACE/external/OpenCL-CLHPP/install" -B$GITHUB_WORKSPACE/build -H$GITHUB_WORKSPACE diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 97c426d1..8c2cca69 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -25,6 +25,12 @@ jobs: repository: KhronosGroup/OpenCL-Headers path: external/OpenCL-Headers + - name: Checkout OpenCL-CLHPP + uses: actions/checkout@v2 + with: + repository: KhronosGroup/OpenCL-CLHPP + path: external/OpenCL-CLHPP + - name: Checkout OpenCL-ICD-Loader uses: actions/checkout@v2 with: @@ -59,6 +65,29 @@ jobs: --parallel `sysctl -n hw.logicalcpu` \ `if [[ "${{matrix.GEN}}" == "Xcode" ]]; then echo "-- -quiet"; fi;` + - name: Build & install OpenCL-CLHPP + shell: bash + run: | + cmake \ + -G "${{matrix.GEN}}" \ + -D BUILD_TESTING=OFF \ + -D BUILD_DOCS=OFF \ + -D BUILD_EXAMPLES=OFF \ + -D OPENCL_CLHPP_BUILD_TESTING=OFF \ + -D CMAKE_C_FLAGS="-w" \ + -D CMAKE_C_COMPILER=/usr/local/bin/gcc-${{matrix.VER}} \ + -D CMAKE_C_EXTENSIONS=${{matrix.EXT}} \ + -D CMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/external/OpenCL-CLHPP/install \ + -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install" \ + -S $GITHUB_WORKSPACE/external/OpenCL-CLHPP \ + -B $GITHUB_WORKSPACE/external/OpenCL-CLHPP/build + cmake \ + --build $GITHUB_WORKSPACE/external/OpenCL-CLHPP/build \ + --target install \ + --config Release \ + --parallel `sysctl -n hw.logicalcpu` \ + `if [[ "${{matrix.GEN}}" == "Xcode" ]]; then echo "-- -quiet"; fi;` + - name: Build & install OpenCL-ICD-Loader shell: bash run: | @@ -89,7 +118,7 @@ jobs: -D CMAKE_CXX_STANDARD=${{matrix.STD}} -D CMAKE_CXX_EXTENSIONS=${{matrix.EXT}} -D CMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install - -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install;$GITHUB_WORKSPACE/external/OpenCL-ICD-Loader/install" + -D CMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/external/OpenCL-Headers/install;$GITHUB_WORKSPACE/external/OpenCL-ICD-Loader/install;$GITHUB_WORKSPACE/external/OpenCL-CLHPP/install" -D CMAKE_FIND_PACKAGE_PREFER_CONFIG=ON -S $GITHUB_WORKSPACE -B $GITHUB_WORKSPACE/build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9ba0075c..8300ec4b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -40,6 +40,12 @@ jobs: repository: KhronosGroup/OpenCL-Headers path: external/OpenCL-Headers + - name: Checkout OpenCL-CLHPP + uses: actions/checkout@v2 + with: + repository: KhronosGroup/OpenCL-CLHPP + path: external/OpenCL-CLHPP + - name: Checkout OpenCL-ICD-Loader uses: actions/checkout@v2 with: @@ -127,6 +133,77 @@ jobs: --config Release if ($LASTEXITCODE -ne 0) { throw "Installing OpenCL-Headers failed." } + - name: Build & install OpenCL-CLHPP (MSBuild) + if: matrix.GEN == 'Visual Studio 17 2022' + shell: pwsh + run: | + $BIN = if('${{matrix.BIN}}' -eq 'x86') {'Win32'} else {'x64'} + $C_FLAGS = '/w' + & cmake ` + -G '${{matrix.GEN}}' ` + -A $BIN ` + -T ${{matrix.VER}} ` + -D BUILD_TESTING=OFF ` + -D BUILD_DOCS=OFF ` + -D BUILD_EXAMPLES=OFF ` + -D OPENCL_CLHPP_BUILD_TESTING=OFF ` + -D CMAKE_C_FLAGS="$C_FLAGS" ` + -D CMAKE_C_STANDARD=99 ` + -D CMAKE_C_EXTENSIONS=${{matrix.EXT}} ` + -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install" ` + -S ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP ` + -B ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build + if ($LASTEXITCODE -ne 0) { throw "Configuring OpenCL-CLHPP failed." } + & cmake ` + --build ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build ` + --config Release ` + -- ` + /verbosity:minimal ` + /maxCpuCount ` + /noLogo + if ($LASTEXITCODE -ne 0) { throw "Building OpenCL-CLHPP failed." } + & cmake ` + --install ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build ` + --prefix ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install ` + --config Release + if ($LASTEXITCODE -ne 0) { throw "Installing OpenCL-CLHPP failed." } + + - name: Build & install OpenCL-CLHPP (Ninja Multi-Config) + if: matrix.GEN == 'Ninja Multi-Config' + shell: pwsh + run: | + $VER = switch ('${{matrix.VER}}') { ` + 'v141' {'14.1'} ` + 'v142' {'14.2'} ` + 'v143' {'14.3'} } + Import-Module "${env:VS_ROOT}\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" + Enter-VsDevShell -VsInstallPath ${env:VS_ROOT} -SkipAutomaticLocation -DevCmdArguments "-host_arch=x64 -arch=${{matrix.BIN}} -vcvars_ver=${VER}" + $C_FLAGS = '/w' + & cmake ` + -G '${{matrix.GEN}}' ` + -D BUILD_TESTING=OFF ` + -D BUILD_DOCS=OFF ` + -D BUILD_EXAMPLES=OFF ` + -D OPENCL_CLHPP_BUILD_TESTING=OFF ` + -D CMAKE_MAKE_PROGRAM="${env:NINJA_ROOT}\ninja.exe" ` + -D CMAKE_C_FLAGS="${C_FLAGS}" ` + -D CMAKE_C_STANDARD=99 ` + -D CMAKE_C_EXTENSIONS='${{matrix.EXT}}' ` + -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install" ` + -S ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP ` + -B ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build + if ($LASTEXITCODE -ne 0) { throw "Configuring OpenCL-CLHPP failed." } + & cmake ` + --build ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build ` + --config Release ` + -- ` + -j ${env:NUMBER_OF_PROCESSORS} + & cmake ` + --install ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\build ` + --prefix ${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install ` + --config Release + if ($LASTEXITCODE -ne 0) { throw "Installing OpenCL-CLHPP failed." } + - name: Build & install OpenCL-ICD-Loader (MSBuild) if: matrix.GEN == 'Visual Studio 17 2022' shell: pwsh @@ -211,7 +288,7 @@ jobs: -D CMAKE_CXX_FLAGS="${CXX_FLAGS}" ` -D CMAKE_CXX_EXTENSIONS='${{matrix.EXT}}' ` -D CMAKE_INSTALL_PREFIX="${env:GITHUB_WORKSPACE}\install" ` - -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install" ` + -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install;${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install" ` -S "${env:GITHUB_WORKSPACE}" ` -B "${env:GITHUB_WORKSPACE}\build" @@ -239,7 +316,7 @@ jobs: -D CMAKE_CXX_EXTENSIONS='${{matrix.EXT}}' ` -D CMAKE_EXE_LINKER_FLAGS='/INCREMENTAL' ` -D CMAKE_INSTALL_PREFIX="${env:GITHUB_WORKSPACE}\install" ` - -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install" ` + -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install;${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install" ` -S "${env:GITHUB_WORKSPACE}" ` -B "${env:GITHUB_WORKSPACE}\build" From 2cd8c60c29b085b678d38eb1fcc4da50219dfe7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Wed, 21 Jun 2023 17:19:28 +0200 Subject: [PATCH 04/29] Implemented preprocessor --- .github/workflows/linux.yml | 44 +-- .github/workflows/macos.yml | 3 +- .github/workflows/windows.yml | 11 +- program-cache/lib/CMakeLists.txt | 22 +- .../inc/ocl_program_cache/program_cache.hpp | 117 ++++---- program-cache/lib/src/instantiate_lexer.cpp | 36 +++ program-cache/lib/src/preprocessor.cpp | 277 ++++++++++++++++++ program-cache/lib/src/preprocessor.hpp | 127 ++++++++ program-cache/lib/src/program_cache.cpp | 41 ++- program-cache/lib/src/utils.hpp | 106 +++++++ program-cache/lib/test/CMakeLists.txt | 18 +- program-cache/lib/test/test_preprocessor.cpp | 103 +++++++ program-cache/lib/test/test_program_cache.cpp | 18 ++ program-cache/lib/test/test_utils.cpp | 72 +++++ 14 files changed, 909 insertions(+), 86 deletions(-) create mode 100644 program-cache/lib/src/instantiate_lexer.cpp create mode 100644 program-cache/lib/src/preprocessor.cpp create mode 100644 program-cache/lib/src/preprocessor.hpp create mode 100644 program-cache/lib/src/utils.hpp create mode 100644 program-cache/lib/test/test_preprocessor.cpp create mode 100644 program-cache/lib/test/test_utils.cpp diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f6a33470..959b156a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -16,18 +16,18 @@ jobs: # For all compilers # For all configurations # For all target architectures - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Debug - BIN: 64 - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Unix Makefiles - CONFIG: Release - BIN: 64 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Debug + # BIN: 64 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Unix Makefiles + # CONFIG: Release + # BIN: 64 # - C_COMPILER: gcc-7 # CXX_COMPILER: g++-7 # CMAKE: 3.22.1 @@ -116,11 +116,11 @@ jobs: # One CMake version # For all compilers # For all architectures - - C_COMPILER: gcc-7 - CXX_COMPILER: g++-7 - CMAKE: 3.22.1 - GEN: Ninja Multi-Config - BIN: 64 + # - C_COMPILER: gcc-7 + # CXX_COMPILER: g++-7 + # CMAKE: 3.22.1 + # GEN: Ninja Multi-Config + # BIN: 64 # - C_COMPILER: gcc-7 # CXX_COMPILER: g++-7 # CMAKE: 3.22.1 @@ -162,6 +162,16 @@ jobs: steps: + - name: Install prerequisites + shell: bash + run: | + apt-get update -qq && apt-get install -y \ + libboost-atomic-dev \ + libboost-date-time-dev \ + libboost-chrono-dev \ + libboost-thread-dev \ + libboost-wave-dev + - name: Checkout OpenCL-Layers uses: actions/checkout@v2 with: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8c2cca69..95061149 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,6 @@ on: [push, pull_request] jobs: compatibility: - if: false runs-on: macos-latest strategy: matrix: @@ -40,6 +39,7 @@ jobs: - name: Create Build Environment shell: bash run: | + brew install boost cmake -E make_directory $GITHUB_WORKSPACE/build; cmake -E make_directory $GITHUB_WORKSPACE/install; if [[ "${{matrix.GEN}}" == "Ninja Multi-Config" && ! `which ninja` ]]; then brew install ninja; fi; @@ -130,6 +130,7 @@ jobs: cmake --build $GITHUB_WORKSPACE/build --config Debug --parallel `sysctl -n hw.logicalcpu` `if [[ "${{matrix.GEN}}" == "Xcode" ]]; then echo "-- -quiet"; fi;` - name: Test + if: false working-directory: ${{runner.workspace}}/OpenCL-Layers/build shell: bash run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 8300ec4b..2588a80b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -29,6 +29,11 @@ jobs: MultiProcMaxCount: '3' # -=- steps: + - name: Install prerequisites with vcpkg + id: vcpkg-install + shell: pwsh + run: C:\vcpkg\vcpkg.exe install boost-wave:${{matrix.BIN}}-windows + - name: Checkout OpenCL-Layers uses: actions/checkout@v2 with: @@ -289,6 +294,7 @@ jobs: -D CMAKE_CXX_EXTENSIONS='${{matrix.EXT}}' ` -D CMAKE_INSTALL_PREFIX="${env:GITHUB_WORKSPACE}\install" ` -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install;${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install" ` + -D CMAKE_TOOLCHAIN_FILE=${env:VCPKG_INSTALLATION_ROOT}\scripts\buildsystems\vcpkg.cmake ` -S "${env:GITHUB_WORKSPACE}" ` -B "${env:GITHUB_WORKSPACE}\build" @@ -317,6 +323,7 @@ jobs: -D CMAKE_EXE_LINKER_FLAGS='/INCREMENTAL' ` -D CMAKE_INSTALL_PREFIX="${env:GITHUB_WORKSPACE}\install" ` -D CMAKE_PREFIX_PATH="${env:GITHUB_WORKSPACE}\external\OpenCL-Headers\install;${env:GITHUB_WORKSPACE}\external\OpenCL-ICD-Loader\install;${env:GITHUB_WORKSPACE}\external\OpenCL-CLHPP\install" ` + -D CMAKE_TOOLCHAIN_FILE=${env:VCPKG_INSTALLATION_ROOT}\scripts\buildsystems\vcpkg.cmake ` -S "${env:GITHUB_WORKSPACE}" ` -B "${env:GITHUB_WORKSPACE}\build" @@ -332,7 +339,7 @@ jobs: /verbosity:minimal ` /maxCpuCount ` /noLogo - if ($LASTEXITCODE -ne 0) { throw "Building OpenCL-ICD-Loader in ${Config} failed." } + if ($LASTEXITCODE -ne 0) { throw "Building OpenCL-Layers in ${Config} failed." } } - name: Build (Ninja) @@ -351,7 +358,7 @@ jobs: --config ${Config} ` -- ` -j ${env:NUMBER_OF_PROCESSORS} - if ($LASTEXITCODE -ne 0) { throw "Building OpenCL-ICD-Loader in ${Config} failed." } + if ($LASTEXITCODE -ne 0) { throw "Building OpenCL-Layers in ${Config} failed." } } - name: Test diff --git a/program-cache/lib/CMakeLists.txt b/program-cache/lib/CMakeLists.txt index d86ea063..b9c3a0bd 100644 --- a/program-cache/lib/CMakeLists.txt +++ b/program-cache/lib/CMakeLists.txt @@ -2,13 +2,27 @@ if (NOT TARGET OpenCL::HeadersCpp) find_package(OpenCLHeadersCpp REQUIRED) endif() +find_package(Boost COMPONENTS wave) + add_library(ProgramCache - src/program_cache.cpp) + src/program_cache.cpp + src/preprocessor.cpp + $<$:src/instantiate_lexer.cpp>) + target_include_directories(ProgramCache PUBLIC inc) -target_link_libraries(ProgramCache PUBLIC LayersCommon OpenCL::HeadersCpp) -target_compile_features(ProgramCache PUBLIC cxx_std_17) -target_compile_definitions(ProgramCache PUBLIC CL_HPP_TARGET_OPENCL_VERSION=300) +target_link_libraries(ProgramCache PRIVATE LayersCommon OpenCL::HeadersCpp Boost::wave) +target_compile_features(ProgramCache PRIVATE cxx_std_17) +target_compile_definitions(ProgramCache PRIVATE + CL_HPP_ENABLE_EXCEPTIONS + CL_HPP_MINIMUM_OPENCL_VERSION=100 + CL_HPP_TARGET_OPENCL_VERSION=300 + $<$:BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION=0>) if (LAYERS_BUILD_TESTS) add_subdirectory(test) endif() + +install(TARGETS ProgramCache + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/program-cache/lib/inc/ocl_program_cache/program_cache.hpp b/program-cache/lib/inc/ocl_program_cache/program_cache.hpp index 4d03fa17..fc464aeb 100644 --- a/program-cache/lib/inc/ocl_program_cache/program_cache.hpp +++ b/program-cache/lib/inc/ocl_program_cache/program_cache.hpp @@ -50,8 +50,8 @@ struct opencl_build_error : public std::runtime_error {} }; -/// @brief Error thrown when a cl::Program is passed which should have been built -/// previously. +/// @brief Error thrown when a cl::Program is passed which should have been +/// built previously. struct unbuilt_program_error : public std::runtime_error { unbuilt_program_error() @@ -63,115 +63,120 @@ struct unbuilt_program_error : public std::runtime_error class program_cache { public: /// @brief Creates a new instance of the \c program_cache. - /// @param context OpenCL context to build the programs for. If \c nullptr is passed, - /// then the default context is used. - /// @param cache_root Path to the program cache root on the filesystem. If \c nullopt - /// is passed, the platform dependent default location is used. + /// @param context OpenCL context to build the programs for. If \c nullptr + /// is passed, then the default context is used. + /// @param cache_root Path to the program cache root on the filesystem. If + /// \c nullopt is passed, the platform dependent default location is used. program_cache( std::shared_ptr context = nullptr, const std::optional& cache_root = std::nullopt); - /// @brief Loads cached binaries for all devices associated with the \c cl::Context - /// passed in the constructor and builds a \c cl::Program. - /// @param key Key to the cache entries. The key must be equal to the key passed to a - /// previous \c store call to retrieve the same binaries. - /// @return The built \c cl::Program if a cache entry was found for all devices, - /// \c std::nullopt otherwise. + /// @brief Loads cached binaries for all devices associated with the \c + /// cl::Context passed in the constructor and builds a \c cl::Program. + /// @param key Key to the cache entries. The key must be equal to the key + /// passed to a previous \c store call to retrieve the same binaries. + /// @return The built \c cl::Program if a cache entry was found for all + /// devices, \c std::nullopt otherwise. std::optional fetch(std::string_view key) const; - /// @brief Loads cached binaries for all devices passed and returns a built \c cl::Program. - /// @param key Key to the cache entries. The key must be equal to the key passed to a - /// previous \c store call to retrieve the same binaries. + /// @brief Loads cached binaries for all devices passed and returns a built + /// \c cl::Program. + /// @param key Key to the cache entries. The key must be equal to the key + /// passed to a previous \c store call to retrieve the same binaries. /// @param devices The devices to load the programs for. - /// @return The built \c cl::Program if a cache entry was found for all devices, - /// \c std::nullopt otherwise. + /// @return The built \c cl::Program if a cache entry was found for all + /// devices, \c std::nullopt otherwise. std::optional fetch(std::string_view key, const std::vector& devices) const; - /// @brief Stores the binary representation of a \c cl::Program in the cache. - /// @param program The program to store. It must be built previously, otherwise - /// \c unbuilt_program_error is thrown. - /// @param key The key to the cache entry, which can be used to retrieve the cache - /// entry later via \c fetch. + /// @brief Stores the binary representation of a \c cl::Program in the + /// cache. + /// @param program The program to store. It must be built previously, + /// otherwise \c unbuilt_program_error is thrown. + /// @param key The key to the cache entry, which can be used to retrieve the + /// cache entry later via \c fetch. void store(const cl::Program& program, std::string_view key) const; - /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in + /// the cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param source Source code of the program. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. - /// @note The cache lookup considers the passed source code, the passed options, - /// the platform's version and the devices to which the code is compiled. - /// In this overload, the code is compiled to all devices associated with the - /// \c cl::Context passed in the constructor. + /// @note The cache lookup considers the passed source code, the passed + /// options, the platform's version and the devices to which the code is + /// compiled. In this overload, the code is compiled to all devices + /// associated with the \c cl::Context passed in the constructor. cl::Program fetch_or_build_source(std::string_view source, std::string_view options = {}) const; - /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in + /// the cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param source Source code of the program. /// @param context The OpenCL context to compile for. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. - /// @note The cache lookup considers the passed source code, the passed options, - /// the platform's version and the devices to which the code is compiled. - /// In this overload, the code is compiled to all devices associated with the - /// \c cl::Context passed. + /// @note The cache lookup considers the passed source code, the passed + /// options, the platform's version and the devices to which the code is + /// compiled. In this overload, the code is compiled to all devices + /// associated with the \c cl::Context passed. cl::Program fetch_or_build_source(std::string_view source, const cl::Context& context, std::string_view options = {}) const; - /// @brief Builds OpenCL source code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL source code to a \c cl::Program and stores it in + /// the cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param source Source code of the program. /// @param context The OpenCL context to compile for. - /// @param devices The OpenCL devices associated with the \c context to compile for. + /// @param devices The OpenCL devices associated with the \c context to + /// compile for. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. - /// @note The cache lookup considers the passed source code, the passed options, - /// the platform's version and the devices to which the code is compiled. + /// @note The cache lookup considers the passed source code, the passed + /// options, the platform's version and the devices to which the code is + /// compiled. cl::Program fetch_or_build_source(std::string_view source, const cl::Context& context, const std::vector& devices, std::string_view options = {}) const; - /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the + /// cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param il IL code of the program. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. /// @note The cache lookup considers the passed IL code, the passed options, /// the platform's version and the devices to which the code is compiled. - /// In this overload, the code is compiled to all devices associated with the - /// \c cl::Context passed in the constructor. + /// In this overload, the code is compiled to all devices associated with + /// the \c cl::Context passed in the constructor. cl::Program fetch_or_build_il(const std::vector& il, std::string_view options = {}) const; - /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the + /// cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param il IL code of the program. /// @param context The OpenCL context to compile for. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. /// @note The cache lookup considers the passed IL code, the passed options, /// the platform's version and the devices to which the code is compiled. - /// In this overload, the code is compiled to all devices associated with the - /// \c cl::Context passed. + /// In this overload, the code is compiled to all devices associated with + /// the \c cl::Context passed. cl::Program fetch_or_build_il(const std::vector& il, const cl::Context& context, std::string_view options = {}) const; - /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the cache. - /// If the program existed in the cache previously, loads it back from the cache - /// without building. + /// @brief Builds OpenCL IL code to a \c cl::Program and stores it in the + /// cache. If the program existed in the cache previously, loads it back + /// from the cache without building. /// @param il IL code of the program. /// @param context The OpenCL context to compile for. - /// @param devices The OpenCL devices associated with the \c context to compile for. + /// @param devices The OpenCL devices associated with the \c context to + /// compile for. /// @param options Build options that are passed to the OpenCL compiler. /// @return The built OpenCL program. /// @note The cache lookup considers the passed IL code, the passed options, diff --git a/program-cache/lib/src/instantiate_lexer.cpp b/program-cache/lib/src/instantiate_lexer.cpp new file mode 100644 index 00000000..9be2298c --- /dev/null +++ b/program-cache/lib/src/instantiate_lexer.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * OpenCL is a trademark of Apple Inc. used under license by Khronos. + */ + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4702) +#pragma warning(disable : 4706) +#endif + +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#include + +template struct boost::wave::cpplexer::new_lexer_gen< + std::string_view::iterator>; +template struct boost::wave::cpplexer::new_lexer_gen< + std::string_view::const_iterator>; diff --git a/program-cache/lib/src/preprocessor.cpp b/program-cache/lib/src/preprocessor.cpp new file mode 100644 index 00000000..343fd4d2 --- /dev/null +++ b/program-cache/lib/src/preprocessor.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * OpenCL is a trademark of Apple Inc. used under license by Khronos. + */ + +#include "preprocessor.hpp" + +#include "utils.hpp" + +// See https://github.com/boostorg/wave/issues/159 +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfree-nonheap-object" +#endif +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4706) +#endif + +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#include +#include +#include +#include +#include +#include + +namespace ocl::program_cache { +namespace { +using lex_iterator_t = + boost::wave::cpplexer::lex_iterator>; +using context_t = + boost::wave::context; + + +void undefine_default_macros(context_t& context) +{ + using string_type = context_t::string_type; + static const std::array not_removable_macros{ + "__LINE__", "__FILE__", "__BASE_FILE__", "__DATE__", + "__TIME__", "__STDC__", "__INCLUDE_LEVEL__", "__cplusplus", + }; + std::vector macro_names; + std::copy(context.macro_names_begin(), context.macro_names_end(), + std::back_inserter(macro_names)); + for (const auto& macro : macro_names) + { + if (std::find(not_removable_macros.begin(), not_removable_macros.end(), + macro) + == not_removable_macros.end()) + { + context.remove_macro_definition(macro, true); + } + } +} + +int get_device_opencl_c_id(const cl::Device& device) +{ + const auto device_opencl_c_version = + device.getInfo(); + const auto [device_c_major, device_c_minor] = + utils::parse_device_opencl_c_version(device_opencl_c_version); + return 100 * device_c_major + 10 * device_c_minor; +} + +void add_opencl_macro_defs(const cl::Device& device, + context_t& context, + LanguageVersion language) +{ + const cl::Platform platform(device.getInfo()); + const auto platform_opencl_version = + platform.getInfo(); + const auto [platform_major, platform_minor] = + utils::parse_platform_opencl_version(platform_opencl_version); + const int platform_id = 100 * platform_major + 10 * platform_minor; + const int device_c_id = get_device_opencl_c_id(device); + + context.add_macro_definition("__OPENCL_VERSION__=" + + std::to_string(platform_id)); + if (device_c_id >= 110) + { + context.add_macro_definition("CL_VERSION_1_0=100"); + context.add_macro_definition("CL_VERSION_1_1=110"); + } + if (device_c_id >= 120) + { + context.add_macro_definition("CL_VERSION_1_2=120"); + if (!language.is_cpp()) + { + context.add_macro_definition("__OPENCL_C_VERSION__=" + + std::to_string(language.id())); + } + } + if (device_c_id >= 200) + { + context.add_macro_definition("CL_VERSION_2_0=200"); + } + if (device_c_id >= 300) + { + context.add_macro_definition("CL_VERSION_3_0=300"); + } + if (device.getInfo()) + { + context.add_macro_definition("__ENDIAN_LITTLE__=1"); + } + if (device.getInfo()) + { + context.add_macro_definition("__IMAGE_SUPPORT__=1"); + } + if (language.is_cpp()) + { + context.add_macro_definition("__OPENCL_CPP_VERSION__=" + + std::to_string(language.id())); + context.add_macro_definition("__CL_CPP_VERSION_1_0__=100"); + context.add_macro_definition("__CL_CPP_VERSION_2021__=202100"); + } +} + +void process_option(const Option& option, + context_t& context, + LanguageVersion& language) +{ + std::visit(utils::overloads{ + [&](const DefinitionOpt& opt) { + context.add_macro_definition( + std::string(opt.definition_)); + }, + [&](const IncludeOpt& opt) { + context.add_include_path(std::string(opt.path_).c_str()); + }, + [&](const LanguageVersionOpt& opt) { + language = opt.get_language(); + }, + [&](const FastRelaxedMathOpt&) { + context.add_macro_definition("__FAST_RELAXED_MATH__=1"); + } }, + option); +} + +LanguageVersion get_default_language(const cl::Device& device) +{ + return LanguageVersion(std::min(120, get_device_opencl_c_id(device))); +} + +} // namespace + +LanguageVersionOpt::LanguageVersionOpt(std::string_view version_str) + : language_(100) +{ + std::string version_str_upper; + std::transform(version_str.begin(), version_str.end(), + std::back_inserter(version_str_upper), + [](const auto c) { return std::toupper(c); }); + if (version_str_upper == "CL1.1") + language_ = LanguageVersion(110, false); + else if (version_str_upper == "CL1.2") + language_ = LanguageVersion(120, false); + else if (version_str_upper == "CL2.0") + language_ = LanguageVersion(200, false); + else if (version_str_upper == "CL3.0") + language_ = LanguageVersion(300, false); + else if (version_str_upper == "CLC++1.0") + language_ = LanguageVersion(100, true); + else if (version_str_upper == "CLC++2021") + language_ = LanguageVersion(2021, true); + else + throw preprocess_exception("Invalid -cl-std specification"); +} + +std::vector