[clang] [z/OS] Add option to target older versions of LE on z/OS (PR #123399)

Sean Perry via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 17 13:22:10 PST 2025


https://github.com/perry-ca created https://github.com/llvm/llvm-project/pull/123399

Add an option similar to the -qtarget option in XL to allow the user to say they want to be able to run the generated program on an older version of the LE environment.  This option will do two things:
- set the `__TARGET_LIBS` macro so the system headers exclude newer interfaces when targeting older environments
- set the arch level to match the minimum arch level for that older version of LE.  It some happens right now all of the supported LE versions have a the same minimum ach level so the option doesn't change this yet.  

The user can specify three different kinds of arguments:
1. -mzos-target=zosv*V*r*R* - where V & R are the version and release
2. -mzos-target=0x4vrrmmmm - v, r, m, p are the hex values for the version, release, and modlevel
3. -mzos-target=current - uses the latest version of LE the system headers have support for

>From 9f5763faa81691f540af42721147daf50042e549 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Wed, 27 Nov 2024 18:33:09 -0600
Subject: [PATCH 1/2] Add -mzos-target

---
 .../clang/Basic/DiagnosticDriverKinds.td      |  5 ++
 clang/include/clang/Driver/Options.td         |  1 +
 clang/lib/Basic/Targets/SystemZ.cpp           | 15 ++++
 clang/lib/Driver/Driver.cpp                   | 72 +++++++++++++++++++
 clang/lib/Driver/ToolChain.cpp                | 15 ++++
 clang/test/Preprocessor/zos-target.c          | 18 +++++
 6 files changed, 126 insertions(+)
 create mode 100644 clang/test/Preprocessor/zos-target.c

diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 5155b23d151c04..140bc52af12b25 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -277,6 +277,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.  Unexpected behavior might occur if an out-of-support target level is specified.  Use z/OS target level \"zOSv2r4\", or later instead">;
+def err_zos_target_unrecognized_release
+  : Error<"\"%0\" 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">;
+
 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 40fd48761928b3..ddbd857414e714 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4734,6 +4734,7 @@ def mwatchsimulator_version_min_EQ : Joined<["-"], "mwatchsimulator-version-min=
 def march_EQ : Joined<["-"], "march=">, Group<m_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<m_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the z/OS release of the runtime environment">;
 def masm_EQ : Joined<["-"], "masm=">, Group<m_Group>, Visibility<[ClangOption, FlangOption]>;
 def inline_asm_EQ : Joined<["-"], "inline-asm=">, Group<m_Group>,
   Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/Basic/Targets/SystemZ.cpp b/clang/lib/Basic/Targets/SystemZ.cpp
index 06f08db2eadd47..2c749c0ba76937 100644
--- a/clang/lib/Basic/Targets/SystemZ.cpp
+++ b/clang/lib/Basic/Targets/SystemZ.cpp
@@ -168,6 +168,21 @@ void SystemZTargetInfo::getTargetDefines(const LangOptions &Opts,
     Builder.defineMacro("__VX__");
   if (Opts.ZVector)
     Builder.defineMacro("__VEC__", "10304");
+
+  /* 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());
+  }
 }
 
 ArrayRef<Builtin::Info> SystemZTargetInfo::getTargetBuiltins() const {
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index ad14b5c9b6dc80..eebe60648ed22b 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -517,6 +517,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<StringRef> 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
@@ -638,6 +704,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 0d426a467e9a3b..24f8ba447052c1 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -846,6 +846,21 @@ ToolChain::getTargetSubDirPath(StringRef BaseDir) const {
   if (auto Path = getPathForTriple(getTriple()))
     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".
diff --git a/clang/test/Preprocessor/zos-target.c b/clang/test/Preprocessor/zos-target.c
new file mode 100644
index 00000000000000..9768812948bfc1
--- /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

>From 5bd29a0c12926e136cf15f18837f37bbdb9bbe0c Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 17 Jan 2025 15:01:02 -0500
Subject: [PATCH 2/2] build on latest source

---
 clang/lib/Basic/Targets/SystemZ.cpp | 1 +
 clang/lib/Driver/ToolChain.cpp      | 9 +++++----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Basic/Targets/SystemZ.cpp b/clang/lib/Basic/Targets/SystemZ.cpp
index 2c749c0ba76937..2731cb596fd27a 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;
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 9a2f438cc45d22..0da0de2e254948 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -855,7 +855,8 @@ 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() &&
@@ -888,14 +889,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 {};



More information about the cfe-commits mailing list