From 5c22f19fd16086c3a85fd6128c511ffa2a32a9a0 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Tue, 12 Jul 2022 17:41:45 +0800 Subject: [PATCH 1/7] [LoongArch64] add crossgen2 for LoongArch64. --- src/coreclr/jit/emitloongarch64.cpp | 2 +- .../DependencyAnalysis/AssemblyStubNode.cs | 8 + .../DependencyAnalysis/ObjectDataBuilder.cs | 2 + .../Compiler/DependencyAnalysis/Relocation.cs | 102 +++++++++ .../Target_LoongArch64/AddrMode.cs | 34 +++ .../Target_LoongArch64/LoongArch64Emitter.cs | 70 +++++++ .../Target_LoongArch64/Register.cs | 50 +++++ .../Target_LoongArch64/TargetRegisterMap.cs | 38 ++++ .../Common/Compiler/InstructionSetSupport.cs | 4 + .../tools/Common/JitInterface/CorInfoImpl.cs | 27 ++- .../tools/Common/JitInterface/CorInfoTypes.cs | 40 ++++ .../Common/JitInterface/JitConfigProvider.cs | 1 + .../LoongArch64PassStructInRegister.cs | 172 ++++++++++++++++ .../TypeSystem/Common/TargetArchitecture.cs | 1 + .../Common/TypeSystem/Common/TargetDetails.cs | 8 + .../ReadyToRun/ArgIterator.cs | 194 +++++++++++++++++- .../ReadyToRun/MethodGCInfoNode.cs | 2 +- .../Target_LoongArch64/ImportThunk.cs | 71 +++++++ .../ReadyToRun/TransitionBlock.cs | 57 +++++ .../ReadyToRunMetadataFieldLayoutAlgorithm.cs | 9 + .../ILCompiler.ReadyToRun.csproj | 6 + .../ObjectWriter/RelocationHelper.cs | 16 +- .../ObjectWriter/SectionBuilder.cs | 4 + .../ObjectWriter/TargetExtensions.cs | 3 + .../ReadyToRunReader.cs | 5 + .../TransitionBlock.cs | 15 ++ src/coreclr/tools/aot/crossgen2/Program.cs | 5 + .../tools/aot/crossgen2/crossgen2.props | 2 +- 28 files changed, 941 insertions(+), 7 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/AddrMode.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs create mode 100644 src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index a1d8c2a9a6cacd..d2213845552a08 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -3213,7 +3213,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dstRW += 4; - emitRecordRelocation(dstRW2, id->idAddr()->iiaAddr, IMAGE_REL_LOONGARCH64_PC); + emitRecordRelocation(dstRW2 - writeableOffset, id->idAddr()->iiaAddr, IMAGE_REL_LOONGARCH64_PC); dstRW2 += 4; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs index b95daf56f0b45e..b289ff347063db 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs @@ -68,6 +68,13 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) arm64Emitter.Builder.AddSymbol(this); return arm64Emitter.Builder.ToObjectData(); + case TargetArchitecture.LoongArch64: + LoongArch64.LoongArch64Emitter loongarch64Emitter = new LoongArch64.LoongArch64Emitter(factory, relocsOnly); + EmitCode(factory, ref loongarch64Emitter, relocsOnly); + loongarch64Emitter.Builder.RequireInitialAlignment(alignment); + loongarch64Emitter.Builder.AddSymbol(this); + return loongarch64Emitter.Builder.ToObjectData(); + default: throw new NotImplementedException(); } @@ -77,5 +84,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) protected abstract void EmitCode(NodeFactory factory, ref X86.X86Emitter instructionEncoder, 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); } } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index da20de18b9f87c..f20020ad71051a 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -303,6 +303,8 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: + case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: 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 eda9378acc36d4..985fca97a18be6 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -16,6 +16,8 @@ public enum RelocType IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, // Thumb2: based B, BL IMAGE_REL_BASED_THUMB_MOV32_PCREL = 0x14, // Thumb2: based MOVW/MOVT 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_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 @@ -294,7 +296,97 @@ private static unsafe void PutArm64Rel28(uint* pCode, long imm28) Debug.Assert(GetArm64Rel28(pCode) == imm28); } + private static unsafe int GetLoongArch64PC12(uint* pCode) + { + uint pcInstr = *pCode; + + // first get the hight 20 bits, + int imm = (int)(((pcInstr >> 5) & 0xFFFFF) << 12); + + // then get the low 12 bits, + pcInstr = *(pCode + 1); + imm += ((int)(((pcInstr >> 10) & 0xFFF) << 20)) >> 20; + + return imm; + } + + // case:EA_HANDLE_CNS_RELOC + // pcaddu12i reg, off-hi-20bits + // addi_d reg, reg, off-lo-12bits + // case:EA_PTR_DSP_RELOC + // pcaddu12i reg, off-hi-20bits + // ld_d reg, reg, off-lo-12bits + private static unsafe void PutLoongArch64PC12(uint* pCode, long imm32) + { + // Verify that we got a valid offset + Debug.Assert((int)imm32 == imm32); + + uint pcInstr = *pCode; + + Debug.Assert((pcInstr & 0xFE000000) == 0x1c000000); // Must be pcaddu12i + + int relOff = (int)imm32 & 0x800; + int imm = (int)imm32 + relOff; + relOff = ((imm & 0x7ff) - relOff) & 0xfff; + + // Assemble the pc-relative hight20bits of 'imm32' into the pcaddu12i instruction + pcInstr |= (uint)(((imm >> 12) & 0xFFFFF) << 5); + + *pCode = pcInstr; // write the assembled instruction + + pcInstr = *(pCode + 1); + + // Assemble the pc-relative low12bits of 'imm32' into the addid or ld instruction + pcInstr |= (uint)(relOff << 10); + + *(pCode + 1) = pcInstr; // write the assembled instruction + + Debug.Assert(GetLoongArch64PC12(pCode) == imm32); + } + + private static unsafe long GetLoongArch64JIR(uint* pCode) + { + uint pcInstr = *pCode; + // first get the high 20 bits, + long imm = ((long)((pcInstr >> 5) & 0xFFFFF) << 18); + + // then get the low 18 bits + pcInstr = *(pCode + 1); + imm += ((long)((short)((pcInstr >> 10) & 0xFFFF))) << 2; + + return imm; + } + + private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) + { + // Verify that we got a valid offset + Debug.Assert((imm38 >= -0x2000000000L) && (imm38 < 0x2000000000L)); + + Debug.Assert((imm38 & 0x3) == 0); // the low two bits must be zero + + uint pcInstr = *pCode; + + Debug.Assert(pcInstr == 0x1e00000e); // Must be pcaddu18i R14, 0 + + long relOff = imm38 & 0x20000; + long imm = imm38 + relOff; + relOff = (((imm & 0x1ffff) - relOff) >> 2) & 0xffff; + + // Assemble the pc-relative hight20bits of 'imm38' into the pcaddu12i instruction + pcInstr |= (uint)(((imm >> 18) & 0xFFFFF) << 5); + + *pCode = pcInstr; // write the assembled instruction + + pcInstr = *(pCode + 1); + + // Assemble the pc-relative low18bits of 'imm38' into the addid or ld instruction + pcInstr |= (uint)(relOff << 10); + + *(pCode + 1) = pcInstr; // write the assembled instruction + + Debug.Assert(GetLoongArch64JIR(pCode) == imm38); + } public Relocation(RelocType relocType, int offset, ISymbolNode target) { @@ -334,6 +426,12 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: PutArm64Rel12((uint*)location, (int)value); break; + case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: + PutLoongArch64PC12((uint*)location, value); + break; + case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: + PutLoongArch64JIR((uint*)location, value); + break; default: Debug.Fail("Invalid RelocType: " + relocType); break; @@ -366,6 +464,10 @@ public static unsafe long ReadValue(RelocType relocType, void* location) return GetArm64Rel21((uint*)location); case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: return GetArm64Rel12((uint*)location); + case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: + return (long)GetLoongArch64PC12((uint*)location); + case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: + return (long)GetLoongArch64JIR((uint*)location); default: Debug.Fail("Invalid RelocType: " + relocType); return 0; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/AddrMode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/AddrMode.cs new file mode 100644 index 00000000000000..5616941cae238f --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/AddrMode.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace ILCompiler.DependencyAnalysis.LoongArch64 +{ + 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_LoongArch64/LoongArch64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs new file mode 100644 index 00000000000000..e01aafee125203 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs @@ -0,0 +1,70 @@ +// 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.LoongArch64 +{ + public struct LoongArch64Emitter + { + public LoongArch64Emitter(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 + + public void EmitMOV(Register regDst, ushort imm16) + { + Debug.Assert((uint)regDst <= 0x1f); + Debug.Assert(imm16 <= 0xfff); + uint instruction = 0x03800000u | (uint)((imm16 & 0xfff) << 10) | (uint)regDst; + Builder.EmitUInt(instruction); + } + + // pcaddi regDst, 0 + public void EmitPC(Register regDst) + { + Debug.Assert((uint)regDst > 0 && (uint)regDst < 32); + Builder.EmitUInt(0x18000000 | (uint)regDst); + } + + // ld_d regDst, regAddr, offset + public void EmitLD(Register regDst, Register regSrc, int offset) + { + Debug.Assert(offset >= -2048 && offset <= 2047); + + Builder.EmitUInt((uint)(0x28c00000 | (uint)((offset & 0xfff) << 10) | ((uint)regSrc << 5) | (uint)regDst)); + } + + public void EmitJMP(ISymbolNode symbol) + { + if (symbol.RepresentsIndirectionCell) + { + // pcaddi R21, 0 + EmitPC(Register.R21); + + EmitLD(Register.R21, Register.R21, 0x10); + + // ld_d R21, R21, 0 + EmitLD(Register.R21, Register.R21, 0); + + // jirl R0,R21,0 + Builder.EmitUInt(0x4c0002a0); + + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); + } + else + { + //Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC); + Builder.EmitUInt(0xffffffff); // bad code. + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs new file mode 100644 index 00000000000000..1ce65e76622878 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs @@ -0,0 +1,50 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.DependencyAnalysis.LoongArch64 +{ + public enum Register + { + R0 = 0, + R1 = 1, + R2 = 2, + R3 = 3, + R4 = 4, + R5 = 5, + R6 = 6, + R7 = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + R16 = 16, + R17 = 17, + R18 = 18, + R19 = 19, + R20 = 20, + R21 = 21, + R22 = 22, + R23 = 23, + R24 = 24, + R25 = 25, + R26 = 26, + R27 = 27, + R28 = 28, + R29 = 29, + R30 = 30, + R31 = 31, + + None = 32, + NoIndex = 128, + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs new file mode 100644 index 00000000000000..fdfdddf2170457 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/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 System; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.LoongArch64 +{ + /// + /// 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 Result; + + public TargetRegisterMap(TargetOS os) + { + Arg0 = Register.R4; + Arg1 = Register.R5; + Arg2 = Register.R6; + Arg3 = Register.R7; + Arg4 = Register.R8; + Arg5 = Register.R9; + Arg6 = Register.R11; + Arg7 = Register.R12; + Result = Register.R4; // TODO: ??? + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs index 2f35f083f24d2e..d21276726b7f56 100644 --- a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs +++ b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs @@ -110,6 +110,10 @@ public SimdVectorLength GetVectorTSimdVector() { return SimdVectorLength.None; } + else if (_targetArchitecture == TargetArchitecture.LoongArch64) + { + 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 7837d885361620..2bb5090312f045 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -52,6 +52,7 @@ private enum ImageFileMachine AMD64 = 0x8664, ARM = 0x01c4, ARM64 = 0xaa64, + LoongArch64 = 0x6264, } internal const string JitLibrary = "clrjitilc"; @@ -3055,7 +3056,8 @@ private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_ST private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) { - throw new NotImplementedException("For LoongArch64, would be implemented later"); + TypeDesc typeDesc = HandleToObject(cls); + return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); } private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) @@ -3656,7 +3658,26 @@ private ref ArrayBuilder findRelocBlock(BlockType blockType, out int private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ushort fRelocType) { if (targetArchitecture != TargetArchitecture.ARM64) - return (RelocType)fRelocType; + { + if (targetArchitecture == TargetArchitecture.LoongArch64) + { + const ushort IMAGE_REL_LOONGARCH64_PC = 3; + const ushort IMAGE_REL_LOONGARCH64_JIR = 4; + + switch (fRelocType) + { + case IMAGE_REL_LOONGARCH64_PC: + return RelocType.IMAGE_REL_BASED_LOONGARCH64_PC; + case IMAGE_REL_LOONGARCH64_JIR: + return RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR; + default: + Debug.Fail("Invalid RelocType: " + fRelocType); + return 0; + } + } + else + return (RelocType)fRelocType; + } const ushort IMAGE_REL_ARM64_BRANCH26 = 3; const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; @@ -3768,6 +3789,8 @@ private uint getExpectedTargetArchitecture() return (uint)ImageFileMachine.ARM; case TargetArchitecture.ARM64: return (uint)ImageFileMachine.ARM64; + case TargetArchitecture.LoongArch64: + return (uint)ImageFileMachine.LoongArch64; default: throw new NotImplementedException("Expected target architecture is not supported"); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 04b36e0f5be90a..4905caea84e229 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1198,6 +1198,46 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR public byte eightByteOffsets1; }; + // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` API + // to convey struct argument passing information. + // + // `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s). + // + // Otherwise, and only for structs with no more than two fields and a total struct size no larger + // than two pointers: + // + // The lowest four bits denote the floating-point info: + // bit 0: `1` means there is only one float or double field within the struct. + // bit 1: `1` means only the first field is floating-point type. + // bit 2: `1` means only the second field is floating-point type. + // bit 3: `1` means the two fields are both floating-point type. + // The bits[5:4] denoting whether the field size is 8-bytes: + // bit 4: `1` means the first field's size is 8. + // bit 5: `1` means the second field's size is 8. + // + // Note that bit 0 and 3 cannot both be set. + public enum LAFlags + { + STRUCT_NO_FLOAT_FIELD = 0x0, + STRUCT_FLOAT_FIELD_ONLY_ONE = 0x1, + STRUCT_FLOAT_FIELD_ONLY_TWO = 0x8, + STRUCT_FLOAT_FIELD_FIRST = 0x2, + STRUCT_FLOAT_FIELD_SECOND = 0x4, + STRUCT_FIRST_FIELD_SIZE_IS8 = 0x10, + STRUCT_SECOND_FIELD_SIZE_IS8 = 0x20, + + STRUCT_FIRST_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8), + STRUCT_SECOND_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8), + STRUCT_FIELD_TWO_DOUBLES = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8 | STRUCT_FLOAT_FIELD_ONLY_TWO), + + STRUCT_MERGE_FIRST_SECOND = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO), + STRUCT_MERGE_FIRST_SECOND_8 = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_SECOND_FIELD_SIZE_IS8), + + STRUCT_HAS_ONE_FLOAT_MASK = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND), + STRUCT_HAS_FLOAT_FIELDS_MASK = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE), + STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), + }; + // DEBUGGER DATA public enum MappingTypes { diff --git a/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs b/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs index 0fad745ec6701e..898ec59e5a5d38 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.X64 => "x64", TargetArchitecture.ARM => "arm", TargetArchitecture.ARM64 => "arm64", + TargetArchitecture.LoongArch64 => "loongarch64", _ => throw new NotImplementedException(target.Architecture.ToString()) }; diff --git a/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs new file mode 100644 index 00000000000000..b3f8ef0372bcba --- /dev/null +++ b/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs @@ -0,0 +1,172 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using ILCompiler; +using Internal.TypeSystem; + +namespace Internal.JitInterface +{ + + internal static class LoongArch64PassStructInRegister + { + public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) + { + FieldDesc firstField = null; + uint floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + int numIntroducedFields = 0; + foreach (FieldDesc field in typeDesc.GetFields()) + { + if (!field.IsStatic) + { + if (firstField == null) + { + firstField = field; + } + numIntroducedFields++; + } + } + + if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16)) + { + return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + } + + //// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers + if (typeDesc.IsIntrinsic) + { + throw new NotImplementedException("For LoongArch64, SIMD would be implemented later"); + } + + MetadataType mdType = typeDesc as MetadataType; + Debug.Assert(mdType != null); + + TypeDesc firstFieldElementType = firstField.FieldType; + int firstFieldSize = firstFieldElementType.GetElementSize().AsInt; + + // A fixed buffer type is always a value type that has exactly one value type field at offset 0 + // and who's size is an exact multiple of the size of the field. + // It is possible that we catch a false positive with this check, but that chance is extremely slim + // and the user can always change their structure to something more descriptive of what they want + // instead of adding additional padding at the end of a one-field structure. + // We do this check here to save looking up the FixedBufferAttribute when loading the field + // from metadata. + bool isFixedBuffer = numIntroducedFields == 1 + && firstFieldElementType.IsValueType + && firstField.Offset.AsInt == 0 + && mdType.HasLayout() + && ((typeDesc.GetElementSize().AsInt % firstFieldSize) == 0); + + if (isFixedBuffer) + { + numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize; + if (numIntroducedFields > 2) + { + return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + } + } + + int fieldIndex = 0; + foreach (FieldDesc field in typeDesc.GetFields()) + { + if (fieldIndex > 1) + { + return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + } + else if (field.IsStatic) + { + continue; + } + + Debug.Assert(fieldIndex < numIntroducedFields); + + switch (field.FieldType.Category) + { + case TypeFlags.Double: + { + if (numIntroducedFields == 1) + floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + else if (fieldIndex == 0) + floatFieldFlags = (uint)LAFlags.STRUCT_FIRST_FIELD_DOUBLE; + else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + floatFieldFlags = floatFieldFlags ^ (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND_8; + else + floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_DOUBLE; + } + break; + + case TypeFlags.Single: + { + if (numIntroducedFields == 1) + floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + else if (fieldIndex == 0) + floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST; + else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + floatFieldFlags ^= (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND; + else + floatFieldFlags |= (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND; + } + break; + + case TypeFlags.ValueType: + //case TypeFlags.Class: + //case TypeFlags.Array: + //case TypeFlags.SzArray: + { + uint floatFieldFlags2 = GetLoongArch64PassStructInRegisterFlags(field.FieldType); + if (numIntroducedFields == 1) + floatFieldFlags = floatFieldFlags2; + else if (field.FieldType.GetElementSize().AsInt > 8) + return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + else if (fieldIndex == 0) + { + if ((floatFieldFlags2 & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST; + if (field.FieldType.GetElementSize().AsInt == 8) + floatFieldFlags |= (uint)LAFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + } + else + { + Debug.Assert(fieldIndex == 1); + if ((floatFieldFlags2 & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + floatFieldFlags |= (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND; + if (field.FieldType.GetElementSize().AsInt == 8) + floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + + floatFieldFlags2 = floatFieldFlags & ((uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND); + if (floatFieldFlags2 == 0) + floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + else if (floatFieldFlags2 == ((uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND)) + floatFieldFlags ^= ((uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND); + } + } + break; + + default: + { + if (field.FieldType.GetElementSize().AsInt == 8) + { + if (numIntroducedFields > 1) + { + if (fieldIndex == 0) + floatFieldFlags = (uint)LAFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + else + floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + } + } + else if (fieldIndex == 1) + floatFieldFlags = (floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + break; + } + } + + fieldIndex++; + } + + return floatFieldFlags; + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs index b3b587d1432f48..160ff674f8d71a 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs @@ -17,5 +17,6 @@ public enum TargetArchitecture X64, X86, Wasm32, + LoongArch64, } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs index 343bb5780483dc..79b252da19eb4c 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs @@ -80,6 +80,7 @@ public int PointerSize { case TargetArchitecture.ARM64: case TargetArchitecture.X64: + case TargetArchitecture.LoongArch64: return 8; case TargetArchitecture.ARM: case TargetArchitecture.X86: @@ -116,6 +117,10 @@ public int MaximumAlignment // Corresponds to alignmet required for __m256 return 16; } + else if (Architecture == TargetArchitecture.LoongArch64) + { + return 16; + } // 256-bit vector is the type with the higest alignment we support return 32; @@ -172,6 +177,7 @@ public int MinimumCodeAlignment case TargetArchitecture.ARM: return 2; case TargetArchitecture.ARM64: + case TargetArchitecture.LoongArch64: return 4; default: return 1; @@ -276,6 +282,7 @@ public LayoutInt GetObjectAlignment(LayoutInt fieldAlignment) return new LayoutInt(8); case TargetArchitecture.X64: case TargetArchitecture.ARM64: + case TargetArchitecture.LoongArch64: return new LayoutInt(8); case TargetArchitecture.X86: return new LayoutInt(4); @@ -318,6 +325,7 @@ public int MaxHomogeneousAggregateElementCount // and Procedure Call Standard for the Arm 64-bit Architecture. Debug.Assert(Architecture == TargetArchitecture.ARM || Architecture == TargetArchitecture.ARM64 || + Architecture == TargetArchitecture.LoongArch64 || Architecture == TargetArchitecture.X64 || Architecture == TargetArchitecture.X86); 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 4a13af01e63410..1cd224d500f190 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 @@ -233,6 +233,7 @@ internal struct ArgLocDesc public int m_byteStackIndex; // Stack offset in bytes (or -1) public int m_byteStackSize; // Stack size in bytes + public uint m_floatFlags; // struct with two-fields can be passed by registers. // Initialize to represent a non-placed argument (no register or stack slots referenced). public void Init() { @@ -242,6 +243,7 @@ public void Init() m_cGenReg = 0; m_byteStackIndex = -1; m_byteStackSize = 0; + m_floatFlags = 0; m_fRequires64BitAlignment = false; } @@ -617,6 +619,13 @@ public bool IsArgPassedByRef() return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) && (!_argTypeHandle.IsHomogeneousAggregate() || IsVarArg)); } return false; + case TargetArchitecture.LoongArch64: + if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) + { + Debug.Assert(!_argTypeHandle.IsNull()); + return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle)); + } + return false; default: throw new NotImplementedException(); } @@ -813,6 +822,13 @@ public int GetNextOffset() _arm64IdxFPReg = 0; break; + case TargetArchitecture.LoongArch64: + _loongarch64IdxGenReg = numRegistersUsed; + _loongarch64OfsStack = 0; + + _loongarch64IdxFPReg = 0; + break; + default: throw new NotImplementedException(); } @@ -1298,6 +1314,120 @@ public int GetNextOffset() return argOfs; } + case TargetArchitecture.LoongArch64: + { + int cFPRegs = 0; + uint floatFieldFlags = (uint)LAFlags.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 = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); + if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + cFPRegs = 2; + else if ((floatFieldFlags & (uint)LAFlags.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)LAFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + { + if ((_loongarch64IdxFPReg < 8) && (_loongarch64IdxGenReg < 8)) + { + _argLocDescForStructInRegs = new ArgLocDesc(); + _argLocDescForStructInRegs.m_idxFloatReg = _loongarch64IdxFPReg; + _argLocDescForStructInRegs.m_cFloatReg = 1; + + _argLocDescForStructInRegs.m_idxGenReg = _loongarch64IdxGenReg; + _argLocDescForStructInRegs.m_cGenReg = 1; + + _hasArgLocDescForStructInRegs = true; + _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; + + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8; + _loongarch64IdxFPReg++; + _loongarch64IdxGenReg++; + return argOfsInner; + } + else + { + _loongarch64IdxFPReg = 8; + } + } + else if (cFPRegs + _loongarch64IdxFPReg <= 8) + { + // Each floating point register in the argument area is 8 bytes. + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8; + _loongarch64IdxFPReg += cFPRegs; + return argOfsInner; + } + else + { + _loongarch64IdxFPReg = 8; + } + } + + { + Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0); + + int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; + // Only R4-R11 are valid argument registers. + if (_loongarch64IdxGenReg + regSlots <= 8) + { + // The entirety of the arg fits in the register slots. + int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8; + _loongarch64IdxGenReg += regSlots; + return argOfsInner; + } + else if (_loongarch64IdxGenReg < 8) + { + int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8; + _loongarch64IdxGenReg = 8; + _loongarch64OfsStack += 8; + return argOfsInner; + } + else + { + // Don't use reg slots for this. It will be passed purely on the stack arg space. + _loongarch64IdxGenReg = 8; + } + } + + argOfs = _transitionBlock.OffsetOfArgs + _loongarch64OfsStack; + _loongarch64OfsStack += cbArg; + return argOfs; + } + default: throw new NotImplementedException(); } @@ -1580,6 +1710,64 @@ private void ForceSigWalk() return pLoc; } + case TargetArchitecture.LoongArch64: + { + 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; + + if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHomogeneousAggregate()) + { + int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize(); + pLoc.m_cFloatReg = GetArgSize() / haElementSize; + } + else + { + 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) { @@ -1665,6 +1853,10 @@ private void ForceSigWalk() private int _arm64OfsStack; // Offset of next stack location to be assigned a value private int _arm64IdxFPReg; // Next FP register to be assigned a value + private int _loongarch64IdxGenReg; // Next general register to be assigned a value + private int _loongarch64OfsStack; // Offset of next stack location to be assigned a value + private int _loongarch64IdxFPReg; // 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; @@ -1693,7 +1885,7 @@ private enum ParamTypeLocation // METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke // RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize - // }; + // {}; private void ComputeReturnFlags() { 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 a43ad0335ccc1c..3246b5a143ca3c 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 @@ -138,7 +138,7 @@ private IEnumerable EncodeDataCore(NodeFactory factory) unwindInfo[0] |= (byte)((UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER) << FlagsShift); } - else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64)) + else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64) || (targetArch == TargetArchitecture.LoongArch64)) { // 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_LoongArch64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs new file mode 100644 index 00000000000000..5e0a8751d472b3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs @@ -0,0 +1,71 @@ +// 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.LoongArch64; + +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 LoongArch64Emitter instructionEncoder, bool relocsOnly) + { + + switch (_thunkKind) + { + case Kind.Eager: + break; + + case Kind.DelayLoadHelper: + case Kind.VirtualStubDispatch: + // T8 contains indirection cell + // Do nothing T8=R20 contains our first param + + if (!relocsOnly) + { + // movz T0=R12, #index + int index = _containingImportSection.IndexFromBeginningOfArray; + instructionEncoder.EmitMOV(Register.R12, checked((ushort)index)); + } + + // get pc + // pcaddi T1=R13, 0 + instructionEncoder.EmitPC(Register.R13); + + // load Module* -> T1 + instructionEncoder.EmitLD(Register.R13, Register.R13, 0x24); + + // ld_d R13, R13, 0 + instructionEncoder.EmitLD(Register.R13, Register.R13, 0); + break; + + case Kind.Lazy: + // get pc + // pcaddi R5, 0 + instructionEncoder.EmitPC(Register.R5); + + // load Module* -> R5=A1 + instructionEncoder.EmitLD(Register.R5, Register.R5, 0x24); + + // ld_d R5, R5, 0 + instructionEncoder.EmitLD(Register.R5, Register.R5, 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 290744f245ed52..8cf01a81938712 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 @@ -43,6 +43,9 @@ public static TransitionBlock FromTarget(TargetDetails target) AppleArm64TransitionBlock.Instance : Arm64TransitionBlock.Instance; + case TargetArchitecture.LoongArch64: + return LoongArch64TransitionBlock.Instance; + default: throw new NotImplementedException(target.Architecture.ToString()); } @@ -60,6 +63,7 @@ public static TransitionBlock FromTarget(TargetDetails target) public bool IsX64 => Architecture == TargetArchitecture.X64; public bool IsARM => Architecture == TargetArchitecture.ARM; public bool IsARM64 => Architecture == TargetArchitecture.ARM64; + public bool IsLoongArch64 => Architecture == TargetArchitecture.LoongArch64; /// /// This property is only overridden in AMD64 Unix variant of the transition block. @@ -629,5 +633,58 @@ public sealed override int StackElemSize(int parmSize, bool isValueType = false, return base.StackElemSize(parmSize, isValueType, isFloatHfa); } } + + private class LoongArch64TransitionBlock : TransitionBlock + { + public static TransitionBlock Instance = new LoongArch64TransitionBlock(); + public override TargetArchitecture Architecture => TargetArchitecture.LoongArch64; + public override int PointerSize => 8; + public override int FloatRegisterSize => 8; // TODO: for SIMD. + // R4(=A0) .. R11(=A7) + public override int NumArgumentRegisters => 8; + // fp=R22,ra=R1,s0-s8(R23-R31),tp=R2 + public override int NumCalleeSavedRegisters => 12; + // Callee-saves, argument registers + public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; + public override int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters; + + // F0..F7 + 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 + if (th.GetSize() > EnregisteredParamTypeMaxSize) + { + return true; + } + else + { + int numIntroducedFields = 0; + foreach (FieldDesc field in th.GetRuntimeTypeHandle().GetFields()) + { + if (!field.IsStatic) + { + numIntroducedFields++; + } + } + return ((numIntroducedFields == 0) || (numIntroducedFields > 2)); + } + } + + public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters; + + 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 62725929b8100c..fb8c2163773ab1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -116,6 +116,11 @@ private class ModuleFieldLayoutMap : LockFreeReaderHashtable private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm = 8; + /// + /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for LoongArch64 + /// + private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64 = 8; + protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value) { return key == value.Module; @@ -414,6 +419,10 @@ public FieldAndOffset[] GetOrAddDynamicLayout(DefType defType, ModuleFieldLayout nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm; break; + case TargetArchitecture.LoongArch64: + nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64; + 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 1d2e28afc787fc..f960334c44bbb9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -78,6 +78,10 @@ + + + + @@ -102,6 +106,7 @@ + @@ -179,6 +184,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs index 09f9244aeffcf1..d0d994737f2626 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs @@ -220,6 +220,14 @@ public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targe break; } + case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: + case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: + { + relocationLength = 8; + delta = targetRVA - sourceRVA; + break; + } + default: throw new NotSupportedException(); } @@ -233,8 +241,14 @@ public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targe { long value = Relocation.ReadValue(relocationType, bufferContent); // Supporting non-zero values for ARM64 would require refactoring this function - if (((relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21) || (relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A)) && (value != 0)) + 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) + ) && (value != 0)) + { throw new NotSupportedException(); + } Relocation.WriteValue(relocationType, bufferContent, unchecked(value + delta)); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index dc34b650f03413..768969174025a5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs @@ -313,6 +313,10 @@ public SectionBuilder(TargetDetails target) _codePadding = 0xD43E0000u; break; + case TargetArchitecture.LoongArch64: + _codePadding = 0x002A0005u; + 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 57f1bad0d78016..8b589193850466 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs @@ -87,6 +87,9 @@ public static Machine MachineFromTarget(this TargetDetails target) case Internal.TypeSystem.TargetArchitecture.ARM: return Machine.ArmThumb2; + case Internal.TypeSystem.TargetArchitecture.LoongArch64: + return Machine.LoongArch64; + default: throw new NotImplementedException(target.Architecture.ToString()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 893f8e3f1f51b2..b582f4b7b92356 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -610,6 +610,11 @@ private unsafe void EnsureHeader() _pointerSize = 8; break; + case Machine.LoongArch64: + _architecture = Architecture.LoongArch64; + _pointerSize = 8; + break; + default: throw new NotImplementedException(Machine.ToString()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs index 3f0bef94f8e40f..96f4108f7910ac 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs @@ -149,5 +149,20 @@ private sealed class Arm64TransitionBlock : TransitionBlock private int OffsetOfX8Register => OffsetOfArgumentRegisters - PointerSize; public override int OffsetOfFirstGCRefMapSlot => OffsetOfX8Register; } + + private sealed class LoongArch64TransitionBlock : TransitionBlock + { + public static readonly TransitionBlock Instance = new LoongArch64TransitionBlock(); + + public override int PointerSize => 8; + // R4 .. R11 + public override int NumArgumentRegisters => 8; + // fp=R22,ra=R1,s0-s8(R23-R31),tp=R2 + public override int NumCalleeSavedRegisters => 12; + // Callee-saves, padding, argument registers + public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; + public override int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters; + } } } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index de6370cc5066e2..e81fa19f19a69b 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -80,6 +80,9 @@ public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture case Architecture.Arm64: arch = TargetArchitecture.ARM64; break; + case Architecture.LoongArch64: + arch = TargetArchitecture.LoongArch64; + break; default: throw new NotImplementedException(); } @@ -206,6 +209,8 @@ public static TargetArchitecture GetTargetArchitectureFromArg(string archArg, ou } else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) return TargetArchitecture.ARM64; + else if (archArg.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.LoongArch64; else throw new CommandLineException(SR.TargetArchitectureUnsupported); } diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index d544530d81378e..4fd71a9573f841 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -5,7 +5,7 @@ Exe $(NetCoreAppToolCurrent) 8002,NU1701 - x64;x86;arm64;arm + x64;x86;arm64;arm;loongarch64 AnyCPU false true From 08bed02024f036de1ab23162c65cd80c6aae2564 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Wed, 13 Jul 2022 09:07:16 +0800 Subject: [PATCH 2/7] update the `ILCompiler.Reflection.ReadyToRun.csproj` with `net6.0` --- .../ILCompiler.Reflection.ReadyToRun.csproj | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index 879ff0b1385317..0ce8c5cfdad801 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -7,8 +7,8 @@ AnyCPU Open true - - netstandard2.0 + + net6.0 false 8002,NU1701 win-x64;win-x86 @@ -17,11 +17,6 @@ AnyCPU - - - - - From 6cb19c9ff23ffc7739ffe2562da4d80c581912a3 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Wed, 13 Jul 2022 17:11:45 +0800 Subject: [PATCH 3/7] add `aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64` --- .../Target_LoongArch64/LoongArch64Emitter.cs | 66 ++++- .../Target_LoongArch64/TargetRegisterMap.cs | 2 + .../tools/Common/JitInterface/CorInfoImpl.cs | 12 +- .../ARMInitialInterfaceDispatchStubNode.cs | 6 + .../LoongArch64JumpStubNode.cs | 15 ++ .../LoongArch64ReadyToRunGenericHelperNode.cs | 241 ++++++++++++++++++ .../LoongArch64ReadyToRunHelperNode.cs | 202 +++++++++++++++ .../LoongArch64TentativeMethodNode.cs | 15 ++ .../LoongArch64UnboxingStubNode.cs | 18 ++ .../ILCompiler.Compiler.csproj | 9 + .../ILCompiler.RyuJit.csproj | 3 + 11 files changed, 582 insertions(+), 7 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64JumpStubNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64TentativeMethodNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64UnboxingStubNode.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs index e01aafee125203..db0fd17192a369 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs @@ -19,6 +19,11 @@ public LoongArch64Emitter(NodeFactory factory, bool relocsOnly) // Assembly stub creation api. TBD, actually make this general purpose + public void EmitBreak() + { + Builder.EmitUInt(0x002a0005); + } + public void EmitMOV(Register regDst, ushort imm16) { Debug.Assert((uint)regDst <= 0x1f); @@ -27,6 +32,21 @@ public void EmitMOV(Register regDst, ushort imm16) Builder.EmitUInt(instruction); } + public void EmitMOV(Register regDst, Register regSrc) + { + Builder.EmitUInt((uint)(0x03800000 | ((uint)regSrc << 5) | (uint)regDst)); + } + + public void EmitMOV(Register regDst, ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC); + // pcaddu12i reg, off-hi-20bits + Builder.EmitUInt(0x1c000000u | (uint)regDst); + + // addi_d reg, reg, off-lo-12bits + Builder.EmitUInt(0x02c00000u | (uint)(((uint)regDst << 5) | (uint)regDst)); + } + // pcaddi regDst, 0 public void EmitPC(Register regDst) { @@ -34,14 +54,41 @@ public void EmitPC(Register regDst) Builder.EmitUInt(0x18000000 | (uint)regDst); } + // addi.d regDst, regSrc, imm12 + public void EmitADD(Register regDst, Register regSrc, int imm) + { + Debug.Assert((imm >= -2048) && (imm <= 2047)); + + Builder.EmitUInt((uint)(0x02c00000 | (uint)((imm & 0xfff) << 10) | ((uint)regSrc << 5) | (uint)regDst)); + } + + // xori regDst, regSrc, imm12 + public void EmitXOR(Register regDst, Register regSrc, int imm) + { + Debug.Assert((imm >= 0) && (imm <= 0xfff)); + + Builder.EmitUInt((uint)(0x03c00000 | (uint)((imm & 0xfff) << 10) | ((uint)regSrc << 5) | (uint)regDst)); + } + // ld_d regDst, regAddr, offset public void EmitLD(Register regDst, Register regSrc, int offset) { - Debug.Assert(offset >= -2048 && offset <= 2047); + Debug.Assert((offset >= -2048) && (offset <= 2047)); Builder.EmitUInt((uint)(0x28c00000 | (uint)((offset & 0xfff) << 10) | ((uint)regSrc << 5) | (uint)regDst)); } + public void EmitRET() + { + // jirl R0,R1,0 + Builder.EmitUInt(0x4c000020); + } + + public void EmitJMP(Register reg) + { + Builder.EmitUInt(0x4c000000u | ((uint)reg << 5)); + } + public void EmitJMP(ISymbolNode symbol) { if (symbol.RepresentsIndirectionCell) @@ -66,5 +113,22 @@ public void EmitJMP(ISymbolNode symbol) throw new NotImplementedException(); } } + + public void EmitRETIfEqual(Register regSrc) + { + // BNEZ regSrc, 8 + Builder.EmitUInt((uint)(0x44000000 | (2 << 10) | ((uint)regSrc << 5))); + EmitRET(); + } + + public void EmitJE(Register regSrc, ISymbolNode symbol) + { + uint offset = symbol.RepresentsIndirectionCell ? 7u : 2u; + + // BNEZ regSrc, offset + Builder.EmitUInt((uint)(0x44000000 | (offset << 10) | ((uint)regSrc << 5))); + + EmitJMP(symbol); + } } } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs index fdfdddf2170457..b2c4fef90d50d0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs @@ -20,6 +20,7 @@ public struct TargetRegisterMap public readonly Register Arg5; public readonly Register Arg6; public readonly Register Arg7; + public readonly Register IntraProcedureCallScratch1; public readonly Register Result; public TargetRegisterMap(TargetOS os) @@ -32,6 +33,7 @@ public TargetRegisterMap(TargetOS os) Arg5 = Register.R9; Arg6 = Register.R11; Arg7 = Register.R12; + IntraProcedureCallScratch1 = Register.R21; Result = Register.R4; // TODO: ??? } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 2bb5090312f045..522aaba7b80965 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3054,12 +3054,6 @@ private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_ST } } - private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) - { - TypeDesc typeDesc = HandleToObject(cls); - return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); - } - private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { int index = (int)args; @@ -3279,6 +3273,12 @@ private bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_STRUCT_ return true; } + private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) + { + TypeDesc typeDesc = HandleToObject(cls); + return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); + } + private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private void* getInlinedCallFrameVptr(ref void* ppIndirection) 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 7096525ff00132..8ba74c2aa06e19 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 @@ -8,6 +8,7 @@ using ILCompiler.DependencyAnalysis.X64; using ILCompiler.DependencyAnalysis.X86; using ILCompiler.DependencyAnalysis.ARM64; +using ILCompiler.DependencyAnalysis.LoongArch64; namespace ILCompiler.DependencyAnalysis { @@ -51,6 +52,11 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi throw new NotImplementedException(); } + protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + public override int ClassCode => 588185132; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64JumpStubNode.cs new file mode 100644 index 00000000000000..20e44d42b3a5f2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64JumpStubNode.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.LoongArch64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..eeb4cfe7c87f3f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs @@ -0,0 +1,241 @@ +// 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.LoongArch64; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ LoongArch64Emitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref LoongArch64Emitter 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. + dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); + } + + // Load the generic dictionary cell + encoder.EmitLD(result, context, dictionarySlot * factory.Target.PointerSize); + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + // Do another indirection + encoder.EmitLD(result, result, 0); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + // andi temp, result, 0x1 + // BEQZ temp L1 + // addi.d result, result,-1 + // L1: + throw new NotImplementedException(); + + default: + break; + } + } + + protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emitter 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); + + MetadataType target = (MetadataType)_target; + 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.EmitADD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg0, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, factory.Target.PointerSize); + encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg2, 1); + encoder.EmitRETIfEqual(Register.R21); + + 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 (!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. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + encoder.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(Register.R21); + + 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 (factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + // 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.EmitADD(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 LoongArch64Emitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref LoongArch64Emitter 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_LoongArch64/LoongArch64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..d618787a086cbd --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs @@ -0,0 +1,202 @@ +// 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.LoongArch64; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// LoongArch64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter 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.EmitADD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, factory.Target.PointerSize); + encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg2, 1); + encoder.EmitRETIfEqual(encoder.TargetRegister.IntraProcedureCallScratch1); + + 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); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); + encoder.EmitJE(encoder.TargetRegister.IntraProcedureCallScratch1, factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + + 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.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(Register.R21); + + 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_LoongArch64/LoongArch64TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64TentativeMethodNode.cs new file mode 100644 index 00000000000000..e21b21ba56b440 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64TentativeMethodNode.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.LoongArch64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64UnboxingStubNode.cs new file mode 100644 index 00000000000000..7f523d168ff6de --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64UnboxingStubNode.cs @@ -0,0 +1,18 @@ +// 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.LoongArch64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter encoder, bool relocsOnly) + { + // addi.d a0, a0, sizeof(void*); + encoder.EmitADD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, factory.Target.PointerSize); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); // b methodEntryPoint + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index ca9c42f21bf34d..5d0b30163588b3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -272,6 +272,10 @@ + + + + @@ -356,6 +360,7 @@ + @@ -511,6 +516,10 @@ + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 9f5e0244dab329..ad41ccef9d096a 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -85,6 +85,9 @@ JitInterface\SystemVStructClassificator.cs + + JitInterface\LoongArch64PassStructInRegister.cs + Pgo\TypeSystemEntityOrUnknown.cs From c17e3d791028573a02feb13e6ed3518982e08f43 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Wed, 13 Jul 2022 17:12:27 +0800 Subject: [PATCH 4/7] update the `ILCompiler.Reflection.ReadyToRun.csproj` with `net7.0` --- .../ILCompiler.Reflection.ReadyToRun.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index 0ce8c5cfdad801..e0012ac7602475 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -7,8 +7,8 @@ AnyCPU Open true - - net6.0 + + net7.0 false 8002,NU1701 win-x64;win-x86 From 7a5d68a3225b6e1c9d8e0e66f43c5fd33e510cb1 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Fri, 15 Jul 2022 09:05:00 +0800 Subject: [PATCH 5/7] Revert "update the `ILCompiler.Reflection.ReadyToRun.csproj` with `net6.0`" This reverts commit 08bed02024f036de1ab23162c65cd80c6aae2564. --- .../ILCompiler.Reflection.ReadyToRun.csproj | 9 +++++++-- .../ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index e0012ac7602475..879ff0b1385317 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -7,8 +7,8 @@ AnyCPU Open true - - net7.0 + + netstandard2.0 false 8002,NU1701 win-x64;win-x86 @@ -17,6 +17,11 @@ AnyCPU + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index b582f4b7b92356..449f1432d84483 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -610,8 +610,8 @@ private unsafe void EnsureHeader() _pointerSize = 8; break; - case Machine.LoongArch64: - _architecture = Architecture.LoongArch64; + case (Machine) 0x6264: /* LoongArch64 */ + _architecture = (Architecture) 6; /* LoongArch64 */ _pointerSize = 8; break; From 663c62b23d6bd622cd55603121b0a4f8234df52d Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Fri, 15 Jul 2022 12:10:45 +0800 Subject: [PATCH 6/7] amend the code format. --- .../tools/Common/JitInterface/CorInfoImpl.cs | 42 ++++---- .../tools/Common/JitInterface/CorInfoTypes.cs | 2 +- .../LoongArch64PassStructInRegister.cs | 100 ++++++++++++------ .../ReadyToRun/ArgIterator.cs | 13 ++- 4 files changed, 101 insertions(+), 56 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 522aaba7b80965..275fbfce94262d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3657,9 +3657,28 @@ private ref ArrayBuilder findRelocBlock(BlockType blockType, out int // Translates relocation type constants used by JIT (defined in winnt.h) to RelocType enumeration private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ushort fRelocType) { - if (targetArchitecture != TargetArchitecture.ARM64) + switch (targetArchitecture) { - if (targetArchitecture == TargetArchitecture.LoongArch64) + case TargetArchitecture.ARM64: + { + const ushort IMAGE_REL_ARM64_BRANCH26 = 3; + const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; + const ushort IMAGE_REL_ARM64_PAGEOFFSET_12A = 6; + + switch (fRelocType) + { + case IMAGE_REL_ARM64_BRANCH26: + return RelocType.IMAGE_REL_BASED_ARM64_BRANCH26; + case IMAGE_REL_ARM64_PAGEBASE_REL21: + return RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21; + case IMAGE_REL_ARM64_PAGEOFFSET_12A: + return RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A; + default: + Debug.Fail("Invalid RelocType: " + fRelocType); + return 0; + } + } + case TargetArchitecture.LoongArch64: { const ushort IMAGE_REL_LOONGARCH64_PC = 3; const ushort IMAGE_REL_LOONGARCH64_JIR = 4; @@ -3675,26 +3694,9 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush return 0; } } - else + default: return (RelocType)fRelocType; } - - const ushort IMAGE_REL_ARM64_BRANCH26 = 3; - const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; - const ushort IMAGE_REL_ARM64_PAGEOFFSET_12A = 6; - - switch (fRelocType) - { - case IMAGE_REL_ARM64_BRANCH26: - return RelocType.IMAGE_REL_BASED_ARM64_BRANCH26; - case IMAGE_REL_ARM64_PAGEBASE_REL21: - return RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21; - case IMAGE_REL_ARM64_PAGEOFFSET_12A: - return RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A; - default: - Debug.Fail("Invalid RelocType: " + fRelocType); - return 0; - }; } private void recordRelocation(void* location, void* locationRW, void* target, ushort fRelocType, ushort slotNum, int addlDelta) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 4905caea84e229..17d36280c02ac7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1216,7 +1216,7 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR // bit 5: `1` means the second field's size is 8. // // Note that bit 0 and 3 cannot both be set. - public enum LAFlags + public enum StructFloatFieldInfoFlags { STRUCT_NO_FLOAT_FIELD = 0x0, STRUCT_FLOAT_FIELD_ONLY_ONE = 0x1, diff --git a/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs index b3f8ef0372bcba..0a164d23ebee15 100644 --- a/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs @@ -14,7 +14,7 @@ internal static class LoongArch64PassStructInRegister public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) { FieldDesc firstField = null; - uint floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; int numIntroducedFields = 0; foreach (FieldDesc field in typeDesc.GetFields()) { @@ -30,7 +30,7 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16)) { - return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; } //// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers @@ -63,7 +63,7 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize; if (numIntroducedFields > 2) { - return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; } } @@ -72,7 +72,7 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) { if (fieldIndex > 1) { - return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; } else if (field.IsStatic) { @@ -86,26 +86,42 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) case TypeFlags.Double: { if (numIntroducedFields == 1) - floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + } else if (fieldIndex == 0) - floatFieldFlags = (uint)LAFlags.STRUCT_FIRST_FIELD_DOUBLE; - else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - floatFieldFlags = floatFieldFlags ^ (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND_8; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_DOUBLE; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags = floatFieldFlags ^ (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND_8; + } else - floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_DOUBLE; + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE; + } } break; case TypeFlags.Single: { if (numIntroducedFields == 1) - floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + } else if (fieldIndex == 0) - floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST; - else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - floatFieldFlags ^= (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; + } else - floatFieldFlags |= (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND; + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND; + } } break; @@ -116,29 +132,45 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) { uint floatFieldFlags2 = GetLoongArch64PassStructInRegisterFlags(field.FieldType); if (numIntroducedFields == 1) + { floatFieldFlags = floatFieldFlags2; + } else if (field.FieldType.GetElementSize().AsInt > 8) - return (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + { + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } else if (fieldIndex == 0) { - if ((floatFieldFlags2 & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - floatFieldFlags = (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST; + if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; + } if (field.FieldType.GetElementSize().AsInt == 8) - floatFieldFlags |= (uint)LAFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + } } else { Debug.Assert(fieldIndex == 1); - if ((floatFieldFlags2 & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - floatFieldFlags |= (uint)LAFlags.STRUCT_MERGE_FIRST_SECOND; + if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; + } if (field.FieldType.GetElementSize().AsInt == 8) - floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + } - floatFieldFlags2 = floatFieldFlags & ((uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND); + floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); if (floatFieldFlags2 == 0) - floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; - else if (floatFieldFlags2 == ((uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND)) - floatFieldFlags ^= ((uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)LAFlags.STRUCT_FLOAT_FIELD_SECOND); + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + else if (floatFieldFlags2 == ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND)) + { + floatFieldFlags ^= ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); + } } } break; @@ -150,15 +182,23 @@ public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc typeDesc) if (numIntroducedFields > 1) { if (fieldIndex == 0) - floatFieldFlags = (uint)LAFlags.STRUCT_FIRST_FIELD_SIZE_IS8; - else if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - floatFieldFlags |= (uint)LAFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + } else - floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } } } else if (fieldIndex == 1) - floatFieldFlags = (floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + { + floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } break; } } 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 1cd224d500f190..63c329f16840bb 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 @@ -1317,7 +1317,7 @@ public int GetNextOffset() case TargetArchitecture.LoongArch64: { int cFPRegs = 0; - uint floatFieldFlags = (uint)LAFlags.STRUCT_NO_FLOAT_FIELD; + uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; _hasArgLocDescForStructInRegs = false; switch (argType) @@ -1342,10 +1342,14 @@ public int GetNextOffset() else { floatFieldFlags = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); - if ((floatFieldFlags & (uint)LAFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + { cFPRegs = 2; - else if ((floatFieldFlags & (uint)LAFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + { cFPRegs = 1; + } } break; @@ -1360,7 +1364,7 @@ public int GetNextOffset() if (cFPRegs > 0 && !IsVarArg) { - if (isValueType && ((floatFieldFlags & (uint)LAFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) { if ((_loongarch64IdxFPReg < 8) && (_loongarch64IdxGenReg < 8)) { @@ -1885,7 +1889,6 @@ private enum ParamTypeLocation // METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke // RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize - // {}; private void ComputeReturnFlags() { From a6cc13a412b09f48fff26fcbf4df343bd55c5072 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Fri, 15 Jul 2022 15:40:16 +0800 Subject: [PATCH 7/7] delete unused comments. --- .../DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs index b2c4fef90d50d0..a1b5808c94b448 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs @@ -34,7 +34,7 @@ public TargetRegisterMap(TargetOS os) Arg6 = Register.R11; Arg7 = Register.R12; IntraProcedureCallScratch1 = Register.R21; - Result = Register.R4; // TODO: ??? + Result = Register.R4; } } }