xref: /llvm-project/libc/utils/gpu/loader/Main.cpp (revision d1b294029004e7c3220ed46e2b06addd32490745)
167d78e3cSJoseph Huber //===-- Main entry into the loader interface ------------------------------===//
267d78e3cSJoseph Huber //
367d78e3cSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
467d78e3cSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
567d78e3cSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667d78e3cSJoseph Huber //
767d78e3cSJoseph Huber //===----------------------------------------------------------------------===//
867d78e3cSJoseph Huber //
967d78e3cSJoseph Huber // This file opens a device image passed on the command line and passes it to
1067d78e3cSJoseph Huber // one of the loader implementations for launch.
1167d78e3cSJoseph Huber //
1267d78e3cSJoseph Huber //===----------------------------------------------------------------------===//
1367d78e3cSJoseph Huber 
1467d78e3cSJoseph Huber #include "Loader.h"
1567d78e3cSJoseph Huber 
165e326983SJoseph Huber #include "llvm/BinaryFormat/Magic.h"
175e326983SJoseph Huber #include "llvm/Support/CommandLine.h"
185e326983SJoseph Huber #include "llvm/Support/Error.h"
19*d1b29402SJoseph Huber #include "llvm/Support/FileSystem.h"
205e326983SJoseph Huber #include "llvm/Support/MemoryBuffer.h"
21*d1b29402SJoseph Huber #include "llvm/Support/Path.h"
225e326983SJoseph Huber #include "llvm/Support/Signals.h"
235e326983SJoseph Huber #include "llvm/Support/WithColor.h"
245e326983SJoseph Huber 
25*d1b29402SJoseph Huber #include <cerrno>
2667d78e3cSJoseph Huber #include <cstdio>
2767d78e3cSJoseph Huber #include <cstdlib>
28*d1b29402SJoseph Huber #include <cstring>
29bc11bb3eSJoseph Huber #include <string>
30*d1b29402SJoseph Huber #include <sys/file.h>
3167d78e3cSJoseph Huber 
325e326983SJoseph Huber using namespace llvm;
335e326983SJoseph Huber 
345e326983SJoseph Huber static cl::OptionCategory loader_category("loader options");
355e326983SJoseph Huber 
365e326983SJoseph Huber static cl::opt<bool> help("h", cl::desc("Alias for -help"), cl::Hidden,
375e326983SJoseph Huber                           cl::cat(loader_category));
385e326983SJoseph Huber 
395e326983SJoseph Huber static cl::opt<unsigned>
405e326983SJoseph Huber     threads_x("threads-x", cl::desc("Number of threads in the 'x' dimension"),
415e326983SJoseph Huber               cl::init(1), cl::cat(loader_category));
425e326983SJoseph Huber static cl::opt<unsigned>
435e326983SJoseph Huber     threads_y("threads-y", cl::desc("Number of threads in the 'y' dimension"),
445e326983SJoseph Huber               cl::init(1), cl::cat(loader_category));
455e326983SJoseph Huber static cl::opt<unsigned>
465e326983SJoseph Huber     threads_z("threads-z", cl::desc("Number of threads in the 'z' dimension"),
475e326983SJoseph Huber               cl::init(1), cl::cat(loader_category));
485e326983SJoseph Huber static cl::alias threads("threads", cl::aliasopt(threads_x),
495e326983SJoseph Huber                          cl::desc("Alias for --threads-x"),
505e326983SJoseph Huber                          cl::cat(loader_category));
515e326983SJoseph Huber 
525e326983SJoseph Huber static cl::opt<unsigned>
535e326983SJoseph Huber     blocks_x("blocks-x", cl::desc("Number of blocks in the 'x' dimension"),
545e326983SJoseph Huber              cl::init(1), cl::cat(loader_category));
555e326983SJoseph Huber static cl::opt<unsigned>
565e326983SJoseph Huber     blocks_y("blocks-y", cl::desc("Number of blocks in the 'y' dimension"),
575e326983SJoseph Huber              cl::init(1), cl::cat(loader_category));
585e326983SJoseph Huber static cl::opt<unsigned>
595e326983SJoseph Huber     blocks_z("blocks-z", cl::desc("Number of blocks in the 'z' dimension"),
605e326983SJoseph Huber              cl::init(1), cl::cat(loader_category));
615e326983SJoseph Huber static cl::alias blocks("blocks", cl::aliasopt(blocks_x),
625e326983SJoseph Huber                         cl::desc("Alias for --blocks-x"),
635e326983SJoseph Huber                         cl::cat(loader_category));
645e326983SJoseph Huber 
655e326983SJoseph Huber static cl::opt<bool>
665e326983SJoseph Huber     print_resource_usage("print-resource-usage",
675e326983SJoseph Huber                          cl::desc("Output resource usage of launched kernels"),
685e326983SJoseph Huber                          cl::init(false), cl::cat(loader_category));
695e326983SJoseph Huber 
70*d1b29402SJoseph Huber static cl::opt<bool>
71*d1b29402SJoseph Huber     no_parallelism("no-parallelism",
72*d1b29402SJoseph Huber                    cl::desc("Allows only a single process to use the GPU at a "
73*d1b29402SJoseph Huber                             "time. Useful to suppress out-of-resource errors"),
74*d1b29402SJoseph Huber                    cl::init(false), cl::cat(loader_category));
75*d1b29402SJoseph Huber 
765e326983SJoseph Huber static cl::opt<std::string> file(cl::Positional, cl::Required,
775e326983SJoseph Huber                                  cl::desc("<gpu executable>"),
785e326983SJoseph Huber                                  cl::cat(loader_category));
795e326983SJoseph Huber static cl::list<std::string> args(cl::ConsumeAfter,
805e326983SJoseph Huber                                   cl::desc("<program arguments>..."),
815e326983SJoseph Huber                                   cl::cat(loader_category));
825e326983SJoseph Huber 
835e326983SJoseph Huber [[noreturn]] void report_error(Error E) {
845e326983SJoseph Huber   outs().flush();
855e326983SJoseph Huber   logAllUnhandledErrors(std::move(E), WithColor::error(errs(), "loader"));
865e326983SJoseph Huber   exit(EXIT_FAILURE);
875e326983SJoseph Huber }
885e326983SJoseph Huber 
89*d1b29402SJoseph Huber std::string get_main_executable(const char *name) {
90*d1b29402SJoseph Huber   void *ptr = (void *)(intptr_t)&get_main_executable;
91*d1b29402SJoseph Huber   auto cow_path = sys::fs::getMainExecutable(name, ptr);
92*d1b29402SJoseph Huber   return sys::path::parent_path(cow_path).str();
93*d1b29402SJoseph Huber }
94*d1b29402SJoseph Huber 
955e326983SJoseph Huber int main(int argc, const char **argv, const char **envp) {
965e326983SJoseph Huber   sys::PrintStackTraceOnErrorSignal(argv[0]);
975e326983SJoseph Huber   cl::HideUnrelatedOptions(loader_category);
985e326983SJoseph Huber   cl::ParseCommandLineOptions(
995e326983SJoseph Huber       argc, argv,
1005e326983SJoseph Huber       "A utility used to launch unit tests built for a GPU target. This is\n"
1015e326983SJoseph Huber       "intended to provide an intrface simular to cross-compiling emulators\n");
1025e326983SJoseph Huber 
1035e326983SJoseph Huber   if (help) {
1045e326983SJoseph Huber     cl::PrintHelpMessage();
10567d78e3cSJoseph Huber     return EXIT_SUCCESS;
10667d78e3cSJoseph Huber   }
10767d78e3cSJoseph Huber 
1085e326983SJoseph Huber   ErrorOr<std::unique_ptr<MemoryBuffer>> image_or_err =
1095e326983SJoseph Huber       MemoryBuffer::getFileOrSTDIN(file);
1105e326983SJoseph Huber   if (std::error_code ec = image_or_err.getError())
1115e326983SJoseph Huber     report_error(errorCodeToError(ec));
1125e326983SJoseph Huber   MemoryBufferRef image = **image_or_err;
11367d78e3cSJoseph Huber 
1145e326983SJoseph Huber   SmallVector<const char *> new_argv = {file.c_str()};
1155e326983SJoseph Huber   llvm::transform(args, std::back_inserter(new_argv),
1165e326983SJoseph Huber                   [](const std::string &arg) { return arg.c_str(); });
11767d78e3cSJoseph Huber 
118*d1b29402SJoseph Huber   // Claim a file lock on the executable so only a single process can enter this
119*d1b29402SJoseph Huber   // region if requested. This prevents the loader from spurious failures.
120*d1b29402SJoseph Huber   int fd = -1;
121*d1b29402SJoseph Huber   if (no_parallelism) {
122*d1b29402SJoseph Huber     fd = open(get_main_executable(argv[0]).c_str(), O_RDONLY);
123*d1b29402SJoseph Huber     if (flock(fd, LOCK_EX) == -1)
124*d1b29402SJoseph Huber       report_error(createStringError("Failed to lock '%s': %s", argv[0],
125*d1b29402SJoseph Huber                                      strerror(errno)));
126*d1b29402SJoseph Huber   }
127*d1b29402SJoseph Huber 
12867d78e3cSJoseph Huber   // Drop the loader from the program arguments.
1295e326983SJoseph Huber   LaunchParameters params{threads_x, threads_y, threads_z,
1305e326983SJoseph Huber                           blocks_x,  blocks_y,  blocks_z};
1315e326983SJoseph Huber   int ret = load(new_argv.size(), new_argv.data(), envp,
1325e326983SJoseph Huber                  const_cast<char *>(image.getBufferStart()),
1335e326983SJoseph Huber                  image.getBufferSize(), params, print_resource_usage);
13467d78e3cSJoseph Huber 
135*d1b29402SJoseph Huber   if (no_parallelism) {
136*d1b29402SJoseph Huber     if (flock(fd, LOCK_UN) == -1)
137*d1b29402SJoseph Huber       report_error(createStringError("Failed to unlock '%s': %s", argv[0],
138*d1b29402SJoseph Huber                                      strerror(errno)));
139*d1b29402SJoseph Huber   }
140*d1b29402SJoseph Huber 
14167d78e3cSJoseph Huber   return ret;
14267d78e3cSJoseph Huber }
143