/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-nvlink-wrapper/ClangNvlinkWrapper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- clang-nvlink-wrapper/ClangNvlinkWrapper.cpp - wrapper over nvlink-===// |
2 | | // |
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 |
6 | | // |
7 | | //===---------------------------------------------------------------------===// |
8 | | /// |
9 | | /// \file |
10 | | /// This tool works as a wrapper over nvlink program. It transparently passes |
11 | | /// every input option and objects to nvlink except archive files. It reads |
12 | | /// each input archive file to extract archived cubin files as temporary files. |
13 | | /// These temp (*.cubin) files are passed to nvlink, because nvlink does not |
14 | | /// support linking of archive files implicitly. |
15 | | /// |
16 | | /// During linking of heterogeneous device archive libraries, the |
17 | | /// clang-offload-bundler creates a device specific archive of cubin files. |
18 | | /// Such an archive is then passed to this tool to extract cubin files before |
19 | | /// passing to nvlink. |
20 | | /// |
21 | | /// Example: |
22 | | /// clang-nvlink-wrapper -o a.out-openmp-nvptx64 /tmp/libTest-nvptx-sm_50.a |
23 | | /// |
24 | | /// 1. Extract (libTest-nvptx-sm_50.a) => /tmp/a.cubin /tmp/b.cubin |
25 | | /// 2. nvlink -o a.out-openmp-nvptx64 /tmp/a.cubin /tmp/b.cubin |
26 | | //===---------------------------------------------------------------------===// |
27 | | |
28 | | #include "clang/Basic/Version.h" |
29 | | #include "llvm/Object/Archive.h" |
30 | | #include "llvm/Support/CommandLine.h" |
31 | | #include "llvm/Support/Errc.h" |
32 | | #include "llvm/Support/FileSystem.h" |
33 | | #include "llvm/Support/MemoryBuffer.h" |
34 | | #include "llvm/Support/Path.h" |
35 | | #include "llvm/Support/Program.h" |
36 | | #include "llvm/Support/Signals.h" |
37 | | #include "llvm/Support/StringSaver.h" |
38 | | #include "llvm/Support/WithColor.h" |
39 | | #include "llvm/Support/raw_ostream.h" |
40 | | |
41 | | using namespace llvm; |
42 | | |
43 | | static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); |
44 | | |
45 | | // Mark all our options with this category, everything else (except for -help) |
46 | | // will be hidden. |
47 | | static cl::OptionCategory |
48 | | ClangNvlinkWrapperCategory("clang-nvlink-wrapper options"); |
49 | | |
50 | | static cl::opt<std::string> NvlinkUserPath("nvlink-path", |
51 | | cl::desc("Path of nvlink binary"), |
52 | | cl::cat(ClangNvlinkWrapperCategory)); |
53 | | |
54 | | // Do not parse nvlink options |
55 | | static cl::list<std::string> |
56 | | NVArgs(cl::Sink, cl::desc("<options to be passed to nvlink>...")); |
57 | | |
58 | 0 | static bool isEmptyFile(StringRef Filename) { |
59 | 0 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = |
60 | 0 | MemoryBuffer::getFileOrSTDIN(Filename, false, false); |
61 | 0 | if (std::error_code EC = BufOrErr.getError()) |
62 | 0 | return false; |
63 | 0 | return (*BufOrErr)->getBuffer().empty(); |
64 | 0 | } |
65 | | |
66 | | static Error runNVLink(std::string NVLinkPath, |
67 | 0 | SmallVectorImpl<std::string> &Args) { |
68 | 0 | std::vector<StringRef> NVLArgs; |
69 | 0 | NVLArgs.push_back(NVLinkPath); |
70 | 0 | StringRef Output = *(llvm::find(Args, "-o") + 1); |
71 | 0 | for (auto &Arg : Args) { |
72 | 0 | if (!(sys::fs::exists(Arg) && Arg != Output && isEmptyFile(Arg))) |
73 | 0 | NVLArgs.push_back(Arg); |
74 | 0 | } |
75 | |
|
76 | 0 | if (sys::ExecuteAndWait(NVLinkPath, NVLArgs)) |
77 | 0 | return createStringError(inconvertibleErrorCode(), "'nvlink' failed"); |
78 | 0 | return Error::success(); |
79 | 0 | } |
80 | | |
81 | | static Error extractArchiveFiles(StringRef Filename, |
82 | | SmallVectorImpl<std::string> &Args, |
83 | 0 | SmallVectorImpl<std::string> &TmpFiles) { |
84 | 0 | std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; |
85 | |
|
86 | 0 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = |
87 | 0 | MemoryBuffer::getFileOrSTDIN(Filename, false, false); |
88 | 0 | if (std::error_code EC = BufOrErr.getError()) |
89 | 0 | return createFileError(Filename, EC); |
90 | | |
91 | 0 | ArchiveBuffers.push_back(std::move(*BufOrErr)); |
92 | 0 | Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = |
93 | 0 | object::Archive::create(ArchiveBuffers.back()->getMemBufferRef()); |
94 | 0 | if (!LibOrErr) |
95 | 0 | return LibOrErr.takeError(); |
96 | | |
97 | 0 | auto Archive = std::move(*LibOrErr); |
98 | |
|
99 | 0 | Error Err = Error::success(); |
100 | 0 | auto ChildEnd = Archive->child_end(); |
101 | 0 | for (auto ChildIter = Archive->child_begin(Err); ChildIter != ChildEnd; |
102 | 0 | ++ChildIter) { |
103 | 0 | if (Err) |
104 | 0 | return Err; |
105 | 0 | auto ChildNameOrErr = (*ChildIter).getName(); |
106 | 0 | if (!ChildNameOrErr) |
107 | 0 | return ChildNameOrErr.takeError(); |
108 | | |
109 | 0 | StringRef ChildName = sys::path::filename(ChildNameOrErr.get()); |
110 | |
|
111 | 0 | auto ChildBufferRefOrErr = (*ChildIter).getMemoryBufferRef(); |
112 | 0 | if (!ChildBufferRefOrErr) |
113 | 0 | return ChildBufferRefOrErr.takeError(); |
114 | | |
115 | 0 | auto ChildBuffer = |
116 | 0 | MemoryBuffer::getMemBuffer(ChildBufferRefOrErr.get(), false); |
117 | 0 | auto ChildNameSplit = ChildName.split('.'); |
118 | |
|
119 | 0 | SmallString<16> Path; |
120 | 0 | int FileDesc; |
121 | 0 | if (std::error_code EC = sys::fs::createTemporaryFile( |
122 | 0 | (ChildNameSplit.first), (ChildNameSplit.second), FileDesc, Path)) |
123 | 0 | return createFileError(ChildName, EC); |
124 | | |
125 | 0 | std::string TmpFileName(Path.str()); |
126 | 0 | Args.push_back(TmpFileName); |
127 | 0 | TmpFiles.push_back(TmpFileName); |
128 | 0 | std::error_code EC; |
129 | 0 | raw_fd_ostream OS(Path.c_str(), EC, sys::fs::OF_None); |
130 | 0 | if (EC) |
131 | 0 | return createFileError(TmpFileName, errc::io_error); |
132 | 0 | OS << ChildBuffer->getBuffer(); |
133 | 0 | OS.close(); |
134 | 0 | } |
135 | 0 | return Err; |
136 | 0 | } |
137 | | |
138 | 0 | static Error cleanupTmpFiles(SmallVectorImpl<std::string> &TmpFiles) { |
139 | 0 | for (auto &TmpFile : TmpFiles) { |
140 | 0 | if (std::error_code EC = sys::fs::remove(TmpFile)) |
141 | 0 | return createFileError(TmpFile, errc::no_such_file_or_directory); |
142 | 0 | } |
143 | 0 | return Error::success(); |
144 | 0 | } |
145 | | |
146 | 0 | static void PrintVersion(raw_ostream &OS) { |
147 | 0 | OS << clang::getClangToolFullVersion("clang-nvlink-wrapper") << '\n'; |
148 | 0 | } |
149 | | |
150 | | int main(int argc, const char **argv) { |
151 | | sys::PrintStackTraceOnErrorSignal(argv[0]); |
152 | | cl::SetVersionPrinter(PrintVersion); |
153 | | cl::HideUnrelatedOptions(ClangNvlinkWrapperCategory); |
154 | | cl::ParseCommandLineOptions( |
155 | | argc, argv, |
156 | | "A wrapper tool over nvlink program. It transparently passes every \n" |
157 | | "input option and objects to nvlink except archive files and path of \n" |
158 | | "nvlink binary. It reads each input archive file to extract archived \n" |
159 | | "cubin files as temporary files.\n"); |
160 | | |
161 | | if (Help) { |
162 | | cl::PrintHelpMessage(); |
163 | | return 0; |
164 | | } |
165 | | |
166 | 0 | auto reportError = [argv](Error E) { |
167 | 0 | logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); |
168 | 0 | exit(1); |
169 | 0 | }; |
170 | | |
171 | | std::string NvlinkPath; |
172 | | SmallVector<const char *, 0> Argv(argv, argv + argc); |
173 | | SmallVector<std::string, 0> ArgvSubst; |
174 | | SmallVector<std::string, 0> TmpFiles; |
175 | | BumpPtrAllocator Alloc; |
176 | | StringSaver Saver(Alloc); |
177 | | cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv); |
178 | | |
179 | | for (const std::string &Arg : NVArgs) { |
180 | | if (sys::path::extension(Arg) == ".a") { |
181 | | if (Error Err = extractArchiveFiles(Arg, ArgvSubst, TmpFiles)) |
182 | | reportError(std::move(Err)); |
183 | | } else { |
184 | | ArgvSubst.push_back(Arg); |
185 | | } |
186 | | } |
187 | | |
188 | | NvlinkPath = NvlinkUserPath; |
189 | | |
190 | | // If user hasn't specified nvlink binary then search it in PATH |
191 | | if (NvlinkPath.empty()) { |
192 | | ErrorOr<std::string> NvlinkPathErr = sys::findProgramByName("nvlink"); |
193 | | if (!NvlinkPathErr) { |
194 | | reportError(createStringError(NvlinkPathErr.getError(), |
195 | | "unable to find 'nvlink' in path")); |
196 | | } |
197 | | NvlinkPath = NvlinkPathErr.get(); |
198 | | } |
199 | | |
200 | | if (Error Err = runNVLink(NvlinkPath, ArgvSubst)) |
201 | | reportError(std::move(Err)); |
202 | | if (Error Err = cleanupTmpFiles(TmpFiles)) |
203 | | reportError(std::move(Err)); |
204 | | |
205 | | return 0; |
206 | | } |