Przeglądaj źródła

Add loongarch64 architecture support to compiler

Jinyang He 2 lat temu
rodzic
commit
12c4290ffe
61 zmienionych plików z 9247 dodań i 31 usunięć
  1. 25 6
      compiler/Makefile.fpc
  2. 3 3
      compiler/aggas.pas
  3. 9 9
      compiler/aoptobj.pas
  4. 7 0
      compiler/cfidwarf.pas
  5. 22 0
      compiler/cgbase.pas
  6. 4 2
      compiler/entfile.pas
  7. 10 0
      compiler/fpcdefs.inc
  8. 6 0
      compiler/globals.pas
  9. 362 0
      compiler/loongarch64/aasmcpu.pas
  10. 216 0
      compiler/loongarch64/agcpugas.pas
  11. 152 0
      compiler/loongarch64/aoptcpu.pas
  12. 106 0
      compiler/loongarch64/aoptcpub.pas
  13. 39 0
      compiler/loongarch64/aoptcpuc.pas
  14. 40 0
      compiler/loongarch64/aoptcpud.pas
  15. 1604 0
      compiler/loongarch64/cgcpu.pas
  16. 448 0
      compiler/loongarch64/cpubase.pas
  17. 122 0
      compiler/loongarch64/cpuinfo.pas
  18. 52 0
      compiler/loongarch64/cpunode.pas
  19. 617 0
      compiler/loongarch64/cpupara.pas
  20. 122 0
      compiler/loongarch64/cpupi.pas
  21. 82 0
      compiler/loongarch64/cputarg.pas
  22. 185 0
      compiler/loongarch64/hlcgcpu.pas
  23. 101 0
      compiler/loongarch64/itcpugas.pas
  24. 409 0
      compiler/loongarch64/loongarch64att.inc
  25. 2 0
      compiler/loongarch64/loongarch64nop.inc
  26. 409 0
      compiler/loongarch64/loongarch64op.inc
  27. 404 0
      compiler/loongarch64/loongarchins.dat
  28. 83 0
      compiler/loongarch64/loongarchreg.dat
  29. 401 0
      compiler/loongarch64/ncpuadd.pas
  30. 236 0
      compiler/loongarch64/ncpucnv.pas
  31. 203 0
      compiler/loongarch64/ncpuinl.pas
  32. 152 0
      compiler/loongarch64/ncpumat.pas
  33. 144 0
      compiler/loongarch64/ncpuset.pas
  34. 48 0
      compiler/loongarch64/racpu.pas
  35. 811 0
      compiler/loongarch64/racpugas.pas
  36. 114 0
      compiler/loongarch64/rgcpu.pas
  37. 74 0
      compiler/loongarch64/rloongarch64abi.inc
  38. 74 0
      compiler/loongarch64/rloongarch64con.inc
  39. 74 0
      compiler/loongarch64/rloongarch64dwa.inc
  40. 2 0
      compiler/loongarch64/rloongarch64nor.inc
  41. 74 0
      compiler/loongarch64/rloongarch64num.inc
  42. 74 0
      compiler/loongarch64/rloongarch64rni.inc
  43. 74 0
      compiler/loongarch64/rloongarch64sri.inc
  44. 74 0
      compiler/loongarch64/rloongarch64sta.inc
  45. 74 0
      compiler/loongarch64/rloongarch64std.inc
  46. 74 0
      compiler/loongarch64/rloongarch64sup.inc
  47. 220 0
      compiler/loongarch64/symcpu.pas
  48. 45 0
      compiler/loongarch64/tripletcpu.pas
  49. 4 0
      compiler/ncgcnv.pas
  50. 22 0
      compiler/options.pas
  51. 7 0
      compiler/pp.pas
  52. 4 0
      compiler/psystem.pas
  53. 46 5
      compiler/raatt.pas
  54. 2 1
      compiler/systems.inc
  55. 2 2
      compiler/systems.pas
  56. 1 1
      compiler/tgobj.pas
  57. 4 0
      compiler/utils/fpc.pp
  58. 198 0
      compiler/utils/mkloongarch64ins.pp
  59. 267 0
      compiler/utils/mkloongarch64reg.pp
  60. 4 2
      compiler/utils/ppuutils/ppudump.pp
  61. 3 0
      compiler/version.pas

+ 25 - 6
compiler/Makefile.fpc

@@ -32,7 +32,7 @@ fpcdir=..
 unexport FPC_VERSION FPC_COMPILERINFO
 
 # Which platforms are ready for inclusion in the cycle
-CYCLETARGETS=i386 powerpc sparc arm x86_64 powerpc64 m68k armeb mipsel mips avr jvm i8086 aarch64 sparc64 riscv32 riscv64 xtensa z80 wasm32
+CYCLETARGETS=i386 powerpc sparc arm x86_64 powerpc64 m68k armeb mipsel mips avr jvm i8086 aarch64 sparc64 riscv32 riscv64 xtensa z80 wasm32 loongarch64
 
 # All supported targets used for clean
 ALLTARGETS=$(CYCLETARGETS)
@@ -128,6 +128,9 @@ endif
 ifdef Z80
 PPC_TARGET=z80
 endif
+ifdef LOONGARCH64
+PPC_TARGET=loongarch64
+endif
 
 # Default is to generate a compiler for the same
 # platform as CPU_TARGET (a native compiler)
@@ -302,6 +305,9 @@ endif
 ifeq ($(CPC_TARGET),wasm32)
 CPUSUF=wasm32
 endif
+ifeq ($(CPC_TARGET),loongarch64)
+CPUSUF=loongarch64
+endif
 
 # Do not define the default -d$(CPU_TARGET) because that
 # will conflict with our -d$(CPC_TARGET)
@@ -468,6 +474,11 @@ ifeq ($(PPC_TARGET),wasm32)
 override LOCALOPT+=-dNOOPT
 endif
 
+# LoongArch64 specific
+ifeq ($(PPC_TARGET),loongarch64)
+override LOCALOPT+=-Fuloongarch64
+endif
+
 OPTWPOCOLLECT=-OWdevirtcalls,optvmts -FW$(BASEDIR)/pp1.wpo
 OPTWPOPERFORM=-Owdevirtcalls,optvmts -Fw$(BASEDIR)/pp1.wpo
 # symbol liveness WPO requires nm, smart linking and no stripping (the latter
@@ -671,8 +682,8 @@ endif
 # cpu targets
 #####################################################################
 
-PPC_TARGETS=i386 m68k powerpc sparc arm armeb x86_64 powerpc64 mips mipsel mips64 mips64el avr jvm i8086 aarch64 sparc64 riscv32 riscv64 xtensa z80 wasm32
-PPC_SUFFIXES=386 68k ppc sparc arm armeb x64 ppc64 mips mipsel mips64 mips64el avr jvm 8086 a64 sparc64 rv32 rv64 xtensa z80 wasm32
+PPC_TARGETS=i386 m68k powerpc sparc arm armeb x86_64 powerpc64 mips mipsel mips64 mips64el avr jvm i8086 aarch64 sparc64 riscv32 riscv64 xtensa z80 wasm32 loongarch64
+PPC_SUFFIXES=386 68k ppc sparc arm armeb x64 ppc64 mips mipsel mips64 mips64el avr jvm 8086 a64 sparc64 rv32 rv64 xtensa z80 wasm32 loongarch64
 INSTALL_TARGETS=$(addsuffix _exe_install,$(sort $(CYCLETARGETS) $(PPC_TARGETS)))
 CLEAN_TARGETS=$(addsuffix _clean,$(sort $(CYCLETARGETS) $(PPC_TARGETS)))
 SYMLINKINSTALL_TARGETS=$(addsuffix _symlink_install,$(sort $(CYCLETARGETS) $(PPC_TARGETS)))
@@ -784,7 +795,11 @@ insdatz80 : z80/z80ins.dat
 	$(COMPILER) -FE$(COMPILERUTILSDIR) $(COMPILERUTILSDIR)/mkz80ins.pp
         cd z80 && ..$(PATHSEP)utils$(PATHSEP)mkz80ins$(SRCEXEEXT)
 
-insdat: insdatx86 insdatarm insdataarch64 insdatz80
+insdatloongarch64 : loongarch64/loongarchins.dat
+	    $(COMPILER) -FE$(COMPILERUTILSDIR) $(COMPILERUTILSDIR)/mkloongarch64ins.pp
+        cd loongarch64 && ..$(PATHSEP)utils$(PATHSEP)mkloongarch64ins$(SRCEXEEXT)
+
+insdat: insdatx86 insdatarm insdataarch64 insdatz80 insdatloongarch64
 
 regdatx86 : x86/x86reg.dat
 	$(COMPILER) -FE$(COMPILERUTILSDIR) $(COMPILERUTILSDIR)/mkx86reg.pp
@@ -839,7 +854,11 @@ regdatrv64 : riscv/rvreg.dat
         cd riscv && ..$(PATHSEP)utils$(PATHSEP)mkrvreg$(SRCEXEEXT) riscv64
 		mv -f riscv/rrv64*.inc riscv64
 
-regdat : regdatx86 regdatarm regdatsp regdatavr regdataarch64 regdatmips regdatsp64 regdatz80 regdatwasm regdatrv32 regdatrv64
+regdatloongarch64 : loongarch64/loongarchreg.dat
+            $(COMPILER) -FE$(COMPILERUTILSDIR) $(COMPILERUTILSDIR)/mkloongarch64reg.pp
+        cd loongarch64 && ..$(PATHSEP)utils$(PATHSEP)mkloongarch64reg$(SRCEXEEXT)
+
+regdat : regdatx86 regdatarm regdatsp regdatavr regdataarch64 regdatmips regdatsp64 regdatz80 regdatwasm regdatrv32 regdatrv64 regdatloongarch64
 
 intrdatx86 : x86/x86intr.dat
 		$(COMPILER) -FE$(COMPILERUTILSDIR) $(COMPILERUTILSDIR)/mkx86inl.pp
@@ -1122,7 +1141,7 @@ ifeq ($(OS_SOURCE),win64)
   EXCLUDE_80BIT_TARGETS=1
 endif
 
-ifneq ($(findstring $(CPU_SOURCE),aarch64 arm avr jvm m68k mips mipsel mips64 mips64el powerpc powerpc64 sparc sparc64 riscv32 riscv64 xtensa),)
+ifneq ($(findstring $(CPU_SOURCE),aarch64 arm avr jvm m68k mips mipsel mips64 mips64el powerpc powerpc64 sparc sparc64 riscv32 riscv64 xtensa loongarch64),)
   EXCLUDE_80BIT_TARGETS=1
 endif
 endif

+ 3 - 3
compiler/aggas.pas

@@ -221,11 +221,11 @@ implementation
 { vtable for a class called Window:                                       }
 { .section .data.rel.ro._ZTV6Window,"awG",@progbits,_ZTV6Window,comdat    }
 { TODO: .data.ro not yet working}
-{$if defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64)}
+{$if defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64) or defined(loongarch64)}
           '.rodata',
-{$else defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64)}
+{$else defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64) or defined(loongarch64)}
           '.data',
-{$endif defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64)}
+{$endif defined(arm) or defined(aarch64) or defined(riscv64) or defined(powerpc) or defined(x86_64) or defined(loongarch64)}
           '.rodata',
           '.bss',
           '.threadvar',

+ 9 - 9
compiler/aoptobj.pas

@@ -473,8 +473,8 @@ Unit AoptObj;
 
     function JumpTargetOp(ai: taicpu): poper; inline;
       begin
-{$if defined(MIPS) or defined(riscv64) or defined(riscv32) or defined(xtensa)}
-        { MIPS, Xtensa or RiscV branches can have 1,2 or 3 operands, target label is the last one. }
+{$if defined(MIPS) or defined(riscv64) or defined(riscv32) or defined(xtensa) or defined(loongarch64)}
+        { Branches of above archs can have 1,2 or 3 operands, target label is the last one. }
         result:=ai.oper[ai.ops-1];
 {$elseif defined(SPARC64)}
         if ai.ops=2 then
@@ -2330,7 +2330,7 @@ Unit AoptObj;
 
       var p1: tai;
           p2: tai;
-{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM)}
+{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM) and not defined(loongarch64)}
           p3: tai;
 {$endif}
           ThisLabel, l: tasmlabel;
@@ -2377,9 +2377,9 @@ Unit AoptObj;
                       Exit;
                   end;
 
-{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM)}
+{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM) and not defined(loongarch64)}
                 p3 := p2;
-{$endif not MIPS and not RV64 and not RV32 and not JVM}
+{$endif not MIPS and not RV64 and not RV32 and not JVM and not loongarch64}
 
                 if { the next instruction after the label where the jump hp arrives}
                    { is unconditional or of the same type as hp, so continue       }
@@ -2388,7 +2388,7 @@ Unit AoptObj;
                    { TODO: For anyone with experience with MIPS or RISC-V, please add support for tracing
                      conditional jumps. [Kit] }
 
-{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM)}
+{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM) and not defined(loongarch64)}
   { for MIPS, it isn't enough to check the condition; first operands must be same, too. }
                    or
                    condition_in(hp.condition, taicpu(p1).condition) or
@@ -2406,7 +2406,7 @@ Unit AoptObj;
                      ) and
                      SetAndTest(p2,p1)
                    )
-{$endif not MIPS and not RV64 and not RV32 and not JVM}
+{$endif not MIPS and not RV64 and not RV32 and not JVM and not loongarch64}
                    then
                   begin
                     { quick check for loops of the form "l5: ; jmp l5" }
@@ -2435,7 +2435,7 @@ Unit AoptObj;
                     GetFinalDestination := True;
                     Exit;
                   end
-{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM)}
+{$if not defined(MIPS) and not defined(riscv64) and not defined(riscv32) and not defined(JVM) and not defined(loongarch64)}
                 else
                   if condition_in(inverse_cond(hp.condition), taicpu(p1).condition) then
                     begin
@@ -2472,7 +2472,7 @@ Unit AoptObj;
                       GetFinalDestination := True;
                       Exit;
                     end;
-{$endif not MIPS and not RV64 and not RV32 and not JVM}
+{$endif not MIPS and not RV64 and not RV32 and not JVM and not loongarch64}
               end;
           end;
 

+ 7 - 0
compiler/cfidwarf.pas

@@ -315,6 +315,13 @@ implementation
         list.concat(tai_const.create_uleb128bit(dwarf_reg(NR_STACK_POINTER_REG)));
         list.concat(tai_const.create_uleb128bit(0));
       end;
+{$elseif defined(loongarch64)}
+    procedure TDwarfAsmCFILowLevel.generate_initial_instructions(list:TAsmList);
+      begin
+        list.concat(tai_const.create_8bit(DW_CFA_def_cfa));
+        list.concat(tai_const.create_uleb128bit(dwarf_reg(NR_STACK_POINTER_REG)));
+        list.concat(tai_const.create_uleb128bit(0));
+      end;
 {$else}
     { if more cpu dependend stuff is implemented, this needs more refactoring }
     procedure TDwarfAsmCFILowLevel.generate_initial_instructions(list:TAsmList);

+ 22 - 0
compiler/cgbase.pas

@@ -103,6 +103,28 @@ interface
          addr_got_pcrel_hi,
          addr_plt
          {$endif RISCV}
+         {$if defined(LOONGARCH64)}
+         ,
+         addr_b16, { %b16(sym) }
+         addr_b21, { %b21(sym) }
+         addr_b26, { %b26(sym) }
+         addr_pcrel, { Some times we only use sym like 'bxx rd,rj,sym'. And la.pcrel..sym }
+         addr_plt, { %plt(sym) }
+         addr_abs_hi20, { %abs_hi20(sym) }
+         addr_abs_lo12, { %abs_lo12(sym) }
+         addr_abs64_lo20, { %abs_lo20(sym) }
+         addr_abs64_hi12, { %abs_hi12(sym) }
+         addr_pc_hi20, { %pc_hi20(sym) }
+         addr_got_pc_hi20, { %got_pc_hi20(sym) }
+         addr_got_pc_lo12, { %got_pc_lo12(sym) }
+         addr_pc_lo12, { %pc_lo12(sym) }
+         addr_got, { la.got..sym }
+         addr_abs, { la.abs..sym }
+         addr_reg_reg, { use by [ld/st]x }
+         addr_reg_12i, { use by [ld/st] }
+         addr_reg_14i, { use by [ldptr/stptr] }
+         addr_reg { use by jr.. }
+         {$endif LOONGARCH64}
          {$IFDEF AVR}
          ,addr_lo8
          ,addr_lo8_gs

+ 4 - 2
compiler/entfile.pas

@@ -163,7 +163,8 @@ const
     { 21 } 32 {'xtensa'},
     { 22 } 16 {'z80'},
     { 23 } 64 {'mips64'},
-    { 24 } 64 {'mips64el'}
+    { 24 } 64 {'mips64el'},
+    { 25 } 64 {'loongarch64'}
     );
   CpuAluBitSize : array[tsystemcpu] of longint =
     (
@@ -191,7 +192,8 @@ const
     { 21 } 32 {'xtensa'},
     { 22 }  8 {'z80'},
     { 23 } 64 {'mips64'},
-    { 24 } 64 {'mips64el'}
+    { 24 } 64 {'mips64el'},
+    { 25 } 64 {'loongarch64'}
     );
 {$endif generic_cpu}
 

+ 10 - 0
compiler/fpcdefs.inc

@@ -391,6 +391,16 @@
   {$define cpufloatintregmov}
 {$endif xtensa}
 
