[llvm] c9e13e0 - [SystemZ][z/OS] Keep read-only data in the code section only if local and without relocations (#226683)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 6 08:50:30 PDT 2026
Author: MMS IT GmbH
Date: 2026-10-06T17:50:22+02:00
New Revision: c9e13e0607d5d05e73c944cc001d86a88b5a1271
URL: https://github.com/llvm/llvm-project/commit/c9e13e0607d5d05e73c944cc001d86a88b5a1271
DIFF: https://github.com/llvm/llvm-project/commit/c9e13e0607d5d05e73c944cc001d86a88b5a1271.diff
LOG: [SystemZ][z/OS] Keep read-only data in the code section only if local and without relocations (#226683)
Read-only data stayed in the code section unless it was classified as
`ReadOnlyWithRel`. This is wrong in two cases:
- Externally visible read-only data: references from other translation
units are always parts in the WSA, so the binder cannot resolve them
against a label in the code section (IEW2456E, name space 3).
- Local read-only data with relocations to local symbols, such as a
`static const` table of pointers to internal functions: with the z/OS
relocation model `DynamicNoPIC`, `getKindForGlobal` returns `ReadOnly`
for an initializer that needs only local relocations. The entries point
to function descriptors in the WSA, and the binder rejects an address
constant from the code section into the deferred WSA (IEW2353E, error
code 250001). #223055 does not cover this case.
This patch:
- In `getKindForGlobal`, returns `ReadOnlyWithRel` on GOFF for every
initializer that needs a relocation, so such data goes to the WSA.
- In `TargetLoweringObjectFileGOFF::SelectSectionForGlobal`, keeps
`ReadOnly` data in the code section only if it has local linkage;
otherwise it becomes a part in the WSA.
- In `SystemZSubtarget::isAddressedViaADA`, applies the same rule, so
that the addressing always matches the placement (otherwise data moved
to the WSA would still be addressed with `LARL`, giving "relative
immediate relocation section mismatch").
Local read-only data without relocations stays in the code section.
New tests `zos-section-readonly-extern.ll` and
`zos-readonly-addressing.ll`. With the first version of this patch (same
placement, the check was in a helper in the GOFF object file), both
reproducers from #226659 (including the comment) bound and ran on z/OS
3.1, as did GMP 6.3.0 and GnuCOBOL 3.2 libcob.
Tests: `llvm-lit test/CodeGen/SystemZ test/MC/SystemZ test/MC/GOFF`
passes (1305 passed, 19 unsupported).
Fixes #226659.
Assisted-by: Claude Code (Anthropic)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Added:
llvm/test/CodeGen/SystemZ/zos-readonly-addressing.ll
llvm/test/CodeGen/SystemZ/zos-section-readonly-extern.ll
Modified:
llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
llvm/lib/Target/TargetLoweringObjectFile.cpp
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index bb5d5d84ec5dc..16912083af9f9 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2928,7 +2928,10 @@ MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
auto *Symbol = TM.getSymbol(GO);
- if (Kind.isBSS() || Kind.isData() || Kind.isReadOnlyWithRel()) {
+ // Read-only data stays in the code section only if it is local: references
+ // from other translation units are always parts in the WSA.
+ if (Kind.isBSS() || Kind.isData() || Kind.isReadOnlyWithRel() ||
+ (Kind.isReadOnly() && !GO->hasLocalLinkage())) {
GOFF::ESDBindingScope PRBindingScope =
GO->hasExternalLinkage()
? (GO->hasDefaultVisibility() ? GOFF::ESD_BSC_ImportExport
diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
index 942ef88385620..0f7b086606478 100644
--- a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
@@ -99,11 +99,10 @@ bool SystemZSubtarget::isAddressedViaADA(const GlobalValue *GV) const {
SectionKind GOKind = TargetLoweringObjectFile::getKindForGlobal(
GO, TLInfo.getTargetMachine());
- if (!GOKind.isReadOnly()) {
- return true;
- }
-
- return false; // R/O variable with multiple of 2 byte alignment
+ // Must match the section selection in TargetLoweringObjectFileGOFF.
+ if (GOKind.isReadOnly() && GO->hasLocalLinkage())
+ return false;
+ return true;
}
return true;
}
diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp
index b94c374e1dbd5..ed0517f939dd2 100644
--- a/llvm/lib/Target/TargetLoweringObjectFile.cpp
+++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp
@@ -353,6 +353,14 @@ SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalObject *GO,
if (containsConstantPtrAuth(C))
return SectionKind::getReadOnlyWithRel();
+ // GOFF: data in the code section is shared by all instances of the
+ // program, while function descriptors and other data live in the WSA
+ // of each instance. The binder rejects a relocation in the code section
+ // that refers to the WSA, even a local one, so every initializer that
+ // needs a relocation goes to the WSA.
+ if (TM.getTargetTriple().isOSBinFormatGOFF())
+ return SectionKind::getReadOnlyWithRel();
+
// In static, ROPI and RWPI relocation models, the linker will resolve
// all addresses, so the relocation entries will actually be constants by
// the time the app starts up. However, we can't put this into a
diff --git a/llvm/test/CodeGen/SystemZ/zos-readonly-addressing.ll b/llvm/test/CodeGen/SystemZ/zos-readonly-addressing.ll
new file mode 100644
index 0000000000000..0d7932217e39f
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-readonly-addressing.ll
@@ -0,0 +1,42 @@
+; Read-only data stays in the code section (and is addressed PC-relative) only
+; if it is local and its initializer needs no relocations. Externally visible
+; read-only data and tables of function pointers (which point to function
+; descriptors in the WSA) are parts in the WSA and are addressed via the ADA.
+;
+; RUN: llc < %s -mtriple=s390x-ibm-zos | FileCheck %s
+
+; CHECK-LABEL: useext DS 0H
+; CHECK: lg 2,0(5)
+; CHECK-NOT: larl
+; CHECK: b 2(7)
+; CHECK-LABEL: useloc DS 0H
+; CHECK: larl 2,loc
+; CHECK: b 2(7)
+; CHECK-LABEL: usetbl DS 0H
+; CHECK: lg 1,8(5)
+; CHECK-NOT: larl
+; CHECK: b 2(7)
+; CHECK: ext CSECT
+; CHECK-NEXT: C_WSA64 CATTR {{.*}}PART(ext)
+; CHECK: loc XATTR LINKAGE(XPLINK),REFERENCE(DATA),SCOPE(SECTION)
+; CHECK-NEXT: loc DS 0H
+; CHECK: tbl CSECT
+; CHECK-NEXT: C_WSA64 CATTR {{.*}}PART(tbl)
+ at ext = constant [2 x i32] [i32 3, i32 4], align 4
+ at loc = internal constant [2 x i32] [i32 5, i32 6], align 4
+ at tbl = internal constant [1 x ptr] [ptr @f], align 8
+define internal void @f() { ret void }
+define signext i32 @useext(i64 %i) {
+ %p = getelementptr inbounds [2 x i32], ptr @ext, i64 0, i64 %i
+ %v = load i32, ptr %p, align 4
+ ret i32 %v
+}
+define signext i32 @useloc(i64 %i) {
+ %p = getelementptr inbounds [2 x i32], ptr @loc, i64 0, i64 %i
+ %v = load i32, ptr %p, align 4
+ ret i32 %v
+}
+define ptr @usetbl() {
+ %v = load ptr, ptr @tbl, align 8
+ ret ptr %v
+}
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-readonly-extern.ll b/llvm/test/CodeGen/SystemZ/zos-section-readonly-extern.ll
new file mode 100644
index 0000000000000..c75ff2a576cf5
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-section-readonly-extern.ll
@@ -0,0 +1,25 @@
+; Externally visible read-only data is placed into the WSA (C_WSA64), because
+; a reference from another translation unit is always a part in the WSA.
+; Local read-only data stays in the code section.
+;
+; RUN: llc < %s -mtriple=s390x-ibm-zos | FileCheck %s
+
+; CHECK: tab CSECT
+; CHECK-NEXT: C_WSA64 CATTR {{.*}}PART(t
+; CHECK: tab XATTR LINKAGE(XPLINK),REFERENCE(DATA),SCOPE(LIBRARY)
+; CHECK: exp CSECT
+; CHECK-NEXT: C_WSA64 CATTR {{.*}}PART(e
+; CHECK: exp XATTR LINKAGE(XPLINK),REFERENCE(DATA),SCOPE(EXPORT)
+; CHECK: C_CODE64 CATTR
+; CHECK: loc XATTR LINKAGE(XPLINK),REFERENCE(DATA),SCOPE(SECTION)
+; CHECK-NEXT: loc DS 0H
+
+ at tab = hidden constant [2 x i32] [i32 1, i32 2], align 4
+ at exp = constant [2 x i32] [i32 3, i32 4], align 4
+ at loc = internal constant [2 x i32] [i32 5, i32 6], align 4
+
+define signext i32 @get(i64 %i) {
+ %p = getelementptr inbounds [2 x i32], ptr @loc, i64 0, i64 %i
+ %v = load i32, ptr %p, align 4
+ ret i32 %v
+}
More information about the llvm-commits
mailing list