[LoongArch64] add crossgen2 for LoongArch64. (#72017)
authorQiao Pengcheng <qiaopengcheng@loongson.cn>
Fri, 15 Jul 2022 23:48:48 +0000 (07:48 +0800)
committerGitHub <noreply@github.com>
Fri, 15 Jul 2022 23:48:48 +0000 (16:48 -0700)
* [LoongArch64] add crossgen2 for LoongArch64.

* add `aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64`

* amend the code format.

* delete unused comments.

36 files changed:
src/coreclr/jit/emitloongarch64.cpp
src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/AddrMode.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/TargetRegisterMap.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs
src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs [new file with mode: 0644]
src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs
src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64JumpStubNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64TentativeMethodNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64UnboxingStubNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs
src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj
src/coreclr/tools/aot/crossgen2/Program.cs
src/coreclr/tools/aot/crossgen2/crossgen2.props

index a1d8c2a..d221384 100644 (file)
@@ -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;
 
index b95daf5..b289ff3 100644 (file)
@@ -68,6 +68,13 @@ namespace ILCompiler.DependencyAnalysis
                     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 @@ namespace ILCompiler.DependencyAnalysis
         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);
     }
 }
index da20de1..f20020a 100644 (file)
@@ -303,6 +303,8 @@ namespace ILCompiler.DependencyAnalysis
                 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.
index eda9378..985fca9 100644 (file)
@@ -16,6 +16,8 @@ namespace ILCompiler.DependencyAnalysis
         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 @@ namespace ILCompiler.DependencyAnalysis
             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 @@ namespace ILCompiler.DependencyAnalysis
                 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 @@ namespace ILCompiler.DependencyAnalysis
                     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 (file)
index 0000000..5616941
--- /dev/null
@@ -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 (file)
index 0000000..db0fd17
--- /dev/null
@@ -0,0 +1,134 @@
+// 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 EmitBreak()
+        {
+            Builder.EmitUInt(0x002a0005);
+        }
+
+        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);
+        }
+
+        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)
+        {
+            Debug.Assert((uint)regDst > 0 && (uint)regDst < 32);
+            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));
+
+            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)
+            {
+                // 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();
+            }
+        }
+
+        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/Register.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/Register.cs
new file mode 100644 (file)
index 0000000..1ce65e7
--- /dev/null
@@ -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 (file)
index 0000000..a1b5808
--- /dev/null
@@ -0,0 +1,40 @@
+// 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
+{
+    /// <summary>
+    /// Maps logical registers to physical registers on a specified OS.
+    /// </summary>
+    public struct TargetRegisterMap
+    {
+        public readonly Register Arg0;
+        public readonly Register Arg1;
+        public readonly Register Arg2;
+        public readonly Register Arg3;
+        public readonly Register Arg4;
+        public readonly Register Arg5;
+        public readonly Register Arg6;
+        public readonly Register Arg7;
+        public readonly Register IntraProcedureCallScratch1;
+        public readonly Register Result;
+
+        public TargetRegisterMap(TargetOS os)
+        {
+            Arg0 = Register.R4;
+            Arg1 = Register.R5;
+            Arg2 = Register.R6;
+            Arg3 = Register.R7;
+            Arg4 = Register.R8;
+            Arg5 = Register.R9;
+            Arg6 = Register.R11;
+            Arg7 = Register.R12;
+            IntraProcedureCallScratch1 = Register.R21;
+            Result = Register.R4;
+        }
+    }
+}
index cab9ca3..a48ca4c 100644 (file)
@@ -110,6 +110,10 @@ namespace ILCompiler
             {
                 return SimdVectorLength.None;
             }
+            else if (_targetArchitecture == TargetArchitecture.LoongArch64)
+            {
+                return SimdVectorLength.None;
+            }
             else
             {
                 Debug.Assert(false); // Unknown architecture
index d707d13..3360016 100644 (file)
@@ -52,6 +52,7 @@ namespace Internal.JitInterface
             AMD64 = 0x8664,
             ARM = 0x01c4,
             ARM64 = 0xaa64,
+            LoongArch64 = 0x6264,
         }
 
         internal const string JitLibrary = "clrjitilc";
