| Line |
Branch |
Exec |
Source |
| 1 |
|
|
// SPDX-License-Identifier: BSD-3-Clause |
| 2 |
|
|
#include "chrono_batch_render/device/compute/runtime_compiler.h" |
| 3 |
|
|
|
| 4 |
|
|
#include <deque> |
| 5 |
|
|
#include <optional> |
| 6 |
|
|
#include <vector> |
| 7 |
|
|
|
| 8 |
|
|
#include <fmt/format.h> |
| 9 |
|
|
|
| 10 |
|
|
#include "chrono_batch_render/util/disk_cache.h" |
| 11 |
|
|
#include "chrono_batch_render/util/error.h" |
| 12 |
|
|
#include "chrono_batch_render/util/library.h" |
| 13 |
|
|
#include "chrono_batch_render/util/log.h" |
| 14 |
|
|
#include "chrono_batch_render/util/text.h" |
| 15 |
|
|
#include "chrono_batch_render/util/version.h" |
| 16 |
|
|
|
| 17 |
|
|
namespace chrono::batch_render::device::compute { |
| 18 |
|
|
|
| 19 |
|
|
namespace { |
| 20 |
|
|
|
| 21 |
|
|
using Program = RuntimeCompiler::Program; |
| 22 |
|
|
|
| 23 |
|
✗ |
void Check(const RuntimeCompiler& compiler, int result, const char* call) { |
| 24 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
if (result == 0) { |
| 25 |
|
✗ |
return; |
| 26 |
|
|
} |
| 27 |
|
✗ |
const char* text = compiler.getErrorString(result); |
| 28 |
0/1
✗ Branch 1 not taken.
|
✗ |
throw ApiError(fmt::format("{}: {}", compiler.name, call), text != nullptr ? text : "unknown", |
| 29 |
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
|
✗ |
result); |
| 30 |
|
|
} |
| 31 |
|
|
|
| 32 |
|
✗ |
Program CreateProgram(const RuntimeCompiler& compiler, std::string_view source, |
| 33 |
|
|
std::span<const SourceHeader> headers) { |
| 34 |
|
✗ |
std::vector<std::string> texts; |
| 35 |
|
✗ |
std::vector<const char*> sources; |
| 36 |
|
✗ |
std::vector<const char*> names; |
| 37 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
for (const SourceHeader& header : headers) { |
| 38 |
0/1
✗ Branch 1 not taken.
|
✗ |
texts.emplace_back(header.text); |
| 39 |
|
|
} |
| 40 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
for (size_t i = 0; i < headers.size(); ++i) { |
| 41 |
0/1
✗ Branch 1 not taken.
|
✗ |
sources.push_back(texts[i].c_str()); |
| 42 |
0/1
✗ Branch 1 not taken.
|
✗ |
names.push_back(headers[i].name); |
| 43 |
|
|
} |
| 44 |
|
|
|
| 45 |
|
✗ |
Program program = nullptr; |
| 46 |
0/1
✗ Branch 1 not taken.
|
✗ |
const std::string text(source); |
| 47 |
0/1
✗ Branch 1 not taken.
|
✗ |
Check(compiler, |
| 48 |
0/1
✗ Branch 1 not taken.
|
✗ |
compiler.createProgram(&program, text.c_str(), "kernel.cu", int(headers.size()), |
| 49 |
0/1
✗ Branch 1 not taken.
|
✗ |
sources.data(), names.data()), |
| 50 |
|
|
"createProgram"); |
| 51 |
|
✗ |
return program; |
| 52 |
|
✗ |
} |
| 53 |
|
|
|
| 54 |
|
✗ |
std::string ReadOutput(const RuntimeCompiler& compiler, Program program, |
| 55 |
|
|
int (*size)(Program, size_t*), int (*fill)(Program, char*), |
| 56 |
|
|
const char* call) { |
| 57 |
|
✗ |
size_t bytes = 0; |
| 58 |
|
✗ |
Check(compiler, size(program, &bytes), call); |
| 59 |
0/1
✗ Branch 2 not taken.
|
✗ |
std::string output(bytes, '\0'); |
| 60 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
Check(compiler, fill(program, output.data()), call); |
| 61 |
|
✗ |
return output; |
| 62 |
|
✗ |
} |
| 63 |
|
|
|
| 64 |
|
|
} |
| 65 |
|
|
|
| 66 |
|
✗ |
std::string RuntimeCompiler::Compile(std::string_view source, std::span<const SourceHeader> headers, |
| 67 |
|
|
std::span<const std::string> options, |
| 68 |
|
|
std::string_view what) const { |
| 69 |
|
✗ |
Program program = CreateProgram(*this, source, headers); |
| 70 |
|
|
|
| 71 |
|
✗ |
struct Destroy { |
| 72 |
|
|
const RuntimeCompiler& compiler; |
| 73 |
|
|
Program& program; |
| 74 |
|
|
|
| 75 |
|
✗ |
~Destroy() { |
| 76 |
|
✗ |
compiler.destroyProgram(&program); |
| 77 |
|
✗ |
} |
| 78 |
|
✗ |
} destroy{*this, program}; |
| 79 |
|
|
|
| 80 |
|
✗ |
std::vector<const char*> arguments; |
| 81 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
for (const std::string& option : options) { |
| 82 |
0/1
✗ Branch 1 not taken.
|
✗ |
arguments.push_back(option.c_str()); |
| 83 |
|
|
} |
| 84 |
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
✗ |
if (compileProgram(program, int(arguments.size()), arguments.data()) != 0) { |
| 85 |
0/1
✗ Branch 1 not taken.
|
✗ |
throw Error("Compiling {} with {} {} failed:\n{}", what, name, version.ToString(), |
| 86 |
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
|
✗ |
ReadOutput(*this, program, getLogSize, getLog, "getProgramLog")); |
| 87 |
|
|
} |
| 88 |
0/1
✗ Branch 1 not taken.
|
✗ |
return ReadOutput(*this, program, getCodeSize, getCode, "getCode"); |
| 89 |
|
✗ |
} |
| 90 |
|
|
|
| 91 |
|
✗ |
RuntimeCompiler::Compiled RuntimeCompiler::CompileCached(std::string_view source, |
| 92 |
|
|
std::span<const SourceHeader> headers, |
| 93 |
|
|
std::span<const std::string> options, |
| 94 |
|
|
std::string_view what, |
| 95 |
|
|
const DiskCache& cache) const { |
| 96 |
0/1
✗ Branch 2 not taken.
|
✗ |
std::string key = fmt::format("{} {} {}", name, version.ToString(), |
| 97 |
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
|
✗ |
Join({options.begin(), options.end()}, " ")); |
| 98 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
for (const SourceHeader& header : headers) { |
| 99 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
key += fmt::format(" {} {}", header.name, Digest(header.text)); |
| 100 |
|
|
} |
| 101 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
key += " source " + Digest(source); |
| 102 |
|
|
|
| 103 |
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
✗ |
if (std::optional<std::string> code = cache.Load(key)) { |
| 104 |
|
✗ |
return {*std::move(code), true}; |
| 105 |
|
✗ |
} |
| 106 |
|
|
|
| 107 |
0/1
✗ Branch 1 not taken.
|
✗ |
Compiled compiled{Compile(source, headers, options, what), false}; |
| 108 |
0/1
✗ Branch 1 not taken.
|
✗ |
cache.Store(key, compiled.code); |
| 109 |
|
✗ |
return compiled; |
| 110 |
|
✗ |
} |
| 111 |
|
|
|
| 112 |
|
✗ |
Loaded<RuntimeCompiler> LoadRuntimeCompiler(std::string_view name, std::string_view prefix, |
| 113 |
|
|
std::string_view code, |
| 114 |
|
|
std::initializer_list<const char*> names, |
| 115 |
|
|
std::initializer_list<const char*> paths, |
| 116 |
|
|
Version minimum, const std::filesystem::path& beside) { |
| 117 |
|
✗ |
const Library library = Library::Load(names, paths, beside); |
| 118 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
if (!library) { |
| 119 |
0/1
✗ Branch 1 not taken.
|
✗ |
return fmt::format("no {} was found", name); |
| 120 |
|
|
} |
| 121 |
|
|
|
| 122 |
|
✗ |
RuntimeCompiler compiler; |
| 123 |
0/1
✗ Branch 1 not taken.
|
✗ |
compiler.name = name; |
| 124 |
0/1
✗ Branch 1 not taken.
|
✗ |
compiler.library = library.Name(); |
| 125 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
SymbolResolver resolve(library); |
| 126 |
0/1
✗ Branch 1 not taken.
|
✗ |
std::deque<std::string> symbols; |
| 127 |
|
✗ |
const auto call = [&](std::string_view suffix, auto& function) { |
| 128 |
0/14
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<char const* (*)(int)>(std::basic_string_view<char, std::char_traits<char> >, char const* (*&)(int)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(chrono::batch_render::device::compute::CompilerProgram**)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(chrono::batch_render::device::compute::CompilerProgram**)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(chrono::batch_render::device::compute::CompilerProgram**, char const*, char const*, int, char const* const*, char const* const*)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(chrono::batch_render::device::compute::CompilerProgram**, char const*, char const*, int, char const* const*, char const* const*)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(chrono::batch_render::device::compute::CompilerProgram*, char*)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(chrono::batch_render::device::compute::CompilerProgram*, char*)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(chrono::batch_render::device::compute::CompilerProgram*, int, char const* const*)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(chrono::batch_render::device::compute::CompilerProgram*, int, char const* const*)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(chrono::batch_render::device::compute::CompilerProgram*, unsigned long*)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(chrono::batch_render::device::compute::CompilerProgram*, unsigned long*)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
auto chrono::batch_render::device::compute::LoadRuntimeCompiler(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, std::initializer_list<char const*>, std::initializer_list<char const*>, chrono::batch_render::Version, std::filesystem::__cxx11::path const&)::{lambda(std::basic_string_view<char, std::char_traits<char> >, auto:1&)#1}::operator()<int (*)(int*, int*)>(std::basic_string_view<char, std::char_traits<char> >, int (*&)(int*, int*)) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
|
✗ |
resolve(symbols.emplace_back(fmt::format("{}{}", prefix, suffix)).c_str(), function); |
| 129 |
|
✗ |
}; |
| 130 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("Version", compiler.getVersion); |
| 131 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("GetErrorString", compiler.getErrorString); |
| 132 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("CreateProgram", compiler.createProgram); |
| 133 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("DestroyProgram", compiler.destroyProgram); |
| 134 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("CompileProgram", compiler.compileProgram); |
| 135 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("GetProgramLogSize", compiler.getLogSize); |
| 136 |
0/1
✗ Branch 1 not taken.
|
✗ |
call("GetProgramLog", compiler.getLog); |
| 137 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
call(fmt::format("Get{}Size", code), compiler.getCodeSize); |
| 138 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
call(fmt::format("Get{}", code), compiler.getCode); |
| 139 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
if (resolve.Missing() != nullptr) { |
| 140 |
0/1
✗ Branch 1 not taken.
|
✗ |
return fmt::format("{} lacks {}", compiler.library, resolve.Missing()); |
| 141 |
|
|
} |
| 142 |
|
|
|
| 143 |
|
✗ |
int major = 0; |
| 144 |
|
✗ |
int minor = 0; |
| 145 |
0/1
✗ Branch 1 not taken.
|
✗ |
compiler.getVersion(&major, &minor); |
| 146 |
|
✗ |
compiler.version = {major, minor}; |
| 147 |
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
✗ |
if (compiler.version < minimum) { |
| 148 |
0/1
✗ Branch 1 not taken.
|
✗ |
return TooOld(name, compiler.version, minimum); |
| 149 |
|
|
} |
| 150 |
|
|
|
| 151 |
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
|
✗ |
Log(LogLevel::Debug, "{} {} from {}", name, compiler.version.ToString(), compiler.library); |
| 152 |
|
✗ |
return compiler; |
| 153 |
|
✗ |
} |
| 154 |
|
|
|
| 155 |
|
|
} |
| 156 |
|
|
|