From 03fe7a83ce79d43b63052e7762573b57a8c52db8 Mon Sep 17 00:00:00 2001 From: Edd Dawson Date: Mon, 15 Jul 2024 16:36:45 +0100 Subject: [PATCH] [PS4/PS5][NFC] Split PScpu::Linker into PS4/PS5 classes (#98884) It has long been the case on PlayStation that the linker itself has taken on much of the responsibility that is traditionally the domain of the C language driver elsewhere: which linker script to use, selection of CRT objects, and so forth. This is changing on PS5. The driver will assume responsibility for such things. However, the situation on PS4 will remain as-is. To accommodate this divergence, we must first separate how linker Jobs are created. `clang/test/Driver/ps4-linker.c` has been similarly split for related reasons. In subsequent changes, PS5-specific linking behaviour can be moved from SIE private patches in the PS5 linker to the (upstream) driver without affecting the behaviour or implementation of PS4. --- clang/lib/Driver/ToolChains/PS4CPU.cpp | 159 +++++++++++++++++++------ clang/lib/Driver/ToolChains/PS4CPU.h | 27 ++++- clang/test/Driver/ps4-linker.c | 20 ++++ clang/test/Driver/ps4-ps5-linker.c | 29 ----- clang/test/Driver/ps5-linker.c | 18 +++ 5 files changed, 185 insertions(+), 68 deletions(-) create mode 100644 clang/test/Driver/ps4-linker.c delete mode 100644 clang/test/Driver/ps4-ps5-linker.c create mode 100644 clang/test/Driver/ps5-linker.c diff --git a/clang/lib/Driver/ToolChains/PS4CPU.cpp b/clang/lib/Driver/ToolChains/PS4CPU.cpp index 3fd62d97930937..974e486a0082bc 100644 --- a/clang/lib/Driver/ToolChains/PS4CPU.cpp +++ b/clang/lib/Driver/ToolChains/PS4CPU.cpp @@ -118,11 +118,11 @@ void toolchains::PS5CPU::addSanitizerArgs(const ArgList &Args, CmdArgs.push_back(arg("SceThreadSanitizer_nosubmission_stub_weak")); } -void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, - const InputInfo &Output, - const InputInfoList &Inputs, - const ArgList &Args, - const char *LinkingOutput) const { +void tools::PS4cpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { auto &TC = static_cast(getToolChain()); const Driver &D = TC.getDriver(); ArgStringList CmdArgs; @@ -155,14 +155,120 @@ void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, const bool UseLTO = D.isUsingLTO(); const bool UseJMC = Args.hasFlag(options::OPT_fjmc, options::OPT_fno_jmc, false); - const bool IsPS4 = TC.getTriple().isPS4(); - const char *PS4LTOArgs = ""; + const char *LTOArgs = ""; auto AddCodeGenFlag = [&](Twine Flag) { - if (IsPS4) - PS4LTOArgs = Args.MakeArgString(Twine(PS4LTOArgs) + " " + Flag); + LTOArgs = Args.MakeArgString(Twine(LTOArgs) + " " + Flag); + }; + + if (UseLTO) { + // We default to creating the arange section, but LTO does not. Enable it + // here. + AddCodeGenFlag("-generate-arange-section"); + + // This tells LTO to perform JustMyCode instrumentation. + if (UseJMC) + AddCodeGenFlag("-enable-jmc-instrument"); + + if (Arg *A = Args.getLastArg(options::OPT_fcrash_diagnostics_dir)) + AddCodeGenFlag(Twine("-crash-diagnostics-dir=") + A->getValue()); + + StringRef Parallelism = getLTOParallelism(Args, D); + if (!Parallelism.empty()) + AddCodeGenFlag(Twine("-threads=") + Parallelism); + + const char *Prefix = nullptr; + if (D.getLTOMode() == LTOK_Thin) + Prefix = "-lto-thin-debug-options="; + else if (D.getLTOMode() == LTOK_Full) + Prefix = "-lto-debug-options="; else - CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=") + Flag)); + llvm_unreachable("new LTO mode?"); + + CmdArgs.push_back(Args.MakeArgString(Twine(Prefix) + LTOArgs)); + } + + if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) + TC.addSanitizerArgs(Args, CmdArgs, "-l", ""); + + if (D.isUsingLTO() && Args.hasArg(options::OPT_funified_lto)) { + if (D.getLTOMode() == LTOK_Thin) + CmdArgs.push_back("--lto=thin"); + else if (D.getLTOMode() == LTOK_Full) + CmdArgs.push_back("--lto=full"); + } + + Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group, + options::OPT_s, options::OPT_t}); + + if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) + CmdArgs.push_back("--no-demangle"); + + AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); + + if (Args.hasArg(options::OPT_pthread)) { + CmdArgs.push_back("-lpthread"); + } + + if (UseJMC) { + CmdArgs.push_back("--whole-archive"); + CmdArgs.push_back("-lSceDbgJmc"); + CmdArgs.push_back("--no-whole-archive"); + } + + if (Args.hasArg(options::OPT_fuse_ld_EQ)) { + D.Diag(diag::err_drv_unsupported_opt_for_target) + << "-fuse-ld" << TC.getTriple().str(); + } + + std::string LdName = TC.qualifyPSCmdName(TC.getLinkerBaseName()); + const char *Exec = Args.MakeArgString(TC.GetProgramPath(LdName.c_str())); + + C.addCommand(std::make_unique(JA, *this, + ResponseFileSupport::AtFileUTF8(), + Exec, CmdArgs, Inputs, Output)); +} + +void tools::PS5cpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + auto &TC = static_cast(getToolChain()); + const Driver &D = TC.getDriver(); + ArgStringList CmdArgs; + + // Silence warning for "clang -g foo.o -o foo" + Args.ClaimAllArgs(options::OPT_g_Group); + // and "clang -emit-llvm foo.o -o foo" + Args.ClaimAllArgs(options::OPT_emit_llvm); + // and for "clang -w foo.o -o foo". Other warning options are already + // handled somewhere else. + Args.ClaimAllArgs(options::OPT_w); + + if (!D.SysRoot.empty()) + CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); + + if (Args.hasArg(options::OPT_pie)) + CmdArgs.push_back("-pie"); + + if (Args.hasArg(options::OPT_rdynamic)) + CmdArgs.push_back("-export-dynamic"); + if (Args.hasArg(options::OPT_shared)) + CmdArgs.push_back("--shared"); + + assert((Output.isFilename() || Output.isNothing()) && "Invalid output."); + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } + + const bool UseLTO = D.isUsingLTO(); + const bool UseJMC = + Args.hasFlag(options::OPT_fjmc, options::OPT_fno_jmc, false); + + auto AddCodeGenFlag = [&](Twine Flag) { + CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=") + Flag)); }; if (UseLTO) { @@ -178,24 +284,8 @@ void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, AddCodeGenFlag(Twine("-crash-diagnostics-dir=") + A->getValue()); StringRef Parallelism = getLTOParallelism(Args, D); - if (!Parallelism.empty()) { - if (IsPS4) - AddCodeGenFlag(Twine("-threads=") + Parallelism); - else - CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=jobs=") + Parallelism)); - } - - if (IsPS4) { - const char *Prefix = nullptr; - if (D.getLTOMode() == LTOK_Thin) - Prefix = "-lto-thin-debug-options="; - else if (D.getLTOMode() == LTOK_Full) - Prefix = "-lto-debug-options="; - else - llvm_unreachable("new LTO mode?"); - - CmdArgs.push_back(Args.MakeArgString(Twine(Prefix) + PS4LTOArgs)); - } + if (!Parallelism.empty()) + CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=jobs=") + Parallelism)); } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) @@ -222,10 +312,7 @@ void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (UseJMC) { CmdArgs.push_back("--whole-archive"); - if (IsPS4) - CmdArgs.push_back("-lSceDbgJmc"); - else - CmdArgs.push_back("-lSceJmc_nosubmission"); + CmdArgs.push_back("-lSceJmc_nosubmission"); CmdArgs.push_back("--no-whole-archive"); } @@ -321,14 +408,18 @@ Tool *toolchains::PS4CPU::buildAssembler() const { return new tools::PScpu::Assembler(*this); } +Tool *toolchains::PS4CPU::buildLinker() const { + return new tools::PS4cpu::Linker(*this); +} + Tool *toolchains::PS5CPU::buildAssembler() const { // PS5 does not support an external assembler. getDriver().Diag(clang::diag::err_no_external_assembler); return nullptr; } -Tool *toolchains::PS4PS5Base::buildLinker() const { - return new tools::PScpu::Linker(*this); +Tool *toolchains::PS5CPU::buildLinker() const { + return new tools::PS5cpu::Linker(*this); } SanitizerMask toolchains::PS4PS5Base::getSupportedSanitizers() const { diff --git a/clang/lib/Driver/ToolChains/PS4CPU.h b/clang/lib/Driver/ToolChains/PS4CPU.h index fee80e77462f39..0be90183c637c8 100644 --- a/clang/lib/Driver/ToolChains/PS4CPU.h +++ b/clang/lib/Driver/ToolChains/PS4CPU.h @@ -38,10 +38,12 @@ class LLVM_LIBRARY_VISIBILITY Assembler final : public Tool { const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; }; +} // namespace PScpu +namespace PS4cpu { class LLVM_LIBRARY_VISIBILITY Linker final : public Tool { public: - Linker(const ToolChain &TC) : Tool("PScpu::Linker", "linker", TC) {} + Linker(const ToolChain &TC) : Tool("PS4cpu::Linker", "linker", TC) {} bool hasIntegratedCPP() const override { return false; } bool isLinkJob() const override { return true; } @@ -51,7 +53,23 @@ class LLVM_LIBRARY_VISIBILITY Linker final : public Tool { const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; }; -} // namespace PScpu +} // namespace PS4cpu + +namespace PS5cpu { +class LLVM_LIBRARY_VISIBILITY Linker final : public Tool { +public: + Linker(const ToolChain &TC) : Tool("PS5cpu::Linker", "linker", TC) {} + + bool hasIntegratedCPP() const override { return false; } + bool isLinkJob() const override { return true; } + + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; +} // namespace PS5cpu + } // namespace tools namespace toolchains { @@ -110,9 +128,6 @@ class LLVM_LIBRARY_VISIBILITY PS4PS5Base : public Generic_ELF { const char *Suffix) const = 0; virtual const char *getProfileRTLibName() const = 0; -protected: - Tool *buildLinker() const override; - private: // We compute the SDK root dir in the ctor, and use it later. std::string SDKRootDir; @@ -143,6 +158,7 @@ class LLVM_LIBRARY_VISIBILITY PS4CPU : public PS4PS5Base { protected: Tool *buildAssembler() const override; + Tool *buildLinker() const override; }; // PS5-specific Toolchain class. @@ -168,6 +184,7 @@ class LLVM_LIBRARY_VISIBILITY PS5CPU : public PS4PS5Base { protected: Tool *buildAssembler() const override; + Tool *buildLinker() const override; }; } // end namespace toolchains diff --git a/clang/test/Driver/ps4-linker.c b/clang/test/Driver/ps4-linker.c new file mode 100644 index 00000000000000..be0103bffe8136 --- /dev/null +++ b/clang/test/Driver/ps4-linker.c @@ -0,0 +1,20 @@ +// Test the driver's control over the JustMyCode behavior with linker flags. + +// RUN: %clang --target=x86_64-scei-ps4 -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-LIB %s +// RUN: %clang --target=x86_64-scei-ps4 -flto=thin -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-THIN-LTO,CHECK-LIB %s +// RUN: %clang --target=x86_64-scei-ps4 -flto=full -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-FULL-LTO,CHECK-LIB %s + +// CHECK-NOT: -enable-jmc-instrument +// CHECK-THIN-LTO: "-lto-thin-debug-options= -generate-arange-section -enable-jmc-instrument" +// CHECK-FULL-LTO: "-lto-debug-options= -generate-arange-section -enable-jmc-instrument" + +// Check the default library name. +// CHECK-LIB: "--whole-archive" "-lSceDbgJmc" "--no-whole-archive" + +// Test the driver's control over the -fcrash-diagnostics-dir behavior with linker flags. + +// RUN: %clang --target=x86_64-scei-ps4 -flto=thin -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-THIN-LTO %s +// RUN: %clang --target=x86_64-scei-ps4 -flto=full -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-FULL-LTO %s + +// CHECK-DIAG-THIN-LTO: "-lto-thin-debug-options= -generate-arange-section -crash-diagnostics-dir=mydumps" +// CHECK-DIAG-FULL-LTO: "-lto-debug-options= -generate-arange-section -crash-diagnostics-dir=mydumps" diff --git a/clang/test/Driver/ps4-ps5-linker.c b/clang/test/Driver/ps4-ps5-linker.c deleted file mode 100644 index 8aae94c8388346..00000000000000 --- a/clang/test/Driver/ps4-ps5-linker.c +++ /dev/null @@ -1,29 +0,0 @@ -// Test the driver's control over the JustMyCode behavior with linker flags. - -// RUN: %clang --target=x86_64-scei-ps4 -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-PS4,CHECK-PS4-LIB %s -// RUN: %clang --target=x86_64-scei-ps4 -flto=thin -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-PS4-THIN-LTO,CHECK-PS4-LIB %s -// RUN: %clang --target=x86_64-scei-ps4 -flto=full -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-PS4-FULL-LTO,CHECK-PS4-LIB %s -// RUN: %clang --target=x86_64-scei-ps5 -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-PS5,CHECK-PS5-LIB %s -// RUN: %clang --target=x86_64-scei-ps5 -flto -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-PS5-LTO,CHECK-PS5-LIB %s - -// CHECK-PS4-NOT: -enable-jmc-instrument -// CHECK-PS4-THIN-LTO: "-lto-thin-debug-options= -generate-arange-section -enable-jmc-instrument" -// CHECK-PS4-FULL-LTO: "-lto-debug-options= -generate-arange-section -enable-jmc-instrument" -// CHECK-PS5-NOT: -plugin-opt=-enable-jmc-instrument -// CHECK-PS5-LTO: -plugin-opt=-enable-jmc-instrument - -// Check the default library name. -// CHECK-PS4-LIB: "--whole-archive" "-lSceDbgJmc" "--no-whole-archive" -// CHECK-PS5-LIB: "--whole-archive" "-lSceJmc_nosubmission" "--no-whole-archive" - -// Test the driver's control over the -fcrash-diagnostics-dir behavior with linker flags. - -// RUN: %clang --target=x86_64-scei-ps4 -flto=thin -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-PS4-THIN-LTO %s -// RUN: %clang --target=x86_64-scei-ps4 -flto=full -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-PS4-FULL-LTO %s -// RUN: %clang --target=x86_64-scei-ps5 -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-PS5 %s -// RUN: %clang --target=x86_64-scei-ps5 -flto -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-PS5-LTO %s - -// CHECK-DIAG-PS4-THIN-LTO: "-lto-thin-debug-options= -generate-arange-section -crash-diagnostics-dir=mydumps" -// CHECK-DIAG-PS4-FULL-LTO: "-lto-debug-options= -generate-arange-section -crash-diagnostics-dir=mydumps" -// CHECK-DIAG-PS5-NOT: -plugin-opt=-crash-diagnostics-dir=mydumps -// CHECK-DIAG-PS5-LTO: -plugin-opt=-crash-diagnostics-dir=mydumps diff --git a/clang/test/Driver/ps5-linker.c b/clang/test/Driver/ps5-linker.c new file mode 100644 index 00000000000000..9f1e3a273b2db2 --- /dev/null +++ b/clang/test/Driver/ps5-linker.c @@ -0,0 +1,18 @@ +// Test the driver's control over the JustMyCode behavior with linker flags. + +// RUN: %clang --target=x86_64-scei-ps5 -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-LIB %s +// RUN: %clang --target=x86_64-scei-ps5 -flto -fjmc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-LTO,CHECK-LIB %s + +// CHECK-NOT: -plugin-opt=-enable-jmc-instrument +// CHECK-LTO: -plugin-opt=-enable-jmc-instrument + +// Check the default library name. +// CHECK-LIB: "--whole-archive" "-lSceJmc_nosubmission" "--no-whole-archive" + +// Test the driver's control over the -fcrash-diagnostics-dir behavior with linker flags. + +// RUN: %clang --target=x86_64-scei-ps5 -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG %s +// RUN: %clang --target=x86_64-scei-ps5 -flto -fcrash-diagnostics-dir=mydumps %s -### 2>&1 | FileCheck --check-prefixes=CHECK-DIAG-LTO %s + +// CHECK-DIAG-NOT: -plugin-opt=-crash-diagnostics-dir=mydumps +// CHECK-DIAG-LTO: -plugin-opt=-crash-diagnostics-dir=mydumps