@@ -2916,11 +2917,6 @@ namespace Internal.JitInterface
             }
         }
 
-        private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls)
-        {
-            throw new NotImplementedException("For LoongArch64, would be implemented later");
-        }
-
         private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args)
         {
             int index = (int)args;
@@ -3140,6 +3136,12 @@ namespace Internal.JitInterface
             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)
@@ -3518,25 +3520,46 @@ namespace Internal.JitInterface
         // 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)
-                return (RelocType)fRelocType;
+            switch (targetArchitecture)
+            {
+                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;
 
-            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;
 
-            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;
+                    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;
+                    }
+                }
                 default:
-                    Debug.Fail("Invalid RelocType: " + fRelocType);
-                    return 0;
-            };
+                    return (RelocType)fRelocType;
+            }
         }
 
         private void recordRelocation(void* location, void* locationRW, void* target, ushort fRelocType, ushort slotNum, int addlDelta)
@@ -3631,6 +3654,8 @@ namespace Internal.JitInterface
                     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");
             }
index 04b36e0..17d3628 100644 (file)
@@ -1198,6 +1198,46 @@ namespace Internal.JitInterface
         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 StructFloatFieldInfoFlags
+    {
+        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
     {
index 0fad745..898ec59 100644 (file)
@@ -138,6 +138,7 @@ namespace Internal.JitInterface
                 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 (file)
index 0000000..0a164d2
--- /dev/null
@@ -0,0 +1,212 @@
+// 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)StructFloatFieldInfoFlags.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)StructFloatFieldInfoFlags.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)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+                }
+            }
+
+            int fieldIndex = 0;
+            foreach (FieldDesc field in typeDesc.GetFields())
+            {
+                if (fieldIndex > 1)
+                {
+                    return (uint)StructFloatFieldInfoFlags.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)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
+                        }
+                        else if (fieldIndex == 0)
+                        {
+                            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)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE;
+                        }
+                    }
+                    break;
+
+                    case  TypeFlags.Single:
+                    {
+                        if (numIntroducedFields == 1)
+                        {
+                            floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
+                        }
+                        else if (fieldIndex == 0)
+                        {
+                            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)StructFloatFieldInfoFlags.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)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+                        }
+                        else if (fieldIndex == 0)
+                        {
+                            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)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8;
+                            }
+                        }
+                        else
+                        {
+                            Debug.Assert(fieldIndex == 1);
+                            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)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8;
+                            }
+
+                            floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND);
+                            if (floatFieldFlags2 == 0)
+                            {
+                                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;
+
+                    default:
+                    {
+                        if (field.FieldType.GetElementSize().AsInt == 8)
+                        {
+                            if (numIntroducedFields > 1)
+                            {
+                                if (fieldIndex == 0)
+                                {
+                                    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)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+                                }
+                            }
+                        }
+                        else if (fieldIndex == 1)
+                        {
+                            floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+                        }
+                        break;
+                    }
+                }
+
+                fieldIndex++;
+            }
+
+            return floatFieldFlags;
+        }
+    }
+}
index b3b587d..160ff67 100644 (file)
@@ -17,5 +17,6 @@ namespace Internal.TypeSystem
         X64,
         X86,
         Wasm32,
+        LoongArch64,
     }
 }
