From 5a2d1519b01b2935e343b6aba2989f97ed596914 Mon Sep 17 00:00:00 2001 From: Aleksandr Shaurtaev <38426614+ashaurtaev@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:28:26 +0300 Subject: [PATCH] [RISC-V] Add crossgen2 for riscv64 (#95188) * Add crossgen2 for riscv64 * Fix review comments * Fix review --- .../tools/Common/CommandLineHelpers.cs | 2 + .../DependencyAnalysis/AssemblyStubNode.cs | 8 + .../DependencyAnalysis/ObjectDataBuilder.cs | 3 + .../Compiler/DependencyAnalysis/Relocation.cs | 47 ++++ .../Target_RiscV64/AddrMode.cs | 32 +++ .../Target_RiscV64/Register.cs | 44 ++++ .../Target_RiscV64/RiscV64Emitter.cs | 135 +++++++++++ .../Target_RiscV64/TargetRegisterMap.cs | 38 +++ .../Common/Compiler/InstructionSetSupport.cs | 4 + .../tools/Common/JitInterface/CorInfoImpl.cs | 16 ++ .../Common/JitInterface/JitConfigProvider.cs | 1 + .../TypeSystem/Common/TargetArchitecture.cs | 1 + .../Common/TypeSystem/Common/TargetDetails.cs | 8 + .../ARMInitialInterfaceDispatchStubNode.cs | 5 + .../Target_RiscV64/RiscV64JumpStubNode.cs | 15 ++ .../RiscV64ReadyToRunGenericHelperNode.cs | 224 ++++++++++++++++++ .../RiscV64ReadyToRunHelperNode.cs | 199 ++++++++++++++++ .../RiscV64TentativeMethodNode.cs | 15 ++ .../Target_RiscV64/RiscV64UnboxingStubNode.cs | 17 ++ .../ILCompiler.Compiler.csproj | 9 + .../ILCompiler.Diagnostics.csproj | 2 +- .../ReadyToRun/ArgIterator.cs | 189 +++++++++++++++ .../ReadyToRun/MethodGCInfoNode.cs | 2 +- .../ReadyToRun/Target_RiscV64/ImportThunk.cs | 68 ++++++ .../ReadyToRun/TransitionBlock.cs | 45 ++++ .../ReadyToRunMetadataFieldLayoutAlgorithm.cs | 9 + .../ILCompiler.ReadyToRun.csproj | 5 + .../ObjectWriter/RelocationHelper.cs | 10 +- .../ObjectWriter/SectionBuilder.cs | 4 + .../ObjectWriter/TargetExtensions.cs | 3 + .../GCInfoTypes.cs | 10 + .../ReadyToRunMethod.cs | 2 +- .../ReadyToRunReader.cs | 2 + .../TransitionBlock.cs | 19 ++ .../aot/ILCompiler/ILCompilerRootCommand.cs | 2 +- .../aot/crossgen2/Crossgen2RootCommand.cs | 2 +- .../tools/aot/crossgen2/crossgen2.props | 2 +- .../PortableExecutable/PEHeaderBuilder.cs | 2 +- 38 files changed, 1193 insertions(+), 8 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index 9593e348a6c90..9dcb7c422de5e 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -96,6 +96,7 @@ public static TargetArchitecture GetTargetArchitecture(string token) Architecture.Arm => TargetArchitecture.ARM, Architecture.Arm64 => TargetArchitecture.ARM64, Architecture.LoongArch64 => TargetArchitecture.LoongArch64, + (Architecture)9 => TargetArchitecture.RiscV64, /* TODO: update with Architecture.RiscV64 */ _ => throw new NotImplementedException() }; } @@ -108,6 +109,7 @@ public static TargetArchitecture GetTargetArchitecture(string token) "arm" or "armel" => TargetArchitecture.ARM, "arm64" => TargetArchitecture.ARM64, "loongarch64" => TargetArchitecture.LoongArch64, + "riscv64" => TargetArchitecture.RiscV64, _ => throw new CommandLineException($"Target architecture '{token}' is not supported") }; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs index 6b51f875d1599..cc1e3898cd576 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs @@ -75,6 +75,13 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) loongarch64Emitter.Builder.AddSymbol(this); return loongarch64Emitter.Builder.ToObjectData(); + case TargetArchitecture.RiscV64: + RiscV64.RiscV64Emitter riscv64Emitter = new RiscV64.RiscV64Emitter(factory, relocsOnly); + EmitCode(factory, ref riscv64Emitter, relocsOnly); + riscv64Emitter.Builder.RequireInitialAlignment(alignment); + riscv64Emitter.Builder.AddSymbol(this); + return riscv64Emitter.Builder.ToObjectData(); + default: throw new NotImplementedException(); } @@ -85,5 +92,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) protected abstract void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly); protected abstract void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly); protected abstract void EmitCode(NodeFactory factory, ref LoongArch64.LoongArch64Emitter instructionEncoder, bool relocsOnly); + protected abstract void EmitCode(NodeFactory factory, ref RiscV64.RiscV64Emitter instructionEncoder, bool relocsOnly); } } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index d9d56e3a58234..19f367e82518c 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -319,6 +319,9 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: + + //TODO: consider removal of IMAGE_REL_RISCV64_JALR from runtime too + case RelocType.IMAGE_REL_BASED_RISCV64_PC: Debug.Assert(delta == 0); // Do not vacate space for this kind of relocation, because // the space is embedded in the instruction. diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index b833c909ff5b7..f98469294672e 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -19,6 +19,7 @@ public enum RelocType IMAGE_REL_BASED_ARM64_BRANCH26 = 0x15, // Arm64: B, BL IMAGE_REL_BASED_LOONGARCH64_PC = 0x16, // LoongArch64: pcaddu12i+imm12 IMAGE_REL_BASED_LOONGARCH64_JIR = 0x17, // LoongArch64: pcaddu18i+jirl + IMAGE_REL_BASED_RISCV64_PC = 0x18, // RiscV64: auipc IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc // This is a special NGEN-specific relocation type // for relative pointer (used to make NGen relocation @@ -411,6 +412,47 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) Debug.Assert(GetLoongArch64JIR(pCode) == imm38); } + private static unsafe int GetRiscV64PC(uint* pCode) + { + uint auipcInstr = *pCode; + Debug.Assert((auipcInstr & 0x7f) == 0x00000017); + // first get the high 20 bits, + int imm = (int)((auipcInstr & 0xfffff000)); + // then get the low 12 bits, + uint addiInstr = *(pCode + 1); + Debug.Assert((addiInstr & 0x707f) == 0x00000013); + imm += ((int)(addiInstr)) >> 20; + + return imm; + } + + // INS_OPTS_RELOC: placeholders. 2-ins: + // case:EA_HANDLE_CNS_RELOC + // auipc reg, off-hi-20bits + // addi reg, reg, off-lo-12bits + // case:EA_PTR_DSP_RELOC + // auipc reg, off-hi-20bits + // ld reg, reg, off-lo-12bits + private static unsafe void PutRiscV64PC(uint* pCode, long imm32) + { + // Verify that we got a valid offset + Debug.Assert((int)imm32 == imm32); + + int doff = (int)(imm32 & 0xfff); + uint auipcInstr = *pCode; + Debug.Assert((auipcInstr & 0x7f) == 0x00000017); + + auipcInstr |= (uint)((imm32 + 0x800) & 0xfffff000); + *pCode = auipcInstr; + + uint addiInstr = *(pCode + 1); + Debug.Assert((addiInstr & 0x707f) == 0x00000013); + addiInstr |= (uint)((doff & 0xfff) << 20); + *(pCode + 1) = addiInstr; + + Debug.Assert(GetRiscV64PC(pCode) == imm32); + } + public Relocation(RelocType relocType, int offset, ISymbolNode target) { RelocType = relocType; @@ -455,6 +497,9 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: PutLoongArch64JIR((uint*)location, value); break; + case RelocType.IMAGE_REL_BASED_RISCV64_PC: + PutRiscV64PC((uint*)location, value); + break; default: Debug.Fail("Invalid RelocType: " + relocType); break; @@ -517,6 +562,8 @@ public static unsafe long ReadValue(RelocType relocType, void* location) return (long)GetLoongArch64PC12((uint*)location); case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: return (long)GetLoongArch64JIR((uint*)location); + case RelocType.IMAGE_REL_BASED_RISCV64_PC: + return (long)GetRiscV64PC((uint*)location); default: Debug.Fail("Invalid RelocType: " + relocType); return 0; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs new file mode 100644 index 0000000000000..03ed433eae950 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.DependencyAnalysis.RiscV64 +{ + public enum AddrModeSize + { + Int8 = 1, + Int16 = 2, + Int32 = 4, + Int64 = 8, + Int128 = 16 + } + + public struct AddrMode + { + public readonly Register BaseReg; + public readonly Register? IndexReg; + public readonly int Offset; + public readonly byte Scale; + public readonly AddrModeSize Size; + + public AddrMode(Register baseRegister, Register? indexRegister, int offset, byte scale, AddrModeSize size) + { + BaseReg = baseRegister; + IndexReg = indexRegister; + Offset = offset; + Scale = scale; + Size = size; + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs new file mode 100644 index 0000000000000..2d3a13fe0039b --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.DependencyAnalysis.RiscV64 +{ + public enum Register + { + X0, + X1, + X2, + X3, + X4, + X5, + X6, + X7, + X8, + X9, + X10, + X11, + X12, + X13, + X14, + X15, + X16, + X17, + X18, + X19, + X20, + X21, + X22, + X23, + X24, + X25, + X26, + X27, + X28, + X29, + X30, + X31, + + None, + NoIndex = 128 + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs new file mode 100644 index 0000000000000..7bec609b539d2 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis.RiscV64 +{ + public struct RiscV64Emitter + { + public RiscV64Emitter(NodeFactory factory, bool relocsOnly) + { + Builder = new ObjectDataBuilder(factory, relocsOnly); + TargetRegister = new TargetRegisterMap(factory.Target.OperatingSystem); + } + + public ObjectDataBuilder Builder; + public TargetRegisterMap TargetRegister; + + // Assembly stub creation api. TBD, actually make this general purpose + + //ebreak + public void EmitBreak() + { + Builder.EmitUInt(0x00100073); + } + + public void EmitLI(Register regDst, int offset) + { + Debug.Assert((offset >= -2048) && (offset <= 2047)); + EmitADDI(regDst, Register.X0, offset); + } + + public void EmitMOV(Register regDst, Register regSrc) + { + EmitADDI(regDst, regSrc, 0); + } + + public void EmitMOV(Register regDst, ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC); + //auipc reg, off-hi-20bits + EmitPC(regDst); + //addi reg, reg, off-lo-12bits + EmitADDI(regDst, regDst, 0); + } + + // auipc regDst, 0 + public void EmitPC(Register regDst) + { + Debug.Assert((uint)regDst > 0 && (uint)regDst < 32); + Builder.EmitUInt(0x00000017u | (uint)regDst << 7); + } + + // addi regDst, regSrc, offset + public void EmitADDI(Register regDst, Register regSrc, int offset) + { + Debug.Assert((uint)regDst <= 0x1f); + Debug.Assert((uint)regSrc <= 0x1f); + Debug.Assert((offset >= -2048) && (offset <= 2047)); + Builder.EmitUInt((uint)(0x00000013u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20))); + } + + // xori regDst, regSrc, offset + public void EmitXORI(Register regDst, Register regSrc, int offset) + { + Debug.Assert((offset >= -2048) && (offset <= 2047)); + Builder.EmitUInt((uint)(0x00004013u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20))); + } + + // ld regDst, offset(regSrc) + public void EmitLD(Register regDst, Register regSrc, int offset) + { + Debug.Assert((offset >= -2048) && (offset <= 2047)); + Builder.EmitUInt((uint)(0x00003003u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20))); + } + + // jalr regDst, offset(regSrc) + public void EmitJALR(Register regDst, Register regSrc, int offset) + { + Debug.Assert((offset >= -2048) && (offset <= 2047)); + Builder.EmitUInt((uint)(0x00000067u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20))); + } + + public void EmitRET() + { + // jalr x0,0(x1) + EmitJALR(Register.X0, Register.X1, 0); + } + + public void EmitJMP(Register reg) + { + //jalr x0, 0(reg) + EmitJALR(Register.X0, reg, 0); + } + + public void EmitJMP(ISymbolNode symbol) + { + if (symbol.RepresentsIndirectionCell) + { + //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); + //jalr x0,0(x29) + EmitJALR(Register.X0, Register.X29, 0); + + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); + } + else + { + Builder.EmitUInt(0x00000000); // bad code. + throw new NotImplementedException(); + } + } + + public void EmitRETIfZero(Register regSrc) + { + // bne regSrc, x0, 8 + Builder.EmitUInt((uint)(0x00001463 | ((uint)regSrc << 15))); + EmitRET(); + } + + public void EmitJMPIfZero(Register regSrc, ISymbolNode symbol) + { + uint offset = symbol.RepresentsIndirectionCell ? 28u : 8u; + uint encodedOffset = ((offset & 0x1e) << 7) | ((offset & 0x7e0) << 20) | ((offset & 0x800) >> 4) | ((offset & 0x1000) << 19); + // bne regSrc, x0, offset + Builder.EmitUInt((uint)(0x00001063 | ((uint)regSrc << 15) | encodedOffset)); + EmitJMP(symbol); + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs new file mode 100644 index 0000000000000..493fc44c2108f --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.RiscV64 +{ + /// + /// Maps logical registers to physical registers on a specified OS. + /// + public struct TargetRegisterMap + { + public readonly Register Arg0; + public readonly Register Arg1; + public readonly Register Arg2; + public readonly Register Arg3; + public readonly Register Arg4; + public readonly Register Arg5; + public readonly Register Arg6; + public readonly Register Arg7; + public readonly Register IntraProcedureCallScratch1; + public readonly Register Result; + + public TargetRegisterMap(TargetOS os) + { + Arg0 = Register.X10; + Arg1 = Register.X11; + Arg2 = Register.X12; + Arg3 = Register.X13; + Arg4 = Register.X14; + Arg5 = Register.X15; + Arg6 = Register.X16; + Arg7 = Register.X17; + IntraProcedureCallScratch1 = Register.X28; + Result = Register.X10; + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs index d66eee9833a66..bb74b7085d3cc 100644 --- a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs +++ b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs @@ -141,6 +141,10 @@ public SimdVectorLength GetVectorTSimdVector() { return SimdVectorLength.None; } + else if (_targetArchitecture == TargetArchitecture.RiscV64) + { + return SimdVectorLength.None; + } else { Debug.Assert(false); // Unknown architecture diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 9e5120609ebff..19faf31a20867 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -56,6 +56,7 @@ private enum ImageFileMachine ARM = 0x01c4, ARM64 = 0xaa64, LoongArch64 = 0x6264, + RiscV64 = 0x5064, } internal const string JitLibrary = "clrjitilc"; @@ -3883,6 +3884,19 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush return 0; } } + case TargetArchitecture.RiscV64: + { + const ushort IMAGE_REL_RISCV64_PC = 3; + + switch (fRelocType) + { + case IMAGE_REL_RISCV64_PC: + return RelocType.IMAGE_REL_BASED_RISCV64_PC; + default: + Debug.Fail("Invalid RelocType: " + fRelocType); + return 0; + } + } default: return (RelocType)fRelocType; } @@ -3984,6 +3998,8 @@ private uint getExpectedTargetArchitecture() return (uint)ImageFileMachine.ARM64; case TargetArchitecture.LoongArch64: return (uint)ImageFileMachine.LoongArch64; + case TargetArchitecture.RiscV64: + return (uint)ImageFileMachine.RiscV64; default: throw new NotImplementedException("Expected target architecture is not supported"); } diff --git a/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs b/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs index 2605d5489dea4..eb7b71e870e26 100644 --- a/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs +++ b/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs @@ -138,6 +138,7 @@ private static string GetTargetSpec(TargetDetails target) TargetArchitecture.ARM => "arm", TargetArchitecture.ARM64 => "arm64", TargetArchitecture.LoongArch64 => "loongarch64", + TargetArchitecture.RiscV64 => "riscv64", _ => throw new NotImplementedException(target.Architecture.ToString()) }; diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs index 928e3018b0672..2d188fbada04c 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs @@ -15,5 +15,6 @@ public enum TargetArchitecture X86, Wasm32, LoongArch64, + RiscV64, } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs index 2a4df49f1c2bd..16b0d9e6a3c75 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs @@ -86,6 +86,7 @@ public int PointerSize case TargetArchitecture.ARM64: case TargetArchitecture.X64: case TargetArchitecture.LoongArch64: + case TargetArchitecture.RiscV64: return 8; case TargetArchitecture.ARM: case TargetArchitecture.X86: @@ -126,6 +127,10 @@ public int MaximumAlignment { return 16; } + else if (Architecture == TargetArchitecture.RiscV64) + { + return 16; + } // 512-bit vector is the type with the highest alignment we support return 64; @@ -183,6 +188,7 @@ public int MinimumCodeAlignment return 2; case TargetArchitecture.ARM64: case TargetArchitecture.LoongArch64: + case TargetArchitecture.RiscV64: return 4; default: return 1; @@ -288,6 +294,7 @@ public LayoutInt GetObjectAlignment(LayoutInt fieldAlignment) case TargetArchitecture.X64: case TargetArchitecture.ARM64: case TargetArchitecture.LoongArch64: + case TargetArchitecture.RiscV64: return new LayoutInt(8); case TargetArchitecture.X86: return new LayoutInt(4); @@ -337,6 +344,7 @@ public int MaxHomogeneousAggregateElementCount Debug.Assert(Architecture == TargetArchitecture.ARM || Architecture == TargetArchitecture.ARM64 || Architecture == TargetArchitecture.LoongArch64 || + Architecture == TargetArchitecture.RiscV64 || Architecture == TargetArchitecture.X64 || Architecture == TargetArchitecture.X86); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs index 13c7335323480..7b7c6ba688c7b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs @@ -9,6 +9,7 @@ using ILCompiler.DependencyAnalysis.X86; using ILCompiler.DependencyAnalysis.ARM64; using ILCompiler.DependencyAnalysis.LoongArch64; +using ILCompiler.DependencyAnalysis.RiscV64; namespace ILCompiler.DependencyAnalysis { @@ -56,6 +57,10 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins { throw new NotImplementedException(); } + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } public override int ClassCode => 588185132; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs new file mode 100644 index 0000000000000..784c856dc6d24 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.RiscV64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs new file mode 100644 index 0000000000000..a382331040c4d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.RiscV64; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ RiscV64Emitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref RiscV64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + // INVARIANT: must not trash context register + + // Find the generic dictionary slot + int dictionarySlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + if (!factory.GenericDictionaryLayout(_dictionaryOwner).TryGetSlotForEntry(lookup, out dictionarySlot)) + { + encoder.EmitLI(result, 0); + return; + } + } + + // Load the generic dictionary cell + encoder.EmitLD(result, context, dictionarySlot * factory.Target.PointerSize); + } + + protected sealed override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + // First load the generic context into the context register. + EmitLoadGenericContext(factory, ref encoder, relocsOnly); + + Register contextRegister = GetContextRegister(ref encoder); + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + + if (!TriggersLazyStaticConstructor(factory)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitADDI(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg0, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, 0); + encoder.EmitRETIfZero(encoder.TargetRegister.Arg2); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0); + + MetadataType target = (MetadataType)_target; + if (!TriggersLazyStaticConstructor(factory)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0); + encoder.EmitRETIfZero(encoder.TargetRegister.Arg3); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + // Look up the index cell + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (TriggersLazyStaticConstructor(factory)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -cctorContextSize); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, 0); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLD(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize); + + encoder.EmitJMP(helperEntrypoint); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor + // method expects. Codegen also passed us the generic context in Arg2. + // We now need to load the delegate target method into Arg2 (using a dictionary lookup) + // and the optional 4th parameter, and call the ctor. + + Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); + + var target = (DelegateCreationInfo)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.ObjectAllocator: + case ReadyToRunHelperId.TypeHandleForCasting: + case ReadyToRunHelperId.ConstrainedDirectCall: + { + EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + break; + + default: + throw new NotImplementedException(); + } + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + public partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + // We start with context register pointing to the MethodTable + Register contextRegister = GetContextRegister(ref encoder); + + // Locate the VTable slot that points to the dictionary + int vtableSlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); + } + + int pointerSize = factory.Target.PointerSize; + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + + // Load the dictionary pointer from the VTable + encoder.EmitLD(contextRegister, contextRegister, slotOffset); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs new file mode 100644 index 0000000000000..e4288acba29a3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using ILCompiler.DependencyAnalysis.RiscV64; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// RiscV64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + switch (Id) + { + case ReadyToRunHelperId.VirtualCall: + { + MethodDesc targetMethod = (MethodDesc)Target; + + Debug.Assert(!targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) + { + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + } + + encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); + encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, + EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); + encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); + } + break; + + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)Target; + + bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitADDI(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, 0); + encoder.EmitRETIfZero(encoder.TargetRegister.Arg2); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, 0); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLD(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + ISymbolNode helper = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(helper); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0); + encoder.EmitJMPIfZero(encoder.TargetRegister.Arg3, helper); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + MetadataType target = (MetadataType)Target; + + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); + encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0); + encoder.EmitRETIfZero(encoder.TargetRegister.Arg3); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo target = (DelegateCreationInfo)Target; + + if (target.TargetNeedsVTableLookup) + { + Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); + + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1, 0); + + int slot = 0; + if (!relocsOnly) + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); + + Debug.Assert(slot != -1); + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, + EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); + } + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + case ReadyToRunHelperId.ResolveVirtualFunction: + { + // Not tested + encoder.EmitBreak(); + + MethodDesc targetMethod = (MethodDesc)Target; + if (targetMethod.OwningType.IsInterface) + { + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); + } + else + { + if (relocsOnly) + break; + + encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0, 0); + + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, + EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); + encoder.EmitRET(); + } + } + break; + + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs new file mode 100644 index 0000000000000..1521b85b9155f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.RiscV64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs new file mode 100644 index 0000000000000..1852a091f39e0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.RiscV64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly) + { + // addi a0, a0, sizeof(void*) + encoder.EmitADDI(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, factory.Target.PointerSize); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 2060c0cea6954..2d3c3eb21fc9a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -313,6 +313,10 @@ + + + + @@ -432,6 +436,7 @@ + @@ -587,6 +592,10 @@ + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj index 508c33ce52440..9856c38cf9810 100644 --- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj @@ -6,7 +6,7 @@ true $(NetCoreAppToolCurrent) READYTORUN;$(DefineConstants) - x64;x86;arm;arm64 + x64;x86;arm;arm64; AnyCPU false diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 78dfc95c757be..eee92e23a8682 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -624,6 +624,13 @@ public bool IsArgPassedByRef() return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle)); } return false; + case TargetArchitecture.RiscV64: + if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) + { + Debug.Assert(!_argTypeHandle.IsNull()); + return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle)); + } + return false; default: throw new NotImplementedException(); } @@ -827,6 +834,12 @@ public int GetNextOffset() _loongarch64IdxFPReg = 0; break; + case TargetArchitecture.RiscV64: + _riscv64IdxGenReg = numRegistersUsed; + _riscv64OfsStack = 0; + + _riscv64IdxFPReg = 0; + break; default: throw new NotImplementedException(); } @@ -1434,6 +1447,128 @@ public int GetNextOffset() return argOfs; } + case TargetArchitecture.RiscV64: + { + int cFPRegs = 0; + uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + _hasArgLocDescForStructInRegs = false; + + switch (argType) + { + case CorElementType.ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: + { + // Composite greater than 16 bytes should be passed by reference + if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize) + { + argSize = _transitionBlock.PointerSize; + } + else + { + floatFieldFlags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); + if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + { + cFPRegs = 2; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + { + cFPRegs = 1; + } + } + + break; + } + + default: + break; + } + + bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE); + int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false); + + if (cFPRegs > 0 && !IsVarArg) + { + if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + { + Debug.Assert(cFPRegs == 1); + if ((_riscv64IdxFPReg < 8) && (_riscv64IdxGenReg < 8)) + { + _argLocDescForStructInRegs = new ArgLocDesc(); + _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; + _argLocDescForStructInRegs.m_cFloatReg = 1; + + _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg; + _argLocDescForStructInRegs.m_cGenReg = 1; + + _hasArgLocDescForStructInRegs = true; + _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; + + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + _riscv64IdxFPReg++; + _riscv64IdxGenReg++; + return argOfsInner; + } + } + else if (cFPRegs + _riscv64IdxFPReg <= 8) + { + // Each floating point register in the argument area is 8 bytes. + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) + { + // struct with two single-float fields. + _argLocDescForStructInRegs = new ArgLocDesc(); + _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; + _argLocDescForStructInRegs.m_cFloatReg = 2; + Debug.Assert(cFPRegs == 2); + Debug.Assert(argSize == 8); + + _hasArgLocDescForStructInRegs = true; + _argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO; + } + _riscv64IdxFPReg += cFPRegs; + return argOfsInner; + } + else + { + _riscv64IdxFPReg = 8; + } + } + + { + Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0); + + int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; + // Only a0-a7 are valid argument registers. + if (_riscv64IdxGenReg + regSlots <= 8) + { + // The entirety of the arg fits in the register slots. + int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8; + _riscv64IdxGenReg += regSlots; + return argOfsInner; + } + else if (_riscv64IdxGenReg < 8) + { + int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8; + _riscv64IdxGenReg = 8; + _riscv64OfsStack += 8; + return argOfsInner; + } + } + + argOfs = _transitionBlock.OffsetOfArgs + _riscv64OfsStack; + _riscv64OfsStack += cbArg; + return argOfs; + } + default: throw new NotImplementedException(); } @@ -1766,6 +1901,56 @@ private void ForceSigWalk() return pLoc; } + case TargetArchitecture.RiscV64: + { + if (_hasArgLocDescForStructInRegs) + { + return _argLocDescForStructInRegs; + } + + // LIMITED_METHOD_CONTRACT; + + ArgLocDesc pLoc = new ArgLocDesc(); + + if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset)) + { + int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters; + Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0); + pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize; + pLoc.m_cFloatReg = 1; + + return pLoc; + } + + int byteArgSize = GetArgSize(); + + // Composites greater than 16bytes are passed by reference + TypeHandle dummy; + if (GetArgType(out dummy) == CorElementType.ELEMENT_TYPE_VALUETYPE && GetArgSize() > _transitionBlock.EnregisteredParamTypeMaxSize) + { + byteArgSize = _transitionBlock.PointerSize; + } + + if (!_transitionBlock.IsStackArgumentOffset(argOffset)) + { + pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset); + if ((pLoc.m_idxGenReg == 7) && (byteArgSize > _transitionBlock.PointerSize)) + { + pLoc.m_cGenReg = 1; + pLoc.m_byteStackIndex = 0; + pLoc.m_byteStackSize = 8; + } + else + pLoc.m_cGenReg = (short)(ALIGN_UP(byteArgSize, _transitionBlock.PointerSize) / _transitionBlock.PointerSize); + } + else + { + pLoc.m_byteStackIndex = _transitionBlock.GetStackArgumentByteIndexFromOffset(argOffset); + pLoc.m_byteStackSize = _transitionBlock.StackElemSize(byteArgSize, IsValueType(), IsFloatHfa()); + } + return pLoc; + } + case TargetArchitecture.X64: if (_transitionBlock.IsX64UnixABI) { @@ -1855,6 +2040,10 @@ private void ForceSigWalk() private int _loongarch64OfsStack; // Offset of next stack location to be assigned a value private int _loongarch64IdxFPReg; // Next FP register to be assigned a value + private int _riscv64IdxGenReg; // Next general register to be assigned a value + private int _riscv64OfsStack; // Offset of next stack location to be assigned a value + private int _riscv64IdxFPReg; // Next FP register to be assigned a value + // These are enum flags in CallingConventions.h, but that's really ugly in C#, so I've changed them to bools. private bool _ITERATION_STARTED; // Started iterating over arguments private bool _SIZE_OF_ARG_STACK_COMPUTED; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs index ccb91bbc8a6df..095387bf52fb9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs @@ -197,7 +197,7 @@ private IEnumerable EncodeDataCore(NodeFactory factory) // as that's what CoreCLR does (zapcode.cpp, ZapUnwindData::Save). unwindInfo[0] |= (byte)((UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER) << FlagsShift); } - else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64) || (targetArch == TargetArchitecture.LoongArch64)) + else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64) || (targetArch == TargetArchitecture.LoongArch64) || (targetArch == TargetArchitecture.RiscV64)) { // Set the 'X' bit to indicate that there is a personality routine associated with this method unwindInfo[2] |= 1 << 4; 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 new file mode 100644 index 0000000000000..9db82c334f1d6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.RiscV64; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + /// + /// This node emits a thunk calling DelayLoad_Helper with a given instance signature + /// to populate its indirection cell. + /// + public partial class ImportThunk + { + protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly) + { + + switch (_thunkKind) + { + case Kind.Eager: + break; + + case Kind.DelayLoadHelper: + case Kind.VirtualStubDispatch: + // t5 contains indirection cell + // Do nothing t5 contains our first param + if (!relocsOnly) + { + // li t0, #index + int index = _containingImportSection.IndexFromBeginningOfArray; + instructionEncoder.EmitLI(Register.X5, index); + } + // get pc + // auipc t1, 0 + instructionEncoder.EmitPC(Register.X6); + + // load Module* -> t1 + instructionEncoder.EmitLD(Register.X6, Register.X6, 0x24); + + // ld t1, t1, 0 + instructionEncoder.EmitLD(Register.X6, Register.X6, 0); + break; + + case Kind.Lazy: + // get pc + instructionEncoder.EmitPC(Register.X11); + + // load Module* -> a1 + instructionEncoder.EmitLD(Register.X11, Register.X11, 0x24); + + // ld a1, a1, 0 + instructionEncoder.EmitLD(Register.X11, Register.X11, 0); + break; + + default: + throw new NotImplementedException(); + } + + // branch to helper + instructionEncoder.EmitJMP(_helperCell); + + // Emit relocation for the Module* load above + if (_thunkKind != Kind.Eager) + instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 09372f143813c..7f2b1fd25ee4f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -46,6 +46,9 @@ public static TransitionBlock FromTarget(TargetDetails target) case TargetArchitecture.LoongArch64: return LoongArch64TransitionBlock.Instance; + case TargetArchitecture.RiscV64: + return RiscV64TransitionBlock.Instance; + default: throw new NotImplementedException(target.Architecture.ToString()); } @@ -64,6 +67,7 @@ public static TransitionBlock FromTarget(TargetDetails target) public bool IsARM => Architecture == TargetArchitecture.ARM; public bool IsARM64 => Architecture == TargetArchitecture.ARM64; public bool IsLoongArch64 => Architecture == TargetArchitecture.LoongArch64; + public bool IsRiscV64 => Architecture == TargetArchitecture.RiscV64; /// /// This property is only overridden in AMD64 Unix variant of the transition block. @@ -386,7 +390,10 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp { if (IsLoongArch64) fpReturnSize = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; + else if (IsRiscV64) + fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; break; + } } @@ -683,5 +690,43 @@ public override int StackElemSize(int parmSize, bool isValueType = false, bool i return ALIGN_UP(parmSize, stackSlotSize); } } + + private class RiscV64TransitionBlock : TransitionBlock + { + public static TransitionBlock Instance = new RiscV64TransitionBlock(); + public override TargetArchitecture Architecture => TargetArchitecture.RiscV64; + public override int PointerSize => 8; + public override int FloatRegisterSize => 8; + // a0 .. a7 + public override int NumArgumentRegisters => 8; + // fp=x8, ra=x1, s1-s11(R9,R18-R27), tp=x3, gp=x4 + public override int NumCalleeSavedRegisters => 15; + // Callee-saves, argument registers + public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters; + public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot; + + public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double); + public override int EnregisteredParamTypeMaxSize => 16; + public override int EnregisteredReturnTypeIntegerMaxSize => 16; + + public override bool IsArgPassedByRef(TypeHandle th) + { + Debug.Assert(!th.IsNull()); + Debug.Assert(th.IsValueType()); + + // Composites greater than 16 bytes are passed by reference + return th.GetSize() > EnregisteredParamTypeMaxSize; + } + + public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0); + + public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + int stackSlotSize = 8; + return ALIGN_UP(parmSize, stackSlotSize); + } + + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index de15cbc1e2314..4cf6d65ea4390 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -121,6 +121,11 @@ private class ModuleFieldLayoutMap : LockFreeReaderHashtable private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64 = 8; + /// + /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for RISCV64 + /// + private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64 = 8; + protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value) { return key == value.Module; @@ -423,6 +428,10 @@ public FieldAndOffset[] GetOrAddDynamicLayout(DefType defType, ModuleFieldLayout nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64; break; + case TargetArchitecture.RiscV64: + nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64; + break; + default: throw new NotImplementedException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 7631bd92e458f..9db169fb6dae0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -98,6 +98,10 @@ + + + + @@ -231,6 +235,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs index d0d994737f262..e68214579f79a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs @@ -228,6 +228,13 @@ public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targe break; } + case RelocType.IMAGE_REL_BASED_RISCV64_PC: + { + relocationLength = 8; + delta = targetRVA - sourceRVA; + break; + } + default: throw new NotSupportedException(); } @@ -244,7 +251,8 @@ public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targe if (((relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21) || (relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A) || (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_PC) || - (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR) + (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR) || + (relocationType == RelocType.IMAGE_REL_BASED_RISCV64_PC) ) && (value != 0)) { throw new NotSupportedException(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index 768969174025a..e10348e562e1e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs @@ -317,6 +317,10 @@ public SectionBuilder(TargetDetails target) _codePadding = 0x002A0005u; break; + case TargetArchitecture.RiscV64: + _codePadding = 0x00100073u; + break; + default: throw new NotImplementedException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs index 8b58919385046..c48c7055de953 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs @@ -90,6 +90,9 @@ public static Machine MachineFromTarget(this TargetDetails target) case Internal.TypeSystem.TargetArchitecture.LoongArch64: return Machine.LoongArch64; + case Internal.TypeSystem.TargetArchitecture.RiscV64: + return (Machine)0x5064; /* TODO: update with RiscV64 */ + default: throw new NotImplementedException(target.Architecture.ToString()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs index 9356bce3ccccd..b6117dd46eff1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs @@ -154,6 +154,11 @@ internal GcInfoTypes(Machine machine) STACK_BASE_REGISTER_ENCBASE = 2; NUM_REGISTERS_ENCBASE = 3; break; + case (Machine)0x5064: /* TODO: update with RiscV64 */ + SIZE_OF_RETURN_KIND_FAT = 4; + STACK_BASE_REGISTER_ENCBASE = 2; + NUM_REGISTERS_ENCBASE = 3; + break; } } @@ -165,6 +170,7 @@ internal int DenormalizeCodeLength(int x) return (x << 1); case Machine.Arm64: case Machine.LoongArch64: + case (Machine)0x5064: /* TODO: update with RiscV64 */ return (x << 2); } return x; @@ -180,6 +186,7 @@ internal int DenormalizeStackSlot(int x) return (x << 2); case Machine.Arm64: case Machine.LoongArch64: + case (Machine)0x5064: /* TODO: update with RiscV64 */ return (x << 3); } return x; @@ -197,6 +204,8 @@ internal uint DenormalizeStackBaseRegister(uint x) return (x ^ 29); case Machine.LoongArch64: return ((x ^ 22) & 0x3); + case (Machine)0x5064: /* TODO: update with RiscV64 */ + return (x ^ 8); } return x; } @@ -211,6 +220,7 @@ internal uint DenormalizeSizeOfStackArea(uint x) return (x << 2); case Machine.Arm64: case Machine.LoongArch64: + case (Machine)0x5064: /* TODO: update with RiscV64 */ return (x << 3); } return x; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs index dee60ea5daf3f..37857ee0876d2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs @@ -492,7 +492,7 @@ private void EnsureInitialized() } else { - // Arm, Arm64 and LoongArch64 use the same GcInfo format as Amd64 + // Arm, Arm64, LoongArch64 and RISCV64 use the same GcInfo format as Amd64 _gcInfo = new Amd64.GcInfo(_readyToRunReader.Image, gcInfoOffset, _readyToRunReader.Machine, _readyToRunReader.ReadyToRunHeader.MajorVersion); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index c46bcc6707d5d..927c9755f258b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -633,6 +633,7 @@ private unsafe void EnsureHeader() case Machine.Amd64: case Machine.Arm64: case Machine.LoongArch64: + case (Machine)0x5064: /* TODO: update with RiscV64 */ _pointerSize = 8; break; @@ -1416,6 +1417,7 @@ private void EnsureImportSections() case Machine.Amd64: case Machine.Arm64: case Machine.LoongArch64: + case (Machine)0x5064: /* TODO: update with RiscV64 */ entrySize = 8; break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs index dcd1f531443bb..4687f632f5d56 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs @@ -34,6 +34,9 @@ public static TransitionBlock FromReader(ReadyToRunReader reader) case Machine.LoongArch64: return LoongArch64TransitionBlock.Instance; + case (Machine)0x5064: /* TODO: update with RiscV64 */ + return RiscV64TransitionBlock.Instance; + default: throw new NotImplementedException(); } @@ -169,5 +172,21 @@ private sealed class LoongArch64TransitionBlock : TransitionBlock public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters; public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot; } + + private sealed class RiscV64TransitionBlock : TransitionBlock + { + public static readonly TransitionBlock Instance = new RiscV64TransitionBlock(); + + public override int PointerSize => 8; + // a0 .. a7 + public override int NumArgumentRegisters => 8; + // fp=x8, ra=x1, s1-s11(R9,R18-R27), tp=x3, gp=x4 + public override int NumCalleeSavedRegisters => 15; + // Callee-saves, argument registers + public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters; + public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot; + } + } } diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index d096cc9bbeb12..84f9c5acd9444 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -319,7 +319,7 @@ public static IEnumerable> GetExtendedHelp(HelpContext _ Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + "considered to be input files. If no input files begin with '--' then this option is not necessary.\n"); - string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; + string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64", "riscv64" }; string[] ValidOS = new string[] { "windows", "linux", "freebsd", "osx", "maccatalyst", "ios", "iossimulator", "tvos", "tvossimulator" }; Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()); diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index 4913c0baa249a..5159f474f0101 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -294,7 +294,7 @@ public static IEnumerable> GetExtendedHelp(HelpContext _ Console.WriteLine(SR.DashDashHelp); Console.WriteLine(); - string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; + string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64", "riscv64"}; string[] ValidOS = new string[] {"windows", "linux", "osx"}; Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant())); diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index 56ed8dd4277b3..f96f22934a8a9 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -4,7 +4,7 @@ true Exe 8002,NU1701 - x64;x86;arm64;arm;loongarch64 + x64;x86;arm64;arm;loongarch64;riscv64 AnyCPU false false diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs index bd7f249b32e90..b32a92677704d 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs @@ -105,7 +105,7 @@ public static PEHeaderBuilder CreateLibraryHeader() return new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll); } - internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64 && Machine != Machine.Arm64; + internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64 && Machine != Machine.Arm64 && Machine != (Machine)0x5064; /* TODO: update with RiscV64 */ internal int ComputeSizeOfPEHeaders(int sectionCount) => PEBuilder.DosHeaderSize +