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 +