From 123d4f5efa49c61cc61f2993ffbc20e1c83582a8 Mon Sep 17 00:00:00 2001
From: liuan <liuan@loongson.cn>
Date: Tue, 11 Jun 2024 20:57:48 +0800
Subject: [PATCH] [LoongArch64-mono] Part-1:Add native code emission module for
 LoongArch64

Change-Id: I8e5937729fe5b57ddd4499b6d976c894701de415
---
 .../arch/loongarch64/loongarch64-codegen.h    |  571 ++
 src/mono/mono/mini/mini-loongarch64.c         | 6369 +++++++++++++++++
 src/mono/mono/mini/mini-loongarch64.h         |  293 +
 3 files changed, 7233 insertions(+)
 create mode 100644 src/mono/mono/arch/loongarch64/loongarch64-codegen.h
 create mode 100644 src/mono/mono/mini/mini-loongarch64.c
 create mode 100644 src/mono/mono/mini/mini-loongarch64.h

diff --git a/src/mono/mono/arch/loongarch64/loongarch64-codegen.h b/src/mono/mono/arch/loongarch64/loongarch64-codegen.h
new file mode 100644
index 00000000000000..1b421c9bcf8c83
--- /dev/null
+++ b/src/mono/mono/arch/loongarch64/loongarch64-codegen.h
@@ -0,0 +1,571 @@
+#ifndef __LOONGARCH64_CODEGEN_H__
+#define __LOONGARCH64_CODEGEN_H__
+/*
+ * Copyright (c) 2021 Loongson Technology, Inc
+ * Author: Qiao Pengcheng (qiaopengcheng@loongson.cn), Liu An (liuan@loongson.cn)
+ *
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+/* registers */
+enum {
+	loongarch_zero,/* contonst zero */
+	loongarch_ra,  /* return values */
+	loongarch_tp,  /* TLS */
+	loongarch_sp,  /* stack pointer */
+	loongarch_a0,  /* 8 - func arguments */
+	loongarch_a1,  /* a0,a1 also return registers */
+	loongarch_a2,
+	loongarch_a3,
+	loongarch_a4,
+	loongarch_a5,
+	loongarch_a6,
+	loongarch_a7,
+	loongarch_t0,  /* 9 temp registers */
+	loongarch_t1,
+	loongarch_t2,
+	loongarch_t3,
+	loongarch_t4,
+	loongarch_t5,
+	loongarch_t6,
+	loongarch_t7,
+	loongarch_t8,
+	loongarch_r21, /* scratch registers for mono. */
+	loongarch_fp,  /* frame pointer */
+	loongarch_s0,  /* 9 callee saved registers */
+	loongarch_s1,
+	loongarch_s2,
+	loongarch_s3,
+	loongarch_s4,
+	loongarch_s5,
+	loongarch_s6,
+	loongarch_s7,
+	loongarch_s8,
+};
+
+/* we treat the register file as containing just doubles... */
+enum {
+	loongarch_f0 = 0, /* 8 float arguments */
+	loongarch_f1 = 1, /* f0,f1 also return regs */
+	loongarch_f2 = 2,
+	loongarch_f3 = 3,
+	loongarch_f4 = 4,
+	loongarch_f5 = 5,
+	loongarch_f6 = 6,
+	loongarch_f7 = 7,
+	loongarch_ft0 = 8, /* 16 temp registers */
+	loongarch_ft1 = 9,
+	loongarch_ft2 = 10,
+	loongarch_ft3 = 11,
+	loongarch_ft4 = 12,
+	loongarch_ft5 = 13,
+	loongarch_ft6 = 14,
+	loongarch_ft7 = 15,
+	loongarch_ft8 = 16,
+	loongarch_ft9 = 17,
+	loongarch_ft10 = 18,
+	loongarch_ft11 = 19,
+	loongarch_ft12 = 20,
+	loongarch_ft13 = 21,
+	loongarch_ft14 = 22,
+	loongarch_ft15 = 23,
+	loongarch_fs0 = 24,  /* 8 callee saved registers */
+	loongarch_fs1 = 25,
+	loongarch_fs2 = 26,
+	loongarch_fs3 = 27,
+	loongarch_fs4 = 28,
+	loongarch_fs5 = 29,
+	loongarch_fs6 = 30,
+	loongarch_fs7 = 31,
+};
+
+/* fpu enable/cause flags, cc */
+enum {
+	LOONGARCH_INEXACT = 1,
+	LOONGARCH_UNDERFLOW = 2,
+	LOONGARCH_OVERFLOW = 4,
+	LOONGARCH_DIVZERO = 8,
+	LOONGARCH_INVALID = 16,
+
+	LOONGARCH_FPU_ENABLES_OFFSET = 0,
+	LOONGARCH_FPU_FLAGS_OFFSET = 20,
+	LOONGARCH_FPU_CAUSES_OFFSET = 28
+};
+
+/* fpu compare condition encode - see manual entry for FCMP.cond.{S/D} instructions */
+#define  LOONGARCH_FPU_CAF      0x0
+#define  LOONGARCH_FPU_CUN      0x8
+#define  LOONGARCH_FPU_CEQ      0x4
+#define  LOONGARCH_FPU_CUEQ     0xc
+#define  LOONGARCH_FPU_CLT      0x2
+#define  LOONGARCH_FPU_CULT     0xe
+#define  LOONGARCH_FPU_CLE      0x6
+#define  LOONGARCH_FPU_CULE     0xe
+#define  LOONGARCH_FPU_CNE      0x10
+#define  LOONGARCH_FPU_COR      0x14
+#define  LOONGARCH_FPU_CUNE     0x18
+#define  LOONGARCH_FPU_SAF      0x1
+#define  LOONGARCH_FPU_SUN      0x9
+#define  LOONGARCH_FPU_SEQ      0x5
+#define  LOONGARCH_FPU_SUEQ     0xd
+#define  LOONGARCH_FPU_SLT      0x3
+#define  LOONGARCH_FPU_SULT     0xb
+#define  LOONGARCH_FPU_SLE      0x7
+#define  LOONGARCH_FPU_SULE     0xf
+#define  LOONGARCH_FPU_SNE      0x11
+#define  LOONGARCH_FPU_SOR      0x15
+#define  LOONGARCH_FPU_SUNE     0x19
+
+
+#if SIZEOF_REGISTER != 8
+#error Unknown SIZEOF_REGISTER
+#endif
+
+#define IS_B_SI28_LOONGARCH(dist)  (((dist) >= -(1 << 27)) && ((dist) < (1 << 27)))
+#define IS_B_SI23_LOONGARCH(dist)  (((dist) >= -(1 << 22)) && ((dist) < (1 << 22)))
+#define IS_B_SI18_LOONGARCH(dist)  (((dist) >= -(1 << 17)) && ((dist) < (1 << 17)))
+
+#define loongarch_is_imm12(val)  (((val) >= -0x800) && ((val) <= 0x7ff))
+#define loongarch_is_imm14(val)  (((val) >= -0x2000) && ((val) <= 0x1fff))
+#define loongarch_is_imm16(val) ((gint)(gshort)(gint)(val) == (gint)(val))
+
+#define GASSERT_IS_INT32(val) g_assert((gint)(val) == (long)(val))
+
+#define loongarch_load_const(c,D,v) do {	\
+		if (!loongarch_is_imm12 ((v))) {	\
+			loongarch_lu12iw ((c), (D), ((v) >> 12) & 0xfffff); \
+			loongarch_ori ((c), (D), (D), ((guint32)(v)) & 0xfff); \
+		}							\
+		else							\
+			loongarch_addiw ((c), (D), loongarch_zero, (v) & 0xfff); \
+	} while (0)
+
+#define loongarch_format_bsi21(pointer,encoding,si21,cc)      do {             \
+        if ((si21)>>16) {   \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | (((si21)&0xffff)<<10) | ((cc)<<5) | ((si21)>>16));    \
+        } else    \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((si21)<<10) | ((cc)<<5));    \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_bsi26(pointer,encoding,si26)      do {                  \
+        if ((si26)>>16) {   \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | (((si26)&0xffff)<<10) | (((si26)>>16)&0x3ff));    \
+        } else    \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((si26)<<10));    \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_1rsi(pointer,encoding,si,rd)      do {                  \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((si)<<5) | (rd));    \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_2r(pointer,encoding,rj,rd)      do {                  \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((rj)<<5) | (rd));    \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_2rui(pointer,encoding,ui,rj,rd)      do {                  \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((ui)<<10) | ((rj)<<5) | (rd));    \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_2r2ui(pointer,encoding,msbd,lsbd,rj,rd)      do {     \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((msbd)<<16) | ((lsbd)<<10) | ((rj)<<5) | (rd));\
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_3r(pointer,encoding,rk,rj,rd)      do {               \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((rk)<<10) | ((rj)<<5) | (rd)); \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_3rsa(pointer,encoding,sa,rk,rj,rd)      do {          \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | ((sa)<<15) | ((rk)<<10) | ((rj)<<5) | (rd)); \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);             \
+	} while (0)
+#define loongarch_format_code(pointer,encoding,opcode)      do {        \
+        *((guint32 *)(pointer)) = (guint32)((encoding) | (opcode));     \
+        (pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1);      \
+	} while (0)
+
+/* arithmetric ops */
+#define loongarch_addw(c,dest,srcj,srck) loongarch_format_3r(c,0x00100000,srck,srcj,dest)
+#define loongarch_addd(c,dest,srcj,srck) loongarch_format_3r(c,0x00108000,srck,srcj,dest)
+#define loongarch_subw(c,dest,srcj,srck) loongarch_format_3r(c,0x00110000,srck,srcj,dest)
+#define loongarch_subd(c,dest,srcj,srck) loongarch_format_3r(c,0x00118000,srck,srcj,dest)
+#define loongarch_alslw(c,dest,srcj,srck, sa2)  loongarch_format_3rsa(c,0x00040000,srck,srcj,dest,sa2)
+#define loongarch_alslwu(c,dest,srcj,srck, sa2) loongarch_format_3rsa(c,0x00060000,srck,srcj,dest,sa2)
+#define loongarch_alsld(c,dest,srcj,srck, sa2)  loongarch_format_3rsa(c,0x002c0000,srck,srcj,dest,sa2)
+#define loongarch_addiw(c,dest,src1,si12)    loongarch_format_2rui(c,0x02800000,(si12 & 0xfff),src1,dest)
+#define loongarch_addid(c,dest,src1,si12)    loongarch_format_2rui(c,0x02c00000,(si12 & 0xfff),src1,dest)
+#define loongarch_addu16id(c,dest,src1,si16) loongarch_format_2rui(c,0x10000000,si16,src1,dest)
+
+#define loongarch_lu12iw(c,dest,si20) loongarch_format_1rsi(c,0x14000000,si20,dest)
+#define loongarch_lu32id(c,dest,si20) loongarch_format_1rsi(c,0x16000000,si20,dest)
+#define loongarch_lu52id(c,dest,src1,si12) loongarch_format_2rui(c,0x03000000,si12,src1,dest)
+
+/* relative pc ops */
+#define loongarch_pcaddi(c,dest,si20)    loongarch_format_1rsi(c,0x18000000,si20,dest)
+#define loongarch_pcaddu12i(c,dest,si20) loongarch_format_1rsi(c,0x1c000000,si20,dest)
+#define loongarch_pcaddu18i(c,dest,si20) loongarch_format_1rsi(c,0x1e000000,si20,dest)
+#define loongarch_pcalau12i(c,dest,si20) loongarch_format_1rsi(c,0x1a000000,si20,dest)
+
+/* mul ops */
+#define loongarch_mulw(c,dest,src1,src2)    loongarch_format_3r(c,0x001c0000,src2,src1,dest)
+#define loongarch_mulhw(c,dest,src1,src2)   loongarch_format_3r(c,0x001c8000,src2,src1,dest)
+#define loongarch_mulhwu(c,dest,src1,src2)  loongarch_format_3r(c,0x001d0000,src2,src1,dest)
+#define loongarch_muld(c,dest,src1,src2)    loongarch_format_3r(c,0x001d8000,src2,src1,dest)
+#define loongarch_mulhd(c,dest,src1,src2)   loongarch_format_3r(c,0x001e0000,src2,src1,dest)
+#define loongarch_mulhdu(c,dest,src1,src2)  loongarch_format_3r(c,0x001e8000,src2,src1,dest)
+#define loongarch_mulwdw(c,dest,src1,src2)  loongarch_format_3r(c,0x001f0000,src2,src1,dest)
+#define loongarch_mulwdwu(c,dest,src1,src2) loongarch_format_3r(c,0x001f8000,src2,src1,dest)
+/* div and mod ops */
+#define loongarch_divw(c,dest,src1,src2)  loongarch_format_3r(c,0x00200000,src2,src1,dest)
+#define loongarch_divwu(c,dest,src1,src2) loongarch_format_3r(c,0x00210000,src2,src1,dest)
+#define loongarch_modw(c,dest,src1,src2)  loongarch_format_3r(c,0x00208000,src2,src1,dest)
+#define loongarch_modwu(c,dest,src1,src2) loongarch_format_3r(c,0x00218000,src2,src1,dest)
+#define loongarch_divd(c,dest,src1,src2)  loongarch_format_3r(c,0x00220000,src2,src1,dest)
+#define loongarch_divdu(c,dest,src1,src2) loongarch_format_3r(c,0x00230000,src2,src1,dest)
+#define loongarch_modd(c,dest,src1,src2)  loongarch_format_3r(c,0x00228000,src2,src1,dest)
+#define loongarch_moddu(c,dest,src1,src2) loongarch_format_3r(c,0x00238000,src2,src1,dest)
+
+/* compares */
+#define loongarch_slt(c,dest,src1,src2)   loongarch_format_3r(c,0x00120000,src2,src1,dest)
+#define loongarch_sltu(c,dest,src1,src2)  loongarch_format_3r(c,0x00128000,src2,src1,dest)
+#define loongarch_slti(c,dest,src1,si12)  loongarch_format_2rui(c,0x02000000,si12,src1,dest)
+#define loongarch_sltui(c,dest,src1,si12) loongarch_format_2rui(c,0x02400000,si12,src1,dest)
+
+/* shift ops */
+#define loongarch_sllw(c,dest,src1,src2)  loongarch_format_3r(c,0x00170000,src2,src1,dest)
+#define loongarch_srlw(c,dest,src1,src2)  loongarch_format_3r(c,0x00178000,src2,src1,dest)
+#define loongarch_sraw(c,dest,src1,src2)  loongarch_format_3r(c,0x00180000,src2,src1,dest)
+#define loongarch_rotrw(c,dest,src1,src2) loongarch_format_3r(c,0x001b0000,src2,src1,dest)
+#define loongarch_slld(c,dest,src1,src2)  loongarch_format_3r(c,0x00188000,src2,src1,dest)
+#define loongarch_srld(c,dest,src1,src2)  loongarch_format_3r(c,0x00190000,src2,src1,dest)
+#define loongarch_srad(c,dest,src1,src2)  loongarch_format_3r(c,0x00198000,src2,src1,dest)
+#define loongarch_rotrd(c,dest,src1,src2) loongarch_format_3r(c,0x001b8000,src2,src1,dest)
+
+#define loongarch_slliw(c,dest,src1,ui5)  loongarch_format_2rui(c,0x00408000,ui5,src1,dest)
+#define loongarch_srliw(c,dest,src1,ui5)  loongarch_format_2rui(c,0x00448000,ui5,src1,dest)
+#define loongarch_sraiw(c,dest,src1,ui5)  loongarch_format_2rui(c,0x00488000,ui5,src1,dest)
+#define loongarch_rotriw(c,dest,src1,ui5) loongarch_format_2rui(c,0x004c8000,ui5,src1,dest)
+#define loongarch_sllid(c,dest,src1,ui6)  loongarch_format_2rui(c,0x00410000,ui6,src1,dest)
+#define loongarch_srlid(c,dest,src1,ui6)  loongarch_format_2rui(c,0x00450000,ui6,src1,dest)
+#define loongarch_sraid(c,dest,src1,ui6)  loongarch_format_2rui(c,0x00490000,ui6,src1,dest)
+#define loongarch_rotrid(c,dest,src1,ui6) loongarch_format_2rui(c,0x004d0000,ui6,src1,dest)
+
+/* logical ops */
+#define loongarch_and(c,dest,src1,src2)  loongarch_format_3r(c,0x00148000,src2,src1,dest)
+#define loongarch_andn(c,dest,src1,src2) loongarch_format_3r(c,0x00168000,src2,src1,dest)
+#define loongarch_or(c,dest,src1,src2)   loongarch_format_3r(c,0x00150000,src2,src1,dest)
+#define loongarch_orn(c,dest,src1,src2)  loongarch_format_3r(c,0x00160000,src2,src1,dest)
+#define loongarch_nor(c,dest,src1,src2)  loongarch_format_3r(c,0x00140000,src2,src1,dest)
+#define loongarch_xor(c,dest,src1,src2)  loongarch_format_3r(c,0x00158000,src2,src1,dest)
+
+#define loongarch_andi(c,dest,src1,ui12) loongarch_format_2rui(c,0x03400000,ui12,src1,dest)
+#define loongarch_ori(c,dest,src1,ui12)  loongarch_format_2rui(c,0x03800000,ui12,src1,dest)
+#define loongarch_xori(c,dest,src1,ui12) loongarch_format_2rui(c,0x03c00000,ui12,src1,dest)
+#define loongarch_move(c,dest,src1)   loongarch_ori(c,dest,src1,0)
+
+/* bits ops */
+#define loongarch_extwb(c,dest,src1) loongarch_format_2r(c,0x00005c00,src1,dest)
+#define loongarch_extwh(c,dest,src1) loongarch_format_2r(c,0x00005800,src1,dest)
+#define loongarch_clow(c,dest,src1)  loongarch_format_2r(c,0x00001000,src1,dest)
+#define loongarch_clzw(c,dest,src1)  loongarch_format_2r(c,0x00001400,src1,dest)
+#define loongarch_ctow(c,dest,src1)  loongarch_format_2r(c,0x00001800,src1,dest)
+#define loongarch_ctzw(c,dest,src1)  loongarch_format_2r(c,0x00001c00,src1,dest)
+#define loongarch_clod(c,dest,src1)  loongarch_format_2r(c,0x00002000,src1,dest)
+#define loongarch_clzd(c,dest,src1)  loongarch_format_2r(c,0x00002400,src1,dest)
+#define loongarch_ctod(c,dest,src1)  loongarch_format_2r(c,0x00002800,src1,dest)
+#define loongarch_ctzd(c,dest,src1)  loongarch_format_2r(c,0x00002c00,src1,dest)
+
+#define loongarch_bytepickw(c,dest,src1,src2,sa2) loongarch_format_3rsa(c,0x00080000,sa2,src2,src1,dest)
+#define loongarch_bytepickd(c,dest,src1,src2,sa3) loongarch_format_3rsa(c,0x000c0000,sa3,src2,src1,dest)
+#define loongarch_revb2h(c,dest,src1)   loongarch_format_2r(c,0x00003000,src1,dest)
+#define loongarch_revb4h(c,dest,src1)   loongarch_format_2r(c,0x00003400,src1,dest)
+#define loongarch_revb2w(c,dest,src1)   loongarch_format_2r(c,0x00003800,src1,dest)
+#define loongarch_revbd(c,dest,src1)    loongarch_format_2r(c,0x00003c00,src1,dest)
+#define loongarch_revh2w(c,dest,src1)   loongarch_format_2r(c,0x00004000,src1,dest)
+#define loongarch_revhd(c,dest,src1)    loongarch_format_2r(c,0x00004400,src1,dest)
+#define loongarch_bitrev4b(c,dest,src1) loongarch_format_2r(c,0x00004800,src1,dest)
+#define loongarch_bitrev8b(c,dest,src1) loongarch_format_2r(c,0x00004c00,src1,dest)
+#define loongarch_bitrevw(c,dest,src1)  loongarch_format_2r(c,0x00005000,src1,dest)
+#define loongarch_bitrevd(c,dest,src1)  loongarch_format_2r(c,0x00005400,src1,dest)
+#define loongarch_bstrinsw(c,dest,src1,msbw,lsbw)  loongarch_format_2r2ui(c,0x00600000,msbw,lsbw,src1,dest)
+#define loongarch_bstrinsd(c,dest,src1,msbd,lsbd)  loongarch_format_2r2ui(c,0x00800000,msbd,lsbd,src1,dest)
+#define loongarch_bstrpickw(c,dest,src1,msbw,lsbw) loongarch_format_2r2ui(c,0x00608000,msbw,lsbw,src1,dest)
+#define loongarch_bstrpickd(c,dest,src1,msbd,lsbd) loongarch_format_2r2ui(c,0x00c00000,msbd,lsbd,src1,dest)
+#define loongarch_maskeqz(c,dest,src1,src2) loongarch_format_3r(c,0x00130000,src2,src1,dest)
+#define loongarch_masknez(c,dest,src1,src2) loongarch_format_3r(c,0x00138000,src2,src1,dest)
+
+/* conditional branches */
+#define loongarch_beq(c,src1,src2,offs16)  loongarch_format_2rui(c,0x58000000,offs16,src1,src2)
+#define loongarch_bne(c,src1,src2,offs16)  loongarch_format_2rui(c,0x5c000000,offs16,src1,src2)
+#define loongarch_blt(c,src1,src2,offs16)  loongarch_format_2rui(c,0x60000000,offs16,src1,src2)
+#define loongarch_bge(c,src1,src2,offs16)  loongarch_format_2rui(c,0x64000000,offs16,src1,src2)
+#define loongarch_bltu(c,src1,src2,offs16) loongarch_format_2rui(c,0x68000000,offs16,src1,src2)
+#define loongarch_bgeu(c,src1,src2,offs16) loongarch_format_2rui(c,0x6c000000,offs16,src1,src2)
+#define loongarch_beqz(c,src1,offs21) loongarch_format_bsi21(c,0x40000000,offs21,src1)
+#define loongarch_bnez(c,src1,offs21) loongarch_format_bsi21(c,0x44000000,offs21,src1)
+
+/* uncond branches and calls */
+#define loongarch_b(c,offs26)  loongarch_format_bsi26(c,0x50000000,offs26)
+#define loongarch_bl(c,offs26) loongarch_format_bsi26(c,0x54000000,offs26)
+#define loongarch_jirl(c,retreg,src1,offs16) loongarch_format_2rui(c,0x4c000000,offs16,src1,retreg)
+
+/* loads */
+#define loongarch_ldb(c,dest,base,si12)    loongarch_format_2rui(c,0x28000000,si12,base,dest)
+#define loongarch_ldh(c,dest,base,si12)    loongarch_format_2rui(c,0x28400000,si12,base,dest)
+#define loongarch_ldw(c,dest,base,si12)    loongarch_format_2rui(c,0x28800000,si12,base,dest)
+#define loongarch_ldd(c,dest,base,si12)    loongarch_format_2rui(c,0x28c00000,si12,base,dest)
+#define loongarch_ldbu(c,dest,base,si12)   loongarch_format_2rui(c,0x2a000000,si12,base,dest)
+#define loongarch_ldhu(c,dest,base,si12)   loongarch_format_2rui(c,0x2a400000,si12,base,dest)
+#define loongarch_ldwu(c,dest,base,si12)   loongarch_format_2rui(c,0x2a800000,si12,base,dest)
+#define loongarch_ldptrw(c,dest,base,si14) loongarch_format_2rui(c,0x24000000,si14,base,dest)
+#define loongarch_ldptrd(c,dest,base,si14) loongarch_format_2rui(c,0x26000000,si14,base,dest)
+#define loongarch_ldxb(c,dest,base,indx)   loongarch_format_2rui(c,0x38000000,indx,base,dest)
+#define loongarch_ldxh(c,dest,base,indx)   loongarch_format_2rui(c,0x38040000,indx,base,dest)
+#define loongarch_ldxw(c,dest,base,indx)   loongarch_format_2rui(c,0x38080000,indx,base,dest)
+#define loongarch_ldxd(c,dest,base,indx)   loongarch_format_2rui(c,0x380c0000,indx,base,dest)
+#define loongarch_ldxbu(c,dest,base,indx)  loongarch_format_2rui(c,0x38200000,indx,base,dest)
+#define loongarch_ldxhu(c,dest,base,indx)  loongarch_format_2rui(c,0x38240000,indx,base,dest)
+#define loongarch_ldxwu(c,dest,base,indx)  loongarch_format_2rui(c,0x38280000,indx,base,dest)
+/* stores */
+#define loongarch_stb(c,src,base,si12)    loongarch_format_2rui(c,0x29000000,si12,base,src)
+#define loongarch_sth(c,src,base,si12)    loongarch_format_2rui(c,0x29400000,si12,base,src)
+#define loongarch_stw(c,src,base,si12)    loongarch_format_2rui(c,0x29800000,si12,base,src)
+#define loongarch_std(c,src,base,si12)    loongarch_format_2rui(c,0x29c00000,si12,base,src)
+#define loongarch_stptrw(c,src,base,si14) loongarch_format_2rui(c,0x25000000,si14,base,src)
+#define loongarch_stptrd(c,src,base,si14) loongarch_format_2rui(c,0x27000000,si14,base,src)
+#define loongarch_stxb(c,src,base,indx)   loongarch_format_3r(c,0x38100000,indx,base,src)
+#define loongarch_stxh(c,src,base,indx)   loongarch_format_3r(c,0x38140000,indx,base,src)
+#define loongarch_stxw(c,src,base,indx)   loongarch_format_3r(c,0x38180000,indx,base,src)
+#define loongarch_stxd(c,src,base,indx)   loongarch_format_3r(c,0x381c0000,indx,base,src)
+/* atomic ops */
+#define loongarch_llw(c,dest,base,si14) loongarch_format_2rui(c,0x20000000,si14,base,dest)
+#define loongarch_lld(c,dest,base,si14) loongarch_format_2rui(c,0x22000000,si14,base,dest)
+#define loongarch_scw(c,src,base,si14)  loongarch_format_2rui(c,0x21000000,si14,base,src)
+#define loongarch_scd(c,src,base,si14)  loongarch_format_2rui(c,0x23000000,si14,base,src)
+#define loongarch_amswapw(c,dest,src1,addr)    loongarch_format_3r(c,0x38600000,addr,src1,dest)
+#define loongarch_amswapd(c,dest,src1,addr)    loongarch_format_3r(c,0x38608000,addr,src1,dest)
+#define loongarch_amaddw(c,dest,src1,addr)     loongarch_format_3r(c,0x38610000,addr,src1,dest)
+#define loongarch_amaddd(c,dest,src1,addr)     loongarch_format_3r(c,0x38618000,addr,src1,dest)
+#define loongarch_amandw(c,dest,src1,addr)     loongarch_format_3r(c,0x38620000,addr,src1,dest)
+#define loongarch_amandd(c,dest,src1,addr)     loongarch_format_3r(c,0x38628000,addr,src1,dest)
+#define loongarch_amorw(c,dest,src1,addr)      loongarch_format_3r(c,0x38630000,addr,src1,dest)
+#define loongarch_amord(c,dest,src1,addr)      loongarch_format_3r(c,0x38638000,addr,src1,dest)
+#define loongarch_amxorw(c,dest,src1,addr)     loongarch_format_3r(c,0x38640000,addr,src1,dest)
+#define loongarch_amxord(c,dest,src1,addr)     loongarch_format_3r(c,0x38648000,addr,src1,dest)
+#define loongarch_ammaxw(c,dest,src1,addr)     loongarch_format_3r(c,0x38650000,addr,src1,dest)
+#define loongarch_ammaxd(c,dest,src1,addr)     loongarch_format_3r(c,0x38658000,addr,src1,dest)
+#define loongarch_amminw(c,dest,src1,addr)     loongarch_format_3r(c,0x38660000,addr,src1,dest)
+#define loongarch_ammind(c,dest,src1,addr)     loongarch_format_3r(c,0x38668000,addr,src1,dest)
+#define loongarch_ammaxwu(c,dest,src1,addr)    loongarch_format_3r(c,0x38670000,addr,src1,dest)
+#define loongarch_ammaxdu(c,dest,src1,addr)    loongarch_format_3r(c,0x38678000,addr,src1,dest)
+#define loongarch_amminwu(c,dest,src1,addr)    loongarch_format_3r(c,0x38680000,addr,src1,dest)
+#define loongarch_ammindu(c,dest,src1,addr)    loongarch_format_3r(c,0x38688000,addr,src1,dest)
+
+#define loongarch_amswapw_db(c,dest,src1,addr) loongarch_format_3r(c,0x38690000,addr,src1,dest)
+#define loongarch_amswapd_db(c,dest,src1,addr) loongarch_format_3r(c,0x38698000,addr,src1,dest)
+#define loongarch_amaddw_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386a0000,addr,src1,dest)
+#define loongarch_amaddd_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386a8000,addr,src1,dest)
+#define loongarch_amandw_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386b0000,addr,src1,dest)
+#define loongarch_amandd_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386b8000,addr,src1,dest)
+#define loongarch_amorw_db(c,dest,src1,addr)   loongarch_format_3r(c,0x386c0000,addr,src1,dest)
+#define loongarch_amord_db(c,dest,src1,addr)   loongarch_format_3r(c,0x386c8000,addr,src1,dest)
+#define loongarch_amxorw_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386d0000,addr,src1,dest)
+#define loongarch_amxord_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386d8000,addr,src1,dest)
+#define loongarch_ammaxw_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386e0000,addr,src1,dest)
+#define loongarch_ammaxd_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386e8000,addr,src1,dest)
+#define loongarch_amminw_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386f0000,addr,src1,dest)
+#define loongarch_ammind_db(c,dest,src1,addr)  loongarch_format_3r(c,0x386f8000,addr,src1,dest)
+#define loongarch_ammaxwu_db(c,dest,src1,addr) loongarch_format_3r(c,0x38700000,addr,src1,dest)
+#define loongarch_ammaxdu_db(c,dest,src1,addr) loongarch_format_3r(c,0x38708000,addr,src1,dest)
+#define loongarch_amminwu_db(c,dest,src1,addr) loongarch_format_3r(c,0x38710000,addr,src1,dest)
+#define loongarch_ammindu_db(c,dest,src1,addr) loongarch_format_3r(c,0x38718000,addr,src1,dest)
+
+/* misc ops */
+////nop = andi 0,0,0
+#define loongarch_nop(pointer)       do {                                \
+		*((guint32 *)(pointer)) = 0x03400000;                      \
+		(pointer) = (typeof(pointer))(((guint32 *)(pointer)) + 1); \
+	} while (0)
+#define loongarch_preld(c,hint,base,si12)  loongarch_format_2rui(c,0x2ac00000,si12,base,hint)
+#define loongarch_preldx(c,hint,base,indx) loongarch_format_3r(c,0x382c0000,indx,base,hint)
+#define loongarch_break(c,code)   loongarch_format_code(c,0x002a0005,code)
+#define loongarch_dbar(c,hint)    loongarch_format_code(c,0x38720000,hint)
+#define loongarch_ibar(c,hint)    loongarch_format_code(c,0x38728000,hint)
+#define loongarch_syscall(c,code) loongarch_format_code(c,0x002b0000,code)
+#define loongarch_asrtled(c,src1,src2) loongarch_format_3r(c,0x00010000,src2,src1,0)
+#define loongarch_asrtgtd(c,src1,src2) loongarch_format_3r(c,0x00018000,src2,src1,0)
+#define loongarch_rdtimed(c,dest,src1) loongarch_format_2r(c,0x00006800,src1,dest)
+#define loongarch_cpucfg(c,dest,src1)  loongarch_format_2r(c,0x00006c00,src1,dest)
+////TODO: for crc* instructions.
+//#define loongarch_crc*(c,dest,src1) loongarch_emit32(c, ((code)<<6)|12)
+
+/* fpu ops */
+#define loongarch_fabss(c,dest,src) loongarch_format_2r(c,0x01140400,src,dest)
+#define loongarch_fabsd(c,dest,src) loongarch_format_2r(c,0x01140800,src,dest)
+#define loongarch_fnegs(c,dest,src) loongarch_format_2r(c,0x01141400,src,dest)
+#define loongarch_fnegd(c,dest,src) loongarch_format_2r(c,0x01141800,src,dest)
+
+#define loongarch_fadds(c,dest,src1,src2) loongarch_format_3r(c,0x01008000,src2,src1,dest)
+#define loongarch_faddd(c,dest,src1,src2) loongarch_format_3r(c,0x01010000,src2,src1,dest)
+#define loongarch_fsubs(c,dest,src1,src2) loongarch_format_3r(c,0x01028000,src2,src1,dest)
+#define loongarch_fsubd(c,dest,src1,src2) loongarch_format_3r(c,0x01030000,src2,src1,dest)
+#define loongarch_fdivs(c,dest,src1,src2) loongarch_format_3r(c,0x01068000,src2,src1,dest)
+#define loongarch_fdivd(c,dest,src1,src2) loongarch_format_3r(c,0x01070000,src2,src1,dest)
+#define loongarch_fmuls(c,dest,src1,src2) loongarch_format_3r(c,0x01048000,src2,src1,dest)
+#define loongarch_fmuld(c,dest,src1,src2) loongarch_format_3r(c,0x01050000,src2,src1,dest)
+
+#define loongarch_fmadds(c,dest,src1,src2,srcadd)  loongarch_format_3rsa(c,,srcadd,src2,src1,dest)
+#define loongarch_fmaddd(c,dest,src1,src2,srcadd)  loongarch_format_3rsa(c,,srcadd,src2,src1,dest)
+#define loongarch_fmsubs(c,dest,src1,src2,srcsub)  loongarch_format_3rsa(c,,srcsub,src2,src1,dest)
+#define loongarch_fmsubd(c,dest,src1,src2,srcsub)  loongarch_format_3rsa(c,,srcsub,src2,src1,dest)
+#define loongarch_fnmadds(c,dest,src1,src2,srcadd) loongarch_format_3rsa(c,,srcadd,src2,src1,dest)
+#define loongarch_fnmaddd(c,dest,src1,src2,srcadd) loongarch_format_3rsa(c,,srcadd,src2,src1,dest)
+#define loongarch_fnmsubs(c,dest,src1,src2,srcsub) loongarch_format_3rsa(c,,srcsub,src2,src1,dest)
+#define loongarch_fnmsubd(c,dest,src1,src2,srcsub) loongarch_format_3rsa(c,,srcsub,src2,src1,dest)
+
+#define loongarch_fsqrts(c,dest,src)  loongarch_format_2r(c,0x01144400,src,dest)
+#define loongarch_fsqrtd(c,dest,src)  loongarch_format_2r(c,0x01144800,src,dest)
+#define loongarch_frecips(c,dest,src) loongarch_format_2r(c,0x01145400,src,dest)
+#define loongarch_frecipd(c,dest,src) loongarch_format_2r(c,0x01145800,src,dest)
+#define loongarch_frsqrts(c,dest,src) loongarch_format_2r(c,0x01146400,src,dest)
+#define loongarch_frsqrtd(c,dest,src) loongarch_format_2r(c,0x01146800,src,dest)
+
+#define loongarch_fmaxs(c,dest,src1,src2)  loongarch_format_3r(c,0x01088000,src2,src1,dest)
+#define loongarch_fmaxd(c,dest,src1,src2)  loongarch_format_3r(c,0x01090000,src2,src1,dest)
+#define loongarch_fmins(c,dest,src1,src2)  loongarch_format_3r(c,0x010a8000,src2,src1,dest)
+#define loongarch_fmind(c,dest,src1,src2)  loongarch_format_3r(c,0x010b0000,src2,src1,dest)
+#define loongarch_fmaxas(c,dest,src1,src2) loongarch_format_3r(c,0x010c8000,src2,src1,dest)
+#define loongarch_fmaxad(c,dest,src1,src2) loongarch_format_3r(c,0x010d0000,src2,src1,dest)
+#define loongarch_fminas(c,dest,src1,src2) loongarch_format_3r(c,0x010e8000,src2,src1,dest)
+#define loongarch_fminad(c,dest,src1,src2) loongarch_format_3r(c,0x010f0000,src2,src1,dest)
+
+#define loongarch_flogbs(c,dest,src)  loongarch_format_2r(c,0x01142400,src,dest)
+#define loongarch_flogbd(c,dest,src)  loongarch_format_2r(c,0x01142800,src,dest)
+#define loongarch_fclasss(c,dest,src) loongarch_format_2r(c,0x01143400,src,dest)
+#define loongarch_fclassd(c,dest,src) loongarch_format_2r(c,0x01143800,src,dest)
+#define loongarch_fcopysigns(c,dest,src1,src2) loongarch_format_3r(c,0x01128000,src2,src1,dest)
+#define loongarch_fcopysignd(c,dest,src1,src2) loongarch_format_3r(c,0x01130000,src2,src1,dest)
+#define loongarch_fscalebs(c,dest,src1,src2)   loongarch_format_3r(c,0x01108000,src2,src1,dest)
+#define loongarch_fscalebd(c,dest,src1,src2)   loongarch_format_3r(c,0x01110000,src2,src1,dest)
+
+/* fp compare */
+#define loongarch_fcmpcafs(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c100000,0,src2,src1,cc)
+#define loongarch_fcmpcuns(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c140000,0,src2,src1,cc)
+#define loongarch_fcmpceqs(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c120000,0,src2,src1,cc)
+#define loongarch_fcmpcueqs(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c160000,0,src2,src1,cc)
+#define loongarch_fcmpclts(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c110000,0,src2,src1,cc)
+#define loongarch_fcmpcults(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c150000,0,src2,src1,cc)
+#define loongarch_fcmpcles(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c130000,0,src2,src1,cc)
+#define loongarch_fcmpcules(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c170000,0,src2,src1,cc)
+#define loongarch_fcmpcnes(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c180000,0,src2,src1,cc)
+#define loongarch_fcmpcors(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c1a0000,0,src2,src1,cc)
+#define loongarch_fcmpcunes(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c1c0000,0,src2,src1,cc)
+
+#define loongarch_fcmpsafs(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c108000,0,src2,src1,cc)
+#define loongarch_fcmpsuns(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c148000,0,src2,src1,cc)
+#define loongarch_fcmpseqs(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c128000,0,src2,src1,cc)
+#define loongarch_fcmpsueqs(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c168000,0,src2,src1,cc)
+#define loongarch_fcmpslts(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c118000,0,src2,src1,cc)
+#define loongarch_fcmpsults(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c158000,0,src2,src1,cc)
+#define loongarch_fcmpsles(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c138000,0,src2,src1,cc)
+#define loongarch_fcmpsules(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c178000,0,src2,src1,cc)
+#define loongarch_fcmpsnes(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c188000,0,src2,src1,cc)
+#define loongarch_fcmpsors(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c1a8000,0,src2,src1,cc)
+#define loongarch_fcmpsunes(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c1c8000,0,src2,src1,cc)
+#define loongarch_fcmpcafd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c200000,0,src2,src1,cc)
+#define loongarch_fcmpcund(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c240000,0,src2,src1,cc)
+#define loongarch_fcmpceqd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c220000,0,src2,src1,cc)
+#define loongarch_fcmpcueqd(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c260000,0,src2,src1,cc)
+#define loongarch_fcmpcltd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c210000,0,src2,src1,cc)
+#define loongarch_fcmpcultd(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c250000,0,src2,src1,cc)
+#define loongarch_fcmpcled(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c230000,0,src2,src1,cc)
+#define loongarch_fcmpculed(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c270000,0,src2,src1,cc)
+#define loongarch_fcmpcned(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c280000,0,src2,src1,cc)
+#define loongarch_fcmpcord(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c2a0000,0,src2,src1,cc)
+#define loongarch_fcmpcuned(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c2c0000,0,src2,src1,cc)
+#define loongarch_fcmpsafd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c208000,0,src2,src1,cc)
+#define loongarch_fcmpsund(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c248000,0,src2,src1,cc)
+#define loongarch_fcmpseqd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c228000,0,src2,src1,cc)
+#define loongarch_fcmpsueqd(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c268000,0,src2,src1,cc)
+#define loongarch_fcmpsltd(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c218000,0,src2,src1,cc)
+#define loongarch_fcmpsultd(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c258000,0,src2,src1,cc)
+#define loongarch_fcmpsled(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c238000,0,src2,src1,cc)
+#define loongarch_fcmpsuled(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c278000,0,src2,src1,cc)
+#define loongarch_fcmpsned(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c288000,0,src2,src1,cc)
+#define loongarch_fcmpsord(c,cc,src1,src2)  loongarch_format_3rsa(c,0x0c2a8000,0,src2,src1,cc)
+#define loongarch_fcmpsuned(c,cc,src1,src2) loongarch_format_3rsa(c,0x0c2c8000,0,src2,src1,cc)
+/* fp branch */
+#define loongarch_bceqz(c,cj,offs21) loongarch_format_bsi21(c,0x48000000,offs21,cj)
+#define loongarch_bcnez(c,cj,offs21) loongarch_format_bsi21(c,0x48000100,offs21,cj)
+
+/* fp convert */
+#define loongarch_fcvtds(c,dest,src)  loongarch_format_2r(c,0x01192400,src,dest)
+#define loongarch_fcvtsd(c,dest,src)  loongarch_format_2r(c,0x01191800,src,dest)
+#define loongarch_frints(c,dest,src)  loongarch_format_2r(c,0x011e4400,src,dest)
+#define loongarch_frintd(c,dest,src)  loongarch_format_2r(c,0x011e4800,src,dest)
+#define loongarch_ffintsw(c,dest,src) loongarch_format_2r(c,0x011d1000,src,dest)
+#define loongarch_ffintsl(c,dest,src) loongarch_format_2r(c,0x011d1800,src,dest)
+#define loongarch_ffintdw(c,dest,src) loongarch_format_2r(c,0x011d2000,src,dest)
+#define loongarch_ffintdl(c,dest,src) loongarch_format_2r(c,0x011d2800,src,dest)
+//round mode by fcsr.
+#define loongarch_ftintws(c,dest,src)   loongarch_format_2r(c,0x011b0400,src,dest)
+#define loongarch_ftintwd(c,dest,src)   loongarch_format_2r(c,0x011b0800,src,dest)
+#define loongarch_ftintls(c,dest,src)   loongarch_format_2r(c,0x011b2400,src,dest)
+#define loongarch_ftintld(c,dest,src)   loongarch_format_2r(c,0x011b2800,src,dest)
+//round minus.
+#define loongarch_ftintrmws(c,dest,src) loongarch_format_2r(c,0x011a0400,src,dest)
+#define loongarch_ftintrmwd(c,dest,src) loongarch_format_2r(c,0x011a0800,src,dest)
+#define loongarch_ftintrmls(c,dest,src) loongarch_format_2r(c,0x011a2400,src,dest)
+#define loongarch_ftintrmld(c,dest,src) loongarch_format_2r(c,0x011a2800,src,dest)
+//round positive.
+#define loongarch_ftintrpws(c,dest,src) loongarch_format_2r(c,0x011a4400,src,dest)
+#define loongarch_ftintrpwd(c,dest,src) loongarch_format_2r(c,0x011a4800,src,dest)
+#define loongarch_ftintrpls(c,dest,src) loongarch_format_2r(c,0x011a6400,src,dest)
+#define loongarch_ftintrpld(c,dest,src) loongarch_format_2r(c,0x011a6800,src,dest)
+//round zero.
+#define loongarch_ftintrzws(c,dest,src) loongarch_format_2r(c,0x011a8400,src,dest)
+#define loongarch_ftintrzwd(c,dest,src) loongarch_format_2r(c,0x011a8800,src,dest)
+#define loongarch_ftintrzls(c,dest,src) loongarch_format_2r(c,0x011aa400,src,dest)
+#define loongarch_ftintrzld(c,dest,src) loongarch_format_2r(c,0x011aa800,src,dest)
+//round near-even.
+#define loongarch_ftintrnews(c,dest,src) loongarch_format_2r(c,0x011ac400,src,dest)
+#define loongarch_ftintrnewd(c,dest,src) loongarch_format_2r(c,0x011ac800,src,dest)
+#define loongarch_ftintrnels(c,dest,src) loongarch_format_2r(c,0x011ae400,src,dest)
+#define loongarch_ftintrneld(c,dest,src) loongarch_format_2r(c,0x011ae800,src,dest)
+
+/* fp moves, loads */
+#define loongarch_fmovs(c,dest,src) loongarch_format_2r(c,0x01149400,src,dest)
+#define loongarch_fmovd(c,dest,src) loongarch_format_2r(c,0x01149800,src,dest)
+#define loongarch_fsel(c,dest,src1,src2,ca) loongarch_format_3rsa(c,0x0d000000,ca,src2,src1,dest)
+//gpr --> fpr.
+#define loongarch_movgr2frw(c,dest,src)  loongarch_format_2r(c,0x0114a400,src,dest)
+#define loongarch_movgr2frhw(c,dest,src) loongarch_format_2r(c,0x0114ac00,src,dest)
+#define loongarch_movgr2frd(c,dest,src)  loongarch_format_2r(c,0x0114a800,src,dest)
+//fpr --> gpr.
+#define loongarch_movfr2grs(c,dest,src)  loongarch_format_2r(c,0x0114b400,src,dest)
+#define loongarch_movfrh2grs(c,dest,src) loongarch_format_2r(c,0x0114bc00,src,dest)
+#define loongarch_movfr2grd(c,dest,src)  loongarch_format_2r(c,0x0114b800,src,dest)
+//gpr --> fcsr.
+#define loongarch_movgr2fcsr(c,dest,src) loongarch_format_2r(c,0x0114c000,src,dest)
+//fcsr --> gpr.
+#define loongarch_movfcsr2gr(c,dest,src) loongarch_format_2r(c,0x0114c800,src,dest)
+//float cond flag <--> fpr.
+#define loongarch_movfr2cf(c,dest,src)   loongarch_format_2r(c,0x0114d000,src,dest)
+#define loongarch_movcf2fr(c,dest,src)   loongarch_format_2r(c,0x0114d400,src,dest)
+//float cond flag <--> gpr.
+#define loongarch_movgr2cf(c,dest,src)   loongarch_format_2r(c,0x0114d800,src,dest)
+#define loongarch_movcf2gr(c,dest,src)   loongarch_format_2r(c,0x0114dc00,src,dest)
+
+/* fp load/store ops */
+#define loongarch_flds(c,dest,base,si12)  loongarch_format_2rui(c,0x2b000000,si12,base,dest)
+#define loongarch_fldd(c,dest,base,si12)  loongarch_format_2rui(c,0x2b800000,si12,base,dest)
+#define loongarch_fsts(c,src,base,si12)   loongarch_format_2rui(c,0x2b400000,si12,base,src)
+#define loongarch_fstd(c,src,base,si12)   loongarch_format_2rui(c,0x2bc00000,si12,base,src)
+
+#define loongarch_fldxs(c,dest,base,indx) loongarch_format_3r(c,0x38300000,indx,base,dest)
+#define loongarch_fldxd(c,dest,base,indx) loongarch_format_3r(c,0x38340000,indx,base,dest)
+#define loongarch_fstxs(c,src,base,indx)  loongarch_format_3r(c,0x38380000,base,indx,src)
+#define loongarch_fstxd(c,src,base,indx)  loongarch_format_3r(c,0x383c0000,base,indx,src)
+
+////TODO:no float boundary load/store instructions.
+
+#endif /* __LOONGARCH64_CODEGEN_H__ */
diff --git a/src/mono/mono/mini/mini-loongarch64.c b/src/mono/mono/mini/mini-loongarch64.c
new file mode 100644
index 00000000000000..bfce3606c0c60b
--- /dev/null
+++ b/src/mono/mono/mini/mini-loongarch64.c
@@ -0,0 +1,6369 @@
+/**
+ * \file
+ * LOONGARCH64 backend for the Mono code generator
+ *
+ * Authors:
+ *   Qiao Pengcheng (qiaopengcheng@loongson.cn), Liu An (liuan@loongson.cn)
+ *
+ * Copyright (c) 2021 Loongson Technology, Inc
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+#include "mini.h"
+#include "cpu-loongarch64.h"
+#include "ir-emit.h"
+#include "aot-runtime.h"
+#include "mini-runtime.h"
+
+#include <mono/arch/loongarch64/loongarch64-codegen.h>
+#include <mono/utils/mono-mmap.h>
+#include <mono/utils/mono-memory-model.h>
+#include <mono/metadata/abi-details.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/marshal-shared.h>
+
+#include "interp/interp.h"
+
+#define THUNK_SIZE (4 * 6)
+
+/* The single step trampoline */
+static gpointer ss_trampoline;
+
+/* The breakpoint trampoline */
+static gpointer bp_trampoline;
+
+#define EMIT_STORE(code, dreg, base, offset, tmp_reg)  if (loongarch_is_imm12 (offset)) {  \
+			loongarch_std (code, dreg, base, offset);                \
+		} else {                                                     \
+			/* NOTE: Assuming the offset is less than 2G. */         \
+			loongarch_lu12iw (code, tmp_reg, (offset >> 12) & 0xfffff);          \
+			loongarch_ori (code, tmp_reg, tmp_reg, offset & 0xfff);  \
+			loongarch_stxd (code, dreg, base, tmp_reg);              \
+		}
+
+#define EMIT_IMM_PTR(code, dreg, imm_ptr)   loongarch_lu12iw (code, dreg, (imm_ptr >> 12) & 0xfffff); \
+			loongarch_lu32id (code, dreg, (imm_ptr >> 32) & 0xfffff);  \
+			loongarch_ori (code, dreg, dreg, imm_ptr & 0xfff)
+
+/* Emit a call sequence to 'v', using 'D' as a scratch register if necessary */
+#define loongarch_call(c,D,v) do {	\
+		guint32 _target = (guint32)(v); \
+		if (1 || ((v) == NULL) || ((_target & 0xfc000000) != (((guint32)(c)) & 0xfc000000))) { \
+			loongarch_load_const (c, D, _target); \
+			loongarch_jirl (c, D, loongarch_ra); \
+		} \
+		else { \
+			;/*loongarch_jumpl (c, _target >> 2);*/ \
+		} \
+	} while (0)
+
+/* This mutex protects architecture specific caches */
+#define mono_mini_arch_lock() mono_os_mutex_lock (&mini_arch_mutex)
+#define mono_mini_arch_unlock() mono_os_mutex_unlock (&mini_arch_mutex)
+static mono_mutex_t mini_arch_mutex;
+
+/* Index of ms word/register. */
+static int ls_word_idx;
+/* Index of ls word/register */
+static int ms_word_idx;
+/* Same for offsets */
+static int ls_word_offset;
+static int ms_word_offset;
+
+/*
+ * The code generated for sequence points reads from this location, which is
+ * made read-only when single stepping is enabled.
+ */
+// static gpointer ss_trigger_page;
+
+/* Enabled breakpoints read from this trigger page */
+// static gpointer bp_trigger_page;
+
+#undef DEBUG
+#define DEBUG(a) if (cfg->verbose_level > 1) a
+#undef DEBUG
+#define DEBUG(a) a
+#undef DEBUG
+#define DEBUG(a)
+
+#define MONO_EMIT_NEW_LOAD_R8(cfg,dr,addr) do { \
+		MonoInst *inst;				   \
+		MONO_INST_NEW ((cfg), (inst), OP_R8CONST); \
+		inst->type = STACK_R8;			   \
+		inst->dreg = (dr);		       \
+		inst->inst_p0 = (void*)(addr);	       \
+		mono_bblock_add_inst (cfg->cbb, inst); \
+	} while (0)
+
+#define ins_is_compare(ins) ((ins) && (((ins)->opcode == OP_COMPARE) \
+				       || ((ins)->opcode == OP_ICOMPARE) \
+				       || ((ins)->opcode == OP_LCOMPARE) \
+				       || ((ins)->opcode == OP_RCOMPARE) \
+				       || ((ins)->opcode == OP_FCOMPARE)))
+#define ins_is_compare_imm(ins) ((ins) && (((ins)->opcode == OP_COMPARE_IMM) \
+					   || ((ins)->opcode == OP_ICOMPARE_IMM) \
+					   || ((ins)->opcode == OP_LCOMPARE_IMM)))
+
+#define INS_REWRITE(ins, op, _s1, _s2) do { \
+			int s1 = _s1;			\
+			int s2 = _s2;			\
+			ins->opcode = (op);		\
+			ins->sreg1 = (s1);		\
+			ins->sreg2 = (s2);		\
+	} while (0);
+
+#define INS_REWRITE_IMM(ins, op, _s1, _imm) do { \
+			int s1 = _s1;			\
+			ins->opcode = (op);		\
+			ins->sreg1 = (s1);		\
+			ins->inst_imm = (_imm);		\
+	} while (0);
+
+void mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg);
+MonoInst *mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
+
+void
+mono_arch_flush_icache (guint8 *code, gint size)
+{
+	__asm__ volatile( "ibar 0;  \n");
+}
+
+void
+mono_arch_flush_register_windows (void)
+{
+}
+
+const char*
+mono_arch_regname (int reg) {
+	static const char * rnames[] = {
+		"zero", "ra", "tp", "sp",
+		"a0", "a1", "a2", "a3",
+		"a4", "a5", "a6", "a7",
+		"t0", "t1", "t2", "t3",
+		"t4", "t5", "t6", "t7",
+		"t8", "r21", "fp", "s0",
+		"s1", "s2", "s3", "s4",
+		"s5", "s6", "s7", "s8",
+	};
+	if (reg >= 0 && reg < 32)
+		return rnames [reg];
+	return "unknown";
+}
+
+const char*
+mono_arch_fregname (int reg) {
+	static const char * rnames[] = {
+		"f0", "f1", "f2", "f3",
+		"f4", "f5", "f6", "f7",
+		"f8", "f9", "f10", "f11",
+		"f12", "f13", "f14", "f15",
+		"f16", "f17", "f18", "f19",
+		"f20", "f21", "f22", "f23",
+		"f24", "f25", "f26", "f27",
+		"f28", "f29", "f30", "f31"
+	};
+	if (reg >= 0 && reg < 32)
+		return rnames [reg];
+	return "unknown";
+}
+
+/*
+ * mono_arch_get_argument_info:
+ * @csig:  a method signature
+ * @param_count: the number of parameters to consider
+ * @arg_info: an array to store the result infos
+ *
+ * Gathers information on parameters such as size, alignment and
+ * padding. arg_info should be large enought to hold param_count + 1 entries.
+ *
+ * Returns the size of the activation frame.
+ */
+int
+mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
+{
+	int k, frame_size = 0;
+	guint32 size, align, pad;
+	int offset = 0;
+
+	if (MONO_TYPE_ISSTRUCT (csig->ret)) {
+		frame_size += sizeof (target_mgreg_t);
+		offset += 4;
+	}
+
+	arg_info [0].offset = offset;
+
+	if (csig->hasthis) {
+		frame_size += sizeof (target_mgreg_t);
+		offset += 4;
+	}
+
+	arg_info [0].size = frame_size;
+
+	for (k = 0; k < param_count; k++) {
+		size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke);
+
+		/* ignore alignment for now */
+		align = 1;
+
+		frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
+		arg_info [k].pad = pad;
+		frame_size += size;
+		arg_info [k + 1].pad = 0;
+		arg_info [k + 1].size = size;
+		offset += pad;
+		arg_info [k + 1].offset = offset;
+		offset += size;
+	}
+
+	align = MONO_ARCH_FRAME_ALIGNMENT;
+	frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
+	arg_info [k].pad = pad;
+
+	return frame_size;
+}
+
+/* The delegate object plus 3 params */
+#define MAX_ARCH_DELEGATE_PARAMS (4 - 1)
+
+static guint8*
+get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, gboolean param_count)
+{
+	guint8 *code, *start;
+
+	if (has_target) {
+		start = code = mono_global_codeman_reserve (16);
+
+		/* Replace the this argument with the target */
+		loongarch_ldd (code, loongarch_r21, loongarch_a0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
+		loongarch_ldd (code, loongarch_a0, loongarch_a0, MONO_STRUCT_OFFSET (MonoDelegate, target));
+		loongarch_jirl (code, 0, loongarch_r21, 0);
+
+		g_assert ((code - start) <= 16);
+
+		mono_arch_flush_icache (start, 16);
+		MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL));
+	} else {
+		int size, i;
+
+		size = 16 + param_count * 4;
+		start = code = mono_global_codeman_reserve (size);
+
+		loongarch_ldd (code, loongarch_r21, loongarch_a0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
+		/* slide down the arguments */
+		for (i = 0; i < param_count; ++i) {
+			loongarch_move (code, loongarch_a0 + i, loongarch_a0 + i + 1);
+		}
+		loongarch_jirl (code, 0, loongarch_r21, 0);
+
+		g_assert ((code - start) <= size);
+
+		mono_arch_flush_icache (start, size);
+		MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL));
+	}
+
+	if (has_target) {
+		*info = mono_tramp_info_create ("delegate_invoke_impl_has_target", start, code - start, NULL, NULL);
+	} else {
+		char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", param_count);
+		*info = mono_tramp_info_create (name, start, code - start, NULL, NULL);
+		g_free (name);
+	}
+
+	return start;
+}
+
+/*
+ * mono_arch_get_delegate_invoke_impls:
+ *
+ *   Return a list of MonoAotTrampInfo structures for the delegate invoke impl
+ * trampolines.
+ */
+GSList*
+mono_arch_get_delegate_invoke_impls (void)
+{
+	GSList *res = NULL;
+	MonoTrampInfo *info;
+	int i;
+
+	get_delegate_invoke_impl (&info, TRUE, 0);
+	res = g_slist_prepend (res, info);
+
+	for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
+		get_delegate_invoke_impl (&info, FALSE, i);
+		res = g_slist_prepend (res, info);
+	}
+
+	return res;
+}
+
+gpointer
+mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
+{
+	guint8 *code, *start;
+
+	if (MONO_TYPE_ISSTRUCT (sig->ret))
+		return NULL;
+
+	if (has_target) {
+		static guint8* cached = NULL;
+		mono_mini_arch_lock ();
+		if (cached) {
+			mono_mini_arch_unlock ();
+			return cached;
+		}
+
+		if (mono_ee_features.use_aot_trampolines) {
+			start = mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
+		} else {
+			MonoTrampInfo *info;
+			start = get_delegate_invoke_impl (&info, TRUE, 0);
+			mono_tramp_info_register (info, NULL);
+		}
+		cached = start;
+		mono_mini_arch_unlock ();
+		return cached;
+	} else {
+		static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
+		int i;
+
+		if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
+			return NULL;
+		for (i = 0; i < sig->param_count; ++i)
+			if (!mono_is_regsize_var (sig->params [i]))
+				return NULL;
+
+		mono_mini_arch_lock ();
+		code = cache [sig->param_count];
+		if (code) {
+			mono_mini_arch_unlock ();
+			return code;
+		}
+
+		if (mono_ee_features.use_aot_trampolines) {
+			char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
+			start = mono_aot_get_trampoline (name);
+			g_free (name);
+		} else {
+			MonoTrampInfo *info;
+			start = get_delegate_invoke_impl (&info, FALSE, sig->param_count);
+			mono_tramp_info_register (info, NULL);
+		}
+		cache [sig->param_count] = start;
+		mono_mini_arch_unlock ();
+		return start;
+	}
+
+	return NULL;
+}
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+	return NULL;
+}
+
+gpointer
+mono_arch_get_this_arg_from_call (host_mgreg_t *regs, guint8 *code)
+{
+	g_assert(regs);
+	return (gpointer)regs [loongarch_a0];
+}
+
+/*
+ * Initialize the cpu to execute managed code.
+ */
+void
+mono_arch_cpu_init (void)
+{
+	ls_word_idx = 0;
+	ms_word_idx = 1;
+
+	ls_word_offset = ls_word_idx * 4;
+	ms_word_offset = ms_word_idx * 4;
+}
+
+/*
+ * Initialize architecture specific code.
+ */
+void
+mono_arch_init (void)
+{
+	if (!mono_aot_only)
+		bp_trampoline = mini_get_breakpoint_trampoline ();
+
+	mono_loongarch_gsharedvt_init ();
+}
+
+/*
+ * Cleanup architecture specific code.
+ */
+void
+mono_arch_cleanup (void)
+{
+}
+
+gboolean
+mono_arch_have_fast_tls (void)
+{
+	return TRUE;
+}
+
+/*
+ * This function returns the optimizations supported on this cpu.
+ */
+guint32
+mono_arch_cpu_optimizations (guint32 *exclude_mask)
+{//TODO: add loongarch features.
+	*exclude_mask = 0;
+	return 0;
+}
+
+/*
+* Set the float struct field info:
+* bit0: whether the first field is float;
+* bit1: whether the size of first field is 8-bytes;
+* bit2: whether the second field is exist;
+* bit3: whether the second field is float;
+* bit4: whether the size of second field is 8-bytes;
+* bit5-bit7 combined representation:
+* 001:all member transmit by fregs;
+* 002:all member transmit by regs;
+* 003:all member transmit by stack;
+* 004:one member transmit by reg another by stack;
+* 005:one member transmit by reg another by freg;
+*/
+static void
+set_struct_field_info (guint8 *field_info, int *index, int size, gboolean is_float)
+{
+	int i = *index;
+	switch (i) {
+	case 0:			//first field
+		if (is_float)
+			*field_info = (*field_info) | 0x01;
+		if (size == 8)
+			*field_info = (*field_info) | 0x02;
+		break;
+	case 1:			//second field
+		*field_info = (*field_info) | 0x04;
+		if (is_float)
+			*field_info = (*field_info) | 0x08;
+		if (size == 8)
+			*field_info = (*field_info) | 0x10;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+	(*index)++;
+}
+
+/*
+* Judge struct whether is a float struct, contains three situations:
+* first is only one float member (float/double);
+* second is only two float member (float and double have four combinations);
+* third is one float and one interger (float、double、int、long have eight combinations);
+*/
+static gboolean
+is_struct_float (MonoType *t, guint8 *field_info, int *index)
+{
+	MonoClass *klass;
+	gpointer iter;
+	MonoClassField *field;
+	MonoType *ftype;
+
+	klass = mono_class_from_mono_type_internal (t);
+	iter = NULL;
+	while ((field = mono_class_get_fields_internal (klass, &iter))) {
+		if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
+			continue;
+		ftype = mono_field_get_type_internal (field);
+		ftype = mini_get_underlying_type (ftype);
+
+		if (MONO_TYPE_ISSTRUCT (ftype)) {
+			if (!is_struct_float (ftype, field_info, index)) {
+				if ((*index == 1) || (*index == 0)) //when Recursive call this is can prevent (((*field_info) & 0x5) == 0) or null struct return false
+					continue;
+				return FALSE;
+			}
+			continue;
+		} else {
+			if ((*index) >= 2)
+				return FALSE;
+			switch (ftype->type) {
+			case MONO_TYPE_I1:
+			case MONO_TYPE_U1:
+			case MONO_TYPE_I2:
+			case MONO_TYPE_U2:
+			case MONO_TYPE_I4:
+			case MONO_TYPE_U4:
+				set_struct_field_info (field_info, index, 4, FALSE);
+				break;
+			case MONO_TYPE_I:
+			case MONO_TYPE_U:
+			case MONO_TYPE_PTR:
+			case MONO_TYPE_FNPTR:
+			case MONO_TYPE_OBJECT:
+			case MONO_TYPE_U8:
+			case MONO_TYPE_I8:
+				set_struct_field_info (field_info, index, 8, FALSE);
+				break;
+			case MONO_TYPE_R8:
+				set_struct_field_info (field_info, index, 8, TRUE);
+				break;
+			case MONO_TYPE_R4:
+				set_struct_field_info (field_info, index, 4, TRUE);
+				break;
+			case MONO_TYPE_GENERICINST:
+				if (!mono_type_generic_inst_is_valuetype (ftype))
+					set_struct_field_info (field_info, index, 8, FALSE);
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		}
+	}
+	if ((*index) == 0 || (*index) > 2)
+		return FALSE;
+	if (((*field_info) & 0x5) == 0) //exclude only one interger field
+		return FALSE;
+	if (((*field_info) & 0x9) == 0) //exclude only two interger field
+		return FALSE;
+	return TRUE;
+}
+
+static void
+add_general (CallInfo *cinfo, ArgInfo *ainfo, int size, gboolean sign)
+{
+	if (cinfo->gr >= PARAM_REGS) {
+		size = ALIGN_TO (size, 8);
+		ainfo->storage = ArgOnStack;
+		cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, size);
+		ainfo->offset = cinfo->stack_usage;
+		ainfo->slot_size = size;
+		ainfo->sign = sign;
+		cinfo->stack_usage += size;
+	}
+	else {
+		ainfo->storage = ArgInIReg;
+		ainfo->reg = cinfo->gr;
+		cinfo->gr++;
+	}
+}
+
+/*
+   Interger standard for struct that size at 0-16 byte
+*/
+static void __attribute__((noinline))
+add_iter_struct (CallInfo *cinfo, ArgInfo *ainfo, int size, int align)
+{
+	int nregs;
+	size = ALIGN_TO (size, 8);
+	nregs = size / 8;
+	guint8 field_info = ainfo->field_info;
+	if (cinfo->gr >= PARAM_REGS) { //all in stack
+		cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, align);
+		ainfo->offset = cinfo->stack_usage;
+		ainfo->size = size;
+		cinfo->stack_usage += size;
+		field_info = field_info | 0x60;
+	} else if (cinfo->gr + nregs <= PARAM_REGS) { //all in reg
+		ainfo->reg = cinfo->gr;
+		ainfo->size = size;
+		cinfo->gr += nregs;
+		field_info = field_info | 0x40;
+	} else { //one in reg and one in stack
+		for (int i = 0; i < nregs; i++) {
+			add_general (cinfo, ainfo, 8, FALSE);
+		}
+		field_info = field_info | 0x80;
+		ainfo->size = size;
+	}
+	ainfo->storage = ArgStructByVal;
+	ainfo->field_info = field_info;
+}
+
+static void
+add_fpr (CallInfo *cinfo, ArgInfo *ainfo, int size)
+{
+	g_assert ((size == 4) || (size == 8));
+
+	if (cinfo->fr >= FP_PARAM_REGS) {
+		add_general (cinfo, ainfo, size, FALSE); //use interger standard
+		ainfo->fin_ireg = TRUE;
+		ainfo->size = size;
+		//ainfo->slot_size = size;
+	} else {
+		ainfo->size = size;
+		ainfo->storage = ArgInFReg;
+		ainfo->reg = cinfo->fr;
+		cinfo->fr ++;
+	}
+}
+
+static void
+add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t)
+{
+	int index, size, align_size, nregs;
+	guint32 align;
+	guint8 field_info;
+	size = mini_type_stack_size_full (t, &align, cinfo->pinvoke);
+	align_size = ALIGN_TO (size, 8);
+	if (align_size > 16) {
+		ainfo->storage = ArgStructByRef;
+		ainfo->size = size;
+		return;
+	}
+
+	nregs = align_size / 8;
+	index = 0;
+	field_info = 0;
+	if (is_struct_float (t, &field_info, &index)) {
+		/*
+		 * The struct might include nested float structs aligned at 8,
+		 * so need to keep track of the offsets of the individual fields.
+		 */
+		gboolean first_field = ((field_info & 0x1) != 0) ? 1 : 0;
+		gboolean second_field = ((field_info & 0x8) != 0) ? 1 : 0;
+
+		if (index == 1) { //only one float field
+			if (cinfo->fr < FP_PARAM_REGS) {
+				ainfo->storage = ArgStructByVal;
+				ainfo->freg = cinfo->fr;
+				cinfo->fr++;
+				ainfo->size = size;
+				field_info = field_info | 0x20;
+			} else { //use interger standard
+				add_iter_struct (cinfo, ainfo, size, align);
+			}
+		}
+		if ((index == 2) && first_field && second_field) { //only two float field
+			if (cinfo->fr + index <= FP_PARAM_REGS) {
+				ainfo->storage = ArgStructByVal;
+				ainfo->freg = cinfo->fr;
+				cinfo->fr +=  index;
+				ainfo->size = size;
+				field_info = field_info | 0x20;
+			} else { //use interger standard
+				add_iter_struct (cinfo, ainfo, size, align);
+			}
+		}
+		if ((index == 2) && ((first_field && !second_field) || (!first_field && second_field))) { //one float and one interger
+			if ((cinfo->fr < FP_PARAM_REGS) && (cinfo->gr < PARAM_REGS)) {
+				ainfo->storage = ArgStructByVal;
+				ainfo->freg = cinfo->fr;
+				ainfo->reg = cinfo->gr;
+				cinfo->fr++;
+				cinfo->gr++;
+				ainfo->size = size;
+				field_info = field_info | 0xa0;
+			} else { //use interger standard
+				add_iter_struct (cinfo, ainfo, size, align);
+			}
+		}
+		ainfo->field_info |= field_info;
+		return;
+	}
+	ainfo->field_info = 0;
+	add_iter_struct (cinfo, ainfo, size, align);
+}
+
+// Perform peephole opts which should/can be performed before local regalloc
+void
+mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	//NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_register_lowlevel_calls (void)
+{
+}
+
+static guint8*
+emit_thunk (guint8 *code, gconstpointer target)
+{
+		guint8 *p = code;
+		loongarch_pcaddi (code, loongarch_r21, 4);
+		loongarch_ldd (code, loongarch_r21, loongarch_r21, 0);
+		loongarch_jirl (code, 0, loongarch_r21, 0);
+		code += 4;
+		*(guint64*)code = (guint64)target;
+		code += sizeof (guint64);
+		mono_arch_flush_icache (p, code - p);
+		return code;
+}
+
+static gpointer
+create_thunk (MonoCompile *cfg, guchar *code, const guchar *target, int relocation)
+{
+		MonoJitInfo *ji;
+		MonoThunkJitInfo *info;
+		guint8 *thunks, *p;
+		int thunks_size;
+		guint8 *orig_target;
+		guint8 *target_thunk;
+		MonoJitMemoryManager* jit_mm;
+
+		if (cfg) {
+			/*
+			 * This can be called multiple times during JITting,
+			 * save the current position in cfg->arch to avoid
+			 * doing a O(n^2) search.
+			 */
+			if (!cfg->arch.thunks) {
+					cfg->arch.thunks = cfg->thunks;
+					cfg->arch.thunks_size = cfg->thunk_area;
+			}
+			thunks = cfg->arch.thunks;
+			thunks_size = cfg->arch.thunks_size;
+			if (!thunks_size) {
+					g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, mono_method_full_name (cfg->method, TRUE));
+					g_assert_not_reached ();
+			}
+
+			g_assert (*(guint32*)thunks == 0);
+			emit_thunk (thunks, target);
+
+			cfg->arch.thunks += THUNK_SIZE;
+			cfg->arch.thunks_size -= THUNK_SIZE;
+
+			return thunks;
+		 } else {
+			ji = mini_jit_info_table_find ((char*)code);
+			g_assert (ji);
+			info = mono_jit_info_get_thunk_info (ji);
+			g_assert (info);
+
+			thunks = (guint8*)ji->code_start + info->thunks_offset;
+			thunks_size = info->thunks_size;
+
+			orig_target = mono_arch_get_call_target (code + 4);
+
+			/* Arbitrary lock */
+			jit_mm = get_default_jit_mm ();
+
+			jit_mm_lock (jit_mm);
+
+			target_thunk = NULL;
+			if (orig_target >= thunks && orig_target < thunks + thunks_size) {
+			    /* The call already points to a thunk, because of trampolines etc. */
+					target_thunk = orig_target;
+			} else {
+					for (p = thunks; p < thunks + thunks_size; p += THUNK_SIZE) {
+						if (((guint32*)p) [0] == 0) {
+								/* Free entry */
+								target_thunk = p;
+								break;
+						} else if (((guint64*)p) [2] == (guint64)target) {
+								/* Thunk already points to target */
+								target_thunk = p;
+								break;
+						}
+					}
+			}
+
+			if (!target_thunk) {
+				jit_mm_unlock (jit_mm);
+				g_print ("thunk failed %p->%p, thunk space=%d method %s, relocation %d", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE), relocation);
+				g_assert_not_reached ();
+            }
+
+			emit_thunk (target_thunk, target);
+
+			jit_mm_unlock (jit_mm);
+			return target_thunk;
+		}
+}
+
+static void
+loongarch64_patch_full (MonoCompile *cfg, guint8 *code, guint8 *target, int relocation)
+{
+	switch (relocation) {
+	case MONO_R_LOONGARCH64_B:
+	{
+		target = MINI_FTNPTR_TO_ADDR (target);
+		gint64 offsets = target - code;
+		if (IS_B_SI28_LOONGARCH (offsets)) {
+			loongarch_b (code, (offsets >> 2) & 0x3ffffff);
+		} else {
+			gpointer thunk;
+			thunk = create_thunk (cfg, code, target, relocation);
+			thunk = MINI_FTNPTR_TO_ADDR (thunk);
+			gint64 ofs = (guint8*)thunk - code;
+			g_assert (IS_B_SI28_LOONGARCH (ofs));
+			loongarch_b (code, (ofs >> 0x2));
+		}
+		break;
+	}
+	case MONO_R_LOONGARCH64_BC: //BEQ、BNE、BLT、BGE、BLTU、BGEU
+	{
+		target = MINI_FTNPTR_TO_ADDR (target);
+		gint64 offsets = target - code;
+		if (IS_B_SI18_LOONGARCH (offsets)) {
+			*(guint32*)code = (*(guint32*)code) | ((offsets << 8) & 0x3fffc00);
+			code += 4;
+		} else {
+			g_assert_not_reached ();
+		}
+		break;
+	}
+	case MONO_R_LOONGARCH64_BZ:
+	{
+		target = MINI_FTNPTR_TO_ADDR (target);
+		gint64 offsets = target - code;
+		if (IS_B_SI23_LOONGARCH (offsets)) {
+			*(guint32*)code = (*(guint32*)code) | ((offsets << 8) & 0x3fffc00) | ((offsets >> 18) & 0x1f);
+			code += 4;
+		} else {
+			g_assert_not_reached ();
+		}
+		break;
+	}
+	case MONO_R_LOONGARCH64_J:
+	{
+		guint64 imm = (guint64)target;
+		int dreg;
+		dreg = (*(guint32*)code) & 0x1f;
+		loongarch_lu12iw (code, dreg, (imm >> 12) & 0xfffff);
+		loongarch_lu32id (code, dreg, (imm >> 32) & 0xfffff);
+		loongarch_lu52id (code, dreg, dreg, (imm >> 52) & 0xfff);
+		loongarch_ori (code, dreg, dreg, imm & 0xfff);
+		break;
+	}
+	case MONO_R_LOONGARCH64_BL:
+	{
+		target = MINI_FTNPTR_TO_ADDR (target);
+		gint64 offsets = target - code;
+		if (IS_B_SI28_LOONGARCH (offsets)) {
+			loongarch_bl (code, offsets >> 0x2);
+		} else {
+			gpointer thunk;
+			thunk = create_thunk (cfg, code, target, relocation);
+			thunk = MINI_FTNPTR_TO_ADDR (thunk);
+			gint64 ofs = (guint8*)thunk - code;
+			g_assert (IS_B_SI28_LOONGARCH (ofs));
+			loongarch_bl (code, (ofs >> 0x2));
+		}
+	}
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+void
+mono_loongarch64_patch (guint8 *code, guint8 *target, int relocation)
+{
+	loongarch64_patch_full (NULL, code, target, relocation);
+}
+
+void
+mono_arch_patch_code_new (MonoCompile *cfg, guint8 *code, MonoJumpInfo *ji, gpointer target)
+{
+	guint8 *ip;
+
+	ip = ji->ip.i + code;
+
+	switch (ji->type) {
+	case MONO_PATCH_INFO_METHOD_JUMP:
+		/* ji->relocation is not set by the caller */
+		loongarch64_patch_full (cfg, ip, (guint8*)target, MONO_R_LOONGARCH64_B);
+		mono_arch_flush_icache (ip, 8);
+		break;
+	default:
+		loongarch64_patch_full (cfg, ip, (guint8*)target, ji->relocation);
+		break;
+	case MONO_PATCH_INFO_NONE:
+		break;
+	}
+}
+
+void
+mono_arch_finish_init (void)
+{
+}
+
+guint8*
+mono_loongarch_emit_imm64 (guint8 *code, int dreg, gint64 imm)
+{
+	if (imm >= -0x800 && imm <= 0x7ff) {
+		loongarch_addid (code, dreg, loongarch_zero, imm & 0xfff);
+	} else if (imm >= -0x80000000L && imm <= 0x7fffffffL) {
+		loongarch_lu12iw (code, dreg, (imm >> 12) & 0xfffff);
+		loongarch_ori (code, dreg, dreg, imm & 0xfff);
+	} else if (imm >= -0x8000000000000L && imm <= 0x7ffffffffffffL) {
+		loongarch_lu12iw (code, dreg, (imm >> 12) & 0xfffff);
+		loongarch_lu32id (code, dreg, (imm >> 32) & 0xfffff);
+		loongarch_ori (code, dreg, dreg, imm & 0xfff);
+	} else {
+		loongarch_lu12iw (code, dreg, (imm >> 12) & 0xfffff);
+		loongarch_lu32id (code, dreg, (imm >> 32) & 0xfffff);
+		loongarch_lu52id (code, dreg, dreg, (imm >> 52) & 0xfff);
+		loongarch_ori (code, dreg, dreg, imm & 0xfff);
+	}
+	return code;
+}
+
+guint8*
+mono_loongarch_emit_jirl (guint8 *code, int reg)
+{
+	loongarch_jirl (code, 0, reg, 0);
+	return code;
+}
+
+void
+mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
+{
+	int this_dreg = loongarch_a0;
+
+	if (vt_reg != -1)
+		this_dreg = loongarch_a1;
+
+	/* add the this argument */
+	if (this_reg != -1) {
+		MonoInst *this_ins;
+		MONO_INST_NEW (cfg, this_ins, OP_MOVE);
+		this_ins->type = this_type;
+		this_ins->sreg1 = this_reg;
+		this_ins->dreg = mono_alloc_ireg (cfg);
+		mono_bblock_add_inst (cfg->cbb, this_ins);
+		mono_call_inst_add_outarg_reg (cfg, inst, this_ins->dreg, this_dreg, FALSE);
+	}
+
+	if (vt_reg != -1) {
+		MonoInst *vtarg;
+		MONO_INST_NEW (cfg, vtarg, OP_MOVE);
+		vtarg->type = STACK_MP;
+		vtarg->sreg1 = vt_reg;
+		vtarg->dreg = mono_alloc_ireg (cfg);
+		mono_bblock_add_inst (cfg->cbb, vtarg);
+		mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, loongarch_a0, FALSE);
+	}
+}
+
+MonoInst*
+mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+	MonoInst *ins = NULL;
+
+	return ins;
+}
+
+host_mgreg_t
+mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
+{
+	return ctx->regs [reg];
+}
+
+host_mgreg_t*
+mono_arch_context_get_int_reg_address (MonoContext *ctx, int reg)
+{
+	return &ctx->regs [reg];
+}
+
+void
+mono_arch_context_set_int_reg (MonoContext *ctx, int reg, host_mgreg_t val)
+{
+	ctx->regs [reg] = val;
+}
+
+MonoMethod*
+mono_arch_find_imt_method (host_mgreg_t *regs, guint8 *code)
+{
+	return (MonoMethod*) regs [MONO_ARCH_IMT_REG];
+}
+
+MonoVTable*
+mono_arch_find_static_call_vtable (host_mgreg_t *regs, guint8 *code)
+{
+	return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
+}
+
+#ifndef DISABLE_JIT
+
+static __attribute__ ((__warn_unused_result__)) guint8*
+emit_ldd (guint8 *code, int rt, int rn, int imm)
+{
+	if (loongarch_is_imm12 (imm)) {
+		loongarch_ldd (code, rt, rn, (imm & 0xfff));
+	} else {
+		g_assert (rn != loongarch_r21);
+		code = mono_loongarch_emit_imm64 (code, loongarch_r21, imm);
+		loongarch_ldxd (code, rt, rn, loongarch_r21);
+	}
+	return code;
+}
+
+static __attribute__ ((__warn_unused_result__)) guint8*
+emit_std (guint8 *code, int rt, int rn, int imm)
+{
+	if (loongarch_is_imm12 (imm)) {
+		loongarch_std (code, rt, rn, (imm & 0xfff));
+	} else {
+		g_assert (rn != loongarch_r21);
+		code = mono_loongarch_emit_imm64 (code, loongarch_r21, imm);
+		loongarch_stxd (code, rt, rn, loongarch_r21);
+	}
+	return code;
+}
+
+static guint8*
+emit_aotconst (MonoCompile *cfg, guint8 *code, int dreg, guint32 patch_type, gconstpointer data)
+{
+	mono_add_patch_info (cfg, code - cfg->native_code, (MonoJumpInfoType)patch_type, data);
+	/* See arch_emit_got_access () in aot-compiler.c */
+	loongarch_ldd (code, dreg, dreg, 0);
+	loongarch_nop (code);
+	loongarch_nop (code);
+	loongarch_nop (code);
+	return NULL;
+}
+
+void
+mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins)
+{
+	int tmp1 = -1;
+	int tmp2 = -1;
+	int tmp3 = -1;
+	int tmp4 = -1;
+	MonoInst* inst_tmp1 = NULL;
+	MonoInst* inst_tmp2 = NULL;
+	MonoInst* inst_tmp3 = NULL;
+	MonoInst* inst_tmp4 = NULL;
+	MonoBasicBlock *target_bb = NULL;
+	MonoBasicBlock *pos_ov = NULL;
+
+	switch (ins->opcode) {
+	case OP_IADD_OVF:
+		tmp1 = mono_alloc_ireg (cfg);
+		tmp2 = mono_alloc_ireg (cfg);
+		tmp3 = mono_alloc_ireg (cfg);
+		tmp4 = mono_alloc_ireg (cfg);
+		NEW_BBLOCK (cfg, target_bb);
+		NEW_BBLOCK (cfg, pos_ov);
+		//first,judge whether the two sreg signs are same (sreg1 > 0 && sreg2 > 0) || (sreg1 < 0 && sreg2 < 0)
+		ins->opcode = OP_IADD;
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp1, OP_ISHL_IMM, tmp1, ins->sreg1, 0x0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp2, OP_LA_SLTI, tmp2, tmp1, 0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp3, OP_ISHL_IMM, tmp3, ins->sreg2, 0x0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp4, OP_LA_SLTI, tmp4, tmp3, 0);
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, tmp2, tmp4);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBNE_UN, target_bb);
+
+		//second,judge whether the sreg1 is a positive number
+		MONO_EMIT_NEW_ICOMPARE_IMM (cfg, tmp2, 0);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, pos_ov);
+
+		//start handler sreg1 < 0,sreg < 0
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE, target_bb);
+
+		//start handler sreg1 > 0,sreg2 > 0
+		MONO_START_BB (cfg, pos_ov);
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
+
+		//skip overflow
+		MONO_START_BB (cfg, target_bb);
+		break;
+	case OP_IADD_OVF_UN:
+		ins->opcode = OP_IADD;
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT_UN, "OverflowException");
+		break;
+	case OP_ISUB_OVF:
+		tmp1 = mono_alloc_ireg (cfg);
+		tmp2 = mono_alloc_ireg (cfg);
+		tmp3 = mono_alloc_ireg (cfg);
+		tmp4 = mono_alloc_ireg (cfg);
+		NEW_BBLOCK (cfg, target_bb);
+		NEW_BBLOCK (cfg, pos_ov);
+		//first,judge whether the two sreg signs are different
+		ins->opcode = OP_ISUB;
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp1, OP_ISHL_IMM, tmp1, ins->sreg1, 0x0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp2, OP_LA_SLTI, tmp2, tmp1, 0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp3, OP_ISHL_IMM, tmp3, ins->sreg2, 0x0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp4, OP_LA_SLTI, tmp4, tmp3, 0);
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, tmp2, tmp4);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBEQ, target_bb);
+
+		//second,judge whether the sreg1 is a positive number
+		MONO_EMIT_NEW_LCOMPARE_IMM (cfg, tmp2, 0);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, pos_ov);
+
+		//start handler sreg1 < 0,sreg2 > 0
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE, target_bb);
+
+		//start handler sreg1 > 0,sreg2 < 0
+		MONO_START_BB (cfg, pos_ov);
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
+
+		//skip overflow
+		MONO_START_BB (cfg, target_bb);
+		break;
+	case OP_ISUB_OVF_UN:
+		ins->opcode = OP_ISUB;
+		MONO_EMIT_NEW_BIALU (cfg, OP_ICOMPARE, -1, ins->sreg1, ins->sreg2);
+		MONO_EMIT_NEW_COND_EXC (cfg, ILT_UN, "OverflowException");
+		break;
+	case OP_LADD_OVF:
+		tmp1 = mono_alloc_ireg (cfg);
+		tmp2 = mono_alloc_ireg (cfg);
+		NEW_BBLOCK (cfg, target_bb);
+		NEW_BBLOCK (cfg, pos_ov);
+		if (COMPILE_LLVM (cfg))
+			break;
+		//first,judge whether the two sreg signs are same (sreg1 > 0 && sreg2 > 0) || (sreg1 < 0 && sreg 2 < 0)
+		ins->opcode = OP_LADD;
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp1, OP_LA_SLTI, tmp1, ins->sreg1, 0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp2, OP_LA_SLTI, tmp2, ins->sreg2, 0);
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, tmp1, tmp2);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBNE_UN, target_bb);
+
+		//second,judge whether the sreg1 is a positive number
+		MONO_EMIT_NEW_LCOMPARE_IMM (cfg, tmp1, 0);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, pos_ov);
+
+		//start handler sreg1 < 0,sreg < 0
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBGE, target_bb);
+
+		//start handler sreg1 > 0,sreg2 < 0
+		MONO_START_BB (cfg, pos_ov);
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
+
+		//skip overflow
+		MONO_START_BB (cfg, target_bb);
+		break;
+	case OP_LADD_OVF_UN:
+		if (COMPILE_LLVM (cfg))
+			break;
+		ins->opcode = OP_LADD;
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "OverflowException");
+		break;
+	case OP_LSUB_OVF:
+		tmp1 = mono_alloc_ireg (cfg);
+		tmp2 = mono_alloc_ireg (cfg);
+		NEW_BBLOCK (cfg, target_bb);
+		NEW_BBLOCK (cfg, pos_ov);
+		if (COMPILE_LLVM (cfg))
+			break;
+		//first,judge whether the two sreg signs are different
+		ins->opcode = OP_LSUB;
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp1, OP_LA_SLTI, tmp1, ins->sreg1, 0);
+		EMIT_NEW_BIALU_IMM (cfg, inst_tmp2, OP_LA_SLTI, tmp2, ins->sreg2, 0);
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, tmp1, tmp2);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBEQ, target_bb);
+
+		//second,judge whether the sreg1 is a positive number
+		MONO_EMIT_NEW_LCOMPARE_IMM (cfg, tmp1, 0);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, pos_ov);
+
+		//start handler sreg1 < 0,sreg > 0
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->sreg1, ins->dreg);
+		MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_LBGE, target_bb);
+
+		//start handler sreg1 > 0,sreg2 < 0
+		MONO_START_BB (cfg, pos_ov);
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->dreg, ins->sreg1);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
+
+		//skip overflow
+		MONO_START_BB (cfg, target_bb);
+		break;
+	case OP_LSUB_OVF_UN:
+		if (COMPILE_LLVM (cfg))
+			break;
+		ins->opcode = OP_LSUB;
+		MONO_EMIT_NEW_BIALU (cfg, OP_LCOMPARE, -1, ins->sreg1, ins->sreg2);
+		MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "OverflowException");
+		break;
+	}
+}
+
+/*
+ * Emits like:
+ * - ori sp, fp, 0
+ * - ldd fp, sp, 0
+ * - ldd ra, sp, 8
+ * - update sp.
+ * maybe clobbers loongarch_r21.
+ */
+__attribute__ ((__warn_unused_result__)) guint8*
+mono_loongarch64_emit_destroy_frame (guint8 *code, int stack_offset)
+{
+	loongarch_ori (code, loongarch_sp, loongarch_fp, 0);
+
+	loongarch_ldd (code, loongarch_fp, loongarch_sp, 0);
+	loongarch_ldd (code, loongarch_ra, loongarch_sp, 8);
+
+	if (loongarch_is_imm12 (stack_offset)) {
+		loongarch_addid (code, loongarch_sp, loongarch_sp, stack_offset);
+	} else {
+		loongarch_lu12iw (code, loongarch_r21, (stack_offset >> 12) & 0xfffff);
+		loongarch_ori (code, loongarch_r21, loongarch_r21, stack_offset & 0xfff);
+		loongarch_addd (code, loongarch_sp, loongarch_sp, loongarch_r21);
+	}
+	return code;
+}
+
+gboolean
+mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode)
+{
+	NOT_IMPLEMENTED;
+	return FALSE;
+}
+
+GList *
+mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
+{
+	GList *vars = NULL;
+	int i;
+
+	for (i = 0; i < cfg->num_varinfo; i++) {
+		MonoInst *ins = cfg->varinfo [i];
+		MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+		/* unused vars */
+		if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
+			continue;
+
+		if ((ins->flags & (MONO_INST_IS_DEAD|MONO_INST_VOLATILE|MONO_INST_INDIRECT)) ||
+		    (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
+			continue;
+
+		/* we can only allocate 32 bit values */
+		if (mono_is_regsize_var (ins->inst_vtype)) {
+			g_assert (MONO_VARINFO (cfg, i)->reg == -1);
+			g_assert (i == vmv->idx);
+			vars = g_list_prepend (vars, vmv);
+		}
+	}
+
+	vars = mono_varlist_sort (cfg, vars, 0);
+
+	return vars;
+}
+
+GList *
+mono_arch_get_global_int_regs (MonoCompile *cfg)
+{
+	GList *regs = NULL;
+
+	regs = g_list_prepend (regs, (gpointer)loongarch_s0);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s1);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s2);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s3);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s4);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s5);
+	regs = g_list_prepend (regs, (gpointer)loongarch_s6);
+	/* s8 is reserved for cfg->arch.args_reg */
+	/* s7 is reserved for the imt argument */
+	return regs;
+}
+
+/*
+ * mono_arch_regalloc_cost:
+ *
+ * Return the cost, in number of memory references, of the action of
+ * allocating the variable VMV into a register during global register
+ * allocation.
+ */
+guint32
+mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
+{
+	MonoInst *ins = cfg->varinfo [vmv->idx];
+	if (ins->opcode == OP_ARG)
+		return 1;
+	else
+		return 2;
+}
+
+static void
+add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t)
+{
+	MonoType *ptype;
+
+	ptype = mini_get_underlying_type (t);
+	switch (ptype->type) {
+	case MONO_TYPE_I1:
+		add_general (cinfo, ainfo, 1, TRUE);
+		break;
+	case MONO_TYPE_U1:
+		add_general (cinfo, ainfo, 1, FALSE);
+		break;
+	case MONO_TYPE_I2:
+		add_general (cinfo, ainfo, 2, TRUE);
+		break;
+	case MONO_TYPE_U2:
+		add_general (cinfo, ainfo, 2, FALSE);
+		break;
+#ifdef MONO_ARCH_ILP32
+	case MONO_TYPE_I:
+#endif
+	case MONO_TYPE_I4:
+		add_general (cinfo, ainfo, 4, TRUE);
+		break;
+#ifdef MONO_ARCH_ILP32
+	case MONO_TYPE_U:
+	case MONO_TYPE_PTR:
+	case MONO_TYPE_FNPTR:
+	case MONO_TYPE_OBJECT:
+#endif
+	case MONO_TYPE_U4:
+		add_general (cinfo, ainfo, 4, FALSE);
+		break;
+#ifndef MONO_ARCH_ILP32
+	case MONO_TYPE_I:
+	case MONO_TYPE_U:
+	case MONO_TYPE_PTR:
+	case MONO_TYPE_FNPTR:
+	case MONO_TYPE_OBJECT:
+#endif
+	case MONO_TYPE_U8:
+	case MONO_TYPE_I8:
+		add_general (cinfo, ainfo, 8, FALSE);
+		break;
+	case MONO_TYPE_R8:
+		add_fpr (cinfo, ainfo, 8);
+		break;
+	case MONO_TYPE_R4:
+		add_fpr (cinfo, ainfo, 4);
+		break;
+	case MONO_TYPE_VALUETYPE:
+	case MONO_TYPE_TYPEDBYREF:
+		add_valuetype (cinfo, ainfo, ptype);
+		break;
+	case MONO_TYPE_VOID:
+		ainfo->storage = ArgNone;
+		break;
+	case MONO_TYPE_GENERICINST:
+		if (!mono_type_generic_inst_is_valuetype (ptype)) {
+			add_general (cinfo, ainfo, 8, FALSE);
+		} else if (mini_is_gsharedvt_variable_type (ptype)) {
+			ainfo->storage = ArgStructByRef;
+		} else {
+			add_valuetype (cinfo, ainfo, ptype);
+		}
+		break;
+	case MONO_TYPE_VAR:
+	case MONO_TYPE_MVAR:
+		g_assert (mini_is_gsharedvt_type (ptype));
+		ainfo->storage = ArgStructByRef;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+static int
+call_info_size (MonoMethodSignature *sig)
+{
+	int n = sig->hasthis + sig->param_count;
+
+	return sizeof (CallInfo) + (sizeof (ArgInfo) * n);
+}
+
+/*
+ * get_call_info:
+ *
+ *  Obtain information about a call according to the calling convention.
+ */
+static CallInfo*
+get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
+{
+	int n = sig->hasthis + sig->param_count;
+	int pstart, i;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+
+	DEBUG(printf("params: %d\n", sig->param_count));
+	if (mp)
+		cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+	else
+		cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+
+	cinfo->nargs = n;
+	cinfo->pinvoke = sig->pinvoke;
+	/* Reset state */
+	cinfo->gr = LOONGARCH_FIRST_ARG_REG;
+	cinfo->fr = LOONGARCH_FIRST_FPARG_REG;
+	cinfo->stack_usage = 0;
+
+	/* return value */
+	add_param (cinfo, &cinfo->ret, sig->ret);
+	if (cinfo->ret.storage == ArgStructByRef) {
+		if (sig->pinvoke) {
+			cinfo->ret.reg = loongarch_a0;
+			cinfo->gr ++;
+		} else {
+			cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, 8);
+			cinfo->ret.offset = cinfo->stack_usage;
+			cinfo->stack_usage += 8;
+		}
+	} else {
+		cinfo->gr = LOONGARCH_FIRST_ARG_REG;
+		cinfo->fr = LOONGARCH_FIRST_FPARG_REG;
+	}
+
+	/* Parameters */
+	if (sig->hasthis)
+		add_general (cinfo, cinfo->args + 0, 8, FALSE);
+
+	pstart = 0;
+	for (i = pstart; i < sig->param_count; ++i) {
+		//TODO: should confirm  MONO_CALL_VARARG.
+		if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+			/* Prevent implicit arguments and sig_cookie from being passed in registers
+			   Emit the signature cookie just before the implicit arguments */
+			cinfo->gr = LOONGARCH_LAST_ARG_REG + 1;
+			cinfo->fr = LOONGARCH_LAST_FPARG_REG + 1;
+			cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, 8);
+			cinfo->sig_cookie.storage = ArgOnStack;
+			cinfo->sig_cookie.offset = cinfo->stack_usage;
+			cinfo->sig_cookie.slot_size = 8;
+			cinfo->stack_usage += 8;
+		}
+		DEBUG(printf("param %d: ", i));
+
+		ainfo = cinfo->args + sig->hasthis + i;
+		add_param (cinfo, ainfo, sig->params [i]);
+		if (ainfo->storage == ArgStructByRef) {
+		/* Pass the argument address in the next register */
+			if (cinfo->gr >= PARAM_REGS) {
+				ainfo->field_info = 0x60; //through stack
+				cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, 8);
+				ainfo->offset = cinfo->stack_usage;
+				cinfo->stack_usage += 8;
+			} else {
+				ainfo->field_info = 0x0;
+				ainfo->reg = cinfo->gr;
+				cinfo->gr ++;
+			}
+		}
+	}
+
+	/* Handle the case where there are no implicit arguments */
+	if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+		/* Prevent implicit arguments and sig_cookie from
+		   being passed in registers */
+		/* Emit the signature cookie just before the implicit arguments */
+		cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, 8);
+		cinfo->sig_cookie.storage = ArgOnStack;
+		cinfo->sig_cookie.offset = cinfo->stack_usage;
+		cinfo->sig_cookie.slot_size = 8;
+		//cinfo->sig_cookie.sign = FALSE;
+		cinfo->stack_usage += 8;
+	}
+
+	/* align stack size to 16 */
+	cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
+
+	return cinfo;
+}
+
+static int
+arg_need_temp (ArgInfo *ainfo)
+{
+	guint8 field_info = ainfo->field_info;
+	int stor_type = (field_info >> 5) & 0x7;
+	if (ainfo->storage == ArgStructByVal && (stor_type == 0x4 || stor_type == 0x5))
+		return ainfo->size;
+	return 0;
+}
+
+static gpointer
+arg_get_storage (CallContext *ccontext, ArgInfo *ainfo)
+{
+		switch (ainfo->storage) {
+		case ArgInIReg:
+			return &ccontext->gregs [ainfo->reg];
+		case ArgInFReg:
+			return &ccontext->fregs [ainfo->freg];
+		case ArgOnStack:
+			return ccontext->stack + ainfo->offset;
+		case ArgStructByRef:
+			if (ainfo->field_info == 0x60) {
+				return ccontext->stack + ainfo->offset;
+			} else {
+				return (gpointer) ccontext->gregs [ainfo->reg];
+			}
+		case ArgStructByVal: {
+			guint8 field_info;
+			field_info = ainfo->field_info;
+			int stor_type = (field_info >> 5) & 0x7;
+			switch (stor_type) {
+			case 0x1: //all store in the freg
+				return &ccontext->fregs [ainfo->freg];
+				break;
+			case 0x2: //all store in the reg
+				return &ccontext->gregs [ainfo->reg];
+				break;
+			case 0x3: //all store in stack
+				return ccontext->stack + ainfo->offset;
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		}
+		default:
+			g_error ("Arg storage type not yet supported");
+		}
+}
+
+static void
+arg_get_val (CallContext *ccontext, ArgInfo *ainfo, gpointer dest)
+{
+	g_assert (arg_need_temp (ainfo));
+
+	guint8 field_info = ainfo->field_info;
+	int stor_type = (field_info >> 5) & 0x7;
+	int fp1_size = (field_info & 0x2) ? 8 : 4;
+	int fp2_size = (field_info & 0x10) ? 8 : 4;
+	if (stor_type == 5) {
+		// one store in reg another in freg
+		if (field_info & 0x1) {
+			if (fp1_size == 4)
+				*(float*)dest = *(float*)&ccontext->fregs [ainfo->freg];
+			else
+				*(double*)dest = *(double*)&ccontext->fregs [ainfo->freg];
+			if (fp2_size == 4) {
+				dest = (gpointer)((long)dest + fp1_size);
+				*(int*)dest = *(int*)&ccontext->gregs [ainfo->reg];
+			} else {
+				dest = (gpointer)((long)dest + 8);
+				*(long*)dest = *(long*)&ccontext->gregs [ainfo->reg];
+			}
+		} else {
+			if (fp1_size == 4)
+				*(int*)dest = *(int*)&ccontext->gregs [ainfo->reg];
+			else
+				*(long*)dest = *(long*)&ccontext->gregs [ainfo->reg];
+			if (fp2_size == 4) {
+				dest = (gpointer)((long)dest + fp1_size);
+				*(float*)dest = *(float*)&ccontext->fregs [ainfo->freg];
+			} else {
+				dest = (gpointer)((long)dest + 8);
+				*(double*)dest = *(double*)&ccontext->fregs [ainfo->freg];
+			}
+		}
+	} else {
+		g_assert_not_reached ();
+	}
+}
+
+static void
+arg_set_val (CallContext *ccontext, ArgInfo *ainfo, gpointer src)
+{
+	g_assert (arg_need_temp (ainfo));
+
+	guint8 field_info = ainfo->field_info;
+	int stor_type = (field_info >> 5) & 0x7;
+	int fp1_size = (field_info & 0x2) ? 8 : 4;
+	int fp2_size = (field_info & 0x10) ? 8 : 4;
+	switch (stor_type) {
+		case 0x4: //one store in reg another in stack
+			*(long *)&ccontext->gregs [ainfo->reg] = *(long *)src;
+			src = (gpointer)((long)src + 8);
+			*(gsize *)(ccontext->stack + ainfo->offset) = *(gsize *)src;
+			break;
+		case 0x5: //one store in reg another in freg
+			if (field_info & 0x1) {
+				if (fp1_size == 4)
+					*(float*)&ccontext->fregs [ainfo->freg] = *(float*)src;
+				else
+					*(double*)&ccontext->fregs [ainfo->freg] = *(double*)src;
+				if (fp2_size == 4) {
+					src = (gpointer)((long)src + fp1_size);
+					*(int*)&ccontext->gregs [ainfo->reg] = *(int*)src;
+				} else {
+					src = (gpointer)((long)src + 8);
+					*(long*)&ccontext->gregs [ainfo->reg] = *(long*)src;
+				}
+			} else {
+				if (fp1_size == 4)
+					*(int*)&ccontext->gregs [ainfo->reg] = *(int*)src;
+				else
+					*(long*)&ccontext->gregs [ainfo->reg] = *(long*)src;
+				if (fp2_size == 4) {
+					src = (gpointer)((long)src + fp1_size);
+					*(float*)&ccontext->fregs [ainfo->freg] = *(float*)src;
+				} else {
+					src = (gpointer)((long)src + 8);
+					*(double*)&ccontext->fregs [ainfo->freg] = *(double*)src;
+				}
+			}
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+	}
+}
+
+gpointer
+mono_arch_get_interp_native_call_info (MonoMemoryManager *mem_manager, MonoMethodSignature *sig)
+{
+	CallInfo *cinfo = get_call_info (NULL, sig);
+	if (mem_manager) {
+		int size = call_info_size (sig);
+		gpointer res = mono_mem_manager_alloc0 (mem_manager, size);
+		memcpy (res, cinfo, size);
+		g_free (cinfo);
+		return res;
+	} else {
+		return cinfo;
+	}
+}
+
+void
+mono_arch_free_interp_native_call_info (gpointer call_info)
+{
+	/* Allocated by get_call_info () */
+	g_free (call_info);
+}
+
+/* Set arguments in the ccontext (for i2n entry) */
+void
+mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer call_info)
+{
+	const MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
+	CallInfo *cinfo = (CallInfo*)call_info;
+	gpointer storage;
+	ArgInfo *ainfo;
+
+	memset (ccontext, 0, sizeof (CallContext));
+
+	ccontext->stack_size = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
+	if (ccontext->stack_size)
+		ccontext->stack = (guint8*)g_calloc (1, ccontext->stack_size);
+
+	if (sig->ret->type != MONO_TYPE_VOID) {
+		ainfo = &cinfo->ret;
+		if (ainfo->storage == ArgStructByRef) {
+			storage = interp_cb->frame_arg_to_storage ((MonoInterpFrameHandle)frame, sig, -1);
+			*(gpointer*)(ccontext->stack + cinfo->ret.offset) = (gsize*)storage;
+		}
+	}
+
+	g_assert (!sig->hasthis);
+
+	for (int i = 0; i < sig->param_count; i++) {
+		ainfo = &cinfo->args [i];
+
+		if (ainfo->storage == ArgStructByRef) {
+			storage = arg_get_storage (ccontext, ainfo);
+			*(host_mgreg_t *)storage = (host_mgreg_t)interp_cb->frame_arg_to_storage ((MonoInterpFrameHandle)frame, sig, i);
+			continue;
+		}
+
+		int temp_size = arg_need_temp (ainfo);
+
+		if (temp_size)
+			storage = alloca (temp_size); // FIXME? alloca in a loop
+		else
+			storage = arg_get_storage (ccontext, ainfo);
+
+		interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, i, storage);
+		if (temp_size)
+			arg_set_val (ccontext, ainfo, storage);
+	}
+
+	g_free (cinfo);
+}
+
+/* Gets the return value from ccontext (for i2n exit) */
+void
+mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer call_info)
+{
+	const MonoEECallbacks *interp_cb;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	gpointer storage;
+
+	if (sig->ret->type == MONO_TYPE_VOID)
+		return;
+
+	interp_cb = mini_get_interp_callbacks ();
+	cinfo = (CallInfo*)call_info;
+	ainfo = &cinfo->ret;
+
+	if (ainfo->storage != ArgStructByRef) {
+		int temp_size = arg_need_temp (ainfo);
+
+		if (temp_size) {
+			storage = alloca (temp_size);
+			arg_get_val (ccontext, ainfo, storage);
+		} else {
+			storage = arg_get_storage (ccontext, ainfo);
+		}
+		interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, -1, storage);
+	}
+
+	g_free (cinfo);
+}
+
+void
+mono_arch_create_vars (MonoCompile *cfg)
+{
+	MonoMethodSignature *sig;
+	CallInfo *cinfo;
+
+	sig = mono_method_signature_internal (cfg->method);
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	if (cinfo->ret.storage == ArgStructByRef) {
+		cfg->vret_addr = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+		cfg->vret_addr->flags |= MONO_INST_VOLATILE;
+		if (G_UNLIKELY (cfg->verbose_level > 1)) {
+			printf ("vret_addr = ");
+			mono_print_ins (cfg->vret_addr);
+		}
+	}
+
+	if (cfg->gen_sdb_seq_points) {
+		MonoInst *ins;
+
+		if (cfg->compile_aot) {
+			ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+			ins->flags |= MONO_INST_VOLATILE;
+			cfg->arch.seq_point_info_var = ins;
+		}
+
+		ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+		ins->flags |= MONO_INST_VOLATILE;
+		cfg->arch.ss_tramp_var = ins;
+
+		ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+		ins->flags |= MONO_INST_VOLATILE;
+		cfg->arch.bp_tramp_var = ins;
+	}
+
+	if (cfg->method->save_lmf) {
+		cfg->create_lmf_var = TRUE;
+		cfg->lmf_ir = TRUE;
+	}
+}
+
+/*
+ * Set var information according to the calling convention. loongarch64 version.
+ * The locals var stuff should most likely be split in another method.
+ */
+void
+mono_arch_allocate_vars (MonoCompile *cfg)
+{
+	MonoMethodSignature *sig;
+	MonoInst *ins;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	int i, offset, size, align;
+	guint32 locals_stack_size, locals_stack_align;
+	gint32 *offsets;
+	MonoInst *vtaddr;
+	/*
+	 * Allocate arguments and locals to either register (OP_REGVAR) or to a stack slot (OP_REGOFFSET).
+	 * Compute cfg->stack_offset and update cfg->used_int_regs.
+	 */
+
+	sig = mono_method_signature_internal (cfg->method);
+
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	/*
+	 * The LoongArch64 ABI always uses a frame pointer.
+	 * Now the fp points to the bottom of the frame,
+	 * and stack slots are at positive offsets.
+	 * If some arguments are received on the stack, their offsets relative to fp can
+	 * not be computed right now because the stack frame might grow due to spilling
+	 * done by the local register allocator. To solve this, we reserve a register
+	 * which points to them.
+	 * The stack frame looks like this:
+	 * args_reg -> <bottom of parent frame>
+	 *             <locals etc>
+	 *       fp -> <saved fp+lr>
+	 *       sp -> <localloc/params area>
+	 */
+	cfg->frame_reg = loongarch_fp;
+	cfg->flags |= MONO_CFG_HAS_SPILLUP;
+
+	//offset = 0;
+	/* Saved fp+lr */
+	offset = 16;
+
+	if (cinfo->stack_usage) {
+		g_assert (!(cfg->used_int_regs & (1 << loongarch_s8)));
+		cfg->arch.args_reg = loongarch_s8;
+		cfg->used_int_regs |= 1 << loongarch_s8;
+	}
+
+	if (cfg->method->save_lmf) {
+		/* The LMF var is allocated normally */
+	} else {
+		/* Callee saved regs */
+		cfg->arch.saved_gregs_offset = offset;
+		for (i = loongarch_s0; i < 32; ++i)
+			if (cfg->used_int_regs & (1 << i))
+				offset += 8;
+	}
+
+	/* Return value */
+	switch (cinfo->ret.storage) {
+	case ArgStructByVal: {
+		/* Allocate a local to hold the result, the epilog will copy it to the correct place */
+		cfg->ret->opcode = OP_REGOFFSET;
+		cfg->ret->inst_basereg = cfg->frame_reg;
+		cfg->ret->inst_offset = offset;
+		offset += 16;
+	}
+		break;
+	case ArgInFReg:
+	case ArgInIReg: {
+		cfg->ret->opcode = OP_REGVAR;
+		cfg->ret->dreg = cinfo->ret.reg;
+	}
+		break;
+	case ArgStructByRef: {
+		cfg->vret_addr->opcode = OP_REGOFFSET;
+		cfg->vret_addr->inst_basereg = cfg->frame_reg;
+		cfg->vret_addr->inst_offset = offset;
+		offset += 8;
+		if (G_UNLIKELY (cfg->verbose_level > 1)) {
+			printf ("vret_addr =");
+			mono_print_ins (cfg->vret_addr);
+		}
+	}
+		break;
+	case ArgNone:
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	/* Arguments */
+	for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+		ainfo = cinfo->args + i;
+
+		ins = cfg->args [i];
+		if (ins->opcode == OP_REGVAR)
+			continue;
+
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+			// FIXME: Use nregs/size
+			/* These will be copied to the stack in the prolog */
+			ins->inst_offset = offset;
+			offset += 8;
+			break;
+		case ArgOnStack:
+			/* These are in the parent frame */
+			g_assert (cfg->arch.args_reg);
+			ins->inst_basereg = cfg->arch.args_reg;
+			ins->inst_offset = ainfo->offset;
+			break;
+		case ArgStructByVal:
+			if (((ainfo->field_info >> 5) & 0x7) == 3) {
+				g_assert (cfg->arch.args_reg);
+				ins->inst_basereg = cfg->arch.args_reg;
+				ins->inst_offset = ainfo->offset;
+			} else {
+				ins->opcode = OP_REGOFFSET;
+				ins->inst_basereg = cfg->frame_reg;
+				/* These arguments are saved to the stack in the prolog */
+				ins->inst_offset = offset;
+				if (cfg->verbose_level >= 2)
+					printf ("arg %d allocated to %s+0x%0x.\n", i, mono_arch_regname (ins->inst_basereg), (int)ins->inst_offset);
+				offset += 16;
+			}
+			break;
+		case ArgStructByRef:
+			 /* The vtype address is in a register, will be copied to the stack in the prolog */
+			MONO_INST_NEW (cfg, vtaddr, 0);
+			vtaddr->opcode = OP_REGOFFSET;
+			if (((ainfo->field_info >> 5) & 0x7) == 3) {
+				g_assert (cfg->arch.args_reg);
+				vtaddr->inst_basereg = cfg->arch.args_reg;
+				vtaddr->inst_offset = ainfo->offset;
+			} else {
+				vtaddr->inst_basereg = cfg->frame_reg;
+				vtaddr->inst_offset = offset;
+				offset += 8;
+			}
+			 /* Need an indirection */
+			ins->opcode = OP_VTARG_ADDR;
+			ins->inst_left = vtaddr;
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	/* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */
+	// FIXME: Allocate these to registers
+	ins = cfg->arch.seq_point_info_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+	ins = cfg->arch.ss_tramp_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+	ins = cfg->arch.bp_tramp_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+
+	/* Locals */
+	offsets = mono_allocate_stack_slots (cfg, FALSE, &locals_stack_size, &locals_stack_align);
+	if (locals_stack_align)
+		offset = ALIGN_TO (offset, locals_stack_align);
+
+	for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
+		if (offsets [i] != -1) {
+			ins = cfg->varinfo [i];
+			ins->opcode = OP_REGOFFSET;
+			ins->inst_basereg = cfg->frame_reg;
+			ins->inst_offset = offset + offsets [i];
+			//printf ("allocated local %d to ", i); mono_print_tree_nl (ins);
+		}
+	}
+	offset += locals_stack_size;
+
+	offset = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	cfg->stack_offset = offset;
+}
+
+#ifdef ENABLE_LLVM
+LLVMCallInfo*
+mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
+{
+	int i, n;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	LLVMCallInfo *linfo;
+
+	n = sig->param_count + sig->hasthis;
+
+	cinfo = get_call_info (cfg->mempool, sig);
+
+	linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
+
+	switch (cinfo->ret.storage) {
+	case ArgInIReg:
+	case ArgInFReg:
+	case ArgInFRegR4:
+	case ArgNone:
+		break;
+	case ArgVtypeByRef:
+		linfo->ret.storage = LLVMArgVtypeByRef;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	for (i = 0; i < n; ++i) {
+		LLVMArgInfo *lainfo = &linfo->args [i];
+
+		ainfo = cinfo->args + i;
+
+		lainfo->storage = LLVMArgNone;
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+		case ArgInFRegR4:
+		case ArgOnStack:
+		case ArgOnStackR4:
+		case ArgOnStackR8:
+			lainfo->storage = LLVMArgNormal;
+			break;
+		case ArgVtypeByRef:
+		case ArgVtypeByRefOnStack:
+			lainfo->storage = LLVMArgVtypeByRef;
+			break;
+		case ArgHFA: {
+			int j;
+
+			lainfo->storage = LLVMArgAsFpArgs;
+			lainfo->nslots = ainfo->nregs;
+			lainfo->esize = ainfo->esize;
+			for (j = 0; j < ainfo->nregs; ++j)
+				lainfo->pair_storage [j] = LLVMArgInFPReg;
+			break;
+		}
+		case ArgVtypeInIRegs:
+			lainfo->storage = LLVMArgAsIArgs;
+			lainfo->nslots = ainfo->nregs;
+			break;
+		case ArgVtypeOnStack:
+			if (ainfo->hfa) {
+				int j;
+				/* Same as above */
+				lainfo->storage = LLVMArgAsFpArgs;
+				lainfo->nslots = ainfo->nregs;
+				lainfo->esize = ainfo->esize;
+				for (j = 0; j < ainfo->nregs; ++j)
+					lainfo->pair_storage [j] = LLVMArgInFPReg;
+			} else {
+				lainfo->storage = LLVMArgAsIArgs;
+				lainfo->nslots = ainfo->size / 8;
+			}
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	return linfo;
+}
+#endif
+
+static void
+add_outarg_reg (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int reg, MonoInst *arg, gboolean isR4, gboolean fin_ireg)
+{
+	MonoInst *ins;
+
+	switch (storage) {
+	case ArgInIReg:
+		if (fin_ireg) {
+			if (isR4) {
+				MONO_INST_NEW (cfg, ins, OP_MOVE_F_TO_I4);
+			} else {
+				MONO_INST_NEW (cfg, ins, OP_MOVE_F_TO_I8);
+			}
+		} else {
+			MONO_INST_NEW (cfg, ins, OP_MOVE);
+		}
+		ins->dreg = mono_alloc_ireg_copy (cfg, arg->dreg);
+		ins->sreg1 = arg->dreg;
+		MONO_ADD_INS (cfg->cbb, ins);
+		mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE);
+		break;
+	case ArgInFReg:
+		if (isR4) {
+			if (cfg->r4fp)
+				MONO_INST_NEW (cfg, ins, OP_RMOVE);
+			else
+				MONO_INST_NEW (cfg, ins, OP_LA_SETFREG_R4);
+		} else {
+			MONO_INST_NEW (cfg, ins, OP_FMOVE);
+		}
+		ins->dreg = mono_alloc_freg (cfg);
+		ins->sreg1 = arg->dreg;
+		MONO_ADD_INS (cfg->cbb, ins);
+		mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+/* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
+ * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info
+ */
+
+/*
+ * take the arguments and generate the arch-specific
+ * instructions to properly call the function in call.
+ * This includes pushing, moving arguments to the right register
+ * etc.
+ * Issue: who does the spilling if needed, and when?
+ */
+static void
+emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
+{
+	MonoMethodSignature *tmp_sig;
+	MonoInst *sig_arg;
+
+	if (MONO_IS_TAILCALL_OPCODE (call))
+		NOT_IMPLEMENTED;
+
+	/* TODO: Add support for signature tokens to AOT */
+	cfg->disable_aot = TRUE;
+
+	/*
+	 * mono_ArgIterator_Setup assumes the signature cookie is
+	 * passed first and all the arguments which were before it are
+	 * passed on the stack after the signature. So compensate by
+	 * passing a different signature.
+	 */
+	tmp_sig = mono_metadata_signature_dup (call->signature);
+	tmp_sig->param_count -= call->signature->sentinelpos;
+	tmp_sig->sentinelpos = 0;
+	memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
+
+	MONO_INST_NEW (cfg, sig_arg, OP_I8CONST);
+	sig_arg->dreg = mono_alloc_ireg (cfg);
+	sig_arg->inst_p0 = tmp_sig;
+	MONO_ADD_INS (cfg->cbb, sig_arg);
+
+	MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, loongarch_sp, cinfo->sig_cookie.offset, sig_arg->dreg);
+}
+
+void
+mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
+{
+	MonoInst *arg, *vtarg;
+	MonoMethodSignature *sig;
+	ArgInfo *ainfo;
+	int i, n;
+	CallInfo *cinfo;
+
+	sig = call->signature;
+
+	cinfo = get_call_info (cfg->mempool, sig);
+
+	if (cinfo->ret.storage == ArgStructByVal && !MONO_IS_TAILCALL_OPCODE (call)) {
+		/*
+		 * The vtype is returned in registers, save the return area address in a local, and save the vtype into
+		 * the location pointed to by it after call in emit_move_return_value ().
+		 */
+		if (!cfg->arch.vret_addr_loc) {
+			cfg->arch.vret_addr_loc = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+			/* Prevent it from being register allocated or optimized away */
+			cfg->arch.vret_addr_loc->flags |= MONO_INST_VOLATILE;
+		}
+
+		MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->arch.vret_addr_loc->dreg, call->vret_var->dreg);
+	}
+
+	if (cinfo->ret.storage == ArgStructByRef) {
+		g_assert (call->vret_var == cfg->vret_addr || !MONO_IS_TAILCALL_OPCODE (call));
+		if (sig->pinvoke) {
+			/* Pass the vtype return address in R4 */
+			MONO_INST_NEW (cfg, vtarg, OP_MOVE);
+			vtarg->sreg1 = call->vret_var->dreg;
+			vtarg->dreg = mono_alloc_preg (cfg);
+			MONO_ADD_INS (cfg->cbb, vtarg);
+
+			mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
+		} else {
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, loongarch_sp, cinfo->ret.offset, call->vret_var->dreg);
+		}
+	}
+
+	n = cinfo->nargs;
+	for (i = 0; i < n; ++i) {
+		ainfo = cinfo->args + i;
+		arg = call->args [i];
+		gboolean isR4 = ainfo->size == 4 ? TRUE : FALSE;
+		gboolean fin_ireg = ainfo->fin_ireg;
+
+		if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+			/* Emit the signature cookie just before the implicit arguments */
+			emit_sig_cookie (cfg, call, cinfo);
+		}
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+			add_outarg_reg (cfg, call, ainfo->storage, ainfo->reg, arg, isR4, fin_ireg);
+			break;
+		case ArgOnStack:
+			switch (ainfo->slot_size) {
+			case 8:
+				if (fin_ireg)  //double pass from stack
+					MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				else
+					MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				break;
+			case 4:
+				if (fin_ireg)  //float pass from stack
+					MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				else
+					MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				break;
+			case 2:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				break;
+			case 1:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, loongarch_sp, ainfo->offset, arg->dreg);
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+			break;
+		case ArgStructByVal:
+		case ArgStructByRef: {
+			MonoInst *ins;
+			guint32 align;
+			guint32 size;
+
+			size = mono_class_value_size (arg->klass, &align);
+
+			MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
+			ins->sreg1 = arg->dreg;
+			ins->klass = arg->klass;
+			ins->backend.size = size;
+			ins->inst_p0 = call;
+			ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
+			memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
+			MONO_ADD_INS (cfg->cbb, ins);
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	/* Handle the case where there are no implicit arguments */
+	if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos))
+		emit_sig_cookie (cfg, call, cinfo);
+
+	call->call_info = cinfo;
+	call->stack_usage = cinfo->stack_usage;
+}
+
+void
+mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
+{
+	MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
+	ArgInfo *ainfo = (ArgInfo*)ins->inst_p1;
+	MonoInst *load;
+
+	switch (ainfo->storage) {
+	case ArgStructByVal: {
+		guint8 field_info = ainfo->field_info;
+		int second_exist = (field_info >> 2) & 0x1;
+		int stor_type = (field_info >> 5) & 0x7;
+		int fp1_size = (field_info & 0x2) ? 8 : 4;
+		int fp2_size = (field_info & 0x10) ? 8 : 4;
+		int offsets = 0;
+		gboolean isR4 = FALSE;
+		switch (stor_type) {
+		case 0x1: {//all store in the freg
+			if (fp1_size == 4) {
+				MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
+				isR4 = TRUE;
+			} else {
+				MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
+			}
+			load->dreg = mono_alloc_freg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = 0;
+			MONO_ADD_INS (cfg->cbb, load);
+			add_outarg_reg (cfg, call, ArgInFReg, ainfo->freg, load, isR4, FALSE);
+			if (second_exist) {
+				if (fp2_size == 4) {
+					offsets = fp1_size;
+					MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
+					isR4 = TRUE;
+				} else {
+					offsets = 8;
+					MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
+				}
+				load->dreg = mono_alloc_freg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = offsets;
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInFReg, ainfo->freg+1, load, isR4, FALSE);
+			}
+			break;
+		}
+		case 0x2: {//all store in the reg
+			MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+			load->dreg = mono_alloc_ireg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = 0;
+			MONO_ADD_INS (cfg->cbb, load);
+			add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, load, isR4, FALSE);
+			if (ainfo->size > 8) {
+				MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+				load->dreg = mono_alloc_ireg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = sizeof (target_mgreg_t);
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg + 1, load, isR4, FALSE);
+			}
+			break;
+		}
+		case 0x3: {//all store in stack
+			for (int i = 0; i < ainfo->size / 8; ++i) {
+				MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+				load->dreg = mono_alloc_ireg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = i * 8;
+				MONO_ADD_INS (cfg->cbb, load);
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, loongarch_sp, ainfo->offset + (i * 8), load->dreg);
+			}
+			break;
+		}
+		case 0x4: {//one store in reg another in stack
+			MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+			load->dreg = mono_alloc_ireg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = 0;
+			MONO_ADD_INS (cfg->cbb, load);
+			add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, load, isR4, FALSE);
+			MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+			load->dreg = mono_alloc_ireg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = 8;
+			MONO_ADD_INS (cfg->cbb, load);
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, loongarch_sp, ainfo->offset, load->dreg);
+			break;
+		}
+		case 0x5: {//one store in reg another in freg
+			if (field_info & 0x1) {
+				if (fp1_size == 4) {
+					MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
+					isR4 = TRUE;
+				} else {
+					MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
+				}
+				load->dreg = mono_alloc_freg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = 0;
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInFReg, ainfo->freg, load, isR4, FALSE);
+				if (fp2_size == 4) {
+					MONO_INST_NEW (cfg, load, OP_LOADI4_MEMBASE);
+					offsets = fp1_size;
+				} else {
+					MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+					offsets = 8;
+				}
+				load->dreg = mono_alloc_ireg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = offsets;
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, load, isR4, FALSE);
+			} else {
+				if (fp1_size == 4)
+					MONO_INST_NEW (cfg, load, OP_LOADI4_MEMBASE);
+				else
+					MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+				load->dreg = mono_alloc_ireg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = 0;
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, load, isR4, FALSE);
+				if (fp2_size == 4) {
+					MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
+					isR4 = TRUE;
+					offsets = fp1_size;
+				} else {
+					MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
+					offsets = 8;
+				}
+				load->dreg = mono_alloc_freg (cfg);
+				load->inst_basereg = src->dreg;
+				load->inst_offset = offsets;
+				MONO_ADD_INS (cfg->cbb, load);
+				add_outarg_reg (cfg, call, ArgInFReg, ainfo->freg, load, isR4, FALSE);
+			}
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+		break;
+	}
+	case ArgStructByRef: {
+		MonoInst *vtaddr, *load, *arg;
+		/* Make a copy of the argument */
+		vtaddr = mono_compile_create_var (cfg, m_class_get_byval_arg (ins->klass), OP_LOCAL);
+
+		MONO_INST_NEW (cfg, load, OP_LDADDR);
+		load->inst_p0 = vtaddr;
+		vtaddr->flags |= MONO_INST_INDIRECT;
+		load->type = STACK_MP;
+		load->klass = vtaddr->klass;
+		load->dreg = mono_alloc_ireg (cfg);
+		MONO_ADD_INS (cfg->cbb, load);
+		mini_emit_memcpy (cfg, load->dreg, 0, src->dreg, 0, ainfo->size, 8);
+		if (((ainfo->field_info >> 5) & 0x7) == 3) {
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, loongarch_sp, ainfo->offset, load->dreg);
+		} else {
+			MONO_INST_NEW (cfg, arg, OP_MOVE);
+			arg->dreg = mono_alloc_preg (cfg);
+			arg->sreg1 = load->dreg;
+			MONO_ADD_INS (cfg->cbb, arg);
+			add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, arg, FALSE, FALSE);
+		}
+		break;
+	}
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+}
+
+gboolean
+mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig, gboolean virtual_)
+{
+	g_assert (caller_sig);
+	g_assert (callee_sig);
+
+	CallInfo *caller_info = get_call_info (NULL, caller_sig);
+	CallInfo *callee_info = get_call_info (NULL, callee_sig);
+
+	gboolean res = IS_SUPPORTED_TAILCALL (callee_info->stack_usage <= caller_info->stack_usage)
+		  && IS_SUPPORTED_TAILCALL (caller_info->ret.storage == callee_info->ret.storage);
+
+	// FIXME Limit stack_usage to 1G. emit_ld / st has 32bit limits.
+	res &= IS_SUPPORTED_TAILCALL (callee_info->stack_usage < (1 << 30));
+	res &= IS_SUPPORTED_TAILCALL (caller_info->stack_usage < (1 << 30));
+
+	// valuetype parameters are the address of a local
+	const ArgInfo *ainfo;
+	ainfo = callee_info->args + callee_sig->hasthis;
+	for (int i = 0; res && i < callee_sig->param_count; ++i) {
+		res = IS_SUPPORTED_TAILCALL (ainfo [i].storage != ArgStructByRef);
+	}
+
+	g_free (caller_info);
+	g_free (callee_info);
+
+	return res;
+}
+
+void
+mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
+{
+	MonoMethodSignature *sig;
+	CallInfo *cinfo;
+
+	sig = mono_method_signature_internal (cfg->method);
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+		break;
+	case ArgInIReg:
+		MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
+		break;
+	case ArgInFReg:
+		if (mini_get_underlying_type (sig->ret)->type == MONO_TYPE_R4) {
+			if (cfg->r4fp)
+				MONO_EMIT_NEW_UNALU (cfg, OP_RMOVE, cfg->ret->dreg, val->dreg);
+			else
+				MONO_EMIT_NEW_UNALU (cfg, OP_LA_SETFREG_R4, cfg->ret->dreg, val->dreg);
+		} else {
+			MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
+		}
+			break;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+gboolean
+mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm)
+{
+	return (imm >= -((gint64)1 << 31) && imm <= (((gint64)1 << 31)-1));
+}
+
+void
+mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	MonoInst *ins, *n, *last_ins = NULL;
+	ins = bb->code;
+
+	MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
+		MonoInst *last_ins = mono_inst_prev (ins, FILTER_IL_SEQ_POINT);
+
+		switch (ins->opcode) {
+		case OP_MUL_IMM:
+			/* remove unnecessary multiplication with 1 */
+			if (ins->inst_imm == 1) {
+				if (ins->dreg != ins->sreg1) {
+					ins->opcode = OP_MOVE;
+				} else {
+					MONO_DELETE_INS (bb, ins);
+					continue;
+				}
+			} else {
+				int power2 = mono_is_power_of_two (ins->inst_imm);
+				if (power2 > 0) {
+					ins->opcode = OP_SHL_IMM;
+					ins->inst_imm = power2;
+				}
+			}
+			break;
+		case OP_LOAD_MEMBASE:
+		case OP_LOADI4_MEMBASE:
+			/*
+			 * OP_STORE_MEMBASE_REG reg, offset(basereg)
+			 * OP_LOAD_MEMBASE offset(basereg), reg
+			 */
+			if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
+					 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
+			    ins->inst_basereg == last_ins->inst_destbasereg &&
+			    ins->inst_offset == last_ins->inst_offset) {
+				if (ins->dreg == last_ins->sreg1) {
+					MONO_DELETE_INS (bb, ins);
+					continue;
+				} else {
+					//static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+					ins->opcode = OP_MOVE;
+					ins->sreg1 = last_ins->sreg1;
+				}
+				break;
+			}
+			/*
+			 * Note: reg1 must be different from the basereg in the second load
+			 * OP_LOAD_MEMBASE offset(basereg), reg1
+			 * OP_LOAD_MEMBASE offset(basereg), reg2
+			 * -->
+			 * OP_LOAD_MEMBASE offset(basereg), reg1
+			 * OP_MOVE reg1, reg2
+			 */
+			if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
+					   || last_ins->opcode == OP_LOAD_MEMBASE) &&
+			      ins->inst_basereg != last_ins->dreg &&
+			      ins->inst_basereg == last_ins->inst_basereg &&
+			      ins->inst_offset == last_ins->inst_offset) {
+
+				if (ins->dreg == last_ins->dreg) {
+					MONO_DELETE_INS (bb, ins);
+					continue;
+				} else {
+					ins->opcode = OP_MOVE;
+					ins->sreg1 = last_ins->dreg;
+				}
+
+				//g_assert_not_reached ();
+				break;
+			}
+			break;
+		case OP_LOADU1_MEMBASE:
+		case OP_LOADI1_MEMBASE:
+			if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
+					ins->inst_basereg == last_ins->inst_destbasereg &&
+					ins->inst_offset == last_ins->inst_offset) {
+				ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
+				ins->sreg1 = last_ins->sreg1;
+			}
+			break;
+		case OP_LOADU2_MEMBASE:
+		case OP_LOADI2_MEMBASE:
+			if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
+					ins->inst_basereg == last_ins->inst_destbasereg &&
+					ins->inst_offset == last_ins->inst_offset) {
+				ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
+				ins->sreg1 = last_ins->sreg1;
+			}
+			break;
+		case OP_ICONV_TO_I4:
+		case OP_ICONV_TO_U4:
+		case OP_MOVE:
+			ins->opcode = OP_MOVE;
+			/*
+			 * OP_MOVE reg, reg
+			 */
+			if (ins->dreg == ins->sreg1) {
+				MONO_DELETE_INS (bb, ins);
+				continue;
+			}
+			/*
+			 * OP_MOVE sreg, dreg
+			 * OP_MOVE dreg, sreg
+			 */
+			if (last_ins && last_ins->opcode == OP_MOVE &&
+			    ins->sreg1 == last_ins->dreg &&
+			    ins->dreg == last_ins->sreg1) {
+				MONO_DELETE_INS (bb, ins);
+				continue;
+			}
+			break;
+		}
+		last_ins = ins;
+		ins = ins->next;
+	}
+	bb->last_ins = last_ins;
+}
+
+void
+mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *ins)
+{
+}
+
+#define ADD_NEW_INS(cfg,dest,op) do { 				 \
+		MONO_INST_NEW ((cfg), (dest), (op)); 		\
+		mono_bblock_insert_before_ins (bb, ins, (dest)); \
+	} while (0)
+
+#define NEW_INS(cfg,after,dest,op) do {					\
+		MONO_INST_NEW((cfg), (dest), (op));			\
+		mono_bblock_insert_after_ins (bb, (after), (dest));	\
+	} while (0)
+
+/*
+ * Remove from the instruction list the instructions that can't be
+ * represented with very simple instructions with no register
+ * requirements.
+ *
+ * Converts complex opcodes into simpler ones so that each IR instruction
+ * corresponds to one machine instruction.
+ */
+void
+mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	MonoInst *ins, *next, *temp, *last_ins = NULL;
+
+	MONO_BB_FOR_EACH_INS (bb, ins) {
+loop_start:
+		switch (ins->opcode) {
+		case OP_COMPARE:
+			next = ins->next;
+			/* Branch opts can eliminate the branch */
+			if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
+				NULLIFY_INS (ins);
+				break;
+			}
+			if (next->opcode == OP_IBEQ)
+				next->opcode = OP_LBEQ;
+			else if (next->opcode == OP_IBGE)
+				next->opcode = OP_LBGE;
+			else if (next->opcode == OP_IBGT)
+				next->opcode = OP_LBGT;
+			else if (next->opcode == OP_IBLE)
+				next->opcode = OP_LBLE;
+			else if (next->opcode == OP_IBLT)
+				next->opcode = OP_LBLT;
+			else if (next->opcode == OP_IBNE_UN)
+				next->opcode = OP_LBNE_UN;
+			else if (next->opcode == OP_IBGE_UN)
+				next->opcode = OP_LBGE_UN;
+			else if (next->opcode == OP_IBGT_UN)
+				next->opcode = OP_LBGT_UN;
+			else if (next->opcode == OP_IBLE_UN)
+				next->opcode = OP_LBLE_UN;
+			else if (next->opcode == OP_IBLT_UN)
+				next->opcode = OP_LBLT_UN;
+			else if (next->opcode == OP_ICEQ)
+				next->opcode = OP_LCEQ;
+			else if (next->opcode == OP_ICGT)
+				next->opcode = OP_LCGT;
+			else if (next->opcode == OP_ICGT_UN)
+				next->opcode = OP_LCGT_UN;
+			else if (next->opcode == OP_ICLT)
+				next->opcode = OP_LCLT;
+			else if (next->opcode == OP_ICLT_UN)
+				next->opcode = OP_LCLT_UN;
+			else if (next->opcode == OP_COND_EXC_IEQ)
+				next->opcode = OP_COND_EXC_EQ;
+			else if (next->opcode == OP_COND_EXC_IGE)
+				next->opcode = OP_COND_EXC_GE;
+			else if (next->opcode == OP_COND_EXC_IGT)
+				next->opcode = OP_COND_EXC_GT;
+			else if (next->opcode == OP_COND_EXC_ILE)
+				next->opcode = OP_COND_EXC_LE;
+			else if (next->opcode == OP_COND_EXC_ILT)
+				next->opcode = OP_COND_EXC_LT;
+			else if (next->opcode == OP_COND_EXC_INE_UN)
+				next->opcode = OP_COND_EXC_NE_UN;
+			else if (next->opcode == OP_COND_EXC_IGE_UN)
+				next->opcode = OP_COND_EXC_GE_UN;
+			else if (next->opcode == OP_COND_EXC_IGT_UN)
+				next->opcode = OP_COND_EXC_GT_UN;
+			else if (next->opcode == OP_COND_EXC_ILE_UN)
+				next->opcode = OP_COND_EXC_LE_UN;
+			else if (next->opcode == OP_COND_EXC_ILT_UN)
+				next->opcode = OP_COND_EXC_LT_UN;
+			else if (next->opcode == OP_COND_EXC_IOV)
+				next->opcode = OP_COND_EXC_OV;
+			else if (next->opcode == OP_COND_EXC_INO)
+				next->opcode = OP_COND_EXC_NO;
+			else if (next->opcode == OP_COND_EXC_IC)
+				next->opcode = OP_COND_EXC_C;
+			else if (next->opcode == OP_COND_EXC_INC)
+				next->opcode = OP_COND_EXC_NC;
+			break;
+
+		case OP_ICOMPARE:
+			next = ins->next;
+			/* Branch opts can eliminate the branch */
+			if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
+				NULLIFY_INS (ins);
+				break;
+			}
+			if (next->opcode == OP_LBEQ)
+				next->opcode = OP_IBEQ;
+			else if (next->opcode == OP_LBGE)
+				next->opcode = OP_IBGE;
+			else if (next->opcode == OP_LBGT)
+				next->opcode = OP_IBGT;
+			else if (next->opcode == OP_LBLE)
+				next->opcode = OP_IBLE;
+			else if (next->opcode == OP_LBLT)
+				next->opcode = OP_IBLT;
+			else if (next->opcode == OP_LBNE_UN)
+				next->opcode = OP_IBNE_UN;
+			else if (next->opcode == OP_LBGE_UN)
+				next->opcode = OP_IBGE_UN;
+			else if (next->opcode == OP_LBGT_UN)
+				next->opcode = OP_IBGT_UN;
+			else if (next->opcode == OP_LBLE_UN)
+				next->opcode = OP_IBLE_UN;
+			else if (next->opcode == OP_LBLT_UN)
+				next->opcode = OP_IBLT_UN;
+			else if (next->opcode == OP_LCEQ)
+				next->opcode = OP_ICEQ;
+			else if (next->opcode == OP_LCGT)
+				next->opcode = OP_ICGT;
+			else if (next->opcode == OP_LCGT_UN)
+				next->opcode = OP_ICGT_UN;
+			else if (next->opcode == OP_LCLT)
+				next->opcode = OP_ICLT;
+			else if (next->opcode == OP_LCLT_UN)
+				next->opcode = OP_ICLT_UN;
+			else if (next->opcode == OP_COND_EXC_EQ)
+				next->opcode = OP_COND_EXC_IEQ;
+			else if (next->opcode == OP_COND_EXC_GE)
+				next->opcode = OP_COND_EXC_IGE;
+			else if (next->opcode == OP_COND_EXC_GT)
+				next->opcode = OP_COND_EXC_IGT;
+			else if (next->opcode == OP_COND_EXC_LE)
+				next->opcode = OP_COND_EXC_ILE;
+			else if (next->opcode == OP_COND_EXC_LT)
+				next->opcode = OP_COND_EXC_ILT;
+			else if (next->opcode == OP_COND_EXC_NE_UN)
+				next->opcode = OP_COND_EXC_INE_UN;
+			else if (next->opcode == OP_COND_EXC_GE_UN)
+				next->opcode = OP_COND_EXC_IGE_UN;
+			else if (next->opcode == OP_COND_EXC_GT_UN)
+				next->opcode = OP_COND_EXC_IGT_UN;
+			else if (next->opcode == OP_COND_EXC_LE_UN)
+				next->opcode = OP_COND_EXC_ILE_UN;
+			else if (next->opcode == OP_COND_EXC_LT_UN)
+				next->opcode = OP_COND_EXC_ILT_UN;
+			else if (next->opcode == OP_COND_EXC_OV)
+				next->opcode = OP_COND_EXC_IOV;
+			else if (next->opcode == OP_COND_EXC_NO)
+				next->opcode = OP_COND_EXC_INO;
+			else if (next->opcode == OP_COND_EXC_C)
+				next->opcode = OP_COND_EXC_IC;
+			else if (next->opcode == OP_COND_EXC_NC)
+				next->opcode = OP_COND_EXC_INC;
+			break;
+		case OP_LCOMPARE:
+			next = ins->next;
+			/* Branch opts can eliminate the branch */
+			if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
+				NULLIFY_INS (ins);
+				break;
+			}
+			if (next->opcode == OP_IBEQ)
+				next->opcode = OP_LBEQ;
+			else if (next->opcode == OP_IBGE)
+				next->opcode = OP_LBGE;
+			else if (next->opcode == OP_IBGT)
+				next->opcode = OP_LBGT;
+			else if (next->opcode == OP_IBLE)
+				next->opcode = OP_LBLE;
+			else if (next->opcode == OP_IBLT)
+				next->opcode = OP_LBLT;
+			else if (next->opcode == OP_IBNE_UN)
+				next->opcode = OP_LBNE_UN;
+			else if (next->opcode == OP_IBGE_UN)
+				next->opcode = OP_LBGE_UN;
+			else if (next->opcode == OP_IBGT_UN)
+				next->opcode = OP_LBGT_UN;
+			else if (next->opcode == OP_IBLE_UN)
+				next->opcode = OP_LBLE_UN;
+			else if (next->opcode == OP_IBLT_UN)
+				next->opcode = OP_LBLT_UN;
+			else if (next->opcode == OP_ICEQ)
+				next->opcode = OP_LCEQ;
+			else if (next->opcode == OP_ICGT)
+				next->opcode = OP_LCGT;
+			else if (next->opcode == OP_ICGT_UN)
+				next->opcode = OP_LCGT_UN;
+			else if (next->opcode == OP_ICLT)
+				next->opcode = OP_LCLT;
+			else if (next->opcode == OP_ICLT_UN)
+				next->opcode = OP_LCLT_UN;
+			else if (next->opcode == OP_COND_EXC_IEQ)
+				next->opcode = OP_COND_EXC_EQ;
+			else if (next->opcode == OP_COND_EXC_IGE)
+				next->opcode = OP_COND_EXC_GE;
+			else if (next->opcode == OP_COND_EXC_IGT)
+				next->opcode = OP_COND_EXC_GT;
+			else if (next->opcode == OP_COND_EXC_ILE)
+				next->opcode = OP_COND_EXC_LE;
+			else if (next->opcode == OP_COND_EXC_ILT)
+				next->opcode = OP_COND_EXC_LT;
+			else if (next->opcode == OP_COND_EXC_INE_UN)
+				next->opcode = OP_COND_EXC_NE_UN;
+			else if (next->opcode == OP_COND_EXC_IGE_UN)
+				next->opcode = OP_COND_EXC_GE_UN;
+			else if (next->opcode == OP_COND_EXC_IGT_UN)
+				next->opcode = OP_COND_EXC_GT_UN;
+			else if (next->opcode == OP_COND_EXC_ILE_UN)
+				next->opcode = OP_COND_EXC_LE_UN;
+			else if (next->opcode == OP_COND_EXC_ILT_UN)
+				next->opcode = OP_COND_EXC_LT_UN;
+			else if (next->opcode == OP_COND_EXC_IOV)
+				next->opcode = OP_COND_EXC_OV;
+			else if (next->opcode == OP_COND_EXC_INO)
+				next->opcode = OP_COND_EXC_NO;
+			else if (next->opcode == OP_COND_EXC_IC)
+				next->opcode = OP_COND_EXC_C;
+			else if (next->opcode == OP_COND_EXC_INC)
+				next->opcode = OP_COND_EXC_NC;
+			break;
+
+		case OP_COMPARE_IMM:
+		case OP_ICOMPARE_IMM:
+		case OP_LCOMPARE_IMM:
+			next = ins->next;
+			/* Branch opts can eliminate the branch */
+			if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
+				NULLIFY_INS (ins);
+				break;
+			}
+			if (ins->inst_imm) {
+				NEW_INS (cfg, last_ins, temp, OP_I8CONST);
+				temp->inst_c0 = ins->inst_imm;
+				temp->dreg = mono_alloc_ireg (cfg);
+				ins->sreg2 = temp->dreg;
+				last_ins = temp;
+			}
+			else {
+				ins->sreg2 = loongarch_zero;
+			}
+			if (ins->opcode == OP_COMPARE_IMM)
+				ins->opcode = OP_COMPARE;
+			else if (ins->opcode == OP_ICOMPARE_IMM)
+				ins->opcode = OP_ICOMPARE;
+			else if (ins->opcode == OP_LCOMPARE_IMM)
+				ins->opcode = OP_LCOMPARE;
+			goto loop_start;
+
+		case OP_IDIV_IMM:
+		case OP_IREM_IMM:
+		case OP_IDIV_UN_IMM:
+		case OP_IREM_UN_IMM:
+		case OP_LREM_IMM:
+			mono_decompose_op_imm (cfg, bb, ins);
+			break;
+		case OP_LOCALLOC_IMM:
+			if (ins->inst_imm > 32) {
+				ADD_NEW_INS (cfg, temp, OP_ICONST);
+				temp->inst_c0 = ins->inst_imm;
+				temp->dreg = mono_alloc_ireg (cfg);
+				ins->sreg1 = temp->dreg;
+				ins->opcode = mono_op_imm_to_op (ins->opcode);
+			}
+			break;
+		case OP_IBEQ:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->sreg2 == loongarch_zero) {
+				INS_REWRITE (ins, OP_LA_LBEQZ, last_ins->sreg1, -1);
+			} else {
+				INS_REWRITE (ins, OP_LA_IBEQ, last_ins->sreg1, last_ins->sreg2);
+			}
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBNE_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->sreg2 == loongarch_zero) {
+				INS_REWRITE (ins, OP_LA_LBNEZ, last_ins->sreg1, -1);
+			} else {
+				INS_REWRITE (ins, OP_LA_IBNE_UN, last_ins->sreg1, last_ins->sreg2);
+			}
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBGE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBGE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBGE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBGE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBLT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBLT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBLT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBLT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBLE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBLE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBLE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBLE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBGT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBGT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_IBGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_IBGT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBEQ:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->sreg2 == loongarch_zero) {
+				INS_REWRITE (ins, OP_LA_LBEQZ, last_ins->sreg1, -1);
+			} else {
+				INS_REWRITE (ins, OP_LA_LBEQ, last_ins->sreg1, last_ins->sreg2);
+			}
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBNE_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->sreg2 == loongarch_zero) {
+				INS_REWRITE (ins, OP_LA_LBNEZ, last_ins->sreg1, -1);
+			} else {
+				INS_REWRITE (ins, OP_LA_LBNE_UN, last_ins->sreg1, last_ins->sreg2);
+			}
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBGE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBGE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBGE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBGE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBLT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBLT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBLT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBLT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBLE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBLE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBLE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBLE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBGT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBGT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LBGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LBGT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_CEQ:
+		case OP_ICEQ:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICEQ, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_CLT:
+		case OP_ICLT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICLT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_CLT_UN:
+		case OP_ICLT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICLT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_CGT:
+		case OP_ICGT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICGT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_CGT_UN:
+		case OP_ICGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICGT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_ICNEQ:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICNEQ, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_ICGE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICGE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_ICGE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICGE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_ICLE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICLE, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_ICLE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_ICLE_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LCEQ:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LCEQ, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LCLT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LCLT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LCLT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LCLT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LCGT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LCGT, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_LCGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_LCGT_UN, last_ins->sreg1, last_ins->sreg2);
+			NULLIFY_INS (last_ins);
+			break;
+
+		case OP_COND_EXC_EQ:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_EQ, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_GE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_GE, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_GT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_GT, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_LE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_LE, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_LT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_LT, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_NE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_NE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_GE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_GE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_GT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_GT_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_LE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_LE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_LT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_LT_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_OV:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_OV, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_NO:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_NO, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_C:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_C, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_NC:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_NC, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IEQ:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IEQ, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IGE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IGE, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IGT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IGT, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_ILE:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_ILE, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_ILT:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_ILT, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_INE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_INE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IGE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IGE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IGT_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_ILE_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_ILE_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_ILT_UN:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_ILT_UN, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IOV:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IOV, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_INO:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_INO, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_IC:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_IC, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_COND_EXC_INC:
+			g_assert (ins_is_compare (last_ins));
+			INS_REWRITE (ins, OP_LA_COND_EXC_INC, last_ins->sreg1, last_ins->sreg2);
+			MONO_DELETE_INS (bb, last_ins);
+			break;
+
+		case OP_FCOMPARE:
+		case OP_RCOMPARE:
+			next = ins->next;
+			/* Branch opts can eliminate the branch */
+			if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
+				NULLIFY_INS (ins);
+				break;
+			}
+			ins->dreg = mono_alloc_ireg (cfg);
+			break;
+
+		case OP_FBEQ:
+		case OP_RBEQ:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCEQ;
+			else
+				last_ins->opcode = OP_RCEQ;
+			break;
+
+		case OP_FBGE:
+		case OP_RBGE:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCGE;
+			else
+				last_ins->opcode = OP_RCGE;
+			break;
+
+		case OP_FBGT:
+		case OP_RBGT:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCGT;
+			else
+				last_ins->opcode = OP_RCGT;
+			break;
+
+		case OP_FBLE:
+		case OP_RBLE:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCLE;
+			else
+				last_ins->opcode = OP_RCLE;
+			break;
+
+		case OP_FBLT:
+		case OP_RBLT:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCLT;
+			else
+				last_ins->opcode = OP_RCLT;
+			break;
+
+		case OP_FBNE_UN:
+		case OP_RBNE_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_LA_FCNEQ_UN;
+			else
+				last_ins->opcode = OP_LA_RCNEQ_UN;
+			break;
+
+		case OP_FBGE_UN:
+		case OP_RBGE_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_LA_FCGE_UN;
+			else
+				last_ins->opcode = OP_LA_RCGE_UN;
+			break;
+
+		case OP_FBGT_UN:
+		case OP_RBGT_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCGT_UN;
+			else
+				last_ins->opcode = OP_RCGT_UN;
+			break;
+
+		case OP_FBLE_UN:
+		case OP_RBLE_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_LA_FCLE_UN;
+			else
+				last_ins->opcode = OP_LA_RCLE_UN;
+			break;
+
+		case OP_FBLT_UN:
+		case OP_RBLT_UN:
+			g_assert (ins_is_compare (last_ins));
+			if (last_ins->opcode == OP_FCOMPARE)
+				last_ins->opcode = OP_FCLT_UN;
+			else
+				last_ins->opcode = OP_RCLT_UN;
+			break;
+
+		case OP_RCNEQ:
+			ins->opcode == OP_LA_RCNEQ_UN;
+			break;
+		default:
+			break;
+		}
+
+		last_ins = ins;
+	}
+	bb->last_ins = last_ins;
+	bb->max_vreg = cfg->next_vreg;
+}
+
+static int
+opcode_to_loongarchcond (int opcode, gint *src1, gint *src2)
+{
+	gint swp;
+	switch (opcode) {
+	case OP_LA_IBEQ:
+	case OP_LA_LBEQ:
+	case OP_FBEQ:
+	case OP_LA_COND_EXC_EQ:
+	case OP_LA_COND_EXC_IEQ:
+		return 0x58000000;
+	case OP_LA_IBGE:
+	case OP_LA_LBGE:
+	case OP_FBGE:
+	case OP_LA_COND_EXC_IGE:
+	case OP_LA_COND_EXC_GE:
+		return 0x64000000;
+	case OP_LA_IBGT:
+	case OP_LA_LBGT:
+	case OP_FBGT:
+	case OP_LA_COND_EXC_IGT:
+	case OP_LA_COND_EXC_GT:
+		swp = *src1;
+		*src1 = *src2;
+		*src2 = swp;
+		return 0x60000000;
+	case OP_LA_IBLE:
+	case OP_LA_LBLE:
+	case OP_FBLE:
+	case OP_LA_COND_EXC_ILE:
+	case OP_LA_COND_EXC_LE:
+		swp = *src1;
+		*src1 = *src2;
+		*src2 = swp;
+		return 0x64000000;
+	case OP_LA_IBLT:
+	case OP_LA_LBLT:
+	case OP_FBLT:
+	case OP_LA_COND_EXC_ILT:
+	case OP_LA_COND_EXC_LT:
+		return 0x60000000;
+	case OP_LA_IBNE_UN:
+	case OP_LA_LBNE_UN:
+	case OP_FBNE_UN:
+	case OP_LA_COND_EXC_NE_UN:
+	case OP_LA_COND_EXC_INE_UN:
+		return 0x5c000000;
+	case OP_LA_IBGE_UN:
+	case OP_LA_LBGE_UN:
+	case OP_FBGE_UN:
+	case OP_LA_COND_EXC_IGE_UN:
+	case OP_LA_COND_EXC_GE_UN:
+	case OP_LA_COND_EXC_C:
+	case OP_LA_COND_EXC_IC:
+		return 0x6c000000;
+	case OP_LA_IBGT_UN:
+	case OP_LA_LBGT_UN:
+	case OP_FBGT_UN:
+	case OP_LA_COND_EXC_IGT_UN:
+	case OP_LA_COND_EXC_GT_UN:
+		swp = *src1;
+		*src1 = *src2;
+		*src2 = swp;
+		return 0x68000000;
+	case OP_LA_IBLE_UN:
+	case OP_LA_LBLE_UN:
+	case OP_LA_COND_EXC_ILE_UN:
+	case OP_LA_COND_EXC_LE_UN:
+		swp = *src1;
+		*src1 = *src2;
+		*src2 = swp;
+		return 0x6c000000;
+	case OP_LA_IBLT_UN:
+	case OP_LA_LBLT_UN:
+	case OP_LA_COND_EXC_ILT_UN:
+	case OP_LA_COND_EXC_LT_UN:
+	case OP_LA_COND_EXC_NC:
+	case OP_LA_COND_EXC_INC:
+		return 0x68000000;
+	default:
+		printf ("%s\n", mono_inst_name (opcode));
+		g_assert_not_reached ();
+		return -1;
+	}
+}
+
+static guint8*
+emit_move_return_value (MonoCompile *cfg, guint8 * code, MonoInst *ins)
+{
+	CallInfo *cinfo;
+	MonoCallInst *call;
+
+	call = (MonoCallInst*)ins;
+	cinfo = call->call_info;
+	g_assert (cinfo);
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+		break;
+	case ArgInIReg:
+		/* LLVM compiled code might only set the bottom bits */
+		if (call->signature && mini_get_underlying_type (call->signature->ret)->type == MONO_TYPE_I4)
+			loongarch_sllw (code, call->inst.dreg, cinfo->ret.reg, loongarch_zero);
+		else if (call->inst.dreg != cinfo->ret.reg)
+			loongarch_ori (code, call->inst.dreg, cinfo->ret.reg, 0);
+		break;
+	case ArgInFReg:
+		if (mini_get_underlying_type (call->signature->ret)->type == MONO_TYPE_R4) {
+			if (cfg->r4fp)
+				loongarch_fmovs (code, call->inst.dreg, cinfo->ret.reg);
+			else
+				loongarch_fcvtds (code, call->inst.dreg, cinfo->ret.reg);
+		} else {
+			if (call->inst.dreg != cinfo->ret.reg)
+				loongarch_fmovd (code, call->inst.dreg, cinfo->ret.reg);
+		}
+		break;
+	case ArgStructByVal: {
+		MonoInst *loc = cfg->arch.vret_addr_loc;
+		int i;
+
+		/* Load the destination address */
+		g_assert (loc && loc->opcode == OP_REGOFFSET);
+		code = emit_ldd (code, loongarch_ra, loc->inst_basereg, loc->inst_offset);
+		int field_info = cinfo->ret.field_info;
+		int offsets;
+
+		if (field_info & 0x1f) {
+			if (field_info & 1) {
+				g_assert (cinfo->ret.freg == loongarch_f0);
+				if (field_info  & 2) {
+					loongarch_fstd (code, loongarch_f0 , loongarch_ra, 0);
+					offsets = 8;
+				} else {
+					loongarch_fsts (code, loongarch_f0 , loongarch_ra, 0);
+					offsets = 4;
+				}
+			} else {
+				g_assert (cinfo->ret.reg == loongarch_a0);
+				if (field_info & 2) {
+					loongarch_std (code, loongarch_a0, loongarch_ra, 0);
+					offsets = 8;
+				} else {
+					loongarch_stw (code, loongarch_a0, loongarch_ra, 0);
+					offsets = 4;
+				}
+			}
+
+			if (field_info & 4) {
+				if (field_info & 8) {
+					i = field_info & 1 ? loongarch_f1 : loongarch_f0;
+					if (field_info & 0x10) {
+						loongarch_fstd (code, i, loongarch_ra, 8);
+					} else {
+						loongarch_fsts (code, i, loongarch_ra, offsets & 0xfff);
+					}
+				} else {
+					i = field_info & 1 ? loongarch_a0 : loongarch_a1;
+					if (field_info  & 0x10) {
+						loongarch_std (code, i, loongarch_ra, 8);
+					} else {
+						loongarch_stw (code, i, loongarch_ra, offsets & 0xfff);
+					}
+				}
+			}
+		} else {
+			if (cinfo->ret.size > 8) {
+				loongarch_std (code, loongarch_a0, loongarch_ra, 0);
+				loongarch_std (code, loongarch_a1, loongarch_ra, 8);
+			} else {
+				loongarch_std (code, loongarch_a0, loongarch_ra, 0);
+			}
+		}
+		break;
+	}
+	case ArgStructByRef:
+		loongarch_ldd (code, cinfo->ret.reg, loongarch_sp, cinfo->ret.offset);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+	return code;
+}
+
+static guint8*
+emit_call (MonoCompile *cfg, guint8* code, MonoJumpInfoType patch_type, gconstpointer data)
+{
+	mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_LOONGARCH64_BL);
+	loongarch_bl (code, 0);
+	cfg->thunk_area += THUNK_SIZE;
+	return code;
+}
+
+/*
+ * emit_branch_island:
+ *
+ *   Emit a branch island for the conditional branches from cfg->native_code + start_offset to code.
+ */
+static guint8*
+emit_branch_island (MonoCompile *cfg, guint8 *code, int start_offset)
+{
+	MonoJumpInfo *ji;
+
+	/* Iterate over the patch infos added so far by this bb */
+	int island_size = 0;
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->ip.i < start_offset)
+			/* The patch infos are in reverse order, so this means the end */
+			break;
+		if ((cfg->arch.cond_branch_islands == 1 && ji->relocation == MONO_R_LOONGARCH64_BC) || (cfg->arch.cond_branch_islands == 2 && (ji->relocation == MONO_R_LOONGARCH64_BC || ji->relocation == MONO_R_LOONGARCH64_BZ))) {
+			island_size += 4;
+		}
+	}
+
+	if (island_size) {
+		code = realloc_code (cfg, island_size);
+
+		/* Branch over the island */
+		loongarch_b (code, 1 + island_size/4);
+
+		for (ji = cfg->patch_info; ji; ji = ji->next) {
+			if (ji->ip.i < start_offset)
+				break;
+			if ((cfg->arch.cond_branch_islands == 1 && ji->relocation == MONO_R_LOONGARCH64_BC) || (cfg->arch.cond_branch_islands == 2 && (ji->relocation == MONO_R_LOONGARCH64_BC || ji->relocation == MONO_R_LOONGARCH64_BZ))) {
+				/* Rewrite the cond branch so it branches to an unconditional branch in the branch island */
+				mono_loongarch64_patch (cfg->native_code + ji->ip.i, code, ji->relocation);
+				/* Rewrite the patch so it points to the unconditional branch */
+				ji->ip.i = code - cfg->native_code;
+				ji->relocation = MONO_R_LOONGARCH64_B;
+				loongarch_b (code, 0);
+			}
+		}
+		set_code_cursor (cfg, code);
+	}
+	return code;
+}
+
+void
+mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	MonoInst *ins;
+	MonoCallInst *call;
+	guint8 *code = cfg->native_code + cfg->code_len;
+	MonoInst *last_ins = NULL;
+	int start_offset, max_len, cpos;
+	int ins_cnt = 0;
+
+	/* we don't align basic blocks of loops on loongarch64 */
+
+	if (cfg->verbose_level > 2)
+		g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
+
+	cpos = bb->max_offset;
+	start_offset = code - cfg->native_code;
+	g_assert (start_offset <= cfg->code_size);
+
+	MONO_BB_FOR_EACH_INS (bb, ins) {
+		guint offset = code - cfg->native_code;
+		set_code_cursor (cfg, code);
+		max_len = ins_get_size (ins->opcode);
+		code = realloc_code (cfg, max_len);
+
+		if (G_UNLIKELY (cfg->arch.cond_branch_islands > 0 && offset - start_offset > 4 * 0x3fff)) {
+			/* Emit a branch island for large basic blocks */
+			code = emit_branch_island (cfg, code, start_offset);
+			offset = code - cfg->native_code;
+			start_offset = offset;
+		}
+
+		mono_debug_record_line_number (cfg, ins, offset);
+		if (cfg->verbose_level > 2) {
+			g_print ("    @ 0x%x\t", offset);
+			mono_print_ins_index (ins_cnt++, ins);
+		}
+		/* Check for virtual regs that snuck by */
+		g_assert ((ins->dreg >= -1) && (ins->dreg < 32));
+
+		switch (ins->opcode) {
+		case OP_RELAXED_NOP:
+		case OP_NOP:
+		case OP_DUMMY_USE:
+		case OP_DUMMY_ICONST:
+		case OP_DUMMY_I8CONST:
+		case OP_DUMMY_R8CONST:
+		case OP_DUMMY_R4CONST:
+		case OP_NOT_REACHED:
+		case OP_NOT_NULL:
+			break;
+		case OP_IL_SEQ_POINT:
+			mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+			break;
+		case OP_SEQ_POINT: {
+			MonoInst *info_var = cfg->arch.seq_point_info_var;
+
+			/*
+			 * For AOT, we use one got slot per method, which will point to a
+			 * SeqPointInfo structure, containing all the information required
+			 * by the code below.
+			 */
+			if (cfg->compile_aot) {
+				g_assert (info_var);
+				g_assert (info_var->opcode == OP_REGOFFSET);
+			}
+
+			if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
+				MonoInst *var = cfg->arch.ss_tramp_var;
+
+				g_assert (var);
+				g_assert (var->opcode == OP_REGOFFSET);
+				/* Load ss_tramp_var */
+				/* This is equal to &ss_trampoline */
+				loongarch_ldd (code, loongarch_r21, var->inst_basereg, var->inst_offset);
+				/* Load the trampoline address */
+				loongarch_ldd (code, loongarch_r21, loongarch_r21, 0);
+				/* Call it if it is non-null */
+				loongarch_beqz (code, loongarch_r21, 2);
+				loongarch_jirl (code, 1, loongarch_r21, 0);
+			}
+
+			mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+
+			if (cfg->compile_aot) {
+				const guint32 offset = GPTRDIFF_TO_UINT32 (code - cfg->native_code);
+				guint32 val;
+
+				loongarch_ldd (code, loongarch_r21, info_var->inst_basereg, info_var->inst_offset);
+				/* Add the offset */
+				val = ((offset / 4) * sizeof (target_mgreg_t)) + MONO_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
+				/* Load the info->bp_addrs [offset], which is either 0 or the address of the bp trampoline */
+				code = emit_ldd (code, loongarch_r21, loongarch_r21, val);
+				/* Skip the load if its 0 */
+				loongarch_beqz (code, loongarch_r21, 2);
+				/* Call the breakpoint trampoline */
+				loongarch_jirl (code, 1, loongarch_r21, 0);
+			} else {
+				MonoInst *var = cfg->arch.bp_tramp_var;
+
+				g_assert (var);
+				g_assert (var->opcode == OP_REGOFFSET);
+				/* Load the address of the bp trampoline into IP0 */
+				loongarch_ldd (code, loongarch_t0, var->inst_basereg, var->inst_offset);
+				/*
+				 * A placeholder for a possible breakpoint inserted by
+				 * mono_arch_set_breakpoint ().
+				 */
+				loongarch_nop (code);
+			}
+
+			break;
+		}
+		case OP_BIGMUL:
+			loongarch_muld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_mulhd (code, ins->dreg+1, ins->sreg1, ins->sreg2);
+			break;
+		case OP_BIGMUL_UN:
+			loongarch_muld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_mulhdu (code, ins->dreg+1, ins->sreg1, ins->sreg2);
+			break;
+		case OP_MEMORY_BARRIER:
+			loongarch_dbar (code, 0);
+			break;
+		case OP_STOREI1_MEMBASE_IMM:
+			loongarch_load_const (code, loongarch_temp, ins->inst_imm);
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_stb (code, loongarch_temp, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxb (code, loongarch_temp, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STOREI2_MEMBASE_IMM:
+			loongarch_load_const (code, loongarch_temp, ins->inst_imm);
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_sth (code, loongarch_temp, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxh (code, loongarch_temp, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STORE_MEMBASE_IMM:
+		case OP_STOREI8_MEMBASE_IMM:
+			loongarch_load_const (code, loongarch_temp, ins->inst_imm);
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_std (code, loongarch_temp, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxd (code, loongarch_temp, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STOREI4_MEMBASE_IMM:
+			loongarch_load_const (code, loongarch_temp, ins->inst_imm);
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_stw (code, loongarch_temp, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxw (code, loongarch_temp, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STOREI1_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_stb (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxb (code, ins->sreg1, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STOREI2_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_sth (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxh (code, ins->sreg1, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STOREI4_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_stw (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxw (code, ins->sreg1, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_STORE_MEMBASE_REG:
+		case OP_STOREI8_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_std (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_stxd (code, ins->sreg1, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_LOADU4_MEM:
+			loongarch_load_const (code, loongarch_temp, ins->inst_imm);
+			loongarch_ldwu (code, ins->dreg, loongarch_temp, 0);
+			break;
+		case OP_LOAD_MEMBASE:
+		case OP_LOADI8_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldd (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxd (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADI4_MEMBASE:
+			g_assert (ins->dreg != -1);
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldw (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxw (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADU4_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldwu (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxwu (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADI1_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldb (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxb (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADU1_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldbu (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxbu (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADI2_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldh (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxh (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_LOADU2_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_ldhu (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_ldxhu (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_ICONV_TO_I1:
+		case OP_LCONV_TO_I1:
+			loongarch_extwb (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_ICONV_TO_I2:
+		case OP_LCONV_TO_I2:
+			loongarch_extwh (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_ICONV_TO_U1:
+		case OP_LCONV_TO_U1:
+			loongarch_andi (code, ins->dreg, ins->sreg1, 0xff);
+			break;
+		case OP_ICONV_TO_U2:
+		case OP_LCONV_TO_U2:
+			loongarch_bstrpickd (code, ins->dreg, ins->sreg1, 15, 0);
+			break;
+		case OP_BREAK:
+			/*
+			 * gdb does not like encountering the hw breakpoint ins in the debugged code.
+			 * So instead of emitting a trap, we emit a call a C function and place a
+			 * breakpoint there.
+			 */
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_break), MONO_R_LOONGARCH64_BL);
+			loongarch_bl (code, 0);
+			cfg->thunk_area += THUNK_SIZE;
+			break;
+		case OP_IADD:
+			loongarch_addw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LADD:
+			loongarch_addd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+
+		case OP_IADD_IMM:
+			if (loongarch_is_imm12 (ins->inst_imm)) {
+				loongarch_addiw (code, ins->dreg, ins->sreg1, (ins->inst_imm) & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+				loongarch_addw (code, ins->dreg, ins->sreg1, loongarch_r21);
+			}
+			break;
+		case OP_ADD_IMM:
+		case OP_LADD_IMM:
+			if (loongarch_is_imm12 (ins->inst_imm)) {
+				loongarch_addid (code, ins->dreg, ins->sreg1, ins->inst_imm & 0xfff);
+			} else {
+				code = mono_loongarch_emit_imm64 (code, loongarch_r21, ins->inst_imm);
+				loongarch_addd (code, ins->dreg, ins->sreg1, loongarch_r21);
+			}
+			break;
+
+		case OP_ISUB:
+			loongarch_subw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LSUB:
+			loongarch_subd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+
+		case OP_ISUB_IMM:
+			// we add the negated value
+			if (loongarch_is_imm12 (-ins->inst_imm)) {
+				loongarch_addiw (code, ins->dreg, ins->sreg1, (-ins->inst_imm) & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+				loongarch_subw (code, ins->dreg, ins->sreg1, loongarch_r21);
+            }
+			break;
+
+		case OP_SUB_IMM:
+		case OP_LSUB_IMM:
+			// we add the negated value
+			if (loongarch_is_imm12 (-ins->inst_imm)) {
+				loongarch_addid (code, ins->dreg, ins->sreg1, -ins->inst_imm & 0xfff);
+			} else {
+				code = mono_loongarch_emit_imm64 (code, loongarch_r21, ins->inst_imm);
+				loongarch_subd (code, ins->dreg, ins->sreg1, loongarch_r21);
+			}
+			break;
+
+		case OP_IAND:
+			loongarch_and (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			break;
+		case OP_LAND:
+			loongarch_and (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+
+		case OP_AND_IMM:
+		case OP_IAND_IMM:
+		case OP_LAND_IMM:
+			if (ins->inst_imm & 0xfffff000)
+			{
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+				loongarch_and (code, ins->dreg, ins->sreg1, loongarch_r21);
+			} else {
+				loongarch_andi (code, ins->dreg, ins->sreg1, (ins->inst_imm) & 0xfff);
+			}
+			if (ins->opcode == OP_IAND_IMM) {
+				loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			}
+			break;
+
+		case OP_IDIV:
+		case OP_IREM: {
+			/* Check for zero */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "DivideByZeroException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, ins->sreg2, 0);
+
+			/* Check for INT_MIN/-1 */
+			loongarch_lu12iw (code, loongarch_r21, 0x80000);
+			loongarch_xor (code, loongarch_r21, ins->sreg1, loongarch_r21);
+			loongarch_addiw (code, loongarch_ra, ins->sreg2, 1);
+			loongarch_or (code, loongarch_r21, loongarch_r21, loongarch_ra);
+			loongarch_bstrpickd (code, loongarch_r21, loongarch_r21, 31, 0);
+
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, loongarch_r21, 0);
+
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			if (ins->opcode == OP_IDIV) {
+				loongarch_divw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			} else {
+				loongarch_modw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			}
+			break;
+		}
+		case OP_IDIV_UN:
+		case OP_IREM_UN: {
+			/* Check for zero */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "DivideByZeroException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, ins->sreg2, 0);
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			if (ins->opcode == OP_IDIV_UN) {
+				loongarch_divwu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			} else {
+				loongarch_modwu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			}
+			break;
+		}
+		case OP_LDIV:
+		case OP_LREM: {
+			/* Check for zero */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "DivideByZeroException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, ins->sreg2, 0);
+
+			/* Check for INT_MIN/-1 */
+			loongarch_lu52id (code, loongarch_r21, loongarch_zero, 0x800);
+			loongarch_xor (code, loongarch_r21, ins->sreg1, loongarch_r21);
+			loongarch_addid (code, loongarch_ra, ins->sreg2, 1);
+			loongarch_or (code, loongarch_r21, loongarch_r21, loongarch_ra);
+
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, loongarch_r21, 0);
+
+			if (ins->opcode == OP_LDIV) {
+				loongarch_divd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			} else {
+				loongarch_modd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			}
+			break;
+		}
+		case OP_LDIV_UN:
+		case OP_LREM_UN: {
+			/* Check for zero */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "DivideByZeroException", MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, ins->sreg2, 0);
+			if (ins->opcode == OP_LDIV_UN) {
+				loongarch_divdu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			} else {
+				loongarch_moddu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			}
+			break;
+		}
+		case OP_DIV_IMM:
+			g_assert_not_reached ();
+			break;
+		case OP_REM_IMM:
+			g_assert_not_reached ();
+		case OP_IOR:
+			loongarch_or (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			break;
+		case OP_LOR:
+			loongarch_or (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_OR_IMM:
+		case OP_IOR_IMM:
+		case OP_LOR_IMM:
+			/* unsigned 12-bit immediate */
+			if (!(ins->inst_imm & 0xfffff000)) {
+				loongarch_ori (code, ins->dreg, ins->sreg1, (ins->inst_imm) & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+				loongarch_or (code, ins->dreg, ins->sreg1, loongarch_r21);
+			}
+			if (ins->opcode == OP_IOR_IMM) {
+				loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			}
+			break;
+		case OP_IXOR:
+			loongarch_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			break;
+		case OP_LXOR:
+			loongarch_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_XOR_IMM:
+		case OP_IXOR_IMM:
+		case OP_LXOR_IMM:
+			/* unsigned 12-bit immediate */
+			if (!(ins->inst_imm & 0xfffff000)) {
+				loongarch_xori (code, ins->dreg, ins->sreg1, (ins->inst_imm) & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+				loongarch_xor (code, ins->dreg, ins->sreg1, loongarch_r21);
+			}
+			if (ins->opcode == OP_IXOR_IMM) {
+				loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			}
+			break;
+		case OP_ISHL:
+			loongarch_sllw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_ISHL_IMM:
+			loongarch_slliw (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
+			break;
+		case OP_SHL_IMM:
+		case OP_LSHL_IMM:
+			loongarch_sllid (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x3f);
+			break;
+		case OP_LSHL:
+			loongarch_slld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_ISHR:
+			loongarch_sraw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LSHR:
+			loongarch_srad (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_ISHR_IMM:
+			loongarch_sraiw (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
+			break;
+		case OP_SHR_IMM:
+		case OP_LSHR_IMM:
+			loongarch_sraid (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x3f);
+			break;
+		case OP_ISHR_UN_IMM:
+			loongarch_srliw (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
+			break;
+		case OP_SHR_UN_IMM:
+		case OP_LSHR_UN_IMM:
+			loongarch_srlid (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x3f);
+			break;
+		case OP_ISHR_UN:
+			loongarch_srlw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LSHR_UN:
+			loongarch_srld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_INOT:
+		case OP_LNOT:
+			loongarch_nor (code, ins->dreg, loongarch_zero, ins->sreg1);
+			break;
+		case OP_INEG:
+			loongarch_subw (code, ins->dreg, loongarch_zero, ins->sreg1);
+			break;
+		case OP_LNEG:
+			loongarch_subd (code, ins->dreg, loongarch_zero, ins->sreg1);
+			break;
+		case OP_IMUL:
+			loongarch_mulw (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LMUL:
+			loongarch_muld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_IMUL_IMM:
+			loongarch_lu12iw (code, loongarch_r21, (ins->inst_imm >> 12) & 0xfffff);
+			loongarch_ori (code, loongarch_r21, loongarch_r21, (ins->inst_imm & 0xfff));
+			loongarch_mulw (code, ins->dreg, ins->sreg1, loongarch_r21);
+			break;
+		case OP_MUL_IMM:
+		case OP_LMUL_IMM:
+			code = mono_loongarch_emit_imm64 (code, loongarch_r21, ins->inst_imm);
+			loongarch_muld (code, ins->dreg, ins->sreg1, loongarch_r21);
+			break;
+		case OP_ICONST:
+			if (loongarch_is_imm12 (ins->inst_c0)) {
+				loongarch_addid (code, ins->dreg, loongarch_zero, ins->inst_c0 & 0xfff);
+			} else {
+				loongarch_lu12iw (code, ins->dreg, (ins->inst_c0 >> 12) & 0xfffff);
+				loongarch_ori (code, ins->dreg, ins->dreg, ins->inst_c0 & 0xfff);
+				if ((ins->inst_c0 > 0) && ((ins->inst_c0) & (0x0000000080000000)))
+					loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 0x3f, 0x20);
+			}
+			break;
+		case OP_I8CONST:
+			code = mono_loongarch_emit_imm64 (code, ins->dreg, ins->inst_c0);
+			break;
+		case OP_AOTCONST:
+			code = emit_aotconst (cfg, code, ins->dreg, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0);
+			break;
+
+		case OP_ICONV_TO_I4:
+		case OP_ICONV_TO_U4:
+		case OP_MOVE:
+			if (ins->dreg != ins->sreg1)
+				loongarch_ori (code, ins->dreg, ins->sreg1, 0);
+			break;
+		case OP_ZEXT_I4:
+			if (ins->dreg != ins->sreg1)
+				loongarch_ori (code, ins->dreg, ins->sreg1, 0);
+			loongarch_bstrinsd (code, ins->dreg, loongarch_zero, 63, 32);
+			break;
+		case OP_SEXT_I4:
+			loongarch_slliw (code, ins->dreg, ins->sreg1, 0);
+			break;
+		case OP_FMOVE:
+			if (ins->dreg != ins->sreg1) {
+				loongarch_fmovd (code, ins->dreg, ins->sreg1);
+			}
+			break;
+		case OP_RMOVE:
+			if (ins->dreg != ins->sreg1) {
+				loongarch_fmovs (code, ins->dreg, ins->sreg1);
+			}
+			break;
+		case OP_MOVE_F_TO_I4:
+			if (cfg->r4fp) {
+				loongarch_movfr2grs (code, ins->dreg, ins->sreg1);
+			} else {
+				loongarch_fcvtsd (code, loongarch_ftemp, ins->sreg1);
+				loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			}
+			break;
+		case OP_MOVE_I4_TO_F:
+			if (cfg->r4fp) {
+				loongarch_movgr2frw (code, ins->dreg, ins->sreg1);
+			} else {
+				loongarch_movgr2frd (code, loongarch_ftemp, ins->sreg1);
+				loongarch_fcvtds (code, ins->dreg, loongarch_ftemp);
+			}
+			break;
+		case OP_MOVE_F_TO_I8:
+			loongarch_movfr2grd (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_MOVE_I8_TO_F:
+			loongarch_movgr2frd (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_CHECK_THIS:
+			/* ensure ins->sreg1 is not NULL */
+			loongarch_ldw (code, loongarch_zero, ins->sreg1, 0);
+			break;
+		case OP_ARGLIST: {
+			g_assert (loongarch_is_imm12 (cfg->sig_cookie));
+			loongarch_addid (code, loongarch_r21, cfg->arch.args_reg, cfg->arch.cinfo->sig_cookie.offset);
+			loongarch_std (code, loongarch_r21, ins->sreg1, 0);
+			break;
+		}
+		case OP_FCALL:
+		case OP_LCALL:
+		case OP_VCALL2:
+		case OP_VOIDCALL:
+		case OP_CALL:
+		case OP_RCALL: {
+			call = (MonoCallInst*)ins;
+			const MonoJumpInfoTarget patch = mono_call_to_patch (call);
+			code = emit_call (cfg, code, patch.type, patch.target);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+		}
+		case OP_FCALL_REG:
+		case OP_LCALL_REG:
+		case OP_VCALL2_REG:
+		case OP_VOIDCALL_REG:
+		case OP_CALL_REG:
+		case OP_RCALL_REG:
+			loongarch_jirl (code, 1, ins->sreg1, 0);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+
+		case OP_FCALL_MEMBASE:
+		case OP_LCALL_MEMBASE:
+		case OP_VCALL2_MEMBASE:
+		case OP_VOIDCALL_MEMBASE:
+		case OP_CALL_MEMBASE:
+		case OP_RCALL_MEMBASE:
+			code = emit_ldd (code, loongarch_r21, ins->inst_basereg, ins->inst_offset);
+			loongarch_jirl (code, 1, loongarch_r21, 0);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+		case OP_TAILCALL_PARAMETER:
+			// This opcode helps compute sizes, i.e.
+			// of the subsequent OP_TAILCALL, but contributes no code.
+			g_assert (ins->next);
+			break;
+
+		case OP_TAILCALL:
+		case OP_TAILCALL_REG:
+		case OP_TAILCALL_MEMBASE: {
+			int branch_reg = loongarch_r21;
+			call = (MonoCallInst*)ins;
+
+			g_assert (!cfg->method->save_lmf);
+
+			max_len += call->stack_usage / sizeof (target_mgreg_t) * ins_get_size (OP_TAILCALL_PARAMETER);
+			while (G_UNLIKELY (offset + max_len > cfg->code_size)) {
+				cfg->code_size *= 2;
+				cfg->native_code = (unsigned char *)mono_realloc_native_code (cfg);
+				code = cfg->native_code + offset;
+				cfg->stat_code_reallocs++;
+			}
+
+			switch (ins->opcode) {
+			case OP_TAILCALL:
+				break;
+
+			case OP_TAILCALL_REG:
+				g_assert (ins->sreg1 != -1);
+				g_assert (ins->sreg1 != loongarch_r21);
+				g_assert (ins->sreg1 != loongarch_ra);
+				g_assert (ins->sreg1 != loongarch_sp);
+				g_assert (ins->sreg1 != loongarch_fp);
+				g_assert (ins->sreg1 != loongarch_s8);
+				if ((ins->sreg1 << 1) & MONO_ARCH_CALLEE_SAVED_REGS) {
+					loongarch_ori (code, branch_reg, ins->sreg1, 0);
+				} else {
+					branch_reg = ins->sreg1;
+				}
+				break;
+
+			case OP_TAILCALL_MEMBASE:
+				g_assert (ins->inst_basereg != -1);
+				g_assert (ins->inst_basereg != loongarch_r21);
+				g_assert (ins->inst_basereg != loongarch_ra);
+				g_assert (ins->inst_basereg != loongarch_sp);
+				g_assert (ins->inst_basereg != loongarch_fp);
+				g_assert (ins->inst_basereg != loongarch_s8);
+				code = emit_ldd (code, branch_reg, ins->inst_basereg, ins->inst_offset);
+				break;
+
+			default:
+				g_assert_not_reached ();
+			}
+
+			// Copy stack arguments.
+			// FIXME a fixed size memcpy is desirable here,
+			// at least for larger values of stack_usage.
+			for (int i = 0; i < call->stack_usage; i += sizeof (target_mgreg_t)) {
+				code = emit_ldd (code, loongarch_ra, loongarch_sp, i);
+				code = emit_std (code, loongarch_ra, loongarch_s8, i);
+			}
+
+			code = mono_loongarch_emit_load_regset (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, loongarch_fp, cfg->arch.saved_gregs_offset);
+
+			/* Destroy frame */
+			code = mono_loongarch64_emit_destroy_frame (code, cfg->stack_offset);
+
+			switch (ins->opcode) {
+			case OP_TAILCALL:
+				if (cfg->compile_aot) {
+					/* This is not a PLT patch */
+					code = emit_aotconst (cfg, code, branch_reg, MONO_PATCH_INFO_METHOD_JUMP, call->method);
+				} else {
+					mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, call->method, MONO_R_LOONGARCH64_B);
+					loongarch_b (code, 0);
+					cfg->thunk_area += THUNK_SIZE;
+					break;
+				}
+				// fallthrough
+			case OP_TAILCALL_MEMBASE:
+			case OP_TAILCALL_REG:
+				loongarch_jirl (code, 0, branch_reg, 0);
+				break;
+
+			default:
+				g_assert_not_reached ();
+			}
+
+			ins->flags |= MONO_INST_GC_CALLSITE;
+			ins->backend.pc_offset = code - cfg->native_code;
+			break;
+		}
+		case OP_LOCALLOC: {
+			/* Round up ins->sreg1, loongarch_r21 ends up holding size */
+			loongarch_addid (code, loongarch_r21, ins->sreg1, MONO_ARCH_FRAME_ALIGNMENT-1);
+			loongarch_bstrinsd (code, loongarch_r21, loongarch_zero, 3, 0);
+
+			loongarch_subd (code, loongarch_sp, loongarch_sp, loongarch_r21);
+
+			if (ins->flags & MONO_INST_INIT) {
+				loongarch_beqz (code, loongarch_r21, 6);
+
+				loongarch_addd (code, loongarch_temp, loongarch_sp, loongarch_r21);
+				loongarch_std (code, loongarch_zero, loongarch_temp, 0xff0);//-16
+				loongarch_std (code, loongarch_zero, loongarch_temp, 0xff8);//-8
+				loongarch_addid (code, loongarch_r21, loongarch_r21, 0xff0);//-16
+				loongarch_bnez (code, loongarch_r21, 0x1ffffc);//-4
+			}
+			loongarch_ori (code, ins->dreg, loongarch_sp, 0);
+
+			int area_offset = cfg->param_area;
+			g_assert ((area_offset >= 0) && (area_offset < 0x7ff));
+			if (area_offset)
+				loongarch_addid (code, loongarch_sp, loongarch_sp, (-area_offset) & 0xfff);
+
+			break;
+		}
+		case OP_LOCALLOC_IMM: {
+			int imm, offset;
+
+			imm = ALIGN_TO (ins->inst_imm, MONO_ARCH_FRAME_ALIGNMENT);
+			g_assert (((imm) >= 0) && ((imm) < 0xfff));
+			loongarch_addid (code, loongarch_sp, loongarch_sp, -imm);
+
+			/* Init */
+			g_assert (MONO_ARCH_FRAME_ALIGNMENT == 16);
+			offset = 0;
+			while (offset < imm) {
+				loongarch_std (code, loongarch_zero, loongarch_sp, offset);
+				loongarch_std (code, loongarch_zero, loongarch_sp, (offset+0x8));
+				offset += 16;
+			}
+			loongarch_ori (code, ins->dreg, loongarch_sp, 0);
+
+			int area_offset = cfg->param_area;
+			g_assert ((area_offset >= 0) && (area_offset < 0x7ff));
+			if (area_offset)
+				loongarch_addid (code, loongarch_sp, loongarch_sp, (-area_offset) & 0xfff);
+			break;
+		}
+			/* EH */
+		case OP_LA_COND_EXC_IC:
+		case OP_LA_COND_EXC_IOV:
+		case OP_LA_COND_EXC_INC:
+		case OP_LA_COND_EXC_INO:
+		case OP_LA_COND_EXC_IEQ:
+		case OP_LA_COND_EXC_INE_UN:
+		case OP_LA_COND_EXC_ILT_UN:
+		case OP_LA_COND_EXC_ILT:
+		case OP_LA_COND_EXC_IGT:
+		case OP_LA_COND_EXC_IGT_UN:
+		case OP_LA_COND_EXC_IGE:
+		case OP_LA_COND_EXC_IGE_UN:
+		case OP_LA_COND_EXC_ILE:
+		case OP_LA_COND_EXC_ILE_UN: {
+			gint src1;
+			gint src2;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			src1 = ins->sreg1;
+			src2 = ins->sreg2;
+			gint cond = opcode_to_loongarchcond (ins->opcode, &src1, &src2);
+			/* Capture PC */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, (const char*)ins->inst_p1, MONO_R_LOONGARCH64_BC);
+			loongarch_format_2rui (code, cond, 0, src1, src2);
+			break;
+		}
+		case OP_LA_COND_EXC_C:
+		case OP_LA_COND_EXC_OV:
+		case OP_LA_COND_EXC_NC:
+		case OP_LA_COND_EXC_NO:
+		case OP_LA_COND_EXC_EQ:
+		case OP_LA_COND_EXC_NE_UN:
+		case OP_LA_COND_EXC_LT:
+		case OP_LA_COND_EXC_LT_UN:
+		case OP_LA_COND_EXC_GT:
+		case OP_LA_COND_EXC_GT_UN:
+		case OP_LA_COND_EXC_GE:
+		case OP_LA_COND_EXC_GE_UN:
+		case OP_LA_COND_EXC_LE:
+		case OP_LA_COND_EXC_LE_UN: {
+			gint src1;
+			gint src2;
+			src1 = ins->sreg1;
+			src2 = ins->sreg2;
+			gint cond = opcode_to_loongarchcond (ins->opcode, &src1, &src2);
+			/* Capture PC */
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, (const char*)ins->inst_p1, MONO_R_LOONGARCH64_BC);
+			loongarch_format_2rui (code, cond, 0, src1, src2);
+			break;
+		}
+		case OP_THROW: {
+			if (ins->sreg1 != loongarch_a0)
+				loongarch_ori (code, loongarch_a0, ins->sreg1, 0);
+			code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID,
+							  GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_throw_exception));
+			break;
+		}
+		case OP_RETHROW: {
+			if (ins->sreg1 != loongarch_a0)
+				loongarch_ori (code, loongarch_a0, ins->sreg1, 0);
+			code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID,
+							  GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_rethrow_exception));
+			break;
+		}
+		case OP_START_HANDLER: {
+			/*
+			 * The START_HANDLER instruction marks the beginning of
+			 * a handler block. It is called using a call
+			 * instruction, so loongarch_ra contains the return address.
+			 * Since the handler executes in the same stack frame
+			 * as the method itself, we can't use save/restore to
+			 * save the return address. Instead, we save it into
+			 * a dedicated variable.
+			 */
+			MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
+
+			if (loongarch_is_imm12 (spvar->inst_offset)) {
+				loongarch_std (code, loongarch_ra, spvar->inst_basereg, spvar->inst_offset);
+			} else {
+				target_mgreg_t tmp_off = spvar->inst_offset;
+				g_assert ((-0x80000000L <= tmp_off) && (tmp_off < 0x7fffffffL));
+				g_assert (!(3 & tmp_off));
+				tmp_off += (tmp_off & 0x8000);
+				loongarch_addu16id (code, loongarch_r21, spvar->inst_basereg, (tmp_off >> 16) & 0xffff);
+				tmp_off = spvar->inst_offset & 0xffff;
+				tmp_off -= ((tmp_off & 0x8000) << 1);
+				loongarch_stptrd (code, loongarch_ra, loongarch_r21, ((tmp_off >> 2) & 0x3fff));
+			}
+			/*
+			 * Reserve a param area, see test_0_finally_param_area ().
+			 * This is needed because the param area is not set up when
+			 * we are called from EH code.
+			 */
+			if (cfg->param_area) {
+				g_assert ((cfg->param_area > 0) && (cfg->param_area < 0x7fffffff));
+				g_assert (!(cfg->param_area & 0xf));
+				if (cfg->param_area < 0x7ff) {
+					loongarch_addid (code, loongarch_sp, loongarch_sp, -cfg->param_area & 0xfff);
+				} else {
+					loongarch_lu12iw (code, loongarch_r21, (cfg->param_area >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, cfg->param_area & 0xfff);
+					loongarch_subd (code, loongarch_sp, loongarch_sp, loongarch_r21);
+				}
+			}
+			break;
+		}
+		case OP_ENDFINALLY:
+		case OP_ENDFILTER: {
+			MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
+
+			g_assert ((cfg->param_area >= 0) && (cfg->param_area < 0x7ff));
+			if (cfg->param_area)
+				loongarch_addid (code, loongarch_sp, loongarch_sp, cfg->param_area & 0xfff);
+
+			if (ins->opcode == OP_ENDFILTER && ins->sreg1 != loongarch_a0)
+				loongarch_ori (code, loongarch_a0, ins->sreg1, 0);
+
+			/* Return to either after the branch in OP_CALL_HANDLER, or to the EH code */
+			code = emit_ldd (code, loongarch_ra, spvar->inst_basereg, spvar->inst_offset);
+			loongarch_jirl (code, 0, loongarch_ra, 0);
+			break;
+		}
+		case OP_GET_EX_OBJ:
+			if (ins->dreg != loongarch_a0)
+				loongarch_ori (code, ins->dreg, loongarch_a0, 0);
+			break;
+		case OP_CALL_HANDLER:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_LOONGARCH64_BL);
+			loongarch_bl (code, 0);
+			cfg->thunk_area += THUNK_SIZE;
+			for (GList *tmp = ins->inst_eh_blocks; tmp != bb->clause_holes; tmp = tmp->prev)
+				mono_cfg_add_try_hole (cfg, ((MonoLeaveClause *) tmp->data)->clause, code, bb);
+
+			break;
+		case OP_BR:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_LOONGARCH64_B);
+			loongarch_b (code, 0);
+			break;
+		case OP_BR_REG:
+			loongarch_jirl (code, 0, ins->sreg1, 0);
+			break;
+		case OP_RCNEQ:
+			loongarch_fcmpcnes (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_RCNEQ_UN:
+			loongarch_fcmpcunes (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCLE:
+			loongarch_fcmpcles (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_RCLE_UN:
+			loongarch_fcmpcules (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCGE:
+			loongarch_fcmpcles (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_RCGE_UN:
+			loongarch_fcmpcules (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCNEQ:
+			loongarch_fcmpcuned (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_FCNEQ_UN:
+			loongarch_fcmpcuned (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCLE:
+			loongarch_fcmpcled (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_FCLE_UN:
+			loongarch_fcmpculed (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCGE:
+			loongarch_fcmpcled (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_LA_FCGE_UN:
+			loongarch_fcmpculed (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+
+		case OP_LA_SLTI:
+			if (loongarch_is_imm12 (ins->inst_imm)) {
+				loongarch_slti (code, ins->dreg, ins->sreg1, ins->inst_imm & 0xfff);
+			} else {
+				g_assert_not_reached ();
+			}
+			break;
+		case OP_LA_SLTUI:
+			if (loongarch_is_imm12 (ins->inst_imm)) {
+				loongarch_sltui (code, ins->dreg, ins->sreg1, ins->inst_imm & 0xfff);
+			} else {
+				g_assert_not_reached ();
+			}
+			break;
+
+		case OP_LA_CEQ:
+		case OP_LA_ICEQ:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_sltui (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_ICNEQ:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_sltui (code, ins->dreg, ins->dreg, 1);
+			loongarch_xori (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_CLT:
+		case OP_LA_ICLT:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_slt (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LA_CLT_UN:
+		case OP_LA_ICLT_UN:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_sltu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LA_ICLE:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_slt (code, ins->dreg, ins->sreg2, ins->sreg1);
+			loongarch_xori (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_ICLE_UN:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_sltu (code, ins->dreg, ins->sreg2, ins->sreg1);
+			loongarch_xori (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_CGT:
+		case OP_LA_ICGT:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_slt (code, ins->dreg, ins->sreg2, ins->sreg1);
+			break;
+		case OP_LA_CGT_UN:
+		case OP_LA_ICGT_UN:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_sltu (code, ins->dreg, ins->sreg2, ins->sreg1);
+			break;
+		case OP_LA_ICGE:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_slt (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_xori (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_ICGE_UN:
+			//NOTE:sreg1 and sreg2 should had been sign-extented.;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			loongarch_sltu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_xori (code, ins->dreg, ins->dreg, 1);
+			break;
+
+		case OP_LA_LCEQ:
+			loongarch_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+			loongarch_sltui (code, ins->dreg, ins->dreg, 1);
+			break;
+		case OP_LA_LCLT:
+			loongarch_slt (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LA_LCLT_UN:
+			loongarch_sltu (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_LA_LCGT:
+			loongarch_slt (code, ins->dreg, ins->sreg2, ins->sreg1);
+			break;
+		case OP_LA_LCGT_UN:
+			loongarch_sltu (code, ins->dreg, ins->sreg2, ins->sreg1);
+			break;
+
+		case OP_RCEQ:
+			loongarch_fcmpceqs (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCLT:
+			loongarch_fcmpclts (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCLT_UN:
+			loongarch_fcmpcults (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCGT:
+			loongarch_fcmpclts (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_RCGT_UN:
+			loongarch_fcmpcults (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCEQ:
+			loongarch_fcmpceqd (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCLT:
+			loongarch_fcmpcltd (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCLT_UN:
+			loongarch_fcmpcultd (code, 1, ins->sreg1, ins->sreg2);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCGT:
+			loongarch_fcmpcltd (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+		case OP_FCGT_UN:
+			loongarch_fcmpcultd (code, 1, ins->sreg2, ins->sreg1);//cc=1
+			loongarch_movcf2gr (code, ins->dreg, 1);
+			break;
+
+		/* floating point opcodes */
+		case OP_R8CONST:
+		{
+			guint64 imm = *(guint64*)ins->inst_p0;
+			if (imm == 0) {
+				loongarch_movgr2frd (code, ins->dreg, loongarch_zero);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (imm >> 12) & 0xfffff);
+				loongarch_lu32id (code, loongarch_r21, (imm >> 32) & 0xfffff);
+				loongarch_lu52id (code, loongarch_r21, loongarch_r21, (imm >> 52) & 0xfff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, imm & 0xfff);
+				loongarch_movgr2frd (code, ins->dreg, loongarch_r21);
+			}
+			break;
+		}
+		case OP_R4CONST:
+		{
+			guint32 imm = *(guint32*)ins->inst_p0;
+			if (imm == 0) {
+				loongarch_movgr2frd (code, ins->dreg, loongarch_zero);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (imm >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, imm & 0xfff);
+				if (cfg->r4fp) {
+					loongarch_movgr2frd (code, ins->dreg, loongarch_r21);
+				} else {
+					loongarch_movgr2frd (code, ins->dreg, loongarch_r21);
+					loongarch_fcvtds (code, ins->dreg, ins->dreg);
+				}
+			}
+			break;
+		}
+		case OP_STORER8_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_fstd (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_fstxd (code, ins->sreg1, loongarch_r21, ins->inst_destbasereg);
+			}
+			break;
+		case OP_LOADR8_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				loongarch_fldd (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+				loongarch_fldxd (code, ins->dreg, loongarch_r21, ins->inst_basereg);
+			}
+			break;
+		case OP_STORER4_MEMBASE_REG:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				if (cfg->r4fp) {
+					loongarch_fsts (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
+				} else {
+					/* Convert to single-float precision */
+					loongarch_fcvtsd (code, loongarch_ftemp, ins->sreg1);
+					loongarch_fsts (code, loongarch_ftemp, ins->inst_destbasereg, ins->inst_offset);
+				}
+			} else {
+				if (cfg->r4fp) {
+					loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+					loongarch_fstxs (code, ins->sreg1, ins->inst_destbasereg, loongarch_r21);
+				} else {
+					/* Convert to single-float precision */
+					loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+					loongarch_fcvtsd (code, loongarch_ftemp, ins->sreg1);
+					loongarch_fstxs (code, loongarch_ftemp, ins->inst_destbasereg, loongarch_r21);
+				}
+			}
+			break;
+		case OP_LOADR4_MEMBASE:
+			if (loongarch_is_imm12 (ins->inst_offset)) {
+				if (cfg->r4fp) {
+					loongarch_flds (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+				} else {
+					loongarch_flds (code, ins->dreg, ins->inst_basereg, ins->inst_offset & 0xfff);
+					/* Convert to double precision in place */
+					loongarch_fcvtds (code, ins->dreg, ins->dreg);
+				}
+			} else {
+				if (cfg->r4fp) {
+					loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+					loongarch_fldxs (code, ins->dreg, ins->inst_basereg, loongarch_r21);
+				} else {
+					/* Convert to single-float precision */
+					loongarch_lu12iw (code, loongarch_r21, (ins->inst_offset >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, ins->inst_offset & 0xfff);
+					loongarch_fldxs (code, ins->dreg, ins->inst_basereg, loongarch_r21);
+					/* Convert to double precision in place */
+					loongarch_fcvtds (code, ins->dreg, ins->dreg);
+				}
+			}
+			break;
+		case OP_LOADR4_MEMINDEX:
+			if (cfg->r4fp) {
+				loongarch_fldxs (code, ins->dreg, ins->inst_basereg, ins->sreg2);
+			} else {
+				loongarch_fldxs (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
+				/* Convert to double precision in place */
+				loongarch_fcvtds (code, ins->dreg, ins->dreg);
+			}
+			break;
+		case OP_LOADR8_MEMINDEX:
+			loongarch_fldxd (code, ins->dreg, ins->inst_basereg, ins->sreg2);
+			break;
+		case OP_STORER4_MEMINDEX:
+			if (cfg->r4fp) {
+				loongarch_fstxs (code, ins->dreg, ins->inst_basereg, ins->sreg2);
+			} else {
+				/* Convert to single-float precision */
+				loongarch_fcvtsd (code, loongarch_ftemp, ins->dreg);
+				loongarch_fstxs (code, loongarch_ftemp, ins->inst_basereg, ins->sreg2);
+			}
+			break;
+		case OP_STORER8_MEMINDEX:
+			loongarch_fstxd (code, ins->dreg, ins->inst_basereg, ins->sreg2);
+			break;
+		case OP_ICONV_TO_R_UN: {
+			/* convert int to unsigned int */
+			loongarch_bstrpickd (code, loongarch_r21, ins->sreg1, 31, 0);
+			loongarch_movgr2frd (code, ins->dreg, loongarch_r21);
+			/* convert unsigned int to double */
+			loongarch_ffintdl (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_LCONV_TO_R_UN:
+			loongarch_movgr2frd (code, loongarch_ftemp, ins->sreg1);
+			loongarch_bge (code, ins->sreg1, loongarch_zero, 4);
+			loongarch_andi (code, loongarch_r21, ins->sreg1, 0x1);
+			loongarch_srlid (code, ins->sreg1, ins->sreg1, 0x1);
+			loongarch_or (code, ins->sreg1, ins->sreg1, loongarch_r21);
+			loongarch_movgr2frd (code, ins->dreg, ins->sreg1);
+			loongarch_ffintdl (code, ins->dreg, ins->dreg);
+			loongarch_movfr2grd (code, ins->sreg1, loongarch_ftemp);
+			loongarch_bge (code, ins->sreg1, loongarch_zero, 3);
+			loongarch_fmovd (code, loongarch_ftemp, ins->dreg);
+			loongarch_faddd (code, ins->dreg, loongarch_ftemp, ins->dreg);
+			break;
+		case OP_ICONV_TO_R4:
+			if (cfg->r4fp) {
+				loongarch_movgr2frw (code, ins->dreg, ins->sreg1);
+				loongarch_ffintsw (code, ins->dreg, ins->dreg);
+			} else {
+				loongarch_movgr2frw (code, ins->dreg, ins->sreg1);
+				loongarch_ffintsw (code, ins->dreg, ins->dreg);
+				loongarch_fcvtds (code, ins->dreg, ins->dreg);
+			}
+			break;
+		case OP_LCONV_TO_R4:
+			if (cfg->r4fp) {
+				loongarch_movgr2frd (code, ins->dreg, ins->sreg1);
+				loongarch_ffintsl (code, ins->dreg, ins->dreg);
+			} else {
+				loongarch_movgr2frd (code, ins->dreg, ins->sreg1);
+				loongarch_ffintsl (code, ins->dreg, ins->dreg);
+				loongarch_fcvtds (code, ins->dreg, ins->dreg);
+			}
+			break;
+		case OP_ICONV_TO_R8:
+			loongarch_movgr2frw (code, ins->dreg, ins->sreg1);
+			loongarch_ffintdw (code, ins->dreg, ins->dreg);
+			break;
+		case OP_LCONV_TO_R8:
+			loongarch_movgr2frd (code, ins->dreg, ins->sreg1);
+			loongarch_ffintdl (code, ins->dreg, ins->dreg);
+			break;
+		case OP_FCONV_TO_I1:
+			loongarch_ftintrzld (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_extwb (code, ins->dreg, ins->dreg);
+			break;
+		case OP_FCONV_TO_U1:
+			loongarch_lu52id (code, loongarch_r21, loongarch_zero, 0x43e);
+			loongarch_movgr2frd (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpcltd (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubd (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzld (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 0x7, 0x0);
+			break;
+		case OP_FCONV_TO_I2:
+			loongarch_ftintrzld (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_extwh (code, ins->dreg, ins->dreg);
+			break;
+		case OP_FCONV_TO_U2:
+			loongarch_lu52id (code, loongarch_r21, loongarch_zero, 0x43e);
+			loongarch_movgr2frd (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpcltd (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubd (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzld (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 15, 0);
+			break;
+		case OP_FCONV_TO_I4:
+		case OP_FCONV_TO_I:
+			loongarch_ftintrzld (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_slliw (code, ins->dreg, ins->dreg, 0);
+			break;
+		case OP_FCONV_TO_U4:
+			loongarch_lu52id (code, loongarch_r21, loongarch_zero, 0x41e);
+			loongarch_movgr2frd (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpcltd (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubd (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_slliw (code, loongarch_r21, loongarch_r21, 31);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzwd (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 31, 0);
+			break;
+		case OP_FCONV_TO_I8:
+			loongarch_ftintrzld (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			break;
+		case OP_FCONV_TO_U8:
+			loongarch_lu52id (code, loongarch_r21, loongarch_zero, 0x43e);
+			loongarch_movgr2frd (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpcltd (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubd (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzld (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			break;
+		case OP_FCONV_TO_R4:
+			if (cfg->r4fp) {
+				loongarch_fcvtsd (code, ins->dreg, ins->sreg1);
+			} else {
+				loongarch_fcvtsd (code, loongarch_ftemp, ins->sreg1);
+				loongarch_fcvtds (code, ins->dreg, loongarch_ftemp);
+			}
+			break;
+		case OP_SQRT:
+			if (ins->type == STACK_R4)
+				loongarch_fsqrts (code, ins->dreg, ins->sreg1);
+			else
+				loongarch_fsqrtd (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_FADD:
+			loongarch_faddd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_FSUB:
+			loongarch_fsubd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_FMUL:
+			loongarch_fmuld (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_FDIV:
+			loongarch_fdivd (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_FNEG:
+			loongarch_fnegd (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_LA_SETFREG_R4:
+			loongarch_fcvtsd (code, ins->dreg, ins->sreg1);
+			break;
+
+			/* R4 */
+		case OP_RADD:
+			loongarch_fadds (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_RSUB:
+			loongarch_fsubs (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_RMUL:
+			loongarch_fmuls (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_RDIV:
+			loongarch_fdivs (code, ins->dreg, ins->sreg1, ins->sreg2);
+			break;
+		case OP_RNEG:
+			loongarch_fnegs (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_RCONV_TO_I1:
+			loongarch_ftintrzls (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_extwb (code, ins->dreg, ins->dreg);
+			break;
+		case OP_RCONV_TO_U1:
+			loongarch_lu12iw (code, loongarch_r21, 0x5f000);
+			loongarch_movgr2frw (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpclts (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubs (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzls (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 0x7, 0x0);
+			break;
+		case OP_RCONV_TO_I2:
+			loongarch_ftintrzls (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_extwh (code, ins->dreg, ins->dreg);
+			break;
+        case OP_RCONV_TO_U2:
+			loongarch_lu12iw (code, loongarch_r21, 0x5f000);
+			loongarch_movgr2frw (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpclts (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubs (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzls (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 15, 0);
+			break;
+		case OP_RCONV_TO_I:
+		case OP_RCONV_TO_I4:
+			loongarch_ftintrzls (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grs (code, ins->dreg, loongarch_ftemp);
+			loongarch_slliw (code, ins->dreg, ins->dreg, 0);
+			break;
+		case OP_RCONV_TO_U4:
+			loongarch_lu12iw (code, loongarch_r21, 0x5f000);
+			loongarch_movgr2frw (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpclts (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubs (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzls (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			loongarch_bstrpickd (code, ins->dreg, ins->dreg, 31, 0);
+			break;
+		case OP_RCONV_TO_I8:
+			loongarch_ftintrzls (code, loongarch_ftemp, ins->sreg1);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			break;
+		case OP_RCONV_TO_U8:
+			loongarch_lu12iw (code, loongarch_r21, 0x5f000);
+			loongarch_movgr2frw (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fcmpclts (code, 0x1, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x0);
+			loongarch_bcnez (code, 0x1, 0x4);
+			loongarch_fsubs (code, loongarch_ftemp, ins->sreg1, loongarch_ftemp);
+			loongarch_ori (code, loongarch_r21, loongarch_zero, 0x1);
+			loongarch_sllid (code, loongarch_r21, loongarch_r21, 63);
+			loongarch_fsel (code, loongarch_ftemp, loongarch_ftemp, ins->sreg1, 0x1);
+			loongarch_ftintrzls (code, loongarch_ftemp, loongarch_ftemp);
+			loongarch_movfr2grd (code, ins->dreg, loongarch_ftemp);
+			loongarch_or (code, ins->dreg, loongarch_r21, ins->dreg);
+			break;
+		case OP_RCONV_TO_R8:
+			loongarch_fcvtds (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_RCONV_TO_R4:
+			if (ins->dreg != ins->sreg1)
+				loongarch_fmovs (code, ins->dreg, ins->sreg1);
+			break;
+
+		case OP_CKFINITE: {
+			/* Check for infinity */
+			loongarch_addid (code, loongarch_r21, loongarch_zero, 0xfff);
+			loongarch_lu52id (code, loongarch_r21, loongarch_r21, 0x7fe);
+
+			loongarch_movgr2frd (code, loongarch_ftemp, loongarch_r21);
+			loongarch_fabsd (code, loongarch_ftemp2, ins->sreg1);
+			loongarch_fcmpcltd (code, 1, loongarch_ftemp, loongarch_ftemp2);//cc=1
+
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "ArithmeticException", MONO_R_LOONGARCH64_BZ);
+			loongarch_bcnez (code, 1, 0);//cc=1
+
+			/* Check for nans */
+			loongarch_fcmpcund (code, 1, loongarch_ftemp2, loongarch_ftemp2);//cc=1
+			loongarch_pcaddi (code, loongarch_t0, 1);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "ArithmeticException", MONO_R_LOONGARCH64_BZ);
+			loongarch_bcnez (code, 1, 0);//cc=1
+			loongarch_fmovd (code, ins->dreg, ins->sreg1);
+			break;
+		}
+		case OP_JUMP_TABLE:
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0, MONO_R_LOONGARCH64_J);
+			loongarch_lu12iw (code, ins->dreg, 0);
+			loongarch_lu32id (code, ins->dreg, 0);
+			loongarch_lu52id (code, ins->dreg, ins->dreg, 0);
+			loongarch_ori (code, ins->dreg, ins->dreg, 0);
+			break;
+		case OP_LIVERANGE_START: {
+			if (cfg->verbose_level > 1)
+				printf ("R%d START=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code));
+			MONO_VARINFO (cfg, ins->inst_c0)->live_range_start = code - cfg->native_code;
+			break;
+		}
+		case OP_LIVERANGE_END: {
+			if (cfg->verbose_level > 1)
+				printf ("R%d END=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code));
+			MONO_VARINFO (cfg, ins->inst_c0)->live_range_end = code - cfg->native_code;
+			break;
+		}
+		case OP_GC_SAFE_POINT: {
+			guint8 *buf [1];
+			loongarch_ldd (code, loongarch_r21, ins->sreg1, 0);
+			buf [0] = code;
+			/* Call it if it is non-null */
+			loongarch_beqz (code, loongarch_r21, 0);
+			mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_threads_state_poll), MONO_R_LOONGARCH64_BL);
+			cfg->thunk_area += THUNK_SIZE;
+			loongarch_bl (code, 0);
+			mono_loongarch64_patch (buf [0], code, MONO_R_LOONGARCH64_BZ);
+			break;
+		}
+			break;
+		case OP_FILL_PROF_CALL_CTX:
+			for (int i = 0; i < MONO_MAX_IREGS; i++)
+				if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) || i == loongarch_sp || i == loongarch_fp)
+					loongarch_std (code, i, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, regs) + i * sizeof (target_mgreg_t));
+			break;
+
+		case OP_LA_IBEQ:
+		case OP_LA_IBGE:
+		case OP_LA_IBGT:
+		case OP_LA_IBLE:
+		case OP_LA_IBLT:
+		case OP_LA_IBNE_UN:
+		case OP_LA_IBGE_UN:
+		case OP_LA_IBGT_UN:
+		case OP_LA_IBLE_UN:
+		case OP_LA_IBLT_UN: {
+			gint src1;
+			gint src2;
+			gint cond;
+			loongarch_slliw (code, ins->sreg1, ins->sreg1, 0x0);
+			loongarch_slliw (code, ins->sreg2, ins->sreg2, 0x0);
+			src1 = ins->sreg1;
+			src2 = ins->sreg2;
+			mono_add_patch_info_rel (cfg, offset + 8, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_LOONGARCH64_BC);
+			cond = opcode_to_loongarchcond (ins->opcode, &src1, &src2);
+			loongarch_format_2rui (code, cond, 0, src1, src2);
+			break;
+		}
+		case OP_LA_LBEQ:
+		case OP_LA_LBGE:
+		case OP_LA_LBGT:
+		case OP_LA_LBLE:
+		case OP_LA_LBLT:
+		case OP_LA_LBNE_UN:
+		case OP_LA_LBGE_UN:
+		case OP_LA_LBGT_UN:
+		case OP_LA_LBLE_UN:
+		case OP_LA_LBLT_UN: {
+			gint src1;
+			gint src2;
+			gint cond;
+			src1 = ins->sreg1;
+			src2 = ins->sreg2;
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_LOONGARCH64_BC);
+			cond = opcode_to_loongarchcond (ins->opcode, &src1, &src2);
+			loongarch_format_2rui (code, cond, 0, src1, src2);
+			break;
+		}
+		case OP_FBEQ:
+		case OP_FBNE_UN:
+		case OP_FBLT:
+		case OP_FBLT_UN:
+		case OP_FBGT:
+		case OP_FBGT_UN:
+		case OP_FBLE:
+		case OP_FBLE_UN:
+		case OP_FBGE:
+		case OP_FBGE_UN: {
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_LOONGARCH64_BZ);
+			loongarch_bcnez (code, 1, 0);
+			break;
+		}
+		case OP_LA_LBEQZ:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_LOONGARCH64_BZ);
+			loongarch_beqz (code, ins->sreg1, 0);
+			break;
+		case OP_LA_LBNEZ:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_LOONGARCH64_BZ);
+			loongarch_bnez (code, ins->sreg1, 0);
+			break;
+
+		default:
+			g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
+			g_assert_not_reached ();
+		}
+
+		if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
+			g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
+				   mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
+			g_assert_not_reached ();
+		}
+
+		cpos += max_len;
+
+		last_ins = ins;
+	}
+
+	set_code_cursor (cfg, code);
+	/*
+	 * If the compiled code size is larger than the bcc displacement (16 bits signed),
+	 * insert branch islands between/inside basic blocks.
+	 */
+	if (cfg->arch.cond_branch_islands > 0)
+		code = emit_branch_island (cfg, code, start_offset);
+}
+
+/*
+ * emit_store_regarray:
+ *
+ *   Emit code to store the registers in REGS into the appropriate elements of
+ * the register array at BASEREG+OFFSET.
+ */
+__attribute__ ((__warn_unused_result__)) guint8*
+mono_loongarch_emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i;
+
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if (i == loongarch_sp) {
+				loongarch_ori (code, loongarch_t0, loongarch_sp, 0);
+				loongarch_std (code, loongarch_t0, basereg, offset + (i * 8));
+			} else {
+				loongarch_std (code, i, basereg, offset + (i * 8));
+			}
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_load_regarray:
+ *
+ *   Emit code to load the registers in REGS from the appropriate elements of
+ * the register array at BASEREG+OFFSET.
+ */
+__attribute__ ((__warn_unused_result__)) guint8*
+mono_loongarch_emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i;
+
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			loongarch_ldd (code, i, basereg, offset + (i * 8));
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_store_regset:
+ *
+ *   Emit code to store the registers in REGS into consecutive memory locations starting
+ * at BASEREG+OFFSET.
+ */
+__attribute__ ((__warn_unused_result__)) guint8*
+mono_loongarch_emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i, pos;
+
+	pos = 0;
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if (i == loongarch_sp) {
+				loongarch_ori (code, loongarch_t0, loongarch_sp, 0);
+				loongarch_std (code, loongarch_t0, basereg, offset + (pos * 8));
+			} else {
+				loongarch_std (code, i, basereg, offset + (pos * 8));
+			}
+			pos++;
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_load_regset:
+ *
+ *   Emit code to load the registers in REGS from consecutive memory locations starting
+ * at BASEREG+OFFSET.
+ */
+__attribute__ ((__warn_unused_result__)) guint8*
+mono_loongarch_emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i, pos;
+
+	pos = 0;
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if (i == loongarch_sp) {
+				g_assert_not_reached ();
+			} else {
+				loongarch_ldd (code, i, basereg, offset + (pos * 8));
+			}
+			pos++;
+		}
+	}
+	return code;
+}
+
+/*
+ * Stack frame layout:
+ * (Hight-addr)
+ *      param area		 incoming arguments.
+ *   ------------------- sp + cfg->stack_usage + cfg->param_area
+ *   ------------------- //sp + cfg->arch.iregs_offset     ,
+ *   	locals
+ *   ------------------- //sp + cfg->arch.lmf_offset       ,
+ *   	saved registers		s0-s8,fs0-fs7,  optional
+ *   	MonoLMF structure	optional
+ *      ra-incoming
+ *      fp-incoming      <------  fp-current pointing.
+ *   ------------------- sp + cfg->param_area
+ *   	param area		 outgoing arguments.
+ *   ------------------- sp
+ * (Low-addr)
+ *   	red zone
+ */
+guint8 *
+mono_arch_emit_prolog (MonoCompile *cfg)
+{
+	MonoMethod *method = cfg->method;
+	MonoMethodSignature *sig;
+	MonoBasicBlock *bb;
+	guint8 *code;
+	int cfa_offset, max_offset;
+
+	sig = mono_method_signature_internal (method);
+	cfg->code_size = 256 + sig->param_count * 64;
+	code = cfg->native_code = g_malloc (cfg->code_size);
+
+	/* This can be unaligned */
+	cfg->stack_offset = ALIGN_TO (cfg->stack_offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	 // update the unwind info.
+	mono_emit_unwind_op_def_cfa (cfg, code, loongarch_sp, 0);
+
+	/* Setup frame */
+	cfa_offset = cfg->stack_offset;
+	if (loongarch_is_imm12 (-cfg->stack_offset)) {
+		loongarch_addid (code, loongarch_sp, loongarch_sp, -cfa_offset);
+	} else {
+		////NOTE: Assuming the stack size is less than 2G.
+		loongarch_lu12iw (code, loongarch_r21, (cfa_offset >> 12) & 0xfffff);
+		loongarch_ori (code, loongarch_r21, loongarch_r21, cfa_offset & 0xfff);
+		loongarch_subd (code, loongarch_sp, loongarch_sp, loongarch_r21);
+	}
+	mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+	loongarch_std (code, loongarch_fp, loongarch_sp, 0);
+	loongarch_std (code, loongarch_ra, loongarch_sp, 8); //will be optimized in the future.
+	mono_emit_unwind_op_offset (cfg, code, loongarch_fp, (- cfa_offset) + 0);
+	mono_emit_unwind_op_offset (cfg, code, loongarch_ra, (- cfa_offset) + 8);
+	loongarch_ori (code, loongarch_fp, loongarch_sp, 0);
+	mono_emit_unwind_op_def_cfa_reg (cfg, code, loongarch_fp);
+	if (cfg->param_area) {
+		/* The param area is below the frame pointer */
+		if (loongarch_is_imm12 (-cfg->param_area)) {
+			loongarch_addid (code, loongarch_sp, loongarch_sp, -cfg->param_area);
+		} else {
+			////NOTE: Assuming the stack size is less than 2G.
+			loongarch_lu12iw (code, loongarch_r21, (cfg->param_area >> 12) & 0xfffff);
+			loongarch_ori (code, loongarch_r21, loongarch_r21, cfg->param_area & 0xfff);
+			loongarch_subd (code, loongarch_sp, loongarch_sp, loongarch_r21);
+		}
+	}
+
+	if (cfg->method->save_lmf) {
+		/*
+		 * The LMF should contain all the state required to be able to reconstruct the machine state
+		 * at the current point of execution. Since the LMF is only read during EH, only callee
+		 * saved etc. registers need to be saved.
+		 * FIXME: Save callee saved fp regs, JITted code doesn't use them, but native code does, and they
+		 * need to be restored during EH.
+		 */
+
+		/* pc */
+		loongarch_pcaddi (code, loongarch_ra, 0);
+		int pc_offsets = cfg->lmf_var->inst_offset + MONO_STRUCT_OFFSET (MonoLMF, pc);
+		if (loongarch_is_imm12 (pc_offsets)) {
+			loongarch_std (code, loongarch_ra, loongarch_fp, pc_offsets & 0xfff);
+		} else {
+			loongarch_lu12iw (code, loongarch_r21, (pc_offsets >> 12) & 0xfffff);
+			loongarch_ori (code, loongarch_r21, loongarch_r21, pc_offsets & 0xfff);
+			loongarch_stxd (code, loongarch_ra, loongarch_fp, loongarch_r21);
+		}
+		/* gregs + fp + sp */
+		int offsets = cfg->lmf_var->inst_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs);
+		if (loongarch_is_imm12 (offsets + 80)) {
+			loongarch_addid (code, loongarch_r21, loongarch_fp, offsets & 0xfff);
+		} else {
+			loongarch_lu12iw (code, loongarch_r21, (offsets >> 12) & 0xfffff);
+			loongarch_ori (code, loongarch_r21, loongarch_r21, offsets & 0xfff);
+			loongarch_addd (code, loongarch_r21, loongarch_fp, loongarch_r21);
+		}
+		/* Don't emit unwind info for sp/fp, they are already handled in the prolog */
+		loongarch_std (code, loongarch_sp, loongarch_r21, 0);
+		loongarch_std (code, loongarch_fp, loongarch_r21, 8);
+		loongarch_std (code, loongarch_s0, loongarch_r21, 16);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s0, -cfa_offset + offsets + 16);
+		loongarch_std (code, loongarch_s1, loongarch_r21, 24);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s1, -cfa_offset + offsets + 24);
+		loongarch_std (code, loongarch_s2, loongarch_r21, 32);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s2, -cfa_offset + offsets + 32);
+		loongarch_std (code, loongarch_s3, loongarch_r21, 40);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s3, -cfa_offset + offsets + 40);
+		loongarch_std (code, loongarch_s4, loongarch_r21, 48);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s4, -cfa_offset + offsets + 48);
+		loongarch_std (code, loongarch_s5, loongarch_r21, 56);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s5, -cfa_offset + offsets + 56);
+		loongarch_std (code, loongarch_s6, loongarch_r21, 64);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s6, -cfa_offset + offsets + 64);
+		loongarch_std (code, loongarch_s7, loongarch_r21, 72);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s7, -cfa_offset + offsets + 72);
+		loongarch_std (code, loongarch_s8, loongarch_r21, 80);
+		mono_emit_unwind_op_offset (cfg, code, loongarch_s8, -cfa_offset + offsets + 80);
+	} else {
+		/* Save gregs */
+		int offsets = cfg->arch.saved_gregs_offset;
+		int i = loongarch_s0;
+		int saved_regs = (MONO_ARCH_CALLEE_SAVED_REGS & ((guint)cfg->used_int_regs)) >> loongarch_s0;
+
+		while (saved_regs) {
+			if (saved_regs & 1) {
+				if (loongarch_is_imm12 (offsets)) {
+					loongarch_std (code, i, loongarch_fp, offsets & 0xfff);
+				} else {
+					loongarch_lu12iw (code, loongarch_r21, (offsets >> 12) & 0xfffff);
+					loongarch_ori (code, loongarch_r21, loongarch_r21, offsets & 0xfff);
+					loongarch_stxd (code, i, loongarch_fp, loongarch_r21);
+				}
+				mono_emit_unwind_op_offset (cfg, code, i, -cfa_offset + offsets);
+				offsets += 8;
+			}
+			saved_regs >>= 1;
+			i++;
+		};
+	}
+
+	/* Setup args reg */
+	if (cfg->arch.args_reg) {
+		/* The register was already saved above */
+
+		if (loongarch_is_imm12 (cfg->stack_offset)) {
+			loongarch_addid (code, cfg->arch.args_reg, loongarch_fp, cfg->stack_offset & 0xfff);
+		} else {
+			////NOTE: Assuming the args_reg's size is less than 2G.
+			loongarch_lu12iw (code, loongarch_r21, (cfg->stack_offset >> 12) & 0xfffff);
+			loongarch_ori (code, loongarch_r21, loongarch_r21, cfg->stack_offset & 0xfff);
+			loongarch_addd (code, cfg->arch.args_reg, loongarch_fp, loongarch_r21);
+		}
+	}
+
+	if (cfg->vret_addr) {
+		MonoInst *ins = cfg->vret_addr;
+		g_assert (ins->opcode == OP_REGOFFSET);
+		if (sig->pinvoke) {
+			loongarch_std (code, loongarch_a0, ins->inst_basereg, ins->inst_offset);
+		} else {
+			loongarch_ldd (code, loongarch_r21, cfg->arch.args_reg, 0x0);
+			loongarch_std (code, loongarch_r21, ins->inst_basereg, ins->inst_offset);
+		}
+	}
+
+	/* Save mrgctx received in MONO_ARCH_RGCTX_REG */
+	if (cfg->rgctx_var) {
+		MonoInst *ins = cfg->rgctx_var;
+
+		g_assert (ins->opcode == OP_REGOFFSET);
+		EMIT_STORE (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset, loongarch_r21);
+
+		mono_add_var_location (cfg, cfg->rgctx_var, TRUE, MONO_ARCH_RGCTX_REG, 0, 0, code - cfg->native_code);
+		mono_add_var_location (cfg, cfg->rgctx_var, FALSE, ins->inst_basereg, ins->inst_offset, code - cfg->native_code, 0);
+	}
+
+	/*
+	 * Move arguments to their registers/stack locations.
+	 */
+	{
+		MonoInst *ins;
+		CallInfo *cinfo;
+		ArgInfo *ainfo;
+		int i;
+		guint8 field_info;
+
+		cinfo = cfg->arch.cinfo;
+		g_assert (cinfo);
+		for (i = 0; i < cinfo->nargs; ++i) {
+			ainfo = cinfo->args + i;
+			ins = cfg->args [i];
+
+			if (ins->opcode == OP_REGVAR) {
+				switch (ainfo->storage) {
+				case ArgInIReg:
+					loongarch_ori (code, ins->dreg, ainfo->reg, 0);
+					if (i == 0 && sig->hasthis) {
+						mono_add_var_location (cfg, ins, TRUE, ainfo->reg, 0, 0, code - cfg->native_code);
+						mono_add_var_location (cfg, ins, TRUE, ins->dreg, 0, code - cfg->native_code, 0);
+					}
+					break;
+				case ArgOnStack:
+					switch (ainfo->slot_size) {
+					case 1:
+						g_assert (loongarch_is_imm12 (ainfo->offset));
+						if (ainfo->sign)
+							loongarch_ldb (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						else
+							loongarch_ldbu (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						break;
+					case 2:
+						if (ainfo->sign)
+							loongarch_ldh (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						else
+							loongarch_ldhu (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						break;
+					case 4:
+						if (ainfo->sign)
+							loongarch_ldw (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						else
+							loongarch_ldwu (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						break;
+					default:
+						loongarch_ldd (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+						break;
+					}
+					break;
+				default:
+					g_assert_not_reached ();
+					break;
+				}
+			} else {
+				gboolean isR4 = ainfo->size == 4 ? TRUE : FALSE;
+				switch (ainfo->storage) {
+				case ArgInIReg:
+					/* Stack slots for arguments have size 8 */
+					loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+					if (i == 0 && sig->hasthis) {
+						mono_add_var_location (cfg, ins, TRUE, ainfo->reg, 0, 0, code - cfg->native_code);
+						mono_add_var_location (cfg, ins, FALSE, ins->inst_basereg, ins->inst_offset, code - cfg->native_code, 0);
+					}
+					break;
+				case ArgInFReg:
+					if (isR4)
+						loongarch_fsts (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+					else
+						loongarch_fstd (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+					break;
+				case ArgStructByVal:
+					field_info = ainfo->field_info;
+					int second_exist = (field_info >> 2) & 0x1;
+					int stor_type = (field_info >> 5) & 0x7;
+					int fp1_size = (field_info & 0x2) ? 8 : 4;
+					int fp2_size = (field_info & 0x10) ? 8 : 4;
+					switch (stor_type) {
+					case 0x1: //all store in the freg
+						if (second_exist) {
+							if (fp1_size == 4)
+								loongarch_fsts (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+							else
+								loongarch_fstd (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+							if (fp2_size == 4)
+								loongarch_fsts (code, ainfo->freg + 1, ins->inst_basereg, ins->inst_offset + fp1_size);
+							else
+								loongarch_fstd (code, ainfo->freg + 1, ins->inst_basereg, ins->inst_offset + 8);
+						} else {
+							if (fp1_size == 4)
+								loongarch_fsts (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+							else
+								loongarch_fstd (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+						}
+						break;
+					case 0x2: //all store in the reg
+						if (ainfo->size > 8) {
+							loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+							loongarch_std (code, ainfo->reg + 1, ins->inst_basereg, ins->inst_offset + 8);
+						} else {
+							loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+						}
+						break;
+					case 0x3: //all store in stack
+						break;
+					case 0x4: //one store in reg another in stack
+						loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+						loongarch_ldd (code, ainfo->reg, cfg->arch.args_reg, ainfo->offset); //from caller stack to callee stack
+						loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset + 8);
+						break;
+					case 0x5: //one store in reg another in freg
+						if (field_info & 0x1) {
+							if (fp1_size == 4)
+								loongarch_fsts (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+							else
+								loongarch_fstd (code, ainfo->freg, ins->inst_basereg, ins->inst_offset);
+							if (fp2_size == 4)
+								loongarch_stw (code, ainfo->reg, ins->inst_basereg, ins->inst_offset + fp1_size);
+							else
+								loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset + 8);
+						} else {
+							if (fp1_size == 4)
+								loongarch_stw (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+							else
+								loongarch_std (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+							if (fp2_size == 4)
+								loongarch_fsts (code, ainfo->freg, ins->inst_basereg, ins->inst_offset + fp1_size);
+							else
+								loongarch_fstd (code, ainfo->freg, ins->inst_basereg, ins->inst_offset + 8);
+						}
+						break;
+					default:
+						g_assert_not_reached ();
+						break;
+				}
+					break;
+				case ArgStructByRef: {
+					if (((ainfo->field_info >> 5) & 0x7) == 3)
+						continue;
+					MonoInst *addr_arg = ins->inst_left;
+					g_assert (ins->opcode == OP_VTARG_ADDR);
+					g_assert (addr_arg->opcode == OP_REGOFFSET);
+					loongarch_std (code, ainfo->reg, addr_arg->inst_basereg, addr_arg->inst_offset);
+					break;
+				}
+				case ArgOnStack:
+					break;
+				default:
+					g_assert_not_reached ();
+					break;
+				}
+			}
+		}
+	}
+
+	/* Initialize seq_point_info_var */
+	if (cfg->arch.seq_point_info_var) {
+		MonoInst *ins = cfg->arch.seq_point_info_var;
+
+		/* Initialize the variable from a GOT slot */
+		code = emit_aotconst (cfg, code, loongarch_r21, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
+		g_assert (ins->opcode == OP_REGOFFSET);
+		loongarch_std (code, loongarch_r21, ins->inst_basereg, ins->inst_offset);
+
+		/* Initialize ss_tramp_var */
+		ins = cfg->arch.ss_tramp_var;
+		g_assert (ins->opcode == OP_REGOFFSET);
+
+		code = emit_ldd (code, loongarch_ra, loongarch_r21, MONO_STRUCT_OFFSET (SeqPointInfo, ss_tramp_addr));
+		code = emit_std (code, loongarch_ra, ins->inst_basereg, ins->inst_offset);
+	} else {
+		MonoInst *ins;
+		if (cfg->arch.ss_tramp_var) {
+			/* Initialize ss_tramp_var */
+			ins = cfg->arch.ss_tramp_var;
+			g_assert (ins->opcode == OP_REGOFFSET);
+
+			code = mono_loongarch_emit_imm64 (code, loongarch_r21, (guint64)&ss_trampoline);
+			code = emit_std (code, loongarch_r21, ins->inst_basereg, ins->inst_offset);
+		}
+
+		if (cfg->arch.bp_tramp_var) {
+			/* Initialize bp_tramp_var */
+			ins = cfg->arch.bp_tramp_var;
+			g_assert (ins->opcode == OP_REGOFFSET);
+
+			code = mono_loongarch_emit_imm64 (code, loongarch_r21, (guint64)bp_trampoline);
+			loongarch_std (code, loongarch_r21, ins->inst_basereg, ins->inst_offset);
+		}
+	}
+
+	max_offset = 0;
+	if (cfg->opt & MONO_OPT_BRANCH) {
+		for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+			MonoInst *ins;
+			bb->max_offset = max_offset;
+
+			MONO_BB_FOR_EACH_INS (bb, ins) {
+				max_offset += ins_get_size (ins->opcode);
+			}
+		}
+	}
+	if (max_offset <= 0x7fff * 4) {
+		cfg->arch.cond_branch_islands = 0;
+	} else if (max_offset > 0x7fff * 4 && max_offset <= 0xfffff * 4) {
+		cfg->arch.cond_branch_islands = 1;
+	} else {
+		cfg->arch.cond_branch_islands = 2;
+	}
+	return code;
+}
+
+void
+mono_arch_emit_epilog (MonoCompile *cfg)
+{
+	CallInfo *cinfo;
+	guint8 *code;
+	int max_epilog_size;
+	int offsets;
+	guint saved_regs =(MONO_ARCH_CALLEE_SAVED_REGS & (guint)cfg->used_int_regs) >> MONO_ARCH_FIRST_LMF_REG;
+
+	max_epilog_size = 20*4;//now the max size is 20*4.
+	code = realloc_code (cfg, max_epilog_size);
+
+	if (cfg->method->save_lmf) {
+		offsets = cfg->lmf_var->inst_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs) + 16;
+	} else {
+		/* Restore gregs */
+		offsets = cfg->arch.saved_gregs_offset;
+	}
+	g_assert ((offsets & 0x7) == 0);
+
+	int i = loongarch_s0;
+	while (saved_regs) {
+		if (saved_regs & 1) {
+			if (loongarch_is_imm12 (offsets)) {
+				loongarch_ldd (code, i, loongarch_fp, offsets & 0xfff);
+			} else {
+				loongarch_lu12iw (code, loongarch_r21, (offsets >> 12) & 0xfffff);
+				loongarch_ori (code, loongarch_r21, loongarch_r21, offsets & 0xfff);
+				loongarch_ldxd (code, i, loongarch_fp, loongarch_r21);
+			}
+			offsets += 8;
+		} else if (cfg->method->save_lmf) {
+			offsets += 8;
+		}
+		saved_regs >>= 1;
+		i++;
+	};
+
+	/* Load returned vtypes into registers if needed */
+	cinfo = cfg->arch.cinfo;
+	switch (cinfo->ret.storage) {
+	case ArgStructByVal: {
+		MonoInst *ins = cfg->ret;
+		int field_info = cinfo->ret.field_info;
+
+		if (field_info & 0x1f) {
+			if (field_info & 1) {
+				g_assert (cinfo->ret.freg == loongarch_f0);
+				if (field_info & 2) {
+					loongarch_fldd (code, loongarch_f0 /*cinfo->ret.freg*/, ins->inst_basereg, ins->inst_offset & 0xfff);
+					offsets = 8;
+				} else {
+					loongarch_flds (code, loongarch_f0 /*cinfo->ret.freg*/, ins->inst_basereg, ins->inst_offset & 0xfff);
+					offsets = 4;
+				}
+			} else {
+				g_assert (cinfo->ret.reg == loongarch_a0);
+				if (field_info & 2) {
+					loongarch_ldd (code, loongarch_a0 /*cinfo->ret.reg*/, ins->inst_basereg, ins->inst_offset & 0xfff);
+					offsets = 8;
+				} else {
+					loongarch_ldw (code, loongarch_a0 /*cinfo->ret.reg*/, ins->inst_basereg, ins->inst_offset & 0xfff);
+					offsets = 4;
+				}
+			}
+
+			if (field_info & 4) {
+				if (field_info & 8) {
+					i = field_info & 1 ? loongarch_f1 : loongarch_f0;
+					if (field_info & 0x10) {
+						loongarch_fldd (code, i, ins->inst_basereg, (ins->inst_offset + 8) & 0xfff);
+					} else {
+						loongarch_flds (code, i, ins->inst_basereg, (ins->inst_offset + offsets) & 0xfff);
+					}
+				} else {
+					i = field_info & 1 ? loongarch_a0 : loongarch_a1;
+					if (field_info & 0x10) {
+						loongarch_ldd (code, i, ins->inst_basereg, (ins->inst_offset + 8) & 0xfff);
+					} else {
+						loongarch_ldw (code, i, ins->inst_basereg, (ins->inst_offset + offsets) & 0xfff);
+					}
+				}
+			}
+		} else {
+			if (cinfo->ret.size > 8) {
+				loongarch_ldd (code, loongarch_a0, ins->inst_basereg, ins->inst_offset & 0xfff);
+				loongarch_ldd (code, loongarch_a1, ins->inst_basereg, (ins->inst_offset + 8) & 0xfff);
+			} else {
+				loongarch_ldd (code, loongarch_a0, ins->inst_basereg, ins->inst_offset & 0xfff);
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	/* Destroy frame */
+	code = mono_loongarch64_emit_destroy_frame (code, cfg->stack_offset);
+
+	g_assert (code - (cfg->native_code + cfg->code_len) < max_epilog_size);
+
+	loongarch_jirl (code, 0, loongarch_ra, 0);
+
+	set_code_cursor (cfg, code);
+}
+
+void
+mono_arch_emit_exceptions (MonoCompile *cfg)
+{
+	MonoJumpInfo *ji;
+	MonoClass *exc_class;
+	guint8 *code, *ip;
+	guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM];
+	guint8 exc_throw_found [MONO_EXC_INTRINS_NUM];
+	int i, id, size = 0;
+
+	for (i = 0; i < MONO_EXC_INTRINS_NUM; i++) {
+		exc_throw_pos [i] = NULL;
+		exc_throw_found [i] = 0;
+	}
+
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->type == MONO_PATCH_INFO_EXC) {
+			i = mini_exception_id_by_name ((const char*)ji->data.target);
+			if (!exc_throw_found [i]) {
+				size += 40;
+				exc_throw_found [i] = TRUE;
+			}
+		}
+	}
+
+	code = realloc_code (cfg, size);
+
+	/* Emit code to raise corlib exceptions */
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->type != MONO_PATCH_INFO_EXC)
+			continue;
+
+		ip = cfg->native_code + ji->ip.i;
+
+		id = mini_exception_id_by_name ((const char*)ji->data.target);
+
+		if (exc_throw_pos [id]) {
+			/* ip points to the bcc () in OP_COND_EXC_... */
+			mono_loongarch64_patch (ip, exc_throw_pos [id], ji->relocation);
+			ji->type = MONO_PATCH_INFO_NONE;
+			continue;
+		}
+
+		exc_throw_pos [id] = code;
+		mono_loongarch64_patch (ip, code, ji->relocation);
+
+		/* We are being branched to from the code generated by emit_cond_exc (), the pc is in ip1 */
+
+		/* r0 = type token */
+		exc_class = mono_class_load_from_name (mono_defaults.corlib, "System", ji->data.name);
+		code = mono_loongarch_emit_imm64 (code, loongarch_a0, m_class_get_type_token (exc_class) - MONO_TOKEN_TYPE_DEF);
+		/* r1 = throw ip */
+		loongarch_ori (code, loongarch_a1, loongarch_t0, 0);
+		/* Branch to the corlib exception throwing trampoline */
+		ji->ip.i = code - cfg->native_code;
+		ji->type = MONO_PATCH_INFO_JIT_ICALL_ID;
+		ji->data.jit_icall_id = MONO_JIT_ICALL_mono_arch_throw_corlib_exception;
+		ji->relocation = MONO_R_LOONGARCH64_BL;
+		loongarch_bl (code, 0);
+		cfg->thunk_area += THUNK_SIZE;
+		set_code_cursor (cfg, code);
+	}
+
+	set_code_cursor (cfg, code);
+}
+
+MonoInst*
+mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+	return NULL;
+}
+
+guint32
+mono_arch_get_patch_offset (guint8 *code)
+{
+	return 0;
+}
+
+#define ENABLE_WRONG_METHOD_CHECK 0
+#define LOONGARCH64_LOAD_SEQUENCE_LENGTH 16
+#define CMP_SIZE (LOONGARCH64_LOAD_SEQUENCE_LENGTH + 4)
+#define BR_SIZE 4
+#define LOADSTORE_SIZE 4
+#define JUMP_IMM_SIZE 16
+#define JUMP_IMM32_SIZE (LOONGARCH64_LOAD_SEQUENCE_LENGTH + 8)
+#define LOAD_CONST_SIZE 16
+#define JUMP_JR_SIZE 4
+
+/*
+ * LOCKING: called with the domain lock held
+ */
+gpointer
+mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entries, int count,
+								gpointer fail_tramp)
+{
+	int i;
+	int size = 0;
+	guint8 *code, *start, *patch;
+
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+
+		if (item->is_equals) {
+			if (item->check_target_idx) {
+				item->chunk_size += LOAD_CONST_SIZE + BR_SIZE + JUMP_JR_SIZE;
+				if (item->has_target_code) {
+					item->chunk_size += LOAD_CONST_SIZE;
+				} else {
+					if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot)) {
+						item->chunk_size += LOADSTORE_SIZE;
+					} else {
+						item->chunk_size += LOONGARCH64_LOAD_SEQUENCE_LENGTH;
+						item->chunk_size += LOADSTORE_SIZE;
+					}
+				}
+			} else {
+				if (fail_tramp) {
+					item->chunk_size += 3 * LOAD_CONST_SIZE + BR_SIZE + 2 * JUMP_JR_SIZE;
+					if (!item->has_target_code) {
+						if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot))
+							item->chunk_size -= LOONGARCH64_LOAD_SEQUENCE_LENGTH;
+						item->chunk_size += LOADSTORE_SIZE;
+					}
+				} else {
+					if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot)) {
+						item->chunk_size += LOADSTORE_SIZE;
+					} else {
+						item->chunk_size += LOONGARCH64_LOAD_SEQUENCE_LENGTH;
+						item->chunk_size += LOADSTORE_SIZE + JUMP_JR_SIZE;
+					}
+#if ENABLE_WRONG_METHOD_CHECK
+					item->chunk_size += CMP_SIZE + BR_SIZE + 4;
+#endif
+				}
+			}
+		} else {
+			item->chunk_size += LOAD_CONST_SIZE + BR_SIZE + 4;
+			imt_entries [item->check_target_idx]->compare_done = TRUE;
+		}
+		size += item->chunk_size;
+	}
+	size += LOONGARCH64_LOAD_SEQUENCE_LENGTH;
+	/* the initial load of the vtable address */
+	if (fail_tramp) {
+		code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size);
+	} else {
+		MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass);
+		code = mono_mem_manager_code_reserve (mem_manager, size);
+	}
+	start = code;
+	code = mono_loongarch_emit_imm64 (code, loongarch_t7, (gint64)(& (vtable->vtable [0])));
+
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+
+		item->code_target = code;
+		if (item->is_equals) {
+			if (item->check_target_idx) {
+				code = mono_loongarch_emit_imm64 (code, loongarch_temp, (gint64)item->key);
+				item->jmp_code = code;
+				loongarch_bne (code, loongarch_temp, MONO_ARCH_IMT_REG, 0);
+				//loongarch_nop (code);
+				if (item->has_target_code) {
+					code = mono_loongarch_emit_imm64 (code, loongarch_r21, (gint64)item->value.target_code);
+				}
+				else {
+					if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot)) {
+						loongarch_ldd (code, loongarch_r21, loongarch_t7, (sizeof (target_mgreg_t) * item->value.vtable_slot));
+					} else {
+						code = mono_loongarch_emit_imm64 (code, loongarch_r21, (sizeof (target_mgreg_t) * item->value.vtable_slot));
+						loongarch_ldxd (code, loongarch_r21, loongarch_t7, loongarch_r21);
+					}
+				}
+				loongarch_jirl (code, 0, loongarch_r21, 0);
+			} else {
+				if (fail_tramp) {
+					code = mono_loongarch_emit_imm64 (code, loongarch_temp, (gint64)item->key);
+					patch = code;
+					loongarch_bne (code, loongarch_temp, MONO_ARCH_IMT_REG, 0);
+					//loongarch_nop (code);
+					if (item->has_target_code) {
+						code = mono_loongarch_emit_imm64 (code, loongarch_r21, (gint64)item->value.target_code);
+					} else {
+						g_assert (vtable);
+						if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot)) {
+							loongarch_ldd (code, loongarch_r21, loongarch_t7, sizeof (target_mgreg_t) * item->value.vtable_slot);
+						} else {
+							code = mono_loongarch_emit_imm64 (code, loongarch_r21, sizeof (target_mgreg_t) * item->value.vtable_slot);
+							loongarch_ldxd (code, loongarch_r21, loongarch_t7, loongarch_r21);
+						}
+					}
+					loongarch_jirl (code, 0, loongarch_r21, 0);
+					mono_loongarch64_patch (patch, code, MONO_R_LOONGARCH64_BC);
+					code = mono_loongarch_emit_imm64 (code, loongarch_r21, (gint64)fail_tramp);
+					loongarch_jirl (code, 0, loongarch_r21, 0);
+					//loongarch_nop (code);
+				} else {
+					/* enable the commented code to assert on wrong method */
+#if ENABLE_WRONG_METHOD_CHECK
+					ppc_load (code, ppc_r0, (guint32)item->key);
+					ppc_compare_log (code, 0, MONO_ARCH_IMT_REG, ppc_r0);
+					patch = code;
+					ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
+#endif
+					if (loongarch_is_imm12 (sizeof (target_mgreg_t) * item->value.vtable_slot)) {
+						loongarch_ldd (code, loongarch_r21, loongarch_t7, (sizeof (target_mgreg_t) * item->value.vtable_slot));
+					} else {
+						code = mono_loongarch_emit_imm64 (code, loongarch_r21, sizeof (target_mgreg_t) * item->value.vtable_slot);
+						loongarch_ldxd (code, loongarch_r21, loongarch_t7, loongarch_r21);
+					}
+					loongarch_jirl (code, 0, loongarch_r21, 0);
+
+#if ENABLE_WRONG_METHOD_CHECK
+					ppc_patch (patch, code);
+					ppc_break (code);
+#endif
+				}
+			}
+		} else {
+			code = mono_loongarch_emit_imm64 (code, loongarch_temp, (gint64)item->key);
+			loongarch_slt (code, loongarch_temp, MONO_ARCH_IMT_REG, loongarch_temp);
+
+			item->jmp_code = code;
+			loongarch_beq (code, loongarch_temp, loongarch_zero, 0);
+			//loongarch_nop (code);
+		}
+	}
+	/* patch the branches to get to the target items */
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+		if (item->jmp_code && item->check_target_idx) {
+			mono_loongarch64_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target, MONO_R_LOONGARCH64_BC);
+		}
+	}
+	g_assert (code - start <= size);
+	mono_arch_flush_icache (start, size);
+	MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL));
+
+	mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), m_class_get_mem_manager (vtable->klass));
+
+	return start;
+}
+
+GSList *
+mono_arch_get_trampolines (gboolean aot)
+{
+	return mono_loongarch_get_exception_trampolines (aot);
+}
+
+#else /* DISABLE_JIT */
+
+gpointer
+mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+								gpointer fail_tramp)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+#endif /* !DISABLE_JIT */
+
+/* Soft Debug support */
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+
+/*
+ * mono_arch_set_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+	guint8 *code = MINI_FTNPTR_TO_ADDR (ip);
+	guint32 native_offset = GPTRDIFF_TO_UINT32 (ip - (guint8*)ji->code_start);
+
+	if (ji->from_aot) {
+		SeqPointInfo *info = mono_arch_get_seq_point_info ((guint8*)ji->code_start);
+
+		g_assert (native_offset % 4 == 0);
+		g_assert (info->bp_addrs [native_offset / 4] == 0);
+		info->bp_addrs [native_offset / 4] = (guint8*)mini_get_breakpoint_trampoline ();
+	} else {
+		/* ip points to an ldrx */
+		code += 4;
+		mono_codeman_enable_write ();
+		loongarch_jirl (code, 1, loongarch_t0, 0);
+		mono_codeman_disable_write ();
+		mono_arch_flush_icache (ip, code - ip);
+	}
+}
+
+/*
+ * mono_arch_clear_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+	guint8 *code = MINI_FTNPTR_TO_ADDR (ip);
+
+	if (ji->from_aot) {
+		guint32 native_offset = GPTRDIFF_TO_UINT32 (ip - (guint8*)ji->code_start);
+		SeqPointInfo *info = mono_arch_get_seq_point_info ((guint8*)ji->code_start);
+
+		g_assert (native_offset % 4 == 0);
+		info->bp_addrs [native_offset / 4] = NULL;
+	} else {
+		/* ip points to an ldrx */
+		code += 4;
+		mono_codeman_enable_write ();
+		loongarch_nop (code);
+		mono_codeman_disable_write ();
+		mono_arch_flush_icache (ip, code - ip);
+	}
+}
+
+/*
+ * mono_arch_start_single_stepping:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_start_single_stepping (void)
+{
+	ss_trampoline = mini_get_single_step_trampoline ();
+}
+
+/*
+ * mono_arch_stop_single_stepping:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_stop_single_stepping (void)
+{
+	ss_trampoline = NULL;
+}
+
+/*
+ * mono_arch_is_single_step_event:
+ *
+ *   See mini-amd64.c for docs.
+ */
+gboolean
+mono_arch_is_single_step_event (void *info, void *sigctx)
+{
+	return FALSE;
+}
+
+/*
+ * mono_arch_is_breakpoint_event:
+ *
+ *   See mini-amd64.c for docs.
+ */
+gboolean
+mono_arch_is_breakpoint_event (void *info, void *sigctx)
+{
+	return FALSE;
+}
+
+/*
+ * mono_arch_skip_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
+{
+	g_assert_not_reached ();
+}
+
+/*
+ * mono_arch_skip_single_step:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+	g_assert_not_reached ();
+}
+
+/*
+ * mono_arch_get_seq_point_info:
+ *
+ *   See mini-amd64.c for docs.
+ */
+SeqPointInfo*
+mono_arch_get_seq_point_info (guint8 *code)
+{
+	SeqPointInfo *info;
+	MonoJitInfo *ji;
+	MonoJitMemoryManager *jit_mm;
+
+	jit_mm = get_default_jit_mm ();
+
+	// FIXME: Add a free function
+
+	jit_mm_lock (jit_mm);
+	info = (SeqPointInfo *)g_hash_table_lookup (jit_mm->arch_seq_points, code);
+	jit_mm_unlock (jit_mm);
+
+	if (!info) {
+		ji = mini_jit_info_table_find (code);
+		g_assert (ji);
+
+		info = g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size / 4) * sizeof(guint8*));
+
+		info->ss_tramp_addr = &ss_trampoline;
+
+		jit_mm_lock (jit_mm);
+		g_hash_table_insert (jit_mm->arch_seq_points, code, info);
+		jit_mm_unlock (jit_mm);
+	}
+
+	return info;
+}
+
+#endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
+
+gboolean
+mono_arch_opcode_supported (int opcode)
+{
+	return FALSE;
+}
+
+gpointer
+mono_arch_load_function (MonoJitICallId jit_icall_id)
+{
+	return NULL;
+}
+
+GSList*
+mono_arch_get_cie_program (void)
+{
+	NOT_IMPLEMENTED;
+	return NULL;
+}
diff --git a/src/mono/mono/mini/mini-loongarch64.h b/src/mono/mono/mini/mini-loongarch64.h
new file mode 100644
index 00000000000000..fd41713f3bc679
--- /dev/null
+++ b/src/mono/mono/mini/mini-loongarch64.h
@@ -0,0 +1,293 @@
+/**
+ * \file
+ *
+ * Authors:
+ *   Qiao Pengcheng (qiaopengcheng@loongson.cn), Liu An (liuan@loongson.cn)
+ *
+ * Copyright (c) 2021 Loongson Technology, Inc
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#ifndef __MONO_MINI_LOONGARCH64_H__
+#define __MONO_MINI_LOONGARCH64_H__
+
+#include <glib.h>
+#include <mono/arch/loongarch64/loongarch64-codegen.h>
+#include <mono/utils/mono-context.h>
+
+#define MONO_ARCH_CPU_SPEC mono_loongarch64_desc
+
+#define MONO_MAX_IREGS 32
+#define MONO_MAX_FREGS 32
+
+#if SIZEOF_REGISTER != 8
+#error Unknown REGISTER_SIZE
+#endif
+
+#define IREG_SIZE 8
+#define FREG_SIZE 8
+
+#define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
+#define MONO_ARCH_GSHAREDVT_SUPPORTED 1
+#define MONO_ARCH_INTERPRETER_SUPPORTED 1
+#define MONO_ARCH_HAVE_EXCEPTIONS_INIT 1
+#define MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS 1
+#define MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION 1
+#define MONO_ARCH_HAVE_SDB_TRAMPOLINES 1
+
+#define MONO_ARCH_HAVE_GENERALIZED_IMT_TRAMPOLINE 1
+#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
+#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
+#define MONO_ARCH_GSHARED_SUPPORTED 1
+
+/* set the next to 0 once inssel-loongarch.brg is updated */
+#define LOONGARCH_PASS_STRUCTS_BY_VALUE 1
+
+#define MONO_ARCH_USE_SIGACTION 1
+#define MONO_ARCH_NEED_DIV_CHECK 1
+#define MONO_ARCH_NO_IOV_CHECK 1
+#define MONO_ARCH_HAVE_DECOMPOSE_OPTS 1
+
+// Does the ABI have a volatile non-parameter register, so tailcall
+// can pass context to generics or interfaces?
+#define MONO_ARCH_HAVE_VOLATILE_NON_PARAM_REGISTER 1
+#define MONO_CONTEXT_SET_LLVM_EXC_REG(ctx, exc) do { (ctx)->regs [4] = (gsize)exc; } while (0)
+#define MONO_INIT_CONTEXT_FROM_FUNC(ctx,func) do {	\
+	MONO_CONTEXT_SET_BP ((ctx), __builtin_frame_address (0));			\
+	MONO_CONTEXT_SET_SP ((ctx), __builtin_frame_address (0));			\
+	MONO_CONTEXT_SET_IP ((ctx), (func));								\
+	} while (0)
+
+#define MONO_ARCH_INIT_TOP_LMF_ENTRY(lmf)
+
+/*
+ * r21 and t0/t8 used internally,
+ * a0-a7 are for arguments,
+ */
+#define loongarch_temp  loongarch_t8
+
+#define LOONGARCH_T_REGS ((1 << loongarch_t1) | \
+			 (1 << loongarch_t2) | \
+			 (1 << loongarch_t3) | \
+			 (1 << loongarch_t4) | \
+			 (1 << loongarch_t5) | \
+			 (1 << loongarch_t6) | \
+			 (1 << loongarch_t7))
+
+#define LOONGARCH_S_REGS ((1 << loongarch_s0) | \
+			 (1 << loongarch_s1) | \
+			 (1 << loongarch_s2) | \
+			 (1 << loongarch_s3) | \
+			 (1 << loongarch_s4) | \
+			 (1 << loongarch_s5) | \
+			 (1 << loongarch_s6) | \
+			 (1 << loongarch_s7) | \
+			 (1 << loongarch_s8))
+
+#define LOONGARCH_A_REGS ((1 << loongarch_a0) | \
+			 (1 << loongarch_a1) | \
+			 (1 << loongarch_a2) | \
+			 (1 << loongarch_a3) | \
+			 (1 << loongarch_a4) | \
+			 (1 << loongarch_a5) | \
+			 (1 << loongarch_a6) | \
+			 (1 << loongarch_a7))
+
+#define PARAM_REGS (loongarch_a7+1)
+#define FP_PARAM_REGS 8
+#define RETURN_REGS 2
+#define FLOAT_RETURN_REGS 2
+
+#define MONO_ARCH_CALLEE_REGS (LOONGARCH_T_REGS | LOONGARCH_A_REGS)
+#define MONO_ARCH_CALLEE_SAVED_REGS LOONGARCH_S_REGS
+
+#define MONO_ARCH_CALLEE_FREGS ((1 << loongarch_f0) |  \
+			 (1 << loongarch_f1) | \
+			 (1 << loongarch_f2) | \
+			 (1 << loongarch_f3) | \
+			 (1 << loongarch_f4) | \
+			 (1 << loongarch_f5) | \
+			 (1 << loongarch_f6) | \
+			 (1 << loongarch_f7) | \
+			 (1 << loongarch_ft0) | \
+			 (1 << loongarch_ft1) | \
+			 (1 << loongarch_ft2) | \
+			 (1 << loongarch_ft3) | \
+			 (1 << loongarch_ft4) | \
+			 (1 << loongarch_ft5) | \
+			 (1 << loongarch_ft6) | \
+			 (1 << loongarch_ft7) | \
+			 (1 << loongarch_ft8) | \
+			 (1 << loongarch_ft9) | \
+			 (1 << loongarch_ft10) | \
+			 (1 << loongarch_ft11) | \
+			 (1 << loongarch_ft12) | \
+			 (1 << loongarch_ft13))
+
+#define MONO_ARCH_CALLEE_SAVED_FREGS ((1 << loongarch_fs0) |  \
+			 (1 << loongarch_fs1) | \
+			 (1 << loongarch_fs2) | \
+			 (1 << loongarch_fs3) | \
+			 (1 << loongarch_fs4) | \
+			 (1 << loongarch_fs5) | \
+			 (1 << loongarch_fs6) | \
+			 (1 << loongarch_fs7))
+
+#define loongarch_ftemp loongarch_ft14
+#define loongarch_ftemp2 loongarch_ft15
+
+#define MONO_ARCH_USE_FPSTACK FALSE
+
+/* Parameters used by the register allocator */
+#define MONO_ARCH_INST_SREG2_MASK(ins) (0)
+#define MONO_ARCH_INST_IS_REGPAIR(desc) (0)
+#define MONO_ARCH_INST_REGPAIR_REG2(desc,hreg1) (-1)
+#define MONO_ARCH_INST_IS_FLOAT(desc) (desc == 'f')
+
+// This define is called to get specific dest register as defined
+// by md file (letter after "dest"). Overwise return -1
+#define MONO_ARCH_INST_FIXED_REG(desc) ((desc) == 'a' ? loongarch_a0 : -1)
+
+/* fixme: align to 16byte instead of 32byte (we align to 32byte to get
+ * reproduceable results for benchmarks */
+#define MONO_ARCH_CODE_ALIGNMENT 32
+
+/* callee saved regs + fp + sp */
+#define MONO_ARCH_LMF_REGS ((0x1ff << 23) | (1 << loongarch_fp) | (1 << loongarch_sp))
+#define MONO_ARCH_NUM_LMF_REGS (9 + 2)
+#define MONO_ARCH_NUM_LMF_FREGS (8)
+#define MONO_ARCH_FIRST_LMF_REG loongarch_s0
+#define MONO_ARCH_LMF_REG_FP 1
+#define MONO_ARCH_LMF_REG_SP 0
+
+struct MonoLMF {
+	gpointer	previous_lmf;
+	gpointer	lmf_addr;
+	MonoMethod	*method;
+	gpointer	pc;
+	host_mgreg_t    gregs [MONO_ARCH_NUM_LMF_REGS];
+	gdouble	fregs [MONO_ARCH_NUM_LMF_FREGS];//should confirm ?! why not fpr ?!
+	gulong		magic;
+};
+
+/* Structure used by the sequence points in AOTed code */
+struct SeqPointInfo {// metadata/object-offsets.h
+	gpointer ss_tramp_addr;
+	guint8* bp_addrs [MONO_ZERO_LEN_ARRAY];
+};
+
+typedef struct {
+	CallInfo *cinfo;
+	int saved_gregs_offset;
+	/* Points to arguments received on the stack */
+	int args_reg;
+	gint8 cond_branch_islands;
+	MonoInst *vret_addr_loc;
+	MonoInst *seq_point_info_var;
+	MonoInst *ss_tramp_var;
+	MonoInst *bp_tramp_var;
+	guint8 *thunks;
+	int thunks_size;
+} MonoCompileArch;
+
+#define MONO_ARCH_EMULATE_FCONV_TO_U4 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
+#define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS 1
+#define MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS 1
+#define MONO_ARCH_EMULATE_FREM 1
+#define MONO_ARCH_EMULATE_LONG_MUL_OVF_OPTS 1
+#define MONO_ARCH_EMULATE_MUL_OVF 1
+#define MONO_ARCH_FLOAT32_SUPPORTED 1
+#define MONO_ARCH_HAVE_OP_TAILCALL_MEMBASE 1
+#define MONO_ARCH_HAVE_OP_TAILCALL_REG 1
+
+#define LOONGARCH_RET_ADDR_OFFSET (-sizeof (target_mgreg_t))
+
+#define LOONGARCH_STACK_ALIGNMENT 16
+#define MONO_ARCH_FRAME_ALIGNMENT 16
+
+#define LOONGARCH_FIRST_ARG_REG loongarch_a0
+#define LOONGARCH_LAST_ARG_REG loongarch_a7
+#define LOONGARCH_FIRST_FPARG_REG loongarch_f0
+#define LOONGARCH_LAST_FPARG_REG loongarch_f7
+
+#define MONO_ARCH_IMT_REG loongarch_t1
+#define MONO_ARCH_RGCTX_REG MONO_ARCH_IMT_REG
+
+#define MONO_ARCH_VTABLE_REG loongarch_a0
+
+/* Relocations */
+#define MONO_R_LOONGARCH64_BL 1
+#define MONO_R_LOONGARCH64_B 2
+#define MONO_R_LOONGARCH64_BC 3
+#define MONO_R_LOONGARCH64_BZ 4
+#define MONO_R_LOONGARCH64_J 5
+#define MONO_R_LOONGARCH64_JR 6
+
+
+/// some features's macros for LoongArch64
+#define MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED 1
+#define MONO_ARCH_HAVE_SETUP_ASYNC_CALLBACK 1
+#define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP 1
+#define MONO_ARCH_AOT_SUPPORTED 1
+#define MONO_ARCH_LLVM_SUPPORTED 1
+#define MONO_ARCH_HAVE_CONTEXT_SET_INT_REG 1
+
+typedef enum {
+	ArgInIReg,
+	ArgOnStack,
+	ArgInFReg,
+	ArgStructByVal,
+	ArgStructByRef,
+	ArgNone
+} ArgStorage;
+
+typedef struct {
+	gint32  offset; //ArgOnStack.
+	guint8  reg;
+	guint8  freg;
+	guint8  field_info; //bit0: whether the first field is float;
+						//bit1: whether the size of first field is 8-bytes;
+						//bit2: whether the second field is exist;
+						//bit3: whether the second field is float;
+						//bit4: whether the size of second field is 8-bytes;
+						//field_info will be used by ArgStructByVal.
+	guint8  slot_size;
+	ArgStorage storage;
+	gboolean sign;
+	gboolean fin_ireg;  //float in ireg when freg is not available.
+	guint32 size;   //for ArgStructByVal size is struction size, for ArgInFReg size is isR.
+} ArgInfo;
+
+struct CallInfo {
+	int nargs;
+	int gr;
+	int fr;
+	guint32 stack_usage;
+	gboolean pinvoke, vararg;
+	ArgInfo ret;
+	ArgInfo sig_cookie;
+	ArgInfo args [1];
+};
+
+typedef struct {
+	/* General registers for indirect returns */
+	host_mgreg_t gregs [PARAM_REGS];
+	/* Floating registers */
+	double fregs [FP_PARAM_REGS];
+	/* Stack usage, used for passing params on stack */
+	guint32 stack_size;
+	guint8* stack;
+} CallContext;
+
+guint8* mono_loongarch64_emit_destroy_frame (guint8 *code, int stack_offset);
+void mono_loongarch64_patch (guint8 *code, guint8 *target, int relocation);
+void mono_loongarch_gsharedvt_init (void);
+GSList* mono_loongarch_get_exception_trampolines (gboolean aot);
+guint8* mono_loongarch_emit_imm64 (guint8 *code, int dreg, gint64 imm);
+guint8* mono_loongarch_emit_jirl (guint8 *code, int reg);
+guint8* mono_loongarch_emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset);
+guint8* mono_loongarch_emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset);
+guint8* mono_loongarch_emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset);
+guint8* mono_loongarch_emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset);
+#endif /* __MONO_MINI_LOONGARCH64_H__ */