index 343bb57..79b252d 100644 (file)
@@ -80,6 +80,7 @@ namespace Internal.TypeSystem
                 {
                     case TargetArchitecture.ARM64:
                     case TargetArchitecture.X64:
+                    case TargetArchitecture.LoongArch64:
                         return 8;
                     case TargetArchitecture.ARM:
                     case TargetArchitecture.X86:
@@ -116,6 +117,10 @@ namespace Internal.TypeSystem
                     // 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 @@ namespace Internal.TypeSystem
                     case TargetArchitecture.ARM:
                         return 2;
                     case TargetArchitecture.ARM64:
+                    case TargetArchitecture.LoongArch64:
                         return 4;
                     default:
                         return 1;
@@ -276,6 +282,7 @@ namespace Internal.TypeSystem
                         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 @@ namespace Internal.TypeSystem
                 // 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);
 
index 7096525..8ba74c2 100644 (file)
@@ -8,6 +8,7 @@ using ILCompiler.DependencyAnalysis.ARM;
 using ILCompiler.DependencyAnalysis.X64;
 using ILCompiler.DependencyAnalysis.X86;
 using ILCompiler.DependencyAnalysis.ARM64;
+using ILCompiler.DependencyAnalysis.LoongArch64;
 
 namespace ILCompiler.DependencyAnalysis
 {
@@ -51,6 +52,11 @@ namespace ILCompiler.DependencyAnalysis
             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 (file)
index 0000000..20e44d4
--- /dev/null
@@ -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 (file)
index 0000000..eeb4cfe
--- /dev/null
@@ -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 (file)
index 0000000..d618787
--- /dev/null
@@ -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
+{
+    /// <summary>
+    /// LoongArch64 specific portions of ReadyToRunHelperNode
+    /// </summary>
+    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 (file)
index 0000000..e21b21b
--- /dev/null
@@ -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 (file)
index 0000000..7f523d1
--- /dev/null
@@ -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
+        }
+    }
+}
index 7abf44c..827f369 100644 (file)
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\ARMEmitter.cs" Link="Compiler\DependencyAnalysis\Target_ARM\ARMEmitter.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\Register.cs" Link="Compiler\DependencyAnalysis\Target_ARM\Register.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_ARM\TargetRegisterMap.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\AddrMode.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\Register.cs" Link="Compiler\DependencyAnalysis\Target_X64\Register.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" />
     <Compile Include="Compiler\DependencyAnalysis\StructMarshallingDataNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_ARM64\ARM64TentativeMethodNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMTentativeMethodNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64TentativeMethodNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_X64\X64TentativeMethodNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_X86\X86TentativeMethodNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\TentativeInstanceMethodNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMReadyToRunHelperNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMReadyToRunGenericHelperNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMDebug.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64JumpStubNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunHelperNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunGenericHelperNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64UnboxingStubNode.cs" />
     <Compile Include="Compiler\ExportedMethodsRootProvider.cs" />
     <Compile Include="Compiler\GenericDictionaryLookup.cs" />
     <Compile Include="Compiler\IRootingServiceProvider.cs" />
index 5536f42..73e0a1d 100644 (file)
@@ -233,6 +233,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         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 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             m_cGenReg = 0;
             m_byteStackIndex = -1;
             m_byteStackSize = 0;
+            m_floatFlags = 0;
 
             m_fRequires64BitAlignment = false;
         }
@@ -615,6 +617,13 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                             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();
                 }
@@ -811,6 +820,13 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         _arm64IdxFPReg = 0;
                         break;
 
+                    case TargetArchitecture.LoongArch64:
+                        _loongarch64IdxGenReg = numRegistersUsed;
+                        _loongarch64OfsStack = 0;
+
+                        _loongarch64IdxFPReg = 0;
+                        break;
+
                     default:
                         throw new NotImplementedException();
                 }
@@ -1296,6 +1312,124 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         return argOfs;
                     }
 
