From 0e177a1a5f56703d034881201ba00bead6120e1e Mon Sep 17 00:00:00 2001 From: Viktor Yastrebov <119661473+vyast-softserveinc@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:05:50 +0200 Subject: [PATCH] Clang transpiler integration (#756) * development based clang transpiler integration * added missing GitSubmodules.cmake * fixes for code review & OpenMP/Serial bug fix of non-polymorphic call was used * refactoring of integration, use function composition & callbacks strategy * make unchanged files unchanged * fix hipDeviceProp_t type to be the same as original HIP & revert back buildIncludes implementation * fix package build without occa-transpiler * update occa-transpiler version to v1.1 * update occa-transpiler to latest devel(fix cuda/hip intrinsics) * update occa-transpiler taggeed version * move to tag v1.1 occa-transpiler * added example with occa-transpiler and C++ featured okl kernel * fixes for code review, move getTranspilerVersion from options to bin/occa.cpp as local function * update INSTALL.md & README.md documentation files * update occa-transpiler repo * add option to build new transpiler with local installed clang * fix example of new oklt to support serial, openmp modes; remove debug print * add unsigned int to OCCA builtin types * update README and deps * update occa-transpiler to v1.1 * Remove occa-tranpiler as a submodule * Make changes to link occa-transpiler as a library * Add a link to occa-transpiler README in INSTALL.md * Fix a few typos * Add a link to occa-transpiler repo --------- Co-authored-by: Viktor Yastrebov Co-authored-by: Iurii Kobein Co-authored-by: Thilina Ratnayaka Co-authored-by: Iurii Kobein <61540607+IuriiKobein@users.noreply.github.com> --- .gitmodules | 0 CMakeLists.txt | 13 +- INSTALL.md | 26 +- README.md | 4 +- .../cpp/31_oklt_v3_moving_avg/CMakeLists.txt | 10 + .../cpp/31_oklt_v3_moving_avg/constants.h | 5 + examples/cpp/31_oklt_v3_moving_avg/main.cpp | 93 ++++++ .../31_oklt_v3_moving_avg/movingAverage.okl | 85 +++++ examples/cpp/CMakeLists.txt | 4 + include/occa/dtype/builtins.hpp | 2 + src/dtype/builtins.cpp | 4 +- src/dtype/dtype.cpp | 2 + src/occa/internal/bin/occa.cpp | 293 +++++++++++++----- src/occa/internal/core/launchedDevice.cpp | 146 +++++++-- src/occa/internal/modes/hip/polyfill.hpp | 5 +- src/occa/internal/modes/hip/registration.cpp | 2 +- src/occa/internal/modes/openmp/device.cpp | 66 ++++ src/occa/internal/modes/serial/device.cpp | 91 +++++- src/occa/internal/utils/transpiler_utils.cpp | 148 +++++++++ src/occa/internal/utils/transpiler_utils.h | 50 +++ tests/src/internal/bin/occa.cpp | 4 +- 21 files changed, 937 insertions(+), 116 deletions(-) create mode 100644 .gitmodules create mode 100644 examples/cpp/31_oklt_v3_moving_avg/CMakeLists.txt create mode 100644 examples/cpp/31_oklt_v3_moving_avg/constants.h create mode 100644 examples/cpp/31_oklt_v3_moving_avg/main.cpp create mode 100644 examples/cpp/31_oklt_v3_moving_avg/movingAverage.okl create mode 100644 src/occa/internal/utils/transpiler_utils.cpp create mode 100644 src/occa/internal/utils/transpiler_utils.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/CMakeLists.txt b/CMakeLists.txt index 83cd38601..7fd6f9ac7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ option(OCCA_ENABLE_DPCPP "Build with SYCL/DPCPP if available" ON) option(OCCA_ENABLE_TESTS "Build tests" OFF) option(OCCA_ENABLE_EXAMPLES "Build simple examples" OFF) option(OCCA_ENABLE_FORTRAN "Enable Fortran interface" OFF) +option(OCCA_CLANG_BASED_TRANSPILER "Build with occa-transpiler dependecy" OFF) if(OCCA_ENABLE_FORTRAN) enable_language(Fortran) @@ -67,6 +68,11 @@ else() set(OCCA_OS "OCCA_WINDOWS_OS") endif() +# INFO: order is important, deps should not apply compiler flags +if (OCCA_CLANG_BASED_TRANSPILER) + find_package(oklt REQUIRED) +endif() + include(SetCompilerFlags) include(CheckCXXCompilerFlag) @@ -113,6 +119,11 @@ target_include_directories(libocca PRIVATE $) target_compile_definitions(libocca PRIVATE -DUSE_CMAKE) +if (OCCA_CLANG_BASED_TRANSPILER) + target_link_libraries(libocca PRIVATE occa::occa-transpiler) + target_compile_definitions(libocca PRIVATE -DBUILD_WITH_CLANG_BASED_TRANSPILER) +endif() + #======================================= #---[ OpenMP ]-------------------------- @@ -231,7 +242,7 @@ if(OCCA_ENABLE_METAL AND APPLE) endif() endif() #======================================= - + if(NOT OCCA_IS_TOP_LEVEL) # OCCA is being built as a subdirectory in another project set(OCCA_OPENMP_ENABLED ${OCCA_OPENMP_ENABLED} PARENT_SCOPE) diff --git a/INSTALL.md b/INSTALL.md index 13de75448..13c52b029 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,18 +10,19 @@ ### Optional - - Fortan 90 compiler + - Fortran 90 compiler - CUDA 9 or later - HIP 3.5 or later - SYCL 2020 or later - OpenCL 2.0 or later - OpenMP 4.0 or later + - Support Clang based transpiler ## Linux ### **Configure** -OCCA uses the [CMake] build system. For convenience, the shell script `configure-cmake.sh` has been provided to drive the Cmake build. The following table gives a list of build parameters which are set in the file. To override the default value, it is only necessary to assign the variable an alternate value at the top of the script or at the commandline. +OCCA uses the [CMake] build system. For convenience, the shell script `configure-cmake.sh` has been provided to drive the CMake build. The following table gives a list of build parameters which are set in the file. To override the default value, it is only necessary to assign the variable an alternate value at the top of the script or at the commandline. Example ```shell @@ -46,6 +47,7 @@ $ CC=clang CXX=clang++ OCCA_ENABLE_OPENMP="OFF" ./configure-cmake.sh | OCCA_ENABLE_TESTS | Build OCCA's test harness | `ON` | | OCCA_ENABLE_EXAMPLES | Build OCCA examples | `ON` | | OCCA_ENABLE_FORTRAN | Build the Fortran language bindings | `OFF`| +| OCCA_CLANG_BASED_TRANSPILER | Build clang based transpiler that support C++ in OKL | `OFF`| | FC | Fortran 90 compiler | `gfortran` | | FFLAGS | Fortran compiler flags | *empty* | @@ -67,7 +69,25 @@ After CMake configuration is complete, OCCA can be built with the command $ cmake --build build --parallel ``` -When cross compiling for a different platform, the targeted hardware doesn't need to be available; however all dependencies—e.g., headers, libraries—must be present. Commonly this is the case for large HPC systems, where code is compiled on login nodes and run on compute nodes. +When cross compiling for a different platform, the targeted hardware doesn't need to be available; however all dependencies—e.g., headers, libraries—must be present. Commonly this is the case for large HPC systems, where code is compiled on login nodes and run on compute nodes. + + +#### Building with Clang transpiler + +occa-transpiler repository can be found in [libocca/occa-transpiler](https://github.com/libocca/occa-transpiler/). +Please refer [occa-transpiler README](https://github.com/libocca/occa-transpiler/blob/main/README.md) for instructions on how to +build and install the occa-transpiler. +Then you can use the following commands to install OCCA with occa-transpiler enabled. +Please replace `` by the root directory of your +occa-transpiler installation. + +```shell +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release -DOCCA_CLANG_BASED_TRANSPILER=ON -DCMAKE_PREFIX_PATH=/lib/cmake .. +$ cmake --build . --parallel +$ cmake --install . --prefix install +``` ### Testing diff --git a/README.md b/README.md index d309a6bbf..979232f77 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ Mission critical computational science and engineering applications from the pub ### Optional - - Fortan 90 compiler + - Fortran 90 compiler - CUDA 9 or later - HIP 4.2 or later - SYCL 2020 or later - OpenCL 2.0 or later - OpenMP 4.0 or later + - C++ support for OKL with clang based transpiler [new-okl-transpiler](https://github.com/libocca/occa-transpiler) ## Build, Test, Install @@ -67,7 +68,6 @@ $ cmake --install build --prefix install If dependencies are installed in a non-standard location, set the corresponding [environment variable](INSTALL.md#dependency-paths) to this path. - ## Use ### Environment diff --git a/examples/cpp/31_oklt_v3_moving_avg/CMakeLists.txt b/examples/cpp/31_oklt_v3_moving_avg/CMakeLists.txt new file mode 100644 index 000000000..6c319e147 --- /dev/null +++ b/examples/cpp/31_oklt_v3_moving_avg/CMakeLists.txt @@ -0,0 +1,10 @@ +compile_cpp_example_with_modes(oklt_v3_moving_avg main.cpp) + +add_custom_target(cpp_example_oklt_v3_moving_avg_cpy ALL + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/constants.h constants.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/movingAverage.okl movingAverage.okl) +add_dependencies(examples_cpp_oklt_v3_moving_avg cpp_example_oklt_v3_moving_avg_cpy) +target_sources(examples_cpp_oklt_v3_moving_avg + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/movingAverage.okl + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/constants.h + ) diff --git a/examples/cpp/31_oklt_v3_moving_avg/constants.h b/examples/cpp/31_oklt_v3_moving_avg/constants.h new file mode 100644 index 000000000..0406ad392 --- /dev/null +++ b/examples/cpp/31_oklt_v3_moving_avg/constants.h @@ -0,0 +1,5 @@ +#pragma once + +constexpr const int THREADS_PER_BLOCK = 1024; +//INFO: it's not possible to setup dynamicaly extern @shared array for CUDA +constexpr const int WINDOW_SIZE = 16; diff --git a/examples/cpp/31_oklt_v3_moving_avg/main.cpp b/examples/cpp/31_oklt_v3_moving_avg/main.cpp new file mode 100644 index 000000000..462024f63 --- /dev/null +++ b/examples/cpp/31_oklt_v3_moving_avg/main.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include "constants.h" + +std::vector buildData(std::size_t size, + float initialValue, + float fluctuation) +{ + std::vector buffer(size); + float currentValue = initialValue; + float longIncrement = 1.0f; + float fluctuationIncrement = fluctuation; + for(std::size_t i = 0; i < buffer.size(); ++i) { + buffer[i] = currentValue; + fluctuationIncrement = -fluctuationIncrement; + if(i % WINDOW_SIZE == 0) { + longIncrement = -longIncrement; + } + currentValue += longIncrement + fluctuationIncrement; + } + return buffer; +} + +std::vector goldMovingAverage(const std::vector &hostVector) { + std::vector result(hostVector.size() - WINDOW_SIZE); + for(std::size_t i = 0; i < result.size(); ++i) { + float value = 0.0f; + for(std::size_t j = 0; j < WINDOW_SIZE; ++j) { + value += hostVector[i + j]; + } + result[i] = value / WINDOW_SIZE; + } + return result; +} + +bool starts_with(const std::string &str, const std::string &substring) { + return str.rfind(substring, 0) == 0; +} + +occa::json getDeviceOptions(int argc, const char **argv) { + for(int i = 0; i < argc; ++i) { + std::string argument(argv[i]); + if((starts_with(argument,"-d") || starts_with(argument, "--device")) && i + 1 < argc) + { + std::string value(argv[i + 1]); + return occa::json::parse(value); + } + } + return occa::json::parse("{mode: 'Serial'}"); +} + +int main(int argc, const char **argv) { + + occa::json deviceOpts = getDeviceOptions(argc, argv); + auto inputHostBuffer = buildData(THREADS_PER_BLOCK * WINDOW_SIZE + WINDOW_SIZE, 10.0f, 4.0f); + std::vector outputHostBuffer(inputHostBuffer.size() - WINDOW_SIZE); + + occa::device device(deviceOpts); + occa::memory deviceInput = device.malloc(inputHostBuffer.size()); + occa::memory deviceOutput = device.malloc(outputHostBuffer.size()); + + occa::json buildProps({ + {"transpiler-version", 3} + }); + + occa::kernel movingAverageKernel = device.buildKernel("movingAverage.okl", "movingAverage32f", buildProps); + + deviceInput.copyFrom(inputHostBuffer.data(), inputHostBuffer.size()); + + movingAverageKernel(deviceInput, + static_cast(inputHostBuffer.size()), + deviceOutput, + static_cast(deviceOutput.size())); + + // Copy result to the host + deviceOutput.copyTo(&outputHostBuffer[0], outputHostBuffer.size()); + + auto goldValue = goldMovingAverage(inputHostBuffer); + + constexpr const float EPSILON = 0.001f; + for(std::size_t i = 0; i < outputHostBuffer.size(); ++i) { + bool isValid = std::abs(goldValue[i] - outputHostBuffer[i]) < EPSILON; + if(!isValid) { + std::cout << "Comparison with gold values has failed" << std::endl; + return 1; + } + } + std::cout << "Comparison with gold has passed" << std::endl; + std::cout << "Moving average finished" << std::endl; + + return 0; +} diff --git a/examples/cpp/31_oklt_v3_moving_avg/movingAverage.okl b/examples/cpp/31_oklt_v3_moving_avg/movingAverage.okl new file mode 100644 index 000000000..05666a6dc --- /dev/null +++ b/examples/cpp/31_oklt_v3_moving_avg/movingAverage.okl @@ -0,0 +1,85 @@ +#include "constants.h" + +template +struct MovingAverage { + MovingAverage(int inputSize, + int outputSize, + T *shared_input, + T *shared_output) + :_inputSize(inputSize) + ,_outputSize(outputSize) + ,_shared_data(shared_input) + ,_result_data(shared_output) + {} + + void syncCopyFrom(const T *input, int block_idx, int thread_idx) { + int linearIdx = block_idx * THREADS + thread_idx; + //INFO: copy base chunk + if(linearIdx < _inputSize) { + _shared_data[thread_idx] = input[linearIdx]; + } + //INFO: copy WINDOW chunk + int tailIdx = (block_idx + 1) * THREADS + thread_idx; + if(tailIdx < _inputSize && thread_idx < WINDOW) { + _shared_data[THREADS + thread_idx] = input[tailIdx]; + } + @barrier; + } + + void process(int thread_idx) { + T sum = T(); + for(int i = 0; i < WINDOW; ++i) { + sum += _shared_data[thread_idx + i]; + } + _result_data[thread_idx] = sum / WINDOW; + @barrier; + } + + void syncCopyTo(T *output, int block_idx, int thread_idx) { + int linearIdx = block_idx * THREADS + thread_idx; + if(linearIdx < _outputSize) { + output[linearIdx] = _result_data[thread_idx]; + } + @barrier; + } +private: + int _inputSize; + int _outputSize; + + //INFO: not supported + // @shared T _data[THREADS_PER_BLOCK + WINDOW_SIZE]; + // @shared T _result[THREADS_PER_BLOCK]; + + T *_shared_data; + T *_result_data; +}; + +@kernel void movingAverage32f(@restrict const float *inputData, + int inputSize, + @restrict float *outputData, + int outputSize) +{ + @outer(0) for (int block_idx = 0; block_idx < outputSize / THREADS_PER_BLOCK + 1; ++block_idx) { + @shared float blockInput[THREADS_PER_BLOCK + WINDOW_SIZE]; + @shared float blockResult[THREADS_PER_BLOCK]; + MovingAverage ma{ + inputSize, + outputSize, + blockInput, + blockResult + }; + @inner(0) for(int thread_idx = 0; thread_idx < THREADS_PER_BLOCK; ++thread_idx) { + ma.syncCopyFrom(inputData, block_idx, thread_idx); + } + + @inner(0) for(int thread_idx = 0; thread_idx < THREADS_PER_BLOCK; ++thread_idx) { + ma.process(thread_idx); + } + + @inner(0) for(int thread_idx = 0; thread_idx < THREADS_PER_BLOCK; ++thread_idx) { + ma.syncCopyTo(outputData, block_idx, thread_idx); + } + } +} diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 6c51ab1e9..466e33f99 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -18,6 +18,10 @@ add_subdirectory(19_stream_tags) add_subdirectory(20_native_dpcpp_kernel) add_subdirectory(30_device_function) + +if (OCCA_CLANG_BASED_TRANSPILER) + add_subdirectory(31_oklt_v3_moving_avg) +endif() # Don't force-compile OpenGL examples # add_subdirectory(16_finite_difference) # add_subdirectory(17_mandelbulb) diff --git a/include/occa/dtype/builtins.hpp b/include/occa/dtype/builtins.hpp index 3de51e5aa..4c58c44c8 100644 --- a/include/occa/dtype/builtins.hpp +++ b/include/occa/dtype/builtins.hpp @@ -21,7 +21,9 @@ namespace occa { extern const dtype_t char_; extern const dtype_t short_; extern const dtype_t int_; + extern const dtype_t uint_; extern const dtype_t long_; + extern const dtype_t ulong_; extern const dtype_t float_; extern const dtype_t double_; diff --git a/src/dtype/builtins.cpp b/src/dtype/builtins.cpp index 48174b4b8..8a2c874a9 100644 --- a/src/dtype/builtins.cpp +++ b/src/dtype/builtins.cpp @@ -11,7 +11,9 @@ namespace occa { const dtype_t char_("char", sizeof(char), true); const dtype_t short_("short", sizeof(short), true); const dtype_t int_("int", sizeof(int), true); + const dtype_t uint_("unsigned int", sizeof(unsigned int), true); const dtype_t long_("long", sizeof(long), true); + const dtype_t ulong_("unsigned long", sizeof(unsigned long), true); const dtype_t float_("float", sizeof(float), true); const dtype_t double_("double", sizeof(double), true); @@ -111,7 +113,7 @@ namespace occa { } template <> dtype_t get() { - return long_; + return ulong_; } template <> dtype_t get() { diff --git a/src/dtype/dtype.cpp b/src/dtype/dtype.cpp index b742c4c00..d30803002 100644 --- a/src/dtype/dtype.cpp +++ b/src/dtype/dtype.cpp @@ -413,6 +413,8 @@ namespace occa { dtypeMap["long"] = &dtype::long_; dtypeMap["float"] = &dtype::float_; dtypeMap["double"] = &dtype::double_; + dtypeMap["unsigned long"] = &dtype::ulong_; + dtypeMap["unsigned int"] = &dtype::uint_; // Sized primitives dtypeMap["int8"] = dtype::get().ref; diff --git a/src/occa/internal/bin/occa.cpp b/src/occa/internal/bin/occa.cpp index 15bd42437..315daec2c 100644 --- a/src/occa/internal/bin/occa.cpp +++ b/src/occa/internal/bin/occa.cpp @@ -1,9 +1,13 @@ #include +#include +#include #include - #include #include +#include +#include + #include #include #include @@ -12,6 +16,13 @@ #include #include #include +#include + +#if BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#endif +#include + namespace occa { namespace bin { @@ -123,6 +134,203 @@ namespace occa { return true; } + int getTranspilerVersion(const json &options) { + json jsonTranspileVersion = options["transpiler-version"]; + int transpilerVersion = 2; + //INFO: have no idea why json here has array type + if(!jsonTranspileVersion.isArray()) { + return transpilerVersion; + } + json elem = jsonTranspileVersion.asArray()[0]; + if(!elem.isString()) { + return transpilerVersion; + } + + try { + transpilerVersion = std::stoi(elem.string()); + } catch(const std::exception &) + { + return transpilerVersion; + } + return transpilerVersion; + } + + namespace v2 { + bool runTranspiler(const json &options, + const json &arguments, + const json &kernelProps, + const std::string &originalMode, + const std::string &mode) + { + using ParserBuildFunc = std::function(const json ¶ms)>; + static const std::map originalParserBackends = + { + {"", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"serial", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"openmp", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"cuda", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"hip", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"opencl", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"metal", [](const json ¶ms) { + return std::make_unique(params); + }}, + {"dpcpp", [](const json ¶ms) { + return std::make_unique(params); + }} + }; + + const bool printLauncher = options["launcher"]; + const std::string filename = arguments[0]; + + if (!io::exists(filename)) { + printError("File [" + filename + "] doesn't exist" ); + ::exit(1); + } + + auto parserIt = originalParserBackends.find(mode); + if(parserIt == originalParserBackends.end()) { + printError("Unable to translate for mode [" + originalMode + "]"); + ::exit(1); + } + + std::unique_ptr parser = parserIt->second(kernelProps); + parser->parseFile(filename); + + bool success = parser->succeeded(); + if (!success) { + ::exit(1); + } + + if (options["verbose"]) { + json translationInfo; + // Filename + translationInfo["translate_info/filename"] = io::expandFilename(filename); + // Date information + translationInfo["translate_info/date"] = sys::date(); + translationInfo["translate_info/human_date"] = sys::humanDate(); + // Version information + translationInfo["translate_info/occa_version"] = OCCA_VERSION_STR; + translationInfo["translate_info/okl_version"] = OKL_VERSION_STR; + // Kernel properties + translationInfo["kernel_properties"] = kernelProps; + + io::stdout + << "/* Translation Info:\n" + << translationInfo + << "*/\n"; + } + + if (printLauncher && ((mode == "cuda") + || (mode == "hip") + || (mode == "opencl") + || (mode == "dpcpp") + || (mode == "metal"))) { + lang::parser_t *launcherParser = &(((occa::lang::okl::withLauncher*) parser.get())->launcherParser); + io::stdout << launcherParser->toString(); + } else { + io::stdout << parser->toString(); + } + return true; + } + } + +#if BUILD_WITH_CLANG_BASED_TRANSPILER + namespace v3 { + bool runTranspiler(const json &options, + const json &arguments, + const json &kernelProps, + const std::string &originalMode, + const std::string &mode) + { + + auto onFileNotExists = [](const std::string &file) { + printError("File [" + file + "] doesn't exist" ); + ::exit(1); + }; + + auto onWrongBackend = [](const std::string &m) { + printError("Unsupported target backend: [" + m + "]"); + ::exit(1); + }; + + auto onFail = [](const std::vector &errors) { + std::stringstream ss; + for(const auto &err: errors) { + ss << err.desc << std::endl; + } + printError(ss.str()); + ::exit(1); + }; + + std::string filename = arguments[0]; + auto onSuccess = [&](const oklt::UserOutput &output, bool hasLauncher) -> bool { + const bool printLauncher = options["launcher"]; + + if (options["verbose"]) { + json translationInfo; + // Filename + translationInfo["translate_info/filename"] = io::expandFilename(filename); + // Date information + translationInfo["translate_info/date"] = sys::date(); + translationInfo["translate_info/human_date"] = sys::humanDate(); + // Version information + translationInfo["translate_info/occa_version"] = OCCA_VERSION_STR; + translationInfo["translate_info/okl_version"] = OKL_VERSION_STR; + // Kernel properties + translationInfo["kernel_properties"] = kernelProps; + + io::stdout + << "/* Translation Info:\n" + << translationInfo + << "*/\n"; + } + + if(printLauncher && hasLauncher) { + io::stdout << output.launcher.source; + } else { + io::stdout << output.kernel.source; + } + + return true; + }; + + transpiler::Transpiler transpiler(onSuccess, onFail, onFileNotExists, onWrongBackend); + return transpiler.run(filename, mode, kernelProps); + } + } +#endif + bool runTranspiler(const json &options, + const json &arguments, + const json &kernelProps, + const std::string &originalMode, + const std::string &mode) + { + int transpilerVersion = getTranspilerVersion(options); + #ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + if(transpilerVersion > 2) { + return v3::runTranspiler(options, arguments, kernelProps, originalMode, mode); + } + #endif + if (transpilerVersion > 2) { + printError("OCCA compiler is built without BUILD_WITH_CLANG_BASED_TRANSPILER support"); + return false; + } + return v2::runTranspiler(options, arguments, kernelProps, originalMode, mode); + } + + bool runTranslate(const json &args) { const json &options = args["options"]; const json &arguments = args["arguments"]; @@ -130,85 +338,11 @@ namespace occa { const std::string originalMode = options["mode"]; const std::string mode = lowercase(originalMode); - const bool printLauncher = options["launcher"]; - const std::string filename = arguments[0]; - - if (!io::exists(filename)) { - printError("File [" + filename + "] doesn't exist" ); - ::exit(1); - } - json kernelProps = getOptionProperties(options["kernel-props"]); kernelProps["mode"] = mode; kernelProps["defines"].asObject() += getOptionDefines(options["define"]); kernelProps["okl/include_paths"] = options["include-path"]; - - lang::parser_t *parser = NULL; - lang::parser_t *launcherParser = NULL; - if (mode == "" || mode == "serial") { - parser = new lang::okl::serialParser(kernelProps); - } else if (mode == "openmp") { - parser = new lang::okl::openmpParser(kernelProps); - } else if (mode == "cuda") { - parser = new lang::okl::cudaParser(kernelProps); - } else if (mode == "hip") { - parser = new lang::okl::hipParser(kernelProps); - } else if (mode == "opencl") { - parser = new lang::okl::openclParser(kernelProps); - } else if (mode == "metal") { - parser = new lang::okl::metalParser(kernelProps); - } else if (mode == "dpcpp") { - parser = new lang::okl::dpcppParser(kernelProps); - } - - if (!parser) { - printError("Unable to translate for mode [" + originalMode + "]"); - ::exit(1); - } - - parser->parseFile(filename); - - bool success = parser->succeeded(); - if (!success) { - delete parser; - ::exit(1); - } - - if (options["verbose"]) { - json translationInfo; - // Filename - translationInfo["translate_info/filename"] = io::expandFilename(filename); - // Date information - translationInfo["translate_info/date"] = sys::date(); - translationInfo["translate_info/human_date"] = sys::humanDate(); - // Version information - translationInfo["translate_info/occa_version"] = OCCA_VERSION_STR; - translationInfo["translate_info/okl_version"] = OKL_VERSION_STR; - // Kernel properties - translationInfo["kernel_properties"] = kernelProps; - - io::stdout - << "/* Translation Info:\n" - << translationInfo - << "*/\n"; - } - - if (printLauncher && ((mode == "cuda") - || (mode == "hip") - || (mode == "opencl") - || (mode == "dpcpp") - || (mode == "metal"))) { - launcherParser = &(((occa::lang::okl::withLauncher*) parser)->launcherParser); - io::stdout << launcherParser->toString(); - } else { - io::stdout << parser->toString(); - } - - delete parser; - if (!success) { - ::exit(1); - } - return true; + return runTranspiler(options, arguments, kernelProps, originalMode, mode); } bool runCompile(const json &args) { @@ -229,6 +363,7 @@ namespace occa { kernelProps["verbose"] = kernelProps.get("verbose", true); kernelProps["okl/include_paths"] = options["include-path"]; kernelProps["defines"].asObject() += getOptionDefines(options["define"]); + kernelProps["transpiler-version"] = getTranspilerVersion(options); device device(deviceProps); device.buildKernel(filename, kernelName, kernelProps); @@ -367,6 +502,9 @@ namespace occa { .withArg()) .addOption(cli::option('v', "verbose", "Verbose output")) + .addOption(cli::option('t', "transpiler-version", + "provide transpiler version") + .reusable().withArg()) .addArgument(cli::argument("FILE", "An .okl file") .isRequired() @@ -393,6 +531,9 @@ namespace occa { "Add additional define") .reusable() .withArg()) + .addOption(cli::option('t', "transpiler-version", + "provide transpiler version") + .reusable().withArg()) .addArgument(cli::argument("FILE", "An .okl file") .isRequired() diff --git a/src/occa/internal/core/launchedDevice.cpp b/src/occa/internal/core/launchedDevice.cpp index 821f97626..f0c94de34 100644 --- a/src/occa/internal/core/launchedDevice.cpp +++ b/src/occa/internal/core/launchedDevice.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -8,7 +6,77 @@ #include #include +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#include +#include +#include +#endif + +#include +#include + namespace occa { + +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER +namespace v3 { +bool transpileFile(const std::string &filename, + const std::string &outputFile, + const std::string &launcherOutputFile, + const occa::json &kernelProps, + lang::sourceMetadata_t &launcherMetadata, + lang::sourceMetadata_t &deviceMetadata, + const std::string &mode) +{ + auto onFileNotExists = [](const std::string &file) { + std::string errorDescription = "Can't read file: "; + OCCA_FORCE_ERROR(errorDescription << file); + }; + + auto onWrongBackend = [&](const std::string &m) { + std::string errorDescription = "Unsupported target backend: " + m; + OCCA_FORCE_ERROR(errorDescription + + ", for OKL kernel [" << filename << "]"); + }; + + bool isSilent = kernelProps.get("silent", false); + auto onFail = [&](const std::vector &errors) { + if (!isSilent) { + std::stringstream ss; + ss << "Unable to transform OKL kernel [" << filename << "]" << std::endl; + ss << "Transpilation errors occured: " << std::endl; + for(const auto &err: errors) { + ss << err.desc << std::endl; + } + OCCA_FORCE_ERROR(ss.str()); + } + }; + + auto onSuccess = [&](const oklt::UserOutput &output, bool hasLauncher) -> bool { + io::stageFiles( + { outputFile, launcherOutputFile }, + true, + [&](const strVector &tempFilenames) -> bool { + + + std::filesystem::path transpiledSource(tempFilenames[0]); + std::filesystem::path launcherSource(tempFilenames[1]); + + auto ret1 = oklt::util::writeFileAsStr(transpiledSource, output.kernel.source); + auto ret2 = oklt::util::writeFileAsStr(launcherSource, output.launcher.source); + return ret1 && ret2; + }); + + transpiler::makeMetadata(launcherMetadata, output.launcher.metadata); + transpiler::makeMetadata(deviceMetadata, output.kernel.metadata); + return true; + }; + transpiler::Transpiler transpiler(onSuccess, onFail, onFileNotExists, onWrongBackend); + return transpiler.run(filename, mode, kernelProps); +} +} +#endif + launchedModeDevice_t::launchedModeDevice_t(const occa::json &properties_) : modeDevice_t(properties_) { needsLauncherKernel = true; @@ -20,18 +88,16 @@ namespace occa { const occa::json &kernelProps, lang::sourceMetadata_t &launcherMetadata, lang::sourceMetadata_t &deviceMetadata) { - lang::okl::withLauncher &parser = *(createParser(kernelProps)); - parser.parseFile(filename); + std::unique_ptr parser(createParser(kernelProps)); + parser->parseFile(filename); // Verify if parsing succeeded - if (!parser.succeeded()) { + if (!parser->succeeded()) { if (!kernelProps.get("silent", false)) { OCCA_FORCE_ERROR("Unable to transform OKL kernel [" << filename << "]"); } - delete &parser; return false; } - io::stageFiles( { outputFile, launcherOutputFile }, true, @@ -39,17 +105,16 @@ namespace occa { const std::string &tempOutputFilename = tempFilenames[0]; const std::string &tempLauncherOutputFilename = tempFilenames[1]; - parser.writeToFile(tempOutputFilename); - parser.launcherParser.writeToFile(tempLauncherOutputFilename); + parser->writeToFile(tempOutputFilename); + parser->launcherParser.writeToFile(tempLauncherOutputFilename); return true; } ); - parser.launcherParser.setSourceMetadata(launcherMetadata); - parser.setSourceMetadata(deviceMetadata); + parser->launcherParser.setSourceMetadata(launcherMetadata); + parser->setSourceMetadata(deviceMetadata); - delete &parser; return true; } @@ -138,24 +203,53 @@ namespace occa { lang::sourceMetadata_t launcherMetadata, deviceMetadata; if (usingOkl) { // Cache raw origin - sourceFilename = ( - io::cacheFile(filename, - kc::cachedRawSourceFilename(filename), - kernelHash, - assembleKernelHeader(kernelProps)) - ); + sourceFilename = io::cacheFile(filename, + kc::cachedRawSourceFilename(filename), + kernelHash, + assembleKernelHeader(kernelProps)); const std::string outputFile = hashDir + kc::cachedSourceFilename(filename); const std::string launcherOutputFile = hashDir + kc::launcherSourceFile; - bool valid = parseFile(sourceFilename, - outputFile, - launcherOutputFile, - kernelProps, - launcherMetadata, - deviceMetadata); - if (!valid) { - return NULL; + + int transpilerVersion = kernelProps.get("transpiler-version", 2); +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + bool isValid = false; + if(transpilerVersion > 2) { + isValid = v3::transpileFile(sourceFilename, + outputFile, + launcherOutputFile, + kernelProps, + launcherMetadata, + deviceMetadata, + mode); + } else { + isValid = parseFile(sourceFilename, + outputFile, + launcherOutputFile, + kernelProps, + launcherMetadata, + deviceMetadata); + + } + + if (!isValid) { + return nullptr; + } +#else + if(transpilerVersion > 2) { + OCCA_FORCE_ERROR("OCCA compiler is built without BUILD_WITH_CLANG_BASED_TRANSPILER support"); + return nullptr; + } + if(!parseFile(sourceFilename, + outputFile, + launcherOutputFile, + kernelProps, + launcherMetadata, + deviceMetadata)) + { + return nullptr; } +#endif sourceFilename = outputFile; buildLauncherKernel(kernelHash, diff --git a/src/occa/internal/modes/hip/polyfill.hpp b/src/occa/internal/modes/hip/polyfill.hpp index e6d933fc4..d8483950e 100644 --- a/src/occa/internal/modes/hip/polyfill.hpp +++ b/src/occa/internal/modes/hip/polyfill.hpp @@ -34,7 +34,8 @@ namespace occa { class hipDeviceProp_t { public: - char *name; + //INFO: original HIP has exact this definition of name field + char name[256]; size_t totalGlobalMem; int maxThreadsPerBlock; char gcnArchName[256]; @@ -42,7 +43,7 @@ namespace occa { int minor; inline hipDeviceProp_t() : - name(NULL), + name{0}, totalGlobalMem(0), maxThreadsPerBlock(-1), major(-1), diff --git a/src/occa/internal/modes/hip/registration.cpp b/src/occa/internal/modes/hip/registration.cpp index 9e4e84623..7322eff9b 100644 --- a/src/occa/internal/modes/hip/registration.cpp +++ b/src/occa/internal/modes/hip/registration.cpp @@ -25,7 +25,7 @@ namespace occa { hipDeviceProp_t props; OCCA_HIP_ERROR("Getting device properties", hipGetDeviceProperties(&props, deviceId)); - if (props.name != NULL) { + if (std::strlen(props.name) != 0) { strcpy(deviceName, props.name); } diff --git a/src/occa/internal/modes/openmp/device.cpp b/src/occa/internal/modes/openmp/device.cpp index eb354a205..d027b139b 100644 --- a/src/occa/internal/modes/openmp/device.cpp +++ b/src/occa/internal/modes/openmp/device.cpp @@ -6,8 +6,66 @@ #include #include +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#include +#include +#include +#endif + namespace occa { namespace openmp { + +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + namespace v3 { + bool transpileFile(const std::string &filename, + const std::string &outputFile, + const occa::json &kernelProps, + lang::sourceMetadata_t &metadata, + const std::string &mode) + { + auto onFileNotExists = [](const std::string &file) { + std::string errorDescription = "Can't read file: "; + OCCA_FORCE_ERROR(errorDescription << file); + }; + + auto onWrongBackend = [&](const std::string &) { + std::string errorDescription = "Should translate to OpenMP backend"; + OCCA_FORCE_ERROR(errorDescription + + ", unable to transform OKL kernel [" << filename << "]"); + }; + + bool isSilent = kernelProps.get("silent", false); + auto onFail = [&](const std::vector &errors) { + if (!isSilent) { + std::stringstream ss; + ss << "Unable to transform OKL kernel [" << filename << "]" << std::endl; + ss << "Transpilation errors occured: " << std::endl; + for(const auto &err: errors) { + ss << err.desc << std::endl; + } + OCCA_FORCE_ERROR(ss.str()); + } + }; + + auto onSuccess = [&](const oklt::UserOutput &output, bool hasLauncher) -> bool { + io::stageFile( + outputFile, + true, + [&](const std::string &tempFilename) -> bool { + std::filesystem::path transpiledSource(tempFilename); + auto ret = oklt::util::writeFileAsStr(tempFilename, output.kernel.source); + return ret.has_value(); + }); + transpiler::makeMetadata(metadata, output.kernel.metadata); + return true; + }; + transpiler::Transpiler transpiler(onSuccess, onFail, onFileNotExists, onWrongBackend); + return transpiler.run(filename, mode, kernelProps); + } + } +#endif + device::device(const occa::json &properties_) : serial::device(properties_) {} @@ -29,6 +87,14 @@ namespace occa { const std::string &outputFile, const occa::json &kernelProps, lang::sourceMetadata_t &metadata) { + +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + int transpilerVersion = kernelProps.get("transpiler-version", 2); + if(transpilerVersion > 2) { + return v3::transpileFile(filename, outputFile, kernelProps, metadata, mode); + } +#endif + lang::okl::openmpParser parser(kernelProps); parser.parseFile(filename); diff --git a/src/occa/internal/modes/serial/device.cpp b/src/occa/internal/modes/serial/device.cpp index 97376e0ec..8709ca8df 100644 --- a/src/occa/internal/modes/serial/device.cpp +++ b/src/occa/internal/modes/serial/device.cpp @@ -11,8 +11,66 @@ #include #include +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#include +#include +#include +#endif + namespace occa { namespace serial { + +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + namespace v3 { + bool transpileFile(const std::string &filename, + const std::string &outputFile, + const occa::json &kernelProps, + lang::sourceMetadata_t &metadata, + const std::string &mode) + { + auto onFileNotExists = [](const std::string &file) { + std::string errorDescription = "Can't read file: "; + OCCA_FORCE_ERROR(errorDescription << file); + }; + + auto onWrongBackend = [=](const std::string &) { + std::string errorDescription = "Should translate to Serial backend"; + OCCA_FORCE_ERROR(errorDescription + + ", for OKL kernel [" << filename << "]"); + }; + + bool isSilent = kernelProps.get("silent", false); + auto onFail = [=](const std::vector &errors) { + if (!isSilent) { + std::stringstream ss; + ss << "Unable to transform OKL kernel [" << filename << "]" << std::endl; + ss << "Transpilation errors occured: " << std::endl; + for(const auto &err: errors) { + ss << err.desc << std::endl; + } + OCCA_FORCE_ERROR(ss.str()); + } + }; + + auto onSuccess = [&](const oklt::UserOutput &output, bool hasLauncher) -> bool { + io::stageFile( + outputFile, + true, + [&](const std::string &tempFilename) -> bool { + std::filesystem::path transpiledSource(tempFilename); + auto ret = oklt::util::writeFileAsStr(tempFilename, output.kernel.source); + return ret.has_value(); + }); + transpiler::makeMetadata(metadata, output.kernel.metadata); + return true; + }; + transpiler::Transpiler transpiler(onSuccess, onFail, onFileNotExists, onWrongBackend); + return transpiler.run(filename, mode, kernelProps); + } + } +#endif + device::device(const occa::json &properties_) : occa::modeDevice_t(properties_) { // TODO: Maybe theres something more descriptive we can populate here @@ -70,6 +128,7 @@ namespace occa { return (srEndTag->time - srStartTag->time); } + //================================== //---[ Kernel ]--------------------- @@ -286,13 +345,41 @@ namespace occa { if (compilingOkl) { const std::string outputFile = hashDir + kc::cachedSourceFilename(filename); - bool valid = parseFile(sourceFilename, + + int transpilerVersion = kernelProps.get("transpiler-version", 2); +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER + bool valid = false; + if(transpilerVersion > 2) { + valid = v3::transpileFile(sourceFilename, + outputFile, + kernelProps, + metadata, + mode); + } else { + valid = parseFile(sourceFilename, outputFile, kernelProps, metadata); + + } + if (!valid) { - return NULL; + return nullptr; } +#else + if(transpilerVersion > 2) { + OCCA_FORCE_ERROR("OCCA compiler is built without BUILD_WITH_CLANG_BASED_TRANSPILER support"); + return nullptr; + } + + if(!parseFile(sourceFilename, + outputFile, + kernelProps, + metadata)) + { + return nullptr; + } +#endif sourceFilename = outputFile; writeKernelBuildFile(hashDir + kc::buildFile, diff --git a/src/occa/internal/utils/transpiler_utils.cpp b/src/occa/internal/utils/transpiler_utils.cpp new file mode 100644 index 000000000..ba345ac24 --- /dev/null +++ b/src/occa/internal/utils/transpiler_utils.cpp @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#include +#include +#endif + +namespace occa { +namespace transpiler { + +std::string getKernelHash(const json &kernelProp) { + auto hashStr = kernelProp.get("hash"); + if(hashStr.empty()) { + throw std::runtime_error("kernel proerties does not contain hash entry"); + } + return hashStr; +} + +std::vector buildDefines(const json &kernelProp) { + const json &defines = kernelProp["defines"]; + if (!defines.isObject()) { + {}; + } + + std::vector definesStrings; + const jsonObject &defineMap = defines.object(); + jsonObject::const_iterator it = defineMap.cbegin(); + while (it != defineMap.end()) { + const std::string &define = it->first; + const json value = it->second; + + std::string defineString = define + "=" + value.toString(); + definesStrings.push_back(std::move(defineString)); + ++it; + } + return definesStrings; +} + +std::vector buildIncludes(const json &kernelProp) { + std::vector includes; + json oklIncludePaths = kernelProp.get("okl/include_paths", json{}); + if (oklIncludePaths.isArray()) { + jsonArray pathArray = oklIncludePaths.array(); + const int pathCount = (int) pathArray.size(); + for (int i = 0; i < pathCount; ++i) { + json path = pathArray[i]; + if (path.isString()) { + includes.push_back(std::filesystem::path(path.string())); + } + } + } + auto &envIncludes = env::OCCA_INCLUDE_PATH; + for(const auto &includePath: envIncludes) { + includes.push_back(includePath); + } + return includes; +} + +void makeMetadata(lang::sourceMetadata_t &sourceMetadata, + const std::string &jsonStr) +{ + lang::kernelMetadataMap &metadataMap = sourceMetadata.kernelsMetadata; + auto json = json::parse(jsonStr); + auto metadataObj = json.get("metadata"); + if(metadataObj.isArray()) { + jsonArray metaArr = metadataObj.asArray().array(); + for(const auto &elem: metaArr) { + auto name = elem.get("name"); + auto kernelObj = lang::kernelMetadata_t::fromJson(elem); + metadataMap.insert(std::make_pair(name, kernelObj)); + } + } +} + +#if BUILD_WITH_CLANG_BASED_TRANSPILER +Transpiler::Transpiler(SuccessFunc success, + FailFunc fail, + WrongInputFile wrongInputFile, + WrongBackend wrongBackend) + :_success(std::move(success)) + , _fail(std::move(fail)) + , _wrongInput(std::move(wrongInputFile)) + , _wrongBackend(std::move(wrongBackend)) +{} + +bool Transpiler::run(const std::string &filename, + const std::string &mode, + const occa::json &kernelProps) +{ + static const std::map targetBackends = + { + {"openmp", oklt::TargetBackend::OPENMP}, + {"cuda", oklt::TargetBackend::CUDA}, + {"hip", oklt::TargetBackend::HIP}, + {"dpcpp", oklt::TargetBackend::DPCPP}, + {"serial", oklt::TargetBackend::SERIAL}, + }; + + std::string normalizedMode = lowercase(mode); + auto backend = targetBackends.find(normalizedMode); + if(backend == targetBackends.end()) { + _wrongBackend(mode); + return false; + } + auto expandedFile = io::expandFilename(filename); + if (!io::exists(filename)) { + _wrongInput(filename); + return false; + } + auto sourceCode = oklt::util::readFileAsStr(expandedFile); + if(!sourceCode) { + _wrongInput(filename); + return false; + } + + auto defines = transpiler::buildDefines(kernelProps); + auto includes = transpiler::buildIncludes(kernelProps); + auto hash = transpiler::getKernelHash(kernelProps); + + oklt::UserInput input { + .backend = backend->second, + .source = std::move(sourceCode.value()), + .headers = {}, + .sourcePath = expandedFile, + .includeDirectories = std::move(includes), + .defines = std::move(defines), + .hash = std::move(hash) + }; + auto result = normalizeAndTranspile(std::move(input)); + if(!result) { + _fail(result.error()); + return false; + } + bool hasLauncher = backend->second == oklt::TargetBackend::CUDA || + backend->second == oklt::TargetBackend::HIP || + backend->second == oklt::TargetBackend::DPCPP; + auto userOutput = result.value(); + return _success(userOutput, hasLauncher); +} +#endif +} +} diff --git a/src/occa/internal/utils/transpiler_utils.h b/src/occa/internal/utils/transpiler_utils.h new file mode 100644 index 000000000..b1ca25b41 --- /dev/null +++ b/src/occa/internal/utils/transpiler_utils.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +#if BUILD_WITH_CLANG_BASED_TRANSPILER +#include +#include +#endif + +namespace occa { +namespace transpiler { + +void makeMetadata(lang::sourceMetadata_t &sourceMetadata, + const std::string &jsonStr); + + +#if BUILD_WITH_CLANG_BASED_TRANSPILER +struct Transpiler { + using SuccessFunc = std::function; + using FailFunc = std::function &errors)>; + using WrongInputFile = std::function; + using WrongBackend = std::function; + + Transpiler(SuccessFunc success, + FailFunc fail, + WrongInputFile wrongInputFile, + WrongBackend wrongBackend); + ~Transpiler() = default; + Transpiler(const Transpiler&) = delete; + Transpiler & operator = (const Transpiler &) = delete; + Transpiler(Transpiler &&) = delete; + Transpiler & operator = (Transpiler &&) = delete; + + bool run(const std::string &filename, + const std::string &mode, + const occa::json &kernelProps); +private: + SuccessFunc _success; + FailFunc _fail; + WrongInputFile _wrongInput; + WrongBackend _wrongBackend; +}; +#endif + + +} +} diff --git a/tests/src/internal/bin/occa.cpp b/tests/src/internal/bin/occa.cpp index 5a6e587d3..d47f15de7 100644 --- a/tests/src/internal/bin/occa.cpp +++ b/tests/src/internal/bin/occa.cpp @@ -173,7 +173,7 @@ int main(const int argc, const char **argv) { //---[ Compile ]------------------ const std::string compileOptions = ( - "--define --device-props --help --include-path --kernel-props -D -I -d -h -k" + "--define --device-props --help --include-path --kernel-props --transpiler-version -D -I -d -h -k -t" ); ASSERT_AUTOCOMPLETE_EQ( @@ -203,7 +203,7 @@ int main(const int argc, const char **argv) { //---[ Translate ]------------------ const std::string translateOptions = ( - "--define --help --include-path --kernel-props --launcher --mode --verbose -D -I -h -k -l -m -v" + "--define --help --include-path --kernel-props --launcher --mode --transpiler-version --verbose -D -I -h -k -l -m -t -v" ); ASSERT_AUTOCOMPLETE_EQ(