+{$ifdef loongarch64}
+  {$define loongarch}
+  {$define cpu64bit}
+  {$define cpu64bitaddr}
+  {$define cpu64bitalu}
+  {$define cputargethasfixedstack}
+  {$define cpucapabilities}
+  {$define SUPPORT_SAFECALL}
+{$endif loongarch64}
+
 { Stabs is not officially supported on 64 bit targets by gdb, except on Mac OS X
   (but there we don't support it)
 }

+ 6 - 0
compiler/globals.pas

@@ -610,6 +610,12 @@ interface
         asmcputype : cpu_none;
         fputype : fpu_standard;
   {$endif wasm}
+  {$ifdef loongarch64}
+        cputype : cpu_3a;
+        optimizecputype : cpu_3a;
+        asmcputype : cpu_none;
+        fputype : fpu_fd;
+  {$endif loongarch64}
 {$endif not GENERIC_CPU}
         asmmode : asmmode_standard;
 {$ifndef jvm}

+ 362 - 0
compiler/loongarch64/aasmcpu.pas

@@ -0,0 +1,362 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    Contains the assembler object for the LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit aasmcpu;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  globtype,verbose,
+  aasmbase,aasmtai,aasmdata,aasmsym,
+  cpubase,cgbase,cgutils;
+
+    const
+      { "move reg,reg" source operand number }
+      O_MOV_SOURCE = 1;
+      { "move reg,reg" source operand number }
+      O_MOV_DEST = 0;
+
+
+    type
+      { TODO: Branch condition? TRefernece and Symbol? }
+      { taicpu }
+
+      taicpu = class(tai_cpu_abstract_sym)
+         { NOP,TLBSRCH,TLBRD,TLBWR,TLBFILL,TLBCLR,TLBFLUSH,ERTN, }
+         constructor op_none(op : tasmop);
+
+         { DBAR,IBAR,SYSCALL,BREAK,DBCL,IDLE, }
+         constructor op_const(op : tasmop;_op1 : aint);
+
+         { B,BL,JR, }
+         constructor op_ref(op : tasmop; const _op1 : treference);
+
+         { EXT,CLO,CLZ,CTO,CTZ,REVB,REVH,BITREV,ASRTLE,ASRTGT,RDTIMEL,RDTIMEH,
+           RDTIME,CPUCFG,FABS,FNEG,FSQRT,FRECIP,FRSQRT,FLOGB,FCLASS,FCVT,FFINT,
+           FTINT,FTINTRM,FTINTRP,FTINTRZ,FTINTRNE,FRINT,FMOV,MOVGR2FR,MOVGR2FRH,
+           MOVFR2GR,MOVFRH2GR,MOVGR2FCSR,MOVFCSR2GR,MOVFR2CF,MOVCF2FR,MOVGR2CF,
+           MOVCF2GR,IOCSRRD,IOCSRWR,MOVE,}
+         constructor op_reg_reg(op : tasmop;_op1,_op2 : tregister);
+
+         { BEQZ,BNEZ,BCEQZ,BCNEZ,LA,BLTZ,BGTZ,BGEZ,BLEZ,LD,ST,LDX,STX,LDPTR,
+           STPTR,FLD,FST,FLDX,FSTX, }
+         constructor op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
+
+         { LU12I,LU32I,PCADDI,PCADDU12I,PCADDU18I,PCALAU12I,CSRRD,CSRWR,LDPTE,LI, }
+         constructor op_reg_const(op:tasmop; _op1: tregister; _op2: aint);
+
+         { BSTRINS,BSTRPICK, }
+         constructor op_reg_reg_const_const(op: tasmop; _op1, _op2: tregister; _op3, _op4: aint);
+
+         { ADD,SUB,SLT,AND,OR,NOR,XOR,ANDN,ORN,MUL,MULH,MULW,DIV,MOD,SLL,SRL,SRA,
+           RTOR,MASKEQZ,MASKNEZ,LDGT,LDLE,STGT,STLE,AMSWAP,AMSWAP_DB,AMADD,AMADD_DB,
+           AMAND,AMAND_DB,AMOR,AMOR_DB,AMXOR,AMXOR_DB,AMMAX,AMMAX_DB,AMMIN,AMMIN_DB,
+           CRC,CRCC,FADD,FSUB,FMUL,FDIV,FMAX,FMIN,FMAXA,FMINA,FCALEB,FCOPYSIGN,FCMP,
+           FLDGT,FLDLE,FSTGT,FSTLE,}
+         constructor op_reg_reg_reg(op : tasmop;_op1,_op2,_op3 : tregister);
+
+         { ADDI,ADDU16I,LU52I,SLTI,ANDI,ORI,XORI,SLLI,SRLI,SRAI,ROTRI,JIRL,LL,SC,
+           CSRXCHG,LDDIR,}
+         constructor op_reg_reg_const(op : tasmop;_op1,_op2 : tregister; _op3: aint);
+
+         { BEQ,BNE,BLT,BGE,BLTU,BGEU,BGT,BLE,BGTU,BLEU, }
+         constructor op_reg_reg_ref(op : tasmop;_op1,_op2 : tregister; const _op3: treference);
+
+         { PRELD,CACOP, }
+         constructor op_const_reg_const(op : tasmop;_op1 : aint;_op2 : tregister;_op3 : aint);
+
+         { PRELDX,INVTLB }
+         constructor op_const_reg_reg(op : tasmop; _op1 : aint; _op2,_op3 : tregister);
+
+         { FMADD,FMSUB,FNMADD,FNMSUB,FSEL,}
+         constructor op_reg_reg_reg_reg(op : tasmop;_op1,_op2,_op3,_op4 : tregister);
+
+         { ALSL,BYTEPICK, }
+         constructor op_reg_reg_reg_const(op : tasmop; _op1, _op2, _op3 : tregister; _op4 : aint);
+
+         function is_same_reg_move(regtype: Tregistertype):boolean; override;
+
+         { register spilling code }
+         function spilling_get_operation_type(opnr: longint): topertype;override;
+         function spilling_get_operation_type_ref(opnr: longint; reg: tregister): topertype;override;
+      end;
+
+      tai_align = class(tai_align_abstract)
+        { nothing to add }
+      end;
+
+    procedure InitAsm;
+    procedure DoneAsm;
+
+
+    function spilling_create_load(const ref:treference;r:tregister):Taicpu;
+    function spilling_create_store(r:tregister; const ref:treference):Taicpu;
+
+implementation
+
+uses cutils, cclasses;
+
+{*****************************************************************************
+                                 taicpu Constructors
+*****************************************************************************}
+
+    constructor taicpu.op_none(op : tasmop);
+      begin
+         inherited create(op);
+      end;
+
+
+    constructor taicpu.op_const(op : tasmop;_op1 : aint);
+      begin
+         inherited create(op);
+         ops:=1;
+         loadconst(0,_op1);
+      end;
+
+
+    constructor taicpu.op_ref(op : tasmop;const _op1 : treference);
+      begin
+         inherited create(op);
+         ops:=1;
+         loadref(0,_op1);
+      end;
+
+
+    constructor taicpu.op_reg_reg(op : tasmop;_op1,_op2 : tregister);
+      begin
+         inherited create(op);
+         ops:=2;
+         loadreg(0,_op1);
+         loadreg(1,_op2);
+      end;
+
+
+    constructor taicpu.op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
+      begin
+         inherited create(op);
+         ops:=2;
+         loadreg(0,_op1);
+         loadref(1,_op2);
+      end;
+
+
+    constructor taicpu.op_reg_const(op:tasmop; _op1: tregister; _op2: aint);
+      begin
+         inherited create(op);
+         ops:=2;
+         loadreg(0,_op1);
+         loadconst(1,_op2);
+      end;
+
+
+    constructor taicpu.op_reg_reg_const_const(op: tasmop; _op1, _op2: tregister; _op3, _op4: aint);
+      begin
+        inherited create(op);
+        ops := 4;
+        loadreg(0, _op1);
+        loadreg(1, _op2);
+        loadconst(2, _op3);
+        loadconst(3, _op4);
+      end;
+
+
+    constructor taicpu.op_reg_reg_reg(op : tasmop;_op1,_op2,_op3 : tregister);
+      begin
+         inherited create(op);
+         ops:=3;
+         loadreg(0,_op1);
+         loadreg(1,_op2);
+         loadreg(2,_op3);
+      end;
+
+
+     constructor taicpu.op_reg_reg_const(op : tasmop;_op1,_op2 : tregister; _op3: aint);
+       begin
+         inherited create(op);
+         ops:=3;
+         loadreg(0,_op1);
+         loadreg(1,_op2);
+         loadconst(2,_op3);
+      end;
+
+
+     constructor taicpu.op_reg_reg_ref(op : tasmop;_op1,_op2 : tregister; const _op3: treference);
+       begin
+         inherited create(op);
+         ops:=3;
+         loadreg(0,_op1);
+         loadreg(1,_op2);
+         loadref(2,_op3);
+      end;
+
+
+    constructor taicpu.op_const_reg_reg(op : tasmop;_op1 : aint;_op2, _op3 : tregister);
+      begin
+         inherited create(op);
+         ops:=3;
+         loadconst(0,_op1);
+         loadreg(1,_op2);
+         loadreg(2,_op3);
+      end;
+
+
+     constructor taicpu.op_const_reg_const(op : tasmop;_op1 : aint;_op2 : tregister;_op3 : aint);
+      begin
+         inherited create(op);
+         ops:=3;
+         loadconst(0,_op1);
+         loadreg(1,_op2);
+         loadconst(2,_op3);
+      end;
+
+
+     constructor taicpu.op_reg_reg_reg_reg(op : tasmop;_op1,_op2,_op3,_op4 : tregister);
+      begin
+         inherited create(op);
+         ops:=4;
+         loadreg(0,_op1);
+         loadreg(1,_op2);
+         loadreg(2,_op3);
+         loadreg(3,_op4);
+      end;
+
+     constructor taicpu.op_reg_reg_reg_const(op : tasmop; _op1, _op2, _op3 : tregister; _op4 : aint);
+      begin
+        inherited create(op);
+        ops := 4;
+        loadreg(0, _op1);
+        loadreg(1, _op2);
+        loadreg(2, _op3);
+        loadconst(3, cardinal(_op4));
+      end;
+
+
+{ ****************************** newra stuff *************************** }
+
+    function taicpu.is_same_reg_move(regtype: Tregistertype):boolean;
+      begin
+        case regtype of
+          R_INTREGISTER:
+            result:=
+               (opcode=A_MOVE) and (oper[0]^.reg=oper[1]^.reg);
+          R_FPUREGISTER:
+            result:=
+               ((opcode=A_FMOV_S) or (opcode=A_FMOV_D)) and
+               (oper[0]^.reg=oper[1]^.reg);
+         else
+           result:=false;
+        end;
+      end;
+
+    { Mant of case is set opnr0 and read from opnr(others). }
+    function normal_oper_type(opnr: longint): topertype;
+      begin
+        if opnr=0 then
+          result:=operand_write
+        else
+          result:=operand_read;
+      end;
+
+
+    function taicpu.spilling_get_operation_type(opnr: longint): topertype;
+      begin
+        case opcode of
+          A_ST_B,A_ST_D,A_ST_W,A_ST_H,A_STX_B,A_STX_D,A_STX_W,A_STX_H,
+          A_STPTR_W,A_STPTR_D,A_STGT_B,A_STGT_D,A_STGT_W,A_STGT_H,
+          A_STLE_B,A_STLE_D,A_STLE_W,A_STLE_H,A_FST_S,A_FST_D,A_FSTX_S,
+          A_FSTX_D,A_FSTGT_S,A_FSTGT_D,A_FSTLE_S,A_FSTLE_D,A_CSRWR,
+          A_IOCSRWR_B,A_IOCSRWR_D,A_IOCSRWR_W,A_IOCSRWR_H:
+            result:=operand_read;
+          A_RDTIME_D,A_RDTIMEL_W,A_RDTIMEH_W:
+            result:=operand_write;
+          A_PRELD,A_PRELDX,A_DBAR,A_IBAR,A_SYSCALL,A_BREAK,A_ASRTLE_D,
+          A_ASRTGT_D,A_CACOP,A_INVTLB,A_DBCL,A_IDLE,A_BEQ,A_BNE,A_BLT,
+          A_BLTU,A_BGE,A_BGEU,A_BEQZ,A_BNEZ,A_B,A_BL,A_BCEQZ,A_BCNEZ,
+          A_BLTZ,A_BGTZ,A_BGEZ,A_BLEZ,A_BGT,A_BLE,A_BGTU,A_BLEU,A_JR,
+          A_NOP,A_BXX:
+            result:=operand_read;
+          A_AMSWAP_W,A_AMSWAP_D,A_AMSWAP_DB_W,A_AMSWAP_DB_D,A_AMADD_W,
+          A_AMADD_D,A_AMADD_DB_W,A_AMADD_DB_D,A_AMAND_W,A_AMAND_D,
+          A_AMAND_DB_W,A_AMAND_DB_D,A_AMOR_W,A_AMOR_D,A_AMOR_DB_W,
+          A_AMOR_DB_D,A_AMXOR_W,A_AMXOR_D,A_AMXOR_DB_W,A_AMXOR_DB_D,
+          A_AMMAX_W,A_AMMAX_DU,A_AMMAX_D,A_AMMAX_WU,A_AMMAX_DB_W,
+          A_AMMAX_DB_DU,A_AMMAX_DB_D,A_AMMAX_DB_WU,A_AMMIN_W,A_AMMIN_DU,
+          A_AMMIN_D,A_AMMIN_WU,A_AMMIN_DB_W,A_AMMIN_DB_DU,A_AMMIN_DB_D,
+          A_AMMIN_DB_WU,A_SC_W,A_SC_D:
+            result:=operand_readwrite;
+        else
+          result:=normal_oper_type(opnr);
+        end;
+      end;
+
+
+    function taicpu.spilling_get_operation_type_ref(opnr: longint; reg: tregister): topertype;
+      begin
+        result := operand_read;
+      end;
+
+
+    function spilling_create_load(const ref:treference;r:tregister):Taicpu;
+      var
+        href : treference;
+      begin
+        href:=ref;
+        href.refaddr:=addr_reg_12i;
+        case getregtype(r) of
+           R_INTREGISTER: result:=taicpu.op_reg_ref(A_LD_D,r,href);
+           R_FPUREGISTER: result:=taicpu.op_reg_ref(A_FLD_D,r,href);
+        else
+           internalerror(2022111904);
+        end;
+      end;
+
+
+    function spilling_create_store(r:tregister; const ref:treference):Taicpu;
+      var
+        href : treference;
+      begin
+        href:=ref;
+        href.refaddr:=addr_reg_12i;
+        case getregtype(r) of
+           R_INTREGISTER: result:=taicpu.op_reg_ref(A_ST_D,r,href);
+           R_FPUREGISTER: result:=taicpu.op_reg_ref(A_FST_D,r,href);
+        else
+           internalerror(2022111905);
+        end;
+      end;
+
+
+    procedure InitAsm;
+      begin
+      end;
+
+
+    procedure DoneAsm;
+      begin
+      end;
+
+
+begin
+  cai_align:=tai_align;
+  cai_cpu:=taicpu;
+end.

+ 216 - 0
compiler/loongarch64/agcpugas.pas

@@ -0,0 +1,216 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    This unit the GAS asm writers for LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+{****************************************************************************}
+{                  Helper routines for Instruction Writer                    }
+{****************************************************************************}
+
+unit agcpugas;
+
+{$i fpcdefs.inc}
+
+  interface
+
+    uses
+       systems,aasmbase,
+       aasmtai,aasmdata,
+       aggas,
+       assemble,
+       cpubase,cgutils,
+       globtype;
+
+  type
+    TLoongArch64InstrWriter=class(TCPUInstrWriter)
+       procedure WriteInstruction(hp : tai);override;
+    end;
+
+    TLoongArch64GNUAssembler=class(TGNUassembler)
+      constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
+    end;
+
+  var
+    curop: TAsmOp;
+
+  implementation
+
+    uses
+       cutils,globals,verbose,
+       cgbase,
+       itcpugas,cpuinfo,
+       aasmcpu;
+
+
+    function getreferencestring(asminfo: pasminfo; var ref : treference) : string;
+    var
+      s : string;
+    begin
+      { For LoongArch, we support only 4 cases
+        1. Symbol, symbol + addend (cannot used by got).
+        2. Reg + offset.
+        3. Reg + reg.
+        4. Reg
+      }
+      with ref do
+        begin
+          if assigned(relsymbol) then
+            internalerror(2022081001);
+          case refaddr of
+            addr_b16, addr_b21, addr_b26, addr_pcrel,
+            addr_plt, addr_abs_hi20, addr_abs_lo12,
+            addr_abs64_lo20, addr_abs64_hi12,
+            addr_pc_hi20, addr_got_pc_hi20,
+            addr_pc_lo12, addr_got_pc_lo12,
+            addr_got, addr_abs:
+              begin
+                if (base<>NR_NO) or (index<>NR_NO) then
+                  internalerror(2022081002)
+                else if (refaddr in [addr_got_pc_hi20,addr_got_pc_lo12,addr_got]) and (offset<>0) then
+                  internalerror(2022081003)
+                else if not assigned(symbol) then
+                  internalerror(2022081004);
+                s:=ApplyAsmSymbolRestrictions(symbol.name);
+                if offset<0 then
+                  s:=s+tostr(offset)
+                else if offset>0 then
+                  s:=s+'+'+tostr(offset);
+                case refaddr of
+                  addr_b16: s:='%b16('+s+')';
+                  addr_b21: s:='%b21('+s+')';
+                  addr_b26: s:='%b26('+s+')';
+                  addr_plt: s:='%plt('+s+')';
+                  addr_abs_hi20: s:='%abs_hi20('+s+')';
+                  addr_abs_lo12: s:='%abs_lo12('+s+')';
+                  addr_abs64_lo20: s:='%abs64_lo20('+s+')';
+                  addr_abs64_hi12: s:='%abs64_hi12('+s+')';
+                  addr_pc_hi20: s:='%pc_hi20('+s+')';
+                  addr_got_pc_hi20: s:='%got_pc_hi20('+s+')';
+                  addr_got_pc_lo12: s:='%got_pc_lo12('+s+')';
+                  addr_pc_lo12: s:='%pc_lo12('+s+')';
+                else
+                  ;
+                end;
+              end;
+            addr_reg_reg:
+              begin
+                if assigned(symbol) or (offset<>0) then
+                  internalerror(2022081005)
+                else if (base=NR_NO) or (index=NR_NO) then
+                  internalerror(2022081006);
+                s:=gas_regname(base)+','+gas_regname(index);
+              end;
+            addr_reg_12i, addr_reg_14i:
+              begin
+                if assigned(symbol) or (index<>NR_NO) then
+                  internalerror(2022081007)
+                else if (refaddr=addr_reg_12i) and (not is_simm12(offset)) then
+                  internalerror(2022081008)
+                else if (refaddr=addr_reg_14i) and (not is_simm16_and_quadruple(offset)) then
+                  internalerror(2022081009);
+                s:=gas_regname(base)+','+tostr(offset);
+              end;
+            addr_reg:
+              begin
+                if assigned(symbol) or (index<>NR_NO) or (offset<>0) then
+                  internalerror(2022081010)
+                else if (base=NR_NO) then
+                  internalerror(2022081011);
+                s:=gas_regname(base);
+              end;
+          else
+            internalerror(2022081012);
+          end;
+        end; { with ref do }
+      getreferencestring:=s;
+    end;
+
+
+    function getopstr(asminfo: pasminfo; const o:toper) : string;
+    var
+      hs : string;
+    begin
+      case o.typ of
+        top_reg:
+          getopstr:=gas_regname(o.reg);
+        top_const:
+          getopstr:=tostr(o.val);
+        top_ref:
+          getopstr:=getreferencestring(asminfo,o.ref^);
+      else
+        internalerror(2022111901);
+      end;
+    end;
+
+
+    Procedure TLoongArch64InstrWriter.WriteInstruction(hp : tai);
+    var op: TAsmOp;
+        s: string;
+        i: byte;
+        sep: string[3];
+    begin
+      s:=#9+gas_op2str[taicpu(hp).opcode];
+      if taicpu(hp).condition<>C_None then
+        s:=s+cond2str[taicpu(hp).condition];
+
+      curop:=taicpu(hp).opcode;
+      if taicpu(hp).ops<>0 then
+        begin
+          sep:=#9;
+          for i:=0 to taicpu(hp).ops-1 do
+            begin
+               s:=s+sep+getopstr(owner.asminfo,taicpu(hp).oper[i]^);
+               sep:=',';
+            end;
+        end;
+
+      owner.writer.AsmWriteLn(s);
+    end;
+
+
+{****************************************************************************}
+{                     GNU LoongArch Assembler writer                         }
+{****************************************************************************}
+
+    constructor TLoongArch64GNUAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
+      begin
+        inherited;
+        InstrWriter := TLoongArch64InstrWriter.create(self);
+      end;
+
+
+  const
+    as_loongarch64_gas_info : tasminfo =
+       (
+         id     : as_gas;
+         idtxt  : 'AS';
+         asmbin : 'as';
+         asmcmd : '-o $OBJ $EXTRAOPT -mabi=lp64d $ASM';
+         supported_targets : [];
+         flags : [af_needar,af_smartlink_sections];
+         labelprefix : '.L';
+         labelmaxlen : -1;
+         comment : '# ';
+         dollarsign: '$';
+       );
+
+begin
+  RegisterAssembler(as_loongarch64_gas_info,TLoongArch64GNUAssembler);
+end.

+ 152 - 0
compiler/loongarch64/aoptcpu.pas

@@ -0,0 +1,152 @@
+{
+    Copyright (c) 1998-2002 by Jonas Maebe, member of the Free Pascal
+    Development Team
+
+    This unit implements the LoongArch64 optimizer object
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+unit aoptcpu;
+
+interface
+
+{$I fpcdefs.inc}
+
+{$define DEBUG_AOPTCPU}
+
+uses
+  cpubase,
+  globals, globtype,
+  cgbase, cutils,
+  aoptobj, aoptcpub, aopt,
+  aasmtai, aasmcpu;
+
+type
+
+  TCpuAsmOptimizer = class(TAsmOptimizer)
+    function PeepHoleOptPass1Cpu(var p: tai): boolean; override;
+    function RegLoadedWithNewValue(reg: tregister; hp: tai): boolean; override;
+    function GetNextInstructionUsingReg(Current: tai; Out Next: tai; reg: TRegister): Boolean;
+    function OptPass1Mov(var p: tai): Boolean;
+  end;
+
+implementation
+
+  function TCpuAsmOptimizer.RegLoadedWithNewValue(reg: tregister; hp: tai): boolean;
+    var
+      p: taicpu;
+    begin
+      result:=false;
+      if not ((assigned(hp)) and (hp.typ = ait_instruction)) then
+        exit;
+      p := taicpu(hp);
+      if p.ops=0 then
+        exit;
+      case p.oper[0]^.typ of
+        top_reg:
+          result:=(SuperRegistersEqual(p.oper[0]^.reg,reg)) and
+                  (p.spilling_get_operation_type(0)<>operand_read);
+      else
+          ;
+      end;
+    end;
+
+  function TCpuAsmOptimizer.GetNextInstructionUsingReg(Current: tai; out Next: tai; reg: TRegister): Boolean;
+    begin
+      Next:=Current;
+      repeat
+        Result:=GetNextInstruction(Next,Next);
+      until not (Result) or
+            not(cs_opt_level3 in current_settings.optimizerswitches) or
+            (Next.typ<>ait_instruction) or
+            RegInInstruction(reg,Next) or
+            is_calljmp(taicpu(Next).opcode);
+    end;
+
+  function MatchInstruction(const instr: tai; const op: TAsmOp; const AConditions: TAsmConds = []): boolean;
+    begin
+      result :=
+        (instr.typ = ait_instruction) and
+        (taicpu(instr).opcode = op) and
+        ((AConditions=[]) or (taicpu(instr).condition in AConditions));
+    end;
+
+
+  function MatchOperand(const oper1: TOper; const oper2: TOper): boolean; inline;
+    begin
+      result := oper1.typ = oper2.typ;
+
+      if result then
+        case oper1.typ of
+          top_const:
+            Result:=oper1.val = oper2.val;
+          top_reg:
+            Result:=oper1.reg = oper2.reg;
+          {top_ref:
+            Result:=RefsEqual(oper1.ref^, oper2.ref^);}
+          else Result:=false;
+        end
+    end;
+
+
+  function MatchOperand(const oper: TOper; const reg: TRegister): boolean; inline;
+    begin
+      result := (oper.typ = top_reg) and (oper.reg = reg);
+    end;
+
+  function TCpuAsmOptimizer.OptPass1Mov(var p: tai): Boolean;
+    var
+      hp1: tai;
+      alloc, dealloc: tai_regalloc;
+    begin
+      {
+        change
+        mov reg0,reg1
+        mov reg1,reg0
+        into
+        mov reg0,reg1
+      }
+      Result := False;
+      while GetNextInstruction(p, hp1) and
+        MatchInstruction(hp1, A_MOVE, [taicpu(p).condition]) and
+        MatchOperand(taicpu(p).oper[0]^, taicpu(hp1).oper[1]^) and
+        MatchOperand(taicpu(p).oper[1]^, taicpu(hp1).oper[0]^) do
+        begin
+          asml.Remove(hp1);
+          hp1.free;
+          Result:=true;
+        end;
+    end;
+
+  function TCpuAsmOptimizer.PeepHoleOptPass1Cpu(var p: tai): boolean;
+    begin
+      result := false;
+      if p.typ=ait_instruction then
+          begin
+            case taicpu(p).opcode of
+            A_MOVE:
+                Result:=OptPass1Mov(p);
+            else
+                ;
+            end;
+          end;
+    end;
+
+begin
+  casmoptimizer := TCpuAsmOptimizer;
+end.

+ 106 - 0
compiler/loongarch64/aoptcpub.pas

@@ -0,0 +1,106 @@
+{
+   Copyright (c) 1998-2002 by Jonas Maebe, member of the Free Pascal
+   Development Team
+
+   This unit contains several types and constants necessary for the
+   optimizer to work on the LoongArch64 architecture
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+****************************************************************************
+}
+unit aoptcpub; { Assembler OPTimizer CPU specific Base }
+
+{$I fpcdefs.inc}
+
+interface
+
+{ TODO }
+
+uses
+  aasmcpu, AOptBase, cpubase;
+
+type
+
+  { type of a normal instruction }
+  TInstr = Taicpu;
+  PInstr = ^TInstr;
+
+  { ************************************************************************* }
+  { **************************** TCondRegs ********************************** }
+  { ************************************************************************* }
+  { Info about the conditional registers                                      }
+  TCondRegs = object
+    constructor Init;
+    destructor Done;
+  end;
+
+  { ************************************************************************* }
+  { **************************** TAoptBaseCpu ******************************* }
+  { ************************************************************************* }
+
+  TAoptBaseCpu = class(TAoptBase)
+  end;
+
+  { ************************************************************************* }
+  { ******************************* Constants ******************************* }
+  { ************************************************************************* }
+const
+
+  { the maximum number of things (registers, memory, ...) a single instruction }
+  { changes                                                                    }
+
+  MaxCh = 3;
+
+  {Oper index of operand that contains the source (reference) with a load }
+  {instruction                                                            }
+
+  LoadSrc = 1;
+
+  {Oper index of operand that contains the destination (register) with a load }
+  {instruction                                                                }
+
+  LoadDst = 0;
+
+  {Oper index of operand that contains the source (register) with a store }
+  {instruction                                                            }
+
+  StoreSrc = 0;
+
+  {Oper index of operand that contains the destination (reference) with a load }
+  {instruction                                                                 }
+
+  StoreDst = 1;
+
+  aopt_uncondjmp = A_B;
+  aopt_condjmp = A_BXX;
+
+implementation
+
+{ ************************************************************************* }
+{ **************************** TCondRegs ********************************** }
+{ ************************************************************************* }
+
+constructor TCondRegs.init;
+begin
+end;
+
+destructor TCondRegs.Done;
+{$IFDEF inl}inline;
+{$ENDIF inl}
+begin
+end;
+
+end.

+ 39 - 0
compiler/loongarch64/aoptcpuc.pas

@@ -0,0 +1,39 @@
+{
+   Copyright (c) 1998-2002 by Jonas Maebe, member of the Free Pascal
+   Development Team
+
+   This unit contains the processor specific implementation of the
+   assembler optimizer common subexpression elimination object.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+****************************************************************************
+}
+unit aoptcpuc;
+
+interface
+
+{$I fpcdefs.inc}
+
+uses
+  AOptCs;
+
+type
+  TRegInfoCpu = object(TRegInfo)
+  end;
+
+implementation
+
+end.

+ 40 - 0
compiler/loongarch64/aoptcpud.pas

@@ -0,0 +1,40 @@
+{
+    Copyright (c) 1998-2002 by Jonas Maebe, member of the Free Pascal
+    Development Team
+
+    This unit contains the processor specific implementation of the
+    assembler optimizer data flow analyzer.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+Unit aoptcpud;
+
+{$i fpcdefs.inc}
+
+Interface
+
+uses
+  AOptDA;
+
+Type
+  TAOptDFACpu = class(TAOptDFA)
+  End;
+
+Implementation
+
+
+End.

+ 1604 - 0
compiler/loongarch64/cgcpu.pas

@@ -0,0 +1,1604 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    This unit implements the code generator for the LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit cgcpu;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      globtype, symtype, symdef, symsym,
+      cgbase, cgobj,
+      aasmbase, aasmcpu, aasmtai,aasmdata,
+      cpubase, cpuinfo, cgutils, rgcpu,
+      parabase;
+
+    type
+      tfixref = (
+        fr_normal, { Normal type, reg + 12i }
+        fr_big, { Reg + 14i }
+        fr_reg { Reg + reg }
+      );
+      tcgloongarch64 = class(tcg)
+      public
+        procedure init_register_allocators; override;
+        procedure done_register_allocators; override;
+
+        procedure a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const cgpara : TCGPara);override;
+        { call instructions }
+        procedure a_call_name(list : TAsmList;const s : string; weak: boolean); override;
+        procedure a_call_reg(list : TAsmList;reg: tregister); override;
+
+        { move instructions }
+        procedure a_load_const_reg(list: TAsmList; size: tcgsize; a: tcgint; register: tregister); override;
+        procedure a_load_const_ref(list: TAsmList; size: tcgsize; a: tcgint; const ref: treference); override;
+        procedure a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize; reg: tregister; const ref: treference); override;
+        procedure a_load_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override;
+        procedure a_load_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override;
+        procedure a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);override;
+
+        { bit scan instructions }
+        procedure a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); override;
+
+        { fpu move instructions }
+        procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override;
+        procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override;
+        procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); override;
+
+        { basic arithmetic operations }
+        procedure a_op_const_reg(list: TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister); override;
+        procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: TCGSize; src, dst: TRegister); override;
+        procedure a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister); override;
+        procedure a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister); override;
+
+        { comparison operations }
+        procedure a_cmp_const_reg_label(list: TAsmList; size: tcgsize; cmp_op: topcmp; a: tcgint; reg: tregister; l: tasmlabel); override;
+        procedure a_cmp_reg_reg_label(list: TAsmList; size: tcgsize; cmp_op: topcmp; reg1, reg2: tregister; l: tasmlabel); override;
+
+        { jump instructions }
+        procedure a_jmp_name(list: TAsmList;const s : string); override;
+        procedure a_jmp_always(list: TAsmList;l: tasmlabel); override;
+
+        procedure a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
+        procedure a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
+
+
+        { mem operations }
+        procedure g_concatcopy(list: TAsmList; const source, dest: treference; len: aint); override;
+
+        { overflow check }
+        procedure g_overflowcheck(list: TAsmList; const Loc: tlocation; def: tdef); override;
+
+        { proc entry and exit }
+        procedure g_proc_entry(list: TAsmList; localsize: longint; nostackframe: boolean); override;
+        procedure g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean); override;
+
+      protected
+        function fixref(list: TAsmList; var ref: treference; mode : tfixref): boolean;
+        procedure maybeadjustresult(list: TAsmList; op: topcg; size: tcgsize; dst: tregister);
+      end;
+
+    procedure create_codegen;
+
+  { OP_NONE,OP_MOVE,OP_ADD,OP_AND,OP_DIV,OP_IDIV,OP_IMUL,OP_MUL,OP_NEG,
+    OP_NOT,OP_OR,OP_SAR,OP_SHL,OP_SHR,OP_SUB,OP_XOR,OP_ROL,OP_ROR }
+  const
+    TOpCG2AsmConstOp32: Array[topcg] of TAsmOp = (A_NONE,
+          A_NONE,A_ADDI_W,A_ANDI,A_NONE,A_NONE,A_NONE,A_NONE,
+          A_NONE,A_NONE,A_ORI,A_SRAI_W,A_SLLI_W,A_SRLI_W,
+          A_NONE,A_XORI,A_NONE,A_ROTRI_W);
+    TOpCG2AsmOp32: Array[topcg] of TAsmOp = (A_NONE,
+          A_NONE,A_ADD_W,A_AND,A_DIV_WU,A_DIV_W,A_MUL_W,
+          A_MULW_D_WU,A_NONE,A_NONE,A_OR,A_SRA_W,A_SLL_W,A_SRL_W,
+          A_SUB_W,A_XOR,A_NONE,A_ROTR_W);
+    TOpCG2AsmConstOp: Array[topcg] of TAsmOp = (A_NONE,
+          A_NONE,A_ADDI_D,A_ANDI,A_NONE,A_NONE,A_NONE,A_NONE,
+          A_NONE,A_NONE,A_ORI,A_SRAI_D,A_SLLI_D,A_SRLI_D,
+          A_NONE,A_XORI,A_NONE,A_ROTRI_D);
+    TOpCG2AsmOp: Array[topcg] of TAsmOp = (A_NONE,
+          A_NONE,A_ADD_D,A_AND,A_DIV_DU,A_DIV_D,A_MUL_D,
+          A_MUL_D,A_NONE,A_NONE,A_OR,A_SRA_D,A_SLL_D,A_SRL_D,
+          A_SUB_D,A_XOR,A_NONE,A_ROTR_D);
+
+
+implementation
+
+    uses
+      sysutils, cclasses,
+      globals, verbose, systems, cutils,
+      symconst, fmodule, symtable,
+      rgobj, tgobj, cpupi, procinfo, paramgr, cpupara;
+
+
+    procedure tcgloongarch64.init_register_allocators;
+      begin
+        inherited init_register_allocators;
+        { From GCC REG_ALLOC_ORDER }
+        rg[R_INTREGISTER]:=trgintcpu.create(R_INTREGISTER,R_SUBWHOLE,
+          [RS_R12,RS_R13,RS_R14,RS_R16,RS_R16,RS_R17,RS_R18,RS_R19,RS_R20,
+           RS_R4,RS_R5,RS_R6,RS_R7,RS_R8,RS_R9,RS_R10,RS_R11,
+           RS_R1,
+           RS_R23,RS_R24,RS_R25,RS_R26,RS_R27,RS_R28,RS_R29,RS_R30,RS_R31],first_int_imreg,[]);
+        rg[R_FPUREGISTER]:=trgcpu.create(R_FPUREGISTER,R_SUBNONE,
+          [RS_F0,RS_F1,RS_F2,RS_F3,RS_F4,RS_F5,RS_F6,RS_F7,RS_F8,
+           RS_F9,RS_F10,RS_F11,RS_F12,RS_F13,RS_F14,RS_F15,RS_F16,
+           RS_F17,RS_F18,RS_F19,RS_F20,RS_F21,RS_F22,RS_F23,RS_F24,
+           RS_F25,RS_F26,RS_F27,RS_F28,RS_F29,RS_F30,RS_F31],first_fpu_imreg,[]);
+      end;
+
+
+    procedure tcgloongarch64.done_register_allocators;
+      begin
+        rg[R_INTREGISTER].free;
+        rg[R_FPUREGISTER].free;
+        inherited done_register_allocators;
+      end;
+
+
+    procedure tcgloongarch64.a_call_reg(list : TAsmList;reg: tregister);
+      begin
+        list.concat(taicpu.op_reg_reg_const(A_JIRL,NR_RETURN_ADDRESS_REG,reg,0));
+        include(current_procinfo.flags,pi_do_call);
+      end;
+
+
+    procedure tcgloongarch64.a_call_name(list : TAsmList;const s : string; weak: boolean);
+      var
+        href: treference;
+      begin
+        if not(weak) then
+          reference_reset_symbol(href,current_asmdata.RefAsmSymbol(s,AT_FUNCTION),0,0,[])
+        else
+          reference_reset_symbol(href,current_asmdata.WeakRefAsmSymbol(s,AT_FUNCTION),0,0,[]);
+        if cs_create_pic in current_settings.moduleswitches then
+          { PIC need using global symbol through GOT.  }
+          begin
+            href.refaddr:=addr_plt;
+            list.concat(taicpu.op_ref(A_BL,href));
+          end
+        else
+          begin
+            if not(weak) then
+              href.refaddr:=addr_pcrel
+            else
+              href.refaddr:=addr_plt;
+            list.concat(taicpu.op_ref(A_BL,href));
+          end;
+        { not assigned while generating external wrappers }
+        if assigned(current_procinfo) then
+          include(current_procinfo.flags,pi_do_call);
+      end;
+
+
+    procedure tcgloongarch64.a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const cgpara : TCGPara);
+      var
+        tmpref, ref: treference;
+        tmpreg: tregister;
+        location: pcgparalocation;
+        orgsizeleft,
+        sizeleft: tcgint;
+        usesize: tcgsize;
+        reghasvalue: boolean;
+      begin
+        location:=cgpara.location;
+        tmpref:=r;
+        sizeleft:=cgpara.intsize;
+        repeat
+          paramanager.allocparaloc(list,location);
+          case location^.loc of
+            LOC_REGISTER,LOC_CREGISTER:
+              begin
+                 if (size<>OS_NO) and
+                    (tcgsize2size[size]<=sizeof(aint)) then
+                   begin
+                     a_load_ref_reg(list,size,location^.size,tmpref,location^.register);
+                     if location^.shiftval<0 then
+                       a_op_const_reg(list,OP_SHL,location^.size,-location^.shiftval,location^.register);
+                   end
+                 { there's a lot more data left, and the current paraloc's
+                   register is entirely filled with part of that data }
+                 else if (sizeleft>sizeof(aint)) then
+                   begin
+                     a_load_ref_reg(list,location^.size,location^.size,tmpref,location^.register);
+                   end
+                 { we're at the end of the data, and it can be loaded into
+                   the current location's register with a single regular
+                   load }
+                 else if sizeleft in [1,2,4,8] then
+                   begin
+                     a_load_ref_reg(list,int_cgsize(sizeleft),location^.size,tmpref,location^.register);
+                     if location^.shiftval<0 then
+                       a_op_const_reg(list,OP_SHL,location^.size,-location^.shiftval,location^.register);
+                   end
+                 { we're at the end of the data, and we need multiple loads
+                   to get it in the register because it's an irregular size }
+                 else
+                   begin
+                     { should be the last part }
+                     if assigned(location^.next) then
+                       internalerror(2022111934);
+                     { load the value piecewise to get it into the register }
+                     orgsizeleft:=sizeleft;
+                     reghasvalue:=false;
+{$ifdef cpu64bitalu}
+                     if sizeleft>=4 then
+                       begin
+                         a_load_ref_reg(list,OS_32,location^.size,tmpref,location^.register);
+                         dec(sizeleft,4);
+                         inc(tmpref.offset,4);
+                         reghasvalue:=true;
+                       end;
+{$endif cpu64bitalu}
+                     if sizeleft>=2 then
+                       begin
+                         tmpreg:=getintregister(list,location^.size);
+                         a_load_ref_reg(list,OS_16,location^.size,tmpref,tmpreg);
+                         dec(sizeleft,2);
+                         if reghasvalue then
+                           begin
+                             a_op_const_reg(list,OP_SHL,location^.size,(orgsizeleft-(sizeleft+2))*8,tmpreg);
+                             a_op_reg_reg(list,OP_OR,location^.size,tmpreg,location^.register);
+                           end
+                         else
+                           begin
+                             a_load_reg_reg(list,location^.size,location^.size,tmpreg,location^.register);
+                           end;
+                         inc(tmpref.offset,2);
+                         reghasvalue:=true;
+                       end;
+                     if sizeleft=1 then
+                       begin
+                         tmpreg:=getintregister(list,location^.size);
+                         a_load_ref_reg(list,OS_8,location^.size,tmpref,tmpreg);
+                         dec(sizeleft,1);
+                         if reghasvalue then
+                           begin
+                             a_op_const_reg(list,OP_SHL,location^.size,(orgsizeleft-(sizeleft+1))*8,tmpreg);
+                             a_op_reg_reg(list,OP_OR,location^.size,tmpreg,location^.register)
+                           end
+                         else
+                           a_load_reg_reg(list,location^.size,location^.size,tmpreg,location^.register);
+                         inc(tmpref.offset);
+                       end;
+                     if location^.shiftval<0 then
+                       a_op_const_reg(list,OP_SHL,location^.size,-location^.shiftval,location^.register);
+                     { the loop will already adjust the offset and sizeleft }
+                     dec(tmpref.offset,orgsizeleft);
+                     sizeleft:=orgsizeleft;
+                   end;
+              end;
+            LOC_REFERENCE,LOC_CREFERENCE:
+              begin
+                reference_reset_base(ref,location^.reference.index,location^.reference.offset,ctempposinvalid,newalignment(cgpara.alignment,cgpara.intsize-sizeleft),[]);
+                a_load_ref_cgparalocref(list,size,sizeleft,tmpref,ref,cgpara,location);
+              end;
+            LOC_FPUREGISTER,LOC_CFPUREGISTER:
+              begin
+                { can be not a float size in case of a record passed in fpu registers }
+                { the size comparison is to catch F128 passed in two 64 bit floating point registers }
+                if is_float_cgsize(size) and
+                   (tcgsize2size[location^.size]>=tcgsize2size[size]) then
+                  usesize:=size
+                else
+                  usesize:=location^.size;
+                a_loadfpu_ref_reg(list,usesize,location^.size,tmpref,location^.register);
+              end
+            else
+              internalerror(2022111935);
+          end;
+          inc(tmpref.offset,tcgsize2size[location^.size]);
+          dec(sizeleft,tcgsize2size[location^.size]);
+          location:=location^.next;
+        until not assigned(location);
+      end;
+
+    procedure tcgloongarch64.a_load_const_ref(list: TAsmList; size: tcgsize; a: tcgint; const ref: treference);
+      begin
+        if a=0 then
+          a_load_reg_ref(list,size,size,NR_R0,ref)
+        else
+          inherited a_load_const_ref(list,size,a,ref);
+      end;
+
+
+    procedure tcgloongarch64.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
+        reg: tregister; const ref: treference);
+      var
+        href: treference;
+        op: TAsmOp;
+        hlist: TAsmList;
+      const
+        st_ops: array[boolean,OS_8..OS_INT] of TAsmOp = (
+          (A_ST_B,A_ST_H,A_ST_W,A_ST_D),
+          (A_STX_B,A_STX_H,A_STX_W,A_STX_D)
+        );
+        stptr_ops: array[OS_8..OS_INT] of TAsmOp = (A_NONE,A_NONE,A_STPTR_W,A_STPTR_D);
+      begin
+        if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111936);
+        if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111937);
+
+        hlist:=TAsmList.create;
+        tosize:=tcgsize2unsigned[tosize];
+        if stptr_ops[tosize]<>A_NONE then
+          begin
+            href:=ref;
+            if fixref(hlist,href,fr_big) then
+              begin
+                list.concatList(hlist);
+                hlist.free;
+                list.concat(taicpu.op_reg_ref(stptr_ops[tosize],reg,href));
+                exit;
+              end;
+          end;
+        hlist.Clear;
+        hlist.free;
+        href:=ref;
+        op:=st_ops[fixref(list,href,fr_reg),tosize];
+        list.concat(taicpu.op_reg_ref(op,reg,href));
+      end;
+
+
+    procedure tcgloongarch64.a_load_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
+      var
+        href: treference;
+        op: TAsmOp;
+        usizef,usizet: tcgsize;
+        have_done: boolean;
+        hlist: TAsmList;
+        samesign: boolean;
+      const
+        ld_ops: array[boolean,boolean,OS_8..OS_INT] of TAsmOp = (
+          ((A_LD_B,A_LD_H,A_LD_W,A_LD_D),
+           (A_LD_BU,A_LD_HU,A_LD_WU,A_LD_D)),
+          ((A_LDX_B,A_LDX_H,A_LDX_W,A_LDX_D),
+           (A_LDX_BU,A_LDX_HU,A_LDX_WU,A_LDX_D))
+        );
+      begin
+        if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111938);
+        if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111939);
+
+        hlist:=TAsmList.create;
+        have_done:=false;
+        usizef:=tcgsize2unsigned[fromsize];
+        usizet:=tcgsize2unsigned[tosize];
+        samesign:=((fromsize=usizef) and (tosize=usizet)) or ((fromsize<>usizef) and (tosize<>usizet));
+        if (fromsize=OS_S32) then
+          begin
+            href:=ref;
+            if fixref(hlist,href,fr_big) then
+              begin
+                hlist.concat(taicpu.op_reg_ref(A_LDPTR_W,reg,href));
+                have_done:=true;
+              end;
+          end
+        else if (fromsize=OS_S64) or (fromsize=OS_64) then
+          begin
+            href:=ref;
+            if fixref(hlist,href,fr_big) then
+              begin
+                hlist.concat(taicpu.op_reg_ref(A_LDPTR_D,reg,href));
+                have_done:=true;
+              end;
+          end;
+
+        if not(have_done) then
+          begin
+            hlist.Clear;
+            href:=ref;
+            op:=ld_ops[fixref(list,href,fr_reg),fromsize=usizef,usizef];
+            list.concat(taicpu.op_reg_ref(op,reg,href));
+          end
+        else
+          list.concatList(hlist);
+
+        hlist.free;
+        { First, when load a reg to OS_(S)INT, we use signed load operations.
+          Then, when fromsize is less or equal than tosize, we can not do
+          a_load_reg_reg because of signed load operations. }
+        if (fromsize<>tosize) and (not (tosize in [OS_SINT,OS_INT])) and (not((usizef<=usizet) and samesign)) then
+          a_load_reg_reg(list,fromsize,tosize,reg,reg);
+      end;
+
+
+    procedure tcgloongarch64.a_load_const_reg(list: TAsmList; size: tcgsize; a: tcgint; register: tregister);
+      begin
+        case size of
+          OS_S8: a:= tcgint(shortint(a));
+          OS_S16: a:= tcgint(smallint(a));
+          OS_S32: a:= tcgint(longint(a));
+        else
+          ;
+        end;
+        list.concat(taicpu.op_reg_const(A_LI_D,register,a));
+      end;
+
+
+    procedure tcgloongarch64.a_load_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
+      const
+        zeroextbits: array[OS_8..OS_INT] of longint=(7,15,31,63);
+        signextop: array[OS_8..OS_INT] of TAsmOp=(A_EXT_W_B,A_EXT_W_H,A_ADDI_W,A_MOVE);
+      var
+        ai: taicpu;
+        ufromsize,utosize: tcgsize;
+        ufrom,uto : boolean;
+      begin
+        if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111940);
+        if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
+          internalerror(2022111941);
+        ufromsize:=tcgsize2unsigned[fromsize];
+        utosize:=tcgsize2unsigned[tosize];
+        ufrom:=ufromsize=fromsize;
+        uto:=utosize=tosize;
+        if (fromsize=tosize) or ((ufromsize=OS_INT) and (utosize=OS_INT)) then
+          begin
+            ai:=taicpu.op_reg_reg(A_MOVE,reg2,reg1);
+            list.concat(ai);
+            rg[R_INTREGISTER].add_move_instruction(ai);
+          end
+        else if ufromsize>=utosize then
+          begin
+            if uto then
+              list.concat(taicpu.op_reg_reg_const_const(A_BSTRPICK_D,reg2,reg1,zeroextbits[utosize],0))
+            else if utosize=OS_32 then
+              list.concat(taicpu.op_reg_reg_const(signextop[utosize],reg2,reg1,0))
+            else
+              list.concat(taicpu.op_reg_reg(signextop[utosize],reg2,reg1));
+          end
+        else { ufromsize<utosize }
+          begin
+            if ufrom then
+              list.concat(taicpu.op_reg_reg_const_const(A_BSTRPICK_D,reg2,reg1,zeroextbits[ufromsize],0))
+            else
+              begin
+                if ufromsize=OS_32 then
+                  list.concat(taicpu.op_reg_reg_const(signextop[ufromsize],reg2,reg1,0))
+                else
+                  list.concat(taicpu.op_reg_reg(signextop[ufromsize],reg2,reg1));
+                if uto then
+                  list.concat(taicpu.op_reg_reg_const_const(A_BSTRPICK_D,reg2,reg2,zeroextbits[utosize],0));
+              end;
+          end;
+      end;
+
+
+    procedure tcgloongarch64.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
+      var
+        href: treference;
+        l: TAsmLabel;
+      begin
+        href:=ref;
+        fixref(list,href,fr_normal);
+        { Fixref, so simplely work here. }
+        if href.offset=0 then
+          a_load_reg_reg(list,OS_ADDR,OS_ADDR,href.base,r)
+        else if is_simm12(href.offset) and (href.base<>NR_NO) then
+          list.concat(taicpu.op_reg_reg_const(A_ADDI_D,r,href.base,href.offset))
+        else
+          internalerror(2022111942);
+      end;
+
+
+    procedure tcgloongarch64.a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister);
+      begin
+        internalerror(2022111943);
+      end;
+
+
+    { dstreg = dstreg op imm }
+    procedure tcgloongarch64.a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister);
+      begin
+        a_op_const_reg_reg(list,op,size,a,reg,reg);
+      end;
+
+
+    { dstreg = dstreg op srcreg }
+    procedure tcgloongarch64.a_op_reg_reg(list : TAsmList; Op: TOpCG; size: TCGSize; src, dst: TRegister);
+      begin
+        a_op_reg_reg_reg(list,op,size,src,dst,dst);
+      end;
+
+
+    procedure tcgloongarch64.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister);
+      var
+        tmpreg: TRegister;
+        usetmp: boolean;
+      begin
+        optimize_op_const(size,op,a);
+
+        if op=OP_NONE then
+          begin
+            a_load_reg_reg(list,size,size,src,dst);
+            exit;
+          end;
+
+        { Sub const to add -const }
+        if op=OP_SUB then
+          begin
+            op:=OP_ADD;
+            a:=-a;
+          end;
+
+        { Make sure shift operation const not overflow. }
+        if op in [OP_SAR,OP_SHL,OP_SHR] then
+          if (size in [OS_32,OS_S32]) and (a>31) then
+            a:=31
+          else if (size in [OS_16,OS_S16]) and (a>15) then
+            a:=15
+          else if (size in [OS_8,OS_S8]) and (a>7) then
+            a:=7
+          else if (size in [OS_64,OS_S64]) and (a>63) then
+            a:=63;
+
+        { Rotate imm bits left to rotate (size - imm) bits right. }
+        if op=OP_ROL then
+          begin
+            op:=OP_ROR;
+            if size in [OS_32,OS_S32] then
+              a:=32-a
+            else if size in [OS_16,OS_S16] then
+              a:=16-a
+            else if size in [OS_8,OS_S8] then
+              a:=8-a
+            else
+              a:=64-a;
+          end;
+
+        { Only effective imm can be used by insn operation. }
+        if ((is_simm12(a) and (op=OP_ADD)) or
+            (is_uimm12(a) and (op<>OP_ADD))) and
+            (TOpCG2AsmConstOp32[op]<>A_NONE)
+            { or (TOpCG2AsmConstOp[op]<>A_NONE)} then
+          begin
+            usetmp:=false;
+            { Size = 16bits or 8bits do special if rotate. }
+            if (size in [OS_16,OS_S16]) and (op=OP_ROR) then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                list.concat(taicpu.op_reg_reg_const_const(A_BSTRINS_W,tmpreg,src,31,16));
+                usetmp:=true;
+              end
+            else if (size in [OS_8,OS_S8]) and (op=OP_ROR) then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                list.concat(taicpu.op_reg_reg_const_const(A_BSTRINS_W,tmpreg,src,15,8));
+                usetmp:=true;
+              end
+            { Signext to 32bits if sra 16bits or 8bits}
+            else if (size in [OS_S16,OS_S8]) and (op=OP_SAR) then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                a_load_reg_reg(list,size,OS_S32,tmpreg,src);
+                usetmp:=true;
+              end;
+
+            if size in [OS_64,OS_S64] then
+              begin
+                { Use tmp cannot be true here. }
+                list.concat(taicpu.op_reg_reg_const(TOpCG2AsmConstOp[op],dst,src,a));
+                maybeadjustresult(list,op,size,dst);
+              end
+            else { OS_32/S32, OS_16/S16, OS_8/S8 }
+              begin
+                if usetmp then
+                  list.concat(taicpu.op_reg_reg_const(TOpCG2AsmConstOp32[op],dst,tmpreg,a))
+                else
+                  list.concat(taicpu.op_reg_reg_const(TOpCG2AsmConstOp32[op],dst,src,a));
+                maybeadjustresult(list,op,size,dst);
+              end;
+          end
+        else
+          begin
+            tmpreg:=getintregister(list,OS_INT);
+            a_load_const_reg(list,size,a,tmpreg);
+            a_op_reg_reg_reg(list,op,size,tmpreg,src,dst);
+          end;
+      end;
+
+
+    procedure tcgloongarch64.a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister);
+      var
+        tmpreg1, tmpreg2: TRegister;
+        usetmp1, usetmp2: boolean;
+      begin
+        usetmp1:=false;
+        usetmp2:=false;
+        if op=OP_NOT then
+          begin
+            list.concat(taicpu.op_reg_reg_reg(A_NOR,dst,NR_R0,src1));
+            maybeadjustresult(list,op,size,dst);
+            exit;
+          end
+        else if op=OP_NEG then
+          begin
+            list.concat(taicpu.op_reg_reg_reg(A_SUB_D,dst,NR_R0,src1));
+            maybeadjustresult(list,op,size,dst);
+            exit;
+          end
+        else if op=OP_MOVE then
+          begin
+            a_load_reg_reg(list,size,size,src1,dst);
+            exit;
+          end
+        else if op=OP_ROL then
+          begin
+            tmpreg1:=getintregister(list,OS_INT);
+            list.concat(taicpu.op_reg_reg_reg(A_SUB_D,tmpreg1,NR_R0,src1));
+            usetmp1:=true;
+            op:=OP_ROR;
+          end;
+
+        if (TOpCG2AsmOp32[op]<>A_NONE) {or (TOpCG2AsmConstOp[op]<>A_NONE)} then
+          begin
+            { Size = 16bits or 8bits do special if rotate. }
+            if (size in [OS_16,OS_S16]) and (op=OP_ROR) then
+              begin
+                tmpreg2:=getintregister(list,OS_INT);
+                list.concat(taicpu.op_reg_reg_const_const(A_BSTRINS_W,tmpreg2,src2,31,16));
+                usetmp2:=true;
+              end
+            else if (size in [OS_8,OS_S8]) and (op=OP_ROR) then
+              begin
+                tmpreg2:=getintregister(list,OS_INT);
+                list.concat(taicpu.op_reg_reg_const_const(A_BSTRINS_W,tmpreg2,src2,15,8));
+                list.concat(taicpu.op_reg_reg_const_const(A_BSTRINS_W,tmpreg2,tmpreg2,31,16));
+                usetmp2:=true;
+              end
+            { Signext to 32bits if sra 16bits or 8bits}
+            else if (size in [OS_S16,OS_S8]) and (op=OP_SAR) then
+              begin
+                tmpreg2:=getintregister(list,OS_INT);
+                a_load_reg_reg(list,size,OS_S32,src2,tmpreg2);
+                usetmp2:=true;
+              end;
+
+            if size in [OS_64,OS_S64] then
+              begin
+                { usetmp2 cannot be true here. }
+                if usetmp1 then
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp[op],dst,src2,tmpreg1))
+                else
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp[op],dst,src2,src1));
+                maybeadjustresult(list,op,size,dst);
+              end
+            else
+              begin
+                if usetmp1 and usetmp2 then
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp32[op],dst,tmpreg2,tmpreg1))
+                else if usetmp1 then
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp32[op],dst,src2,tmpreg1))
+                else if usetmp2 then
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp32[op],dst,tmpreg2,src1))
+                else
+                  list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp32[op],dst,src2,src1));
+                maybeadjustresult(list,op,size,dst);
+              end;
+          end;
+      end;
+
+
+    procedure tcgloongarch64.a_cmp_const_reg_label(list: tasmlist; size: tcgsize; cmp_op: topcmp; a: tcgint; reg: tregister; l: tasmlabel);
+      var
+        tmpreg: tregister;
+      begin
+        if a = 0 then
+          a_cmp_reg_reg_label(list,size,cmp_op,NR_R0,reg,l)
+        else
+          begin
+            tmpreg := GetIntRegister(list,OS_INT);
+            a_load_const_reg(list,OS_INT,a,tmpreg);
+            a_cmp_reg_reg_label(list, size, cmp_op, tmpreg, reg, l);
+          end;
+      end;
+
+
+    procedure tcgloongarch64.a_cmp_reg_reg_label(list : TAsmList;size : tcgsize;cmp_op : topcmp; reg1,reg2 : tregister;l : tasmlabel);
+      const
+        { OC_NONE,OC_EQ,OC_GT,OC_LT,OC_GTE,OC_LTE,OC_NE,OC_BE,OC_B,OC_AE,OC_A }
+        TOpCmp2AsmCond: Array[TOpCmp] of TAsmCond = (C_NONE,
+          C_EQ,C_GT,C_LT,C_GE,C_LE,C_NE,C_LEU,C_LTU,C_GEU,C_GTU);
+        TOpCmp2AsmCondZ: Array[TOpCmp] of TAsmCond = (C_NONE,
+          C_EQZ,C_GTZ,C_LTZ,C_GEZ,C_LEZ,C_NEZ,C_NONE,C_NONE,C_NONE,C_NONE);
+      var
+        href: treference;
+        ai: taicpu;
+      begin
+        reference_reset_symbol(href,l,0,0,[]);
+        { It is better to use pcrel instead addr_b16 or addr_b21,
+          because we hardly know the real op after optimizing. }
+        href.refaddr:=addr_pcrel;
+        if (reg1=NR_R0) or (reg2=NR_R0) then
+          begin
+            if (reg1=NR_R0) and (reg2=NR_R0) then
+              begin
+                a_jmp_always(list,l);
+                exit;
+              end
+            else if reg2=NR_R0 then
+              begin
+                reg2:=reg1;
+                reg1:=NR_R0;
+                cmp_op:=swap_opcmp(cmp_op);
+              end;
+            if TOpCmp2AsmCondZ[cmp_op]<>C_NONE then
+              begin
+                ai:=taicpu.op_reg_ref(A_BXX,reg2,href);
+                ai.is_jmp:=true;
+                ai.condition:=TOpCmp2AsmCondZ[cmp_op];
+                list.concat(ai);
+                exit;
+              end;
+          end;
+        ai:=taicpu.op_reg_reg_ref(A_BXX,reg2,reg1,href);
+        ai.is_jmp:=true;
+        ai.condition:=TOpCmp2AsmCond[cmp_op];
+        list.concat(ai);
+      end;
+
+
+    procedure tcgloongarch64.a_jmp_name(list : TAsmList;const s : string);
+      var
+        ai: taicpu;
+        href: treference;
+      begin
+        reference_reset_symbol(href,current_asmdata.RefAsmSymbol(s,AT_FUNCTION),0,0,[]);
+        href.refaddr:=addr_b26;
+        ai:=taicpu.op_ref(A_B,href);
+        ai.is_jmp:=true;
+        list.concat(ai);
+      end;
+
+
+    procedure tcgloongarch64.a_jmp_always(list : TAsmList;l: tasmlabel);
+      var
+        ai: taicpu;
+        href: treference;
+      begin
+        reference_reset_symbol(href,l,0,0,[]);
+        href.refaddr:=addr_b26;
+        ai:=taicpu.op_ref(A_B,href);
+        ai.is_jmp:=true;
+        list.concat(ai);
+       end;
+
+
+    procedure tcgloongarch64.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
+      var
+        op: TAsmOp;
+        ai: taicpu;
+      const
+        FpuConvOp: array[OS_F32..OS_F64,OS_F32..OS_F64] of TAsmOp =
+                   ((A_FMOV_S,A_FCVT_D_S),(A_FCVT_S_D,A_FMOV_D));
+      begin
+        if (reg1<>reg2) or (fromsize<>tosize) then
+          begin
+            ai:= taicpu.op_reg_reg(fpuconvop[fromsize,tosize],reg2,reg1);
+            list.concat(ai);
+            if (fromsize=tosize) then
+              rg[R_FPUREGISTER].add_move_instruction(ai);
+          end;
+      end;
+
+
+    procedure tcgloongarch64.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
+      var
+        op: TAsmOp;
+        href: treference;
+      const
+        fld_ops: array[boolean,boolean] of TAsmOp = (
+          (A_FLD_D, A_FLD_S),
+          (A_FLDX_D, A_FLDX_S)
+        );
+      begin
+        href:=ref;
+        op:=fld_ops[fixref(list,href,fr_reg),fromsize=OS_F32];
+        list.concat(taicpu.op_reg_ref(op,reg,href));
+        if fromsize<>tosize then
+          a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg);
+      end;
+
+    procedure tcgloongarch64.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
+      var
+        op: TAsmOp;
+        tmpfreg: TRegister;
+        href: treference;
+        fst_ops: array[boolean,boolean] of TAsmOp = (
+          (A_FST_D, A_FST_S),
+          (A_FSTX_D, A_FSTX_S)
+        );
+      begin
+        if fromsize<>tosize then
+          begin
+            tmpfreg:=getfpuregister(list,tosize);
+            a_loadfpu_reg_reg(list,fromsize,tosize,reg,tmpfreg);
+            reg:=tmpfreg;
+          end;
+
+        href:=ref;
+        op:=fst_ops[fixref(list,href,fr_reg),tosize=OS_F32];
+        list.concat(taicpu.op_reg_ref(op,reg,href));
+      end;
+
+
+    procedure tcgloongarch64.g_concatcopy(list: TAsmList; const source, dest: treference; len: aint);
+      var
+        tmpreg,countreg: TRegister;
+        src,dst: TReference;
+        lab: tasmlabel;
+        len_8,tmplen: longint;
+        len_1,len_2,len_4: boolean;
+      begin
+        { Size is zero }
+        if len=0 then
+          exit;
+        { It's too large or illegal. }
+        if len>high(longint) then
+          internalerror(2022111944);
+        { len = d*8(+4?)(+2?)(+1?). }
+        len_8:=len div 8;
+        tmplen:=len-len_8*8;
+        len_1:=(tmplen and 1)<>0;
+        len_2:=(tmplen and 2)<>0;
+        len_4:=(tmplen and 4)<>0;
+
+        { Set the reference. }
+        reference_reset(src,sizeof(aint),[]);
+        reference_reset(dst,sizeof(aint),[]);
+        src.base:=GetAddressRegister(list);
+        dst.base:=GetAddressRegister(list);
+        src.refaddr:=addr_reg_12i;
+        dst.refaddr:=addr_reg_12i;
+        a_loadaddr_ref_reg(list,source,src.base);
+        a_loadaddr_ref_reg(list,dest,dst.base);
+        tmpreg:= GetIntRegister(list, OS_INT);
+        { TODO Some optimization. }
+        if len_8>0 then
+          begin
+            if len_8>1 then
+              begin
+                countreg := GetIntRegister(list,OS_INT);
+                a_load_const_reg(list,OS_INT,len_8,countreg);
+                current_asmdata.getjumplabel(lab);
+                a_label(list, lab);
+              end;
+            list.concat(taicpu.op_reg_ref(A_LD_D,tmpreg,src));
+            list.concat(taicpu.op_reg_ref(A_ST_D,tmpreg,dst));
+            if len_1 or len_2 or len_4 or (len_8>1) then
+              begin
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,src.base,src.base,8));
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,dst.base,dst.base,8));
+              end;
+            if len_8>1 then
+              begin
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,countreg,countreg,-1));
+                a_cmp_reg_reg_label(list,OS_INT,OC_GT,NR_R0,countreg,lab);
+              end;
+          end; { len_8>0 }
+        if len_4 then
+          begin
+            list.concat(taicpu.op_reg_ref(A_LD_W,tmpreg,src));
+            list.concat(taicpu.op_reg_ref(A_ST_W,tmpreg,dst));
+            inc(src.offset,4);
+            inc(dst.offset,4);
+          end;
+        if len_2 then
+          begin
+            list.concat(taicpu.op_reg_ref(A_LD_H,tmpreg,src));
+            list.concat(taicpu.op_reg_ref(A_ST_H,tmpreg,dst));
+            inc(src.offset,2);
+            inc(dst.offset,2);
+          end;
+        if len_1 then
+          begin
+            list.concat(taicpu.op_reg_ref(A_LD_B,tmpreg,src));
+            list.concat(taicpu.op_reg_ref(A_ST_B,tmpreg,dst));
+          end;
+      end;
+
+
+    procedure tcgloongarch64.g_overflowcheck(list: TAsmList; const loc: tlocation; def: tdef);
+      begin
+        { TODO }
+        { internalerror(2022111945); }
+      end;
+
+
+    procedure tcgloongarch64.g_proc_entry(list: TAsmList; localsize: longint; nostackframe: boolean);
+      var
+        regs, fregs: tcpuregisterset;
+        r: TSuperRegister;
+        href: treference;
+        stackcount, stackAdjust: longint;
+      begin
+        if not(nostackframe) then
+          begin
+            a_reg_alloc(list,NR_STACK_POINTER_REG);
+            { Always use $fp. }
+            a_reg_alloc(list,NR_FRAME_POINTER_REG);
+            { Int registers }
+            regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall);
+            regs:=regs+[RS_FRAME_POINTER_REG];
+            { Does it call another function? }
+            if (pi_do_call in current_procinfo.flags) or
+               (RS_R1 in rg[R_INTREGISTER].used_in_proc) then
+              regs:=regs+[RS_RETURN_ADDRESS_REG];
+            { Float registers }
+            fregs:=rg[R_FPUREGISTER].used_in_proc-paramanager.get_volatile_registers_fpu(pocall_stdcall);
+            { Calculate the stackcount of all regesters. }
+            stackcount:=16;
+            for r:=RS_R1 to RS_R31 do
+              if (r in regs) and (r<>RS_FRAME_POINTER_REG) and (r<>RS_RETURN_ADDRESS_REG) then
+                inc(stackcount,8);
+            for r:=RS_F0 to RS_F31 do
+              if r in fregs then
+                inc(stackcount,8);
+            { Calculate the frame total size. }
+            inc(localsize,stackcount);
+            if localsize=0 then
+              exit;
+            { ADDI instructions only has 12bits-sign-imm range, [-2048,2047]. Once we
+              use -2048 in the prologue, we cannot get back in epilogue use one ADDI.
+              Due to LoongArch psABI, we should save the $ra and then use it as free.
+              We should make $fp as $fp on entry, so ADDI cannot do 2048 size. It
+              seems use -2032 may a good choice without care of many of cases. }
+            if (-localsize<-2032) and (not (RS_RETURN_ADDRESS_REG in regs)) then
+              begin
+                regs:=regs+[RS_RETURN_ADDRESS_REG];
+                inc(localsize,8);
+                inc(stackcount,8);
+              end;
+            if (localsize mod 16)<>0 then
+              inc(localsize,16-(localsize mod 16));
+            { Do first decrease the stack, and record the stackadjust. }
+            stackadjust:=0;
+            if (-localsize<-2032) then
+              begin
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,-2032));
+                reference_reset_base(href,NR_STACK_POINTER_REG,2032-8,ctempposinvalid,0,[]);
+                href.refaddr:=addr_reg_12i;
+                stackadjust:=2032;
+                current_asmdata.asmcfi.cfa_def_cfa_offset(list,2032);
+              end
+            else
+              begin
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,-localsize));
+                reference_reset_base(href,NR_STACK_POINTER_REG,localsize-8,ctempposinvalid,0,[]);
+                href.refaddr:=addr_reg_12i;
+                stackadjust:=localsize;
+                current_asmdata.asmcfi.cfa_def_cfa_offset(list,localsize);
+              end;
+            { $ra in cfa -8. }
+            if RS_RETURN_ADDRESS_REG in regs then
+              begin
+                list.concat(taicpu.op_reg_ref(A_ST_D,newreg(R_INTREGISTER,RS_RETURN_ADDRESS_REG,R_SUBWHOLE),href));
+                current_asmdata.asmcfi.cfa_offset(list,NR_RETURN_ADDRESS_REG,-8);
+              end;
+            { $fp in cfa -16. }
+            dec(href.offset,8);
+            list.concat(taicpu.op_reg_ref(A_ST_D,newreg(R_INTREGISTER,RS_FRAME_POINTER_REG,R_SUBWHOLE),href));
+            current_asmdata.asmcfi.cfa_offset(list,NR_FRAME_POINTER_REG,-16);
+            { $fp = cfa. }
+            list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_FRAME_POINTER_REG,NR_STACK_POINTER_REG,stackadjust));
+            current_asmdata.asmcfi.cfa_def_cfa(list,NR_FRAME_POINTER_REG,0);
+            { Int registers }
+            for r:=RS_R1 to RS_R31 do
+              if (r in regs) and (r<>RS_FRAME_POINTER_REG) and (r<>RS_RETURN_ADDRESS_REG) then
+                begin
+                  dec(href.offset,8);
+                  list.concat(taicpu.op_reg_ref(A_ST_D,newreg(R_INTREGISTER,r,R_SUBWHOLE),href));
+                  current_asmdata.asmcfi.cfa_offset(list,newreg(R_INTREGISTER,r,R_SUBWHOLE),href.offset-stackadjust);
+                end;
+            { Float registers }
+            for r:=RS_F0 to RS_F31 do
+              if r in fregs then
+                begin
+                  dec(href.offset,8);
+                  list.concat(taicpu.op_reg_ref(A_FST_D,newreg(R_FPUREGISTER,r,R_SUBWHOLE),href));
+                  current_asmdata.asmcfi.cfa_offset(list,newreg(R_FPUREGISTER,r,R_SUBWHOLE),href.offset);
+                end;
+            { Decrese the remaining stack size. }
+            if (localsize-stackadjust)>2048 then
+              begin
+                a_load_const_reg(list,OS_INT,localsize-stackadjust,NR_RETURN_ADDRESS_REG);
+                list.concat(taicpu.op_reg_reg_reg(A_SUB_D,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,NR_RETURN_ADDRESS_REG));
+              end
+            else if (localsize-stackadjust)>0 then
+              begin
+                { TODO It seems to no need $ra. }
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,stackadjust-localsize));
+              end;
+          end;
+      end; { g_proc_entry }
+
+    procedure tcgloongarch64.g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean);
+      var
+        r: tsuperregister;
+        regs, fregs: tcpuregisterset;
+        stackcount, localsize, stackleft: longint;
+        href: treference;
+      begin
+        if not(nostackframe) then
+          begin
+            localsize:=current_procinfo.calc_stackframe_size;
+            regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall);
+            fregs:=rg[R_FPUREGISTER].used_in_proc-paramanager.get_volatile_registers_fpu(pocall_stdcall);
+            regs:=regs+[RS_FRAME_POINTER_REG];
+            if (pi_do_call in current_procinfo.flags) or
+               (RS_R1 in rg[R_INTREGISTER].used_in_proc) then
+              regs:=regs+[RS_RETURN_ADDRESS_REG];
+            stackcount:=16;
+            for r:=RS_R1 to RS_R31 do
+              if (r in regs) and (r<>RS_FRAME_POINTER_REG) and (r<>RS_RETURN_ADDRESS_REG) then
+                inc(stackcount,8);
+            for r:=RS_F0 to RS_F31 do
+              if r in fregs then
+                inc(stackcount,8);
+            inc(localsize,stackcount);
+            if (-localsize<-2032) and (not (RS_RETURN_ADDRESS_REG in regs)) then
+              begin
+                regs:=regs+[RS_RETURN_ADDRESS_REG];
+                inc(localsize,8);
+                inc(stackcount,8);
+              end;
+            if (localsize mod 16)<>0 then
+              inc(localsize,16-(localsize mod 16));
+
+            stackleft:=0;
+            if (-localsize<-2032) then
+              stackleft:=2032
+            else
+              stackleft:=localsize;
+            list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_STACK_POINTER_REG,NR_FRAME_POINTER_REG,-stackleft));
+            reference_reset_base(href,NR_STACK_POINTER_REG,stackleft-8,ctempposinvalid,0,[]);
+            href.refaddr:=addr_reg_12i;
+            { Restore registers. }
+            if RS_RETURN_ADDRESS_REG in regs then
+              begin
+                list.concat(taicpu.op_reg_ref(A_LD_D,newreg(R_INTREGISTER,RS_RETURN_ADDRESS_REG,R_SUBWHOLE),href));
+                current_asmdata.asmcfi.cfa_restore(list,NR_RETURN_ADDRESS_REG);
+              end;
+            dec(href.offset,8);
+            list.concat(taicpu.op_reg_ref(A_LD_D,newreg(R_INTREGISTER,RS_FRAME_POINTER_REG,R_SUBWHOLE),href));
+            current_asmdata.asmcfi.cfa_restore(list,NR_FRAME_POINTER_REG);
+            current_asmdata.asmcfi.cfa_def_cfa(list,NR_STACK_POINTER_REG,stackleft);
+            for r:=RS_R1 to RS_R31 do
+              if (r in regs) and (r<>RS_FRAME_POINTER_REG) and (r<>RS_RETURN_ADDRESS_REG) then
+                begin
+                  dec(href.offset,8);
+                  list.concat(taicpu.op_reg_ref(A_LD_D,newreg(R_INTREGISTER,r,R_SUBWHOLE),href));
+                  current_asmdata.asmcfi.cfa_restore(list,newreg(R_INTREGISTER,r,R_SUBWHOLE));
+                end;
+            for r:=RS_F0 to RS_F31 do
+              if r in fregs then
+                begin
+                  dec(href.offset,8);
+                  list.concat(taicpu.op_reg_ref(A_FLD_D,newreg(R_FPUREGISTER,r,R_SUBWHOLE),href));
+                  current_asmdata.asmcfi.cfa_restore(list,newreg(R_FPUREGISTER,r,R_SUBWHOLE));
+                end;
+            { Restore $sp. }
+            list.concat(taicpu.op_reg_reg_const(A_ADDI_D,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,stackleft));
+          end;
+        { jr $ra. }
+        reference_reset_base(href,NR_RETURN_ADDRESS_REG,0,ctempposinvalid,0,[]);
+        href.refaddr:=addr_reg;
+        list.concat(taicpu.op_ref(A_JR,href));
+      end; { g_proc_exit }
+
+
+    procedure tcgloongarch64.a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation);
+    var
+      signed: Boolean;
+      l: TAsmLabel;
+      tmpreg: tregister;
+      ai: taicpu;
+      href: treference;
+      tmpreg0,tmpreg1: tregister;
+    begin
+      if setflags then
+        begin
+          if is_simm12(a) and (op=OP_ADD) then
+            begin
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              if size in [OS_64,OS_S64,OS_32,OS_S32] then
+                begin
+                  { Backup src so we can compare with it. }
+                  tmpreg1:=getintregister(list,OS_INT);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src,tmpreg1);
+                end;
+              if size in [OS_64,OS_S64] then
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_D,dst,src,a))
+              else
+                list.concat(taicpu.op_reg_reg_const(A_ADDI_W,dst,src,a));
+              case size of
+                OS_S64,OS_S32:
+                  begin
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,dst,tmpreg1,href);
+                    if a<0 then
+                      ai.condition:=C_LT
+                    else
+                      ai.condition:=C_GE;
+                  end;
+                OS_S16,OS_S8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    a_load_reg_reg(list,OS_INT,size,dst,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_EQ;
+                  end;
+                OS_64,OS_32:
+                  begin
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,dst,tmpreg1,href);
+                    ai.condition:=C_GEU;
+                  end;
+                OS_16,OS_8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    if size=OS_16 then
+                      a_load_const_reg(list,OS_INT,1 shl 16,tmpreg0)
+                    else
+                      a_load_const_reg(list,OS_INT,1 shl 8,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_GTU;
+                  end;
+                else
+                  internalerror(2022082303);
+                end;
+                ai.is_jmp:=true;
+                list.concat(ai);
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end
+          else if op in [OP_ADD,OP_SUB,OP_MUL,OP_IMUL,OP_IDIV] then
+            begin
+              tmpreg:=getintregister(list,size);
+              a_load_const_reg(list,size,a,tmpreg);
+              a_op_reg_reg_reg_checkoverflow(list,op,size,tmpreg,src,dst,setflags,ovloc);
+            end
+          else
+            internalerror(2022082302);
+        end
+      else
+        a_op_const_reg_reg(list,op,size,a,src,dst);
+    end;
+
+
+    procedure tcgloongarch64.a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation);
+      var
+      signed: Boolean;
+      l: TAsmLabel;
+      tmpreg0,tmpreg1,tmpreg2,tmpreg3: tregister;
+      ai: taicpu;
+      href: treference;
+    begin
+      signed:=tcgsize2unsigned[size]<>size;
+
+      if setflags then
+        case op of
+          OP_ADD:
+            begin
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              if size in [OS_64,OS_S64,OS_32,OS_S32] then
+                begin
+                  { Backup src so we can compare with it. }
+                  tmpreg2:=getintregister(list,OS_INT);
+                  tmpreg3:=getintregister(list,OS_INT);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src1,tmpreg2);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src2,tmpreg3);
+                end;
+              if size in [OS_S64,OS_64] then
+                list.Concat(taicpu.op_reg_reg_reg(A_ADD_D,dst,src2,src1))
+              else
+                list.Concat(taicpu.op_reg_reg_reg(A_ADD_W,dst,src2,src1));
+              case size of
+                OS_S64,OS_S32:
+                  begin
+                    { if (src1<0)<>(dst<src2) overflow; }
+                    tmpreg0:=getintregister(list,OS_INT);
+                    tmpreg1:=getintregister(list,OS_INT);
+                    list.Concat(taicpu.op_reg_reg_const(A_SLTI,tmpreg0,tmpreg2,0));
+                    list.Concat(taicpu.op_reg_reg_reg(A_SLT,tmpreg1,dst,tmpreg3));
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,tmpreg1,href);
+                    ai.condition:=C_EQ;
+                  end;
+                OS_S16,OS_S8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    a_load_reg_reg(list,OS_INT,size,dst,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_EQ;
+                  end;
+                OS_64,OS_32:
+                  begin
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,dst,tmpreg3,href);
+                    ai.condition:=C_GEU;
+                  end;
+                OS_16,OS_8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    if size=OS_16 then
+                      a_load_const_reg(list,OS_INT,1 shl 16,tmpreg0)
+                    else
+                      a_load_const_reg(list,OS_INT,1 shl 8,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_GTU;
+                  end;
+              else
+                internalerror(2022082304);
+              end;
+              ai.is_jmp:=true;
+              list.concat(ai);
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end;
+          OP_SUB:
+            begin
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              if size in [OS_64,OS_S64,OS_32,OS_S32] then
+                begin
+                  { Backup src so we can compare with it. }
+                  tmpreg2:=getintregister(list,OS_INT);
+                  tmpreg3:=getintregister(list,OS_INT);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src1,tmpreg2);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src2,tmpreg3);
+                end;
+              if size in [OS_S64,OS_64] then
+                list.Concat(taicpu.op_reg_reg_reg(A_SUB_D,dst,src2,src1))
+              else
+                list.Concat(taicpu.op_reg_reg_reg(A_SUB_W,dst,src2,src1));
+              case size of
+                OS_S64,OS_S32:
+                  begin
+                    { if (src1<0)<>(dst<src2) overflow; }
+                    tmpreg0:=getintregister(list,OS_INT);
+                    tmpreg1:=getintregister(list,OS_INT);
+                    list.Concat(taicpu.op_reg_reg_reg(A_SLT,tmpreg0,NR_R0,tmpreg2));
+                    list.Concat(taicpu.op_reg_reg_reg(A_SLT,tmpreg1,dst,tmpreg3));
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,tmpreg1,href);
+                    ai.condition:=C_EQ;
+                  end;
+                OS_S16,OS_S8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    a_load_reg_reg(list,OS_INT,size,dst,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_EQ;
+                  end;
+                OS_64,OS_32:
+                  begin
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg3,dst,href);
+                    ai.condition:=C_GEU;
+                  end;
+                OS_16,OS_8:
+                  begin
+                    tmpreg0:=getintregister(list,OS_INT);
+                    if size=OS_16 then
+                      a_load_const_reg(list,OS_INT,1 shl 16,tmpreg0)
+                    else
+                      a_load_const_reg(list,OS_INT,1 shl 8,tmpreg0);
+                    ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,dst,href);
+                    ai.condition:=C_GTU;
+                  end;
+              else
+                internalerror(2022082305);
+              end;
+              ai.is_jmp:=true;
+              list.concat(ai);
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end;
+          OP_IMUL:
+            begin
+              { No overflow if upper result is same as sign of result }
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              tmpreg0:=getintregister(list,OS_INT);
+              tmpreg1:=getintregister(list,OS_INT);
+              if size in [OS_S64,OS_64] then
+                begin
+                  a_load_reg_reg(list,OS_INT,OS_INT,src1,tmpreg0);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src2,tmpreg1);
+                  list.Concat(taicpu.op_reg_reg_reg(A_MUL_D,dst,src1,src2));
+                  list.Concat(taicpu.op_reg_reg_reg(A_MULH_D,tmpreg0,tmpreg0,tmpreg1));
+                  list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg1,dst,63));
+                  ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,tmpreg1,href);
+                  ai.condition:=C_EQ;
+                end
+              else
+                begin
+                  list.Concat(taicpu.op_reg_reg_reg(A_MULW_D_W,dst,src1,src2));
+                  case size of
+                    OS_S32,OS_32:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,31));
+                    OS_S16,OS_16:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,15));
+                    OS_S8,OS_8:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,7));
+                  else
+                    internalerror(2022082306);
+                  end;
+                  list.concat(taicpu.op_reg_reg_const(A_ADDI_D,tmpreg0,tmpreg0,1));
+                  list.Concat(taicpu.op_reg_reg_const(A_SLTI,tmpreg1,tmpreg0,2));
+                  ai:=taicpu.op_reg_ref(A_BXX,tmpreg1,href);
+                  ai.condition:=C_NEZ;
+                end;
+              ai.is_jmp:=true;
+              list.concat(ai);
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end;
+          OP_MUL:
+            begin
+              { No overflow if upper result is 0 }
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              tmpreg0:=getintregister(list,OS_INT);
+              if size in [OS_S64,OS_64] then
+                begin
+                  tmpreg1:=getintregister(list,OS_INT);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src1,tmpreg0);
+                  a_load_reg_reg(list,OS_INT,OS_INT,src2,tmpreg1);
+                  list.Concat(taicpu.op_reg_reg_reg(A_MUL_D,dst,src1,src2));
+                  list.Concat(taicpu.op_reg_reg_reg(A_MULH_DU,tmpreg0,tmpreg0,tmpreg1));
+                  ai:=taicpu.op_reg_ref(A_BXX,tmpreg0,href);
+                  ai.condition:=C_EQZ;
+                end
+              else
+                begin
+                  list.Concat(taicpu.op_reg_reg_reg(A_MULW_D_WU,dst,src1,src2));
+                  case size of
+                    OS_S32,OS_32:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,31));
+                    OS_S16,OS_16:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,15));
+                    OS_S8,OS_8:
+                      list.concat(taicpu.op_reg_reg_const(A_SRAI_D,tmpreg0,dst,7));
+                  else
+                    internalerror(2022082307);
+                  end;
+                  ai:=taicpu.op_reg_ref(A_BXX,tmpreg0,href);
+                  ai.condition:=C_EQZ;
+                end;
+              ai.is_jmp:=true;
+              list.concat(ai);
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end;
+          OP_IDIV:
+            begin
+              { Only overflow if -2^(N-1)/(-1)  }
+              current_asmdata.getjumplabel(l);
+              reference_reset_symbol(href,l,0,0,[]);
+              href.refaddr:=addr_pcrel;
+              tmpreg2:=getintregister(list,OS_INT);
+              tmpreg3:=getintregister(list,OS_INT);
+              a_load_reg_reg(list,OS_INT,OS_INT,src1,tmpreg2);
+              a_load_reg_reg(list,OS_INT,OS_INT,src2,tmpreg3);
+              if size in [OS_S64,OS_64] then
+                list.Concat(taicpu.op_reg_reg_reg(A_DIV_D,dst,src1,src2))
+              else
+                list.Concat(taicpu.op_reg_reg_reg(A_DIV_W,dst,src1,src2));
+              if size in [OS_S64,OS_64,OS_S32,OS_32] then
+                begin
+                  ai:=taicpu.op_reg_reg_ref(A_BXX,dst,tmpreg3,href);
+                  ai.condition:=C_NE;
+                  ai.is_jmp:=true;
+                  list.concat(ai);
+                  ai:=taicpu.op_reg_ref(A_BXX,tmpreg2,href);
+                  ai.condition:=C_GEZ;
+                  ai.is_jmp:=true;
+                  list.concat(ai);
+                end
+              else
+                begin
+                  ai:=taicpu.op_reg_ref(A_BXX,tmpreg2,href);
+                  ai.condition:=C_GEZ;
+                  ai.is_jmp:=true;
+                  list.concat(ai);
+                  tmpreg0:=getintregister(list,OS_INT);
+                  a_load_reg_reg(list,OS_INT,size,dst,tmpreg0);
+                  ai:=taicpu.op_reg_reg_ref(A_BXX,tmpreg0,tmpreg3,href);
+                  ai.condition:=C_NE;
+                  ai.is_jmp:=true;
+                  list.concat(ai);
+                end;
+
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,l);
+            end;
+        else
+          internalerror(2022082301);
+        end
+      else
+        a_op_reg_reg_reg(list,op,size,src1,src2,dst);
+    end;
+
+    function tcgloongarch64.fixref(list: TAsmList; var ref: treference; mode : tfixref): boolean;
+      var
+        tmpreg: TRegister;
+        href: treference;
+      begin
+        if ref.refaddr=addr_reg_12i then
+          begin
+            result:=mode=fr_normal;
+            exit;
+          end
+        else if ref.refaddr=addr_reg_reg then
+          begin
+            result:=mode=fr_reg;
+            exit;
+          end
+        else if ref.refaddr=addr_reg_reg then
+          begin
+            result:=mode=fr_big;
+            exit;
+          end;
+
+        { Reference with symbol, load symbol address first. }
+        if assigned(ref.symbol) then
+          begin
+            tmpreg:=getintregister(list,OS_INT);
+            if ((cs_create_pic in current_settings.moduleswitches) and
+                (ref.symbol.bind in [AB_LOCAL,AB_TEMP])) or
+               ((not(cs_create_pic in current_settings.moduleswitches)) and
+                (ref.symbol.bind in [AB_LOCAL,AB_GLOBAL,AB_TEMP])) then
+              begin
+                { Load symbol address as local. }
+                reference_reset_symbol(href,ref.symbol,ref.offset,ref.alignment,ref.volatility);
+                ref.symbol:=nil;
+                ref.offset:=0;
+                href.refaddr:=addr_pc_hi20;
+                list.concat(taicpu.op_reg_ref(A_PCALAU12I,tmpreg,href));
+                href.refaddr:=addr_pc_lo12;
+                list.concat(taicpu.op_reg_reg_ref(A_ADDI_D,tmpreg,tmpreg,href));
+              end
+            else
+              begin
+                { Load symbol address as global. }
+                reference_reset_symbol(href,ref.symbol,0,0,[]);
+                ref.symbol:=nil;
+                href.refaddr:=addr_got_pc_hi20;
+                list.concat(taicpu.op_reg_ref(A_PCALAU12I,tmpreg,href));
+                href.refaddr:=addr_got_pc_lo12;
+                list.concat(taicpu.op_reg_reg_ref(A_LD_D,tmpreg,tmpreg,href));
+              end;
+            { Make address format become basereg (+indexreg). }
+            if (ref.index<>NR_NO) and (ref.base<>NR_NO) then
+              begin
+                a_op_reg_reg(list,OP_ADD,OS_INT,ref.base,tmpreg);
+                ref.base:=tmpreg;
+              end
+            else if (ref.base=NR_NO) then
+              ref.base:=tmpreg
+            else { ref.index=NR_NO }
+              ref.index:=tmpreg;
+          end
+        { Refernce only offset, make offset become a reg. }
+        else if (ref.index=NR_NO) and (ref.base=NR_NO) then
+          begin
+            tmpreg:=getintregister(list,OS_INT);
+            a_load_const_reg(list,OS_ADDR,ref.offset,tmpreg);
+            reference_reset_base(ref,tmpreg,0,ctempposinvalid,ref.alignment,ref.volatility);
+            ref.index:=NR_R0;
+          end;
+
+        if (ref.index<>NR_NO) and (ref.base=NR_NO) then
+          begin
+            ref.base:=ref.index;
+            ref.index:=NR_R0;
+          end;
+        if (ref.index=NR_NO) then
+          ref.index:=NR_R0;
+
+        { The normal type is widely applicable. When we find it is better to
+          use the normal type, we prevent other types. Or add strong types and
+          adjust it in the future. }
+        if is_simm12(ref.offset) then
+          begin
+            if ref.index<>NR_R0 then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                a_op_reg_reg_reg(list,OP_ADD,OS_INT,ref.base,ref.index,tmpreg);
+                ref.base:=tmpreg;
+              end;
+            ref.index:=NR_NO;
+            ref.refaddr:=addr_reg_12i;
+            result:=mode=fr_normal;
+            exit;
+          end;
+
+        if (mode=fr_big) and (is_simm16_and_quadruple(ref.offset)) then
+          begin
+            if ref.index<>NR_NO then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                a_op_reg_reg_reg(list,OP_ADD,OS_INT,ref.base,ref.index,tmpreg);
+                ref.base:=tmpreg;
+              end;
+            ref.index:=NR_NO;
+            ref.refaddr:=addr_reg_14i;
+            result:=true;
+            exit;
+          end;
+
+        if mode=fr_reg then
+          begin
+            if ref.offset<>0 then
+              begin
+                tmpreg:=getintregister(list,OS_INT);
+                a_load_const_reg(list,OS_INT,ref.offset,tmpreg);
+                if ref.index<>NR_R0 then
+                  begin
+                    a_op_reg_reg(list,OP_ADD,OS_INT,ref.index,tmpreg);
+                    ref.index:=tmpreg;
+                  end
+                else
+                  ref.index:=tmpreg;
+              end;
+            ref.refaddr:=addr_reg_reg;
+            ref.offset:=0;
+            result:=true;
+            exit;
+          end;
+
+        tmpreg:=getintregister(list,OS_INT);
+        a_load_const_reg(list,OS_INT,ref.offset,tmpreg);
+        if ref.index<>NR_R0 then
+          a_op_reg_reg(list,OP_ADD,OS_INT,ref.index,tmpreg);
+        a_op_reg_reg(list,OP_ADD,OS_INT,ref.base,tmpreg);
+        ref.base:=tmpreg;
+        ref.index:=NR_NO;
+        ref.offset:=0;
+        result:=mode=fr_normal;
+      end;
+
+
+    procedure tcgloongarch64.maybeadjustresult(list: TAsmList; op: topcg; size: tcgsize; dst: tregister);
+      const
+        overflowops = [OP_MUL,OP_IMUL,OP_SHL,OP_ADD,OP_SUB,OP_NOT,OP_NEG];
+      begin
+        if (op in overflowops) and
+           (size in [OS_8,OS_S8,OS_16,OS_S16,OS_32,OS_S32]) then
+          a_load_reg_reg(list,OS_INT,size,dst,dst)
+        else if (op in [OP_ROL,OP_ROR]) and (size in [OS_8,OS_S8,OS_16,OS_S16]) then
+          a_load_reg_reg(list,OS_INT,size,dst,dst);
+      end;
+
+
+    procedure create_codegen;
+      begin
+        cg := tcgloongarch64.create;
+        cg128:=tcg128.create;
+      end;
+
+end.

