//===-- Main entry into the loader interface ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file opens a device image passed on the command line and passes it to // one of the loader implementations for launch. // //===----------------------------------------------------------------------===// #include "Loader.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" #include "llvm/Support/WithColor.h" #include #include #include #include #include #include using namespace llvm; static cl::OptionCategory loader_category("loader options"); static cl::opt help("h", cl::desc("Alias for -help"), cl::Hidden, cl::cat(loader_category)); static cl::opt threads_x("threads-x", cl::desc("Number of threads in the 'x' dimension"), cl::init(1), cl::cat(loader_category)); static cl::opt threads_y("threads-y", cl::desc("Number of threads in the 'y' dimension"), cl::init(1), cl::cat(loader_category)); static cl::opt threads_z("threads-z", cl::desc("Number of threads in the 'z' dimension"), cl::init(1), cl::cat(loader_category)); static cl::alias threads("threads", cl::aliasopt(threads_x), cl::desc("Alias for --threads-x"), cl::cat(loader_category)); static cl::opt blocks_x("blocks-x", cl::desc("Number of blocks in the 'x' dimension"), cl::init(1), cl::cat(loader_category)); static cl::opt blocks_y("blocks-y", cl::desc("Number of blocks in the 'y' dimension"), cl::init(1), cl::cat(loader_category)); static cl::opt blocks_z("blocks-z", cl::desc("Number of blocks in the 'z' dimension"), cl::init(1), cl::cat(loader_category)); static cl::alias blocks("blocks", cl::aliasopt(blocks_x), cl::desc("Alias for --blocks-x"), cl::cat(loader_category)); static cl::opt print_resource_usage("print-resource-usage", cl::desc("Output resource usage of launched kernels"), cl::init(false), cl::cat(loader_category)); static cl::opt no_parallelism("no-parallelism", cl::desc("Allows only a single process to use the GPU at a " "time. Useful to suppress out-of-resource errors"), cl::init(false), cl::cat(loader_category)); static cl::opt file(cl::Positional, cl::Required, cl::desc(""), cl::cat(loader_category)); static cl::list args(cl::ConsumeAfter, cl::desc("..."), cl::cat(loader_category)); [[noreturn]] void report_error(Error E) { outs().flush(); logAllUnhandledErrors(std::move(E), WithColor::error(errs(), "loader")); exit(EXIT_FAILURE); } std::string get_main_executable(const char *name) { void *ptr = (void *)(intptr_t)&get_main_executable; auto cow_path = sys::fs::getMainExecutable(name, ptr); return sys::path::parent_path(cow_path).str(); } int main(int argc, const char **argv, const char **envp) { sys::PrintStackTraceOnErrorSignal(argv[0]); cl::HideUnrelatedOptions(loader_category); cl::ParseCommandLineOptions( argc, argv, "A utility used to launch unit tests built for a GPU target. This is\n" "intended to provide an intrface simular to cross-compiling emulators\n"); if (help) { cl::PrintHelpMessage(); return EXIT_SUCCESS; } ErrorOr> image_or_err = MemoryBuffer::getFileOrSTDIN(file); if (std::error_code ec = image_or_err.getError()) report_error(errorCodeToError(ec)); MemoryBufferRef image = **image_or_err; SmallVector new_argv = {file.c_str()}; llvm::transform(args, std::back_inserter(new_argv), [](const std::string &arg) { return arg.c_str(); }); // Claim a file lock on the executable so only a single process can enter this // region if requested. This prevents the loader from spurious failures. int fd = -1; if (no_parallelism) { fd = open(get_main_executable(argv[0]).c_str(), O_RDONLY); if (flock(fd, LOCK_EX) == -1) report_error(createStringError("Failed to lock '%s': %s", argv[0], strerror(errno))); } // Drop the loader from the program arguments. LaunchParameters params{threads_x, threads_y, threads_z, blocks_x, blocks_y, blocks_z}; int ret = load(new_argv.size(), new_argv.data(), envp, const_cast(image.getBufferStart()), image.getBufferSize(), params, print_resource_usage); if (no_parallelism) { if (flock(fd, LOCK_UN) == -1) report_error(createStringError("Failed to unlock '%s': %s", argv[0], strerror(errno))); } return ret; }