1 | //===--- PS4CPU.cpp - PS4CPU ToolChain Implementations ----------*- 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 "PS4CPU.h" |
10 | #include "CommonArgs.h" |
11 | #include "clang/Config/config.h" |
12 | #include "clang/Driver/Compilation.h" |
13 | #include "clang/Driver/Driver.h" |
14 | #include "clang/Driver/DriverDiagnostic.h" |
15 | #include "clang/Driver/Options.h" |
16 | #include "clang/Driver/SanitizerArgs.h" |
17 | #include "llvm/Option/ArgList.h" |
18 | #include "llvm/Support/FileSystem.h" |
19 | #include "llvm/Support/Path.h" |
20 | #include <cstdlib> // ::getenv |
21 | |
22 | using namespace clang::driver; |
23 | using namespace clang; |
24 | using namespace llvm::opt; |
25 | |
26 | // Helper to paste bits of an option together and return a saved string. |
27 | static const char *makeArgString(const ArgList &Args, const char *Prefix, |
28 | const char *Base, const char *Suffix) { |
29 | // Basically "Prefix + Base + Suffix" all converted to Twine then saved. |
30 | return Args.MakeArgString(Str: Twine(StringRef(Prefix), Base) + Suffix); |
31 | } |
32 | |
33 | void tools::PScpu::addProfileRTArgs(const ToolChain &TC, const ArgList &Args, |
34 | ArgStringList &CmdArgs) { |
35 | assert(TC.getTriple().isPS()); |
36 | auto &PSTC = static_cast<const toolchains::PS4PS5Base &>(TC); |
37 | |
38 | if ((Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs, |
39 | false) || |
40 | Args.hasFlag(options::OPT_fprofile_generate, |
41 | options::OPT_fno_profile_generate, false) || |
42 | Args.hasFlag(options::OPT_fprofile_generate_EQ, |
43 | options::OPT_fno_profile_generate, false) || |
44 | Args.hasFlag(options::OPT_fprofile_instr_generate, |
45 | options::OPT_fno_profile_instr_generate, false) || |
46 | Args.hasFlag(options::OPT_fprofile_instr_generate_EQ, |
47 | options::OPT_fno_profile_instr_generate, false) || |
48 | Args.hasFlag(options::OPT_fcs_profile_generate, |
49 | options::OPT_fno_profile_generate, false) || |
50 | Args.hasFlag(options::OPT_fcs_profile_generate_EQ, |
51 | options::OPT_fno_profile_generate, false) || |
52 | Args.hasArg(options::OPT_fcreate_profile) || |
53 | Args.hasArg(options::OPT_coverage))) |
54 | CmdArgs.push_back(Elt: makeArgString( |
55 | Args, Prefix: "--dependent-lib=" , Base: PSTC.getProfileRTLibName(), Suffix: "" )); |
56 | } |
57 | |
58 | void tools::PScpu::Assembler::ConstructJob(Compilation &C, const JobAction &JA, |
59 | const InputInfo &Output, |
60 | const InputInfoList &Inputs, |
61 | const ArgList &Args, |
62 | const char *LinkingOutput) const { |
63 | auto &TC = static_cast<const toolchains::PS4PS5Base &>(getToolChain()); |
64 | claimNoWarnArgs(Args); |
65 | ArgStringList CmdArgs; |
66 | |
67 | Args.AddAllArgValues(Output&: CmdArgs, options::Id0: OPT_Wa_COMMA, options::Id1: OPT_Xassembler); |
68 | |
69 | CmdArgs.push_back(Elt: "-o" ); |
70 | CmdArgs.push_back(Elt: Output.getFilename()); |
71 | |
72 | assert(Inputs.size() == 1 && "Unexpected number of inputs." ); |
73 | const InputInfo &Input = Inputs[0]; |
74 | assert(Input.isFilename() && "Invalid input." ); |
75 | CmdArgs.push_back(Elt: Input.getFilename()); |
76 | |
77 | std::string AsName = TC.qualifyPSCmdName(CmdName: "as" ); |
78 | const char *Exec = Args.MakeArgString(Str: TC.GetProgramPath(Name: AsName.c_str())); |
79 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
80 | args: ResponseFileSupport::AtFileUTF8(), |
81 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
82 | } |
83 | |
84 | void tools::PScpu::addSanitizerArgs(const ToolChain &TC, const ArgList &Args, |
85 | ArgStringList &CmdArgs) { |
86 | assert(TC.getTriple().isPS()); |
87 | auto &PSTC = static_cast<const toolchains::PS4PS5Base &>(TC); |
88 | PSTC.addSanitizerArgs(Args, CmdArgs, Prefix: "--dependent-lib=lib" , Suffix: ".a" ); |
89 | } |
90 | |
91 | void toolchains::PS4CPU::addSanitizerArgs(const ArgList &Args, |
92 | ArgStringList &CmdArgs, |
93 | const char *Prefix, |
94 | const char *Suffix) const { |
95 | auto arg = [&](const char *Name) -> const char * { |
96 | return makeArgString(Args, Prefix, Base: Name, Suffix); |
97 | }; |
98 | const SanitizerArgs &SanArgs = getSanitizerArgs(JobArgs: Args); |
99 | if (SanArgs.needsUbsanRt()) |
100 | CmdArgs.push_back(Elt: arg("SceDbgUBSanitizer_stub_weak" )); |
101 | if (SanArgs.needsAsanRt()) |
102 | CmdArgs.push_back(Elt: arg("SceDbgAddressSanitizer_stub_weak" )); |
103 | } |
104 | |
105 | void toolchains::PS5CPU::addSanitizerArgs(const ArgList &Args, |
106 | ArgStringList &CmdArgs, |
107 | const char *Prefix, |
108 | const char *Suffix) const { |
109 | auto arg = [&](const char *Name) -> const char * { |
110 | return makeArgString(Args, Prefix, Base: Name, Suffix); |
111 | }; |
112 | const SanitizerArgs &SanArgs = getSanitizerArgs(JobArgs: Args); |
113 | if (SanArgs.needsUbsanRt()) |
114 | CmdArgs.push_back(Elt: arg("SceUBSanitizer_nosubmission_stub_weak" )); |
115 | if (SanArgs.needsAsanRt()) |
116 | CmdArgs.push_back(Elt: arg("SceAddressSanitizer_nosubmission_stub_weak" )); |
117 | if (SanArgs.needsTsanRt()) |
118 | CmdArgs.push_back(Elt: arg("SceThreadSanitizer_nosubmission_stub_weak" )); |
119 | } |
120 | |
121 | void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
122 | const InputInfo &Output, |
123 | const InputInfoList &Inputs, |
124 | const ArgList &Args, |
125 | const char *LinkingOutput) const { |
126 | auto &TC = static_cast<const toolchains::PS4PS5Base &>(getToolChain()); |
127 | const Driver &D = TC.getDriver(); |
128 | ArgStringList CmdArgs; |
129 | |
130 | // Silence warning for "clang -g foo.o -o foo" |
131 | Args.ClaimAllArgs(options::OPT_g_Group); |
132 | // and "clang -emit-llvm foo.o -o foo" |
133 | Args.ClaimAllArgs(options::OPT_emit_llvm); |
134 | // and for "clang -w foo.o -o foo". Other warning options are already |
135 | // handled somewhere else. |
136 | Args.ClaimAllArgs(options::OPT_w); |
137 | |
138 | if (!D.SysRoot.empty()) |
139 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--sysroot=" + D.SysRoot)); |
140 | |
141 | if (Args.hasArg(options::OPT_pie)) |
142 | CmdArgs.push_back(Elt: "-pie" ); |
143 | |
144 | if (Args.hasArg(options::OPT_rdynamic)) |
145 | CmdArgs.push_back(Elt: "-export-dynamic" ); |
146 | if (Args.hasArg(options::OPT_shared)) |
147 | CmdArgs.push_back(Elt: "--shared" ); |
148 | |
149 | assert((Output.isFilename() || Output.isNothing()) && "Invalid output." ); |
150 | if (Output.isFilename()) { |
151 | CmdArgs.push_back(Elt: "-o" ); |
152 | CmdArgs.push_back(Elt: Output.getFilename()); |
153 | } |
154 | |
155 | const bool UseLTO = D.isUsingLTO(); |
156 | const bool UseJMC = |
157 | Args.hasFlag(options::OPT_fjmc, options::OPT_fno_jmc, false); |
158 | const bool IsPS4 = TC.getTriple().isPS4(); |
159 | |
160 | const char *PS4LTOArgs = "" ; |
161 | auto AddCodeGenFlag = [&](Twine Flag) { |
162 | if (IsPS4) |
163 | PS4LTOArgs = Args.MakeArgString(Str: Twine(PS4LTOArgs) + " " + Flag); |
164 | else |
165 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine("-plugin-opt=" ) + Flag)); |
166 | }; |
167 | |
168 | if (UseLTO) { |
169 | // We default to creating the arange section, but LTO does not. Enable it |
170 | // here. |
171 | AddCodeGenFlag("-generate-arange-section" ); |
172 | |
173 | // This tells LTO to perform JustMyCode instrumentation. |
174 | if (UseJMC) |
175 | AddCodeGenFlag("-enable-jmc-instrument" ); |
176 | |
177 | if (Arg *A = Args.getLastArg(options::OPT_fcrash_diagnostics_dir)) |
178 | AddCodeGenFlag(Twine("-crash-diagnostics-dir=" ) + A->getValue()); |
179 | |
180 | StringRef Parallelism = getLTOParallelism(Args, D); |
181 | if (!Parallelism.empty()) { |
182 | if (IsPS4) |
183 | AddCodeGenFlag(Twine("-threads=" ) + Parallelism); |
184 | else |
185 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine("-plugin-opt=jobs=" ) + Parallelism)); |
186 | } |
187 | |
188 | if (IsPS4) { |
189 | const char *Prefix = nullptr; |
190 | if (D.getLTOMode() == LTOK_Thin) |
191 | Prefix = "-lto-thin-debug-options=" ; |
192 | else if (D.getLTOMode() == LTOK_Full) |
193 | Prefix = "-lto-debug-options=" ; |
194 | else |
195 | llvm_unreachable("new LTO mode?" ); |
196 | |
197 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine(Prefix) + PS4LTOArgs)); |
198 | } |
199 | } |
200 | |
201 | if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) |
202 | TC.addSanitizerArgs(Args, CmdArgs, Prefix: "-l" , Suffix: "" ); |
203 | |
204 | if (D.isUsingLTO() && Args.hasArg(options::OPT_funified_lto)) { |
205 | if (D.getLTOMode() == LTOK_Thin) |
206 | CmdArgs.push_back(Elt: "--lto=thin" ); |
207 | else if (D.getLTOMode() == LTOK_Full) |
208 | CmdArgs.push_back(Elt: "--lto=full" ); |
209 | } |
210 | |
211 | Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group, |
212 | options::OPT_s, options::OPT_t}); |
213 | |
214 | if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) |
215 | CmdArgs.push_back(Elt: "--no-demangle" ); |
216 | |
217 | AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); |
218 | |
219 | if (Args.hasArg(options::OPT_pthread)) { |
220 | CmdArgs.push_back(Elt: "-lpthread" ); |
221 | } |
222 | |
223 | if (UseJMC) { |
224 | CmdArgs.push_back(Elt: "--whole-archive" ); |
225 | if (IsPS4) |
226 | CmdArgs.push_back(Elt: "-lSceDbgJmc" ); |
227 | else |
228 | CmdArgs.push_back(Elt: "-lSceJmc_nosubmission" ); |
229 | CmdArgs.push_back(Elt: "--no-whole-archive" ); |
230 | } |
231 | |
232 | if (Args.hasArg(options::OPT_fuse_ld_EQ)) { |
233 | D.Diag(diag::DiagID: err_drv_unsupported_opt_for_target) |
234 | << "-fuse-ld" << TC.getTriple().str(); |
235 | } |
236 | |
237 | std::string LdName = TC.qualifyPSCmdName(CmdName: TC.getLinkerBaseName()); |
238 | const char *Exec = Args.MakeArgString(Str: TC.GetProgramPath(Name: LdName.c_str())); |
239 | |
240 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
241 | args: ResponseFileSupport::AtFileUTF8(), |
242 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
243 | } |
244 | |
245 | toolchains::PS4PS5Base::PS4PS5Base(const Driver &D, const llvm::Triple &Triple, |
246 | const ArgList &Args, StringRef Platform, |
247 | const char *EnvVar) |
248 | : Generic_ELF(D, Triple, Args) { |
249 | if (Args.hasArg(clang::driver::options::Ids: OPT_static)) |
250 | D.Diag(clang::diag::DiagID: err_drv_unsupported_opt_for_target) |
251 | << "-static" << Platform; |
252 | |
253 | // Determine where to find the PS4/PS5 libraries. |
254 | // If -isysroot was passed, use that as the SDK base path. |
255 | // If not, we use the EnvVar if it exists; otherwise use the driver's |
256 | // installation path, which should be <SDK_DIR>/host_tools/bin. |
257 | SmallString<80> Whence; |
258 | if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { |
259 | SDKRootDir = A->getValue(); |
260 | if (!llvm::sys::fs::exists(Path: SDKRootDir)) |
261 | D.Diag(clang::diag::DiagID: warn_missing_sysroot) << SDKRootDir; |
262 | Whence = A->getSpelling(); |
263 | } else if (const char *EnvValue = getenv(name: EnvVar)) { |
264 | SDKRootDir = EnvValue; |
265 | Whence = { "environment variable '" , EnvVar, "'" }; |
266 | } else { |
267 | SDKRootDir = D.Dir + "/../../" ; |
268 | Whence = "compiler's location" ; |
269 | } |
270 | |
271 | SmallString<512> SDKIncludeDir(SDKRootDir); |
272 | llvm::sys::path::append(path&: SDKIncludeDir, a: "target/include" ); |
273 | if (!Args.hasArg(options::OPT_nostdinc) && |
274 | !Args.hasArg(options::OPT_nostdlibinc) && |
275 | !Args.hasArg(options::OPT_isysroot) && |
276 | !Args.hasArg(options::OPT__sysroot_EQ) && |
277 | !llvm::sys::fs::exists(Path: SDKIncludeDir)) { |
278 | D.Diag(clang::diag::DiagID: warn_drv_unable_to_find_directory_expected) |
279 | << Twine(Platform, " system headers" ).str() << SDKIncludeDir << Whence; |
280 | } |
281 | |
282 | SmallString<512> SDKLibDir(SDKRootDir); |
283 | llvm::sys::path::append(path&: SDKLibDir, a: "target/lib" ); |
284 | if (!Args.hasArg(options::OPT_nostdlib) && |
285 | !Args.hasArg(options::OPT_nodefaultlibs) && |
286 | !Args.hasArg(options::OPT__sysroot_EQ) && !Args.hasArg(options::OPT_E) && |
287 | !Args.hasArg(options::OPT_c) && !Args.hasArg(options::OPT_S) && |
288 | !Args.hasArg(options::OPT_emit_ast) && |
289 | !llvm::sys::fs::exists(SDKLibDir)) { |
290 | D.Diag(clang::diag::warn_drv_unable_to_find_directory_expected) |
291 | << Twine(Platform, " system libraries" ).str() << SDKLibDir << Whence; |
292 | return; |
293 | } |
294 | getFilePaths().push_back(Elt: std::string(SDKLibDir)); |
295 | } |
296 | |
297 | void toolchains::PS4PS5Base::AddClangSystemIncludeArgs( |
298 | const ArgList &DriverArgs, |
299 | ArgStringList &CC1Args) const { |
300 | const Driver &D = getDriver(); |
301 | |
302 | if (DriverArgs.hasArg(options::OPT_nostdinc)) |
303 | return; |
304 | |
305 | if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { |
306 | SmallString<128> Dir(D.ResourceDir); |
307 | llvm::sys::path::append(path&: Dir, a: "include" ); |
308 | addSystemInclude(DriverArgs, CC1Args, Path: Dir.str()); |
309 | } |
310 | |
311 | if (DriverArgs.hasArg(options::OPT_nostdlibinc)) |
312 | return; |
313 | |
314 | addExternCSystemInclude(DriverArgs, CC1Args, |
315 | Path: SDKRootDir + "/target/include" ); |
316 | addExternCSystemInclude(DriverArgs, CC1Args, |
317 | Path: SDKRootDir + "/target/include_common" ); |
318 | } |
319 | |
320 | Tool *toolchains::PS4CPU::buildAssembler() const { |
321 | return new tools::PScpu::Assembler(*this); |
322 | } |
323 | |
324 | Tool *toolchains::PS5CPU::buildAssembler() const { |
325 | // PS5 does not support an external assembler. |
326 | getDriver().Diag(clang::diag::err_no_external_assembler); |
327 | return nullptr; |
328 | } |
329 | |
330 | Tool *toolchains::PS4PS5Base::buildLinker() const { |
331 | return new tools::PScpu::Linker(*this); |
332 | } |
333 | |
334 | SanitizerMask toolchains::PS4PS5Base::getSupportedSanitizers() const { |
335 | SanitizerMask Res = ToolChain::getSupportedSanitizers(); |
336 | Res |= SanitizerKind::Address; |
337 | Res |= SanitizerKind::PointerCompare; |
338 | Res |= SanitizerKind::PointerSubtract; |
339 | Res |= SanitizerKind::Vptr; |
340 | return Res; |
341 | } |
342 | |
343 | SanitizerMask toolchains::PS5CPU::getSupportedSanitizers() const { |
344 | SanitizerMask Res = PS4PS5Base::getSupportedSanitizers(); |
345 | Res |= SanitizerKind::Thread; |
346 | return Res; |
347 | } |
348 | |
349 | void toolchains::PS4PS5Base::addClangTargetOptions( |
350 | const ArgList &DriverArgs, ArgStringList &CC1Args, |
351 | Action::OffloadKind DeviceOffloadingKind) const { |
352 | // PS4/PS5 do not use init arrays. |
353 | if (DriverArgs.hasArg(options::OPT_fuse_init_array)) { |
354 | Arg *A = DriverArgs.getLastArg(options::OPT_fuse_init_array); |
355 | getDriver().Diag(clang::diag::err_drv_unsupported_opt_for_target) |
356 | << A->getAsString(DriverArgs) << getTriple().str(); |
357 | } |
358 | |
359 | CC1Args.push_back(Elt: "-fno-use-init-array" ); |
360 | |
361 | // Default to -fvisibility-global-new-delete=source for PS5. |
362 | if (getTriple().isPS5() && |
363 | !DriverArgs.hasArg(options::OPT_fvisibility_global_new_delete_EQ, |
364 | options::OPT_fvisibility_global_new_delete_hidden)) |
365 | CC1Args.push_back(Elt: "-fvisibility-global-new-delete=source" ); |
366 | |
367 | const Arg *A = |
368 | DriverArgs.getLastArg(options::OPT_fvisibility_from_dllstorageclass, |
369 | options::OPT_fno_visibility_from_dllstorageclass); |
370 | if (!A || |
371 | A->getOption().matches(options::OPT_fvisibility_from_dllstorageclass)) { |
372 | CC1Args.push_back(Elt: "-fvisibility-from-dllstorageclass" ); |
373 | |
374 | if (DriverArgs.hasArg(options::OPT_fvisibility_dllexport_EQ)) |
375 | DriverArgs.AddLastArg(CC1Args, options::OPT_fvisibility_dllexport_EQ); |
376 | else |
377 | CC1Args.push_back(Elt: "-fvisibility-dllexport=protected" ); |
378 | |
379 | if (DriverArgs.hasArg(options::OPT_fvisibility_nodllstorageclass_EQ)) |
380 | DriverArgs.AddLastArg(CC1Args, |
381 | options::OPT_fvisibility_nodllstorageclass_EQ); |
382 | else |
383 | CC1Args.push_back(Elt: "-fvisibility-nodllstorageclass=hidden" ); |
384 | |
385 | if (DriverArgs.hasArg(options::OPT_fvisibility_externs_dllimport_EQ)) |
386 | DriverArgs.AddLastArg(CC1Args, |
387 | options::OPT_fvisibility_externs_dllimport_EQ); |
388 | else |
389 | CC1Args.push_back(Elt: "-fvisibility-externs-dllimport=default" ); |
390 | |
391 | if (DriverArgs.hasArg( |
392 | options::OPT_fvisibility_externs_nodllstorageclass_EQ)) |
393 | DriverArgs.AddLastArg( |
394 | CC1Args, options::OPT_fvisibility_externs_nodllstorageclass_EQ); |
395 | else |
396 | CC1Args.push_back(Elt: "-fvisibility-externs-nodllstorageclass=default" ); |
397 | } |
398 | } |
399 | |
400 | // PS4 toolchain. |
401 | toolchains::PS4CPU::PS4CPU(const Driver &D, const llvm::Triple &Triple, |
402 | const llvm::opt::ArgList &Args) |
403 | : PS4PS5Base(D, Triple, Args, "PS4" , "SCE_ORBIS_SDK_DIR" ) {} |
404 | |
405 | // PS5 toolchain. |
406 | toolchains::PS5CPU::PS5CPU(const Driver &D, const llvm::Triple &Triple, |
407 | const llvm::opt::ArgList &Args) |
408 | : PS4PS5Base(D, Triple, Args, "PS5" , "SCE_PROSPERO_SDK_DIR" ) {} |
409 | |