Skip to content

Commit 11702c4

Browse files
CopilotjkoritzinskyCopilot
authored
Use ADRP/LDR pairs for ARM64 64-bit indirect address loads in crossgen2 (#121352)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jkoritzinsky <[email protected]> Co-authored-by: Jeremy Koritzinsky <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent e1136d1 commit 11702c4

File tree

8 files changed

+87
-33
lines changed

8 files changed

+87
-33
lines changed

src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,49 @@ private static unsafe void PutArm64TlsRel12(uint* pCode, int imm12)
323323
Debug.Assert(GetArm64Rel12(pCode) == imm12);
324324
}
325325

326+
//*****************************************************************************
327+
// Extract the 12-bit page offset from an LDR instruction (unsigned immediate)
328+
// For 64-bit LDR, the immediate is scaled by 8 bytes
329+
//*****************************************************************************
330+
private static unsafe int GetArm64Rel12Ldr(uint* pCode)
331+
{
332+
uint ldrInstr = *pCode;
333+
334+
// 0x003FFC00: Mask for bits 21-10 of the 32-bit ARM64 LDR instruction
335+
// which contain the scaled immediate value
336+
int scaledImm12 = (int)(ldrInstr & 0x003FFC00) >> 10;
337+
338+
// Scale back to byte offset (multiply by 8)
339+
return scaledImm12 << 3;
340+
}
341+
342+
//*****************************************************************************
343+
// Deposit the 12-bit page offset 'imm12' into an LDR instruction (unsigned immediate)
344+
// For 64-bit LDR, the immediate represents offset/8 (scaled by 8 bytes)
345+
//*****************************************************************************
346+
private static unsafe void PutArm64Rel12Ldr(uint* pCode, int imm12)
347+
{
348+
// Verify that we got a valid offset and that it's aligned to 8 bytes
349+
Debug.Assert(FitsInRel12(imm12));
350+
Debug.Assert((imm12 & 7) == 0, "LDR offset must be 8-byte aligned");
351+
352+
uint ldrInstr = *pCode;
353+
// Check ldr opcode: 0b11111001010000000000000000000000 (LDR 64-bit register, unsigned immediate)
354+
Debug.Assert((ldrInstr & 0xFFC00000) == 0xF9400000);
355+
356+
// Scale the offset by dividing by 8 for the instruction encoding
357+
int scaledImm12 = imm12 >> 3;
358+
359+
// 0xFFC003FF: Mask to preserve bits 31-22 (opcode) and bits 9-0 (registers)
360+
// Clear bits 21-10 which will hold the scaled immediate value
361+
ldrInstr &= 0xFFC003FF;
362+
ldrInstr |= (uint)(scaledImm12 << 10); // Set bits 21-10 with scaled offset
363+
364+
*pCode = ldrInstr; // write the assembled instruction
365+
366+
Debug.Assert(GetArm64Rel12Ldr(pCode) == imm12);
367+
}
368+
326369
private static unsafe int GetArm64Rel28(uint* pCode)
327370
{
328371
uint branchInstr = *pCode;
@@ -548,6 +591,9 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v
548591
case RelocType.IMAGE_REL_ARM64_TLS_SECREL_LOW12A:
549592
PutArm64Rel12((uint*)location, (int)value);
550593
break;
594+
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
595+
PutArm64Rel12Ldr((uint*)location, (int)value);
596+
break;
551597
case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC:
552598
PutLoongArch64PC12((uint*)location, value);
553599
break;
@@ -578,6 +624,7 @@ public static int GetSize(RelocType relocType)
578624
// a span of this many bytes.
579625
RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => 4,
580626
RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => 4,
627+
RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => 4,
581628
RelocType.IMAGE_REL_BASED_THUMB_MOV32 => 8,
582629
RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8,
583630
RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 16,
@@ -617,6 +664,8 @@ public static unsafe long ReadValue(RelocType relocType, void* location)
617664
case RelocType.IMAGE_REL_ARM64_TLS_SECREL_HIGH12A:
618665
case RelocType.IMAGE_REL_ARM64_TLS_SECREL_LOW12A:
619666
return GetArm64Rel12((uint*)location);
667+
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
668+
return GetArm64Rel12Ldr((uint*)location);
620669
case RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12:
621670
case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12:
622671
case RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12:

src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ public void EmitMOV(Register regDst, ISymbolNode symbol)
4949
Builder.EmitUInt((uint)(0b1_0_0_100010_0_000000000000_00000_00000 | ((byte)regDst << 5) | (byte)regDst));
5050
}
5151

