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, |
212 | {options::OPT_L, options::OPT_T_Group, options::OPT_s, |
213 | options::OPT_t, options::OPT_r}); |
214 | |
215 | if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) |
216 | CmdArgs.push_back(Elt: "--no-demangle" ); |
217 | |
218 | AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); |
219 | |
220 | if (Args.hasArg(options::OPT_pthread)) { |
221 | CmdArgs.push_back(Elt: "-lpthread" ); |
222 | } |
223 | |
224 | if (UseJMC) { |
225 | CmdArgs.push_back(Elt: "--whole-archive" ); |
226 | if (IsPS4) |
227 | CmdArgs.push_back(Elt: "-lSceDbgJmc" ); |
228 | else |
229 | CmdArgs.push_back(Elt: "-lSceJmc_nosubmission" ); |
230 | CmdArgs.push_back(Elt: "--no-whole-archive" ); |
231 | } |
232 | |
233 | if (Args.hasArg(options::OPT_fuse_ld_EQ)) { |
234 | D.Diag(diag::DiagID: err_drv_unsupported_opt_for_target) |
235 | << "-fuse-ld" << TC.getTriple().str(); |
236 | } |
237 | |
238 | std::string LdName = TC.qualifyPSCmdName(CmdName: TC.getLinkerBaseName()); |
239 | const char *Exec = Args.MakeArgString(Str: TC.GetProgramPath(Name: LdName.c_str())); |
240 | |
241 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
242 | args: ResponseFileSupport::AtFileUTF8(), |
243 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
244 | } |
245 | |
246 | toolchains::PS4PS5Base::PS4PS5Base(const Driver &D, const llvm::Triple &Triple, |
247 | const ArgList &Args, StringRef Platform, |
248 | const char *EnvVar) |
249 | : Generic_ELF(D, Triple, Args) { |
250 | if (Args.hasArg(clang::driver::options::Ids: OPT_static)) |
251 | D.Diag(clang::diag::DiagID: err_drv_unsupported_opt_for_target) |
252 | << "-static" << Platform; |
253 | |
254 | // Determine where to find the PS4/PS5 libraries. |
255 | // If -isysroot was passed, use that as the SDK base path. |
256 | // If not, we use the EnvVar if it exists; otherwise use the driver's |
257 | // installation path, which should be <SDK_DIR>/host_tools/bin. |
258 | SmallString<80> Whence; |
259 | if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { |
260 | SDKRootDir = A->getValue(); |
261 | if (!llvm::sys::fs::exists(Path: SDKRootDir)) |
262 | D.Diag(clang::diag::DiagID: warn_missing_sysroot) << SDKRootDir; |
263 | Whence = A->getSpelling(); |
264 | } else if (const char *EnvValue = getenv(name: EnvVar)) { |
265 | SDKRootDir = EnvValue; |
266 | Whence = { "environment variable '" , EnvVar, "'" }; |
267 | } else { |
268 | SDKRootDir = D.Dir + "/../../" ; |
269 | Whence = "compiler's location" ; |
270 | } |
271 | |
272 | SmallString<512> SDKIncludeDir(SDKRootDir); |
273 | llvm::sys::path::append(path&: SDKIncludeDir, a: "target/include" ); |
274 | if (!Args.hasArg(options::OPT_nostdinc) && |
275 | !Args.hasArg(options::OPT_nostdlibinc) && |
276 | !Args.hasArg(options::OPT_isysroot) && |
277 | !Args.hasArg(options::OPT__sysroot_EQ) && |
278 | !llvm::sys::fs::exists(Path: SDKIncludeDir)) { |
279 | D.Diag(clang::diag::DiagID: warn_drv_unable_to_find_directory_expected) |
280 | << Twine(Platform, " system headers" ).str() << SDKIncludeDir << Whence; |
281 | } |
282 | |
283 | SmallString<512> SDKLibDir(SDKRootDir); |
284 | llvm::sys::path::append(path&: SDKLibDir, a: "target/lib" ); |
285 | if (!Args.hasArg(options::OPT_nostdlib) && |
286 | !Args.hasArg(options::OPT_nodefaultlibs) && |
287 | !Args.hasArg(options::OPT__sysroot_EQ) && !Args.hasArg(options::OPT_E) && |
288 | !Args.hasArg(options::OPT_c) && !Args.hasArg(options::OPT_S) && |
289 | !Args.hasArg(options::OPT_emit_ast) && |
290 | !llvm::sys::fs::exists(SDKLibDir)) { |
291 | D.Diag(clang::diag::warn_drv_unable_to_find_directory_expected) |
292 | << Twine(Platform, " system libraries" ).str() << SDKLibDir << Whence; |
293 | return; |
294 | } |
295 | getFilePaths().push_back(Elt: std::string(SDKLibDir)); |
296 | } |
297 | |
298 | void toolchains::PS4PS5Base::AddClangSystemIncludeArgs( |
299 | const ArgList &DriverArgs, |
300 | ArgStringList &CC1Args) const { |
301 | const Driver &D = getDriver(); |
302 | |
303 | if (DriverArgs.hasArg(options::OPT_nostdinc)) |
304 | return; |
305 | |
306 | if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { |
307 | SmallString<128> Dir(D.ResourceDir); |
308 | llvm::sys::path::append(path&: Dir, a: "include" ); |
309 | addSystemInclude(DriverArgs, CC1Args, Path: Dir.str()); |
310 | } |
311 | |
312 | if (DriverArgs.hasArg(options::OPT_nostdlibinc)) |
313 | return; |
314 | |
315 | addExternCSystemInclude(DriverArgs, CC1Args, |
316 | Path: SDKRootDir + "/target/include" ); |
317 | addExternCSystemInclude(DriverArgs, CC1Args, |
318 | Path: SDKRootDir + "/target/include_common" ); |
319 | } |
320 | |
321 | Tool *toolchains::PS4CPU::buildAssembler() const { |
322 | return new tools::PScpu::Assembler(*this); |
323 | } |
324 | |
325 | Tool *toolchains::PS5CPU::buildAssembler() const { |
326 | // PS5 does not support an external assembler. |
327 | getDriver().Diag(clang::diag::err_no_external_assembler); |
328 | return nullptr; |
329 | } |
330 | |
331 | Tool *toolchains::PS4PS5Base::buildLinker() const { |
332 | return new tools::PScpu::Linker(*this); |
333 | } |
334 | |
335 | SanitizerMask toolchains::PS4PS5Base::getSupportedSanitizers() const { |
336 | SanitizerMask Res = ToolChain::getSupportedSanitizers(); |
337 | Res |= SanitizerKind::Address; |
338 | Res |= SanitizerKind::PointerCompare; |
339 | Res |= SanitizerKind::PointerSubtract; |
340 | Res |= SanitizerKind::Vptr; |
341 | return Res; |
342 | } |
343 | |
344 | SanitizerMask toolchains::PS5CPU::getSupportedSanitizers() const { |
345 | SanitizerMask Res = PS4PS5Base::getSupportedSanitizers(); |
346 | Res |= SanitizerKind::Thread; |
347 | return Res; |
348 | } |
349 | |
350 | void toolchains::PS4PS5Base::addClangTargetOptions( |
351 | const ArgList &DriverArgs, ArgStringList &CC1Args, |
352 | Action::OffloadKind DeviceOffloadingKind) const { |
353 | // PS4/PS5 do not use init arrays. |
354 | if (DriverArgs.hasArg(options::OPT_fuse_init_array)) { |
355 | Arg *A = DriverArgs.getLastArg(options::OPT_fuse_init_array); |
356 | getDriver().Diag(clang::diag::err_drv_unsupported_opt_for_target) |
357 | << A->getAsString(DriverArgs) << getTriple().str(); |
358 | } |
359 | |
360 | CC1Args.push_back(Elt: "-fno-use-init-array" ); |
361 | |
362 | // Default to -fvisibility-global-new-delete=source for PS5. |
363 | if (getTriple().isPS5() && |
364 | !DriverArgs.hasArg(options::OPT_fvisibility_global_new_delete_EQ, |
365 | options::OPT_fvisibility_global_new_delete_hidden)) |
366 | CC1Args.push_back(Elt: "-fvisibility-global-new-delete=source" ); |
367 | |
368 | const Arg *A = |
369 | DriverArgs.getLastArg(options::OPT_fvisibility_from_dllstorageclass, |
370 | options::OPT_fno_visibility_from_dllstorageclass); |
371 | if (!A || |
372 | A->getOption().matches(options::OPT_fvisibility_from_dllstorageclass)) { |
373 | CC1Args.push_back(Elt: "-fvisibility-from-dllstorageclass" ); |
374 | |
375 | if (DriverArgs.hasArg(options::OPT_fvisibility_dllexport_EQ)) |
376 | DriverArgs.AddLastArg(CC1Args, options::OPT_fvisibility_dllexport_EQ); |
377 | else |
378 | CC1Args.push_back(Elt: "-fvisibility-dllexport=protected" ); |
379 | |
380 | if (DriverArgs.hasArg(options::OPT_fvisibility_nodllstorageclass_EQ)) |
381 | DriverArgs.AddLastArg(CC1Args, |
382 | options::OPT_fvisibility_nodllstorageclass_EQ); |
383 | else |
384 | CC1Args.push_back(Elt: "-fvisibility-nodllstorageclass=hidden" ); |
385 | |
386 | if (DriverArgs.hasArg(options::OPT_fvisibility_externs_dllimport_EQ)) |
387 | DriverArgs.AddLastArg(CC1Args, |
388 | options::OPT_fvisibility_externs_dllimport_EQ); |
389 | else |
390 | CC1Args.push_back(Elt: "-fvisibility-externs-dllimport=default" ); |
391 | |
392 | if (DriverArgs.hasArg( |
393 | options::OPT_fvisibility_externs_nodllstorageclass_EQ)) |
394 | DriverArgs.AddLastArg( |
395 | CC1Args, options::OPT_fvisibility_externs_nodllstorageclass_EQ); |
396 | else |
397 | CC1Args.push_back(Elt: "-fvisibility-externs-nodllstorageclass=default" ); |
398 | } |
399 | } |
400 | |
401 | // PS4 toolchain. |
402 | toolchains::PS4CPU::PS4CPU(const Driver &D, const llvm::Triple &Triple, |
403 | const llvm::opt::ArgList &Args) |
404 | : PS4PS5Base(D, Triple, Args, "PS4" , "SCE_ORBIS_SDK_DIR" ) {} |
405 | |
406 | // PS5 toolchain. |
407 | toolchains::PS5CPU::PS5CPU(const Driver &D, const llvm::Triple &Triple, |
408 | const llvm::opt::ArgList &Args) |
409 | : PS4PS5Base(D, Triple, Args, "PS5" , "SCE_PROSPERO_SDK_DIR" ) {} |
410 | |