1 //===-- Main entry into the loader interface ------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file opens a device image passed on the command line and passes it to
10 // one of the loader implementations for launch.
12 //===----------------------------------------------------------------------===//
16 #include "llvm/BinaryFormat/Magic.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/Signals.h"
23 #include "llvm/Support/WithColor.h"
34 static cl::OptionCategory
loader_category("loader options");
36 static cl::opt
<bool> help("h", cl::desc("Alias for -help"), cl::Hidden
,
37 cl::cat(loader_category
));
39 static cl::opt
<unsigned>
40 threads_x("threads-x", cl::desc("Number of threads in the 'x' dimension"),
41 cl::init(1), cl::cat(loader_category
));
42 static cl::opt
<unsigned>
43 threads_y("threads-y", cl::desc("Number of threads in the 'y' dimension"),
44 cl::init(1), cl::cat(loader_category
));
45 static cl::opt
<unsigned>
46 threads_z("threads-z", cl::desc("Number of threads in the 'z' dimension"),
47 cl::init(1), cl::cat(loader_category
));
48 static cl::alias
threads("threads", cl::aliasopt(threads_x
),
49 cl::desc("Alias for --threads-x"),
50 cl::cat(loader_category
));
52 static cl::opt
<unsigned>
53 blocks_x("blocks-x", cl::desc("Number of blocks in the 'x' dimension"),
54 cl::init(1), cl::cat(loader_category
));
55 static cl::opt
<unsigned>
56 blocks_y("blocks-y", cl::desc("Number of blocks in the 'y' dimension"),
57 cl::init(1), cl::cat(loader_category
));
58 static cl::opt
<unsigned>
59 blocks_z("blocks-z", cl::desc("Number of blocks in the 'z' dimension"),
60 cl::init(1), cl::cat(loader_category
));
61 static cl::alias
blocks("blocks", cl::aliasopt(blocks_x
),
62 cl::desc("Alias for --blocks-x"),
63 cl::cat(loader_category
));
66 print_resource_usage("print-resource-usage",
67 cl::desc("Output resource usage of launched kernels"),
68 cl::init(false), cl::cat(loader_category
));
71 no_parallelism("no-parallelism",
72 cl::desc("Allows only a single process to use the GPU at a "
73 "time. Useful to suppress out-of-resource errors"),
74 cl::init(false), cl::cat(loader_category
));
76 static cl::opt
<std::string
> file(cl::Positional
, cl::Required
,
77 cl::desc("<gpu executable>"),
78 cl::cat(loader_category
));
79 static cl::list
<std::string
> args(cl::ConsumeAfter
,
80 cl::desc("<program arguments>..."),
81 cl::cat(loader_category
));
83 [[noreturn
]] void report_error(Error E
) {
85 logAllUnhandledErrors(std::move(E
), WithColor::error(errs(), "loader"));
89 std::string
get_main_executable(const char *name
) {
90 void *ptr
= (void *)(intptr_t)&get_main_executable
;
91 auto cow_path
= sys::fs::getMainExecutable(name
, ptr
);
92 return sys::path::parent_path(cow_path
).str();
95 int main(int argc
, const char **argv
, const char **envp
) {
96 sys::PrintStackTraceOnErrorSignal(argv
[0]);
97 cl::HideUnrelatedOptions(loader_category
);
98 cl::ParseCommandLineOptions(
100 "A utility used to launch unit tests built for a GPU target. This is\n"
101 "intended to provide an intrface simular to cross-compiling emulators\n");
104 cl::PrintHelpMessage();
108 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> image_or_err
=
109 MemoryBuffer::getFileOrSTDIN(file
);
110 if (std::error_code ec
= image_or_err
.getError())
111 report_error(errorCodeToError(ec
));
112 MemoryBufferRef image
= **image_or_err
;
114 SmallVector
<const char *> new_argv
= {file
.c_str()};
115 llvm::transform(args
, std::back_inserter(new_argv
),
116 [](const std::string
&arg
) { return arg
.c_str(); });
118 // Claim a file lock on the executable so only a single process can enter this
119 // region if requested. This prevents the loader from spurious failures.
121 if (no_parallelism
) {
122 fd
= open(get_main_executable(argv
[0]).c_str(), O_RDONLY
);
123 if (flock(fd
, LOCK_EX
) == -1)
124 report_error(createStringError("Failed to lock '%s': %s", argv
[0],
128 // Drop the loader from the program arguments.
129 LaunchParameters params
{threads_x
, threads_y
, threads_z
,
130 blocks_x
, blocks_y
, blocks_z
};
131 int ret
= load(new_argv
.size(), new_argv
.data(), envp
,
132 const_cast<char *>(image
.getBufferStart()),
133 image
.getBufferSize(), params
, print_resource_usage
);
135 if (no_parallelism
) {
136 if (flock(fd
, LOCK_UN
) == -1)
137 report_error(createStringError("Failed to unlock '%s': %s", argv
[0],