Uh oh!
There was an error while loading. Please reload this page.
Arm64 R2R: fold adrp+add+ldr into adrp+ldr[#:lo12:] - #129589
Conversation
Add an Arm64 PAGEOFFSET_12L relocation so a value load through a relocatable cell folds the page offset into the ldr, saving one instruction per access (R2R and runtime JIT). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR adds a new Arm64 relocation kind (ARM64_PAGEOFFSET_12L) and teaches RyuJIT (and supporting infrastructure) to fold a relocatable adrp + add + ldr sequence into adrp + ldr [#:lo12:], saving an instruction for common indirection-cell value loads in R2R/relocatable codegen.
Changes:
- Add
CorInfoReloc::ARM64_PAGEOFFSET_12Land wire it through the JIT/EE boundary (including a JIT/EE GUID bump). - Introduce
GetArm64Rel12Ldr/PutArm64Rel12Ldrhelpers and use them in VM and SuperPMI relocation fixups. - Implement an emitter peephole to remove the relocatable
addand emit anldrcarrying the new relocation.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/jitinterface.cpp | Handles the new ARM64_PAGEOFFSET_12L relocation in the runtime relocation fixer. |
| src/coreclr/utilcode/util.cpp | Adds LDR-specific rel12 extract/deposit helpers for Arm64. |
| src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h | Declares PutArm64Rel12Ldr for SuperPMI’s relocation patching utilities. |
| src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp | Implements PutArm64Rel12Ldr in SuperPMI’s local copy of utilcode helpers. |
| src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp | Adds stringification and application support for ARM64_PAGEOFFSET_12L during replay normalization. |
| src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | Extends the managed mirror of CorInfoReloc with ARM64_PAGEOFFSET_12L. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Maps ARM64_PAGEOFFSET_12L to the object-writer relocation type. |
| src/coreclr/jit/emitarm64.h | Declares TryFoldPageOffsetIntoLdr helper for the emitter peephole. |
| src/coreclr/jit/emitarm64.cpp | Implements folding logic and records the new relocation for the folded ldr. |
| src/coreclr/inc/utilcode.h | Declares GetArm64Rel12Ldr / PutArm64Rel12Ldr helpers. |
| src/coreclr/inc/jiteeversionguid.h | Bumps the JIT/EE interface GUID due to the new relocation enum value. |
| src/coreclr/inc/corinfo.h | Adds ARM64_PAGEOFFSET_12L to the native CorInfoReloc enum. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Address review feedback: the predicate gates on EA_SIZE == EA_8BYTE in a general register, which includes GCREF/BYREF; "integer loads" was misleading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
EgorBo
commented
Jun 19, 2026
/azp list |
This comment was marked as resolved.
This comment was marked as resolved.
EgorBo
commented
Jun 19, 2026
/azp run runtime-coreclr outerloop, runtime-coreclr crossgen2, runtime-coreclr r2r |
|
Azure Pipelines successfully started running 3 pipeline(s). |
Uh oh!
There was an error while loading. Please reload this page.
EgorBo
commented
Jun 19, 2026
PTAL @dotnet/jit-contrib all outerloop pipelines passed |
EgorBo
commented
Jun 22, 2026
PTAL @dhartglassMSFT @dotnet/jit-contrib |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
tannergooding
left a comment
There was a problem hiding this comment.
LGTM. Just a couple questions about handling other scenarios (possibly just in the future).
EgorBo
commented
Jun 26, 2026
/ba-g timeouts |
Uh oh!
There was an error while loading. Please reload this page.
…9966) Fixes#129936. PR #129589 added an Arm64 fold of `adrp+add+ldr` into `adrp+ldr[#:lo12:]` via a 64-bit `PAGEOFFSET_12L` reloc (`R_AARCH64_LDST64_ABS_LO12_NC`). That reloc encodes the offset scaled by 8, so the target must be 8-byte aligned. In R2R the relocatable loads always go through pointer-aligned indirection cells, but NativeAOT can fold a direct 64-bit load of byte-packed frozen data (4-byte aligned), which `ld.lld` rejects: ``` ld.lld : error : improper alignment for relocation R_AARCH64_LDST64_ABS_LO12_NC: 0x74A52C is not aligned to 8 bytes ``` The fold is purely a size optimization, so this restricts it to non-NativeAOT and falls back to the always-safe `adrp+add+ldr`. Validation (SPMI, linux-arm64): - `benchmarks.run` (R2R, ~427K contexts): **0 asm diffs** — the win is preserved. - `smoke_tests.nativeaot` (18891 ctx): clean replay, no diffs. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
On Arm64 R2R (and runtime JIT), a value load through a relocatable
indirection cell emits `adrp + add + ldr`. The page offset can be folded
directly into the `ldr` via a new `PAGEOFFSET_12L` relocation, saving
one instruction per access. This is what x64 gets for free with
RIP-relative addressing, and the managed AOT object-writer side already
supported `12L` — only RyuJIT never emitted it.
For example:
```cs
public static void Test()
{
Console.WriteLine("hello");
}
```
Before → after (Arm64 R2R):
```asm
adrp x0, [HIGH RELOC] adrp x0, [HIGH RELOC]
add x0, x0, [LOW RELOC] → ldr x0, [x0, [LOW RELOC]] ← folded
ldr x0, [x0] ldr x0, [x0]
ldr x0, [x0]
```
**-2.1 MB** on libraries.crossgen SPMI collection.
System.Private.CoreLib.dll (R2R): 18032 KB -> 17840 KB (**-192 KB**)
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>…9966) Fixes#129936. PR #129589 added an Arm64 fold of `adrp+add+ldr` into `adrp+ldr[#:lo12:]` via a 64-bit `PAGEOFFSET_12L` reloc (`R_AARCH64_LDST64_ABS_LO12_NC`). That reloc encodes the offset scaled by 8, so the target must be 8-byte aligned. In R2R the relocatable loads always go through pointer-aligned indirection cells, but NativeAOT can fold a direct 64-bit load of byte-packed frozen data (4-byte aligned), which `ld.lld` rejects: ``` ld.lld : error : improper alignment for relocation R_AARCH64_LDST64_ABS_LO12_NC: 0x74A52C is not aligned to 8 bytes ``` The fold is purely a size optimization, so this restricts it to non-NativeAOT and falls back to the always-safe `adrp+add+ldr`. Validation (SPMI, linux-arm64): - `benchmarks.run` (R2R, ~427K contexts): **0 asm diffs** — the win is preserved. - `smoke_tests.nativeaot` (18891 ctx): clean replay, no diffs. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
On Arm64 R2R (and runtime JIT), a value load through a relocatable indirection cell emits
adrp + add + ldr. The page offset can be folded directly into theldrvia a newPAGEOFFSET_12Lrelocation, saving one instruction per access. This is what x64 gets for free with RIP-relative addressing, and the managed AOT object-writer side already supported12L— only RyuJIT never emitted it.For example:
Before → after (Arm64 R2R):
-2.1 MB on libraries.crossgen SPMI collection.
System.Private.CoreLib.dll (R2R): 18032 KB -> 17840 KB (-192 KB)