52+
// adrp regDst, symbol
53+
public void EmitADRP(Register regDst, ISymbolNode symbol)
54+
{
55+
Debug.Assert((uint)regDst <= 0x1f);
56+
Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21);
57+
Builder.EmitUInt(0x90000000u | (uint)regDst);
58+
}
59+
5260
// ldr regDst, [PC + imm19]
5361
public void EmitLDR(Register regDst, short offset)
5462
{
@@ -90,6 +98,15 @@ public void EmitLDR(Register regDst, Register regSrc, int offset)
9098
}
9199
}
92100

101+
// ldr regDst, [regAddr, symbol page offset]
102+
public void EmitLDR(Register regDst, Register regAddr, ISymbolNode symbol)
103+
{
104+
Debug.Assert((uint)regDst <= 0x1f);
105+
Debug.Assert((uint)regAddr <= 0x1f);
106+
Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L);
107+
Builder.EmitUInt(0xf9400000 | ((uint)regAddr << 5) | (uint)regDst);
108+
}
109+
93110
// ldar regDst, [regAddr]
94111
public void EmitLDAR(Register regDst, Register regAddr)
95112
{
@@ -150,24 +167,15 @@ public void EmitJMP(ISymbolNode symbol)
150167
{
151168
if (symbol.RepresentsIndirectionCell)
152169
{
153-
Builder.RequireInitialPointerAlignment();
170+
// Use ADRP/LDR pair to load the indirection cell address
171+
// adrp x12, symbol
172+
EmitADRP(Register.X12, symbol);
154173

155-
if (Builder.CountBytes % Builder.TargetPointerSize == 0)
156-
{
157-
// Emit a NOP instruction to align the 64-bit reloc below.
158-
EmitNOP();
159-
}
160-
161-
// ldr x12, [PC+0xc]
162-
EmitLDR(Register.X12, 0xc);
163-
164-
// ldr x12, [x12]
165-
EmitLDR(Register.X12, Register.X12);
174+
// ldr x12, [x12, symbol page offset]
175+
EmitLDR(Register.X12, Register.X12, symbol);
166176

167177
// br x12
168178
EmitJMP(Register.X12);
169-
170-
Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64);
171179
}
172180
else
173181
{

src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ private protected override void EmitRelocations(int sectionIndex, List<SymbolicR
356356
IMAGE_REL_BASED_ARM64_BRANCH26 => IMAGE_REL_ARM64_BRANCH26,
357357
IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => IMAGE_REL_ARM64_PAGEBASE_REL21,
358358
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => IMAGE_REL_ARM64_PAGEOFFSET_12A,
359+
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => IMAGE_REL_ARM64_PAGEOFFSET_12L,
359360
IMAGE_REL_ARM64_TLS_SECREL_HIGH12A => IMAGE_REL_ARM64_SECREL_HIGH12A,
360361
IMAGE_REL_ARM64_TLS_SECREL_LOW12A => IMAGE_REL_ARM64_SECREL_LOW12A,
361362
IMAGE_REL_SECREL => IMAGE_REL_ARM64_SECREL,

src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ private void EmitRelocationsARM64(int sectionIndex, List<SymbolicRelocation> rel
487487
IMAGE_REL_BASED_ARM64_BRANCH26 => R_AARCH64_CALL26,
488488
IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => R_AARCH64_ADR_PREL_PG_HI21,
489489
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => R_AARCH64_ADD_ABS_LO12_NC,
490+
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => R_AARCH64_LDST64_ABS_LO12_NC,
490491
IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12 => R_AARCH64_TLSLE_ADD_TPREL_HI12,
491492
IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC => R_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
492493
IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 => R_AARCH64_TLSDESC_ADR_PAGE21,

src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ protected internal override unsafe void EmitRelocation(
417417
{
418418
case IMAGE_REL_BASED_ARM64_PAGEBASE_REL21:
419419
case IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A:
420+
case IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
420421
// Addend is handled through ARM64_RELOC_ADDEND
421422
break;
422423

@@ -629,7 +630,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> rel
629630
IsPCRelative = true,
630631
});
631632
}
632-
else if (symbolicRelocation.Type is IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A)
633+
else if (symbolicRelocation.Type is IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L)
633634
{
634635
if (symbolicRelocation.Addend != 0)
635636
{
@@ -649,6 +650,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> rel
649650
{
650651
IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => ARM64_RELOC_PAGE21,
651652
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => ARM64_RELOC_PAGEOFF12,
653+
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => ARM64_RELOC_PAGEOFF12,
652654
_ => 0
653655
};
654656

@@ -660,7 +662,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> rel
660662
Length = 4,
661663
RelocationType = type,
662664
IsExternal = true,
663-
IsPCRelative = symbolicRelocation.Type != IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A,
665+
IsPCRelative = symbolicRelocation.Type == IMAGE_REL_BASED_ARM64_PAGEBASE_REL21,
664666
});
665667
}
666668
else if (symbolicRelocation.Type == IMAGE_REL_BASED_DIR64)

src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,7 @@ private unsafe void ResolveRelocations(int sectionIndex, List<SymbolicRelocation
874874
break;
875875
}
876876
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A:
877+
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
877878
if (addend != 0)
878879
{
879880
throw new NotSupportedException();

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ public ImportThunk(NodeFactory factory, ReadyToRunHelper helperId, ImportSection
6464
}
6565

6666
if (_thunkKind != Kind.Eager
67-
&& factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.ARM64
68-
or Internal.TypeSystem.TargetArchitecture.LoongArch64
67+
&& factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.LoongArch64
6968
or Internal.TypeSystem.TargetArchitecture.RiscV64)
7069
{
7170
// We stuff the reloc to the module import pointer before the start of the thunk

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi
2121
return;
2222
}
2323

24-
instructionEncoder.Builder.RequireInitialPointerAlignment();
25-
Debug.Assert(instructionEncoder.Builder.CountBytes == 0);
26-
27-
instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
28-
29-
Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset);
30-
3124
if (relocsOnly)
3225
{
3326
// When doing relocs only, we don't need to generate the actual instructions
@@ -50,21 +43,21 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi
5043
instructionEncoder.EmitMOV(Register.X9, checked((ushort)index));
5144

5245
// Move Module* -> x10
53-
// ldr x10, [PC-0xc]
54-
instructionEncoder.EmitLDR(Register.X10, -0xc);
46+
// adrp x10, ModuleImport
47+
instructionEncoder.EmitADRP(Register.X10, factory.ModuleImport);
5548

56-
// ldr x10, [x10]
57-
instructionEncoder.EmitLDR(Register.X10, Register.X10);
49+
// ldr x10, [x10, ModuleImport page offset]
50+
instructionEncoder.EmitLDR(Register.X10, Register.X10, factory.ModuleImport);
5851
break;
5952

6053
case Kind.Lazy:
6154

6255
// Move Module* -> x1
63-
// ldr x1, [PC-0x8]
64-
instructionEncoder.EmitLDR(Register.X1, -0x8);
56+
// adrp x1, ModuleImport
57+
instructionEncoder.EmitADRP(Register.X1, factory.ModuleImport);
6558

66-
// ldr x1, [x1]
67-
instructionEncoder.EmitLDR(Register.X1, Register.X1);
59+
// ldr x1, [x1, ModuleImport page offset]
60+
instructionEncoder.EmitLDR(Register.X1, Register.X1, factory.ModuleImport);
6861
break;
6962

7063
default:

0 commit comments

Comments
 (0)