[compiler-rt] [llvm] {JITLink] Add initial TLS support for SystemZ (PR #170706)

via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 4 11:26:42 PST 2025


https://github.com/anoopkg6 updated https://github.com/llvm/llvm-project/pull/170706

>From ea8070c2eb7614dc7fd42787c2bd06e5ada24fb9 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Thu, 4 Dec 2025 18:40:29 +0100
Subject: [PATCH 1/2] Add TLS support in jitlink for SystemZ

---
 .../cmake/Modules/AllSupportedArchDefs.cmake  |  2 +-
 compiler-rt/lib/orc/CMakeLists.txt            |  1 +
 compiler-rt/lib/orc/elfnix_tls.systemz.S      | 60 +++++++++++++
 .../TestCases/Linux/systemz/lit.local.cfg.py  |  5 ++
 .../orc/TestCases/Linux/systemz/trivial-tls.S | 84 +++++++++++++++++++
 .../llvm/ExecutionEngine/JITLink/systemz.h    | 17 +++-
 .../ExecutionEngine/JITLink/ELF_systemz.cpp   | 66 ++++++++++++++-
 llvm/lib/ExecutionEngine/JITLink/systemz.cpp  |  2 +
 .../ExecutionEngine/Orc/ELFNixPlatform.cpp    | 10 +++
 9 files changed, 243 insertions(+), 4 deletions(-)
 create mode 100644 compiler-rt/lib/orc/elfnix_tls.systemz.S
 create mode 100644 compiler-rt/test/orc/TestCases/Linux/systemz/lit.local.cfg.py
 create mode 100644 compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S

diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index f2317de8916e9..9065bb2de4943 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -114,7 +114,7 @@ set(ALL_SHADOWCALLSTACK_SUPPORTED_ARCH ${ARM64})
 
 if (UNIX)
   if (OS_NAME MATCHES "Linux")
-    set(ALL_ORC_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM32} ${PPC64} ${LOONGARCH64})
+    set(ALL_ORC_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM32} ${PPC64} ${LOONGARCH64} ${S390X})
   else()
     set(ALL_ORC_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM32})
   endif()
diff --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt
index b8d1b03b788c9..649d988d9d608 100644
--- a/compiler-rt/lib/orc/CMakeLists.txt
+++ b/compiler-rt/lib/orc/CMakeLists.txt
@@ -119,6 +119,7 @@ else() # not Apple
       elfnix_tls.x86-64.S
       elfnix_tls.aarch64.S
       elfnix_tls.ppc64.S
+      elfnix_tls.systemz.S
       sysv_reenter.arm64.S
       sysv_reenter.x86-64.S
       )