+                case TargetArchitecture.LoongArch64:
+                    {
+                        int cFPRegs = 0;
+                        uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+                        _hasArgLocDescForStructInRegs = false;
+
+                        switch (argType)
+                        {
+                            case CorElementType.ELEMENT_TYPE_R4:
+                                // 32-bit floating point argument.
+                                cFPRegs = 1;
+                                break;
+
+                            case CorElementType.ELEMENT_TYPE_R8:
+                                // 64-bit floating point argument.
+                                cFPRegs = 1;
+                                break;
+
+                            case CorElementType.ELEMENT_TYPE_VALUETYPE:
+                                {
+                                    // Composite greater than 16 bytes should be passed by reference
+                                    if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
+                                    {
+                                        argSize = _transitionBlock.PointerSize;
+                                    }
+                                    else
+                                    {
+                                        floatFieldFlags = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle());
+                                        if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
+                                        {
+                                            cFPRegs = 2;
+                                        }
+                                        else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
+                                        {
+                                            cFPRegs = 1;
+                                        }
+                                    }
+
+                                    break;
+                                }
+
+                            default:
+                                break;
+                        }
+
+                        bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
+                        int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false);
+
+                        if (cFPRegs > 0 && !IsVarArg)
+                        {
+                            if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0))
+                            {
+                                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();
             }
@@ -1578,6 +1712,64 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         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)
                     {
@@ -1663,6 +1855,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         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;
@@ -1691,7 +1887,6 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         //        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()
         {
index a43ad03..3246b5a 100644 (file)
@@ -138,7 +138,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
 
                     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 (file)
index 0000000..5e0a875
--- /dev/null
@@ -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
+{
+    /// <summary>
+    /// This node emits a thunk calling DelayLoad_Helper with a given instance signature
+    /// to populate its indirection cell.
+    /// </summary>
+    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);
+        }
+    }
+}
index 290744f..8cf01a8 100644 (file)
@@ -43,6 +43,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         AppleArm64TransitionBlock.Instance :
                         Arm64TransitionBlock.Instance;
 
+                case TargetArchitecture.LoongArch64:
+                    return LoongArch64TransitionBlock.Instance;
+
                 default:
                     throw new NotImplementedException(target.Architecture.ToString());
             }
@@ -60,6 +63,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         public bool IsX64 => Architecture == TargetArchitecture.X64;
         public bool IsARM => Architecture == TargetArchitecture.ARM;
         public bool IsARM64 => Architecture == TargetArchitecture.ARM64;
+        public bool IsLoongArch64 => Architecture == TargetArchitecture.LoongArch64;
 
         /// <summary>
         /// This property is only overridden in AMD64 Unix variant of the transition block.
@@ -629,5 +633,58 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                 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);
+            }
+        }
     }
 }
index 6272592..fb8c216 100644 (file)
@@ -116,6 +116,11 @@ namespace ILCompiler
             /// </summary>
             private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm = 8;
 
+            /// <summary>
+            /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for LoongArch64
+            /// </summary>
+            private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64 = 8;
+
             protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value)
             {
                 return key == value.Module;
@@ -414,6 +419,10 @@ namespace ILCompiler
                             nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm;
                             break;
 
+                        case TargetArchitecture.LoongArch64:
+                            nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64;
+                            break;
+
                         default:
                             throw new NotImplementedException();
                     }
index 7dd9d2e..458daf1 100644 (file)
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\ARMEmitter.cs" Link="Compiler\DependencyAnalysis\Target_ARM\ARMEmitter.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\Register.cs" Link="Compiler\DependencyAnalysis\Target_ARM\Register.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_ARM\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_ARM\TargetRegisterMap.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\AddrMode.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" />
+    <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\Register.cs" Link="Compiler\DependencyAnalysis\Target_X64\Register.cs" />
     <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" />
     <Compile Include="..\..\Common\Compiler\VectorFieldLayoutAlgorithm.cs" Link="Compiler\VectorFieldLayoutAlgorithm.cs" />
     <Compile Include="..\..\Common\JitInterface\CorInfoTypes.VarInfo.cs" Link="JitInterface\CorInfoTypes.VarInfo.cs" />
     <Compile Include="..\..\Common\JitInterface\SystemVStructClassificator.cs" Link="JitInterface\SystemVStructClassificator.cs" />
