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