+ 448 - 0
compiler/loongarch64/cpubase.pas

@@ -0,0 +1,448 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    Contains the base types for the LoongArch64.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit cpubase;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  strings,globtype,
+  cutils,cclasses,aasmbase,cpuinfo,cgbase;
+
+{*****************************************************************************
+                                Assembler Opcodes
+*****************************************************************************}
+
+    type
+      TAsmOp= {$i loongarch64op.inc}
+
+      {# This should define the array of instructions as string }
+      op2strtable=array[tasmop] of string[16];
+
+    Const
+      {# First value of opcode enumeration }
+      firstop = low(tasmop);
+      {# Last value of opcode enumeration  }
+      lastop  = high(tasmop);
+
+
+{*****************************************************************************
+                                  Registers
+*****************************************************************************}
+
+    type
+      { Number of registers used for indexing in tables }
+      tregisterindex=0..{$i rloongarch64nor.inc}-1;
+
+    const
+      maxvarregs = 32-6; { 32 int registers - r0 - stackpointer - r2 - 3 scratch registers }
+      maxfpuvarregs = 28; { 32 fpuregisters - some scratch registers (minimally 2) }
+
+      { Available Superregisters }
+      {$i rloongarch64sup.inc}
+
+      { No Subregisters }
+      R_SUBWHOLE=R_SUBNONE;
+
+      { Available Registers }
+      {$i rloongarch64con.inc}
+
+      { Integer Super registers first and last }
+      first_int_supreg = RS_R0;
+      first_int_imreg = $20;
+
+      { Float Super register first and last }
+      first_fpu_supreg    = RS_F0;
+      first_fpu_imreg     = $20;
+
+      { MM Super register first and last }
+      first_mm_supreg    = 0;
+      first_mm_imreg     = 1;
+
+{ TODO: Calculate bsstart}
+      regnumber_count_bsstart = 64;
+
+      regnumber_table : array[tregisterindex] of tregister = (
+        {$i rloongarch64num.inc}
+      );
+
+      regstabs_table : array[tregisterindex] of shortint = (
+        {$i rloongarch64sta.inc}
+      );
+
+      regdwarf_table : array[tregisterindex] of shortint = (
+        {$i rloongarch64dwa.inc}
+      );
+
+{*****************************************************************************
+                                Conditions
+*****************************************************************************}
+
+    type
+      TAsmCond = (C_NONE,C_EQ,C_GT,C_LT,C_GE,C_LE,C_NE,C_LEU,C_LTU,C_GEU,C_GTU,
+                  C_EQZ,C_GTZ,C_LTZ,C_GEZ,C_LEZ,C_NEZ);
+      TAsmConds = set of TAsmCond;
+    const
+      cond2str: Array[TAsmCond] of string[4] = ('',
+                'eq','gt','lt','ge','le','ne','leu','ltu','geu','gtu',
+                'eqz','gtz','ltz','gez','lez','nez');
+
+
+{*****************************************************************************
+                                   Flags
+*****************************************************************************}
+
+    type
+      TResFlagsEnum = (F_EQ,F_NE,F_LT,F_LTU,F_GE,F_GEU);
+
+{*****************************************************************************
+                                Reference
+*****************************************************************************}
+
+{*****************************************************************************
+                                Operands
+*****************************************************************************}
+
+{*****************************************************************************
+                                 Constants
+*****************************************************************************}
+
+    const
+      max_operands = 5;
+
+{*****************************************************************************
+                          Default generic sizes
+*****************************************************************************}
+
+  {# Defines the default address size for a processor, }
+  OS_ADDR = OS_64;
+  {# the natural int size for a processor,
+     has to match osuinttype/ossinttype as initialized in psystem }
+  OS_INT = OS_64;
+  OS_SINT = OS_S64;
+  {# the maximum float size for a processor,           }
+  OS_FLOAT = OS_F64;
+  {# the size of a vector register for a processor     }
+  OS_VECTOR = OS_M128;
+
+{*****************************************************************************
+                               GDB Information
+*****************************************************************************}
+
+      stab_regindex : array[tregisterindex] of shortint = (
+        {$i rloongarch64sta.inc}
+      );
+
+{*****************************************************************************
+                          Generic Register names
+*****************************************************************************}
+
+      {# Stack pointer register }
+      NR_STACK_POINTER_REG = NR_R3;
+      RS_STACK_POINTER_REG = RS_R3;
+      {# Frame pointer register }
+      NR_FRAME_POINTER_REG = NR_R22;
+      RS_FRAME_POINTER_REG = RS_R22;
+
+      { Return address of a function }
+      NR_RETURN_ADDRESS_REG = NR_R1;
+      RS_RETURN_ADDRESS_REG = RS_R1;
+      { Results are returned in this register (32-bit values) }
+      NR_FUNCTION_RETURN_REG = NR_R4;
+      RS_FUNCTION_RETURN_REG = RS_R4;
+      { Low part of 64bit return value }
+      NR_FUNCTION_RETURN64_LOW_REG = NR_R4;
+      RS_FUNCTION_RETURN64_LOW_REG = RS_R4;
+      { High part of 64bit return value }
+      NR_FUNCTION_RETURN64_HIGH_REG = NR_R4;
+      RS_FUNCTION_RETURN64_HIGH_REG = RS_R4;
+      { The value returned from a function is available in this register }
+      NR_FUNCTION_RESULT_REG = NR_FUNCTION_RETURN_REG;
+      RS_FUNCTION_RESULT_REG = RS_FUNCTION_RETURN_REG;
+      { The lowh part of 64bit value returned from a function }
+      NR_FUNCTION_RESULT64_LOW_REG = NR_FUNCTION_RETURN64_LOW_REG;
+      RS_FUNCTION_RESULT64_LOW_REG = RS_FUNCTION_RETURN64_LOW_REG;
+      { The high part of 64bit value returned from a function }
+      NR_FUNCTION_RESULT64_HIGH_REG = NR_FUNCTION_RETURN64_HIGH_REG;
+      RS_FUNCTION_RESULT64_HIGH_REG = RS_FUNCTION_RETURN64_HIGH_REG;
+
+      NR_FPU_RESULT_REG = NR_F0;
+      NR_MM_RESULT_REG = NR_NO;
+
+      NR_DEFAULTFLAGS = NR_NO;
+      RS_DEFAULTFLAGS = RS_NO;
+
+      RS_FIRST_INT_PARAM_SUPREG = RS_R4;
+      RS_FIRST_FLOAT_PARAM_SUPREG = RS_F0;
+      RS_FIRST_MM_PARAM_SUPREG = RS_NO;
+
+
+{*****************************************************************************
+                       GCC /ABI linking information
+*****************************************************************************}
+
+      {# Registers which must be saved when calling a routine declared as
+         cppdecl, cdecl, stdcall, safecall, palmossyscall. The registers
+         saved should be the ones as defined in the target ABI and / or GCC.
+
+         This value can be deduced from CALLED_USED_REGISTERS array in the
+         GCC source.
+      }
+      saved_standard_registers : array[0..10] of tsuperregister = (
+        RS_R3, RS_R22,
+        RS_R23, RS_R24, RS_R25, RS_R26, RS_R27, RS_R28, RS_R29, RS_R30, RS_R31
+      );
+
+      { this is only for the generic code which is not used for this architecture }
+      saved_address_registers : array[0..0] of tsuperregister = (RS_INVALID);
+      saved_mm_registers : array[0..0] of tsuperregister = (RS_INVALID);
+
+      {# Required parameter alignment when calling a routine declared as
+         stdcall and cdecl. The alignment value should be the one defined
+         by GCC or the target ABI.
+
+         The value of this constant is equal to the constant
+         PARM_BOUNDARY / BITS_PER_UNIT in the GCC source.
+      }
+      std_param_align = 8;
+
+{*****************************************************************************
+                            CPU Dependent Constants
+*****************************************************************************}
+
+      maxfpuregs = 8;
+
+{*****************************************************************************
+                                  Helpers
+*****************************************************************************}
+
+    function is_simm12(value: tcgint): boolean;
+    function is_uimm12(value: tcgint): boolean;
+    function is_simm16_and_quadruple(value: tcgint): boolean;
+
+    function is_calljmp(o:tasmop):boolean;
+
+    function cgsize2subreg(regtype: tregistertype; s:Tcgsize):Tsubregister;
+    { Returns the tcgsize corresponding with the size of reg.}
+    function reg_cgsize(const reg: tregister) : tcgsize;
+
+    function findreg_by_number(r:Tregister):tregisterindex;
+    function std_regnum_search(const s:string):Tregister;
+    function std_regname(r:Tregister):string;
+
+    function inverse_cond(const c: TAsmCond): Tasmcond; {$ifdef USEINLINE}inline;{$endif USEINLINE}
+    function dwarf_reg(r:tregister):shortint;
+    function dwarf_reg_no_error(r:tregister):shortint;
+    function eh_return_data_regno(nr: longint): longint;
+
+    function conditions_equal(const c1,c2: TAsmCond): boolean;
+
+    { Checks if Subset is a subset of c (e.g. "less than" is a subset of "less than or equal" }
+    function condition_in(const Subset, c: TAsmCond): Boolean;
+
+    function is_extra_reg(const s : string) : tregister;
+
+implementation
+
+    uses
+      rgbase,verbose;
+
+    const
+      std_regname_table : TRegNameTable = (
+        {$i rloongarch64std.inc}
+      );
+
+      abi_regname_table : TRegNameTable = (
+        {$i rloongarch64abi.inc}
+      );
+
+      regnumber_index : array[tregisterindex] of tregisterindex = (
+        {$i rloongarch64rni.inc}
+      );
+
+      std_regname_index : array[tregisterindex] of tregisterindex = (
+        {$i rloongarch64sri.inc}
+      );
+
+{*****************************************************************************
+                                  Helpers
+*****************************************************************************}
+
+    function is_simm12(value: tcgint): boolean;
+      begin
+        result:=(value >= -2048) and (value <= 2047);
+      end;
+
+    function is_uimm12(value: tcgint): boolean;
+      begin
+        result:=(value >= 0) and (value <= 4095);
+      end;
+
+    function is_simm16_and_quadruple(value: tcgint): boolean;
+      begin
+        result:=(value >= -32768) and (value <= 32767) and ((value mod 4) = 0);
+      end;
+
+    function is_calljmp(o:tasmop):boolean;
+      begin
+        case o of
+          { Most of time is call. }
+          A_JIRL,A_BL: result:=true;
+          { Most of time is jump. }
+          A_JR,A_B,A_BEQ,A_BNE,A_BLT,A_BLTU,A_BGE,
+          A_BGEU,A_BEQZ,A_BNEZ,A_BCEQZ,A_BCNEZ,
+          A_BLTZ,A_BGTZ,A_BGEZ,A_BLEZ,A_BGT,A_BLE,
+          A_BGTU,A_BLEU,A_BXX: result:=true;
+        else
+          result:=false;
+        end;
+      end;
+
+    function inverse_cond(const c: TAsmCond): Tasmcond; {$ifdef USEINLINE}inline;{$endif USEINLINE}
+      const
+        inv_condflags:array[TAsmCond] of TAsmCond=(C_NONE,
+          C_NE,C_LE,C_GE,C_LT,C_GT,C_EQ,C_GTU,C_GEU,C_LTU,C_LEU,
+          C_NEZ,C_LEZ,C_GEZ,C_LTZ,C_GTZ,C_EQZ);
+      begin
+        result := inv_condflags[c];
+      end;
+
+
+    function reg_cgsize(const reg: tregister): tcgsize;
+      begin
+        case getregtype(reg) of
+          R_INTREGISTER :
+            result:=OS_INT;
+          R_MMREGISTER:
+            result:=OS_M128;
+          R_FPUREGISTER:
+            result:=OS_F64;
+          else
+            internalerror(2022111902);
+        end;
+      end;
+
+
+    function cgsize2subreg(regtype: tregistertype; s:Tcgsize):Tsubregister;
+      begin
+        cgsize2subreg:=R_SUBWHOLE;
+      end;
+
+
+    function findreg_by_number(r:Tregister):tregisterindex;
+      begin
+        result:=rgBase.findreg_by_number_table(r,regnumber_index);
+      end;
+
+
+    function std_regnum_search(const s:string):Tregister;
+      begin
+        result:=regnumber_table[findreg_by_name_table(s,std_regname_table,std_regname_index)];
+      end;
+
+
+    function std_regname(r:Tregister):string;
+      var
+        p : tregisterindex;
+      begin
+        p:=findreg_by_number_table(r,regnumber_index);
+        if p<>0 then
+          result:=std_regname_table[p]
+        else
+          result:=generic_regname(r);
+      end;
+
+
+    function dwarf_reg(r:tregister):shortint;
+      begin
+        result:=regdwarf_table[findreg_by_number(r)];
+        if result=-1 then
+          internalerror(2022111903);
+      end;
+
+    function dwarf_reg_no_error(r:tregister):shortint;
+      begin
+        result:=regdwarf_table[findreg_by_number(r)];
+      end;
+
+    function eh_return_data_regno(nr: longint): longint;
+      begin
+        if (nr>=0) and (nr<4) then
+          result:=nr+10
+        else
+          result:=-1;
+      end;
+
+    function conditions_equal(const c1, c2: TAsmCond): boolean;
+      begin
+        result:=c1=c2;
+      end;
+
+
+    { Checks if Subset is a subset of c (e.g. "less than" is a subset of "less than or equal" }
+    function condition_in(const Subset, c: TAsmCond): Boolean;
+      begin
+        Result := (c = C_None) or conditions_equal(Subset, c);
+
+        if not Result then
+          case Subset of
+            C_EQ:
+              Result := (c in [C_GE,C_GEU,C_LE,C_LEU]);
+            C_EQZ:
+              Result := (c in [C_GEZ,C_LEZ]);
+            else
+              Result := False;
+          end;
+      end;
+
+
+    function is_extra_reg(const s: string): tregister;
+      const abiname2reg : array[tregisterindex] of tregister = (NR_NO,
+            NR_R0,NR_R1,NR_R2,NR_R3,NR_R4,NR_R5,NR_R6,NR_R7,
+            NR_R8,NR_R9,NR_R10,NR_R11,NR_R12,NR_R13,NR_R14,NR_R15,
+            NR_R16,NR_R17,NR_R18,NR_R19,NR_R20,NR_R21,NR_R22,NR_R23,
+            NR_R24,NR_R25,NR_R26,NR_R27,NR_R28,NR_R29,NR_R30,NR_R31,
+            NR_F0,NR_F1,NR_F2,NR_F3,NR_F4,NR_F5,NR_F6,NR_F7,
+            NR_F8,NR_F9,NR_F10,NR_F11,NR_F12,NR_F13,NR_F14,NR_F15,
+            NR_F16,NR_F17,NR_F18,NR_F19,NR_F20,NR_F21,NR_F22,NR_F23,
+            NR_F24,NR_F25,NR_F26,NR_F27,NR_F28,NR_F29,NR_F30,NR_F31,
+            NR_FCC0,NR_FCC1,NR_FCC2,NR_FCC3,NR_FCC4,NR_FCC5,NR_FCC6,NR_FCC7);
+      var
+        i : longint;
+      begin
+        result:=NR_NO;
+        { LoongArch registers start by '$' and abiname length <= 5 }
+        if not(length(s) in [2..5]) and (s[1]<>'$') then
+          exit;
+        for i:=low(abi_regname_table) to high(abi_regname_table) do
+          begin
+            if s=abi_regname_table[i] then
+              begin
+                result:=abiname2reg[i];
+                exit;
+              end;
+          end;
+      end;
+
+begin
+end.

+ 122 - 0
compiler/loongarch64/cpuinfo.pas

@@ -0,0 +1,122 @@
+{
+    Copyright (c) 1998-2002 by the Free Pascal development team
+
+    Basic Processor information for the LoongArch64
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit CPUInfo;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  globtype;
+
+type
+  bestreal = double;
+  bestrealrec = TDoubleRec;
+  ts32real = single;
+  ts64real = double;
+  ts80real = extended;
+  ts128real = extended;
+  ts64comp = comp;
+
+  pbestreal = ^bestreal;
+
+  { possible supported processors for this target }
+  tcputype = (cpu_none,
+    cpu_3a
+  );
+
+  tfputype =
+    (fpu_none,
+    fpu_fd
+    );
+
+   tcontrollertype =
+     (ct_none
+     );
+
+   tcontrollerdatatype = record
+      controllertypestr, controllerunitstr: string[20];
+      cputype: tcputype; fputype: tfputype;
+      flashbase, flashsize, srambase, sramsize, eeprombase, eepromsize, bootbase, bootsize: dword;
+   end;
+
+
+Const
+  { Is there support for dealing with multiple microcontrollers available }
+  { for this platform? }
+  ControllerSupport = false;
+
+  { We know that there are fields after sramsize
+    but we don't care about this warning }
+  {$PUSH}
+   {$WARN 3177 OFF}
+  embedded_controllers : array [tcontrollertype] of tcontrollerdatatype =
+  (
+      (controllertypestr:''; controllerunitstr:''; cputype:cpu_none; fputype:fpu_none; flashbase:0; flashsize:0; srambase:0; sramsize:0));
+  {$POP}
+
+  { calling conventions supported by the code generator }
+  supported_calling_conventions: tproccalloptions = [
+    pocall_internproc,
+    pocall_safecall,
+    pocall_stdcall,
+    { the difference to stdcall is only the name mangling }
+    pocall_cdecl,
+    { the difference to stdcall is only the name mangling }
+    pocall_cppdecl,
+    { the difference with stdcall is that all const record
+      parameters are passed by reference }
+    pocall_mwpascal
+    ];
+
+  cputypestr: array[tcputype] of string[11] = ('',
+    'LOONGARCH64'
+    );
+
+  fputypestr: array[tfputype] of string[8] = ('',
+    'FD'
+    );
+
+   { Supported optimizations, only used for information }
+   supported_optimizerswitches = genericlevel1optimizerswitches+
+                                 genericlevel2optimizerswitches+
+                                 genericlevel3optimizerswitches-
+                                 { no need to write info about those }
+                                 [cs_opt_level1,cs_opt_level2,cs_opt_level3]+
+                                 [{$ifndef llvm}cs_opt_regvar,{$endif}cs_opt_loopunroll,cs_opt_nodecse,
+                                  cs_opt_tailrecursion,cs_opt_reorder_fields,cs_opt_fastmath,
+                                  cs_opt_stackframe];
+
+   level1optimizerswitches = genericlevel1optimizerswitches;
+   level2optimizerswitches = genericlevel2optimizerswitches + level1optimizerswitches +
+     [{$ifndef llvm}cs_opt_regvar,{$endif}cs_opt_stackframe,cs_opt_nodecse,cs_opt_tailrecursion];
+   level3optimizerswitches = genericlevel3optimizerswitches + level2optimizerswitches;
+   level4optimizerswitches = genericlevel4optimizerswitches + level3optimizerswitches + [cs_opt_stackframe];
+
+ { TODO Unaligned accesses, hard float. }
+ type
+   tcpuflags =
+      (CPULOONGARCH_HAS_ATOMIC
+      );
+
+ const
+   cpu_capabilities : array[tcputype] of set of tcpuflags =
+     ( { cpu_none       } [],
+       { cpu_loongarch3a    } [CPULOONGARCH_HAS_ATOMIC]
+     );
+
+implementation
+
+end.

+ 52 - 0
compiler/loongarch64/cpunode.pas

@@ -0,0 +1,52 @@
+{
+    Copyright (c) 2000-2002 by Florian Klaempfl
+
+    Includes the LoongArch64 code generator
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit cpunode;
+
+{$I fpcdefs.inc}
+
+interface
+
+implementation
+
+uses
+  { generic nodes }
+  ncgbas, ncgld, ncgflw, ncgcnv, ncgmem, ncgcon, ncgcal, ncgset, ncginl, ncgopt,
+  ncgobjc,
+  { symtable }
+  symcpu,
+  aasmdef,
+  { to be able to only parts of the generic code,
+    the processor specific nodes must be included
+    after the generic one (FK)
+  }
+{$ifndef llvm}
+  ncpuadd,
+  ncpucnv,
+  ncpuinl,
+  ncpumat,
+  ncpuset
+{$else not llvm}
+  llvmnode
+{$endif not llvm}
+  ;
+
+end.

+ 617 - 0
compiler/loongarch64/cpupara.pas

@@ -0,0 +1,617 @@
+{
+    Copyright (c) 2002 by Florian Klaempfl
+
+    LoongArch64 specific calling conventions
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ****************************************************************************
+}
+unit cpupara;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      globtype,
+      aasmtai,aasmdata,
+      cpubase,
+      symconst, symtype, symdef, symsym,
+      paramgr, parabase, cgbase, cgutils;
+
+    type
+      tcpuparamanager = class(tparamanager)
+        function get_volatile_registers_int(calloption: tproccalloption): tcpuregisterset; override;
+        function get_volatile_registers_fpu(calloption: tproccalloption): tcpuregisterset; override;
+        function push_addr_param(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean; override;
+        function ret_in_param(def: tdef; pd: tabstractprocdef): boolean; override;
+
+        procedure getcgtempparaloc(list: TAsmList; pd : tabstractprocdef; nr: longint; var cgpara: tcgpara); override;
+        function create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint; override;
+        function create_varargs_paraloc_info(p: tabstractprocdef; side: tcallercallee; varargspara: tvarargsparalist): longint; override;
+        function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
+
+      private
+        procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
+        function create_paraloc_info_intern(p: tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; isVararg : boolean): longint;
+        function parseparaloc(p: tparavarsym; const s: string): boolean; override;
+        procedure create_paraloc_for_def(var para: TCGPara; varspez: tvarspez; paradef: tdef; var nextfloatreg, nextintreg: tsuperregister; var stack_offset: aword; const isVararg, forceintmem: boolean; const side: tcallercallee; const p: tabstractprocdef);
+      end;
+
+implementation
+
+    uses
+      verbose, systems,
+      globals, cpuinfo,
+      defutil,symtable,symcpu,
+      procinfo, cpupi;
+
+    function tcpuparamanager.get_volatile_registers_int(calloption: tproccalloption): tcpuregisterset;
+      begin
+        result:=[RS_R0..RS_R31]-[RS_R3,RS_R22..RS_R31];
+      end;
+
+    function tcpuparamanager.get_volatile_registers_fpu(calloption: tproccalloption): tcpuregisterset;
+      begin
+        result:=[RS_F0..RS_F31]-[RS_F24..RS_F31];
+      end;
+
+    procedure tcpuparamanager.getcgtempparaloc(list: TAsmList; pd : tabstractprocdef; nr: longint; var cgpara: tcgpara);
+      var
+        paraloc: pcgparalocation;
+        psym: tparavarsym;
+        pdef: tdef;
+      begin
+        psym:=tparavarsym(pd.paras[nr-1]);
+        pdef:=psym.vardef;
+        if push_addr_param(psym.varspez,pdef,pd.proccalloption) then
+          pdef:=cpointerdef.getreusable_no_free(pdef);
+        cgpara.reset;
+        cgpara.size := def_cgsize(pdef);
+        cgpara.intsize := tcgsize2size[cgpara.size];
+        cgpara.alignment := get_para_align(pd.proccalloption);
+        cgpara.def:=pdef;
+        paraloc := cgpara.add_location;
+        with paraloc^ do
+          begin
+            size := def_cgsize(pdef);
+            def := pdef;
+            if (nr <= 8) then
+              begin
+                if (nr = 0) then
+                  internalerror(2022111916);
+                loc := LOC_REGISTER;
+                register := newreg(R_INTREGISTER, RS_R4 + nr-1, R_SUBWHOLE);
+              end
+            else
+              begin
+                loc := LOC_REFERENCE;
+                paraloc^.reference.index := NR_STACK_POINTER_REG;
+                reference.offset := sizeof(aint) * (nr - 9);
+              end;
+          end;
+      end;
+
+    function getparaloc(p: tdef): tcgloc;
+      var
+        hfabasedef: tdef;
+      begin
+        { Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER
+          if push_addr_param for the def is true
+        }
+        case p.typ of
+          orddef:
+            result := LOC_REGISTER;
+          floatdef:
+            result := LOC_FPUREGISTER;
+          enumdef:
+            result := LOC_REGISTER;
+          pointerdef:
+            result := LOC_REGISTER;
+          formaldef:
+            result := LOC_REGISTER;
+          classrefdef:
+            result := LOC_REGISTER;
+          procvardef:
+            result := LOC_REGISTER;
+          recorddef:
+            result := LOC_REGISTER;
+            { result := LOC_FPUREGISTER; }
+          objectdef:
+            if is_object(p) then
+              result := LOC_REFERENCE
+            else
+              result := LOC_REGISTER;
+          stringdef:
+            if is_shortstring(p) or is_longstring(p) then
+              result := LOC_REFERENCE
+            else
+              result := LOC_REGISTER;
+          filedef:
+            result := LOC_REGISTER;
+          arraydef:
+            if is_dynamic_array(p) then
+              getparaloc:=LOC_REGISTER
+            else
+              result := LOC_REFERENCE;
+          setdef:
+            if is_smallset(p) then
+              result := LOC_REGISTER
+            else
+              result := LOC_REFERENCE;
+          variantdef:
+            result := LOC_REFERENCE;
+          { avoid problems with errornous definitions }
+          errordef:
+            result := LOC_REGISTER;
+        else
+          internalerror(2022111917);
+        end;
+      end;
+
+    function tcpuparamanager.push_addr_param(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean;
+      begin
+        result := false;
+        { var,out,constref always require address }
+        if varspez in [vs_var, vs_out, vs_constref] then
+        begin
+          result := true;
+          exit;
+        end;
+        case def.typ of
+          variantdef,
+          formaldef:
+            result := true;
+          procvardef,
+          recorddef:
+            result := (def.size > 16);
+          arraydef:
+            result := (tarraydef(def).highrange >= tarraydef(def).lowrange) or
+              is_open_array(def) or
+              is_array_of_const(def) or
+              is_array_constructor(def);
+          objectdef:
+            result := is_object(def);
+          setdef:
+            result := not is_smallset(def);
+          stringdef:
+            result := tstringdef(def).stringtype in [st_shortstring, st_longstring];
+          else
+            ;
+        end;
+      end;
+
+    function tcpuparamanager.ret_in_param(def: tdef; pd: tabstractprocdef): boolean;
+      var
+        tmpdef: tdef;
+      begin
+        if handle_common_ret_in_param(def,pd,result) then
+          exit;
+
+        { general rule: passed in registers -> returned in registers }
+        result:=push_addr_param(vs_value,def,pd.proccalloption);
+      end;
+
+    procedure tcpuparamanager.init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
+      begin
+        cur_stack_offset := 0;
+        curintreg := RS_R4;
+        curfloatreg := RS_F0;
+        curmmreg := RS_NO;
+      end;
+
+    function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
+      var
+        paraloc: pcgparalocation;
+        retcgsize: tcgsize;
+        nextfloatreg, nextintreg, nextmmreg: tsuperregister;
+        stack_offset: aword;
+      begin
+        if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
+          exit;
+
+         { in this case, it must be returned in registers as if it were passed
+           as the first parameter }
+         init_values(nextintreg,nextfloatreg,nextmmreg,stack_offset);
+         create_paraloc_for_def(result,vs_value,result.def,nextfloatreg,nextintreg,stack_offset,false,false,side,p);
+         { sanity check (LOC_VOID for empty records) }
+         if not assigned(result.location) or
+            not(result.location^.loc in [LOC_REGISTER,LOC_FPUREGISTER,LOC_VOID]) then
+           internalerror(2022111918);
+      end;
+
+    function tcpuparamanager.create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint;
+      var
+        cur_stack_offset: aword;
+        curintreg, curfloatreg, curmmreg : tsuperregister;
+      begin
+        init_values(curintreg, curfloatreg, curmmreg, cur_stack_offset);
+
+        result := create_paraloc_info_intern(p, side, p.paras, curintreg, curfloatreg, curmmreg, cur_stack_offset, false);
+
+        create_funcretloc_info(p, side);
+      end;
+
+    function tcpuparamanager.create_paraloc_info_intern(p: tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; isVararg : boolean): longint;
+      var
+        nextintreg, nextfloatreg, nextmmreg : tsuperregister;
+        i: integer;
+        hp: tparavarsym;
+        paraloc: pcgparalocation;
+        delphi_nestedfp: boolean;
+
+      begin
+{$IFDEF extdebug}
+        if po_explicitparaloc in p.procoptions then
+          internalerror(2022111919);
+{$ENDIF extdebug}
+
+        result := 0;
+        nextintreg := curintreg;
+        nextfloatreg := curfloatreg;
+        nextmmreg := curmmreg;
+
+        for i := 0 to paras.count - 1 do begin
+          hp := tparavarsym(paras[i]);
+
+          if (vo_has_explicit_paraloc in hp.varoptions) then begin
+            internalerror(2022111920);
+          end;
+
+          { currently only support C-style array of const }
+          if (p.proccalloption in [pocall_cdecl, pocall_cppdecl]) and
+            is_array_of_const(hp.vardef) then begin
+            paraloc := hp.paraloc[side].add_location;
+            { hack: the paraloc must be valid, but is not actually used }
+            paraloc^.loc := LOC_REGISTER;
+            paraloc^.register := NR_R0;
+            paraloc^.size := OS_ADDR;
+            paraloc^.def := voidpointertype;
+            break;
+          end;
+          delphi_nestedfp:=(vo_is_parentfp in hp.varoptions) and (po_delphi_nested_cc in p.procoptions);
+          create_paraloc_for_def(hp.paraloc[side], hp.varspez, hp.vardef,
+            nextfloatreg, nextintreg, cur_stack_offset, isVararg, delphi_nestedfp, side, p);
+        end;
+
+        curintreg := nextintreg;
+        curfloatreg := nextfloatreg;
+        curmmreg := nextmmreg;
+        result := cur_stack_offset;
+      end;
+
+    procedure tcpuparamanager.create_paraloc_for_def(var para: TCGPara; varspez: tvarspez; paradef: tdef; var nextfloatreg, nextintreg: tsuperregister; var stack_offset: aword; const isVararg, forceintmem: boolean; const side: tcallercallee; const p: tabstractprocdef);
+      var
+        paracgsize: tcgsize;
+        loc: tcgloc;
+        paraloc: pcgparalocation;
+        { def to use for all paralocs if <> nil }
+        alllocdef,
+        { def to use for the current paraloc }
+        locdef,
+        tmpdef,tmpdef1,tmpdef2: tdef;
+        tmpdep1_size,tmpdep2_size,record_offset,para_same_num: integer;
+        paralen: aint;
+        firstparaloc,
+        paraaligned: boolean;
+      begin
+        alllocdef:=nil;
+        locdef:=nil;
+        para.reset;
+        para_same_num := 0;
+        { have we ensured that the next parameter location will be aligned to the
+          next 8 byte boundary? }
+        paraaligned:=false;
+        if push_addr_param(varspez, paradef, p.proccalloption) then
+          begin
+            paradef := cpointerdef.getreusable_no_free(paradef);
+            loc := LOC_REGISTER;
+            paracgsize := OS_ADDR;
+            paralen := tcgsize2size[OS_ADDR];
+          end
+        else
+          begin
+            if not is_special_array(paradef) then
+              paralen := paradef.size
+            else
+              paralen := tcgsize2size[def_cgsize(paradef)];
+
+            if (paradef.typ=recorddef) and
+               tabstractrecordsymtable(tabstractrecorddef(paradef).symtable).has_single_field(tmpdef) and
+               (tmpdef.typ=floatdef) then
+              begin
+                paradef:=tmpdef;
+                loc:=getparaloc(paradef);
+                paracgsize:=def_cgsize(paradef)
+              end
+            else if (((paradef.typ=arraydef) and not
+                    is_special_array(paradef)) or
+                    (paradef.typ=recorddef) and (varspez in [vs_value,vs_const]) ) then
+              begin
+                { general fallback rule: pass aggregate types in integer registers
+                  without special adjustments (incl. Darwin h) }
+                loc:=LOC_REGISTER;
+                paracgsize:=int_cgsize(paralen);
+                { Check if the element types in the record are the same}
+                if paralen <= 16 then
+                  para_same_num := tabstractrecordsymtable(tabstractrecorddef(paradef).symtable).has_double_field(tmpdef1, tmpdef2, record_offset);
+                if para_same_num <> 0 then
+                  begin
+                    if (((tmpdef1.typ = floatdef) or (tmpdef2.typ = floatdef)) and (nextfloatreg > RS_F7)) then
+                      para_same_num := 0;
+                  end;
+                if ((para_same_num <> 0) and (record_offset <= 8))then
+                  begin
+                    { Floating point elements less than two will be passed through the floating point register}
+                    if tmpdef1.typ = floatdef then
+                      begin
+                        loc:=LOC_FPUREGISTER;
+                        paracgsize:=int_float_cgsize(paralen);
+                      end;
+                    if ((tmpdef1.typ<>floatdef) and (tmpdef2.typ<>floatdef) and (para_same_num=1))then
+                      para_same_num := 2;
+                  end;
+              end
+            else
+              begin
+                loc:=getparaloc(paradef);
+                paracgsize:=def_cgsize(paradef);
+                { for things like formaldef }
+                if (paracgsize=OS_NO) then
+                  begin
+                    paracgsize:=OS_ADDR;
+                    paralen:=tcgsize2size[OS_ADDR];
+                  end;
+              end
+          end;
+
+        { patch FPU values into integer registers if we are processing varargs }
+        if (isVararg) and (paradef.typ = floatdef) then
+          begin
+            loc := LOC_REGISTER;
+            if paracgsize = OS_F64 then
+              paracgsize := OS_64
+            else
+              paracgsize := OS_32;
+          end;
+
+        para.alignment := std_param_align;
+        para.size := paracgsize;
+        para.intsize := paralen;
+        para.def := paradef;
+        if (paralen = 0) then
+          if (paradef.typ = recorddef) then
+            begin
+              paraloc := para.add_location;
+              paraloc^.loc := LOC_VOID;
+            end
+          else
+            internalerror(2022111921);
+
+        if not assigned(alllocdef) then
+          locdef:=paradef
+        else
+          begin
+            locdef:=alllocdef;
+            paracgsize:=def_cgsize(locdef);
+          end;
+        firstparaloc:=true;
+
+        // Parameters passed in 2 registers are passed in a register starting with an even number.
+        if isVararg and
+           (paralen > 8) and
+           (loc = LOC_REGISTER) and
+           (nextintreg <= RS_R11) and
+           odd(nextintreg) then
+          inc(nextintreg);
+
+        { can become < 0 for e.g. 3-byte records }
+        while paralen > 0 do
+          begin
+            paraloc := para.add_location;
+            { In case of po_delphi_nested_cc, the parent frame pointer
+              is always passed on the stack. }
+            if (loc = LOC_REGISTER) and
+               (nextintreg <= RS_R11) and
+               not forceintmem then
+              begin
+                paraloc^.loc := loc;
+
+                { make sure we don't lose whether or not the type is signed }
+                if (paracgsize <> OS_NO) and
+                   (paradef.typ <> orddef) and
+                   not assigned(alllocdef) then
+                  begin
+                    paracgsize := int_cgsize(paralen);
+                    locdef:=get_paraloc_def(paradef, paralen, firstparaloc);
+                  end;
+                if para_same_num = 1 then
+                  begin
+                    paraloc^.size := int_cgsize(record_offset);
+                    paraloc^.def := locdef;
+                    para_same_num := 0;
+                    {set for next element}
+                    if tmpdef2.typ = floatdef then
+                      begin
+                        loc := LOC_FPUREGISTER;
+                        paracgsize := int_float_cgsize(paralen - record_offset);
+                      end
+                    else
+                      internalerror(2022082601);
+                  end
+                else if (paracgsize in [OS_NO, OS_128, OS_S128]) then
+                  begin
+                    if (paralen>4) then
+                      begin
+                        paraloc^.size := OS_INT;
+                        paraloc^.def := osuinttype;
+                      end
+                    else
+                      begin
+                        { for 3-byte records aligned in the lower bits of register }
+                        paraloc^.size := OS_32;
+                        paraloc^.def := u32inttype;
+                      end;
+                  end
+                else
+                  begin
+                    paraloc^.size := paracgsize;
+                    paraloc^.def := locdef;
+                  end;
+
+                paraloc^.register := newreg(R_INTREGISTER, nextintreg, R_SUBNONE);
+                inc(nextintreg);
+                dec(paralen, tcgsize2size[paraloc^.size]);
+              end
+            else if (loc = LOC_FPUREGISTER) and
+                    (nextfloatreg <= RS_F7) then
+              begin
+                paraloc^.loc := loc;
+                if para_same_num = 1 then
+                  begin
+                    paraloc^.size := int_float_cgsize(record_offset);
+                    if record_offset = 8 then
+                      paraloc^.def := s64floattype
+                    else
+                      paraloc^.def := s32floattype;
+                    para_same_num := 0;
+                    {set for next element}
+                    if tmpdef2.typ <> floatdef then
+                      begin
+                        loc := LOC_REGISTER;
+                        paracgsize:=int_cgsize(paralen - record_offset);
+                      end
+                    else
+                      internalerror(2022082602);
+                  end
+                else if((paracgsize = OS_F128) and (para_same_num = 2)) then
+                  begin
+                    paraloc^.size := OS_FLOAT;
+                    paraloc^.def := s64floattype;
+                  end
+                else if((paracgsize = OS_F64) and (para_same_num = 2)) then
+                  begin
+                    paraloc^.size := OS_F32;
+                    paraloc^.def := s32floattype;
+                  end
+                else
+                  begin
+                    paraloc^.size := paracgsize;
+                    paraloc^.def := locdef;
+                  end;
+                paraloc^.register := newreg(R_FPUREGISTER, nextfloatreg, R_SUBWHOLE);
+                inc(nextfloatreg);
+                dec(paralen, tcgsize2size[paraloc^.size]);
+              end
+            else if (loc = LOC_MMREGISTER) then
+              begin
+                { no mm registers }
+                internalerror(2022111922);
+              end
+            else
+              begin
+                { either LOC_REFERENCE, or one of the above which must be passed on the
+                  stack because of insufficient registers }
+                paraloc^.loc := LOC_REFERENCE;
+                case loc of
+                  LOC_FPUREGISTER:
+                    begin
+                      paraloc^.size:=int_float_cgsize(paralen);
+                      case paraloc^.size of
+                        OS_F32: paraloc^.def:=s32floattype;
+                        OS_F64: paraloc^.def:=s64floattype;
+                      else
+                        internalerror(2022111923);
+                      end;
+                    end;
+                  LOC_REGISTER,
+                  LOC_REFERENCE:
+                    begin
+                      paraloc^.size:=int_cgsize(paralen);
+                      paraloc^.def:=get_paraloc_def(paradef, paralen, firstparaloc);
+                    end;
+                else
+                  internalerror(2022111924);
+                end;
+                if (side = callerside) then
+                  paraloc^.reference.index := NR_STACK_POINTER_REG
+                else
+                  begin
+                    { during procedure entry, NR_OLD_STACK_POINTER_REG contains the old stack pointer }
+                    paraloc^.reference.index := NR_FRAME_POINTER_REG;
+                    { create_paraloc_info_intern might be also called when being outside of
+                      code generation so current_procinfo might be not set }
+                    if assigned(current_procinfo) then
+                      tloongarch64procinfo(current_procinfo).needs_frame_pointer := true;
+                  end;
+                paraloc^.reference.offset := stack_offset;
+
+                { align temp contents to next register size }
+                if not paraaligned then
+                  inc(stack_offset, align(paralen, 8))
+                else
+                  inc(stack_offset, paralen);
+                paralen := 0;
+              end;
+            firstparaloc:=false;
+          end;
+      end;
+
+    function tcpuparamanager.create_varargs_paraloc_info(p: tabstractprocdef; side: tcallercallee; varargspara: tvarargsparalist): longint;
+      var
+        cur_stack_offset: aword;
+        parasize, l: longint;
+        curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
+        i: integer;
+        hp: tparavarsym;
+        paraloc: pcgparalocation;
+      begin
+        init_values(curintreg, curfloatreg, curmmreg, cur_stack_offset);
+        firstfloatreg := curfloatreg;
+
+        result := create_paraloc_info_intern(p, side, p.paras, curintreg, curfloatreg, curmmreg, cur_stack_offset, false);
+        if (p.proccalloption in [pocall_cdecl, pocall_cppdecl, pocall_mwpascal]) then
+          begin
+            { just continue loading the parameters in the registers }
+            if assigned(varargspara) then
+              begin
+                if side=callerside then
+                  result := create_paraloc_info_intern(p, side, varargspara, curintreg, curfloatreg, curmmreg, cur_stack_offset, true)
+                else
+                  internalerror(2022111925);
+                if curfloatreg <> firstfloatreg then
+                  include(varargspara.varargsinfo, va_uses_float_reg);
+              end;
+            { varargs routines have to reserve at least 64 bytes for the RiscV ABI }
+            if (result < 64) then
+              result := 64;
+          end
+        else
+          internalerror(2022111926);
+
+        create_funcretloc_info(p, side);
+      end;
+
+    function tcpuparamanager.parseparaloc(p: tparavarsym; const s: string): boolean;
+      begin
+        internalerror(2022111927);
+        result := true;
+     end;
+
+
+begin
+  paramanager := tcpuparamanager.create;
+end.
+

+ 122 - 0
compiler/loongarch64/cpupi.pas

@@ -0,0 +1,122 @@
+{
+    Copyright (c) 2002 by Florian Klaempfl
+
+    This unit contains the CPU specific part of tprocinfo
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+{ This unit contains the CPU specific part of tprocinfo. }
+unit cpupi;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      cutils,aasmdata,
+      globtype, cgutils, cgbase,
+      procinfo, cpuinfo, psub;
+
+    type
+      tloongarch64procinfo = class(tcgprocinfo)
+        stackframesize,
+        floatregstart : aint;
+        stackpaddingreg: TSuperRegister;
+
+        needs_frame_pointer: boolean;
+
+        constructor create(aparent: tprocinfo); override;
+        procedure set_first_temp_offset; override;
+        function calc_stackframe_size: longint; override;
+      end;
+
+implementation
+
+    uses
+      globals, systems,
+      cpubase,
+      aasmtai,
+      tgobj,cgobj,
+      symconst, symsym, paramgr, symutil, symtable,
+      verbose,
+      aasmcpu;
+
+
+    constructor tloongarch64procinfo.create(aparent: tprocinfo);
+      begin
+        inherited create(aparent);
+        maxpushedparasize := 0;
+        { GCC option -fomit-frame-pointer is default. }
+        framepointer:=NR_STACK_POINTER_REG;
+      end;
+
+
+    procedure tloongarch64procinfo.set_first_temp_offset;
+      begin
+        { TODO framepointer:=NR_STACK_POINTER_REG; Need get backtrace updated. }
+        { TODO Profile }
+
+        if (po_nostackframe in procdef.procoptions) then
+          begin
+            tg.setfirsttemp(align(maxpushedparasize,
+              max(current_settings.alignment.localalignmin,8)));
+          end
+        else
+          begin
+            tg.setfirsttemp(align(maxpushedparasize,
+              max(current_settings.alignment.localalignmin,8)));
+          end;
+      end;
+
+
+    function tloongarch64procinfo.calc_stackframe_size: longint;
+      var
+         firstfloatreg,lastfloatreg,
+         r : byte;
+         floatsavesize : aword;
+         regs: tcpuregisterset;
+      begin
+        maxpushedparasize:=align(maxpushedparasize,
+                             max(current_settings.alignment.localalignmin,8));
+        floatsavesize:=0;
+        case current_settings.fputype of
+          fpu_fd:
+            begin
+              floatsavesize:=0;
+              regs:=cg.rg[R_FPUREGISTER].used_in_proc-paramanager.get_volatile_registers_fpu(pocall_stdcall);
+              for r:=RS_F0 to RS_F31 do
+                if r in regs then
+                  inc(floatsavesize,8);
+            end;
+          else
+            ;
+        end;
+        result:=align(tg.direction*tg.lasttemp,
+                  max(current_settings.alignment.localalignmin,8))
+                  +maxpushedparasize+aint(floatsavesize);
+        if tg.direction=1 then
+          floatregstart:=result-aint(floatsavesize)
+        else
+          floatregstart:=-result+maxpushedparasize;
+      end;
+
+
+begin
+  cprocinfo := tloongarch64procinfo;
+end.
+

+ 82 - 0
compiler/loongarch64/cputarg.pas

@@ -0,0 +1,82 @@
+{
+    Copyright (c) 2001-2002 by Peter Vreman
+
+    Includes the LoongArch64 dependent target units
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit cputarg;
+
+{$i fpcdefs.inc}
+
+interface
+
+
+implementation
+
+    uses
+      systems { prevent a syntax error when nothing is included }
+
+{**************************************
+             Targets
+**************************************}
+
+    {$ifndef NOTARGETLINUX}
+      ,t_linux
+    {$endif}
+
+{**************************************
+             Assemblers
+**************************************}
+
+    {$ifndef NOAGRVGAS}
+      ,agcpugas
+    {$endif}
+
+{**************************************
+        Assembler Readers
+**************************************}
+
+  {$ifndef NoRaRVGas}
+       ,racpugas
+  {$endif NoRaRVGas}
+
+{**************************************
+             Debuginfo
+**************************************}
+
+  {$ifndef NoDbgStabs}
+      ,dbgstabs
+  {$endif NoDbgStabs}
+  {$ifndef NoDbgStabx}
+      ,dbgstabx
+  {$endif NoDbgStabx}
+  {$ifndef NoDbgDwarf}
+      ,dbgdwarf
+  {$endif NoDbgDwarf}
+
+
+{**************************************
+             Optimizer
+**************************************}
+
+    {$ifndef NOOPT}
+      , aoptcpu
+    {$endif NOOPT}
+      ;
+
+end.

+ 185 - 0
compiler/loongarch64/hlcgcpu.pas

@@ -0,0 +1,185 @@
+{
+    Copyright (c) 1998-2010 by Florian Klaempfl and Jonas Maebe
+    Member of the Free Pascal development team
+
+    This unit contains routines high-level cg support LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit hlcgcpu;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  globals,
+  aasmdata,
+  symtype,symdef,
+  cgbase,cgutils,hlcgobj,hlcg2ll, parabase;
+
+type
+
+  thlcgloongarch64 = class(thlcg2ll)
+    protected
+    public
+      procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
+  end;
+
+implementation
+
+  uses
+    verbose,
+    systems,fmodule,
+    symconst, symsym,
+    aasmbase,aasmtai,aasmcpu,
+    cpubase,globtype,
+    procinfo,cpupi,cgobj,cgcpu,
+    defutil;
+
+
+  procedure thlcgloongarch64.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);
+    procedure loadvmttor12;
+      var
+        tmpref,
+        href : treference;
+        l : TAsmLabel;
+      begin
+        reference_reset_base(href,voidpointertype,NR_R4,0,ctempposinvalid,sizeof(pint),[]);
+        href.refaddr:=addr_reg_12i;
+        list.concat(taicpu.op_reg_ref(A_LD_D,NR_R12,href));
+      end;
+    procedure op_onr12methodaddr;
+      var
+        tmpref,
+        href : treference;
+        l : TAsmLabel;
+        offset: longint;
+      begin
+        if (procdef.extnumber=$ffff) then
+          Internalerror(2022111915);
+
+        offset:=tobjectdef(procdef.struct).vmtmethodoffset(procdef.extnumber);
+        if not is_simm12(offset) then
+          begin
+            list.concat(taicpu.op_reg_const(A_LI_D,NR_R15,offset));
+            list.concat(taicpu.op_reg_reg_reg(A_ADD_D,NR_R12,NR_R12,NR_R15));
+            offset:=0;
+          end;
+        reference_reset_base(href,voidpointertype,NR_R12,offset,ctempposinvalid, sizeof(pint),[]);
+        href.refaddr:=addr_reg_12i;
+        cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_R12);
+
+        reference_reset_base(href,voidpointertype,NR_R12,0,ctempposinvalid,0,[]);
+        href.refaddr:=addr_reg;
+        list.concat(taicpu.op_ref(A_JR,href));
+      end;
+    var
+      make_global : boolean;
+      tmpref , href: treference;
+      l : TAsmLabel;
+      hsym: tsym;
+      paraloc: PCGParaLocation;
+      tmpreg: TRegister;
+    begin
+      if not(procdef.proctypeoption in [potype_function,potype_procedure]) then
+        Internalerror(2022111909);
+      if not assigned(procdef.struct) or
+         (procdef.procoptions*[po_classmethod, po_staticmethod,
+           po_methodpointer, po_interrupt, po_iocheck]<>[]) then
+        Internalerror(2022111910);
+      if procdef.owner.symtabletype<>ObjectSymtable then
+        Internalerror(2022111911);
+
+      make_global:=false;
+      if (not current_module.is_unit) or
+         create_smartlink or
+         (procdef.owner.defowner.owner.symtabletype=globalsymtable) then
+        make_global:=true;
+
+      if make_global then
+        list.concat(Tai_symbol.Createname_global(labelname,AT_FUNCTION,0,voidcodepointertype))
+      else
+        list.concat(Tai_symbol.Createname_hidden(labelname,AT_FUNCTION,0,voidcodepointertype));
+
+      { the wrapper might need aktlocaldata for the additional data to
+        load the constant }
+      current_procinfo:=cprocinfo.create(nil);
+
+      { set param1 interface to self  }
+      procdef.init_paraloc_info(callerside);
+      hsym:=tsym(procdef.parast.Find('self'));
+      if not(assigned(hsym) and
+        (hsym.typ=paravarsym)) then
+        internalerror(2022111912);
+      paraloc:=tparavarsym(hsym).paraloc[callerside].location;
+      if assigned(paraloc^.next) then
+        InternalError(2022111913);
+      case paraloc^.loc of
+        LOC_REGISTER:
+          begin
+            if is_simm12(ioffset) then
+              cg.a_op_const_reg(list,OP_SUB, paraloc^.size,ioffset,paraloc^.register)
+            else
+              begin
+                cg.a_load_const_reg(list, paraloc^.size, ioffset, NR_R13);
+                cg.a_op_reg_reg(list, OP_SUB, paraloc^.size, NR_R13, paraloc^.register);
+              end;
+          end;
+      else
+        internalerror(2022111914);
+      end;
+
+      { case 4 }
+      if (po_virtualmethod in procdef.procoptions) and
+          not is_objectpascal_helper(procdef.struct) then
+        begin
+          loadvmttor12;
+          op_onr12methodaddr;
+        end
+      else
+        begin
+          tmpreg:=NR_R12;
+          reference_reset_symbol(href,current_asmdata.RefAsmSymbol(procdef.mangledname,AT_FUNCTION),0,0,[]);
+          href.refaddr:=addr_pc_hi20;
+          list.concat(taicpu.op_reg_ref(A_PCALAU12I,tmpreg,href));
+          href.refaddr:=addr_pc_lo12;
+          list.concat(taicpu.op_reg_reg_ref(A_ADDI_D,tmpreg,tmpreg,href));
+          reference_reset_base(href,voidpointertype,tmpreg,0,ctempposinvalid,0,[]);
+          href.refaddr:=addr_reg;
+          list.concat(taicpu.op_ref(A_JR,href));
+        end;
+      list.concatlist(current_procinfo.aktlocaldata);
+
+      current_procinfo.Free;
+      current_procinfo:=nil;
+
+      list.concat(Tai_symbol_end.Createname(labelname));
+    end;
+
+
+  procedure create_hlcodegen_cpu;
+    begin
+      hlcg:=thlcgloongarch64.create;
+      create_codegen;
+    end;
+
+begin
+  chlcgobj:=thlcgloongarch64;
+  create_hlcodegen:=@create_hlcodegen_cpu;
+end.
+

+ 101 - 0
compiler/loongarch64/itcpugas.pas

@@ -0,0 +1,101 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl
+
+    This unit contains the LoongArch64 GAS instruction tables
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit itcpugas;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      cpubase, cgbase;
+
+    const
+      gas_op2str: array[tasmop] of string[16] = {$i loongarch64att.inc}
+
+    function gas_regnum_search(const s: string): Tregister;
+    function gas_regname(r: Tregister): string;
+
+  implementation
+
+    uses
+      globtype,globals,aasmbase,
+      cutils,verbose, systems,
+      rgbase;
+
+    const
+      gas_regname_table : TRegNameTable = (
+        {$i rloongarch64std.inc}
+      );
+
+      gas_abi_regname_table : TRegNameTable = (
+        {$i rloongarch64abi.inc}
+      );
+
+      gas_regname_index : array[tregisterindex] of tregisterindex = (
+        {$i rloongarch64sri.inc}
+      );
+
+    function findreg_by_gasname(const s:string):tregisterindex;
+      var
+        i,p : tregisterindex;
+      begin
+        {Binary search.}
+        p:=0;
+        i:=regnumber_count_bsstart;
+        repeat
+          if (p+i<=high(tregisterindex)) and (gas_regname_table[gas_regname_index[p+i]]<=s) then
+            p:=p+i;
+          i:=i shr 1;
+        until i=0;
+        if gas_regname_table[gas_regname_index[p]]=s then
+          findreg_by_gasname:=gas_regname_index[p]
+        else
+          findreg_by_gasname:=0;
+      end;
+
+
+    function gas_regnum_search(const s:string):Tregister;
+      begin
+        result:=regnumber_table[findreg_by_gasname(s)];
+      end;
+
+const
+ __regnumber_index : array[tregisterindex] of tregisterindex = (
+        {$i rloongarch64rni.inc}
+      );
+
+
+    function gas_regname(r:Tregister):string;
+      var
+        p : tregisterindex;
+      begin
+        p:=findreg_by_number(r);
+        if p<>0 then
+          { result:=gas_regname_table[p] }
+          { Use abi name instead. }
+          result:=gas_abi_regname_table[p]
+        else
+          result:=generic_regname(r);
+      end;
+
+end.
+

+ 409 - 0
compiler/loongarch64/loongarch64att.inc

@@ -0,0 +1,409 @@
+{ don't edit, this file is generated from loongarchins.dat }
+(
+'none',
+'add.w',
+'add.d',
+'sub.w',
+'sub.d',
+'addi.w',
+'addi.d',
+'addu16i.d',
+'alsl.w',
+'alsl.d',
+'alsl.wu',
+'lu12i.w',
+'lu32i.d',
+'lu52i.d',
+'slt',
+'sltu',
+'slti',
+'sltui',
+'pcaddi',
+'pcaddu12i',
+'pcaddu18i',
+'pcalau12i',
+'and',
+'or',
+'nor',
+'xor',
+'andn',
+'orn',
+'andi',
+'ori',
+'xori',
+'mul.w',
+'mul.d',
+'mulh.w',
+'mulh.du',
+'mulh.d',
+'mulh.wu',
+'mulw.d.w',
+'mulw.d.wu',
+'div.w',
+'div.du',
+'div.d',
+'div.wu',
+'mod.w',
+'mod.du',
+'mod.d',
+'mod.wu',
+'sll.w',
+'sll.d',
+'srl.w',
+'srl.d',
+'sra.w',
+'sra.d',
+'rotr.w',
+'rotr.d',
+'slli.w',
+'slli.d',
+'srli.w',
+'srli.d',
+'srai.w',
+'srai.d',
+'rotri.w',
+'rotri.d',
+'ext.w.b',
+'ext.w.h',
+'clo.w',
+'clo.d',
+'clz.w',
+'clz.d',
+'cto.w',
+'cto.d',
+'ctz.w',
+'ctz.d',
+'bytepick.w',
+'bytepick.d',
+'revb.2h',
+'revb.d',
+'revb.2w',
+'revb.4h',
+'revh.2w',
+'revh.d',
+'bitrev.4b',
+'bitrev.d',
+'bitrev.w',
+'bitrev.8b',
+'bstrins.w',
+'bstrins.d',
+'bstrpick.w',
+'bstrpick.d',
+'maskeqz',
+'masknez',
+'beq',
+'bne',
+'blt',
+'bltu',
+'bge',
+'bgeu',
+'beqz',
+'bnez',
+'b',
+'bl',
+'jirl',
+'ld.b',
+'ld.d',
+'ld.w',
+'ld.h',
+'ld.bu',
+'ld.wu',
+'ld.hu',
+'st.b',
+'st.d',
+'st.w',
+'st.h',
+'ldx.b',
+'ldx.d',
+'ldx.w',
+'ldx.h',
+'ldx.bu',
+'ldx.wu',
+'ldx.hu',
+'stx.b',
+'stx.d',
+'stx.w',
+'stx.h',
+'ldptr.w',
+'ldptr.d',
+'stptr.w',
+'stptr.d',
+'preld',
+'preldx',
+'ldgt.b',
+'ldgt.d',
+'ldgt.w',
+'ldgt.h',
+'ldle.b',
+'ldle.d',
+'ldle.w',
+'ldle.h',
+'stgt.b',
+'stgt.d',
+'stgt.w',
+'stgt.h',
+'stle.b',
+'stle.d',
+'stle.w',
+'stle.h',
+'amswap.w',
+'amswap.d',
+'amswap_db.w',
+'amswap_db.d',
+'amadd.w',
+'amadd.d',
+'amadd_db.w',
+'amadd_db.d',
+'amand.w',
+'amand.d',
+'amand_db.w',
+'amand_db.d',
+'amor.w',
+'amor.d',
+'amor_db.w',
+'amor_db.d',
+'amxor.w',
+'amxor.d',
+'amxor_db.w',
+'amxor_db.d',
+'ammax.w',
+'ammax.du',
+'ammax.d',
+'ammax.wu',
+'ammax_db.w',
+'ammax_db.du',
+'ammax_db.d',
+'ammax_db.wu',
+'ammin.w',
+'ammin.du',
+'ammin.d',
+'ammin.wu',
+'ammin_db.w',
+'ammin_db.du',
+'ammin_db.d',
+'ammin_db.wu',
+'ll.w',
+'ll.d',
+'sc.w',
+'sc.d',
+'dbar',
+'ibar',
+'crc.w.b.w',
+'crc.w.d.w',
+'crc.w.w.w',
+'crc.w.h.w',
+'crcc.w.b.w',
+'crcc.w.d.w',
+'crcc.w.w.w',
+'crcc.w.h.w',
+'syscall',
+'break',
+'asrtle.d',
+'asrtgt.d',
+'rdtimel.w',
+'rdtimeh.w',
+'rdtime.d',
+'cpucfg',
+'fadd.s',
+'fadd.d',
+'fsub.s',
+'fsub.d',
+'fmul.s',
+'fmul.d',
+'fdiv.s',
+'fdiv.d',
+'fmadd.s',
+'fmadd.d',
+'fmsub.s',
+'fmsub.d',
+'fnmadd.s',
+'fnmadd.d',
+'fnmsub.s',
+'fnmsub.d',
+'fmax.s',
+'fmax.d',
+'fmin.s',
+'fmin.d',
+'fmaxa.s',
+'fmaxa.d',
+'fmina.s',
+'fmina.d',
+'fabs.s',
+'fabs.d',
+'fneg.s',
+'fneg.d',
+'fsqrt.s',
+'fsqrt.d',
+'frecip.s',
+'frecip.d',
+'frsqrt.s',
+'frsqrt.d',
+'fcaleb.s',
+'fcaleb.d',
+'flogb.s',
+'flogb.d',
+'fcopysign.s',
+'fcopysign.d',
+'fclass.s',
+'fclass.d',
+'fcmp.caf.s',
+'fcmp.sune.s',
+'fcmp.sor.s',
+'fcmp.sne.s',
+'fcmp.sule.s',
+'fcmp.sge.s',
+'fcmp.sle.s',
+'fcmp.sult.s',
+'fcmp.sgt.s',
+'fcmp.slt.s',
+'fcmp.sueq.s',
+'fcmp.seq.s',
+'fcmp.sun.s',
+'fcmp.saf.s',
+'fcmp.cune.s',
+'fcmp.cor.s',
+'fcmp.cne.s',
+'fcmp.cuge.s',
+'fcmp.cule.s',
+'fcmp.cle.s',
+'fcmp.cugt.s',
+'fcmp.cult.s',
+'fcmp.clt.s',
+'fcmp.cueq.s',
+'fcmp.ceq.s',
+'fcmp.cun.s',
+'fcmp.caf.d',
+'fcmp.sune.d',
+'fcmp.sor.d',
+'fcmp.sne.d',
+'fcmp.sule.d',
+'fcmp.sge.d',
+'fcmp.sle.d',
+'fcmp.sult.d',
+'fcmp.sgt.d',
+'fcmp.slt.d',
+'fcmp.sueq.d',
+'fcmp.seq.d',
+'fcmp.sun.d',
+'fcmp.saf.d',
+'fcmp.cune.d',
+'fcmp.cor.d',
+'fcmp.cne.d',
+'fcmp.cuge.d',
+'fcmp.cule.d',
+'fcmp.cle.d',
+'fcmp.cugt.d',
+'fcmp.cult.d',
+'fcmp.clt.d',
+'fcmp.cueq.d',
+'fcmp.ceq.d',
+'fcmp.cun.d',
+'fcvt.s.d',
+'fcvt.d.s',
+'ffint.s.l',
+'ffint.d.l',
+'ffint.s.w',
+'ffint.d.w',
+'ftint.l.s',
+'ftint.w.s',
+'ftint.l.d',
+'ftint.w.d',
+'ftintrm.l.s',
+'ftintrm.w.s',
+'ftintrm.l.d',
+'ftintrm.w.d',
+'ftintrp.l.s',
+'ftintrp.w.s',
+'ftintrp.l.d',
+'ftintrp.w.d',
+'ftintrz.l.s',
+'ftintrz.w.s',
+'ftintrz.l.d',
+'ftintrz.w.d',
+'ftintrne.l.s',
+'ftintrne.w.s',
+'ftintrne.l.d',
+'ftintrne.w.d',
+'frint.s',
+'frint.d',
+'fmov.s',
+'fmov.d',
+'fsel',
+'movgr2fr.w',
+'movgr2fr.d',
+'movgr2frh.w',
+'movfr2gr.s',
+'movfr2gr.d',
+'movfrh2gr.s',
+'movgr2fcsr',
+'movfcsr2gr',
+'movfr2cf',
+'movcf2fr',
+'movgr2cf',
+'movcf2gr',
+'bceqz',
+'bcnez',
+'fld.s',
+'fld.d',
+'fst.s',
+'fst.d',
+'fldx.s',
+'fldx.d',
+'fstx.s',
+'fstx.d',
+'fldgt.s',
+'fldgt.d',
+'fldle.s',
+'fldle.d',
+'fstgt.s',
+'fstgt.d',
+'fstle.s',
+'fstle.d',
+'csrrd',
+'csrwr',
+'csrxchg',
+'iocsrrd.b',
+'iocsrrd.d',
+'iocsrrd.w',
+'iocsrrd.h',
+'iocsrwr.b',
+'iocsrwr.d',
+'iocsrwr.w',
+'iocsrwr.h',
+'cacop',
+'tlbsrch',
+'tlbrd',
+'tlbwr',
+'tlbfill',
+'tlbclr',
+'tlbflush',
+'invtlb',
+'lddir',
+'ldpte',
+'ertn',
+'dbcl',
+'idle',
+'nop',
+'li.w',
+'li.d',
+'la.global',
+'la.tls.gd',
+'la.tls.ld',
+'la.tls.ie',
+'la.tle.le',
+'la.got',
+'la.pcrel',
+'la.abs',
+'la.local',
+'bltz',
+'bgtz',
+'bgez',
+'blez',
+'jr',
+'bgt',
+'ble',
+'bgtu',
+'bleu',
+'move',
+'b'
+);

+ 2 - 0
compiler/loongarch64/loongarch64nop.inc

@@ -0,0 +1,2 @@
+{ don't edit, this file is generated from loongarchins.dat }
+406;

+ 409 - 0
compiler/loongarch64/loongarch64op.inc

@@ -0,0 +1,409 @@
+{ don't edit, this file is generated from loongarchins.dat }
+(
+A_NONE,
+A_ADD_W,
+A_ADD_D,
+A_SUB_W,
+A_SUB_D,
+A_ADDI_W,
+A_ADDI_D,
+A_ADDU16I_D,
+A_ALSL_W,
+A_ALSL_D,
+A_ALSL_WU,
+A_LU12I_W,
+A_LU32I_D,
+A_LU52I_D,
+A_SLT,
+A_SLTU,
+A_SLTI,
+A_SLTUI,
+A_PCADDI,
+A_PCADDU12I,
+A_PCADDU18I,
+A_PCALAU12I,
+A_AND,
+A_OR,
+A_NOR,
+A_XOR,
+A_ANDN,
+A_ORN,
+A_ANDI,
+A_ORI,
+A_XORI,
+A_MUL_W,
+A_MUL_D,
+A_MULH_W,
+A_MULH_DU,
+A_MULH_D,
+A_MULH_WU,
+A_MULW_D_W,
+A_MULW_D_WU,
+A_DIV_W,
+A_DIV_DU,
+A_DIV_D,
+A_DIV_WU,
+A_MOD_W,
+A_MOD_DU,
+A_MOD_D,
+A_MOD_WU,
+A_SLL_W,
+A_SLL_D,
+A_SRL_W,
+A_SRL_D,
+A_SRA_W,
+A_SRA_D,
+A_ROTR_W,
+A_ROTR_D,
+A_SLLI_W,
+A_SLLI_D,
+A_SRLI_W,
+A_SRLI_D,
+A_SRAI_W,
+A_SRAI_D,
+A_ROTRI_W,
+A_ROTRI_D,
+A_EXT_W_B,
+A_EXT_W_H,
+A_CLO_W,
+A_CLO_D,
+A_CLZ_W,
+A_CLZ_D,
+A_CTO_W,
+A_CTO_D,
+A_CTZ_W,
+A_CTZ_D,
+A_BYTEPICK_W,
+A_BYTEPICK_D,
+A_REVB_2H,
+A_REVB_D,
+A_REVB_2W,
+A_REVB_4H,
+A_REVH_2W,
+A_REVH_D,
+A_BITREV_4B,
+A_BITREV_D,
+A_BITREV_W,
+A_BITREV_8B,
+A_BSTRINS_W,
+A_BSTRINS_D,
+A_BSTRPICK_W,
+A_BSTRPICK_D,
+A_MASKEQZ,
+A_MASKNEZ,
+A_BEQ,
+A_BNE,
+A_BLT,
+A_BLTU,
+A_BGE,
+A_BGEU,
+A_BEQZ,
+A_BNEZ,
+A_B,
+A_BL,
+A_JIRL,
+A_LD_B,
+A_LD_D,
+A_LD_W,
+A_LD_H,
+A_LD_BU,
+A_LD_WU,
+A_LD_HU,
+A_ST_B,
+A_ST_D,
+A_ST_W,
+A_ST_H,
+A_LDX_B,
+A_LDX_D,
+A_LDX_W,
+A_LDX_H,
+A_LDX_BU,
+A_LDX_WU,
+A_LDX_HU,
+A_STX_B,
+A_STX_D,
+A_STX_W,
+A_STX_H,
+A_LDPTR_W,
+A_LDPTR_D,
+A_STPTR_W,
+A_STPTR_D,
+A_PRELD,
+A_PRELDX,
+A_LDGT_B,
+A_LDGT_D,
+A_LDGT_W,
+A_LDGT_H,
+A_LDLE_B,
+A_LDLE_D,
+A_LDLE_W,
+A_LDLE_H,
+A_STGT_B,
+A_STGT_D,
+A_STGT_W,
+A_STGT_H,
+A_STLE_B,
+A_STLE_D,
+A_STLE_W,
+A_STLE_H,
+A_AMSWAP_W,
+A_AMSWAP_D,
+A_AMSWAP_DB_W,
+A_AMSWAP_DB_D,
+A_AMADD_W,
+A_AMADD_D,
+A_AMADD_DB_W,
+A_AMADD_DB_D,
+A_AMAND_W,
+A_AMAND_D,
+A_AMAND_DB_W,
+A_AMAND_DB_D,
+A_AMOR_W,
+A_AMOR_D,
+A_AMOR_DB_W,
+A_AMOR_DB_D,
+A_AMXOR_W,
+A_AMXOR_D,
+A_AMXOR_DB_W,
+A_AMXOR_DB_D,
+A_AMMAX_W,
+A_AMMAX_DU,
+A_AMMAX_D,
+A_AMMAX_WU,
+A_AMMAX_DB_W,
+A_AMMAX_DB_DU,
+A_AMMAX_DB_D,
+A_AMMAX_DB_WU,
+A_AMMIN_W,
+A_AMMIN_DU,
+A_AMMIN_D,
+A_AMMIN_WU,
+A_AMMIN_DB_W,
+A_AMMIN_DB_DU,
+A_AMMIN_DB_D,
+A_AMMIN_DB_WU,
+A_LL_W,
+A_LL_D,
+A_SC_W,
+A_SC_D,
+A_DBAR,
+A_IBAR,
+A_CRC_W_B_W,
+A_CRC_W_D_W,
+A_CRC_W_W_W,
+A_CRC_W_H_W,
+A_CRCC_W_B_W,
+A_CRCC_W_D_W,
+A_CRCC_W_W_W,
+A_CRCC_W_H_W,
+A_SYSCALL,
+A_BREAK,
+A_ASRTLE_D,
+A_ASRTGT_D,
+A_RDTIMEL_W,
+A_RDTIMEH_W,
+A_RDTIME_D,
+A_CPUCFG,
+A_FADD_S,
+A_FADD_D,
+A_FSUB_S,
+A_FSUB_D,
+A_FMUL_S,
+A_FMUL_D,
+A_FDIV_S,
+A_FDIV_D,
+A_FMADD_S,
+A_FMADD_D,
+A_FMSUB_S,
+A_FMSUB_D,
+A_FNMADD_S,
+A_FNMADD_D,
+A_FNMSUB_S,
+A_FNMSUB_D,
+A_FMAX_S,
+A_FMAX_D,
+A_FMIN_S,
+A_FMIN_D,
+A_FMAXA_S,
+A_FMAXA_D,
+A_FMINA_S,
+A_FMINA_D,
+A_FABS_S,
+A_FABS_D,
+A_FNEG_S,
+A_FNEG_D,
+A_FSQRT_S,
+A_FSQRT_D,
+A_FRECIP_S,
+A_FRECIP_D,
+A_FRSQRT_S,
+A_FRSQRT_D,
+A_FCALEB_S,
+A_FCALEB_D,
+A_FLOGB_S,
+A_FLOGB_D,
+A_FCOPYSIGN_S,
+A_FCOPYSIGN_D,
+A_FCLASS_S,
+A_FCLASS_D,
+A_FCMP_CAF_S,
+A_FCMP_SUNE_S,
+A_FCMP_SOR_S,
+A_FCMP_SNE_S,
+A_FCMP_SULE_S,
+A_FCMP_SGE_S,
+A_FCMP_SLE_S,
+A_FCMP_SULT_S,
+A_FCMP_SGT_S,
+A_FCMP_SLT_S,
+A_FCMP_SUEQ_S,
+A_FCMP_SEQ_S,
+A_FCMP_SUN_S,
+A_FCMP_SAF_S,
+A_FCMP_CUNE_S,
+A_FCMP_COR_S,
+A_FCMP_CNE_S,
+A_FCMP_CUGE_S,
+A_FCMP_CULE_S,
+A_FCMP_CLE_S,
+A_FCMP_CUGT_S,
+A_FCMP_CULT_S,
+A_FCMP_CLT_S,
+A_FCMP_CUEQ_S,
+A_FCMP_CEQ_S,
+A_FCMP_CUN_S,
+A_FCMP_CAF_D,
+A_FCMP_SUNE_D,
+A_FCMP_SOR_D,
+A_FCMP_SNE_D,
+A_FCMP_SULE_D,
+A_FCMP_SGE_D,
+A_FCMP_SLE_D,
+A_FCMP_SULT_D,
+A_FCMP_SGT_D,
+A_FCMP_SLT_D,
+A_FCMP_SUEQ_D,
+A_FCMP_SEQ_D,
+A_FCMP_SUN_D,
+A_FCMP_SAF_D,
+A_FCMP_CUNE_D,
+A_FCMP_COR_D,
+A_FCMP_CNE_D,
+A_FCMP_CUGE_D,
+A_FCMP_CULE_D,
+A_FCMP_CLE_D,
+A_FCMP_CUGT_D,
+A_FCMP_CULT_D,
+A_FCMP_CLT_D,
+A_FCMP_CUEQ_D,
+A_FCMP_CEQ_D,
+A_FCMP_CUN_D,
+A_FCVT_S_D,
+A_FCVT_D_S,
+A_FFINT_S_L,
+A_FFINT_D_L,
+A_FFINT_S_W,
+A_FFINT_D_W,
+A_FTINT_L_S,
+A_FTINT_W_S,
+A_FTINT_L_D,
+A_FTINT_W_D,
+A_FTINTRM_L_S,
+A_FTINTRM_W_S,
+A_FTINTRM_L_D,
+A_FTINTRM_W_D,
+A_FTINTRP_L_S,
+A_FTINTRP_W_S,
+A_FTINTRP_L_D,
+A_FTINTRP_W_D,
+A_FTINTRZ_L_S,
+A_FTINTRZ_W_S,
+A_FTINTRZ_L_D,
+A_FTINTRZ_W_D,
+A_FTINTRNE_L_S,
+A_FTINTRNE_W_S,
+A_FTINTRNE_L_D,
+A_FTINTRNE_W_D,
+A_FRINT_S,
+A_FRINT_D,
+A_FMOV_S,
+A_FMOV_D,
+A_FSEL,
+A_MOVGR2FR_W,
+A_MOVGR2FR_D,
+A_MOVGR2FRH_W,
+A_MOVFR2GR_S,
+A_MOVFR2GR_D,
+A_MOVFRH2GR_S,
+A_MOVGR2FCSR,
+A_MOVFCSR2GR,
+A_MOVFR2CF,
+A_MOVCF2FR,
+A_MOVGR2CF,
+A_MOVCF2GR,
+A_BCEQZ,
+A_BCNEZ,
+A_FLD_S,
+A_FLD_D,
+A_FST_S,
+A_FST_D,
+A_FLDX_S,
+A_FLDX_D,
+A_FSTX_S,
+A_FSTX_D,
+A_FLDGT_S,
+A_FLDGT_D,
+A_FLDLE_S,
+A_FLDLE_D,
+A_FSTGT_S,
+A_FSTGT_D,
+A_FSTLE_S,
+A_FSTLE_D,
+A_CSRRD,
+A_CSRWR,
+A_CSRXCHG,
+A_IOCSRRD_B,
+A_IOCSRRD_D,
+A_IOCSRRD_W,
+A_IOCSRRD_H,
+A_IOCSRWR_B,
+A_IOCSRWR_D,
+A_IOCSRWR_W,
+A_IOCSRWR_H,
+A_CACOP,
+A_TLBSRCH,
+A_TLBRD,
+A_TLBWR,
+A_TLBFILL,
+A_TLBCLR,
+A_TLBFLUSH,
+A_INVTLB,
+A_LDDIR,
+A_LDPTE,
+A_ERTN,
+A_DBCL,
+A_IDLE,
+A_NOP,
+A_LI_W,
+A_LI_D,
+A_LA_GLOBAL,
+A_LA_TLS_GD,
+A_LA_TLS_LD,
+A_LA_TLS_IE,
+A_LA_TLE_LE,
+A_LA_GOT,
+A_LA_PCREL,
+A_LA_ABS,
+A_LA_LOCAL,
+A_BLTZ,
+A_BGTZ,
+A_BGEZ,
+A_BLEZ,
+A_JR,
+A_BGT,
+A_BLE,
+A_BGTU,
+A_BLEU,
+A_MOVE,
+A_BXX
+);

+ 404 - 0
compiler/loongarch64/loongarchins.dat

@@ -0,0 +1,404 @@
+; [NAME](format0)
+;
+; format
+; a, 'w' or 'd'
+; b, 'w'
+; c, 'd'
+; d, 'w' or 'wu' or 'd'
+; e, 'w' or 'wu' or 'd' or 'du'
+; f, 'w' or 'wu'
+; g, 'b' or 'h'
+; h, '2h' or '4h' or '2w' or 'd'
+; i, '2w' or 'd'
+; j, '4b' or '8b' or 'w' or 'd'
+; k, 'b' or 'h' or 'w' or 'd'
+; l, 'bu' or 'hu' or 'wu'
+; m, 's' or 'd'
+; n, fcmp condtions, caf cun ceq cueq clt     cult cugt cle     cule cuge cne cor cune
+;                    saf sun seq sueq slt sgt sult      sle sge sule      sne sor sune
+; o, 's'
+; p, 'd'
+; q, 'l' or 'w'
+; r, 'global' or 'local' or 'abs/pcrel/got' or 'tls.le/ie/ld/gd'
+
+[NONE](0)
+
+[ADD](a0)
+
+[SUB](a0)
+
+[ADDI](a0)
+
+[ADDU16I](c0)
+
+[ALSL](d0)
+
+[LU12I](b0)
+
+[LU32I](c0)
+
+[LU52I](c0)
+
+[SLT](0)
+
+[SLTU](0)
+
+[SLTI](0)
+
+[SLTUI](0)
+
+[PCADDI](0)
+
+[PCADDU12I](0)
+
+[PCADDU18I](0)
+
+[PCALAU12I](0)
+
+[AND](0)
+
+[OR](0)
+
+[NOR](0)
+
+[XOR](0)
+
+[ANDN](0)
+
+[ORN](0)
+
+[ANDI](0)
+
+[ORI](0)
+
+[XORI](0)
+
+[MUL](a0)
+
+[MULH](e0)
+
+[MULW](cf0)
+
+[DIV](e0)
+
+[MOD](e0)
+
+[SLL](a0)
+
+[SRL](a0)
+
+[SRA](a0)
+
+[ROTR](a0)
+
+[SLLI](a0)
+
+[SRLI](a0)
+
+[SRAI](a0)
+
+[ROTRI](a0)
+
+[EXT](bg0)
+
+[CLO](a0)
+
+[CLZ](a0)
+
+[CTO](a0)
+
+[CTZ](a0)
+
+[BYTEPICK](a0)
+
+[REVB](h0)
+
+[REVH](i0)
+
+[BITREV](j0)
+
+[BSTRINS](a0)
+
+[BSTRPICK](a0)
+
+[MASKEQZ](0)
+
+[MASKNEZ](0)
+
+[BEQ](0)
+
+[BNE](0)
+
+[BLT](0)
+
+[BLTU](0)
+
+[BGE](0)
+
+[BGEU](0)
+
+[BEQZ](0)
+
+[BNEZ](0)
+
+[B](0)
+
+[BL](0)
+
+[JIRL](0)
+
+[LD](k0)
+
+[LD](l0)
+
+[ST](k0)
+
+[LDX](k0)
+
+[LDX](l0)
+
+[STX](k0)
+
+[LDPTR](a0)
+
+[STPTR](a0)
+
+[PRELD](0)
+
+[PRELDX](0)
+
+[LDGT](k0)
+
+[LDLE](k0)
+
+[STGT](k0)
+
+[STLE](k0)
+
+[AMSWAP](a0)
+
+[AMSWAP_DB](a0)
+
+[AMADD](a0)
+
+[AMADD_DB](a0)
+
+[AMAND](a0)
+
+[AMAND_DB](a0)
+
+[AMOR](a0)
+
+[AMOR_DB](a0)
+
+[AMXOR](a0)
+
+[AMXOR_DB](a0)
+
+[AMMAX](e0)
+
+[AMMAX_DB](e0)
+
+[AMMIN](e0)
+
+[AMMIN_DB](e0)
+
+[LL](a0)
+
+[SC](a0)
+
+[DBAR](0)
+
+[IBAR](0)
+
+[CRC](bkb0)
+
+[CRCC](bkb0)
+
+[SYSCALL](0)
+
+[BREAK](0)
+
+[ASRTLE](c0)
+
+[ASRTGT](c0)
+
+[RDTIMEL](b0)
+
+[RDTIMEH](b0)
+
+[RDTIME](c0)
+
+[CPUCFG](0)
+
+[FADD](m0)
+
+[FSUB](m0)
+
+[FMUL](m0)
+
+[FDIV](m0)
+
+[FMADD](m0)
+
+[FMSUB](m0)
+
+[FNMADD](m0)
+
+[FNMSUB](m0)
+
+[FMAX](m0)
+
+[FMIN](m0)
+
+[FMAXA](m0)
+
+[FMINA](m0)
+
+[FABS](m0)
+
+[FNEG](m0)
+
+[FSQRT](m0)
+
+[FRECIP](m0)
+
+[FRSQRT](m0)
+
+[FCALEB](m0)
+
+[FLOGB](m0)
+
+[FCOPYSIGN](m0)
+
+[FCLASS](m0)
+
+[FCMP](nm0)
+
+[FCVT](op0)
+
+[FCVT](po0)
+
+[FFINT](mq0)
+
+[FTINT](qm0)
+
+[FTINTRM](qm0)
+
+[FTINTRP](qm0)
+
+[FTINTRZ](qm0)
+
+[FTINTRNE](qm0)
+
+[FRINT](m0)
+
+[FMOV](m0)
+
+[FSEL](0)
+
+[MOVGR2FR](a0)
+
+[MOVGR2FRH](b0)
+
+[MOVFR2GR](m0)
+
+[MOVFRH2GR](o0)
+
+[MOVGR2FCSR](0)
+
+[MOVFCSR2GR](0)
+
+[MOVFR2CF](0)
+
+[MOVCF2FR](0)
+
+[MOVGR2CF](0)
+
+[MOVCF2GR](0)
+
+[BCEQZ](0)
+
+[BCNEZ](0)
+
+[FLD](m0)
+
+[FST](m0)
+
+[FLDX](m0)
+
+[FSTX](m0)
+
+[FLDGT](m0)
+
+[FLDLE](m0)
+
+[FSTGT](m0)
+
+[FSTLE](m0)
+
+[CSRRD](0)
+
+[CSRWR](0)
+
+[CSRXCHG](0)
+
+[IOCSRRD](k0)
+
+[IOCSRWR](k0)
+
+[CACOP](0)
+
+[TLBSRCH](0)
+
+[TLBRD](0)
+
+[TLBWR](0)
+
+[TLBFILL](0)
+
+[TLBCLR](0)
+
+[TLBFLUSH](0)
+
+[INVTLB](0)
+
+[LDDIR](0)
+
+[LDPTE](0)
+
+[ERTN](0)
+
+[DBCL](0)
+
+[IDLE](0)
+
+; Macro Used by Binutils
+[NOP](0)
+
+[LI](a0)
+
+[LA](r0)
+
+[BLTZ](0)
+
+[BGTZ](0)
+
+[BGEZ](0)
+
+[BLEZ](0)
+
+[JR](0)
+
+[BGT](0)
+
+[BLE](0)
+
+[BGTU](0)
+
+[BLEU](0)
+
+[MOVE](0)
+
+; Macro Used Bt Free Pascal
+[BXX](0)

+ 83 - 0
compiler/loongarch64/loongarchreg.dat

@@ -0,0 +1,83 @@
+;
+; LoongArch registers
+;
+; layout
+; <name>,<type>,<subtype>,<value>,<abiname>,<stdname>,<stab idx>,<dwarf idx>
+;
+; We don't care stabs because STABS dbginfo is obsolete
+
+NO,$00,$00,$00,INVALID,INVALID,-1,-1
+R0,$01,$00,$00,$zero,$r0,0,0
+R1,$01,$00,$01,$ra,$r1,1,1
+R2,$01,$00,$02,$tp,$r2,2,2
+R3,$01,$00,$03,$sp,$r3,3,3
+R4,$01,$00,$04,$a0,$r4,4,4
+R5,$01,$00,$05,$a1,$r5,5,5
+R6,$01,$00,$06,$a2,$r6,6,6
+R7,$01,$00,$07,$a3,$r7,7,7
+R8,$01,$00,$08,$a4,$r8,8,8
+R9,$01,$00,$09,$a5,$r9,9,9
+R10,$01,$00,$0A,$a6,$r10,10,10
+R11,$01,$00,$0B,$a7,$r11,11,11
+R12,$01,$00,$0C,$t0,$r12,12,12
+R13,$01,$00,$0D,$t1,$r13,13,13
+R14,$01,$00,$0E,$t2,$r14,14,14
+R15,$01,$00,$0F,$t3,$r15,15,15
+R16,$01,$00,$10,$t4,$r16,16,16
+R17,$01,$00,$11,$t5,$r17,17,17
+R18,$01,$00,$12,$t6,$r18,18,18
+R19,$01,$00,$13,$t7,$r19,19,19
+R20,$01,$00,$14,$t8,$r20,20,20
+R21,$01,$00,$15,$x,$r21,21,21
+R22,$01,$00,$16,$fp,$r22,22,22
+R23,$01,$00,$17,$s0,$r23,23,23
+R24,$01,$00,$18,$s1,$r24,24,24
+R25,$01,$00,$19,$s2,$r25,25,25
+R26,$01,$00,$1A,$s3,$r26,26,26
+R27,$01,$00,$1B,$s4,$r27,27,27
+R28,$01,$00,$1C,$s5,$r28,28,28
+R29,$01,$00,$1D,$s6,$r29,29,29
+R30,$01,$00,$1E,$s7,$r30,30,30
+R31,$01,$00,$1F,$s8,$r31,31,31
+
+F0,$02,$00,$00,$fa0,$f0,32,32
+F1,$02,$00,$01,$fa1,$f1,33,33
+F2,$02,$00,$02,$fa2,$f2,34,34
+F3,$02,$00,$03,$fa3,$f3,35,35
+F4,$02,$00,$04,$fa4,$f4,36,36
+F5,$02,$00,$05,$fa5,$f5,37,37
+F6,$02,$00,$06,$fa6,$f6,38,38
+F7,$02,$00,$07,$fa7,$f7,39,39
+F8,$02,$00,$08,$ft0,$f8,40,40
+F9,$02,$00,$09,$ft1,$f9,41,41
+F10,$02,$00,$0A,$ft2,$f10,42,42
+F11,$02,$00,$0B,$ft3,$f11,43,43
+F12,$02,$00,$0C,$ft4,$f12,44,44
+F13,$02,$00,$0D,$ft5,$f13,45,45
+F14,$02,$00,$0E,$ft6,$f14,46,46
+F15,$02,$00,$0F,$ft7,$f15,47,47
+F16,$02,$00,$10,$ft8,$f16,48,48
+F17,$02,$00,$11,$ft9,$f17,49,49
+F18,$02,$00,$12,$ft10,$f18,50,50
+F19,$02,$00,$13,$ft11,$f19,51,51
+F20,$02,$00,$14,$ft12,$f20,52,52
+F21,$02,$00,$15,$ft13,$f21,53,53
+F22,$02,$00,$16,$ft14,$f22,54,54
+F23,$02,$00,$17,$ft15,$f23,55,55
+F24,$02,$00,$18,$fs0,$f24,56,56
+F25,$02,$00,$19,$fs1,$f25,57,57
+F26,$02,$00,$1A,$fs2,$f26,58,58
+F27,$02,$00,$1B,$fs3,$f27,59,59
+F28,$02,$00,$1C,$fs4,$f28,60,60
+F29,$02,$00,$1D,$fs5,$f29,61,61
+F30,$02,$00,$1E,$fs6,$f30,62,62
+F31,$02,$00,$1F,$fs7,$f31,63,63
+
+FCC0,$05,$00,$00,$fcc0,$fcc0,64,64
+FCC1,$05,$00,$01,$fcc1,$fcc1,65,65
+FCC2,$05,$00,$02,$fcc2,$fcc2,66,66
+FCC3,$05,$00,$03,$fcc3,$fcc3,67,67
+FCC4,$05,$00,$04,$fcc4,$fcc4,68,68
+FCC5,$05,$00,$05,$fcc5,$fcc5,69,69
+FCC6,$05,$00,$06,$fcc6,$fcc6,70,70
+FCC7,$05,$00,$07,$fcc7,$fcc7,71,71

+ 401 - 0
compiler/loongarch64/ncpuadd.pas

@@ -0,0 +1,401 @@
+{
+    Copyright (c) 2000-2006 by Florian Klaempfl and Jonas Maebe
+
+    Code generation for add nodes on the LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+unit ncpuadd;
+
+{$i fpcdefs.inc}
+
+  interface
+
+    uses
+       node,nadd,ncgadd,cpubase;
+
+    type
+      tloongarch64addnode = class(tcgaddnode)
+      private
+        procedure Cmp(signed,is_smallset: boolean);
+      protected
+        procedure second_cmpsmallset;override;
+        procedure second_cmpordinal;override;
+        procedure second_cmp64bit; override;
+
+        procedure second_addordinal; override;
+        procedure second_add64bit; override;
+
+        procedure pass_left_and_right;
+
+        procedure second_addfloat;override;
+        procedure second_cmpfloat;override;
+      public
+        function use_generic_mul32to64: boolean; override;
+      end;
+
+
+implementation
+
+    uses
+      globtype,systems,
+      cutils,verbose,globals,
+      symconst,symdef,paramgr,
+      aasmbase,aasmtai,aasmdata,aasmcpu,defutil,htypechk,
+      cgbase,cpuinfo,pass_1,pass_2,
+      cpupara,cgcpu,cgutils,procinfo,
+      ncon,nset,
+      ncgutil,tgobj,rgobj,rgcpu,cgobj,hlcgobj;
+
+
+    procedure tloongarch64addnode.Cmp(signed,is_smallset: boolean);
+      var
+        flabel,tlabel: tasmlabel;
+        op, opi: TAsmOp;
+        allow_constant : boolean;
+      begin
+        pass_left_right;
+
+        allow_constant:=(not is_smallset) or not (nodetype in [lten,gten]);
+
+        force_reg_left_right(true,allow_constant);
+
+        if nf_swapped in flags then
+          swapleftright;
+
+        location_reset(location,LOC_REGISTER,OS_INT);
+        location.register:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
+
+        if signed then op:=A_SLT else op:=A_SLTU;
+        if signed then opi:=A_SLTI else opi:=A_SLTUI;
+
+        case nodetype of
+          equaln:
+            begin
+              if not (left.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+              if (right.location.loc=LOC_CONSTANT) and
+                 (not is_uimm12(right.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+
+              if right.location.loc=LOC_CONSTANT then
+                if right.location.value = 0 then
+                  cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,left.location.register,location.register)
+                else
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_XORI,location.register,left.location.register,right.location.value))
+              else
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_XOR,location.register,left.location.register,right.location.register));
+              current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_SLTUI,location.register,location.register,1));
+            end;
+          unequaln:
+            begin
+              if not (left.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+              if (right.location.loc=LOC_CONSTANT) and
+                 (not is_uimm12(right.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+
+              if right.location.loc=LOC_CONSTANT then
+                if right.location.value = 0 then
+                  cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,left.location.register,location.register)
+                else
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_XORI,location.register,left.location.register,right.location.value))
+              else
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_XOR,location.register,left.location.register,right.location.register));
+              current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_SLTU,location.register,NR_R0,location.register));
+            end;
+          ltn:
+            begin
+              if not (left.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+              if (right.location.loc=LOC_CONSTANT) and
+                 (not is_simm12(right.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+
+              if right.location.loc=LOC_CONSTANT then
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(opi,location.register,left.location.register,right.location.value))
+              else
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,location.register,left.location.register,right.location.register));
+            end;
+          gtn:
+            begin
+              if not (right.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+
+              if (left.location.loc=LOC_CONSTANT) and
+                 (not is_simm12(left.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+              if left.location.loc=LOC_CONSTANT then
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(opi,location.register,right.location.register,left.location.value))
+              else
+                current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,location.register,right.location.register,left.location.register));
+            end;
+
+          lten:
+            begin
+              if not (right.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+
+              if (left.location.loc=LOC_CONSTANT) and
+                 (not is_simm12(left.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+              if is_smallset then
+                begin
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_ANDN,location.register,left.location.register,right.location.register));
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_SLTUI,location.register,location.register,1));
+                end
+              else
+                begin
+                  if left.location.loc=LOC_CONSTANT then
+                    current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(opi,location.register,right.location.register,left.location.value))
+                  else
+                    current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,location.register,right.location.register,left.location.register));
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_XORI,location.register,location.register,1));
+                end;
+            end;
+          gten:
+            begin
+              if not (left.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+              if (right.location.loc=LOC_CONSTANT) and
+                 (not is_simm12(right.location.value)) then
+                hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false);
+              if is_smallset then
+                begin
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_ANDN,location.register,right.location.register,left.location.register));
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_SLTUI,location.register,location.register,1));
+                end
+              else
+                begin
+                   if right.location.loc=LOC_CONSTANT then
+                    current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(opi,location.register,left.location.register,right.location.value))
+                  else
+                    current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,location.register,left.location.register,right.location.register));
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_XORI,location.register,location.register,1));
+                end;
+            end;
+        else
+          Internalerror(2022111946);
+        end;
+      end;
+
+
+    { Smallset means the one all bits in another one. }
+    procedure tloongarch64addnode.second_cmpsmallset;
+      begin
+        Cmp(false,true);
+      end;
+
+
+    procedure tloongarch64addnode.second_cmpordinal;
+      var
+        unsigned: Boolean;
+      begin
+        unsigned:=not(is_signed(left.resultdef)) or
+                  not(is_signed(right.resultdef));
+
+        Cmp(not unsigned,false);
+      end;
+
+
+    procedure tloongarch64addnode.second_cmp64bit;
+      var
+        unsigned: Boolean;
+      begin
+        unsigned:=not(is_signed(left.resultdef)) or
+                  not(is_signed(right.resultdef));
+
+        Cmp(not unsigned,false);
+      end;
+
+
+    procedure tloongarch64addnode.second_addordinal;
+      const
+        multops: array[boolean] of TAsmOp = (A_MULW_D_W,A_MULW_D_WU);
+      var
+        unsigned: boolean;
+      begin
+        { 32x32->64 multiplication }
+        if (nodetype=muln) and
+           is_32bit(left.resultdef) and
+           is_32bit(right.resultdef) and
+           is_64bit(resultdef) then
+          begin
+            unsigned:=not(is_signed(left.resultdef)) or
+                      not(is_signed(right.resultdef));
+            pass_left_right;
+            force_reg_left_right(true,true);
+            { force_reg_left_right can leave right as a LOC_CONSTANT (we can't
+              say "a constant register is okay, but an ordinal constant isn't) }
+            if right.location.loc=LOC_CONSTANT then
+              hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,true);
+            location_reset(location,LOC_REGISTER,def_cgsize(resultdef));
+            location.register:=cg.getintregister(current_asmdata.CurrAsmList,def_cgsize(resultdef));
+            current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(multops[unsigned],location.register,left.location.register,right.location.register));
+          end
+        else
+          inherited second_addordinal;
+      end;
+
+
+    procedure tloongarch64addnode.second_add64bit;
+      begin
+        second_addordinal;
+      end;
+
+
+    procedure tloongarch64addnode.pass_left_and_right;
+      begin
+        { calculate the operator which is more difficult }
+        firstcomplex(self);
+
+        { in case of constant put it to the left }
+        if (left.nodetype=ordconstn) then
+         swapleftright;
+
+        secondpass(left);
+        secondpass(right);
+      end;
+
+
+    procedure tloongarch64addnode.second_addfloat;
+      var
+        op    : TAsmOp;
+        cmpop,
+        singleprec: boolean;
+      begin
+        pass_left_and_right;
+        if (nf_swapped in flags) then
+          swapleftright;
+
+        hlcg.location_force_fpureg(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+        hlcg.location_force_fpureg(current_asmdata.CurrAsmList,right.location,right.resultdef,true);
+
+        cmpop:=false;
+        singleprec:=tfloatdef(left.resultdef).floattype=s32real;
+        case nodetype of
+          addn :
+            if singleprec then
+              op:=A_FADD_S
+            else
+              op:=A_FADD_D;
+          muln :
+            if singleprec then
+              op:=A_FMUL_S
+            else
+            op:=A_FMUL_D;
+          subn :
+            if singleprec then
+              op:=A_FSUB_S
+            else
+              op:=A_FSUB_D;
+          slashn :
+            if singleprec then
+              op:=A_FDIV_S
+            else
+             op:=A_FDIV_D;
+          equaln:
+            begin
+              if singleprec then
+                op:=A_FCMP_CEQ_S
+              else
+                op:=A_FCMP_CEQ_D;
+              cmpop:=true;
+            end;
+          unequaln:
+            begin
+              if singleprec then
+                op:=A_FCMP_CUNE_S
+              else
+                op:=A_FCMP_CUNE_D;
+              cmpop:=true;
+            end;
+          ltn:
+            begin
+              if singleprec then
+                op:=A_FCMP_SLT_S
+              else
+                op:=A_FCMP_SLT_D;
+              cmpop:=true;
+            end;
+          lten:
+            begin
+              if singleprec then
+                op:=A_FCMP_SLE_S
+              else
+                op:=A_FCMP_SLE_D;
+              cmpop:=true;
+            end;
+          gtn:
+            begin
+              if singleprec then
+                op:=A_FCMP_SGT_S
+              else
+                op:=A_FCMP_SGT_D;
+              cmpop:=true;
+            end;
+          gten:
+            begin
+              if singleprec then
+                op:=A_FCMP_SGE_S
+              else
+                op:=A_FCMP_SGE_D;
+              cmpop:=true;
+            end;
+          else
+            internalerror(2022111947);
+        end;
+
+        if cmpop then
+          begin
+            { TODO This should be like mips, but... }
+            { location_reset(location, LOC_FLAGS, OS_NO); }
+            { location.register := cg.getfpuregister(current_asmdata.CurrAsmList,location.size); }
+            location_reset(location,LOC_REGISTER,OS_8);
+            location.register:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
+            current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,NR_FCC0,left.location.register,right.location.register));
+            current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_MOVCF2GR,location.register,NR_FCC0));
+            cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+          end
+        else
+          begin
+            location_reset(location, LOC_FPUREGISTER, def_cgsize(resultdef));
+            location.register:=cg.getfpuregister(current_asmdata.CurrAsmList,location.size);
+            current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,location.register,left.location.register,right.location.register));
+            cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+          end;
+      end;
+
+    procedure tloongarch64addnode.second_cmpfloat;
+      begin
+        second_addfloat;
+      end;
+
+    function tloongarch64addnode.use_generic_mul32to64: boolean;
+      begin
+        result:=false;
+      end;
+
+begin
+  caddnode := tloongarch64addnode;
+end.