+    <Compile Include="..\..\Common\JitInterface\LoongArch64PassStructInRegister.cs" Link="JitInterface\LoongArch64PassStructInRegister.cs" />
     <Compile Include="..\..\Common\TypeSystem\Interop\InteropTypes.cs" Link="Interop\InteropTypes.cs" />
     <Compile Include="..\..\Common\TypeSystem\Interop\UnmanagedCallingConventions.cs" Link="Interop\UnmanagedCallingConventions.cs" />
     <Compile Include="..\ILCompiler.Reflection.ReadyToRun\PEReaderExtensions.cs" Link="Reflection\PEReaderExtensions.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\StringImportSignature.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_ARM64\ImportThunk.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_ARM\ImportThunk.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_LoongArch64\ImportThunk.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_X64\ImportThunk.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_X86\ImportThunk.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TransitionBlock.cs" />
index 09f9244..d0d9947 100644 (file)
@@ -220,6 +220,14 @@ namespace ILCompiler.PEWriter
                         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 @@ namespace ILCompiler.PEWriter
                     {
                         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));
                     }
index dc34b65..7689691 100644 (file)
@@ -313,6 +313,10 @@ namespace ILCompiler.PEWriter
                     _codePadding = 0xD43E0000u;
                     break;
 
+                case TargetArchitecture.LoongArch64:
+                    _codePadding = 0x002A0005u;
+                    break;
+
                 default:
                     throw new NotImplementedException();
             }
index 57f1bad..8b58919 100644 (file)
@@ -87,6 +87,9 @@ namespace ILCompiler.PEWriter
                 case Internal.TypeSystem.TargetArchitecture.ARM:
                     return Machine.ArmThumb2;
 
+                case Internal.TypeSystem.TargetArchitecture.LoongArch64:
+                    return Machine.LoongArch64;
+
                 default:
                     throw new NotImplementedException(target.Architecture.ToString());
             }
index 893f8e3..449f143 100644 (file)
@@ -610,6 +610,11 @@ namespace ILCompiler.Reflection.ReadyToRun
                     _pointerSize = 8;
                     break;
 
+                case (Machine) 0x6264: /* LoongArch64 */
+                    _architecture = (Architecture) 6; /* LoongArch64 */
+                    _pointerSize = 8;
+                    break;
+
                 default:
                     throw new NotImplementedException(Machine.ToString());
             }
index 3f0bef9..96f4108 100644 (file)
@@ -149,5 +149,20 @@ namespace ILCompiler.Reflection.ReadyToRun
             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;
+        }
     }
 }
index 7777fb4..017e373 100644 (file)
@@ -82,6 +82,9 @@
     <Compile Include="..\..\Common\JitInterface\SystemVStructClassificator.cs">
       <Link>JitInterface\SystemVStructClassificator.cs</Link>
     </Compile>
+    <Compile Include="..\..\Common\JitInterface\LoongArch64PassStructInRegister.cs">
+      <Link>JitInterface\LoongArch64PassStructInRegister.cs</Link>
+    </Compile>
     <Compile Include="..\..\Common\Pgo\TypeSystemEntityOrUnknown.cs">
       <Link>Pgo\TypeSystemEntityOrUnknown.cs</Link>
     </Compile>
index 81bdb61..10e6660 100644 (file)
@@ -81,6 +81,9 @@ namespace ILCompiler
                 case Architecture.Arm64:
                     arch = TargetArchitecture.ARM64;
                     break;
+                case Architecture.LoongArch64:
+                    arch = TargetArchitecture.LoongArch64;
+                    break;
                 default:
                     throw new NotImplementedException();
             }
@@ -207,6 +210,8 @@ namespace ILCompiler
             }
             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);
         }
index d544530..4fd71a9 100644 (file)
@@ -5,7 +5,7 @@
     <OutputType>Exe</OutputType>
     <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
     <NoWarn>8002,NU1701</NoWarn>
-    <Platforms>x64;x86;arm64;arm</Platforms>
+    <Platforms>x64;x86;arm64;arm;loongarch64</Platforms>
     <PlatformTarget>AnyCPU</PlatformTarget>
     <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
     <AppendTargetFrameworkToOutputPath Condition="'$(BuildingInsideVisualStudio)' == 'true'">true</AppendTargetFrameworkToOutputPath>