/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Driver/ToolChains/HIPSPV.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- HIPSPV.cpp - HIPSPV ToolChain Implementation -----------*- C++ -*-===// |
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 | | #include "HIPSPV.h" |
10 | | #include "CommonArgs.h" |
11 | | #include "HIPUtility.h" |
12 | | #include "clang/Driver/Compilation.h" |
13 | | #include "clang/Driver/Driver.h" |
14 | | #include "clang/Driver/DriverDiagnostic.h" |
15 | | #include "clang/Driver/InputInfo.h" |
16 | | #include "clang/Driver/Options.h" |
17 | | #include "llvm/Support/FileSystem.h" |
18 | | #include "llvm/Support/Path.h" |
19 | | |
20 | | using namespace clang::driver; |
21 | | using namespace clang::driver::toolchains; |
22 | | using namespace clang::driver::tools; |
23 | | using namespace clang; |
24 | | using namespace llvm::opt; |
25 | | |
26 | | // Convenience function for creating temporary file for both modes of |
27 | | // isSaveTempsEnabled(). |
28 | | static const char *getTempFile(Compilation &C, StringRef Prefix, |
29 | 16 | StringRef Extension) { |
30 | 16 | if (C.getDriver().isSaveTempsEnabled()) { |
31 | 0 | return C.getArgs().MakeArgString(Prefix + "." + Extension); |
32 | 0 | } |
33 | 16 | auto TmpFile = C.getDriver().GetTemporaryPath(Prefix, Extension); |
34 | 16 | return C.addTempFile(C.getArgs().MakeArgString(TmpFile)); |
35 | 16 | } |
36 | | |
37 | | // Locates HIP pass plugin. |
38 | | static std::string findPassPlugin(const Driver &D, |
39 | 9 | const llvm::opt::ArgList &Args) { |
40 | 9 | StringRef Path = Args.getLastArgValue(options::OPT_hipspv_pass_plugin_EQ); |
41 | 9 | if (!Path.empty()) { |
42 | 2 | if (llvm::sys::fs::exists(Path)) |
43 | 1 | return Path.str(); |
44 | 1 | D.Diag(diag::err_drv_no_such_file) << Path; |
45 | 1 | } |
46 | | |
47 | 8 | StringRef hipPath = Args.getLastArgValue(options::OPT_hip_path_EQ); |
48 | 8 | if (!hipPath.empty()) { |
49 | 6 | SmallString<128> PluginPath(hipPath); |
50 | 6 | llvm::sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so"); |
51 | 6 | if (llvm::sys::fs::exists(PluginPath)) |
52 | 6 | return PluginPath.str().str(); |
53 | 0 | PluginPath.assign(hipPath); |
54 | 0 | llvm::sys::path::append(PluginPath, "lib", "llvm", |
55 | 0 | "libLLVMHipSpvPasses.so"); |
56 | 0 | if (llvm::sys::fs::exists(PluginPath)) |
57 | 0 | return PluginPath.str().str(); |
58 | 0 | } |
59 | | |
60 | 2 | return std::string(); |
61 | 8 | } |
62 | | |
63 | | void HIPSPV::Linker::constructLinkAndEmitSpirvCommand( |
64 | | Compilation &C, const JobAction &JA, const InputInfoList &Inputs, |
65 | 9 | const InputInfo &Output, const llvm::opt::ArgList &Args) const { |
66 | | |
67 | 9 | assert(!Inputs.empty() && "Must have at least one input."); |
68 | 9 | std::string Name = std::string(llvm::sys::path::stem(Output.getFilename())); |
69 | 9 | const char *TempFile = getTempFile(C, Name + "-link", "bc"); |
70 | | |
71 | | // Link LLVM bitcode. |
72 | 9 | ArgStringList LinkArgs{}; |
73 | 9 | for (auto Input : Inputs) |
74 | 10 | LinkArgs.push_back(Input.getFilename()); |
75 | 9 | LinkArgs.append({"-o", TempFile}); |
76 | 9 | const char *LlvmLink = |
77 | 9 | Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); |
78 | 9 | C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), |
79 | 9 | LlvmLink, LinkArgs, Inputs, Output)); |
80 | | |
81 | | // Post-link HIP lowering. |
82 | | |
83 | | // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate |
84 | | // to SPIR-V (E.g. dynamic shared memory). |
85 | 9 | auto PassPluginPath = findPassPlugin(C.getDriver(), Args); |
86 | 9 | if (!PassPluginPath.empty()) { |
87 | 7 | const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath); |
88 | 7 | const char *OptOutput = getTempFile(C, Name + "-lower", "bc"); |
89 | 7 | ArgStringList OptArgs{TempFile, "-load-pass-plugin", |
90 | 7 | PassPathCStr, "-passes=hip-post-link-passes", |
91 | 7 | "-o", OptOutput}; |
92 | 7 | const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt")); |
93 | 7 | C.addCommand(std::make_unique<Command>( |
94 | 7 | JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output)); |
95 | 7 | TempFile = OptOutput; |
96 | 7 | } |
97 | | |
98 | | // Emit SPIR-V binary. |
99 | | |
100 | 9 | llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1", |
101 | 9 | "--spirv-ext=+all"}; |
102 | 9 | InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, ""); |
103 | 9 | SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs); |
104 | 9 | } |
105 | | |
106 | | void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
107 | | const InputInfo &Output, |
108 | | const InputInfoList &Inputs, |
109 | | const ArgList &Args, |
110 | 18 | const char *LinkingOutput) const { |
111 | 18 | if (Inputs.size() > 0 && Inputs[0].getType() == types::TY_Image && |
112 | 18 | JA.getType() == types::TY_Object9 ) |
113 | 1 | return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs, |
114 | 1 | Args, JA, *this); |
115 | | |
116 | 17 | if (JA.getType() == types::TY_HIP_FATBIN) |
117 | 8 | return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs, |
118 | 8 | Args, *this); |
119 | | |
120 | 9 | constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args); |
121 | 9 | } |
122 | | |
123 | | HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, |
124 | | const ToolChain &HostTC, const ArgList &Args) |
125 | 9 | : ToolChain(D, Triple, Args), HostTC(HostTC) { |
126 | | // Lookup binaries into the driver directory, this is used to |
127 | | // discover the clang-offload-bundler executable. |
128 | 9 | getProgramPaths().push_back(getDriver().Dir); |
129 | 9 | } |
130 | | |
131 | | void HIPSPVToolChain::addClangTargetOptions( |
132 | | const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, |
133 | 10 | Action::OffloadKind DeviceOffloadingKind) const { |
134 | 10 | HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); |
135 | | |
136 | 10 | assert(DeviceOffloadingKind == Action::OFK_HIP && |
137 | 10 | "Only HIP offloading kinds are supported for GPUs."); |
138 | | |
139 | 10 | CC1Args.append( |
140 | 10 | {"-fcuda-is-device", "-fcuda-allow-variadic-functions", |
141 | | // A crude workaround for llvm-spirv which does not handle the |
142 | | // autovectorized code well (vector reductions, non-i{8,16,32,64} types). |
143 | | // TODO: Allow autovectorization when SPIR-V backend arrives. |
144 | 10 | "-mllvm", "-vectorize-loops=false", "-mllvm", "-vectorize-slp=false"}); |
145 | | |
146 | | // Default to "hidden" visibility, as object level linking will not be |
147 | | // supported for the foreseeable future. |
148 | 10 | if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, |
149 | 10 | options::OPT_fvisibility_ms_compat)) |
150 | 10 | CC1Args.append( |
151 | 10 | {"-fvisibility=hidden", "-fapply-global-visibility-to-externs"}); |
152 | | |
153 | 10 | llvm::for_each(getDeviceLibs(DriverArgs), |
154 | 10 | [&](const BitCodeLibraryInfo &BCFile) { |
155 | 8 | CC1Args.append({"-mlink-builtin-bitcode", |
156 | 8 | DriverArgs.MakeArgString(BCFile.Path)}); |
157 | 8 | }); |
158 | 10 | } |
159 | | |
160 | 9 | Tool *HIPSPVToolChain::buildLinker() const { |
161 | 9 | assert(getTriple().getArch() == llvm::Triple::spirv64); |
162 | 9 | return new tools::HIPSPV::Linker(*this); |
163 | 9 | } |
164 | | |
165 | 10 | void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { |
166 | 10 | HostTC.addClangWarningOptions(CC1Args); |
167 | 10 | } |
168 | | |
169 | | ToolChain::CXXStdlibType |
170 | 0 | HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const { |
171 | 0 | return HostTC.GetCXXStdlibType(Args); |
172 | 0 | } |
173 | | |
174 | | void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, |
175 | 20 | ArgStringList &CC1Args) const { |
176 | 20 | HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); |
177 | 20 | } |
178 | | |
179 | | void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs( |
180 | 20 | const ArgList &Args, ArgStringList &CC1Args) const { |
181 | 20 | HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); |
182 | 20 | } |
183 | | |
184 | | void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args, |
185 | 0 | ArgStringList &CC1Args) const { |
186 | 0 | HostTC.AddIAMCUIncludeArgs(Args, CC1Args); |
187 | 0 | } |
188 | | |
189 | | void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, |
190 | 10 | ArgStringList &CC1Args) const { |
191 | 10 | if (DriverArgs.hasArg(options::OPT_nogpuinc)) |
192 | 4 | return; |
193 | | |
194 | 6 | StringRef hipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); |
195 | 6 | if (hipPath.empty()) { |
196 | 0 | getDriver().Diag(diag::err_drv_hipspv_no_hip_path) << 1 << "'-nogpuinc'"; |
197 | 0 | return; |
198 | 0 | } |
199 | 6 | SmallString<128> P(hipPath); |
200 | 6 | llvm::sys::path::append(P, "include"); |
201 | 6 | CC1Args.append({"-isystem", DriverArgs.MakeArgString(P)}); |
202 | 6 | } |
203 | | |
204 | | llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> |
205 | 10 | HIPSPVToolChain::getDeviceLibs(const llvm::opt::ArgList &DriverArgs) const { |
206 | 10 | llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> BCLibs; |
207 | 10 | if (DriverArgs.hasArg(options::OPT_nogpulib)) |
208 | 3 | return {}; |
209 | | |
210 | 7 | ArgStringList LibraryPaths; |
211 | | // Find device libraries in --hip-device-lib-path and HIP_DEVICE_LIB_PATH. |
212 | 7 | auto HipDeviceLibPathArgs = DriverArgs.getAllArgValues( |
213 | | // --hip-device-lib-path is alias to this option. |
214 | 7 | clang::driver::options::OPT_rocm_device_lib_path_EQ); |
215 | 7 | for (auto Path : HipDeviceLibPathArgs) |
216 | 2 | LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); |
217 | | |
218 | 7 | StringRef HipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); |
219 | 7 | if (!HipPath.empty()) { |
220 | 7 | SmallString<128> Path(HipPath); |
221 | 7 | llvm::sys::path::append(Path, "lib", "hip-device-lib"); |
222 | 7 | LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); |
223 | 7 | } |
224 | | |
225 | 7 | addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH"); |
226 | | |
227 | | // Maintain compatability with --hip-device-lib. |
228 | 7 | auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ); |
229 | 7 | if (!BCLibArgs.empty()) { |
230 | 2 | llvm::for_each(BCLibArgs, [&](StringRef BCName) { |
231 | 2 | StringRef FullName; |
232 | 4 | for (std::string LibraryPath : LibraryPaths) { |
233 | 4 | SmallString<128> Path(LibraryPath); |
234 | 4 | llvm::sys::path::append(Path, BCName); |
235 | 4 | FullName = Path; |
236 | 4 | if (llvm::sys::fs::exists(FullName)) { |
237 | 2 | BCLibs.emplace_back(FullName.str()); |
238 | 2 | return; |
239 | 2 | } |
240 | 4 | } |
241 | 0 | getDriver().Diag(diag::err_drv_no_such_file) << BCName; |
242 | 0 | }); |
243 | 6 | } else { |
244 | | // Search device library named as 'hipspv-<triple>.bc'. |
245 | 6 | auto TT = getTriple().normalize(); |
246 | 6 | std::string BCName = "hipspv-" + TT + ".bc"; |
247 | 6 | for (auto *LibPath : LibraryPaths) { |
248 | 6 | SmallString<128> Path(LibPath); |
249 | 6 | llvm::sys::path::append(Path, BCName); |
250 | 6 | if (llvm::sys::fs::exists(Path)) { |
251 | 6 | BCLibs.emplace_back(Path.str().str()); |
252 | 6 | return BCLibs; |
253 | 6 | } |
254 | 6 | } |
255 | 0 | getDriver().Diag(diag::err_drv_no_hipspv_device_lib) |
256 | 0 | << 1 << ("'" + TT + "' target"); |
257 | 0 | return {}; |
258 | 6 | } |
259 | | |
260 | 1 | return BCLibs; |
261 | 7 | } |
262 | | |
263 | 10 | SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const { |
264 | | // The HIPSPVToolChain only supports sanitizers in the sense that it allows |
265 | | // sanitizer arguments on the command line if they are supported by the host |
266 | | // toolchain. The HIPSPVToolChain will actually ignore any command line |
267 | | // arguments for any of these "supported" sanitizers. That means that no |
268 | | // sanitization of device code is actually supported at this time. |
269 | | // |
270 | | // This behavior is necessary because the host and device toolchains |
271 | | // invocations often share the command line, so the device toolchain must |
272 | | // tolerate flags meant only for the host toolchain. |
273 | 10 | return HostTC.getSupportedSanitizers(); |
274 | 10 | } |
275 | | |
276 | | VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D, |
277 | 10 | const ArgList &Args) const { |
278 | 10 | return HostTC.computeMSVCVersion(D, Args); |
279 | 10 | } |
280 | | |
281 | | void HIPSPVToolChain::adjustDebugInfoKind( |
282 | | llvm::codegenoptions::DebugInfoKind &DebugInfoKind, |
283 | 10 | const llvm::opt::ArgList &Args) const { |
284 | | // Debug info generation is disabled for SPIRV-LLVM-Translator |
285 | | // which currently aborts on the presence of DW_OP_LLVM_convert. |
286 | | // TODO: Enable debug info when the SPIR-V backend arrives. |
287 | 10 | DebugInfoKind = llvm::codegenoptions::NoDebugInfo; |
288 | 10 | } |