+ 236 - 0
compiler/loongarch64/ncpucnv.pas

@@ -0,0 +1,236 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl
+
+    Generate LoongArch64 assembler for type converting nodes
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit ncpucnv;
+
+{$i fpcdefs.inc}
+
+interface
+
+    uses
+      node,ncnv,ncgcnv;
+
+    type
+       tloongarch64typeconvnode = class(tcgtypeconvnode)
+         protected
+         { procedure second_int_to_int;override; }
+         { procedure second_string_to_string;override; }
+         { procedure second_cstring_to_pchar;override; }
+         { procedure second_string_to_chararray;override; }
+         { procedure second_array_to_pointer;override; }
+           function first_int_to_real: tnode; override;
+         { procedure second_pointer_to_array;override; }
+         { procedure second_chararray_to_string;override; }
+         { procedure second_char_to_string;override; }
+           procedure second_int_to_real;override;
+         { procedure second_real_to_real;override; }
+         { procedure second_cord_to_pointer;override; }
+         { procedure second_proc_to_procvar;override; }
+         { procedure second_bool_to_int;override; }
+           procedure second_int_to_bool;override;
+         { procedure second_load_smallset;override;  }
+         { procedure second_ansistring_to_pchar;override; }
+         { procedure second_pchar_to_string;override; }
+         { procedure second_class_to_intf;override; }
+         { procedure second_char_to_char;override; }
+       end;
+
+
+implementation
+
+   uses
+      verbose,globtype,globals,systems,
+      symconst,symdef,aasmbase,aasmtai,aasmdata,
+      defutil, symcpu,
+      cgbase,cgutils,pass_1,pass_2,
+      ncon, ncal,procinfo,
+      ncgutil,
+      cpubase,aasmcpu,
+      rgobj,tgobj,cgobj,hlcgobj;
+
+
+    {*****************************************************************************
+                                 FirstTypeConv
+    *****************************************************************************}
+
+    function tloongarch64typeconvnode.first_int_to_real: tnode;
+      var
+        fname: string[19];
+      begin
+        { converting a 64bit integer to a float requires a helper }
+        if is_64bitint(left.resultdef) or
+          is_currency(left.resultdef) then
+          begin
+            { hack to avoid double division by 10000, as it's
+              already done by typecheckpass.resultdef_int_to_real }
+            if is_currency(left.resultdef) then
+              left.resultdef := s64inttype
+            else if not is_signed(left.resultdef) then
+              begin
+                fname := 'fpc_qword_to_double';
+                result := ccallnode.createintern(fname,ccallparanode.create(left,nil));
+                left:=nil;
+                if (tfloatdef(resultdef).floattype=s32real) then
+                  inserttypeconv(result,s32floattype);
+                firstpass(result);
+                exit;
+              end;
+          end
+        else
+          begin
+            { Else signed supposed to be 32 bit, or unsigned supposed to be 64 bit }
+            if is_signed(left.resultdef) then
+              inserttypeconv(left,s32inttype)
+            else
+              inserttypeconv(left,s64inttype);
+            firstpass(left);
+          end;
+        result := nil;
+        expectloc:=LOC_FPUREGISTER;
+      end;
+
+
+    {*****************************************************************************
+                                 SecondTypeConv
+    *****************************************************************************}
+
+
+    procedure tloongarch64typeconvnode.second_int_to_real;
+      var
+        op, movop: TAsmOp;
+        restype: tfloattype;
+        hreg: tregister;
+      begin
+        location_reset(location, LOC_FPUREGISTER, def_cgsize(resultdef));
+        restype:=tfloatdef(resultdef).floattype;
+        location.Register := cg.getfpuregister(current_asmdata.CurrAsmList, tfloat2tcgsize[restype]);
+
+        if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
+          hlcg.location_force_reg(current_asmdata.CurrAsmList, left.location, left.resultdef, left.resultdef, true);
+        case left.location.size of
+          OS_32,OS_64: internalerror(2022111928);
+          OS_S32:
+            begin
+              if restype=s32real then
+                op:=A_FFINT_S_W
+              else if restype=s64real then
+                op:=A_FFINT_D_W
+              else
+                internalerror(2022111929);
+              hreg:=cg.getfpuregister(current_asmdata.CurrAsmList, OS_F32);
+              movop:=A_MOVGR2FR_W;
+            end;
+          OS_S64:
+            begin
+              if restype=s32real then
+                op:=A_FFINT_S_L
+              else if restype=s64real then
+                op:=A_FFINT_D_L
+              else
+                internalerror(2022111930);
+              hreg:= cg.getfpuregister(current_asmdata.CurrAsmList, OS_F64);
+              movop:=A_MOVGR2FR_D;
+            end;
+        else
+          internalerror(2022111931);
+        end;
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(movop, hreg, left.location.register));
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op, location.register, hreg));
+      end;
+
+
+    procedure tloongarch64typeconvnode.second_int_to_bool;
+      var
+        hreg1, hreg2: tregister;
+        opsize: tcgsize;
+        hlabel: tasmlabel;
+        newsize  : tcgsize;
+        href: treference;
+      begin
+        secondpass(left);
+        if codegenerror then
+          exit;
+
+        { Explicit typecasts from any ordinal type to a boolean type }
+        { must not change the ordinal value                          }
+        if (nf_explicit in flags) and
+           not(left.location.loc in [LOC_FLAGS,LOC_JUMP]) then
+          begin
+             location_copy(location,left.location);
+             newsize:=def_cgsize(resultdef);
+             { change of size? change sign only if location is LOC_(C)REGISTER? Then we have to sign/zero-extend }
+             if (tcgsize2size[newsize]<>tcgsize2size[left.location.size]) or
+                ((newsize<>left.location.size) and (location.loc in [LOC_REGISTER,LOC_CREGISTER])) then
+               hlcg.location_force_reg(current_asmdata.CurrAsmList,location,left.resultdef,resultdef,true)
+             else
+               location.size:=newsize;
+             exit;
+          end;
+
+        location_reset(location, LOC_REGISTER, def_cgsize(resultdef));
+        opsize := def_cgsize(left.resultdef);
+
+        if (left.location.loc in [LOC_SUBSETREG,LOC_CSUBSETREG,LOC_SUBSETREF,LOC_CSUBSETREF]) then
+          hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,true);
+
+        case left.location.loc of
+          LOC_CREFERENCE, LOC_REFERENCE, LOC_REGISTER, LOC_CREGISTER:
+            begin
+              if left.location.loc in [LOC_CREFERENCE, LOC_REFERENCE] then
+                begin
+                  hreg2 := cg.getintregister(current_asmdata.CurrAsmList, opsize);
+                  cg.a_load_ref_reg(current_asmdata.CurrAsmList, opsize, opsize, left.location.reference, hreg2);
+                end
+              else
+                begin
+                  hreg2:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
+                  cg.a_load_reg_reg(current_asmdata.CurrAsmList,opsize,opsize,left.location.register,hreg2);
+                end;
+              hreg1 := cg.getintregister(current_asmdata.CurrAsmList, opsize);
+              current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_SLTU, hreg1, NR_R0, hreg2));
+            end;
+          LOC_JUMP:
+            begin
+              hreg1 := cg.getintregister(current_asmdata.CurrAsmList, OS_INT);
+              current_asmdata.getjumplabel(hlabel);
+              cg.a_label(current_asmdata.CurrAsmList, left.location.truelabel);
+              cg.a_load_const_reg(current_asmdata.CurrAsmList, OS_INT, 1, hreg1);
+              cg.a_jmp_always(current_asmdata.CurrAsmList, hlabel);
+              cg.a_label(current_asmdata.CurrAsmList, left.location.falselabel);
+              cg.a_load_const_reg(current_asmdata.CurrAsmList, OS_INT, 0, hreg1);
+              cg.a_label(current_asmdata.CurrAsmList, hlabel);
+            end;
+          LOC_FLAGS:
+            Internalerror(2022111932);
+          else
+            internalerror(2022111933);
+        end;
+        { Now hreg1 is either 0 or 1. For C booleans it must be 0 or -1. }
+        if is_cbool(resultdef) then
+          cg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_NEG,OS_SINT,hreg1,hreg1);
+
+        location.Register := hreg1;
+      end;
+
+
+begin
+  ctypeconvnode := tloongarch64typeconvnode;
+end.

