//===--- AIX.cpp - AIX ToolChain Implementations ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "AIX.h" #include "Arch/PPC.h" #include "CommonArgs.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Option/ArgList.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Path.h" #include using AIX = clang::driver::toolchains::AIX; using namespace clang::driver; using namespace clang::driver::tools; using namespace clang::driver::toolchains; using namespace llvm::opt; using namespace llvm::sys; void aix::Assembler::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const Driver &D = getToolChain().getDriver(); ArgStringList CmdArgs; const bool IsArch32Bit = getToolChain().getTriple().isArch32Bit(); const bool IsArch64Bit = getToolChain().getTriple().isArch64Bit(); // Only support 32 and 64 bit. if (!IsArch32Bit && !IsArch64Bit) llvm_unreachable("Unsupported bit width value."); if (Arg *A = C.getArgs().getLastArg(options::OPT_G)) { D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getSpelling() << D.getTargetTriple(); } // Specify the mode in which the as(1) command operates. if (IsArch32Bit) { CmdArgs.push_back("-a32"); } else { // Must be 64-bit, otherwise asserted already. CmdArgs.push_back("-a64"); } // Accept any mixture of instructions. // On Power for AIX and Linux, this behaviour matches that of GCC for both the // user-provided assembler source case and the compiler-produced assembler // source case. Yet XL with user-provided assembler source would not add this. CmdArgs.push_back("-many"); Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); // Specify assembler output file. assert((Output.isFilename() || Output.isNothing()) && "Invalid output."); if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); } // Specify assembler input file. // The system assembler on AIX takes exactly one input file. The driver is // expected to invoke as(1) separately for each assembler source input file. if (Inputs.size() != 1) llvm_unreachable("Invalid number of input files."); const InputInfo &II = Inputs[0]; assert((II.isFilename() || II.isNothing()) && "Invalid input."); if (II.isFilename()) CmdArgs.push_back(II.getFilename()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("as")); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } // Determine whether there are any linker options that supply an export list // (or equivalent information about what to export) being sent to the linker. static bool hasExportListLinkerOpts(const ArgStringList &CmdArgs) { for (size_t i = 0, Size = CmdArgs.size(); i < Size; ++i) { llvm::StringRef ArgString(CmdArgs[i]); if (ArgString.starts_with("-bE:") || ArgString.starts_with("-bexport:") || ArgString == "-bexpall" || ArgString == "-bexpfull") return true; // If we split -b option, check the next opt. if (ArgString == "-b" && i + 1 < Size) { ++i; llvm::StringRef ArgNextString(CmdArgs[i]); if (ArgNextString.starts_with("E:") || ArgNextString.starts_with("export:") || ArgNextString == "expall" || ArgNextString == "expfull") return true; } } return false; } void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const AIX &ToolChain = static_cast(getToolChain()); const Driver &D = ToolChain.getDriver(); ArgStringList CmdArgs; const bool IsArch32Bit = ToolChain.getTriple().isArch32Bit(); const bool IsArch64Bit = ToolChain.getTriple().isArch64Bit(); // Only support 32 and 64 bit. if (!(IsArch32Bit || IsArch64Bit)) llvm_unreachable("Unsupported bit width value."); if (Arg *A = C.getArgs().getLastArg(options::OPT_G)) { D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getSpelling() << D.getTargetTriple(); } // Force static linking when "-static" is present. if (Args.hasArg(options::OPT_static)) CmdArgs.push_back("-bnso"); // Add options for shared libraries. if (Args.hasArg(options::OPT_shared)) { CmdArgs.push_back("-bM:SRE"); CmdArgs.push_back("-bnoentry"); } if (Args.hasFlag(options::OPT_mxcoff_roptr, options::OPT_mno_xcoff_roptr, false)) { if (Args.hasArg(options::OPT_shared)) D.Diag(diag::err_roptr_cannot_build_shared); // The `-mxcoff-roptr` option places constants in RO sections as much as // possible. Then `-bforceimprw` changes such sections to RW if they contain // imported symbols that need to be resolved. CmdArgs.push_back("-bforceimprw"); } // PGO instrumentation generates symbols belonging to special sections, and // the linker needs to place all symbols in a particular section together in // memory; the AIX linker does that under an option. if (Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs, false) || Args.hasFlag(options::OPT_fprofile_generate, options::OPT_fno_profile_generate, false) || Args.hasFlag(options::OPT_fprofile_generate_EQ, options::OPT_fno_profile_generate, false) || Args.hasFlag(options::OPT_fprofile_instr_generate, options::OPT_fno_profile_instr_generate, false) || Args.hasFlag(options::OPT_fprofile_instr_generate_EQ, options::OPT_fno_profile_instr_generate, false) || Args.hasFlag(options::OPT_fcs_profile_generate, options::OPT_fno_profile_generate, false) || Args.hasFlag(options::OPT_fcs_profile_generate_EQ, options::OPT_fno_profile_generate, false) || Args.hasArg(options::OPT_fcreate_profile) || Args.hasArg(options::OPT_coverage)) CmdArgs.push_back("-bdbg:namedsects:ss"); if (Arg *A = Args.getLastArg(clang::driver::options::OPT_mxcoff_build_id_EQ)) { StringRef BuildId = A->getValue(); if (BuildId[0] != '0' || BuildId[1] != 'x' || BuildId.find_if_not(llvm::isHexDigit, 2) != StringRef::npos) ToolChain.getDriver().Diag(diag::err_drv_unsupported_option_argument) << A->getSpelling() << BuildId; else { std::string LinkerFlag = "-bdbg:ldrinfo:xcoff_binary_id:0x"; if (BuildId.size() % 2) // Prepend a 0 if odd number of digits. LinkerFlag += "0"; LinkerFlag += BuildId.drop_front(2).lower(); CmdArgs.push_back(Args.MakeArgString(LinkerFlag)); } } // Specify linker output file. assert((Output.isFilename() || Output.isNothing()) && "Invalid output."); if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); } // Set linking mode (i.e., 32/64-bit) and the address of // text and data sections based on arch bit width. if (IsArch32Bit) { CmdArgs.push_back("-b32"); CmdArgs.push_back("-bpT:0x10000000"); CmdArgs.push_back("-bpD:0x20000000"); } else { // Must be 64-bit, otherwise asserted already. CmdArgs.push_back("-b64"); CmdArgs.push_back("-bpT:0x100000000"); CmdArgs.push_back("-bpD:0x110000000"); } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles, options::OPT_shared, options::OPT_r)) { auto getCrt0Basename = [&Args, IsArch32Bit] { if (Arg *A = Args.getLastArgNoClaim(options::OPT_p, options::OPT_pg)) { // Enable gprofiling when "-pg" is specified. if (A->getOption().matches(options::OPT_pg)) return IsArch32Bit ? "gcrt0.o" : "gcrt0_64.o"; // Enable profiling when "-p" is specified. return IsArch32Bit ? "mcrt0.o" : "mcrt0_64.o"; } return IsArch32Bit ? "crt0.o" : "crt0_64.o"; }; CmdArgs.push_back( Args.MakeArgString(ToolChain.GetFilePath(getCrt0Basename()))); CmdArgs.push_back(Args.MakeArgString( ToolChain.GetFilePath(IsArch32Bit ? "crti.o" : "crti_64.o"))); } // Collect all static constructor and destructor functions in both C and CXX // language link invocations. This has to come before AddLinkerInputs as the // implied option needs to precede any other '-bcdtors' settings or // '-bnocdtors' that '-Wl' might forward. CmdArgs.push_back("-bcdtors:all:0:s"); // Specify linker input file(s). AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); if (D.isUsingLTO()) { assert(!Inputs.empty() && "Must have at least one input."); // Find the first filename InputInfo object. auto Input = llvm::find_if( Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); }); if (Input == Inputs.end()) // For a very rare case, all of the inputs to the linker are // InputArg. If that happens, just use the first InputInfo. Input = Inputs.begin(); addLTOOptions(ToolChain, Args, CmdArgs, Output, *Input, D.getLTOMode() == LTOK_Thin); } if (Args.hasArg(options::OPT_shared) && !hasExportListLinkerOpts(CmdArgs)) { const char *CreateExportListExec = Args.MakeArgString( path::parent_path(ToolChain.getDriver().ClangExecutable) + "/llvm-nm"); ArgStringList CreateExportCmdArgs; std::string CreateExportListPath = C.getDriver().GetTemporaryPath("CreateExportList", "exp"); const char *ExportList = C.addTempFile(C.getArgs().MakeArgString(CreateExportListPath)); for (const auto &II : Inputs) if (II.isFilename()) CreateExportCmdArgs.push_back(II.getFilename()); CreateExportCmdArgs.push_back("--export-symbols"); CreateExportCmdArgs.push_back("-X"); if (IsArch32Bit) { CreateExportCmdArgs.push_back("32"); } else { // Must be 64-bit, otherwise asserted already. CreateExportCmdArgs.push_back("64"); } auto ExpCommand = std::make_unique( JA, *this, ResponseFileSupport::None(), CreateExportListExec, CreateExportCmdArgs, Inputs, Output); ExpCommand->setRedirectFiles( {std::nullopt, std::string(ExportList), std::nullopt}); C.addCommand(std::move(ExpCommand)); CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-bE:") + ExportList)); } // Add directory to library search path. Args.AddAllArgs(CmdArgs, options::OPT_L); if (!Args.hasArg(options::OPT_r)) { ToolChain.AddFilePathLibArgs(Args, CmdArgs); ToolChain.addProfileRTLibs(Args, CmdArgs); if (getToolChain().ShouldLinkCXXStdlib(Args)) getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { AddRunTimeLibs(ToolChain, D, CmdArgs, Args); // Add OpenMP runtime if -fopenmp is specified. if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) { switch (ToolChain.getDriver().getOpenMPRuntime(Args)) { case Driver::OMPRT_OMP: CmdArgs.push_back("-lomp"); break; case Driver::OMPRT_IOMP5: CmdArgs.push_back("-liomp5"); break; case Driver::OMPRT_GOMP: CmdArgs.push_back("-lgomp"); break; case Driver::OMPRT_Unknown: // Already diagnosed. break; } } // Support POSIX threads if "-pthreads" or "-pthread" is present. if (Args.hasArg(options::OPT_pthreads, options::OPT_pthread)) CmdArgs.push_back("-lpthreads"); if (D.CCCIsCXX()) CmdArgs.push_back("-lm"); CmdArgs.push_back("-lc"); if (Args.hasArgNoClaim(options::OPT_p, options::OPT_pg)) { CmdArgs.push_back(Args.MakeArgString((llvm::Twine("-L") + D.SysRoot) + "/lib/profiled")); CmdArgs.push_back(Args.MakeArgString((llvm::Twine("-L") + D.SysRoot) + "/usr/lib/profiled")); } } } if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); addFortranRuntimeLibs(ToolChain, Args, CmdArgs); CmdArgs.push_back("-lm"); CmdArgs.push_back("-lpthread"); } const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath()); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } /// AIX - AIX tool chain which can call as(1) and ld(1) directly. AIX::AIX(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : ToolChain(D, Triple, Args) { getProgramPaths().push_back(getDriver().Dir); ParseInlineAsmUsingAsmParser = Args.hasFlag( options::OPT_fintegrated_as, options::OPT_fno_integrated_as, true); getLibraryPaths().push_back(getDriver().SysRoot + "/usr/lib"); } // Returns the effective header sysroot path to use. // This comes from either -isysroot or --sysroot. llvm::StringRef AIX::GetHeaderSysroot(const llvm::opt::ArgList &DriverArgs) const { if (DriverArgs.hasArg(options::OPT_isysroot)) return DriverArgs.getLastArgValue(options::OPT_isysroot); if (!getDriver().SysRoot.empty()) return getDriver().SysRoot; return "/"; } void AIX::AddOpenMPIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { // Add OpenMP include paths if -fopenmp is specified. if (DriverArgs.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) { SmallString<128> PathOpenMP; switch (getDriver().getOpenMPRuntime(DriverArgs)) { case Driver::OMPRT_OMP: PathOpenMP = GetHeaderSysroot(DriverArgs); llvm::sys::path::append(PathOpenMP, "opt/IBM/openxlCSDK", "include", "openmp"); addSystemInclude(DriverArgs, CC1Args, PathOpenMP.str()); break; case Driver::OMPRT_IOMP5: case Driver::OMPRT_GOMP: case Driver::OMPRT_Unknown: // Unknown / unsupported include paths. break; } } } void AIX::AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { // Return if -nostdinc is specified as a driver option. if (DriverArgs.hasArg(options::OPT_nostdinc)) return; llvm::StringRef Sysroot = GetHeaderSysroot(DriverArgs); const Driver &D = getDriver(); if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { SmallString<128> P(D.ResourceDir); // Add the PowerPC intrinsic headers (/include/ppc_wrappers) path::append(P, "include", "ppc_wrappers"); addSystemInclude(DriverArgs, CC1Args, P); // Add the Clang builtin headers (/include) addSystemInclude(DriverArgs, CC1Args, path::parent_path(P.str())); } // Add the include directory containing omp.h. This needs to be before // adding the system include directory because other compilers put their // omp.h in /usr/include. AddOpenMPIncludeArgs(DriverArgs, CC1Args); // Return if -nostdlibinc is specified as a driver option. if (DriverArgs.hasArg(options::OPT_nostdlibinc)) return; // Add /usr/include. SmallString<128> UP(Sysroot); path::append(UP, "/usr/include"); addSystemInclude(DriverArgs, CC1Args, UP.str()); } void AIX::AddClangCXXStdlibIncludeArgs( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { if (DriverArgs.hasArg(options::OPT_nostdinc) || DriverArgs.hasArg(options::OPT_nostdincxx) || DriverArgs.hasArg(options::OPT_nostdlibinc)) return; switch (GetCXXStdlibType(DriverArgs)) { case ToolChain::CST_Libstdcxx: llvm::report_fatal_error( "picking up libstdc++ headers is unimplemented on AIX"); case ToolChain::CST_Libcxx: { llvm::StringRef Sysroot = GetHeaderSysroot(DriverArgs); SmallString<128> PathCPP(Sysroot); llvm::sys::path::append(PathCPP, "opt/IBM/openxlCSDK", "include", "c++", "v1"); addSystemInclude(DriverArgs, CC1Args, PathCPP.str()); // Required in order to suppress conflicting C++ overloads in the system // libc headers that were used by XL C++. CC1Args.push_back("-D__LIBC_NO_CPP_MATH_OVERLOADS__"); return; } } llvm_unreachable("Unexpected C++ library type; only libc++ is supported."); } void AIX::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { switch (GetCXXStdlibType(Args)) { case ToolChain::CST_Libstdcxx: llvm::report_fatal_error("linking libstdc++ unimplemented on AIX"); case ToolChain::CST_Libcxx: CmdArgs.push_back("-lc++"); if (Args.hasArg(options::OPT_fexperimental_library)) CmdArgs.push_back("-lc++experimental"); CmdArgs.push_back("-lc++abi"); return; } llvm_unreachable("Unexpected C++ library type; only libc++ is supported."); } // This function processes all the mtocdata options to build the final // simplified toc data options to pass to CC1. static void addTocDataOptions(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1Args, const Driver &D) { // Check the global toc-data setting. The default is -mno-tocdata. // To enable toc-data globally, -mtocdata must be specified. // Additionally, it must be last to take effect. const bool TOCDataGloballyinEffect = [&Args]() { if (const Arg *LastArg = Args.getLastArg(options::OPT_mtocdata, options::OPT_mno_tocdata)) return LastArg->getOption().matches(options::OPT_mtocdata); else return false; }(); enum TOCDataSetting { AddressInTOC = 0, // Address of the symbol stored in the TOC. DataInTOC = 1 // Symbol defined in the TOC. }; const TOCDataSetting DefaultTocDataSetting = TOCDataGloballyinEffect ? DataInTOC : AddressInTOC; // Process the list of variables in the explicitly specified options // -mtocdata= and -mno-tocdata= to see which variables are opposite to // the global setting of tocdata in TOCDataGloballyinEffect. // Those that have the opposite setting to TOCDataGloballyinEffect, are added // to ExplicitlySpecifiedGlobals. std::set ExplicitlySpecifiedGlobals; for (const auto Arg : Args.filtered(options::OPT_mtocdata_EQ, options::OPT_mno_tocdata_EQ)) { TOCDataSetting ArgTocDataSetting = Arg->getOption().matches(options::OPT_mtocdata_EQ) ? DataInTOC : AddressInTOC; if (ArgTocDataSetting != DefaultTocDataSetting) for (const char *Val : Arg->getValues()) ExplicitlySpecifiedGlobals.insert(Val); else for (const char *Val : Arg->getValues()) ExplicitlySpecifiedGlobals.erase(Val); } auto buildExceptionList = [](const std::set &ExplicitValues, const char *OptionSpelling) { std::string Option(OptionSpelling); bool IsFirst = true; for (const auto &E : ExplicitValues) { if (!IsFirst) Option += ","; IsFirst = false; Option += E.str(); } return Option; }; // Pass the final tocdata options to CC1 consisting of the default // tocdata option (-mtocdata/-mno-tocdata) along with the list // option (-mno-tocdata=/-mtocdata=) if there are any explicitly specified // variables which would be exceptions to the default setting. const char *TocDataGlobalOption = TOCDataGloballyinEffect ? "-mtocdata" : "-mno-tocdata"; CC1Args.push_back(TocDataGlobalOption); const char *TocDataListOption = TOCDataGloballyinEffect ? "-mno-tocdata=" : "-mtocdata="; if (!ExplicitlySpecifiedGlobals.empty()) CC1Args.push_back(Args.MakeArgString(llvm::Twine( buildExceptionList(ExplicitlySpecifiedGlobals, TocDataListOption)))); } void AIX::addClangTargetOptions( const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadingKind) const { Args.AddLastArg(CC1Args, options::OPT_mignore_xcoff_visibility); Args.AddLastArg(CC1Args, options::OPT_mdefault_visibility_export_mapping_EQ); Args.addOptInFlag(CC1Args, options::OPT_mxcoff_roptr, options::OPT_mno_xcoff_roptr); // Forward last mtocdata/mno_tocdata options to -cc1. if (Args.hasArg(options::OPT_mtocdata_EQ, options::OPT_mno_tocdata_EQ, options::OPT_mtocdata)) addTocDataOptions(Args, CC1Args, getDriver()); if (Args.hasFlag(options::OPT_fxl_pragma_pack, options::OPT_fno_xl_pragma_pack, true)) CC1Args.push_back("-fxl-pragma-pack"); // Pass "-fno-sized-deallocation" only when the user hasn't manually enabled // or disabled sized deallocations. if (!Args.getLastArgNoClaim(options::OPT_fsized_deallocation, options::OPT_fno_sized_deallocation)) CC1Args.push_back("-fno-sized-deallocation"); } void AIX::addProfileRTLibs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { if (needsProfileRT(Args)) { // Add linker option -u__llvm_profile_runtime to cause runtime // initialization to occur. CmdArgs.push_back(Args.MakeArgString( Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); if (const auto *A = Args.getLastArgNoClaim(options::OPT_fprofile_update_EQ)) { StringRef Val = A->getValue(); if (Val == "atomic" || Val == "prefer-atomic") CmdArgs.push_back("-latomic"); } } ToolChain::addProfileRTLibs(Args, CmdArgs); } ToolChain::CXXStdlibType AIX::GetDefaultCXXStdlibType() const { return ToolChain::CST_Libcxx; } ToolChain::RuntimeLibType AIX::GetDefaultRuntimeLibType() const { return ToolChain::RLT_CompilerRT; } auto AIX::buildAssembler() const -> Tool * { return new aix::Assembler(*this); } auto AIX::buildLinker() const -> Tool * { return new aix::Linker(*this); }