diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 2e571ee33caf..8f532a63f9e0 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -279,6 +279,11 @@ def err_cpu_unsupported_isa def err_arch_unsupported_isa : Error<"architecture '%0' does not support '%1' execution mode">; +def err_zos_target_release_discontinued + : Error<"z/OS target level \"%0\" is discontinued">; +def err_zos_target_unrecognized_release + : Error<"z/OS target level \"%0\" is invalid">; + def err_drv_I_dash_not_supported : Error< "'%0' not supported, please use -iquote instead">; def err_drv_unknown_argument : Error<"unknown argument: '%0'">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index e6932821d8af..e521cbf678d9 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4728,6 +4728,7 @@ def mwatchsimulator_version_min_EQ : Joined<["-"], "mwatchsimulator-version-min= def march_EQ : Joined<["-"], "march=">, Group, Flags<[TargetSpecific]>, Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, HelpText<"For a list of available architectures for the target use '-mcpu=help'">; +def mzos_target_EQ : Joined<["-"], "mzos-target=">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the z/OS release of the runtime environment">; def masm_EQ : Joined<["-"], "masm=">, Group, Visibility<[ClangOption, FlangOption]>; def inline_asm_EQ : Joined<["-"], "inline-asm=">, Group, Visibility<[ClangOption, CC1Option]>, diff --git a/clang/lib/Basic/Targets/SystemZ.cpp b/clang/lib/Basic/Targets/SystemZ.cpp index 0e9a61c6a01c..e15563fb3a78 100644 --- a/clang/lib/Basic/Targets/SystemZ.cpp +++ b/clang/lib/Basic/Targets/SystemZ.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/MacroBuilder.h" #include "clang/Basic/TargetBuiltins.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -178,6 +179,21 @@ void SystemZTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__VX__"); if (Opts.ZVector) Builder.defineMacro("__VEC__", "10305"); + + /* Set __TARGET_LIB__ only if a value was given. If no value was given */ + /* we rely on the LE headers to define __TARGET_LIB__. */ + if (!getTriple().getOSVersion().empty()) { + llvm::VersionTuple V = getTriple().getOSVersion(); + // Create string with form: 0xPVRRMMMM, where P=4 + std::string Str("0x"); + unsigned int Librel = 0x40000000; + Librel |= V.getMajor() << 24; + Librel |= (V.getMinor() ? V.getMinor().value() : 1) << 16; + Librel |= V.getSubminor() ? V.getSubminor().value() : 0; + Str += llvm::utohexstr(Librel); + + Builder.defineMacro("__TARGET_LIB__", Str.c_str()); + } } llvm::SmallVector diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 50941d2aaa42..eca96c1cce7f 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -564,6 +564,72 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const { return DAL; } +static void setZosTargetVersion(const Driver &D, llvm::Triple &Target, + StringRef ArgTarget) { + + static bool BeSilent = false; + auto IsTooOldToBeSupported = [](int v, int r) -> bool { + return ((v < 2) || ((v == 2) && (r < 4))); + }; + + /* expect CURRENT, zOSV2R[45], or 0xnnnnnnnn */ + if (ArgTarget.equals_insensitive("CURRENT")) { + /* If the user gives CURRENT, then we rely on the LE to set */ + /* __TARGET_LIB__. There's nothing more we need to do. */ + } else { + unsigned int Version = 0; + unsigned int Release = 0; + unsigned int Modification = 0; + bool IsOk = true; + llvm::Regex ZOsvRegex("[zZ][oO][sS][vV]([0-9])[rR]([0-9])"); + llvm::Regex HexRegex( + "0x4" /* product */ + "([0-9a-fA-F])" /* version */ + "([0-9a-fA-F][0-9a-fA-F])" /* release */ + "([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])" /* modification */); + SmallVector Matches; + + if (ZOsvRegex.match(ArgTarget, &Matches)) { + Matches[1].getAsInteger(10, Version); + Matches[2].getAsInteger(10, Release); + Modification = 0; + if (IsTooOldToBeSupported(Version, Release)) { + if (!BeSilent) + D.Diag(diag::err_zos_target_release_discontinued) << ArgTarget; + IsOk = false; + } + } else if (HexRegex.match(ArgTarget, &Matches)) { + Matches[1].getAsInteger(16, Version); + Matches[2].getAsInteger(16, Release); + Matches[3].getAsInteger(16, Modification); + if (IsTooOldToBeSupported(Version, Release)) { + if (!BeSilent) + D.Diag(diag::err_zos_target_release_discontinued) << ArgTarget; + IsOk = false; + } + } else { + /* something else: need to report an error */ + if (!BeSilent) + D.Diag(diag::err_zos_target_unrecognized_release) << ArgTarget; + IsOk = false; + } + + if (IsOk) { + llvm::VersionTuple V(Version, Release, Modification); + llvm::VersionTuple TV = Target.getOSVersion(); + // The goal is to pick the minimally supported version of + // the OS. Pick the lesser as the target. + if (TV.empty() || V < TV) { + SmallString<16> Str; + Str = llvm::Triple::getOSTypeName(Target.getOS()); + Str += V.getAsString(); + Target.setOSName(Str); + } + } + } + BeSilent = true; +} + /// Compute target triple from args. /// /// This routine provides the logic to compute a target triple from various @@ -689,6 +755,12 @@ static llvm::Triple computeTargetTriple(const Driver &D, } } + if (Target.isOSzOS()) { + if ((A = Args.getLastArg(options::OPT_mzos_target_EQ))) { + setZosTargetVersion(D, Target, A->getValue()); + } + } + // Handle -miamcu flag. if (Args.hasFlag(options::OPT_miamcu, options::OPT_mno_iamcu, false)) { if (Target.get32BitArchVariant().getArch() != llvm::Triple::x86) diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 56bc500da66b..65acbe8a9dbe 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -854,9 +854,25 @@ ToolChain::getTargetSubDirPath(StringRef BaseDir) const { return {}; }; - if (auto Path = getPathForTriple(getTriple())) + const llvm::Triple &T = getTriple(); + if (auto Path = getPathForTriple(T)) return *Path; + if (T.isOSzOS() && + (!T.getOSVersion().empty() || !T.getEnvironmentVersion().empty())) { + // Build the triple without version information + const llvm::Triple &TripleWithoutVersion = + (T.hasEnvironment() + ? llvm::Triple( + T.getArchName(), T.getVendorName(), + llvm::Triple::getOSTypeName(T.getOS()), + llvm::Triple::getEnvironmentTypeName(T.getEnvironment())) + : llvm::Triple(T.getArchName(), T.getVendorName(), + llvm::Triple::getOSTypeName(T.getOS()))); + if (auto Path = getPathForTriple(TripleWithoutVersion)) + return *Path; + } + // When building with per target runtime directories, various ways of naming // the Arm architecture may have been normalised to simply "arm". // For example "armv8l" (Armv8 AArch32 little endian) is replaced with "arm". @@ -872,14 +888,14 @@ ToolChain::getTargetSubDirPath(StringRef BaseDir) const { // // M profile Arm is bare metal and we know they will not be using the per // target runtime directory layout. - if (getTriple().getArch() == Triple::arm && !getTriple().isArmMClass()) { - llvm::Triple ArmTriple = getTriple(); + if (T.getArch() == Triple::arm && !T.isArmMClass()) { + llvm::Triple ArmTriple = T; ArmTriple.setArch(Triple::arm); if (auto Path = getPathForTriple(ArmTriple)) return *Path; } - if (getTriple().isAndroid()) + if (T.isAndroid()) return getFallbackAndroidTargetPath(BaseDir); return {}; diff --git a/clang/test/Preprocessor/zos-target.c b/clang/test/Preprocessor/zos-target.c new file mode 100644 index 000000000000..9768812948bf --- /dev/null +++ b/clang/test/Preprocessor/zos-target.c @@ -0,0 +1,18 @@ +// REQUIRES: target={{s390x-ibm-zos}} + +// In this case we expect __TARGET_LIB__ not to be defined because we don't +// include any files here, and in particular, any from the LE. +// RUN: %clang -mzos-target=current -dM -E %s | FileCheck --check-prefix=CURRENT %s +// CURRENT-NOT: #define __TARGET_LIB__ + +// RUN: %clang -mzos-target=zosv2r5 -dM -E %s | FileCheck --check-prefix=ZOSVR %s +// ZOSVR: #define __TARGET_LIB__ 0x42050000 + +// RUN: %clang -mzos-target=0x4204001f -dM -E %s | FileCheck --check-prefix=HEX %s +// HEX: #define __TARGET_LIB__ 0x4204001F + +// RUN: not %clang -mzos-target=0x42010000 -dM -E %s 2>&1 | FileCheck --check-prefix=ERR-DISCONTINUED %s +// ERR-DISCONTINUED: z/OS target level "0x42010000" is discontinued. Unexpected behavior might occur if an out-of-support target level is specified. Use z/OS target level "zosv2r4", or later instead + +// RUN: not %clang -mzos-target=Rubbish -dM -E %s 2>&1 | FileCheck --check-prefix=ERR-INVALID-ARG %s +// ERR-INVALID-ARG: "Rubbish" is not recognized as a valid z/OS target level. The z/OS target level must be "current", or of the form "zosvVrR", where "V" is the version and "R" is the release, or given as a "0x"-prefixed eight digit hexadecimal value