+ 203 - 0
compiler/loongarch64/ncpuinl.pas

@@ -0,0 +1,203 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl
+
+    Generate LoongArch64 inline nodes
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit ncpuinl;
+
+{$i fpcdefs.inc}
+
+interface
+
+    uses
+       cpubase,
+       node,ninl,ncginl;
+
+    type
+
+       tloongarch64inlinenode = class(tcginlinenode)
+          { first pass override
+            so that the code generator will actually generate
+            these nodes.
+          }
+          function first_sqrt_real: tnode; override;
+          function first_abs_real: tnode; override;
+          function first_sqr_real: tnode; override;
+          function first_round_real: tnode; override;
+          function first_trunc_real: tnode; override;
+
+          procedure second_sqrt_real; override;
+          procedure second_abs_real; override;
+          procedure second_sqr_real; override;
+          procedure second_round_real; override;
+          procedure second_trunc_real; override;
+       protected
+          procedure load_fpu_location;
+       end;
+
+implementation
+
+    uses
+      ncal,
+      cutils,globals,verbose,globtype,
+      aasmtai,aasmdata,aasmcpu,
+      symconst,symdef,
+      defutil,
+      cgbase,pass_2,
+      cpuinfo,ncgutil,
+      hlcgobj,cgutils,cgobj,rgobj,tgobj;
+
+
+{*****************************************************************************
+                              tloongarch64inlinenode
+*****************************************************************************}
+
+     function tloongarch64inlinenode.first_sqrt_real : tnode;
+       begin
+         expectloc:=LOC_FPUREGISTER;
+         first_sqrt_real := nil;
+       end;
+
+
+     function tloongarch64inlinenode.first_abs_real : tnode;
+       begin
+         expectloc:=LOC_FPUREGISTER;
+         first_abs_real := nil;
+       end;
+
+
+     function tloongarch64inlinenode.first_sqr_real : tnode;
+       begin
+         expectloc:=LOC_FPUREGISTER;
+         first_sqr_real := nil;
+       end;
+
+
+     function tloongarch64inlinenode.first_round_real: tnode;
+       begin
+         expectloc:=LOC_FPUREGISTER;
+         first_round_real := nil;
+       end;
+
+
+     function tloongarch64inlinenode.first_trunc_real: tnode;
+       begin
+         expectloc:=LOC_FPUREGISTER;
+         first_trunc_real := nil;
+       end;
+
+
+     { load the FPU into the an fpu register }
+     procedure tloongarch64inlinenode.load_fpu_location;
+       begin
+         location_reset(location,LOC_FPUREGISTER,def_cgsize(resultdef));
+         secondpass(left);
+         hlcg.location_force_fpureg(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+         location.loc := LOC_FPUREGISTER;
+         location.register := cg.getfpuregister(current_asmdata.CurrAsmList,def_cgsize(resultdef));
+       end;
+
+
+     procedure tloongarch64inlinenode.second_sqrt_real;
+       var
+         op: TAsmOp;
+       begin
+         location.loc:=LOC_FPUREGISTER;
+         load_fpu_location;
+         if (left.location.size = OS_F32) then
+           op := A_FSQRT_S
+         else
+           op := A_FSQRT_D;
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op,location.register,left.location.register));
+         cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+       end;
+
+
+     procedure tloongarch64inlinenode.second_abs_real;
+       var
+         op: TAsmOp;
+       begin
+         location.loc:=LOC_FPUREGISTER;
+         load_fpu_location;
+         if (left.location.size = OS_F32) then
+           op := A_FABS_S
+         else
+           op := A_FABS_D;
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op,location.register,left.location.register));
+       end;
+
+
+     procedure tloongarch64inlinenode.second_sqr_real;
+       var
+         op: tasmop;
+       begin
+         location.loc:=LOC_FPUREGISTER;
+         load_fpu_location;
+         if (left.location.size = OS_F32) then
+           op := A_FMUL_S
+         else
+           op := A_FMUL_D;
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,location.register,left.location.register,left.location.register));
+         cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+       end;
+
+
+     procedure tloongarch64inlinenode.second_round_real;
+       var
+         op: TAsmOp;
+         hreg: tregister;
+       begin
+         secondpass(left);
+         hlcg.location_force_fpureg(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+         location_reset(location,LOC_REGISTER,def_cgsize(resultdef));
+         location.register:=cg.getintregister(current_asmdata.CurrAsmList,location.size);
+         hreg:= cg.getfpuregister(current_asmdata.CurrAsmList, OS_F64);
+         if (left.location.size = OS_F32) then
+           op := A_FTINT_L_S
+         else
+           op := A_FTINT_L_D;
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op,hreg,left.location.register));
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_MOVFR2GR_D,location.register,hreg));
+         cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+       end;
+
+
+     procedure tloongarch64inlinenode.second_trunc_real;
+       var
+         op,movop: TAsmOp;
+         hreg: tregister;
+       begin
+         secondpass(left);
+         hlcg.location_force_fpureg(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+         location_reset(location,LOC_REGISTER,def_cgsize(resultdef));
+         location.register:=cg.getintregister(current_asmdata.CurrAsmList,location.size);
+         hreg:= cg.getfpuregister(current_asmdata.CurrAsmList, OS_F64);
+         if (left.location.size = OS_F32) then
+           op := A_FTINTRZ_L_S
+         else
+           op := A_FTINTRZ_L_D;
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op,hreg,left.location.register));
+         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_MOVFR2GR_D,location.register,hreg));
+         cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+       end;
+
+
+begin
+   cinlinenode:=tloongarch64inlinenode;
+end.

