This introduces LipoProteinLipase (lpl), a simple tool for creating fat binaries. It represents a direct replacement of the creaky hccgenco.sh script, which had various issues. The format it uses is that of a code object bundle, generated by the Clang Offload Bundler. The output is always suffixed with the ".adipose" extension. It is shared with HCC. The hipcc script and associated tests are modified to use lpl. Help can be obtained by invoking lpl --help. A more computer-sciency / corporate friendly name is likely to be beneficial, which is a reason for choosing easily searchable/replaceable names such as lpl or adipose.
Šī revīzija ir iekļauta:
@@ -0,0 +1,12 @@
|
||||
add_executable(lpl lpl.cpp)
|
||||
set_target_properties(
|
||||
lpl PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
target_include_directories(lpl PUBLIC ${PROJECT_SOURCE_DIR}/src)
|
||||
# Install LPL if platform is hcc
|
||||
if (HIP_PLATFORM STREQUAL "hcc")
|
||||
install(TARGETS lpl RUNTIME DESTINATION bin)
|
||||
endif ()
|
||||
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Ielādēt izmaiņas
@@ -0,0 +1,56 @@
|
||||
#include "lpl.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace clara;
|
||||
using namespace hip_impl;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try {
|
||||
if (!hipcc_and_lpl_colocated()) {
|
||||
throw runtime_error{
|
||||
"The LPL executable and hipcc must be in the same directory."};
|
||||
}
|
||||
|
||||
bool help = false;
|
||||
string flags;
|
||||
string output;
|
||||
vector<string> sources;
|
||||
string targets;
|
||||
|
||||
auto cmd = cmdline_parser(help, sources, targets, flags, output);
|
||||
|
||||
const auto r = cmd.parse(Args{argc, argv});
|
||||
|
||||
if (!r) throw runtime_error{r.errorMessage()};
|
||||
|
||||
if (help) cout << cmd << endl;
|
||||
else {
|
||||
if (sources.empty()) throw runtime_error{"No inputs specified."};
|
||||
|
||||
auto tmp = tokenize_targets(targets);
|
||||
if (tmp.empty()) {
|
||||
tmp.assign(amdgpu_targets().cbegin(), amdgpu_targets().cend());
|
||||
}
|
||||
else validate_targets(tmp);
|
||||
|
||||
if (output.empty()) for (auto&& x : tmp) output += x;
|
||||
|
||||
generate_fat_binary(sources, tmp, flags, output);
|
||||
}
|
||||
}
|
||||
catch (const exception& ex) {
|
||||
cerr << ex.what() << endl;
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
#include "clara/clara.hpp"
|
||||
#include "pstreams/pstream.h"
|
||||
#include "../src/elfio/elfio.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace hip_impl
|
||||
{
|
||||
inline
|
||||
const std::unordered_set<std::string>& amdgpu_targets()
|
||||
{ // The evolving list lives at:
|
||||
// https://www.llvm.org/docs/AMDGPUUsage.html#processors.
|
||||
static const std::unordered_set<std::string> r{
|
||||
"gfx701", "gfx801", "gfx802", "gfx803", "gfx900"};
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
const std::string& fat_binary_extension()
|
||||
{
|
||||
static const std::string r{".adipose"};
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
const std::string& kernel_section()
|
||||
{
|
||||
static const std::string r{".kernel"};
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
const std::string& path_to_self()
|
||||
{
|
||||
static constexpr const char self[] = "/proc/self/exe";
|
||||
|
||||
static std::string r(PATH_MAX, '\0');
|
||||
static std::once_flag f;
|
||||
|
||||
std::call_once(f, []() {
|
||||
decltype(readlink(self, &r.front(), r.size())) read_cnt;
|
||||
do {
|
||||
read_cnt = readlink(self, &r.front(), r.size());
|
||||
} while (read_cnt == -1);
|
||||
|
||||
r.resize(read_cnt);
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
const std::string& path_to_hipcc()
|
||||
{
|
||||
assert(!path_to_self().empty());
|
||||
|
||||
static const auto r = path_to_self().substr(
|
||||
0, path_to_self().find_last_of('/')) += "/hipcc";
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string make_hipcc_call(
|
||||
const std::vector<std::string>& sources,
|
||||
const std::vector<std::string>& targets,
|
||||
const std::string& flags,
|
||||
const std::string& hipcc_output)
|
||||
{
|
||||
assert(!sources.empty() && !targets.empty() && !hipcc_output.empty());
|
||||
|
||||
std::string r{path_to_hipcc() + ' '};
|
||||
|
||||
for (auto&& x : sources) r += x + ' ';
|
||||
r += "-o " + hipcc_output + ' ';
|
||||
for (auto&& x : targets) r += "--amdgpu-target=" + x + ' ';
|
||||
r += flags + " -fPIC -shared";
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
void copy_kernel_section_to_fat_binary(
|
||||
const std::string& tmp, const std::string& output)
|
||||
{
|
||||
ELFIO::elfio reader;
|
||||
if (!reader.load(tmp)) {
|
||||
throw std::runtime_error{
|
||||
"The result of the compilation is inaccessible."};
|
||||
}
|
||||
|
||||
const auto it = std::find_if(
|
||||
reader.sections.begin(),
|
||||
reader.sections.end(),
|
||||
[](const ELFIO::section* x) {
|
||||
return x->get_name() == kernel_section();
|
||||
});
|
||||
|
||||
std::ofstream out{output + fat_binary_extension()};
|
||||
|
||||
if (it == reader.sections.end()) {
|
||||
std::cerr << "Warning: no kernels were generated; fat binary shall "
|
||||
"be empty." << std::endl;
|
||||
}
|
||||
else {
|
||||
std::copy_n(
|
||||
(*it)->get_data(),
|
||||
(*it)->get_size(),
|
||||
std::ostreambuf_iterator<char>{out});
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void generate_fat_binary(
|
||||
const std::vector<std::string>& sources,
|
||||
const std::vector<std::string>& targets,
|
||||
const std::string& flags,
|
||||
const std::string& output)
|
||||
{
|
||||
static const auto d = [](const std::string* f) { remove(f->c_str()); };
|
||||
|
||||
std::unique_ptr<const std::string, decltype(d)> tmp{&output, d};
|
||||
|
||||
redi::ipstream hipcc{
|
||||
make_hipcc_call(sources, targets, flags, *tmp),
|
||||
redi::pstream::pstderr};
|
||||
|
||||
if (!hipcc.is_open()) {
|
||||
throw std::runtime_error{"Compiler invocation failed."};
|
||||
}
|
||||
|
||||
std::string log;
|
||||
while (std::getline(hipcc, log)) std::cout << log << '\n';
|
||||
|
||||
hipcc.close();
|
||||
|
||||
if (hipcc.rdbuf()->exited() &&
|
||||
hipcc.rdbuf()->status() != EXIT_SUCCESS) {
|
||||
throw std::runtime_error{"Compilation failed."};
|
||||
}
|
||||
|
||||
copy_kernel_section_to_fat_binary(*tmp, output);
|
||||
}
|
||||
|
||||
inline
|
||||
bool file_exists(const std::string& path_to)
|
||||
{
|
||||
return static_cast<bool>(std::ifstream{path_to});
|
||||
}
|
||||
|
||||
inline
|
||||
bool hipcc_and_lpl_colocated()
|
||||
{
|
||||
if (path_to_self().empty()) return false;
|
||||
|
||||
return file_exists(path_to_hipcc());
|
||||
}
|
||||
|
||||
inline
|
||||
std::vector<std::string> tokenize_targets(const std::string& x)
|
||||
{ // TODO: move to regular expressions once we clarify the need to support
|
||||
// ancient standard library implementations.
|
||||
if (x.empty()) return {};
|
||||
|
||||
static constexpr const char valid_characters[] = "gfx0123456789,";
|
||||
|
||||
if (x.find_first_not_of(valid_characters) != std::string::npos) {
|
||||
throw std::runtime_error{"Invalid target string: " + x};
|
||||
}
|
||||
|
||||
std::vector<std::string> r;
|
||||
|
||||
auto it = x.cbegin();
|
||||
do {
|
||||
auto it1 = std::find(it, x.cend(), ',');
|
||||
r.emplace_back(it, it1);
|
||||
|
||||
if (it1 == x.cend()) break;
|
||||
|
||||
it = ++it1;
|
||||
} while (true);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
void validate_targets(const std::vector<std::string>& x)
|
||||
{
|
||||
assert(!x.empty());
|
||||
|
||||
for (auto&& t : x) {
|
||||
static const std::string digits{"0123456789"};
|
||||
static const std::string pre{"gfx"};
|
||||
|
||||
if (t.find(pre) != 0 ||
|
||||
t.find_first_not_of(digits, pre.size()) != std::string::npos) {
|
||||
throw std::runtime_error{"Invalid target: " + t};
|
||||
}
|
||||
|
||||
if (amdgpu_targets().find(t) == amdgpu_targets().cend()) {
|
||||
std::cerr << "Warning: target " << t
|
||||
<< " has not been validated yet; it may be invalid."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
clara::Parser cmdline_parser(
|
||||
bool& help,
|
||||
std::vector<std::string>& sources,
|
||||
std::string& targets,
|
||||
std::string& flags,
|
||||
std::string& output)
|
||||
{
|
||||
return
|
||||
clara::Opt{flags, "\"-v -DMACRO etc.\""}
|
||||
["-f"]["--flags"](
|
||||
"flags for compilation; must be valid for hipcc.") |
|
||||
clara::Help{help} |
|
||||
clara::Opt{output, "filename"}
|
||||
["-o"]["--output"](
|
||||
"name of fat-binary output file; the binary format of the "
|
||||
"file is documented at: https://reviews.llvm.org/D13909.") |
|
||||
clara::Arg{sources, "a.cpp b.cpp etc."}(
|
||||
"inputs for compilation; must contain valid C++ code.") |
|
||||
clara::Opt{targets, "gfx803,gfx900 etc."}
|
||||
["-t"]["--targets"](
|
||||
"targets for AMDGPU lowering; must be one of the processors"
|
||||
" with ROCm support from "
|
||||
"https://www.llvm.org/docs/AMDGPUUsage.html#processors.");
|
||||
}
|
||||
}
|
||||
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Ielādēt izmaiņas
Atsaukties uz šo jaunā problēmā
Block a user