From 7845dd1156315d88af86f7982f245919c4c3927a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 7 Nov 2025 11:34:47 -0800 Subject: [PATCH 1/5] Update community architectures to use PC-relative addressing They now match the ARM64 model we just moved to in #121352 Also, clean up the ARM64 emitter while we're at it. --- .../Target_LoongArch64/LoongArch64Emitter.cs | 26 ++++++------- .../Target_RiscV64/RiscV64Emitter.cs | 26 +++++-------- .../ReadyToRun/ImportThunk.cs | 15 -------- .../ReadyToRun/Target_ARM64/ImportThunk.cs | 8 ---- .../Target_LoongArch64/ImportThunk.cs | 38 +------------------ .../ReadyToRun/Target_RiscV64/ImportThunk.cs | 37 ++---------------- 6 files changed, 26 insertions(+), 124 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs index f5df5286d44785..182acc4e803f43 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs @@ -92,6 +92,16 @@ public void EmitLD(Register regDst, Register regSrc, int offset) Builder.EmitUInt((uint)(0x28c00000 | (uint)((offset & 0xfff) << 10) | ((uint)regSrc << 5) | (uint)regDst)); } + public void EmitLD(Register regDst, ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC); + // pcalau12i reg, off-hi-20bits + EmitPCALAU12I(regDst); + + // ld_d reg, reg, off-lo-12bits + EmitLD(regDst, regDst, 0); + } + public void EmitRET() { // jirl R0,R1,0 @@ -107,21 +117,7 @@ public void EmitJMP(ISymbolNode symbol) { if (symbol.RepresentsIndirectionCell) { - Builder.RequireInitialPointerAlignment(); - - if (Builder.CountBytes % Builder.TargetPointerSize != 0) - { - // Emit a NOP instruction to align the 64-bit reloc below. - EmitNOP(); - } - - // pcaddi R21, 0 - EmitPCADDI(Register.R21); - - EmitLD(Register.R21, Register.R21, 0x10); - - // ld_d R21, R21, 0 - EmitLD(Register.R21, Register.R21, 0); + EmitLD(Register.R21, symbol); // jirl R0,R21,0 EmitJMP(Register.R21); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs index 86f7528c7262aa..6a91e43a314545 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs @@ -87,6 +87,15 @@ public void EmitJALR(Register regDst, Register regSrc, int offset) Builder.EmitUInt((uint)(0x00000067u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20))); } + public void EmitLD(Register regDst, ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC); + //auipc reg, off-hi-20bits + EmitPC(regDst); + //ld reg, off-lo-12bits(reg) + EmitLD(regDst, regDst, 0); + } + public void EmitRET() { // jalr x0,0(x1) @@ -103,24 +112,9 @@ public void EmitJMP(ISymbolNode symbol) { if (symbol.RepresentsIndirectionCell) { - Builder.RequireInitialPointerAlignment(); - - if (Builder.CountBytes % Builder.TargetPointerSize != 0) - { - // Emit a NOP instruction to align the 64-bit reloc below. - EmitNOP(); - } - - // auipc x29, 0 - EmitPC(Register.X29); - // ld x29,16(x29) - EmitLD(Register.X29, Register.X29, 16); - // ld x29,0(x29) - EmitLD(Register.X29, Register.X29, 0); + EmitLD(Register.X29, symbol); // jalr x0,0(x29) EmitJALR(Register.X0, Register.X29, 0); - - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); } else { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs index a1dda9c6f8da3e..7f3fe20fe9834a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs @@ -28,8 +28,6 @@ enum Kind private readonly ImportSectionNode _containingImportSection; - private readonly int _symbolOffset = 0; - /// /// Import thunks are used to call a runtime-provided helper which fixes up an indirection cell in a particular /// import section. Optionally they may also contain a relocation for a specific indirection cell to fix up. @@ -62,21 +60,8 @@ public ImportThunk(NodeFactory factory, ReadyToRunHelper helperId, ImportSection { _thunkKind = Kind.Eager; } - - if (_thunkKind != Kind.Eager - && factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.LoongArch64 - or Internal.TypeSystem.TargetArchitecture.RiscV64) - { - // We stuff the reloc to the module import pointer before the start of the thunk - // to ensure alignment. - // The thunk itself starts immediately after the reloc. - // We don't need this for an Eager thunk. - _symbolOffset = 8; - } } - int ISymbolNode.Offset => base.Offset + _symbolOffset; - public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append("DelayLoadHelper->"u8); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs index f5b919fc7742cc..0d37b48dd1b45b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs @@ -21,14 +21,6 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi return; } - if (relocsOnly) - { - // When doing relocs only, we don't need to generate the actual instructions - // as they will be ignored. Just emit the jump so we record the dependency. - instructionEncoder.EmitJMP(_helperCell); - return; - } - switch (_thunkKind) { case Kind.DelayLoadHelper: diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs index 0efca97f939375..cfeb3ea61e975b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs @@ -22,21 +22,6 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins return; } - instructionEncoder.Builder.RequireInitialPointerAlignment(); - Debug.Assert(instructionEncoder.Builder.CountBytes == 0); - - instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64); - - Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset); - - if (relocsOnly) - { - // When doing relocs only, we don't need to generate the actual instructions - // as they will be ignored. Just emit the jump so we record the dependency. - instructionEncoder.EmitJMP(_helperCell); - return; - } - switch (_thunkKind) { case Kind.DelayLoadHelper: @@ -50,32 +35,13 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins int index = _containingImportSection.IndexFromBeginningOfArray; instructionEncoder.EmitMOV(Register.R12, checked((ushort)index)); - int offset = -instructionEncoder.Builder.CountBytes; - - // get pc - // pcaddi T1=R13, 0 - instructionEncoder.EmitPCADDI(Register.R13); - - // load Module* -> T1 - instructionEncoder.EmitLD(Register.R13, Register.R13, offset); - - // ld_d R13, R13, 0 - instructionEncoder.EmitLD(Register.R13, Register.R13, 0); + instructionEncoder.EmitLD(Register.R13, factory.ModuleImport); break; } case Kind.Lazy: { - int offset = -instructionEncoder.Builder.CountBytes; - // get pc - // pcaddi R5, 0 - instructionEncoder.EmitPCADDI(Register.R5); - - // load Module* -> R5=A1 - instructionEncoder.EmitLD(Register.R5, Register.R5, offset); - - // ld_d R5, R5, 0 - instructionEncoder.EmitLD(Register.R5, Register.R5, 0); + instructionEncoder.EmitLD(Register.R5, factory.ModuleImport); break; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs index d4342dd4b3f42a..4b1de675610ddc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs @@ -21,21 +21,6 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instruc return; } - instructionEncoder.Builder.RequireInitialPointerAlignment(); - Debug.Assert(instructionEncoder.Builder.CountBytes == 0); - - instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64); - - Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset); - - if (relocsOnly) - { - // When doing relocs only, we don't need to generate the actual instructions - // as they will be ignored. Just emit the jump so we record the dependency. - instructionEncoder.EmitJMP(_helperCell); - return; - } - switch (_thunkKind) { case Kind.Eager: @@ -51,31 +36,15 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instruc int index = _containingImportSection.IndexFromBeginningOfArray; instructionEncoder.EmitLI(Register.X5, index); - int offset = -instructionEncoder.Builder.CountBytes; - - // get pc - // auipc t1, 0 - instructionEncoder.EmitPC(Register.X6); - - // load Module* -> t1 - instructionEncoder.EmitLD(Register.X6, Register.X6, offset); + // Move Module* -> t1 + instructionEncoder.EmitLD(Register.X6, factory.ModuleImport); - // ld t1, t1, 0 - instructionEncoder.EmitLD(Register.X6, Register.X6, 0); break; } case Kind.Lazy: { - int offset = -instructionEncoder.Builder.CountBytes; - - // get pc - instructionEncoder.EmitPC(Register.X11); - - // load Module* -> a1 - instructionEncoder.EmitLD(Register.X11, Register.X11, offset); - // ld a1, a1, 0 - instructionEncoder.EmitLD(Register.X11, Register.X11, 0); + instructionEncoder.EmitLD(Register.X11, factory.ModuleImport); break; } default: From 60170b20fddc0b07ffec3a579ad052bcdeb7ab92 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 7 Nov 2025 12:03:26 -0800 Subject: [PATCH 2/5] Update src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs index 4b1de675610ddc..aa194d7dd7904e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs @@ -43,7 +43,7 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instruc } case Kind.Lazy: { - // ld a1, a1, 0 + // Load Module* -> a1 instructionEncoder.EmitLD(Register.X11, factory.ModuleImport); break; } From a5dde9c99f6226f3736df02ba15c900b9feb3584 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 7 Nov 2025 14:31:40 -0800 Subject: [PATCH 3/5] Add back relocs-only paths to avoid reading IndexFromBeginningOfArray during the relocs-only phase --- .../ReadyToRun/Target_ARM64/ImportThunk.cs | 9 +++++++++ .../ReadyToRun/Target_LoongArch64/ImportThunk.cs | 8 ++++++++ .../ReadyToRun/Target_RiscV64/ImportThunk.cs | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs index 0d37b48dd1b45b..cc029814feb6c8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs @@ -20,6 +20,15 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi instructionEncoder.EmitJMP(_helperCell); return; } + if (relocsOnly) + { + // When doing relocs only, we don't need to generate the actual instructions + // as they will be ignored. Just emit the module import load and jump so we record the dependencies. + instructionEncoder.EmitADRP(Register.X1, factory.ModuleImport); + instructionEncoder.EmitLDR(Register.X1, Register.X1, factory.ModuleImport); + instructionEncoder.EmitJMP(_helperCell); + return; + } switch (_thunkKind) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs index cfeb3ea61e975b..7a01b3f7483e9b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs @@ -21,6 +21,14 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins instructionEncoder.EmitJMP(_helperCell); return; } + if (relocsOnly) + { + // When doing relocs only, we don't need to generate the actual instructions + // as they will be ignored. Just emit the module import load and jump so we record the dependencies. + instructionEncoder.EmitLD(Register.R5, factory.ModuleImport); + instructionEncoder.EmitJMP(_helperCell); + return; + } switch (_thunkKind) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs index aa194d7dd7904e..d6b84149c2a08d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs @@ -21,6 +21,15 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instruc return; } + if (relocsOnly) + { + // When doing relocs only, we don't need to generate the actual instructions + // as they will be ignored. Just emit the module import load and jump so we record the dependencies. + instructionEncoder.EmitLD(Register.X11, factory.ModuleImport); + instructionEncoder.EmitJMP(_helperCell); + return; + } + switch (_thunkKind) { case Kind.Eager: From ef0686334c0cbfa44e38ba4b9f21c9ec4135b81c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 10 Nov 2025 09:58:06 -0800 Subject: [PATCH 4/5] Remove DIR64 reloc that is unnecessary --- .../DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs index 182acc4e803f43..5c5fedf1137b38 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs @@ -121,8 +121,6 @@ public void EmitJMP(ISymbolNode symbol) // jirl R0,R21,0 EmitJMP(Register.R21); - - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); } else { From b9f98350f9ec984d2eb1c15d98aca818cafc0b76 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 10 Nov 2025 10:19:16 -0800 Subject: [PATCH 5/5] Fix relocation sizes for Loongarch64 and RiscV64 --- .../tools/Common/Compiler/DependencyAnalysis/Relocation.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 2be41335d6b152..eb799d31c07afe 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -627,9 +627,9 @@ public static int GetSize(RelocType relocType) RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => 4, RelocType.IMAGE_REL_BASED_THUMB_MOV32 => 8, RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8, - RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 16, - RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR => 16, - RelocType.IMAGE_REL_BASED_RISCV64_PC => 16, + RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 8, + RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR => 8, + RelocType.IMAGE_REL_BASED_RISCV64_PC => 8, _ => throw new NotSupportedException(), }; }