+ 152 - 0
compiler/loongarch64/ncpumat.pas

@@ -0,0 +1,152 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl
+
+    Generate LoongArch64 assembler for math nodes
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit ncpumat;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      node,nmat, ncgmat,
+      cgbase;
+
+    type
+      tloongarch64moddivnode = class(tcgmoddivnode)
+        function use_moddiv64bitint_helper: boolean; override;
+        procedure emit_div_reg_reg(signed: boolean; denum, num: tregister); override;
+        procedure emit_mod_reg_reg(signed: boolean; denum, num: tregister); override;
+        function first_moddiv64bitint: tnode; override;
+      end;
+
+      tloongarch64shlshrnode = class(tcgshlshrnode)
+      end;
+
+      tloongarch64unaryminusnode = class(tcgunaryminusnode)
+      end;
+
+      tloongarch64notnode = class(tcgnotnode)
+        procedure second_boolean; override;
+      end;
+
+implementation
+
+    uses
+      nadd,ninl,ncal,ncnv,
+      globtype,systems,constexp,
+      cutils,verbose,globals,
+      cpuinfo,
+      symconst,symdef,
+      aasmbase,aasmcpu,aasmtai,aasmdata,
+      defutil,
+      cgutils,cgobj,hlcgobj,
+      pass_1,pass_2,htypechk,
+      ncon,procinfo,
+      cpubase,
+      ncgutil,cgcpu;
+
+    procedure tloongarch64notnode.second_boolean;
+      var
+        tlabel, flabel: tasmlabel;
+      begin
+        secondpass(left);
+        if not handle_locjump then
+          begin
+            case left.location.loc of
+              LOC_FLAGS :
+                begin
+                  Internalerror(2022111907);
+                end;
+              LOC_REGISTER, LOC_CREGISTER,
+              LOC_REFERENCE, LOC_CREFERENCE,
+              LOC_SUBSETREG, LOC_CSUBSETREG,
+              LOC_SUBSETREF, LOC_CSUBSETREF:
+                begin
+                  hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
+
+                  location_reset(location,LOC_REGISTER,OS_INT);
+                  location.register:=hlcg.getintregister(current_asmdata.CurrAsmList,s64inttype);
+
+                  current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_const(A_SLTUI,location.register,left.location.register,1));
+               end;
+              else
+                internalerror(2022111906);
+            end;
+          end;
+      end;
+
+
+    function tloongarch64moddivnode.use_moddiv64bitint_helper: boolean;
+      begin
+        Result:=true;
+      end;
+
+
+    procedure tloongarch64moddivnode.emit_div_reg_reg(signed: boolean; denum, num: tregister);
+      var
+        op: TAsmOp;
+      begin
+        if signed then
+          op:=A_DIV_D
+        else
+          op:=A_DIV_DU;
+
+        current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,num,num,denum));
+      end;
+
+
+    procedure tloongarch64moddivnode.emit_mod_reg_reg(signed: boolean; denum, num: tregister);
+      var
+        op: TAsmOp;
+      begin
+        if signed then
+          op:=A_MOD_D
+        else
+          op:=A_MOD_DU;
+
+        current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(op,num,num,denum));
+      end;
+
+
+    function tloongarch64moddivnode.first_moddiv64bitint: tnode;
+      begin
+        {We can handle all cases of constant division}
+        if not(cs_check_overflow in current_settings.localswitches) and
+           (right.nodetype=ordconstn) and
+           (nodetype=divn) then
+          result:=nil
+        else if (nodetype in [divn,modn]) then
+          result:=nil
+        else
+          result:=inherited;
+
+        { we may not change the result type here }
+        if assigned(result) and (torddef(result.resultdef).ordtype<>torddef(resultdef).ordtype) then
+          inserttypeconv(result,resultdef);
+      end;
+
+begin
+  cmoddivnode := tloongarch64moddivnode;
+  cshlshrnode := tloongarch64shlshrnode;
+  cunaryminusnode := tloongarch64unaryminusnode;
+  cnotnode := tloongarch64notnode;
+end.
+

