[llvm] [SystemZ][z/OS] Keep read-only data in the code section only if local and without relocations (PR #226683)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 26 04:48:48 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-systemz
Author: MMS IT GmbH (mms-it-ch)
<details>
<summary>Changes</summary>
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: `getKindForGlobal` returns `ReadOnly`, but 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.
Add `TargetLoweringObjectFileGOFF::isReadOnlyInCodeSection()` and use it both for the section selection and in `SystemZSubtarget::isAddressedViaADA()`, 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`. Both reproducers from #<!-- -->226659 (including the comment) bind and run on z/OS 3.1; GMP 6.3.0 and GnuCOBOL 3.2 libcob bind and run.
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)
---
Full diff: https://github.com/llvm/llvm-project/pull/226683.diff
5 Files Affected:
- (modified) llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h (+7)
- (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+12-1)
- (modified) llvm/lib/Target/SystemZ/SystemZSubtarget.cpp (+2-5)
- (added) llvm/test/CodeGen/SystemZ/zos-readonly-addressing.ll (+42)
- (added) llvm/test/CodeGen/SystemZ/zos-section-readonly-extern.ll (+25)
``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
index e631d820eb637..390a8441be1e0 100644
--- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
+++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
@@ -331,6 +331,13 @@ class LLVM_ABI TargetLoweringObjectFileGOFF : public TargetLoweringObjectFile {
void getModuleMetadata(Module &M) override;
+ /// Returns true if the read-only global \p GO of kind \p Kind is placed into
+ /// the code section and can therefore be addressed PC-relative. This is only
+ /// the case for local data whose initializer needs no relocations: references
+ /// from other translation units are always parts in the WSA, and function
+ /// pointers point to function descriptors in the WSA.
+ static bool isReadOnlyInCodeSection(const GlobalObject *GO, SectionKind Kind);
+
bool shouldPutJumpTableInFunctionSection(bool UsesLabelDifference,
const Function &F) const override;
MCSection *getSectionForConstant(const DataLayout &DL, SectionKind Kind,
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 8b81aaf9d12d4..11fd38d6f7759 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2921,11 +2921,22 @@ MCSection *TargetLoweringObjectFileGOFF::getSectionForLSDA(
WSA);
}
+bool TargetLoweringObjectFileGOFF::isReadOnlyInCodeSection(
+ const GlobalObject *GO, SectionKind Kind) {
+ if (!Kind.isReadOnly() || !GO->hasLocalLinkage())
+ return false;
+ if (const auto *GVar = dyn_cast<GlobalVariable>(GO))
+ if (GVar->hasInitializer() && GVar->getInitializer()->needsRelocation())
+ return false;
+ return true;
+}
+
MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
auto *Symbol = TM.getSymbol(GO);
- if (Kind.isBSS() || Kind.isData() || Kind.isReadOnlyWithRel()) {
+ if (Kind.isBSS() || Kind.isData() || Kind.isReadOnlyWithRel() ||
+ (Kind.isReadOnly() && !isReadOnlyInCodeSection(GO, Kind))) {
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..d4855038492c7 100644
--- a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
@@ -99,11 +99,8 @@ 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.
+ return !TargetLoweringObjectFileGOFF::isReadOnlyInCodeSection(GO, GOKind);
}
return true;
}
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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/226683
More information about the llvm-commits
mailing list