[llvm] Arm ropi rwpi dynreloc consts (PR #207931)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 7 01:48:16 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-arm
Author: vjonaswolf
<details>
<summary>Changes</summary>
## Problem
Under the combined ROPI-RWPI relocation model, a `const` global whose initializer requires a dynamic relocation (a vtable, a table of pointers to other globals, …) is today:
1. **placed** in `.rodata` — `TargetLoweringObjectFile::getKindForGlobal` demotes `ReadOnlyWithRel` to `ReadOnly` for ROPI-RWPI on the assumption that "the linker will resolve all addresses", and
2. **addressed** PC-relative — `ARMTargetLowering::isReadOnly` returns `isConstant()` without considering the initializer's relocations.
That assumption only holds when the image's final placement is fixed at static-link time. For placement-independent images (e.g. XIP firmware whose flash slot and RAM window are assigned at load time — the use case ROPI-RWPI exists for), such constants cannot be finalized by the static linker: they need a loader fixup, which requires them to live in a writable, relocation-bearing section. Keeping them in `.rodata` produces images where the pointer values inside the constant are stale.
The same placement/addressing mismatch class has been observed downstream as rust-lang/rust#<!-- -->95871 (dyn-trait vtables under ROPI-RWPI).
## Change
Two halves that must land together (placement and addressing must agree):
1. **Placement** (`getKindForGlobal`): under `Reloc::ROPI_RWPI` only, a reloc-bearing constant keeps `SectionKind::ReadOnlyWithRel` and lands in `.data.rel.ro` instead of being demoted to `ReadOnly`.
2. **Addressing** (`ARMTargetLowering::isReadOnly`): under the same gate, such a global reports as non-read-only, so `LowerGlobalAddressELF` (and GlobalISel's `selectGlobal`, which consults the same hook) forms its address SB-relative through r9, matching the new placement.
The gate deliberately mirrors the placement decision exactly:
- **Model**: combined ROPI-RWPI only. Plain RWPI keeps a link-time-fixed RO region, where stock placement and ABS addressing are already correct; SB-addressing a `.rodata` datum there would compute garbage. ROPI-only and Static are untouched.
- **Predicate**: `needsDynamicRelocation()` — the same predicate `getKindForGlobal` and `promoteToConstantPool` use. The wider `needsRelocation()` would also fire on link-time-final relative pointers (`ptrtoint(A) - ptrtoint(B)` cross-references), which placement keeps in `.rodata`; diverting their addressing would recreate the mismatch.
- **Explicit-section carve-out**: a global with a user-pinned section keeps stock placement *and* stock PC-relative addressing. Linker scripts route pinned sections by name (conventionally into the RO region); diverting only one half for them would reintroduce the mismatch for `__attribute__((section))` / `#[link_section]` users.
## Testing
- New test `llvm/test/CodeGen/ARM/ropi-rwpi-reloc-const.ll`: 9 RUN configurations (ARM/Thumb2/Thumb1-v6m, +/-movt, `-global-isel`, across static/ROPI/RWPI/ROPI-RWPI) over 5 global shapes (reloc-bearing const, table of pointers, external declaration, explicit-section-pinned const, mixed link-time/dynamic initializer).
- `check-llvm-codegen-arm` clean on this branch.
---
Full diff: https://github.com/llvm/llvm-project/pull/207931.diff
3 Files Affected:
- (modified) llvm/lib/Target/ARM/ARMISelLowering.cpp (+32-2)
- (modified) llvm/lib/Target/TargetLoweringObjectFile.cpp (+18-1)
- (added) llvm/test/CodeGen/ARM/ropi-rwpi-reloc-const.ll (+343)
``````````diff
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 577e97e736c25..36fc5d629e0e0 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -3620,8 +3620,38 @@ bool ARMTargetLowering::isReadOnly(const GlobalValue *GV) const {
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV))
if (!(GV = GA->getAliaseeObject()))
return false;
- if (const auto *V = dyn_cast<GlobalVariable>(GV))
- return V->isConstant();
+ if (const auto *V = dyn_cast<GlobalVariable>(GV)) {
+ if (!V->isConstant())
+ return false;
+ // Under ROPI-RWPI (both flags set -- exactly the combined model), a
+ // `const` whose initializer needs a dynamic relocation (e.g. a dyn
+ // vtable or any const holding a pointer to another symbol) is placed in
+ // a writable, relocation-bearing section (.data.rel.ro) by
+ // getKindForGlobal so a loader can fix it up after placement is known.
+ // Such a datum lives in the r9-addressed RW image, so it must NOT be
+ // treated as read-only here or its address would be formed PC-relative
+ // (pointing at where text expects rodata, not where the datum actually
+ // is). Report it as non-read-only so LowerGlobalAddressELF takes the
+ // SB-relative (r9) arm, matching the section placement.
+ //
+ // The gate mirrors the placement decision exactly: same model (only
+ // ROPI_RWPI -- under plain RWPI the RO region is link-time fixed, stock
+ // placement AND addressing are already correct, and SB-addressing a
+ // .rodata datum would break), same predicate
+ // (needsDynamicRelocation(), matching getKindForGlobal and
+ // promoteToConstantPool; the wider needsRelocation() would also fire on
+ // link-time-final relative pointers, which placement keeps in .rodata --
+ // addressing those via r9 would recreate the very
+ // addressing-vs-placement mismatch this hook exists to prevent), and
+ // same explicit-section carve-out (a user-pinned section keeps stock
+ // placement in getKindForGlobal, so it must keep stock PC-relative
+ // addressing here; linker scripts route pinned sections by name, and
+ // those names conventionally live in the RO region).
+ if (Subtarget->isROPI() && Subtarget->isRWPI() && !V->hasSection() &&
+ V->hasInitializer() && V->getInitializer()->needsDynamicRelocation())
+ return false;
+ return true;
+ }
return isa<Function>(GV);
}
diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp
index 43649a0cd95c7..e0242e06a900e 100644
--- a/llvm/lib/Target/TargetLoweringObjectFile.cpp
+++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp
@@ -352,9 +352,26 @@ SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalObject *GO,
// the time the app starts up. However, we can't put this into a
// mergable section, because the linker doesn't take relocations into
// consideration when it tries to merge entries in the section.
+ //
+ // ROPI_RWPI is deliberately excluded below: under a two-base
+ // (text via PC, data via r9) model used for placement-independent XIP
+ // images, a reloc-bearing constant (e.g. a dyn vtable) must be fixable
+ // by a loader after placement is known, so it has to land in a writable
+ // relocation-bearing section (.data.rel.ro), addressed via r9 -- see the
+ // matching ARMTargetLowering::isReadOnly change.
+ //
+ // Exception: a global with an explicit section keeps stock behavior
+ // even under ROPI_RWPI. The user pinned the placement, and linker
+ // scripts route pinned sections by NAME; diverting the kind to
+ // ReadOnlyWithRel would stamp SHF_WRITE onto the named section and
+ // (via the matching isReadOnly change) address it r9-relative while
+ // the bytes sit wherever the script put the name -- reintroducing the
+ // addressing-vs-placement mismatch. The loader-fixup model only covers
+ // compiler-placed constants.
Reloc::Model ReloModel = TM.getRelocationModel();
if (ReloModel == Reloc::Static || ReloModel == Reloc::ROPI ||
- ReloModel == Reloc::RWPI || ReloModel == Reloc::ROPI_RWPI ||
+ ReloModel == Reloc::RWPI ||
+ (ReloModel == Reloc::ROPI_RWPI && GVar->hasSection()) ||
!C->needsDynamicRelocation())
return SectionKind::getReadOnly();
diff --git a/llvm/test/CodeGen/ARM/ropi-rwpi-reloc-const.ll b/llvm/test/CodeGen/ARM/ropi-rwpi-reloc-const.ll
new file mode 100644
index 0000000000000..d6ae07ca17ed6
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/ropi-rwpi-reloc-const.ll
@@ -0,0 +1,343 @@
+; Placement + addressing for `constant` globals under the ROPI/RWPI family,
+; distinguishing six shapes:
+;
+; * @relconst -- a const holding a DIRECT POINTER to another symbol (the
+; reduced shape of a Rust `dyn` vtable). needsDynamicRelocation() == true.
+; Under ropi-rwpi ONLY, it must (a) be placed in the writable,
+; relocation-bearing .data.rel.ro (so a loader can fix the slot after
+; placement is known) and (b) be addressed SB-relative via r9 -- the two
+; decisions must agree. Under static/ropi/rwpi it keeps stock behavior.
+;
+; * @reltab -- a const holding a RELATIVE POINTER (ptrtoint(A)-ptrtoint(B),
+; both dso_local). needsRelocation() == true but needsDynamicRelocation()
+; == false: the entry is link-time-final, so it must stay in .rodata and
+; be addressed like read-only data (PC-relative under ropi/ropi-rwpi,
+; absolute otherwise) in EVERY model. Addressing this via r9 while it sits
+; in .rodata would compute S+Delta(rw) for a datum that moved with the RO
+; region -- the rust#95871 class of defect.
+;
+; * @ext / @extmut -- the DECLARATION CONTRACT. A declaration exposes no
+; initializer, so the region classification must ride on the `constant`
+; flag, which the frontend asserts:
+; - `external constant` (@ext): the frontend promises the definition is
+; RO-region data -> PC-relative. rustc emits this under ropi-rwpi for
+; cross-CGU/cross-crate declarations of pure-data immutable statics
+; (whose definitions stay in .rodata).
+; - `external global` (@extmut): definition lives in the RW image
+; (mutable data, or a reloc-bearing const diverted to .data.rel.ro)
+; -> SB-relative via r9.
+; A `constant` declaration whose definition is actually reloc-bearing
+; (e.g. C `extern const`, or ThinLTO's EliminateAvailableExternally
+; stripping an initializer while keeping the constant bit) is
+; misclassified by this contract -- that remains the documented cross-TU
+; limitation: the truth is not computable from such a declaration.
+;
+; * @pinned -- a reloc-bearing const with an EXPLICIT SECTION. The user owns
+; placement (linker scripts route pinned sections by NAME, conventionally
+; into the RO region), so BOTH halves keep stock behavior under ropi-rwpi:
+; the named section keeps "a" flags (no SHF_WRITE stamping) and the
+; address stays PC-relative, NOT r9. Diverting either half alone would
+; reintroduce the addressing-vs-placement mismatch.
+;
+; * @mix -- a const holding BOTH a direct pointer and a relative pointer
+; whose target is a FUNCTION (text). One dynamic slot poisons the whole
+; object (max-over-operands), so the entire datum goes to .data.rel.ro +
+; r9 under ropi-rwpi. NOTE the loader contract implication: the
+; link-time-final `take_addr_relconst-mix` word then lives in the RW-slid
+; region while its target stays in text, so a loader for independently
+; sliding regions must rewrite such surviving REL32-class entries by
+; (Delta(target region) - Delta_rw) -- here (Delta_text - Delta_rw) --
+; or reject mixed objects. (A relative entry whose target is in the SAME
+; region as the slot, e.g. data-to-data, survives sliding unchanged.)
+;
+; RUN: llc -relocation-model=static -mtriple=armv7a--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,ARM_ABS,RODATA
+; RUN: llc -relocation-model=ropi -mtriple=armv7a--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,ARM_PC,RODATA
+; RUN: llc -relocation-model=rwpi -mtriple=armv7a--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,ARM_ABS,RODATA
+; RUN: llc -relocation-model=ropi-rwpi -mtriple=armv7a--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,ARM_RR,RELRO
+; RUN: llc -relocation-model=rwpi -mtriple=thumbv7m--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,T2_ABS,RODATA
+; RUN: llc -relocation-model=ropi-rwpi -mtriple=thumbv7m--none-eabi < %s | FileCheck %s --check-prefixes=CHECK,T2_RR,RELRO
+;
+; No-movt targets must take the literal-pool SBREL path for the diverted
+; class; Thumb1 (v6m) additionally has no register-offset ADD with r9.
+; RUN: llc -relocation-model=ropi-rwpi -mtriple=armv7a--none-eabi -mattr=+no-movt < %s | FileCheck %s --check-prefixes=NOMOVT_RR,RELRO
+; RUN: llc -relocation-model=ropi-rwpi -mtriple=thumbv6m--none-eabi < %s | FileCheck %s --check-prefixes=T1_RR,RELRO
+;
+; GlobalISel resolves RO-ness through the same ARMTargetLowering::isReadOnly
+; hook, so parity (including the explicit-section carve-out) is automatic;
+; abort=1 also asserts nothing in this file falls back to SelectionDAG.
+; RUN: llc -relocation-model=ropi-rwpi -mtriple=armv7a--none-eabi -global-isel -global-isel-abort=1 < %s | FileCheck %s --check-prefixes=GISEL_RR,RELRO
+
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+
+ at target = external global i32, align 4
+ at relconst = constant ptr @target, align 4
+
+ at f = dso_local global i32 1, align 4
+ at reltab = dso_local constant i32 sub (i32 ptrtoint (ptr @f to i32), i32 ptrtoint (ptr @reltab to i32)), align 4
+
+ at ext = external constant ptr
+ at extmut = external global i32
+
+ at pinned = dso_local constant ptr @target, section ".mysec", align 4
+
+ at mix = dso_local constant { ptr, i32 } { ptr @target, i32 sub (i32 ptrtoint (ptr @take_addr_relconst to i32), i32 ptrtoint (ptr @mix to i32)) }, align 4
+
+define ptr @take_addr_relconst() {
+entry:
+ ret ptr @relconst
+; CHECK-LABEL: take_addr_relconst:
+
+; ARM_ABS: movw r0, :lower16:relconst{{$}}
+; ARM_ABS: movt r0, :upper16:relconst{{$}}
+
+; ARM_PC: movw r0, :lower16:(relconst-([[LPC:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_PC: movt r0, :upper16:(relconst-([[LPC]]+8))
+; ARM_PC: [[LPC]]:
+; ARM_PC-NEXT: add r0, pc, r0
+
+; ARM_RR: movw r0, :lower16:relconst(sbrel)
+; ARM_RR: movt r0, :upper16:relconst(sbrel)
+; ARM_RR: add r0, r9, r0
+
+; T2_ABS: movw r0, :lower16:relconst{{$}}
+; T2_ABS: movt r0, :upper16:relconst{{$}}
+
+; T2_RR: movw r0, :lower16:relconst(sbrel)
+; T2_RR: movt r0, :upper16:relconst(sbrel)
+; T2_RR: add r0, r9
+
+; NOMOVT_RR-LABEL: take_addr_relconst:
+; NOMOVT_RR: ldr r0, [[CPI:.LCPI[0-9]+_[0-9]+]]
+; NOMOVT_RR-NEXT: add r0, r9, r0
+; NOMOVT_RR: [[CPI]]:
+; NOMOVT_RR-NEXT: .long relconst(sbrel)
+
+; T1_RR-LABEL: take_addr_relconst:
+; T1_RR: ldr r0, [[CPI:.LCPI[0-9]+_[0-9]+]]
+; T1_RR-NEXT: mov r1, r9
+; T1_RR-NEXT: adds r0, r1, r0
+; T1_RR: [[CPI]]:
+; T1_RR-NEXT: .long relconst(sbrel)
+
+; GISEL_RR-LABEL: take_addr_relconst:
+; GISEL_RR: movw r0, :lower16:relconst(sbrel)
+; GISEL_RR: movt r0, :upper16:relconst(sbrel)
+; GISEL_RR: add r0, r9, r0
+
+; CHECK: bx lr
+}
+
+define ptr @take_addr_reltab() {
+entry:
+ ret ptr @reltab
+; CHECK-LABEL: take_addr_reltab:
+
+; ARM_ABS: movw r0, :lower16:{{(\.Lreltab\$local|reltab)$}}
+; ARM_ABS: movt r0, :upper16:{{(\.Lreltab\$local|reltab)$}}
+
+; ARM_PC: movw r0, :lower16:(.Lreltab$local-([[LPC1:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_PC: movt r0, :upper16:(.Lreltab$local-([[LPC1]]+8))
+; ARM_PC: [[LPC1]]:
+; ARM_PC-NEXT: add r0, pc, r0
+
+; The relative-pointer const stays RO under ropi-rwpi: PC-relative, NOT r9.
+; ARM_RR: movw r0, :lower16:(.Lreltab$local-([[LPC1:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_RR: movt r0, :upper16:(.Lreltab$local-([[LPC1]]+8))
+; ARM_RR: [[LPC1]]:
+; ARM_RR-NEXT: add r0, pc, r0
+
+; T2_ABS: movw r0, :lower16:.Lreltab$local{{$}}
+; T2_ABS: movt r0, :upper16:.Lreltab$local{{$}}
+
+; T2_RR: movw r0, :lower16:(.Lreltab$local-([[LPC1:.LPC[0-9]+_[0-9]+]]+4))
+; T2_RR: movt r0, :upper16:(.Lreltab$local-([[LPC1]]+4))
+; T2_RR: [[LPC1]]:
+; T2_RR-NEXT: add r0, pc
+
+; CHECK: bx lr
+}
+
+define ptr @take_addr_ext() {
+entry:
+ ret ptr @ext
+; CHECK-LABEL: take_addr_ext:
+
+; ARM_ABS: movw r0, :lower16:ext{{$}}
+; ARM_ABS: movt r0, :upper16:ext{{$}}
+
+; ARM_PC: movw r0, :lower16:(ext-([[LPCE:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_PC: movt r0, :upper16:(ext-([[LPCE]]+8))
+; ARM_PC: [[LPCE]]:
+; ARM_PC-NEXT: add r0, pc, r0
+
+; Declaration contract, RO arm: `external constant` -> PC-relative. This is
+; what rustc's decl-marking relies on for pure-data statics; it is WRONG for
+; a reloc-bearing definition (the documented cross-TU limitation).
+; ARM_RR: movw r0, :lower16:(ext-([[LPCE:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_RR: movt r0, :upper16:(ext-([[LPCE]]+8))
+; ARM_RR: [[LPCE]]:
+; ARM_RR-NEXT: add r0, pc, r0
+
+; T2_ABS: movw r0, :lower16:ext{{$}}
+; T2_ABS: movt r0, :upper16:ext{{$}}
+
+; T2_RR: movw r0, :lower16:(ext-([[LPCE:.LPC[0-9]+_[0-9]+]]+4))
+; T2_RR: movt r0, :upper16:(ext-([[LPCE]]+4))
+; T2_RR: [[LPCE]]:
+; T2_RR-NEXT: add r0, pc
+
+; NOMOVT_RR-LABEL: take_addr_ext:
+; NOMOVT_RR: ldr r0, [[CPIE:.LCPI[0-9]+_[0-9]+]]
+; NOMOVT_RR: [[LPCE:.LPC[0-9]+_[0-9]+]]:
+; NOMOVT_RR-NEXT: add r0, pc, r0
+; NOMOVT_RR: [[CPIE]]:
+; NOMOVT_RR-NEXT: .long ext-([[LPCE]]+8)
+
+; T1_RR-LABEL: take_addr_ext:
+; T1_RR: ldr r0, [[CPIE:.LCPI[0-9]+_[0-9]+]]
+; T1_RR: [[LPCE:.LPC[0-9]+_[0-9]+]]:
+; T1_RR-NEXT: add r0, pc
+; T1_RR: [[CPIE]]:
+; T1_RR-NEXT: .long ext-([[LPCE]]+4)
+
+; GISEL_RR-LABEL: take_addr_ext:
+; GISEL_RR: movw r0, :lower16:(ext-([[LPCGE:.LPC[0-9]+_[0-9]+]]+8))
+; GISEL_RR: movt r0, :upper16:(ext-([[LPCGE]]+8))
+; GISEL_RR: [[LPCGE]]:
+; GISEL_RR-NEXT: add r0, pc, r0
+
+; CHECK: bx lr
+}
+
+define ptr @take_addr_extmut() {
+entry:
+ ret ptr @extmut
+; CHECK-LABEL: take_addr_extmut:
+
+; Declaration contract, RW arm: `external global` (no constant flag) -> SB
+; via r9. rustc emits this shape for declarations of mutable statics AND of
+; reloc-bearing immutable statics (whose definitions are diverted to
+; .data.rel.ro) -- both live in the r9-addressed RW image.
+; ARM_RR: movw r0, :lower16:extmut(sbrel)
+; ARM_RR: movt r0, :upper16:extmut(sbrel)
+; ARM_RR: add r0, r9, r0
+
+; T2_RR: movw r0, :lower16:extmut(sbrel)
+; T2_RR: movt r0, :upper16:extmut(sbrel)
+; T2_RR: add r0, r9
+
+; GISEL_RR-LABEL: take_addr_extmut:
+; GISEL_RR: movw r0, :lower16:extmut(sbrel)
+; GISEL_RR: movt r0, :upper16:extmut(sbrel)
+; GISEL_RR: add r0, r9, r0
+
+; NOMOVT_RR-LABEL: take_addr_extmut:
+; NOMOVT_RR: ldr r0, [[CPIEM:.LCPI[0-9]+_[0-9]+]]
+; NOMOVT_RR-NEXT: add r0, r9, r0
+; NOMOVT_RR: [[CPIEM]]:
+; NOMOVT_RR-NEXT: .long extmut(sbrel)
+
+; T1_RR-LABEL: take_addr_extmut:
+; T1_RR: ldr r0, [[CPIEM:.LCPI[0-9]+_[0-9]+]]
+; T1_RR-NEXT: mov r1, r9
+; T1_RR-NEXT: adds r0, r1, r0
+; T1_RR: [[CPIEM]]:
+; T1_RR-NEXT: .long extmut(sbrel)
+
+; CHECK: bx lr
+}
+
+define ptr @take_addr_pinned() {
+entry:
+ ret ptr @pinned
+; CHECK-LABEL: take_addr_pinned:
+
+; ARM_ABS: movw r0, :lower16:{{(\.Lpinned\$local|pinned)$}}
+; ARM_ABS: movt r0, :upper16:{{(\.Lpinned\$local|pinned)$}}
+
+; ARM_PC: movw r0, :lower16:(.Lpinned$local-([[LPCP:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_PC: movt r0, :upper16:(.Lpinned$local-([[LPCP]]+8))
+; ARM_PC: [[LPCP]]:
+; ARM_PC-NEXT: add r0, pc, r0
+
+; Explicit-section carve-out: the user pinned the section, so BOTH halves
+; keep stock behavior under ropi-rwpi -- PC-relative address (NOT r9), and
+; the named section below keeps "a" flags (no SHF_WRITE stamping).
+; ARM_RR: movw r0, :lower16:(.Lpinned$local-([[LPCP:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_RR: movt r0, :upper16:(.Lpinned$local-([[LPCP]]+8))
+; ARM_RR: [[LPCP]]:
+; ARM_RR-NEXT: add r0, pc, r0
+
+; T2_ABS: movw r0, :lower16:.Lpinned$local{{$}}
+; T2_ABS: movt r0, :upper16:.Lpinned$local{{$}}
+
+; T2_RR: movw r0, :lower16:(.Lpinned$local-([[LPCP:.LPC[0-9]+_[0-9]+]]+4))
+; T2_RR: movt r0, :upper16:(.Lpinned$local-([[LPCP]]+4))
+; T2_RR: [[LPCP]]:
+; T2_RR-NEXT: add r0, pc
+
+; GISEL_RR-LABEL: take_addr_pinned:
+; GISEL_RR: movw r0, :lower16:(.Lpinned$local-([[LPCGP:.LPC[0-9]+_[0-9]+]]+8))
+; GISEL_RR: movt r0, :upper16:(.Lpinned$local-([[LPCGP]]+8))
+; GISEL_RR: [[LPCGP]]:
+; GISEL_RR-NEXT: add r0, pc, r0
+
+; CHECK: bx lr
+}
+
+define ptr @take_addr_mix() {
+entry:
+ ret ptr @mix
+; CHECK-LABEL: take_addr_mix:
+
+; ARM_ABS: movw r0, :lower16:{{(\.Lmix\$local|mix)$}}
+; ARM_ABS: movt r0, :upper16:{{(\.Lmix\$local|mix)$}}
+
+; ARM_PC: movw r0, :lower16:(.Lmix$local-([[LPCM:.LPC[0-9]+_[0-9]+]]+8))
+; ARM_PC: movt r0, :upper16:(.Lmix$local-([[LPCM]]+8))
+; ARM_PC: [[LPCM]]:
+; ARM_PC-NEXT: add r0, pc, r0
+
+; One dynamic slot poisons the whole object: .data.rel.ro + r9.
+; ARM_RR: movw r0, :lower16:.Lmix$local(sbrel)
+; ARM_RR: movt r0, :upper16:.Lmix$local(sbrel)
+; ARM_RR: add r0, r9, r0
+
+; T2_ABS: movw r0, :lower16:.Lmix$local{{$}}
+; T2_ABS: movt r0, :upper16:.Lmix$local{{$}}
+
+; T2_RR: movw r0, :lower16:.Lmix$local(sbrel)
+; T2_RR: movt r0, :upper16:.Lmix$local(sbrel)
+; T2_RR: add r0, r9
+
+; CHECK: bx lr
+}
+
+; Section placement. Emission order in all configs:
+; relconst, f (.data), reltab, pinned (.mysec), mix.
+
+; RODATA: .section .rodata,"a",%progbits
+; RODATA: relconst:
+; RODATA-NEXT: .long target
+; RODATA: reltab:
+; RODATA: .long f-reltab
+; RODATA: .section .mysec,"a",%progbits
+; RODATA: pinned:
+; RODATA: .long target
+; RODATA: mix:
+; RODATA: .long target
+; RODATA-NEXT: .long take_addr_relconst-mix
+
+; RELRO: .section .data.rel.ro,"aw",%progbits
+; RELRO: relconst:
+; RELRO-NEXT: .long target
+; RELRO: .section .rodata,"a",%progbits
+; RELRO: reltab:
+; RELRO: .long f-reltab
+; RELRO: .section .mysec,"a",%progbits
+; RELRO: pinned:
+; RELRO: .long target
+; RELRO: .section .data.rel.ro,"aw",%progbits
+; RELRO: mix:
+; RELRO: .long target
+; RELRO-NEXT: .long take_addr_relconst-mix
``````````
</details>
https://github.com/llvm/llvm-project/pull/207931
More information about the llvm-commits
mailing list