+ 144 - 0
compiler/loongarch64/ncpuset.pas

@@ -0,0 +1,144 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl and Carl Eric Codere
+
+    Generate Risc-V32/64 assembler for in set/case nodes
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit ncpuset;
+
+{$i fpcdefs.inc}
+
+interface
+
+    uses
+       node,nset,ncgset,cpubase,cgbase,cgobj,aasmbase,aasmtai,aasmdata,globtype;
+
+    type
+       tloongarch64casenode = class(tcgcasenode)
+         protected
+           procedure optimizevalues(var max_linear_list : int64; var max_dist : qword);override;
+           function  has_jumptable : boolean;override;
+           procedure genjumptable(hp : pcaselabel;min_,max_ : int64);override;
+       end;
+
+
+implementation
+
+    uses
+      systems,
+      verbose,globals,constexp,
+      symconst,symdef,defutil,
+      paramgr,
+      cpuinfo,
+      pass_2,cgcpu,
+      ncon,
+      tgobj,ncgutil,rgobj,aasmcpu,
+      procinfo,
+      cgutils;
+
+{*****************************************************************************
+                            TCGCASENODE
+*****************************************************************************}
+
+
+    procedure tloongarch64casenode.optimizevalues(var max_linear_list : int64; var max_dist : qword);
+      begin
+        max_linear_list := 3;
+      end;
+
+
+    function tloongarch64casenode.has_jumptable : boolean;
+      begin
+        has_jumptable:=true;
+      end;
+
+
+    procedure tloongarch64casenode.genjumptable(hp : pcaselabel;min_,max_ : int64);
+      var
+        table : tasmlabel;
+        last : TConstExprInt;
+        indexreg : tregister;
+        href : treference;
+
+        procedure genitem(list:TAsmList;t : pcaselabel);
+          var
+            i : TConstExprInt;
+          begin
+            if assigned(t^.less) then
+              genitem(list,t^.less);
+            { fill possible hole }
+            i:=last+1;
+            while i<=t^._low-1 do
+              begin
+                list.concat(Tai_const.Create_sym_offset(elselabel,0));
+                i:=i+1;
+              end;
+            i:=t^._low;
+            while i<=t^._high do
+              begin
+                list.concat(Tai_const.Create_sym_offset(blocklabel(t^.blockid),0));
+                i:=i+1;
+              end;
+            last:=t^._high;
+            if assigned(t^.greater) then
+              genitem(list,t^.greater);
+          end;
+
+      begin
+        last:=min_;
+
+        {
+          la.pcrel x,tbl
+          alsl.d y,idx,x,3
+          ld.d z,y,0
+          jr z
+        }
+
+        indexreg:= cg.makeregsize(current_asmdata.CurrAsmList, hregister, OS_INT);
+        { indexreg := hregister; }
+        cg.a_load_reg_reg(current_asmdata.CurrAsmList, def_cgsize(opsize), OS_INT, hregister, indexreg);
+        { a <= x <= b <-> unsigned(x-a) <= (b-a) }
+        cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_SUB,OS_INT,aint(min_),indexreg);
+        if not(jumptable_no_range) then
+          cg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_A,aint(max_)-aint(min_),indexreg,elselabel);
+        current_asmdata.getjumplabel(table);
+        hregister:=cg.getaddressregister(current_asmdata.CurrAsmList);
+        { la.pcrel x,tbl }
+        reference_reset_symbol(href, table, 0, 4,[]);
+        href.refaddr:=addr_pcrel;
+        cg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,href,hregister);
+        { alsl.d y,idx,x,3 }
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg_const(A_ALSL_D,hregister,indexreg,hregister,3));
+        { ld.d z,y,0 }
+        reference_reset_base(href,hregister,0,ctempposinvalid,4,[]);
+        cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,href,hregister);
+        { jr z }
+        reference_reset_base(href,hregister,0,ctempposinvalid,4,[]);
+        href.refaddr:=addr_reg;
+        current_asmdata.CurrAsmList.concat(taicpu.op_ref(A_JR,href));
+
+        { generate jump table }
+        current_asmdata.CurrAsmList.concat(cai_align.Create(8));
+        current_asmdata.CurrAsmList.concat(Tai_label.Create(table));
+        genitem(current_asmdata.CurrAsmList,hp);
+      end;
+
+
+begin
+   ccasenode:=tloongarch64casenode;
+end.

+ 48 - 0
compiler/loongarch64/racpu.pas

