[compiler-rt] [llvm] {JITLink] Add initial TLS support for SystemZ (PR #170706)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 4 09:45:56 PST 2025
https://github.com/anoopkg6 created https://github.com/llvm/llvm-project/pull/170706
This patch supports R_390_TLS_GD64 relocation for SystemZ.
>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] 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));
}
}
More information about the llvm-commits
mailing list