diff --git a/compiler-rt/lib/orc/elfnix_tls.systemz.S b/compiler-rt/lib/orc/elfnix_tls.systemz.S
new file mode 100644
index 0000000000000..af38f433bca47
--- /dev/null
+++ b/compiler-rt/lib/orc/elfnix_tls.systemz.S
@@ -0,0 +1,60 @@
+
+//===-- orc_rt_elfnix_tls_systemz.s -------------------------------*- ASM -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of the ORC runtime support library.
+//
+//===----------------------------------------------------------------------===//
+
+// The special thing about the s390 TLS ABI is that we do not have the
+// standard __tls_get_addr function but the __tls_get_offset function
+// which differs in two important aspects:
+// 1) __tls_get_offset gets a got offset instead of a pointer to the
+//    tls_index structure
+// 2) __tls_get_offset returns the offset of the requested variable to
+//    the thread descriptor instead of a pointer to the variable.
+
+// The content of this file is systemz-only
+
+#if defined(__s390x__)
+
+        .text
+	// returns offset of TLV from TP in %r2, all other registers preserved
+	.globl ___orc_rt_elfnix_tls_get_offset
+___orc_rt_elfnix_tls_get_offset:
+	stmg %r6, %r15, 48(%r15)
+	aghi %r15, -240
+	std %f8, 232(%r15)
+	std %f9, 224(%r15)
+	std %f10, 216(%r15)
+	std %f11, 208(%r15)
+	std %f12, 200(%r15)
+	std %f13, 192(%r15)
+	std %f14, 184(%r15)
+	std %f15, 176(%r15)
+        // Pass pointer to tls_indexc.
+        la %r2,0(%r2,%r12)
+	brasl %r14, __orc_rt_elfnix_tls_get_addr_impl
+	// Return offset from TP.
+	ear %r0, %a0
+	sllg %r0, %r0, 32
+	ear %r0, %a1
+	sgr %r2, %r0
+	// Restoring Regsiters.
+	ld %f8, 232(%r15)
+	ld %f9, 224(%r15)
+	ld %f10, 216(%r15)
+	ld %f11, 208(%r15)
+	ld %f12, 200(%r15)
+	ld %f13, 192(%r15)
+	ld %f14, 184(%r15)
+	ld %f15, 176(%r15)
+	lmg %r6, %r15, 288(%r15)
+        br %r14
+
+#endif // defined(__s390x__)
diff --git a/compiler-rt/test/orc/TestCases/Linux/systemz/lit.local.cfg.py b/compiler-rt/test/orc/TestCases/Linux/systemz/lit.local.cfg.py
new file mode 100644
index 0000000000000..493eeca02b583
--- /dev/null
+++ b/compiler-rt/test/orc/TestCases/Linux/systemz/lit.local.cfg.py
@@ -0,0 +1,5 @@
+if config.root.host_arch != "s390x":
+    config.unsupported = True
+
+if config.target_arch != "s390x":
+    config.unsupported = True
diff --git a/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S b/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S
new file mode 100644
index 0000000000000..dac534e4d9b25
--- /dev/null
+++ b/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S
@@ -0,0 +1,84 @@
+// RUN: %clang -c -o %t %s
+// RUN: %llvm_jitlink %t
+//
+// Test that basic ELF TLS work by adding together TLSs with values
+// 0, 1, and -1, and returning the result (0 for success). This setup
+// tests both zero-initialized (.tbss) and non-zero-initialized
+// (.tdata) sections.
+
+        .section        .data.rel.ro,"aw", at progbits
+        .p2align        3, 0x0                          # -- Begin function main
+.LCPI0_0:
+        .quad   x at TLSGD
+.LCPI0_1:
+        .quad   y at TLSGD
+.LCPI0_2:
+        .quad   z at TLSGD
+
+        .text
+        .globl  main
+        .p2align        4
+        .type   main, at function
+main:                                   # @main
+# %bb.0:                                # %entry
+        stmg    %r11, %r15, 88(%r15)
+        aghi    %r15, -192
+        lgr     %r11, %r15
+        mvhi    188(%r11), 0
+        lgrl    %r2, .LCPI0_0 
+        larl    %r12, _GLOBAL_OFFSET_TABLE_
+        stg     %r12, 160(%r11)                 # 8-byte Spill
+        brasl   %r14, __tls_get_offset at PLT:tls_gdcall:x
+        lg      %r12, 160(%r11)                 # 8-byte Reload
+        ear     %r1, %a0
+                                        # implicit-def: $r0d
+        lr      %r0, %r1
+        sllg    %r1, %r0, 32
+        ear     %r0, %a1
+        lr      %r1, %r0
+        stg     %r1, 176(%r11)                  # 8-byte Spill
+        l       %r0, 0(%r2,%r1)
+        st      %r0, 172(%r11)                  # 4-byte Spill
+        lgrl    %r2, .LCPI0_1 
+        brasl   %r14, __tls_get_offset at PLT:tls_gdcall:y
+        lg      %r12, 160(%r11)                 # 8-byte Reload
+        l       %r0, 172(%r11)                  # 4-byte Reload
+        lg      %r1, 176(%r11)                  # 8-byte Reload
+        l       %r1, 0(%r2,%r1)
+        ar      %r0, %r1
+        st      %r0, 184(%r11)                  # 4-byte Spill
+        lgrl    %r2, .LCPI0_2 
+        brasl   %r14, __tls_get_offset at PLT:tls_gdcall:z
+        lg      %r1, 176(%r11)                  # 8-byte Reload
+        l       %r0, 184(%r11)                  # 4-byte Reload
+        l       %r1, 0(%r2,%r1)
+        ar      %r0, %r1
+        lgfr    %r2, %r0
+        lmg     %r11, %r15, 280(%r11)
+        br      %r14
+.Lfunc_end0:
+        .size   main, .Lfunc_end0-main
+
+
+        .type   x, at object                       # @x
+        .section        .tbss,"awT", at nobits
+        .globl  x
+        .p2align        2, 0x0
+x:
+        .long   0                               # 0x0
+        .size   x, 4
+
+        .type   y, at object                       # @y
+        .section        .tdata,"awT", at progbits
+        .globl  y
+        .p2align        2, 0x0
+y:
+        .long   1                               # 0x1
+        .size   y, 4
+
+        .type   z, at object                       # @z
+        .globl  z
+        .p2align        2, 0x0
+z:
+        .long   4294967295                      # 0xffffffff
+        .size   z, 4
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
index dde3448cd5da7..bfd22ec753074 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
@@ -507,6 +507,21 @@ enum EdgeKind_systemz : Edge::Kind {
   ///
   RequestGOTAndTransformToDelta32dbl,
 
+  /// A TLSInfo entry getter/constructor, transformed to Delta64FromGOT.
+  ///
+  /// Indicates that this edge should be transformed into a Delta64FromGOT
+  /// targeting the TLSInfo entry for the edge's current target. A TLSInfo
+  /// entry for the target should be created if one does not already exist.
+  ///
+  /// Fixup expression:
+  ///   NONE
+  ///
+  /// Errors:
+  ///   - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+  ///     phase will result in an assert/unreachable during the fixup phase.
+  ///
+  RequestTLSDescInGOTAndTransformToDelta64FromGOT,
+
   /// A 32-bit Delta to GOT base.
   ///
   /// Fixup expression:
@@ -814,8 +829,6 @@ class GOTTableManager : public TableManager<GOTTableManager> {
   static StringRef getSectionName() { return "$__GOT"; }
 
   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
-    if (E.getTarget().isDefined())
-      return false;
     Edge::Kind KindToSet = Edge::Invalid;
     switch (E.getKind()) {
     case systemz::RequestGOTAndTransformToDelta12FromGOT:
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
index 29eeecceea766..fb31baa112ae4 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
@@ -27,12 +27,67 @@ using namespace llvm::jitlink;
 namespace {
 
 constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
+constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO";
+
+// TLS Info Builder.
+class TLSInfoTableManager_ELF_systemz
+    : public TableManager<TLSInfoTableManager_ELF_systemz> {
+public:
+  static StringRef getSectionName() { return "$__TLSINFO"; }
+
+  static const uint8_t TLSInfoEntryContent[16];
+
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
+    if (E.getKind() ==
+        systemz::RequestTLSDescInGOTAndTransformToDelta64FromGOT) {
+      LLVM_DEBUG({
+        dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
+               << formatv("{0:x}", B->getFixupAddress(E)) << " ("
+               << formatv("{0:x}", B->getAddress()) << " + "
+               << formatv("{0:x}", E.getOffset()) << ")\n";
+      });
+      E.setKind(systemz::Delta64FromGOT);
+      E.setTarget(getEntryForTarget(G, E.getTarget()));
+      return true;
+    }
+    return false;
+  }
+
+  Symbol &createEntry(LinkGraph &G, Symbol &Target) {
+    // the TLS Info entry's key value will be written by the fixTLVSectionByName
+    // pass, so create mutable content.
+    auto &TLSInfoEntry = G.createMutableContentBlock(
+        getTLSInfoSection(G), G.allocateContent(getTLSInfoEntryContent()),
+        orc::ExecutorAddr(), 8, 0);
+    TLSInfoEntry.addEdge(systemz::Pointer64, 8, Target, 0);
+    return G.addAnonymousSymbol(TLSInfoEntry, 0, 16, false, false);
+  }
+
+private:
+  Section &getTLSInfoSection(LinkGraph &G) {
+    if (!TLSInfoTable)
+      TLSInfoTable = &G.createSection(getSectionName(), orc::MemProt::Read);
+    return *TLSInfoTable;
+  }
+
+  ArrayRef<char> getTLSInfoEntryContent() const {
+    return {reinterpret_cast<const char *>(TLSInfoEntryContent),
+            sizeof(TLSInfoEntryContent)};
+  }
+
+  Section *TLSInfoTable = nullptr;
+};
+
+const uint8_t TLSInfoTableManager_ELF_systemz::TLSInfoEntryContent[16] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
 Error buildTables_ELF_systemz(LinkGraph &G) {
   LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
   systemz::GOTTableManager GOT;
   systemz::PLTTableManager PLT(GOT);
-  visitExistingEdges(G, GOT, PLT);
+  TLSInfoTableManager_ELF_systemz TLSInfo;
+  visitExistingEdges(G, GOT, PLT, TLSInfo);
   return Error::success();
 }
 
@@ -329,6 +384,15 @@ class ELFLinkGraphBuilder_systemz
       Kind = systemz::Delta32dblGOTBase;
       break;
     }
+    // Tag for function call in general dynamic TLS code.
+    case ELF::R_390_TLS_GDCALL: {
+      break;
+    }
+    // Direct 64 bit for general dynamic thread local data.
+    case ELF::R_390_TLS_GD64: {
+      Kind = systemz::RequestTLSDescInGOTAndTransformToDelta64FromGOT;
+      break;
+    }
     default:
       return make_error<JITLinkError>(
           "In " + G->getName() + ": Unsupported systemz relocation type " +
diff --git a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
index f6cc29fa6e6a1..dbb924c3f9291 100644
--- a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
@@ -104,6 +104,8 @@ const char *getEdgeKindName(Edge::Kind R) {
     return "RequestGOTAndTransformToDelta12FromGOT";
   case RequestGOTAndTransformToDelta32dbl:
     return "RequestGOTAndTransformToDelta32dbl";
+  case RequestTLSDescInGOTAndTransformToDelta64FromGOT:
+    return "RequestTLSDescInGOTAndTransformToDelta64FromGOT";
   default:
     return getGenericEdgeKindName(static_cast<Edge::Kind>(R));
   }
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index 9ac8c5ef66de6..0a761290373aa 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -11,6 +11,7 @@
 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
 #include "llvm/ExecutionEngine/JITLink/loongarch.h"
 #include "llvm/ExecutionEngine/JITLink/ppc64.h"
+#include "llvm/ExecutionEngine/JITLink/systemz.h"
 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
 #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
@@ -153,6 +154,9 @@ class DSOHandleMaterializationUnit : public MaterializationUnit {
     case Triple::loongarch64:
       EdgeKind = jitlink::loongarch::Pointer64;
       break;
+    case Triple::systemz:
+      EdgeKind = jitlink::systemz::Pointer64;
+      break;
     default:
       llvm_unreachable("Unrecognized architecture");
     }
@@ -367,6 +371,7 @@ bool ELFNixPlatform::supportedTarget(const Triple &TT) {
   // right now.
   case Triple::ppc64le:
   case Triple::loongarch64:
+  case Triple::systemz:
     return true;
   default:
     return false;
@@ -983,6 +988,7 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges(
     jitlink::LinkGraph &G, JITDylib &JD) {
   auto TLSGetAddrSymbolName = G.intern("__tls_get_addr");
   auto TLSDescResolveSymbolName = G.intern("__tlsdesc_resolver");
+  auto TLSGetOffsetSymbolName = G.intern("__tls_get_offset");
   for (auto *Sym : G.external_symbols()) {
     if (Sym->getName() == TLSGetAddrSymbolName) {
       auto TLSGetAddr =
@@ -992,6 +998,10 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges(
       auto TLSGetAddr =
           MP.getExecutionSession().intern("___orc_rt_elfnix_tlsdesc_resolver");
       Sym->setName(std::move(TLSGetAddr));
+    } else if (Sym->getName() == TLSGetOffsetSymbolName) {
+      auto TLSGetAddr =
+          MP.getExecutionSession().intern("___orc_rt_elfnix_tls_get_offset");
+      Sym->setName(std::move(TLSGetAddr));
     }
   }
 

>From 32c000363516d118516a1e1911997e9934d8bed3 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Thu, 4 Dec 2025 20:24:28 +0100
Subject: [PATCH 2/2] Fixed unused ELFTLSInfoSectionName

---
 llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
index fb31baa112ae4..50acd6ea2e542 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp
@@ -33,7 +33,7 @@ constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO";
 class TLSInfoTableManager_ELF_systemz
     : public TableManager<TLSInfoTableManager_ELF_systemz> {
 public:
-  static StringRef getSectionName() { return "$__TLSINFO"; }
+  static StringRef getSectionName() { return ELFTLSInfoSectionName; }
 
   static const uint8_t TLSInfoEntryContent[16];
 



More information about the llvm-commits mailing list