@@ -0,0 +1,48 @@
+{
+    Copyright (c) 1998-2003 by Carl Eric Codere and Peter Vreman
+
+    Handles the common LoongArch64 assembler reader routines
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit racpu;
+
+{$I fpcdefs.inc}
+
+interface
+
+uses
+  aasmbase, aasmtai,aasmdata, aasmcpu,
+  cpubase, rautils, cclasses;
+
+type
+  TLoongArch64Operand = class(TOperand)
+  end;
+
+  TLoongArch64Instruction = class(TInstruction)
+    function ConcatInstruction(p: TAsmList): tai; override;
+  end;
+
+implementation
+
+  function TLoongArch64Instruction.ConcatInstruction(p: TAsmList): tai;
+    begin
+      Result:=inherited ConcatInstruction(p);
+    end;
+
+end.
+

+ 811 - 0
compiler/loongarch64/racpugas.pas

@@ -0,0 +1,811 @@
+{
+    Copyright (c) 2019 by Jeppe Johansen
+
+    Does the parsing for the LoongArch64 GNU AS styled inline assembler.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit racpugas;
+
+{$I fpcdefs.inc}
+
+  interface
+
+    uses
+      globtype,
+      raatt,racpu,
+      cpubase;
+
+    const
+      NRCalMax=64;
+
+    type
+      TArithOpers = (LLOR,LLAND,LINOR,LEXOR,LAND,LEQU,LUNE,LSLT,
+        LBGT,LSLE,LBGE,LSL,LSR,LADD,LSUB,LMUL,LMOD,LDIV);
+      TArithTreeType = (ATT_NUM,ATT_OP);
+      TArithTree = record
+        left : integer;
+        right : integer;
+        case typ:TArithTreeType of
+          ATT_NUM : (num : tcgint);
+          ATT_OP : (op : TArithOpers);
+      end;
+      TArrCals = array[1..NRCalMax] of TArithTree;
+      TArrNums = array[1..(NRCalMax shr 1)] of tcgint;
+      TArrAOps = array[1..(NRCalMax shr 1)] of TArithOpers;
+
+      tloongarch64gasreader = class(tattreader)
+        function is_register(const s: string):boolean;override;
+        function is_asmopcode(const s: string):boolean;override;
+        procedure handledollar;override;
+        procedure handleopcode;override;
+        procedure BuildOperand(oper : tloongarch64operand);
+        procedure BuildOpCode(instr : tloongarch64instruction);
+        function CalculateExprs(nr,first : tcgint; var nums : TArrNums; var ops : TArrAOps): tcgint;
+        function BuildConstLA(from_question,cond : boolean): tcgint;
+        function BuildSymLA(oper : tloongarch64operand; maybeconst: boolean): boolean;
+      end;
+
+  implementation
+
+    uses
+      { helpers }
+      cutils,
+      { global }
+      globals,verbose,
+      systems,
+      { aasm }
+      aasmbase,aasmtai,aasmdata,aasmcpu,
+      { symtable }
+      symconst,symsym,symdef,
+      { parser }
+      scanner,
+      procinfo,
+      rabase,rautils,
+      cgbase,cgobj
+      ;
+
+
+    procedure tloongarch64gasreader.BuildOperand(oper: tloongarch64operand);
+      var
+        expr : string;
+        typesize,l : TCGInt;
+        tempreg : tregister;
+        hl : tasmlabel;
+        ofs : aint;
+        refaddr: trefaddr;
+        entered_paren: Boolean;
+      Begin
+        expr:='';
+        entered_paren:=false;
+        refaddr:=addr_no;
+        { Although assembler has diverse ways to decode parameters,
+          there are four normal ways to describe them.
+          1. Registers.
+          2. %addrtype(Symbol'addend'), addend means expression.
+          3. Symbol'addend'.
+          4. Else, expression. }
+        case actasmtoken of
+          AS_REGISTER:
+            begin
+              { save the type of register used. }
+              tempreg:=actasmregister;
+              Consume(AS_REGISTER);
+              if (actasmtoken in [AS_END,AS_SEPARATOR,AS_COMMA]) then
+                begin
+                  oper.opr.typ:=OPR_REGISTER;
+                  oper.opr.reg:=tempreg;
+                end
+              else
+                Message(asmr_e_syn_operand);
+            end;
+          AS_MOD:
+            begin
+              Consume(AS_MOD);
+              if actasmtoken<>AS_ID then
+                Message(asmr_e_syntax_error);
+              if lower(actasmpattern)='b16' then
+                refaddr:=addr_b16
+              else if lower(actasmpattern)='b21' then
+                refaddr:=addr_b21
+              else if lower(actasmpattern)='b26' then
+                refaddr:=addr_b26
+              else if lower(actasmpattern)='plt' then
+                refaddr:=addr_plt
+              else if lower(actasmpattern)='abs_hi20' then
+                refaddr:=addr_abs_hi20
+              else if lower(actasmpattern)='abs_lo12' then
+                refaddr:=addr_abs_lo12
+              else if lower(actasmpattern)='abs64_lo20' then
+                refaddr:=addr_abs64_lo20
+              else if lower(actasmpattern)='abs64_hi12' then
+                refaddr:=addr_abs64_hi12
+              else if lower(actasmpattern)='pc_hi20' then
+                refaddr:=addr_pc_hi20
+              else if lower(actasmpattern)='pc_lo12' then
+                refaddr:=addr_pc_lo12
+              else if lower(actasmpattern)='got_pc_hi20' then
+                refaddr:=addr_got_pc_hi20
+              else if lower(actasmpattern)='got_pc_lo12' then
+                refaddr:=addr_got_pc_lo12
+              else
+                Message(asmr_e_syntax_error);
+              Consume(AS_ID);
+              if actasmtoken<>AS_LPAREN then
+                Message(asmr_e_syntax_error);
+              Consume(AS_LPAREN);
+              BuildSymLA(oper,false);
+              if actasmtoken<>AS_RPAREN then
+                Message(asmr_e_syntax_error);
+              Consume(AS_RPAREN);
+            end;
+          AS_DOT, AS_ID:
+            begin
+              if actopcode=A_LA_GOT then
+                refaddr:=addr_pcrel
+              else if actopcode=A_LA_ABS then
+                refaddr:=addr_abs
+              else
+                refaddr:=addr_pcrel;
+              if not BuildSymLA(oper,actasmtoken=AS_ID) then
+                refaddr:=addr_no;
+            end;
+          AS_END,
+          AS_SEPARATOR,
+          AS_COMMA: ;
+        else
+          begin
+            oper.opr.typ:=OPR_CONSTANT;
+            oper.opr.val:=BuildConstLA(false,false);
+          end;
+        end; { end case }
+
+      if refaddr<>addr_no then
+        begin
+          { Indirectly use parameter can be local and sym is paravarsym. }
+          if oper.opr.typ=OPR_LOCAL then
+              exit;
+          if oper.opr.typ<>OPR_REFERENCE then
+            oper.InitRef;
+          oper.opr.ref.refaddr:=refaddr;
+        end
+      else if (oper.opr.typ=OPR_REFERENCE) and
+              (oper.opr.ref.refaddr=addr_no) and
+              assigned(oper.opr.ref.symbol) then
+        oper.opr.ref.refaddr:=addr_pcrel;
+
+      end;
+
+
+{*****************************************************************************
+                                tloongarch64gasreader
+*****************************************************************************}
+
+    procedure tloongarch64gasreader.BuildOpCode(instr : tloongarch64instruction);
+      var
+        operandnum : longint;
+      begin
+        { opcode }
+        if (actasmtoken<>AS_OPCODE) then
+         begin
+           Message(asmr_e_invalid_or_missing_opcode);
+           RecoverConsume(true);
+           exit;
+         end;
+        { Fill the instr object with the current state }
+        with instr do
+          begin
+            Opcode:=ActOpcode;
+            condition:=ActCondition;
+          end;
+
+        { We are reading operands, so opcode will be an AS_ID }
+        operandnum:=1;
+        Consume(AS_OPCODE);
+        { Zero operand opcode ?  }
+        if actasmtoken in [AS_SEPARATOR,AS_END] then
+         begin
+           operandnum:=0;
+           exit;
+         end;
+        { Read the operands }
+        repeat
+          case actasmtoken of
+            AS_COMMA: { Operand delimiter }
+              begin
+                if operandnum>Max_Operands then
+                  Message(asmr_e_too_many_operands)
+                else
+                  begin
+                    { condition operands doesn't set the operand but write to the
+                      condition field of the instruction
+                    }
+                    if instr.Operands[operandnum].opr.typ<>OPR_NONE then
+                      Inc(operandnum);
+                  end;
+                Consume(AS_COMMA);
+              end;
+            AS_SEPARATOR,
+            AS_END : { End of asm operands for this opcode  }
+              begin
+                break;
+              end;
+          else
+            BuildOperand(instr.Operands[operandnum] as tloongarch64operand);
+          end; { end case }
+        until false;
+        if (operandnum=1) and (instr.Operands[operandnum].opr.typ=OPR_NONE) then
+          dec(operandnum);
+        instr.Ops:=operandnum;
+      end;
+
+
+    function tloongarch64gasreader.is_register(const s: string): boolean;
+      var
+        reg: TRegister;
+      begin
+        result:=inherited is_register(s);
+        { reg found? search it in abinames?  }
+        if not(result) then
+          begin
+            reg:=is_extra_reg(s);
+            if reg<>NR_NO then
+              begin
+                actasmregister:=reg;
+                result:=true;
+                actasmtoken:=AS_REGISTER;
+              end;
+          end;
+      end;
+
+
+    function tloongarch64gasreader.is_asmopcode(const s: string):boolean;
+      var
+        cond  : tasmcond;
+        hs, postfix : string;
+        l: longint;
+      begin
+        { making s a value parameter would break other assembler readers }
+        hs:=s;
+        is_asmopcode:=false;
+
+        { clear op code }
+        actopcode:=A_None;
+        { clear condition }
+        fillchar(actcondition,sizeof(actcondition),0);
+
+        { check for direction hint }
+        actopcode := tasmop(ptruint(iasmops.find(hs)));
+        if actopcode <> A_NONE then
+          begin
+            actasmtoken:=AS_OPCODE;
+            is_asmopcode:=true;
+            exit;
+          end;
+      end;
+
+
+    procedure tloongarch64gasreader.handledollar;
+      var
+        len: longint;
+      begin
+        len:=1;
+        actasmpattern[len]:='$';
+        c:=current_scanner.asmgetchar;
+        while c in ['A'..'Z','a'..'z','0'..'9'] do
+          begin
+            inc(len);
+            actasmpattern[len]:=c;
+            c:=current_scanner.asmgetchar;
+          end;
+        actasmpattern[0]:=chr(len);
+        actasmpattern:=lower(actasmpattern);
+        { TODO Something else. }
+        if not is_register(actasmpattern) then
+          internalerror(2022062915);
+      end;
+
+
+    procedure tloongarch64gasreader.handleopcode;
+      var
+        instr : tloongarch64instruction;
+      begin
+        instr:=tloongarch64instruction.create(tloongarch64operand);
+        BuildOpCode(instr);
+        { TODO insruction field }
+        instr.ConcatInstruction(curlist);
+        instr.Free;
+      end;
+
+
+    { In LoongArch binutils gas, the expression calculation is complex.
+      The priority is ternary is the lowest and the unary is the highest.
+      Ternary '?:', binary ordered by form low to high is '||', '&&',
+      '|', '^', '&', '==,!=', '<,<=,>,>=', '<<,>>', '+,-' and '*,/,%',
+      Unary is '+,-,~,!'. It is different form CalculateExpression, so
+      we should implement ourselves. Collect integers and binary,
+      calculate parentheses by recursing call, mark ternay and calculate
+      unary in time. }
+    function tloongarch64gasreader.CalculateExprs(nr,first : tcgint; var nums : TArrNums; var ops : TArrAOps): tcgint;
+
+      procedure alloc_num(var idx : integer; var arr : TArrCals; num : int64);
+        begin
+          idx:=idx+1;
+          if idx>NRCalMax then
+            internalerror(2022081601);
+          arr[idx].typ:=ATT_NUM;
+          arr[idx].num:=num;
+        end;
+
+      procedure alloc_op(var idx : integer; var arr : TArrCals; op : TArithOpers);
+        begin
+          idx:=idx+1;
+          if idx>NRCalMax then
+            internalerror(2022081602);
+          arr[idx].typ:=ATT_OP;
+          arr[idx].op:=op;
+        end;
+
+      function priority_less(op1, op2 : TArithOpers): boolean;
+        begin
+          case op1 of
+            LMUL,LMOD,LDIV:
+              priority_less:=false;
+            LADD,LSUB:
+              priority_less:=op2 in [LMUL..LDIV];
+            LSL,LSR:
+              priority_less:=op2 in [LADD..LDIV];
+            LSLT,LBGT,LSLE,LBGE:
+              priority_less:=op2 in [LSL..LDIV];
+            LEQU,LUNE:
+              priority_less:=op2 in [LSLT..LDIV];
+            LAND:
+              priority_less:=op2 in [LEQU..LDIV];
+            LEXOR:
+              priority_less:=op2 in [LAND..LDIV];
+            LINOR:
+              priority_less:=op2 in [LEXOR..LDIV];
+            LLAND:
+              priority_less:=op2 in [LINOR..LDIV];
+            LLOR:
+              priority_less:=op2<>LLOR;
+          end;
+        end;
+
+      function get_where_insert(head,item : integer; var arr : TArrCals) : integer;
+        var
+          last,t : integer;
+        begin
+          last:=0;
+          t:=head;
+          while arr[t].typ=ATT_OP do
+            begin
+              if priority_less(arr[item].op,arr[t].op) then
+                break;
+              last:=t;
+              t:=arr[t].right;
+            end;
+          get_where_insert:=last;
+        end;
+
+      function arith_treecal(var arr : TArrCals; idx : integer): tcgint;
+        var
+          lv,rv : tcgint;
+        begin
+          if arr[idx].typ=ATT_NUM then
+            begin
+              result:=arr[idx].num;
+              exit;
+            end;
+          if (arr[idx].left=0) and (arr[idx].right=0) then
+            internalerror(2022081705);
+          lv:=arith_treecal(arr,arr[idx].left);
+          rv:=arith_treecal(arr,arr[idx].right);
+          case arr[idx].op of
+            LLOR: result:=tcgint((lv<>0) or (rv<>0));
+            LLAND: result:=tcgint((lv<>0) and (rv<>0));
+            LINOR: result:=lv or rv;
+            LEXOR: result:=lv xor rv;
+            LAND: result:=lv and rv;
+            LEQU: result:=tcgint(lv=rv);
+            LUNE: result:=tcgint(lv<>rv);
+            LSLT: result:=tcgint(lv<rv);
+            LBGT: result:=tcgint(lv>rv);
+            LSLE: result:=tcgint(lv<=rv);
+            LBGE: result:=tcgint(lv>=rv);
+            LSL: result:=lv<<rv;
+            LSR: result:=lv>>rv;
+            LADD: result:=lv+rv;
+            LSUB: result:=lv-rv;
+            LMUL: result:=lv*rv;
+            LMOD: result:=lv mod rv;
+            LDIV: result:=lv div rv;
+          end;
+        end;
+
+      procedure debug_treecal(var arr : TArrCals; idx : integer; first : boolean);
+        const
+          strops: Array[LLOR..LDIV] of string[3] =
+            ('||','&&','|','^','&','==','!=','<','>',
+             '<=','>=','<<','>>','+','-','*','%','/');
+        begin
+          if first then
+            writeln('[Debug] ');
+          if arr[idx].typ=ATT_NUM then
+            begin
+              write(arr[idx].num);
+              exit;
+            end;
+          if (arr[idx].left=0) and (arr[idx].right=0) then
+            writeln(#10,'[Debug Error]');
+          debug_treecal(arr,arr[idx].left,false);
+          if arr[idx].typ=ATT_OP then
+            write(strops[arr[idx].op]);
+          debug_treecal(arr,arr[idx].right,false);
+          if first then
+            writeln('=',arith_treecal(arr,idx));
+        end;
+
+      var
+        i,curidx,curhead,curright,cursym,insidx : integer;
+        exprs : TArrCals;
+      begin
+        if nr=0 then
+          internalerror(2022081704);
+        curidx:=0;
+        alloc_num(curidx,exprs,first);
+        curhead:=curidx;
+        cursym:=0;
+        for i := 1  to nr do
+          begin
+            alloc_op(curidx,exprs,ops[i]);
+            cursym:=curidx;
+            alloc_num(curidx,exprs,nums[i]);
+            curright:=curidx;
+            exprs[cursym].right:=curright;
+            insidx:=get_where_insert(curhead,cursym,exprs);
+            if insidx=0 then
+              begin
+                exprs[cursym].left:=curhead;
+                curhead:=cursym;
+              end
+            else
+              begin
+                exprs[cursym].left:=exprs[insidx].right;
+                exprs[insidx].right:=cursym;
+              end;
+          end;
+        { debug_treecal(exprs,curhead,true); }
+        result:=arith_treecal(exprs,curhead);
+      end;
+
+    function tloongarch64gasreader.BuildConstLA(from_question,cond : boolean): tcgint;
+
+      function get_a_int: tcgint;
+        var
+          l : tcgint;
+        begin
+          result:=0;
+          case actasmtoken of
+            AS_PLUS:
+              begin
+                Consume(AS_PLUS);
+                result:=get_a_int();
+              end;
+            AS_MINUS:
+              begin
+                Consume(AS_MINUS);
+                result:=-get_a_int();
+              end;
+            AS_NOT:
+              begin
+                Consume(AS_NOT);
+                result:=tcgint(get_a_int()=0);
+              end;
+            AS_NOR:
+              begin
+                Consume(AS_NOR);
+                result:=not get_a_int();
+              end;
+            AS_ID:
+              begin
+                if SearchIConstant(actasmpattern,l) then
+                  result:=l
+                else
+                  internalerror(2022081101);
+                Consume(AS_ID);
+              end;
+            AS_INTNUM:
+              begin
+                result:=CalculateExpression(actasmpattern);
+                Consume(AS_INTNUM);
+              end;
+            AS_LPAREN:
+              begin
+                Consume(AS_LPAREN);
+                l:=BuildConstLA(false,false);
+                if actasmtoken<>AS_RPAREN then
+                  Message(asmr_e_syntax_error);
+                Consume(AS_RPAREN);
+                result:=l;
+              end;
+          else
+            Message(asmr_e_syntax_error);
+          end;
+        end;
+
+      function get_a_op: TArithOpers;
+        begin
+          case actasmtoken of
+            AS_SHL:
+              begin
+                Consume(AS_SHL);
+                result:=LSL;
+              end;
+            AS_SHR:
+              begin
+                Consume(AS_SHR);
+                result:=LSR;
+              end;
+            AS_LT:
+              begin
+                Consume(AS_LT);
+                if actasmtoken=AS_EQUAL then
+                  begin
+                    Consume(AS_EQUAL);
+                    result:=LSLE;
+                  end
+                else
+                  result:=LSLT;
+              end;
+            AS_GT:
+              begin
+                Consume(AS_GT);
+                if actasmtoken=AS_EQUAL then
+                  begin
+                    Consume(AS_EQUAL);
+                    result:=LBGE;
+                  end
+                else
+                  result:=LBGT;
+              end;
+            AS_OR:
+              begin
+                Consume(AS_OR);
+                if actasmtoken=AS_OR then
+                  begin
+                    Consume(AS_OR);
+                    result:=LLOR;
+                  end
+                else
+                  result:=LINOR;
+              end;
+            AS_AND:
+              begin
+                Consume(AS_AND);
+                if actasmtoken=AS_AND then
+                  begin
+                    Consume(AS_AND);
+                    result:=LLAND;
+                  end
+                else
+                  result:=LAND;
+              end;
+            AS_EQUAL:
+              begin
+                Consume(AS_EQUAL);
+                if actasmtoken<>AS_EQUAL then
+                  internalerror(2022081701);
+                Consume(AS_EQUAL);
+                result:=LEQU;
+              end;
+            AS_NOT:
+              begin
+                Consume(AS_NOT);
+                if actasmtoken<>AS_EQUAL then
+                  internalerror(2022081702);
+                Consume(AS_EQUAL);
+                result:=LUNE;
+              end;
+            AS_XOR:
+              begin
+                Consume(AS_XOR);
+                result:=LEXOR;
+              end;
+            AS_PLUS:
+              begin
+                Consume(AS_PLUS);
+                result:=LADD;
+              end;
+            AS_MINUS:
+              begin
+                Consume(AS_MINUS);
+                result:=LSUB;
+              end;
+            AS_STAR:
+              begin
+                Consume(AS_STAR);
+                result:=LMUL;
+              end;
+            AS_MOD:
+              begin
+                Consume(AS_MOD);
+                result:=LMOD;
+              end;
+            AS_SLASH:
+              begin
+                Consume(AS_SLASH);
+                result:=LDIV;
+              end;
+          else
+            Message(asmr_e_syntax_error);
+          end;
+        end;
+
+      var
+        firstnum,nr,l,l2 : tcgint;
+        IntStack : TArrNums;
+        OpsStack : TArrAOps;
+        op_or_int: boolean;
+      begin
+        result:=0;
+        nr:=0;
+
+        firstnum:=get_a_int;
+        { Most of case will return as they only have an integer. }
+
+        repeat
+          case actasmtoken of
+            AS_END,AS_SEPARATOR,AS_COMMA,AS_RPAREN: break;
+            AS_LPAREN:
+              begin
+                Consume(AS_LPAREN);
+                l:=BuildConstLA(false,false);
+                if actasmtoken<>AS_RPAREN then
+                  Message(asmr_e_syntax_error);
+                Consume(AS_RPAREN);
+              end;
+            AS_QUESTION:
+              begin
+                Consume(AS_QUESTION);
+                if nr=0 then
+                  l:=firstnum
+                else
+                  l:=CalculateExprs(nr,firstnum,IntStack,OpsStack);
+                result:=BuildConstLA(true,l<>0);
+                exit;
+              end;
+            AS_COLON:
+              begin
+                if not from_question then
+                  Message(asmr_e_syntax_error);
+                Consume(AS_COLON);
+                if nr=0 then
+                  l:=firstnum
+                else
+                  l:=CalculateExprs(nr,firstnum,IntStack,OpsStack);
+                l2:=BuildConstLA(false,false);
+                if cond then
+                  result:=l
+                else
+                  result:=l2;
+                exit;
+              end;
+          else
+            begin
+              nr:=nr+1;
+              if nr>(NRCalMax shr 1) then
+                internalerror(2022081703);
+              OpsStack[nr]:=get_a_op;
+              IntStack[nr]:=get_a_int;
+            end;
+          end; { case actasmtoken }
+        until false;
+
+        if nr=0 then
+          result:=firstnum
+        else
+          result:=CalculateExprs(nr,firstnum,IntStack,OpsStack);
+      end;
+
+
+    function tloongarch64gasreader.BuildSymLA(oper : tloongarch64operand; maybeconst : boolean): boolean;
+      var
+        hl : tasmlabel;
+        value : tcgint;
+        toffset, tsize: tcgint;
+        expr,mangledname: string;
+      begin
+        result:=True;
+        if (is_locallabel(actasmpattern)) then
+          begin
+            CreateLocalLabel(actasmpattern,hl,false);
+            Consume(AS_ID);
+            oper.InitRef;
+            oper.opr.ref.symbol:=hl;
+          end
+        else if SearchLabel(actasmpattern,hl,false) then
+          begin
+            Consume(AS_ID);
+            oper.InitRef;
+            oper.opr.ref.symbol:=hl;
+          end
+        else if maybeconst then
+          begin
+            if not SearchIConstant(actasmpattern,value) then
+              begin
+                expr:=actasmpattern;
+                Consume(AS_ID);
+                if actasmtoken = AS_DOT then
+                  begin
+                    mangledname:='';
+                    BuildRecordOffsetSize(expr, toffset, tsize, mangledname, false);
+                    if mangledname <> '' then
+                      Message(asmr_e_wrong_sym_type);
+                    oper.opr.typ:=OPR_CONSTANT;
+                    oper.opr.val:=toffset;
+                    Result:=False;
+                  end
+                else
+                  begin
+                    oper.InitRef;
+                    oper.SetupVar(expr,false);
+                  end;
+              end;
+          end
+        else if actasmtoken=AS_ID then
+          begin
+            oper.InitRef;
+            oper.SetupVar(actasmpattern,false);
+            Consume(AS_ID);
+          end
+        else
+          begin
+            Message(asmr_e_syntax_error);
+            internalerror(2022082501);
+          end;
+
+        if actasmtoken in [AS_END,AS_SEPARATOR,AS_COMMA,AS_RPAREN] then
+          exit;
+
+        value:=BuildConstLA(false,false);
+        if assigned(oper.opr.ref.symbol) then
+          oper.opr.ref.offset:=value
+        else
+          begin
+            oper.opr.typ:=OPR_CONSTANT;
+            oper.opr.val:=value;
+            Result:=False;
+          end;
+      end;
+
+
+{*****************************************************************************
+                                     Initialize
+*****************************************************************************}
+
+    const
+      asmmode_loongarch64_standard_info : tasmmodeinfo =
+              (
+                id    : asmmode_standard;
+                idtxt : 'STANDARD';
+                casmreader : tloongarch64gasreader;
+              );
+
+initialization
+  RegisterAsmMode(asmmode_loongarch64_standard_info);
+end.

+ 114 - 0
compiler/loongarch64/rgcpu.pas

@@ -0,0 +1,114 @@
+{
+    Copyright (c) 1998-2002 by Florian Klaempfl
+
+    This unit implements the LoongArch64 specific class for the register
+    allocator
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+unit rgcpu;
+
+{$i fpcdefs.inc}
+
+  interface
+
+     uses
+       aasmbase,aasmtai,aasmdata,aasmcpu,
+       cgbase,cgutils,
+       cpubase,
+       rgobj;
+
+     type
+       trgcpu = class(trgobj)
+         procedure do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override;
+         procedure do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override;
+       end;
+
+       trgintcpu = class(trgcpu)
+       end;
+
+  implementation
+
+    uses
+      verbose, cutils,globtype,
+      cgobj,
+      procinfo;
+
+
+    procedure trgcpu.do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister);
+      var
+        tmpref : treference;
+        helplist : TAsmList;
+        hreg : tregister;
+        helpins: Taicpu;
+      begin
+        if not is_simm12(spilltemp.offset) then
+          begin
+            helplist:=tasmlist.create;
+
+            if getregtype(tempreg)=R_INTREGISTER then
+              hreg:=tempreg
+            else
+              hreg:=cg.getintregister(helplist,OS_ADDR);
+
+            helplist.concat(taicpu.op_reg_const(A_LI_D,hreg,spilltemp.offset));
+            helplist.concat(taicpu.op_reg_reg_reg(A_ADD_D,hreg,hreg,spilltemp.base));
+
+            reference_reset_base(tmpref,hreg,0,ctempposinvalid,sizeof(aint),[]);
+            helpins:=spilling_create_load(tmpref,tempreg);
+            helplist.concat(helpins);
+            list.insertlistafter(pos,helplist);
+            helplist.free;
+          end
+        else
+          inherited;
+      end;
+
+
+    procedure trgcpu.do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister);
+      var
+        tmpref   : treference;
+        helplist : tasmlist;
+        hreg     : tregister;
+      begin
+        if not is_simm12(spilltemp.offset) then
+          begin
+            helplist:=tasmlist.create;
+
+            if getregtype(tempreg)=R_INTREGISTER then
+              hreg:=getregisterinline(helplist,[R_SUBWHOLE])
+            else
+              hreg:=cg.getintregister(helplist,OS_ADDR);
+
+            helplist.concat(taicpu.op_reg_const(A_LI_D,hreg,spilltemp.offset));
+            helplist.concat(taicpu.op_reg_reg_reg(A_ADD_D,hreg,hreg,spilltemp.base));
+
+            reference_reset_base(tmpref,hreg,0,ctempposinvalid,sizeof(aint),[]);
+            helplist.concat(spilling_create_store(tempreg,tmpref));
+            if getregtype(tempreg)=R_INTREGISTER then
+              ungetregisterinline(helplist,hreg);
+
+            list.insertlistafter(pos,helplist);
+            helplist.free;
+          end
+        else
+          inherited;
+      end;
+
+
+end.

+ 74 - 0
compiler/loongarch64/rloongarch64abi.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+'INVALID',
+'$zero',
+'$ra',
+'$tp',
+'$sp',
+'$a0',
+'$a1',
+'$a2',
+'$a3',
+'$a4',
+'$a5',
+'$a6',
+'$a7',
+'$t0',
+'$t1',
+'$t2',
+'$t3',
+'$t4',
+'$t5',
+'$t6',
+'$t7',
+'$t8',
+'$x',
+'$fp',
+'$s0',
+'$s1',
+'$s2',
+'$s3',
+'$s4',
+'$s5',
+'$s6',
+'$s7',
+'$s8',
+'$fa0',
+'$fa1',
+'$fa2',
+'$fa3',
+'$fa4',
+'$fa5',
+'$fa6',
+'$fa7',
+'$ft0',
+'$ft1',
+'$ft2',
+'$ft3',
+'$ft4',
+'$ft5',
+'$ft6',
+'$ft7',
+'$ft8',
+'$ft9',
+'$ft10',
+'$ft11',
+'$ft12',
+'$ft13',
+'$ft14',
+'$ft15',
+'$fs0',
+'$fs1',
+'$fs2',
+'$fs3',
+'$fs4',
+'$fs5',
+'$fs6',
+'$fs7',
+'$fcc0',
+'$fcc1',
+'$fcc2',
+'$fcc3',
+'$fcc4',
+'$fcc5',
+'$fcc6',
+'$fcc7'

+ 74 - 0
compiler/loongarch64/rloongarch64con.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+NR_NO = tregister(0);
+NR_R0 = tregister(16777216);
+NR_R1 = tregister(16777217);
+NR_R2 = tregister(16777218);
+NR_R3 = tregister(16777219);
+NR_R4 = tregister(16777220);
+NR_R5 = tregister(16777221);
+NR_R6 = tregister(16777222);
+NR_R7 = tregister(16777223);
+NR_R8 = tregister(16777224);
+NR_R9 = tregister(16777225);
+NR_R10 = tregister(16777226);
+NR_R11 = tregister(16777227);
+NR_R12 = tregister(16777228);
+NR_R13 = tregister(16777229);
+NR_R14 = tregister(16777230);
+NR_R15 = tregister(16777231);
+NR_R16 = tregister(16777232);
+NR_R17 = tregister(16777233);
+NR_R18 = tregister(16777234);
+NR_R19 = tregister(16777235);
+NR_R20 = tregister(16777236);
+NR_R21 = tregister(16777237);
+NR_R22 = tregister(16777238);
+NR_R23 = tregister(16777239);
+NR_R24 = tregister(16777240);
+NR_R25 = tregister(16777241);
+NR_R26 = tregister(16777242);
+NR_R27 = tregister(16777243);
+NR_R28 = tregister(16777244);
+NR_R29 = tregister(16777245);
+NR_R30 = tregister(16777246);
+NR_R31 = tregister(16777247);
+NR_F0 = tregister(33554432);
+NR_F1 = tregister(33554433);
+NR_F2 = tregister(33554434);
+NR_F3 = tregister(33554435);
+NR_F4 = tregister(33554436);
+NR_F5 = tregister(33554437);
+NR_F6 = tregister(33554438);
+NR_F7 = tregister(33554439);
+NR_F8 = tregister(33554440);
+NR_F9 = tregister(33554441);
+NR_F10 = tregister(33554442);
+NR_F11 = tregister(33554443);
+NR_F12 = tregister(33554444);
+NR_F13 = tregister(33554445);
+NR_F14 = tregister(33554446);
+NR_F15 = tregister(33554447);
+NR_F16 = tregister(33554448);
+NR_F17 = tregister(33554449);
+NR_F18 = tregister(33554450);
+NR_F19 = tregister(33554451);
+NR_F20 = tregister(33554452);
+NR_F21 = tregister(33554453);
+NR_F22 = tregister(33554454);
+NR_F23 = tregister(33554455);
+NR_F24 = tregister(33554456);
+NR_F25 = tregister(33554457);
+NR_F26 = tregister(33554458);
+NR_F27 = tregister(33554459);
+NR_F28 = tregister(33554460);
+NR_F29 = tregister(33554461);
+NR_F30 = tregister(33554462);
+NR_F31 = tregister(33554463);
+NR_FCC0 = tregister(83886080);
+NR_FCC1 = tregister(83886081);
+NR_FCC2 = tregister(83886082);
+NR_FCC3 = tregister(83886083);
+NR_FCC4 = tregister(83886084);
+NR_FCC5 = tregister(83886085);
+NR_FCC6 = tregister(83886086);
+NR_FCC7 = tregister(83886087);

+ 74 - 0
compiler/loongarch64/rloongarch64dwa.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+60,
+61,
+62,
+63,
+64,
+65,
+66,
+67,
+68,
+69,
+70,
+71

+ 2 - 0
compiler/loongarch64/rloongarch64nor.inc

@@ -0,0 +1,2 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+73

+ 74 - 0
compiler/loongarch64/rloongarch64num.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+tregister(0),
+tregister(16777216),
+tregister(16777217),
+tregister(16777218),
+tregister(16777219),
+tregister(16777220),
+tregister(16777221),
+tregister(16777222),
+tregister(16777223),
+tregister(16777224),
+tregister(16777225),
+tregister(16777226),
+tregister(16777227),
+tregister(16777228),
+tregister(16777229),
+tregister(16777230),
+tregister(16777231),
+tregister(16777232),
+tregister(16777233),
+tregister(16777234),
+tregister(16777235),
+tregister(16777236),
+tregister(16777237),
+tregister(16777238),
+tregister(16777239),
+tregister(16777240),
+tregister(16777241),
+tregister(16777242),
+tregister(16777243),
+tregister(16777244),
+tregister(16777245),
+tregister(16777246),
+tregister(16777247),
+tregister(33554432),
+tregister(33554433),
+tregister(33554434),
+tregister(33554435),
+tregister(33554436),
+tregister(33554437),
+tregister(33554438),
+tregister(33554439),
+tregister(33554440),
+tregister(33554441),
+tregister(33554442),
+tregister(33554443),
+tregister(33554444),
+tregister(33554445),
+tregister(33554446),
+tregister(33554447),
+tregister(33554448),
+tregister(33554449),
+tregister(33554450),
+tregister(33554451),
+tregister(33554452),
+tregister(33554453),
+tregister(33554454),
+tregister(33554455),
+tregister(33554456),
+tregister(33554457),
+tregister(33554458),
+tregister(33554459),
+tregister(33554460),
+tregister(33554461),
+tregister(33554462),
+tregister(33554463),
+tregister(83886080),
+tregister(83886081),
+tregister(83886082),
+tregister(83886083),
+tregister(83886084),
+tregister(83886085),
+tregister(83886086),
+tregister(83886087)

+ 74 - 0
compiler/loongarch64/rloongarch64rni.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+60,
+61,
+62,
+63,
+64,
+65,
+66,
+67,
+68,
+69,
+70,
+71,
+72

+ 74 - 0
compiler/loongarch64/rloongarch64sri.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+33,
+34,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+35,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+60,
+61,
+62,
+36,
+63,
+64,
+37,
+38,
+39,
+40,
+41,
+42,
+65,
+66,
+67,
+68,
+69,
+70,
+71,
+72,
+1,
+2,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+3,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+4,
+31,
+32,
+5,
+6,
+7,
+8,
+9,
+10,
+0

+ 74 - 0
compiler/loongarch64/rloongarch64sta.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+60,
+61,
+62,
+63,
+64,
+65,
+66,
+67,
+68,
+69,
+70,
+71

+ 74 - 0
compiler/loongarch64/rloongarch64std.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+'INVALID',
+'$r0',
+'$r1',
+'$r2',
+'$r3',
+'$r4',
+'$r5',
+'$r6',
+'$r7',
+'$r8',
+'$r9',
+'$r10',
+'$r11',
+'$r12',
+'$r13',
+'$r14',
+'$r15',
+'$r16',
+'$r17',
+'$r18',
+'$r19',
+'$r20',
+'$r21',
+'$r22',
+'$r23',
+'$r24',
+'$r25',
+'$r26',
+'$r27',
+'$r28',
+'$r29',
+'$r30',
+'$r31',
+'$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',
+'$fcc0',
+'$fcc1',
+'$fcc2',
+'$fcc3',
+'$fcc4',
+'$fcc5',
+'$fcc6',
+'$fcc7'

+ 74 - 0
compiler/loongarch64/rloongarch64sup.inc

@@ -0,0 +1,74 @@
+{ don't edit, this file is generated from loongarchreg.dat }
+RS_NO = 0;
+RS_R0 = 0;
+RS_R1 = 1;
+RS_R2 = 2;
+RS_R3 = 3;
+RS_R4 = 4;
+RS_R5 = 5;
+RS_R6 = 6;
+RS_R7 = 7;
+RS_R8 = 8;
+RS_R9 = 9;
+RS_R10 = 10;
+RS_R11 = 11;
+RS_R12 = 12;
+RS_R13 = 13;
+RS_R14 = 14;
+RS_R15 = 15;
+RS_R16 = 16;
+RS_R17 = 17;
+RS_R18 = 18;
+RS_R19 = 19;
+RS_R20 = 20;
+RS_R21 = 21;
+RS_R22 = 22;
+RS_R23 = 23;
+RS_R24 = 24;
+RS_R25 = 25;
+RS_R26 = 26;
+RS_R27 = 27;
+RS_R28 = 28;
+RS_R29 = 29;
+RS_R30 = 30;
+RS_R31 = 31;
+RS_F0 = 0;
+RS_F1 = 1;
+RS_F2 = 2;
+RS_F3 = 3;
+RS_F4 = 4;
+RS_F5 = 5;
+RS_F6 = 6;
+RS_F7 = 7;
+RS_F8 = 8;
+RS_F9 = 9;
+RS_F10 = 10;
+RS_F11 = 11;
+RS_F12 = 12;
+RS_F13 = 13;
+RS_F14 = 14;
+RS_F15 = 15;
+RS_F16 = 16;
+RS_F17 = 17;
+RS_F18 = 18;
+RS_F19 = 19;
+RS_F20 = 20;
+RS_F21 = 21;
+RS_F22 = 22;
+RS_F23 = 23;
+RS_F24 = 24;
+RS_F25 = 25;
+RS_F26 = 26;
+RS_F27 = 27;
+RS_F28 = 28;
+RS_F29 = 29;
+RS_F30 = 30;
+RS_F31 = 31;
+RS_FCC0 = 0;
+RS_FCC1 = 1;
+RS_FCC2 = 2;
+RS_FCC3 = 3;
+RS_FCC4 = 4;
+RS_FCC5 = 5;
+RS_FCC6 = 6;
+RS_FCC7 = 7;

+ 220 - 0
compiler/loongarch64/symcpu.pas

@@ -0,0 +1,220 @@
+{
+    Copyright (c) 2014 by Florian Klaempfl
+
+    Symbol table overrides for LoongArch64
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit symcpu;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  symtype,symdef,symsym;
+
+type
+  { defs }
+  tcpufiledef = class(tfiledef)
+  end;
+  tcpufiledefclass = class of tcpufiledef;
+
+  tcpuvariantdef = class(tvariantdef)
+  end;
+  tcpuvariantdefclass = class of tcpuvariantdef;
+
+  tcpuformaldef = class(tformaldef)
+  end;
+  tcpuformaldefclass = class of tcpuformaldef;
+
+  tcpuforwarddef = class(tforwarddef)
+  end;
+  tcpuforwarddefclass = class of tcpuforwarddef;
+
+  tcpuundefineddef = class(tundefineddef)
+  end;
+  tcpuundefineddefclass = class of tcpuundefineddef;
+
+  tcpuerrordef = class(terrordef)
+  end;
+  tcpuerrordefclass = class of tcpuerrordef;
+
+  tcpupointerdef = class(tpointerdef)
+  end;
+  tcpupointerdefclass = class of tcpupointerdef;
+
+  tcpurecorddef = class(trecorddef)
+  end;
+  tcpurecorddefclass = class of tcpurecorddef;
+
+  tcpuimplementedinterface = class(timplementedinterface)
+  end;
+  tcpuimplementedinterfaceclass = class of tcpuimplementedinterface;
+
+  tcpuobjectdef = class(tobjectdef)
+  end;
+  tcpuobjectdefclass = class of tcpuobjectdef;
+
+  tcpuclassrefdef = class(tclassrefdef)
+  end;
+  tcpuclassrefdefclass = class of tcpuclassrefdef;
+
+  tcpuarraydef = class(tarraydef)
+  end;
+  tcpuarraydefclass = class of tcpuarraydef;
+
+  tcpuorddef = class(torddef)
+  end;
+  tcpuorddefclass = class of tcpuorddef;
+
+  tcpufloatdef = class(tfloatdef)
+  end;
+  tcpufloatdefclass = class of tcpufloatdef;
+
+  tcpuprocvardef = class(tprocvardef)
+  end;
+  tcpuprocvardefclass = class of tcpuprocvardef;
+
+  tcpuprocdef = class(tprocdef)
+  end;
+  tcpuprocdefclass = class of tcpuprocdef;
+
+  tcpustringdef = class(tstringdef)
+  end;
+  tcpustringdefclass = class of tcpustringdef;
+
+  tcpuenumdef = class(tenumdef)
+  end;
+  tcpuenumdefclass = class of tcpuenumdef;
+
+  tcpusetdef = class(tsetdef)
+  end;
+  tcpusetdefclass = class of tcpusetdef;
+
+  { syms }
+  tcpulabelsym = class(tlabelsym)
+  end;
+  tcpulabelsymclass = class of tcpulabelsym;
+
+  tcpuunitsym = class(tunitsym)
+  end;
+  tcpuunitsymclass = class of tcpuunitsym;
+
+  tcpuprogramparasym = class(tprogramparasym)
+  end;
+  tcpuprogramparasymclass = class(tprogramparasym);
+
+  tcpunamespacesym = class(tnamespacesym)
+  end;
+  tcpunamespacesymclass = class of tcpunamespacesym;
+
+  tcpuprocsym = class(tprocsym)
+  end;
+  tcpuprocsymclass = class of tcpuprocsym;
+
+  tcputypesym = class(ttypesym)
+  end;
+  tcpuypesymclass = class of tcputypesym;
+
+  tcpufieldvarsym = class(tfieldvarsym)
+  end;
+  tcpufieldvarsymclass = class of tcpufieldvarsym;
+
+  tcpulocalvarsym = class(tlocalvarsym)
+  end;
+  tcpulocalvarsymclass = class of tcpulocalvarsym;
+
+  tcpuparavarsym = class(tparavarsym)
+  end;
+  tcpuparavarsymclass = class of tcpuparavarsym;
+
+  tcpustaticvarsym = class(tstaticvarsym)
+  end;
+  tcpustaticvarsymclass = class of tcpustaticvarsym;
+
+  tcpuabsolutevarsym = class(tabsolutevarsym)
+  end;
+  tcpuabsolutevarsymclass = class of tcpuabsolutevarsym;
+
+  tcpupropertysym = class(tpropertysym)
+  end;
+  tcpupropertysymclass = class of tcpupropertysym;
+
+  tcpuconstsym = class(tconstsym)
+  end;
+  tcpuconstsymclass = class of tcpuconstsym;
+
+  tcpuenumsym = class(tenumsym)
+  end;
+  tcpuenumsymclass = class of tcpuenumsym;
+
+  tcpusyssym = class(tsyssym)
+  end;
+  tcpusyssymclass = class of tcpusyssym;
+
+
+const
+  pbestrealtype : ^tdef = @s64floattype;
+
+
+implementation
+
+  uses
+    symconst, defutil, defcmp;
+
+
+begin
+  { used tdef classes }
+  cfiledef:=tcpufiledef;
+  cvariantdef:=tcpuvariantdef;
+  cformaldef:=tcpuformaldef;
+  cforwarddef:=tcpuforwarddef;
+  cundefineddef:=tcpuundefineddef;
+  cerrordef:=tcpuerrordef;
+  cpointerdef:=tcpupointerdef;
+  crecorddef:=tcpurecorddef;
+  cimplementedinterface:=tcpuimplementedinterface;
+  cobjectdef:=tcpuobjectdef;
+  cclassrefdef:=tcpuclassrefdef;
+  carraydef:=tcpuarraydef;
+  corddef:=tcpuorddef;
+  cfloatdef:=tcpufloatdef;
+  cprocvardef:=tcpuprocvardef;
+  cprocdef:=tcpuprocdef;
+  cstringdef:=tcpustringdef;
+  cenumdef:=tcpuenumdef;
+  csetdef:=tcpusetdef;
+
+  { used tsym classes }
+  clabelsym:=tcpulabelsym;
+  cunitsym:=tcpuunitsym;
+  cprogramparasym:=tcpuprogramparasym;
+  cnamespacesym:=tcpunamespacesym;
+  cprocsym:=tcpuprocsym;
+  ctypesym:=tcputypesym;
+  cfieldvarsym:=tcpufieldvarsym;
+  clocalvarsym:=tcpulocalvarsym;
+  cparavarsym:=tcpuparavarsym;
+  cstaticvarsym:=tcpustaticvarsym;
+  cabsolutevarsym:=tcpuabsolutevarsym;
+  cpropertysym:=tcpupropertysym;
+  cconstsym:=tcpuconstsym;
+  cenumsym:=tcpuenumsym;
+  csyssym:=tcpusyssym;
+end.
+

+ 45 - 0
compiler/loongarch64/tripletcpu.pas

@@ -0,0 +1,45 @@
+{
+    Copyright (c) 2020 by Jonas Maebe
+
+    Construct the cpu part of the triplet
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit tripletcpu;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  globtype;
+
+function tripletcpustr(tripletstyle: ttripletstyle): ansistring;
+
+implementation
+
+uses
+  globals, cpuinfo;
+
+function tripletcpustr(tripletstyle: ttripletstyle): ansistring;
+  begin
+    result:='loongarch';
+  end;
+
+
+end.
+

+ 4 - 0
compiler/ncgcnv.pas

@@ -677,7 +677,11 @@ interface
           the bits that define the true status can be outside the limits
           of the new size and truncating the register can result in a 0
           value }
+{$ifndef loongarch64}
         if (left.expectloc in [LOC_FLAGS,LOC_JUMP]) and
+{$else loongarch64}
+        if (left.expectloc=LOC_JUMP) and
+{$endif loongarch64}
            { a cbool must be converted to -1/0 }
            not is_cbool(resultdef) then
           begin

+ 22 - 0
compiler/options.pas

@@ -981,6 +981,9 @@ begin
 {$endif}
 {$ifdef wasm32}
       'W',
+{$endif}
+{$ifdef loongarch64}
+      'l',
 {$endif}
       '*' : show:=true;
      end;
@@ -4395,6 +4398,16 @@ procedure read_arguments(cmd:TCmdStr);
         def_system_macro('FPC_COMP_IS_INT64');
       {$endif wasm32}
 
+      {$ifdef loongarch64}
+        def_system_macro('CPULOONGARCH');
+        def_system_macro('CPULOONGARCH64');
+        def_system_macro('CPU64');
+        def_system_macro('FPC_CURRENCY_IS_INT64');
+        def_system_macro('FPC_COMP_IS_INT64');
+        def_system_macro('FPC_REQUIRES_PROPER_ALIGNMENT');
+        def_system_macro('FPC_LOCALS_ARE_STACK_REG_RELATIVE');
+      {$endif loongarch64}
+
       {$if defined(cpu8bitalu)}
         def_system_macro('CPUINT8');
       {$elseif defined(cpu16bitalu)}
@@ -5182,6 +5195,15 @@ begin
     end;
 {$endif wasm}
 
+{$if defined(loongarch64)}
+  { LoongArch defaults }
+  if (target_info.abi = abi_riscv_hf) then
+    begin
+      init_settings.cputype:=cpu_3a;
+      init_settings.fputype:=fpu_fd;
+    end;
+{$endif defined(loongarch64)}
+
   { now we can define cpu and fpu type }
   def_cpu_macros;
 

+ 7 - 0
compiler/pp.pas

@@ -45,6 +45,7 @@ program pp;
   X86_64              generate a compiler for the AMD x86-64 architecture
   XTENSA              generate a compiler for XTENSA
   Z80                 generate a compiler for Z80
+  LOONGARCH64         generate a compiler for the LoongArch64 architecture
 
   -----------------------------------------------------------------
   Other compiler switches
@@ -201,6 +202,12 @@ program pp;
   {$endif CPUDEFINED}
   {$define CPUDEFINED}
 {$endif WASM32}
+{$ifdef LOONGARCH64}
+  {$ifdef CPUDEFINED}
+    {$fatal ONLY one of the switches for the CPU type must be defined}
+  {$endif CPUDEFINED}
+  {$define CPUDEFINED}
+{$endif LOONGARCH64}
 
 {$ifndef CPUDEFINED}
   {$fatal A CPU type switch must be defined}

+ 4 - 0
compiler/psystem.pas

@@ -394,6 +394,10 @@ implementation
         create_fpu_types;
         s64currencytype:=corddef.create(scurrency,low(int64),high(int64),true);
 {$endif xtensa}
+{$ifdef loongarch64}
+        create_fpu_types;
+        s64currencytype:=corddef.create(scurrency,low(int64),high(int64),true);
+{$endif loongarch64}
         set_default_int_types;
         { some other definitions }
         charpointertype:=cpointerdef.create(cansichartype);

+ 46 - 5
compiler/raatt.pas

@@ -48,7 +48,7 @@ unit raatt;
         AS_RPAREN,AS_COLON,AS_DOT,AS_PLUS,AS_MINUS,AS_STAR,
         AS_SEPARATOR,AS_ID,AS_REGISTER,AS_OPCODE,AS_SLASH,AS_DOLLAR,
         AS_HASH,AS_LSBRACKET,AS_RSBRACKET,AS_LBRACKET,AS_RBRACKET,
-        AS_EQUAL,
+        AS_EQUAL,AS_QUESTION,AS_LT,AS_GT,
         {------------------ Assembler directives --------------------}
         AS_DB,AS_DW,AS_DD,AS_DQ,AS_GLOBAL,
         AS_ALIGN,AS_BALIGN,AS_P2ALIGN,AS_ASCII,
@@ -76,7 +76,7 @@ unit raatt;
         ')',':','.','+','-','*',
         ';','identifier','register','opcode','/','$',
         '#','{','}','[',']',
-        '=',
+        '=','?','<','>',
         '.byte','.word','.long','.quad','.globl',
         '.align','.balign','.p2align','.ascii',
         '.asciz','.lcomm','.comm','.single','.double','.tfloat','.tcfloat',
@@ -363,6 +363,18 @@ unit raatt;
                end;
            end;
 {$endif xtensa}
+{$ifdef loongarch64}
+           { LoongArch have multiple postfixes. So... }
+           case c of
+             '.' :
+               begin
+                 repeat
+                   actasmpattern:=actasmpattern+c;
+                   c:=current_scanner.asmgetchar;
+                 until not(c in ['a'..'z','A'..'Z', '0'..'9', '.']);
+               end;
+           end;
+{$endif loongarch64}
            { Opcode ? }
            If is_asmopcode(upper(actasmpattern)) then
             Begin
@@ -718,14 +730,23 @@ unit raatt;
                  exit;
                end;
 {$endif arm or aarch64}
-{$ifdef arm}
+{$if defined(arm) or defined(loongarch64)}
              '=' :
                begin
                  actasmtoken:=AS_EQUAL;
                  c:=current_scanner.asmgetchar;
                  exit;
                end;
-{$endif arm}
+{$endif arm or loongarch64}
+
+{$ifdef loongarch64}
+             '?' :
+               begin
+                 actasmtoken:=AS_QUESTION;
+                 c:=current_scanner.asmgetchar;
+                 exit;
+               end;
+{$endif loongarch64}
 
              ',' :
                begin
@@ -736,19 +757,39 @@ unit raatt;
 
              '<' :
                begin
+{$if defined(loongarch64)}
+                 actasmtoken:=AS_LT;
+                 c:=current_scanner.asmgetchar;
+                 if c = '<' then
+                   begin
+                     actasmtoken:=AS_SHL;
+                     c:=current_scanner.asmgetchar;
+                   end;
+{$else}
                  actasmtoken:=AS_SHL;
                  c:=current_scanner.asmgetchar;
                  if c = '<' then
                   c:=current_scanner.asmgetchar;
+{$endif loongarch64}
                  exit;
                end;
 
              '>' :
                begin
-                 actasmtoken:=AS_SHL;
+{$if defined(loongarch64)}
+                 actasmtoken:=AS_GT;
+                 c:=current_scanner.asmgetchar;
+                 if c = '>' then
+                   begin
+                     actasmtoken:=AS_SHR;
+                     c:=current_scanner.asmgetchar;
+                   end;
+{$else}
+                 actasmtoken:=AS_SHR;
                  c:=current_scanner.asmgetchar;
                  if c = '>' then
                   c:=current_scanner.asmgetchar;
+{$endif loongarch64}
                  exit;
                end;
 

+ 2 - 1
compiler/systems.inc

@@ -58,7 +58,8 @@
              cpu_xtensa,                   { 21 }
              cpu_z80,                      { 22 }
              cpu_mips64,                   { 23 }
-             cpu_mips64el                  { 24 }
+             cpu_mips64el,                 { 24 }
+             cpu_loongarch64               { 25 }
        );
 
        tasmmode= (asmmode_none

+ 2 - 2
compiler/systems.pas

@@ -470,11 +470,11 @@ interface
        asms_int_coff = [as_arm_pecoffwince,as_x86_64_pecoff,as_i386_pecoffwince,
                         as_i386_pecoffwdosx,as_i386_pecoff,as_i386_coff];
 
-       cpu2str : array[TSystemCpu] of string[10] =
+       cpu2str : array[TSystemCpu] of string[12] =
             ('','i386','m68k','alpha','powerpc','sparc','vm','ia64','x86_64',
              'mips','arm', 'powerpc64', 'avr', 'mipsel','jvm', 'i8086',
              'aarch64', 'wasm32', 'sparc64', 'riscv32', 'riscv64', 'xtensa',
-             'z80', 'mips64', 'mips64el');
+             'z80', 'mips64', 'mips64el', 'loongarch64');
 
        abiinfo : array[tabi] of tabiinfo = (
          (name: 'DEFAULT'; supported: true),

+ 1 - 1
compiler/tgobj.pas

@@ -186,7 +186,7 @@ implementation
        tempfreelist:=nil;
        templist:=nil;
        { we could create a new child class for this but I don't if it is worth the effort (FK) }
-{$if defined(powerpc) or defined(powerpc64) or defined(avr) or defined(jvm) or defined(aarch64) or defined(xtensa) or defined(wasm32)}
+{$if defined(powerpc) or defined(powerpc64) or defined(avr) or defined(jvm) or defined(aarch64) or defined(xtensa) or defined(wasm32) or defined(loongarch64)}
        direction:=1;
 {$else}
        direction:=-1;

+ 4 - 0
compiler/utils/fpc.pp

@@ -188,6 +188,10 @@ program fpc;
      ppcbin:='ppcwasm32';
      processorname:='wasm32';
 {$endif wasm32}
+{$ifdef loongarch64}
+     ppcbin:='ppcloongarch64';
+     processorname:='loongarch64';
+{$endif loongarch64}
      versionstr:='';                      { Default is just the name }
      if ParamCount = 0 then
        begin

+ 198 - 0
compiler/utils/mkloongarch64ins.pp

@@ -0,0 +1,198 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    Convert loongarchins.dat from Nasm to a .inc file for usage with
+    the Free pascal compiler
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$mode objfpc}
+
+program mkloongarch64ins;
+
+const
+  Version = '1.0';
+
+var
+  insfile,opfile,nopfile,attfile : text;
+
+type
+  topcodes = array[1..64] of string;
+
+procedure bug(errormsg : string);
+begin
+  writeln(errormsg);
+  close(insfile);
+  close(opfile);
+  close(nopfile);
+  close(attfile);
+  halt;
+end;
+
+procedure copy_present_opcodes(start,len : longint; var ops : topcodes);
+var
+  i : longint;
+begin
+  for i:=start to start+len-1 do
+    ops[i]:=ops[1+i-start];
+end;
+
+procedure set_suffix(start,len : longint; var ops : topcodes; suffix : string);
+var
+  i : longint;
+begin
+  for i:=start to start+len-1 do
+    ops[i]:=ops[i]+suffix;
+end;
+
+function decode_format(format,prefix : string; var ops : topcodes) : longint;
+var
+  i,j,nr_comma,last_comma,nr_op : longint;
+  suffixs : string;
+begin
+  nr_op:=1;
+  ops[1]:=prefix;
+  i:=1;
+  while (format[i]<>'0') do
+    begin
+      case format[i] of
+        'a': suffixs:='W,D';
+        'b': suffixs:='W';
+        'c': suffixs:='D';
+        'd': suffixs:='W,WU,D';
+        'e': suffixs:='W,WU,D,DU';
+        'f': suffixs:='W,WU';
+        'g': suffixs:='B,H';
+        'h': suffixs:='2H,4H,2W,D';
+        'i': suffixs:='2W,D';
+        'j': suffixs:='4B,8B,W,D';
+        'k': suffixs:='B,H,W,D';
+        'l': suffixs:='BU,HU,WU';
+        'm': suffixs:='S,D';
+        'n': suffixs:='CAF,CUN,CEQ,CUEQ,CLT,CULT,CUGT,CLE,CULE,CUGE,CNE,COR,CUNE,SAF,SUN,SEQ,SUEQ,SLT,SGT,SULT,SLE,SGE,SULE,SNE,SOR,SUNE';
+        'o': suffixs:='S';
+        'p': suffixs:='D';
+        'q': suffixs:='L,W';
+        'r': suffixs:='GLOBAL,LOCAL,ABS,PCREL,GOT,TLE#LE,TLS#IE,TLS#LD,TLS#GD';
+      else
+        bug('Error Format');
+      end;
+      i:=i+1;
+
+      { For each comma, add suffix for present opcodes }
+      nr_comma:=1;
+      last_comma:=length(suffixs)+1;
+      for j:=length(suffixs) downto 0 do
+        begin
+          if (j=0) then
+            set_suffix(1,nr_op,ops,'#'+copy(suffixs,1,last_comma-1));
+          if (suffixs[j]<>',') then
+            continue;
+          copy_present_opcodes(nr_comma*nr_op+1, nr_op, ops);
+          set_suffix(nr_comma*nr_op+1,nr_op,ops,'#'+copy(suffixs,j+1,last_comma-j-1));
+          last_comma:=j;
+          nr_comma:=nr_comma+1;
+        end;
+      nr_op:=nr_comma*nr_op;
+    end;
+    result:=nr_op;
+end;
+
+procedure writeop(op : string);
+var
+  i : longint;
+  s : string;
+begin
+  for i:=1 to length(op) do
+    if op[i]='#' then
+      s[i]:='_'
+    else
+      s[i]:=op[i];
+  s[0]:=op[0];
+  write(opfile, 'A_', s);
+end;
+
+procedure writeatt(op : string);
+var
+  i : longint;
+  s : string;
+begin
+  for i:=1 to length(op) do
+    if op[i] in ['A'..'Z'] then
+      s[i]:=char(byte(op[i])+32)
+    else if op[i]='#' then
+      s[i]:='.'
+    else
+      s[i]:=op[i];
+  s[0]:=op[0];
+  write(attfile, '''', s, '''');
+end;
+
+var
+  i,j,all_op,nr_op : longint;
+  s : string;
+  opcode : string;
+  opcodes : topcodes;
+  is_not_first_op : boolean;
+begin
+  writeln('FPC Instruction Table Converter Version ',Version);
+  assign(insfile,'../loongarch64/loongarchins.dat');
+  reset(insfile);
+  assign(opfile,'../loongarch64/loongarch64op.inc');
+  rewrite(opfile);
+  writeln(opfile,'{ don''t edit, this file is generated from loongarchins.dat }');
+  writeln(opfile,'(');
+  assign(nopfile,'../loongarch64/loongarch64nop.inc');
+  rewrite(nopfile);
+  writeln(nopfile,'{ don''t edit, this file is generated from loongarchins.dat }');
+  assign(attfile,'../loongarch64/loongarch64att.inc');
+  rewrite(attfile);
+  writeln(attfile,'{ don''t edit, this file is generated from loongarchins.dat }');
+  writeln(attfile,'(');
+
+  all_op:=0;
+  is_not_first_op:=false;
+  while not(eof(insfile)) do
+    begin
+      readln(insfile,s);
+      if (s='') or (s[1]=';') then
+        continue;
+      if (s[1]<>'[') then
+        continue;
+      i:=pos(']',s);
+      opcode:=copy(s,2,i-2);
+      i:=pos('(',s);
+      j:=pos(')',s);
+      nr_op:=decode_format(copy(s,i+1,j-i-1),opcode,opcodes);
+      for i:=1 to nr_op do
+        begin
+          if is_not_first_op then
+            begin
+              writeln(opfile,',');
+              writeln(attfile,',');
+            end;
+          writeop(opcodes[i]);
+          if opcodes[i]='BXX' then
+            writeatt('B')
+          else
+            writeatt(opcodes[i]);
+          is_not_first_op:=true;
+        end;
+      all_op:=all_op+nr_op;
+    end;
+  writeln(opfile);
+  write(opfile,');');
+  writeln(attfile);
+  write(attfile,');');
+  write(nopfile, all_op, ';');
+  close(insfile);
+  close(opfile);
+  close(nopfile);
+  close(attfile);
+end.

+ 267 - 0
compiler/utils/mkloongarch64reg.pp

@@ -0,0 +1,267 @@
+{
+    Copyright (C) 2022 Loongson Technology Corporation Limited.
+
+    Convert loongarchreg.dat to a .inc file for usage with
+    the Free pascal compiler
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$mode objfpc}
+
+program mkloongarchreg;
+
+const
+  Version = '1.0';
+  max_regcount = 512;
+
+var
+  nr_regs : word;
+  srcfile : text;
+  names,abinames,stdnames : array[0..max_regcount] of string;
+  regtypes,subtypes,values,stabs,dwarf,std_regname_index,regnumber_index : array[0..max_regcount] of word;
+  numbers : array[0..max_regcount] of longint;
+
+procedure bug(errormsg : string);
+begin
+  writeln(errormsg);
+  close(srcfile);
+  halt;
+end;
+
+function str2word(s : string) : word;
+var
+  v,errcode : integer;
+begin
+  val(s,v,errcode);
+  if (errcode<>0) or (v>65535) or (v<0) then
+    str2word:=0
+  else
+    str2word:=word(v);
+end;
+
+procedure readdatfile;
+var
+  i,last,idx : longint;
+  s,subs : string;
+begin
+  assign(srcfile, '../loongarch64/loongarchreg.dat');
+  reset(srcfile);
+  nr_regs:=0;
+  while not(eof(srcfile)) do
+    begin
+      readln(srcfile,s);
+      if (s='') or (s[1]=';') then
+        continue;
+      { <name>,<type>,<subtype>,<value>,<abiname>,<stdname>,<stab idx>,<dwarf idx> }
+      last:=length(s)+1;
+      idx:=1;
+      for i:=length(s) downto 0 do
+        begin
+          if (i=0) then
+            begin
+              if (idx<>8) then
+                bug('Incomplete tables');
+              names[nr_regs]:=copy(s,1,last-1);
+              continue;
+            end;
+          if (s[i]<>',') then
+            continue;
+          subs:=copy(s,i+1,last-i-1);
+          case (idx) of
+            1: dwarf[nr_regs]:=str2word(subs);
+            2: stabs[nr_regs]:=str2word(subs);
+            3: stdnames[nr_regs]:=subs;
+            4: abinames[nr_regs]:=subs;
+            5: values[nr_regs]:=str2word(subs);
+            6: subtypes[nr_regs]:=str2word(subs);
+            7: regtypes[nr_regs]:=str2word(subs);
+            8: bug('Overflow tables');
+          end;
+          idx:=idx+1;
+          last:=i;
+        end;
+      nr_regs:=nr_regs+1;
+    end;
+end;
+
+type
+  SWAP_FUNC=procedure (i,j : longint; p : pointer);
+  CMPR_FUNC=function (i,j : longint; p :pointer) : boolean;
+
+procedure qsort(l,r : longint; p : pointer; s : SWAP_FUNC; c : CMPR_FUNC);
+var
+  i,j : longint;
+begin
+  if l>=r then
+    exit;
+  i:=l;
+  j:=r;
+  while i<j do
+    begin
+      while (i<j) and c(i,j,p) do
+        j:=j-1;
+      if i<j then
+        begin
+          s(i,j,p);
+          i:=i+1;
+        end;
+      while (i<j) and c(i,j,p) do
+        i:=i+1;
+      if i<j then
+        begin
+          s(i,j,p);
+          j:=j-1;
+        end;
+    end;
+  qsort(l,i-1,p,s,c);
+  qsort(i+1,r,p,s,c);
+end;
+
+procedure swap_rni(i,j : longint; p : pointer);
+var
+  t : word;
+begin
+  t:=regnumber_index[i];
+  regnumber_index[i]:=regnumber_index[j];
+  regnumber_index[j]:=t;
+end;
+
+function cmpr_rn(i,j : longint; p :pointer) : boolean;
+begin
+  cmpr_rn:=numbers[regnumber_index[i]]<numbers[regnumber_index[j]];
+end;
+
+procedure swap_sri(i,j : longint; p : pointer);
+var
+  t : word;
+begin
+  t:=std_regname_index[i];
+  std_regname_index[i]:=std_regname_index[j];
+  std_regname_index[j]:=t;
+end;
+
+function cmpr_sr(i,j : longint; p :pointer) : boolean;
+begin
+  cmpr_sr:=stdnames[std_regname_index[i]]<stdnames[std_regname_index[j]];
+end;
+
+procedure build_regnum_index;
+var
+  i :longint;
+  s : SWAP_FUNC;
+  c : CMPR_FUNC;
+begin
+  for i:=0 to nr_regs-1 do
+    regnumber_index[i]:=i;
+  s:=@swap_rni;
+  c:=@cmpr_rn;
+  qsort(0,nr_regs-1,nil,s,c);
+end;
+
+procedure build_std_regname_index;
+var
+  i : longint;
+  s : SWAP_FUNC;
+  c : CMPR_FUNC;
+begin
+  for i:=0 to nr_regs-1 do
+    std_regname_index[i]:=i;
+  s:=@swap_sri;
+  c:=@cmpr_sr;
+  qsort(0,nr_regs-1,nil,s,c);
+end;
+
+procedure setarrays;
+var
+  i : longint;
+begin
+  for i:=0 to nr_regs-1 do
+    numbers[i]:=(regtypes[i] shl 24) or (subtypes[i] shl 16) or values[i];
+  build_regnum_index;
+  build_std_regname_index;
+end;
+
+procedure openinc(out f:text;const fn:string);
+begin
+  writeln('creating ',fn);
+  assign(f,fn);
+  rewrite(f);
+  writeln(f,'{ don''t edit, this file is generated from loongarchreg.dat }');
+end;
+
+procedure closeinc(var f:text);
+begin
+  writeln(f);
+  close(f);
+end;
+
+procedure write_inc_files;
+var
+  first : boolean;
+  numfile,stdfile,stabfile,dwarffile,rnifile,srifile,supfile,norfile,confile,abinamefile : text;
+  i : longint;
+begin
+  { create inc files }
+  openinc(confile,'../loongarch64/rloongarch64con.inc');
+  openinc(supfile,'../loongarch64/rloongarch64sup.inc');
+  openinc(numfile,'../loongarch64/rloongarch64num.inc');
+  openinc(stdfile,'../loongarch64/rloongarch64std.inc');
+  openinc(abinamefile,'../loongarch64/rloongarch64abi.inc');
+  openinc(stabfile,'../loongarch64/rloongarch64sta.inc');
+  openinc(dwarffile,'../loongarch64/rloongarch64dwa.inc');
+  openinc(rnifile,'../loongarch64/rloongarch64rni.inc');
+  openinc(srifile,'../loongarch64/rloongarch64sri.inc');
+  first:=true;
+  for i:=0 to nr_regs-1 do
+    begin
+      if not first then
+        begin
+          writeln(numfile,',');
+          writeln(stdfile,',');
+          writeln(abinamefile,',');
+          writeln(stabfile,',');
+          writeln(dwarffile,',');
+          writeln(rnifile,',');
+          writeln(srifile,',');
+        end
+      else
+        first:=false;
+      writeln(supfile,'RS_',names[i],' = ',values[i],';');
+      writeln(confile,'NR_'+names[i],' = ','tregister(',numbers[i],')',';');
+      write(numfile,'tregister(',numbers[i],')');
+      write(stdfile,'''',stdnames[i],'''');
+      write(abinamefile,'''',abinames[i],'''');
+      write(stabfile,stabs[i]);
+      write(dwarffile,dwarf[i]);
+      write(rnifile,regnumber_index[i]);
+      write(srifile,std_regname_index[i]);
+    end;
+  openinc(norfile,'../loongarch64/rloongarch64nor.inc');
+  write(norfile,nr_regs);
+  closeinc(norfile);
+  close(confile);
+  close(supfile);
+  closeinc(numfile);
+  closeinc(stdfile);
+  closeinc(abinamefile);
+  closeinc(stabfile);
+  closeinc(dwarffile);
+  closeinc(rnifile);
+  closeinc(srifile);
+  writeln('Done!');
+  writeln(nr_regs,' registers processed');
+end;
+
+begin
+   writeln('Register Table Converter Version ',Version);
+   readdatfile;
+   setarrays;
+   write_inc_files;
+end.
+

+ 4 - 2
compiler/utils/ppuutils/ppudump.pp

@@ -89,7 +89,8 @@ const
     { 21 } 'xtensa',
     { 22 } 'z80',
     { 23 } 'mips64',
-    { 24 } 'mips64el'
+    { 24 } 'mips64el',
+    { 25 } 'loongarch64'
     );
 
   CpuHasController : array[tsystemcpu] of boolean =
@@ -118,7 +119,8 @@ const
     { 21 } true  {'xtensa'},
     { 22 } true  {'z80'},
     { 23 } false {'mips64'},
-    { 24 } false {'mips64el'}
+    { 24 } false {'mips64el'},
+    { 25 } false {'loongarch64'}
     );
 
 { List of all supported system-cpu couples }

+ 3 - 0
compiler/version.pas

@@ -86,6 +86,9 @@ interface
 {$ifdef cpuwasm32}
         source_cpu_string = 'wasm32';
 {$endif cpuwasm32}
+{$ifdef cpuloongarch64}
+        source_cpu_string = 'loongarch64';
+{$endif cpuloongarch64}
 
 function version_string:string;
 function full_version_string:string;