public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v4 00/12] Add LoongArch support.
@ 2021-12-24  9:28 chenglulu
  2021-12-24  9:28 ` [PATCH v4 01/12] LoongArch Port: Regenerate configure chenglulu
                   ` (12 more replies)
  0 siblings, 13 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

The LoongArch architecture (LoongArch) is an Instruction Set
Architecture (ISA) that has a Reduced Instruction Set Computer (RISC)
style.
The documents are on
https://loongson.github.io/LoongArch-Documentation/README-EN.html

The ELF ABI Documents are on:
https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

The binutils has been merged into trunk:
https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=560b3fe208255ae909b4b1c88ba9c28b09043307

Note: We split -mabi= into -mabi=lp64d/f/s, the new options not support by upstream binutils yet, 
this GCC port requires the following patch applied to binutils to build.
https://github.com/loongson/binutils-gdb/commit/aacb0bf860f02aa5a7dcb76dd0e392bf871c7586
(will be submitted to upstream after gcc side comfirmed)


changelog:

v1 -> v2
1. Split patch set.
2. Change some code style.
3. Add -mabi=lp64d/f/s options.
4. Change GLIBC_DYNAMIC_LINKER_LP64 name.

v2 -> v3
1. Change some code style.
2. Bug fix.

v3 -> v4
1. Change some code style.
2. Bug fix.
3. Delete some builtin macros.

Add LoongArch support.

chenglulu (12):
  LoongArch Port: Regenerate configure
  LoongArch Port: gcc build
  LoongArch Port: Regenerate gcc/configure.
  LoongArch Port: Machine Decsription files.
  LoongArch Port: Machine description C files and .h files.
  LoongArch Port: Builtin functions.
  LoongArch Port: Builtin macros.
  LoongArch Port: libgcc
  LoongArch Port: Regenerate libgcc/configure.
  LoongArch Port: libgomp
  LoongArch Port: gcc/testsuite
  LoongArch Port: Add doc.

 config/picflag.m4                             |    3 +
 configure                                     |   10 +-
 configure.ac                                  |   10 +-
 contrib/config-list.mk                        |    5 +-
 .../config/loongarch/loongarch-common.c       |   63 +
 gcc/config.gcc                                |  400 +-
 gcc/config/host-linux.c                       |    2 +
 gcc/config/loongarch/constraints.md           |  212 +
 gcc/config/loongarch/generic.md               |  132 +
 gcc/config/loongarch/genopts/genstr.sh        |   91 +
 .../loongarch/genopts/loongarch-strings       |   58 +
 gcc/config/loongarch/genopts/loongarch.opt.in |  189 +
 gcc/config/loongarch/gnu-user.h               |   78 +
 gcc/config/loongarch/la464.md                 |  132 +
 gcc/config/loongarch/larchintrin.h            |  413 ++
 gcc/config/loongarch/linux.h                  |   50 +
 gcc/config/loongarch/loongarch-builtins.c     |  511 ++
 gcc/config/loongarch/loongarch-c.c            |  111 +
 gcc/config/loongarch/loongarch-cpu.c          |  206 +
 gcc/config/loongarch/loongarch-cpu.h          |   30 +
 gcc/config/loongarch/loongarch-def.c          |  164 +
 gcc/config/loongarch/loongarch-def.h          |  151 +
 gcc/config/loongarch/loongarch-driver.c       |  187 +
 gcc/config/loongarch/loongarch-driver.h       |   69 +
 gcc/config/loongarch/loongarch-ftypes.def     |  106 +
 gcc/config/loongarch/loongarch-modes.def      |   29 +
 gcc/config/loongarch/loongarch-opts.c         |  582 ++
 gcc/config/loongarch/loongarch-opts.h         |   86 +
 gcc/config/loongarch/loongarch-protos.h       |  242 +
 gcc/config/loongarch/loongarch-str.h          |   57 +
 gcc/config/loongarch/loongarch-tune.h         |   72 +
 gcc/config/loongarch/loongarch.c              | 6461 +++++++++++++++++
 gcc/config/loongarch/loongarch.h              | 1291 ++++
 gcc/config/loongarch/loongarch.md             | 3829 ++++++++++
 gcc/config/loongarch/loongarch.opt            |  189 +
 gcc/config/loongarch/predicates.md            |  553 ++
 gcc/config/loongarch/sync.md                  |  574 ++
 gcc/config/loongarch/t-linux                  |   53 +
 gcc/config/loongarch/t-loongarch              |   59 +
 gcc/configure                                 |   85 +-
 gcc/configure.ac                              |   33 +-
 gcc/doc/install.texi                          |   47 +-
 gcc/doc/invoke.texi                           |  201 +
 gcc/doc/md.texi                               |   55 +
 gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C    |    2 +-
 gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C   |    2 +-
 gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C   |    2 +-
 gcc/testsuite/gcc.dg/20020312-2.c             |    2 +
 gcc/testsuite/gcc.dg/loop-8.c                 |    2 +-
 .../torture/stackalign/builtin-apply-2.c      |    2 +-
 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c     |    2 +-
 .../gcc.target/loongarch/loongarch.exp        |   40 +
 .../gcc.target/loongarch/tst-asm-const.c      |   16 +
 gcc/testsuite/go.test/go-test.exp             |    3 +
 gcc/testsuite/lib/target-supports.exp         |   14 +
 libgcc/config.host                            |   28 +-
 libgcc/config/loongarch/crtfastmath.c         |   52 +
 libgcc/config/loongarch/crti.S                |   43 +
 libgcc/config/loongarch/crtn.S                |   39 +
 libgcc/config/loongarch/lib2funcs.c           |    0
 libgcc/config/loongarch/linux-unwind.h        |   80 +
 libgcc/config/loongarch/sfp-machine.h         |  152 +
 libgcc/config/loongarch/t-crtstuff            |    2 +
 libgcc/config/loongarch/t-elf                 |    3 +
 libgcc/config/loongarch/t-loongarch           |    9 +
 libgcc/config/loongarch/t-loongarch64         |    1 +
 libgcc/config/loongarch/t-softfp-tf           |    3 +
 libgcc/configure                              |    5 +-
 libgcc/configure.ac                           |    2 +-
 libgomp/configure.tgt                         |    4 +
 70 files changed, 18363 insertions(+), 28 deletions(-)
 create mode 100644 gcc/common/config/loongarch/loongarch-common.c
 create mode 100644 gcc/config/loongarch/constraints.md
 create mode 100644 gcc/config/loongarch/generic.md
 create mode 100755 gcc/config/loongarch/genopts/genstr.sh
 create mode 100644 gcc/config/loongarch/genopts/loongarch-strings
 create mode 100644 gcc/config/loongarch/genopts/loongarch.opt.in
 create mode 100644 gcc/config/loongarch/gnu-user.h
 create mode 100644 gcc/config/loongarch/la464.md
 create mode 100644 gcc/config/loongarch/larchintrin.h
 create mode 100644 gcc/config/loongarch/linux.h
 create mode 100644 gcc/config/loongarch/loongarch-builtins.c
 create mode 100644 gcc/config/loongarch/loongarch-c.c
 create mode 100644 gcc/config/loongarch/loongarch-cpu.c
 create mode 100644 gcc/config/loongarch/loongarch-cpu.h
 create mode 100644 gcc/config/loongarch/loongarch-def.c
 create mode 100644 gcc/config/loongarch/loongarch-def.h
 create mode 100644 gcc/config/loongarch/loongarch-driver.c
 create mode 100644 gcc/config/loongarch/loongarch-driver.h
 create mode 100644 gcc/config/loongarch/loongarch-ftypes.def
 create mode 100644 gcc/config/loongarch/loongarch-modes.def
 create mode 100644 gcc/config/loongarch/loongarch-opts.c
 create mode 100644 gcc/config/loongarch/loongarch-opts.h
 create mode 100644 gcc/config/loongarch/loongarch-protos.h
 create mode 100644 gcc/config/loongarch/loongarch-str.h
 create mode 100644 gcc/config/loongarch/loongarch-tune.h
 create mode 100644 gcc/config/loongarch/loongarch.c
 create mode 100644 gcc/config/loongarch/loongarch.h
 create mode 100644 gcc/config/loongarch/loongarch.md
 create mode 100644 gcc/config/loongarch/loongarch.opt
 create mode 100644 gcc/config/loongarch/predicates.md
 create mode 100644 gcc/config/loongarch/sync.md
 create mode 100644 gcc/config/loongarch/t-linux
 create mode 100644 gcc/config/loongarch/t-loongarch
 create mode 100644 gcc/testsuite/gcc.target/loongarch/loongarch.exp
 create mode 100644 gcc/testsuite/gcc.target/loongarch/tst-asm-const.c
 create mode 100644 libgcc/config/loongarch/crtfastmath.c
 create mode 100644 libgcc/config/loongarch/crti.S
 create mode 100644 libgcc/config/loongarch/crtn.S
 create mode 100644 libgcc/config/loongarch/lib2funcs.c
 create mode 100644 libgcc/config/loongarch/linux-unwind.h
 create mode 100644 libgcc/config/loongarch/sfp-machine.h
 create mode 100644 libgcc/config/loongarch/t-crtstuff
 create mode 100644 libgcc/config/loongarch/t-elf
 create mode 100644 libgcc/config/loongarch/t-loongarch
 create mode 100644 libgcc/config/loongarch/t-loongarch64
 create mode 100644 libgcc/config/loongarch/t-softfp-tf

-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 01/12] LoongArch Port: Regenerate configure
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 02/12] LoongArch Port: gcc build chenglulu
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

        * config/picflag.m4: Default add build option '-fpic' for LoongArch.
        * configure: Add LoongArch tuples.
        * configure.ac: Like wise.
---
 config/picflag.m4 |  3 +++
 configure         | 10 +++++++++-
 configure.ac      | 10 +++++++++-
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/config/picflag.m4 b/config/picflag.m4
index 8b106f9af88..0aefcf619bf 100644
--- a/config/picflag.m4
+++ b/config/picflag.m4
@@ -44,6 +44,9 @@ case "${$2}" in
 	# sets the default TLS model and affects inlining.
 	$1=-fPIC
 	;;
+    loongarch*-*-*)
+	$1=-fpic
+	;;
     mips-sgi-irix6*)
 	# PIC is the default.
 	;;
diff --git a/configure b/configure
index 9c2d7df1bb2..87548f0da96 100755
--- a/configure
+++ b/configure
@@ -3060,7 +3060,7 @@ case "${ENABLE_GOLD}" in
       # Check for target supported by gold.
       case "${target}" in
         i?86-*-* | x86_64-*-* | sparc*-*-* | powerpc*-*-* | arm*-*-* \
-        | aarch64*-*-* | tilegx*-*-* | mips*-*-* | s390*-*-*)
+        | aarch64*-*-* | tilegx*-*-* | mips*-*-* | s390*-*-* | loongarch*-*-*)
 	  configdirs="$configdirs gold"
 	  if test x${ENABLE_GOLD} = xdefault; then
 	    default_ld=gold
@@ -3646,6 +3646,9 @@ case "${target}" in
   i[3456789]86-*-*)
     libgloss_dir=i386
     ;;
+  loongarch*-*-*)
+    libgloss_dir=loongarch
+    ;;
   m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*)
     libgloss_dir=m68hc11
     ;;
@@ -4030,6 +4033,11 @@ case "${target}" in
   wasm32-*-*)
     noconfigdirs="$noconfigdirs ld"
     ;;
+  loongarch*-*-linux*)
+    ;;
+  loongarch*-*-*)
+    noconfigdirs="$noconfigdirs gprof"
+    ;;
 esac
 
 # If we aren't building newlib, then don't build libgloss, since libgloss
diff --git a/configure.ac b/configure.ac
index 68cc5cc31fe..55362afeeae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -353,7 +353,7 @@ case "${ENABLE_GOLD}" in
       # Check for target supported by gold.
       case "${target}" in
         i?86-*-* | x86_64-*-* | sparc*-*-* | powerpc*-*-* | arm*-*-* \
-        | aarch64*-*-* | tilegx*-*-* | mips*-*-* | s390*-*-*)
+        | aarch64*-*-* | tilegx*-*-* | mips*-*-* | s390*-*-* | loongarch*-*-*)
 	  configdirs="$configdirs gold"
 	  if test x${ENABLE_GOLD} = xdefault; then
 	    default_ld=gold
@@ -899,6 +899,9 @@ case "${target}" in
   i[[3456789]]86-*-*)
     libgloss_dir=i386
     ;;
+  loongarch*-*-*)
+    libgloss_dir=loongarch
+    ;;
   m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*)
     libgloss_dir=m68hc11
     ;;
@@ -1283,6 +1286,11 @@ case "${target}" in
   wasm32-*-*)
     noconfigdirs="$noconfigdirs ld"
     ;;
+  loongarch*-*-linux*)
+    ;;
+  loongarch*-*-*)
+    noconfigdirs="$noconfigdirs gprof"
+    ;;
 esac
 
 # If we aren't building newlib, then don't build libgloss, since libgloss
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 02/12] LoongArch Port: gcc build
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
  2021-12-24  9:28 ` [PATCH v4 01/12] LoongArch Port: Regenerate configure chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 03/12] LoongArch Port: Regenerate gcc/configure chenglulu
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/

        * common/config/loongarch/loongarch-common.c: New file.
        * config/loongarch/genopts/genstr.sh: New file.
        * config/loongarch/genopts/loongarch-strings: New file.
        * config/loongarch/genopts/loongarch.opt.in: New file.
        * config/loongarch/gnu-user.h: New file.
        * config/loongarch/linux.h: New file.
        * config/loongarch/loongarch-cpu.c: New file.
        * config/loongarch/loongarch-cpu.h: New file.
        * config/loongarch/loongarch-driver.c: New file.
        * config/loongarch/loongarch-driver.h: New file.
        * config/loongarch/loongarch-opts.c: New file.
        * config/loongarch/loongarch-opts.h: New file.
        * config/loongarch/loongarch.opt: New file.
        * config/loongarch/t-linux: New file.
        * config/loongarch/t-loongarch: New file.
        * config.gcc: Add LoongArch support.
        * configure.ac: Add LoongArch support.
---
 .../config/loongarch/loongarch-common.c       |  63 ++
 gcc/config.gcc                                | 400 +++++++++++-
 gcc/config/loongarch/genopts/genstr.sh        |  91 +++
 .../loongarch/genopts/loongarch-strings       |  58 ++
 gcc/config/loongarch/genopts/loongarch.opt.in | 189 ++++++
 gcc/config/loongarch/gnu-user.h               |  78 +++
 gcc/config/loongarch/linux.h                  |  50 ++
 gcc/config/loongarch/loongarch-cpu.c          | 206 +++++++
 gcc/config/loongarch/loongarch-cpu.h          |  30 +
 gcc/config/loongarch/loongarch-def.c          | 164 +++++
 gcc/config/loongarch/loongarch-def.h          | 151 +++++
 gcc/config/loongarch/loongarch-driver.c       | 187 ++++++
 gcc/config/loongarch/loongarch-driver.h       |  69 +++
 gcc/config/loongarch/loongarch-opts.c         | 582 ++++++++++++++++++
 gcc/config/loongarch/loongarch-opts.h         |  86 +++
 gcc/config/loongarch/loongarch-str.h          |  57 ++
 gcc/config/loongarch/loongarch.opt            | 189 ++++++
 gcc/config/loongarch/t-linux                  |  53 ++
 gcc/config/loongarch/t-loongarch              |  59 ++
 gcc/configure.ac                              |  33 +-
 20 files changed, 2790 insertions(+), 5 deletions(-)
 create mode 100644 gcc/common/config/loongarch/loongarch-common.c
 create mode 100755 gcc/config/loongarch/genopts/genstr.sh
 create mode 100644 gcc/config/loongarch/genopts/loongarch-strings
 create mode 100644 gcc/config/loongarch/genopts/loongarch.opt.in
 create mode 100644 gcc/config/loongarch/gnu-user.h
 create mode 100644 gcc/config/loongarch/linux.h
 create mode 100644 gcc/config/loongarch/loongarch-cpu.c
 create mode 100644 gcc/config/loongarch/loongarch-cpu.h
 create mode 100644 gcc/config/loongarch/loongarch-def.c
 create mode 100644 gcc/config/loongarch/loongarch-def.h
 create mode 100644 gcc/config/loongarch/loongarch-driver.c
 create mode 100644 gcc/config/loongarch/loongarch-driver.h
 create mode 100644 gcc/config/loongarch/loongarch-opts.c
 create mode 100644 gcc/config/loongarch/loongarch-opts.h
 create mode 100644 gcc/config/loongarch/loongarch-str.h
 create mode 100644 gcc/config/loongarch/loongarch.opt
 create mode 100644 gcc/config/loongarch/t-linux
 create mode 100644 gcc/config/loongarch/t-loongarch

diff --git a/gcc/common/config/loongarch/loongarch-common.c b/gcc/common/config/loongarch/loongarch-common.c
new file mode 100644
index 00000000000..3f440aef1e7
--- /dev/null
+++ b/gcc/common/config/loongarch/loongarch-common.c
@@ -0,0 +1,63 @@
+/* Common hooks for LoongArch.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "common/common-target.h"
+#include "common/common-target-def.h"
+#include "opts.h"
+#include "flags.h"
+#include "diagnostic-core.h"
+
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+loongarch_handle_option (struct gcc_options *opts,
+			 struct gcc_options *opts_set ATTRIBUTE_UNUSED,
+			 const struct cl_decoded_option *decoded,
+			 location_t loc ATTRIBUTE_UNUSED)
+{
+  size_t code = decoded->opt_index;
+  int value = decoded->value;
+
+  switch (code)
+    {
+    case OPT_mmemcpy:
+      if (value)
+	{
+	  if (opts->x_optimize_size)
+	    opts->x_target_flags |= MASK_MEMCPY;
+	}
+      else
+	opts->x_target_flags &= ~MASK_MEMCPY;
+      return true;
+
+    default:
+      return true;
+    }
+}
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS	MASK_CHECK_ZERO_DIV
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION loongarch_handle_option
+
+struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 91806aa706f..7058a454b1b 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -454,6 +454,13 @@ mips*-*-*)
 	extra_objs="frame-header-opt.o"
 	extra_options="${extra_options} g.opt fused-madd.opt mips/mips-tables.opt"
 	;;
+loongarch*-*-*)
+	cpu_type=loongarch
+	extra_headers="larchintrin.h"
+	extra_objs="loongarch-c.o loongarch-builtins.o loongarch-cpu.o loongarch-opts.o loongarch-def.o"
+	extra_gcc_objs="loongarch-driver.o loongarch-cpu.o loongarch-opts.o loongarch-def.o"
+	extra_options="${extra_options} g.opt fused-madd.opt"
+	;;
 nds32*)
 	cpu_type=nds32
 	extra_headers="nds32_intrinsic.h nds32_isr.h nds32_init.inc"
@@ -2480,6 +2487,20 @@ riscv*-*-freebsd*)
 	# automatically detect that GAS supports it, yet we require it.
 	gcc_cv_initfini_array=yes
 	;;
+
+loongarch*-*-linux-gnu*)
+	tm_file="dbxelf.h elfos.h gnu-user.h linux.h linux-android.h glibc-stdint.h ${tm_file}"
+	tm_file="${tm_file} loongarch/gnu-user.h loongarch/linux.h"
+	extra_options="${extra_options} linux-android.opt"
+	tmake_file="${tmake_file} loongarch/t-linux"
+	gnu_ld=yes
+	gas=yes
+
+	# Force .init_array support.  The configure script cannot always
+	# automatically detect that GAS supports it, yet we require it.
+	gcc_cv_initfini_array=yes
+	;;
+
 mips*-*-netbsd*)			# NetBSD/mips, either endian.
 	target_cpu_default="MASK_ABICALLS"
 	tm_file="elfos.h ${tm_file} mips/elf.h ${nbsd_tm_file} mips/netbsd.h"
@@ -3586,7 +3607,7 @@ case ${target} in
         ;;
 *-*-linux* | *-*-gnu*)
 	case ${target} in
-	aarch64*-* | arm*-* | i[34567]86-* | powerpc*-* | s390*-* | sparc*-* | x86_64-*)
+	aarch64*-* | arm*-* | i[34567]86-* | powerpc*-* | s390*-* | sparc*-* | x86_64-* | loongarch*-*)
 		default_gnu_indirect_function=yes
 		;;
 	esac
@@ -4916,6 +4937,338 @@ case "${target}" in
 		esac
 		;;
 
+	loongarch*-*-*)
+		supported_defaults="abi arch tune fpu"
+
+		# Local variables
+		unset \
+			abi_pattern      abi_default    \
+			abiext_pattern   abiext_default \
+			arch_pattern     arch_default   \
+			fpu_pattern      fpu_default    \
+			tune_pattern     tune_default
+
+		# Inferring ABI from the triplet: pass 1
+		case ${target} in
+		loongarch64-*)
+			abiext_pattern="*"
+			abiext_default="base"
+			;;
+		esac
+
+		# Inferring ABI from the triplet: pass 2
+		case ${target} in
+		loongarch64-*-linux-gnuf64)
+			la_canonical_triplet="loongarch64-linux-gnuf64"
+			abi_pattern="lp64d"
+			;;
+		loongarch64-*-linux-gnuf32)
+			la_canonical_triplet="loongarch64-linux-gnuf32"
+			abi_pattern="lp64f"
+			;;
+		loongarch64-*-linux-gnusf)
+			la_canonical_triplet="loongarch64-linux-gnusf"
+			abi_pattern="lp64s"
+			;;
+		loongarch64-*-linux-gnu)
+			la_canonical_triplet="loongarch64-linux-gnuf64"
+			abi_pattern="lp64[dfs]"
+			abi_default="lp64d"
+			;;
+		*)
+			echo "Unsupported target ${target}." 1>&2
+			exit 1
+		esac
+
+		## Setting default value for with_abi.
+		case ${with_abi} in
+		"")
+			if test x${abi_default} != x; then
+				with_abi=${abi_default}
+			else
+				with_abi=${abi_pattern}
+			fi
+			;;
+
+		*)
+			if echo "${with_abi}" | grep -E "^${abi_pattern}$"; then
+				: # OK
+			else
+				echo "Incompatible options:" \
+				"--with-abi=${with_abi} and --target=${target}." 1>&2
+				exit 1
+			fi
+			;;
+		esac
+
+		## Setting default value for with_abiext (internal)
+		case ${with_abiext} in
+		"")
+			if test x${abiext_default} != x; then
+				with_abiext=${abiext_default}
+			else
+				with_abiext=${abiext_pattern}
+			fi
+			;;
+
+		*)
+			if echo "${with_abiext}" | grep -E "^${abiext_pattern}$"; then
+				: # OK
+			else
+				echo "The ABI extension type \"${with_abiext}\"" \
+				"is incompatible with --target=${target}." 1>&2
+				exit 1
+			fi
+
+			;;
+		esac
+
+
+		# Inferring ISA-related default options from the ABI: pass 1
+		case ${with_abi}/${with_abiext} in
+		lp64*/base)
+			# architectures that support lp64* ABI
+			arch_pattern="native|loongarch64|la464"
+			# default architecture for lp64* ABI
+			arch_default="loongarch64"
+			;;
+		*)
+			echo "Unsupported ABI type ${with_abi}/${with_abiext}." 1>&2
+			exit 1
+			;;
+		esac
+
+		# Inferring ISA-related default options from the ABI: pass 2
+		case ${with_abi}/${with_abiext} in
+		lp64d/base)
+			fpu_pattern="64"
+			;;
+		lp64f/base)
+			fpu_pattern="32|64"
+			fpu_default="32"
+			;;
+		lp64s/base)
+			fpu_pattern="none|32|64"
+			fpu_default="none"
+			;;
+		*)
+			echo "Unsupported ABI type ${with_abi}/${with_abiext}." 1>&2
+			exit 1
+			;;
+		esac
+
+		## Setting default value for with_arch.
+		case ${with_arch} in
+		"")
+			if test x${arch_default} != x; then
+				with_arch=${arch_default}
+			else
+				with_arch=${arch_pattern}
+			fi
+			;;
+
+		*)
+			if echo "${with_arch}" | grep -E "^${arch_pattern}$"; then
+				: # OK
+			else
+				echo "${with_abi}/${with_abiext} ABI cannot be implemented with" \
+				"--with-arch=${with_arch}." 1>&2
+				exit 1
+			fi
+			;;
+		esac
+
+		## Setting default value for with_fpu.
+		if test x${with_fpu} == xnone; then
+			with_fpu="0"
+		fi
+
+		case ${with_fpu} in
+		"")
+			if test x${fpu_default} != x; then
+				with_fpu=${fpu_default}
+			else
+				with_fpu=${fpu_pattern}
+			fi
+			;;
+
+		*)
+			if echo "${with_fpu}" | grep -E "^${fpu_pattern}$"; then
+				: # OK
+			else
+				echo "${with_abi}/${with_abiext} ABI cannot be implemented with" \
+				"--with-fpu=${with_fpu}." 1>&2
+				exit 1
+			fi
+			;;
+		esac
+
+
+		# Inferring default with_tune from with_arch: pass 1
+		case ${with_arch} in
+		native)
+			tune_pattern="*"
+			tune_default="native"
+			;;
+		loongarch64)
+			tune_pattern="loongarch64 | la464"
+			tune_default="la464"
+			;;
+		*)
+			# By default, $with_tune == $with_arch
+			tune_pattern="$with_arch"
+			;;
+		esac
+
+		## Setting default value for with_tune.
+		case ${with_tune} in
+		"")
+			if test x${tune_default} != x; then
+				with_tune=${tune_default}
+			else
+				with_tune=${tune_pattern}
+			fi
+			;;
+
+		*)
+			if echo "${with_tune}" | grep -E "^${tune_pattern}$"; then
+				: # OK
+			else
+				echo "Incompatible options: --with-tune=${with_tune}" \
+				"and --with-arch=${with_arch}." 1>&2
+				exit 1
+			fi
+			;;
+		esac
+
+
+		# Perform final sanity checks.
+		case ${with_arch} in
+		loongarch64 | la464) ;; # OK, append here.
+		native)
+			if test x${host} != x${target}; then
+				echo "--with-arch=native is illegal for cross-compiler." 1>&2
+				exit 1
+			fi
+			;;
+		"")
+			echo "Please set a default value for \${with_arch}" \
+			     "according to your target triplet \"${target}\"." 1>&2
+			exit 1
+			;;
+		*)
+			echo "Unknown arch in --with-arch=$with_arch" 1>&2
+			exit 1
+			;;
+		esac
+
+		case ${with_abi} in
+		lp64d | lp64f | lp64s) ;; # OK, append here.
+		*)
+			echo "Unsupported ABI given in --with-abi=$with_abi" 1>&2
+			exit 1
+			;;
+		esac
+
+		case ${with_abiext} in
+		base) ;; # OK, append here.
+		*)
+			echo "Unsupported ABI extention type $with_abiext" 1>&2
+			exit 1
+			;;
+		esac
+
+		case ${with_fpu} in
+		none | 32 | 64) ;; # OK, append here.
+		*)
+			echo "Unknown fpu type in --with-fpu=$with_fpu" 1>&2
+			exit 1
+			;;
+		esac
+
+		# Handle --with-multilib-list.
+		if test x${with_multilib_list} == x \
+		   || test x${with_multilib_list} == xno \
+		   || test x${with_multilib_list} == xdefault \
+		   || test x${enable_multilib} != xyes; then
+
+			with_multilib_list="${with_abi}/${with_abiext}"
+		fi
+
+		# Check if the configured default ABI combination is included in
+		# ${with_multilib_list}.
+		loongarch_multilib_list_sane=no
+
+		# This one goes to TM_MULTILIB_CONFIG, for use in t-linux.
+		loongarch_multilib_list_make=""
+
+		# This one goes to tm_defines, for use in loongarch-driver.c.
+		loongarch_multilib_list_c=""
+
+		for e in $(tr ',' ' ' <<< "${with_multilib_list}"); do
+			e=($(tr '/' ' ' <<< "$e"))
+
+			# Base ABI type
+			case ${e[0]} in
+			lp64d) loongarch_multilib_list_c+="ABI_BASE_LP64D,";;
+			lp64f) loongarch_multilib_list_c+="ABI_BASE_LP64F,";;
+			lp64s) loongarch_multilib_list_c+="ABI_BASE_LP64S,";;
+			*)
+				echo "Unknown base ABI \"${e[0]}\" in --with-multilib-list." 1>&2
+				exit 1
+				;;
+			esac
+			loongarch_multilib_list_make+="mabi=${e[0]}"
+
+			# ABI extension type
+			case ${e[1]} in
+			"" | base)
+				loongarch_multilib_list_make+=""
+				loongarch_multilib_list_c+="ABI_EXT_BASE,"
+				e[1]="base"
+				;;
+			*)
+				echo "Unknown ABI extension \"${e[1]}\" in --with-multilib-list." 1>&2
+				exit 1
+				;;
+			esac
+
+			case ${e[2]} in
+			"") ;; # OK
+			*)
+				echo "Unknown ABI in --with-multilib-list." 1>&2
+				exit 1
+				;;
+			esac
+
+			if test x${with_abi} != x && test x${with_abiext} != x; then
+				if test x${e[0]} == x${with_abi} \
+				&& test x${e[1]} == x${with_abiext}; then
+					loongarch_multilib_list_sane=yes
+				fi
+			fi
+
+			loongarch_multilib_list_make+=","
+		done
+
+		# Check if the default ABI combination is in the default list.
+		if test x${loongarch_multilib_list_sane} == xno; then
+			if test x${with_abiext} == xbase; then
+				with_abiext=""
+			else
+				with_abiext="/${with_abiext}"
+			fi
+
+			echo "Default ABI combination (${with_abi}${with_abiext})" \
+			"not found in --with-multilib-list." 1>&2
+			exit 1
+		fi
+
+		# Remove the excessive appending comma.
+		loongarch_multilib_list_c=${loongarch_multilib_list_c:0:-1}
+		loongarch_multilib_list_make=${loongarch_multilib_list_make:0:-1}
+		;;
+
 	nds32*-*-*)
 		supported_defaults="arch cpu nds32_lib float fpu_config"
 
@@ -5353,6 +5706,51 @@ case ${target} in
 		tmake_file="mips/t-mips $tmake_file"
 		;;
 
+	loongarch*-*-*)
+		# Export canonical triplet.
+		tm_defines+=" LA_MULTIARCH_TRIPLET=${la_canonical_triplet}"
+
+		# Define macro __DISABLE_MULTILIB if --disable-multilib
+		tm_defines+=" TM_MULTILIB_LIST=${loongarch_multilib_list_c}"
+		if test x$enable_multilib == xyes; then
+			TM_MULTILIB_CONFIG="${loongarch_multilib_list_make}"
+		else
+			tm_defines+=" __DISABLE_MULTILIB"
+		fi
+
+		# Let --with- flags initialize the enum variables from loongarch.opt.
+		# See macro definitions from loongarch-opts.h and loongarch-cpu.h.
+		case ${with_arch} in
+		native)		tm_defines+=" DEFAULT_CPU_ARCH=CPU_NATIVE" ;;
+		la464)		tm_defines+=" DEFAULT_CPU_ARCH=CPU_LA464" ;;
+		loongarch64)	tm_defines+=" DEFAULT_CPU_ARCH=CPU_LOONGARCH64" ;;
+		esac
+
+		case ${with_tune} in
+		native)		tm_defines+=" DEFAULT_CPU_TUNE=CPU_NATIVE" ;;
+		la464)		tm_defines+=" DEFAULT_CPU_TUNE=CPU_LA464" ;;
+		loongarch64)	tm_defines+=" DEFAULT_CPU_TUNE=CPU_LOONGARCH64" ;;
+		esac
+
+		case ${with_abi} in
+		lp64d)     tm_defines+=" DEFAULT_ABI_BASE=ABI_BASE_LP64D" ;;
+		lp64f)     tm_defines+=" DEFAULT_ABI_BASE=ABI_BASE_LP64F" ;;
+		lp64s)     tm_defines+=" DEFAULT_ABI_BASE=ABI_BASE_LP64S" ;;
+		esac
+
+		case ${with_abiext} in
+		base)      tm_defines+=" DEFAULT_ABI_EXT=ABI_EXT_BASE" ;;
+		esac
+
+		case ${with_fpu} in
+		none)    tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_NOFPU" ;;
+		32)      tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_FPU32" ;;
+		64)      tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_FPU64" ;;
+		esac
+
+		tmake_file="loongarch/t-loongarch $tmake_file"
+		;;
+
 	powerpc*-*-* | rs6000-*-*)
 		# FIXME: The PowerPC port uses the value set at compile time,
 		# although it's only cosmetic.
diff --git a/gcc/config/loongarch/genopts/genstr.sh b/gcc/config/loongarch/genopts/genstr.sh
new file mode 100755
index 00000000000..62a0a0770a2
--- /dev/null
+++ b/gcc/config/loongarch/genopts/genstr.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+# A simple script that generates loongarch-str.h and loongarch.opt
+# from genopt/loongarch-optstr.
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC 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 3, or (at your option) any later
+# version.
+#
+# GCC 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+cd "$(dirname "$0")"
+
+# Generate a header containing definitions from the string table.
+gen_defines() {
+    cat <<EOF
+/* Generated automatically by "genstr" from "loongarch-strings".
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_STR_H
+#define LOONGARCH_STR_H
+EOF
+
+    sed -e '/^$/n' -e 's@#.*$@@' -e '/^$/d' \
+	-e 's@^\([^ \t]\+\)[ \t]*\([^ \t]*\)@#define \1 "\2"@' \
+	loongarch-strings
+
+    echo
+    echo "#endif /* LOONGARCH_STR_H */"
+}
+
+
+# Substitute all "@@<KEY>@@" to "<VALUE>" in loongarch.opt.in
+# according to the key-value pairs defined in loongarch-strings.
+
+gen_options() {
+
+    sed -e '/^$/n' -e 's@#.*$@@' -e '/^$/d' \
+	-e 's@^\([^ \t]\+\)[ \t]*\([^ \t]*\)@\1="\2"@' \
+	loongarch-strings | { \
+
+	# read the definitions
+	while read -r line; do
+	    eval "$line"
+	done
+
+	# make the substitutions
+	sed -e 's@"@\\"@g' -e 's/@@\([^@]\+\)@@/${\1}/g' loongarch.opt.in | \
+	    while read -r line; do
+		eval "echo \"$line\""
+	    done
+    }
+}
+
+main() {
+    case "$1" in
+	header) gen_defines;;
+	opt) gen_options;;
+	*) echo "Unknown Command: \"$1\". Available: header, opt"; exit 1;;
+    esac
+}
+
+main "$@"
diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings
new file mode 100644
index 00000000000..553304f9b83
--- /dev/null
+++ b/gcc/config/loongarch/genopts/loongarch-strings
@@ -0,0 +1,58 @@
+# Defines the key strings for LoongArch compiler options.
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC 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 3, or (at your option) any later
+# version.
+#
+# GCC 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# -march= / -mtune=
+OPTSTR_ARCH	      arch
+OPTSTR_TUNE	      tune
+
+STR_CPU_NATIVE	      native
+STR_CPU_LOONGARCH64   loongarch64
+STR_CPU_LA464	      la464
+
+# Base architecture
+STR_ISA_BASE_LA64V100 la64
+
+# -mfpu
+OPTSTR_ISA_EXT_FPU    fpu
+STR_ISA_EXT_NOFPU     none
+STR_ISA_EXT_FPU0      0
+STR_ISA_EXT_FPU32     32
+STR_ISA_EXT_FPU64     64
+
+OPTSTR_SOFT_FLOAT     soft-float
+OPTSTR_SINGLE_FLOAT   single-float
+OPTSTR_DOUBLE_FLOAT   double-float
+
+# -mabi=
+OPTSTR_ABI_BASE	      abi
+STR_ABI_BASE_LP64D    lp64d
+STR_ABI_BASE_LP64F    lp64f
+STR_ABI_BASE_LP64S    lp64s
+
+# ABI extension types
+STR_ABI_EXT_BASE      base
+
+# -mcmodel=
+OPTSTR_CMODEL	      cmodel
+STR_CMODEL_NORMAL     normal
+STR_CMODEL_TINY	      tiny
+STR_CMODEL_TS	      tiny-static
+STR_CMODEL_LARGE      large
+STR_CMODEL_EXTREME    extreme
diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
new file mode 100644
index 00000000000..08950a32179
--- /dev/null
+++ b/gcc/config/loongarch/genopts/loongarch.opt.in
@@ -0,0 +1,189 @@
+; Generated by "genstr" from the template "loongarch.opt.in"
+; and definitions from "loongarch-strings".
+;
+; Copyright (C) 2021 Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC 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 3, or (at your option) any later
+; version.
+;
+; GCC 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 GCC; see the file COPYING3.  If not see
+; <http://www.gnu.org/licenses/>.
+;
+
+; Variables (macros) that should be exported by loongarch.opt:
+;   la_opt_switches,
+;   la_opt_abi_base, la_opt_abi_ext,
+;   la_opt_cpu_arch, la_opt_cpu_tune,
+;   la_opt_fpu,
+;   la_cmodel.
+
+HeaderInclude
+config/loongarch/loongarch-opts.h
+
+HeaderInclude
+config/loongarch/loongarch-str.h
+
+Variable
+HOST_WIDE_INT la_opt_switches = 0
+
+; ISA related options
+;; Base ISA
+Enum
+Name(isa_base) Type(int)
+Basic ISAs of LoongArch:
+
+EnumValue
+Enum(isa_base) String(@@STR_ISA_BASE_LA64V100@@) Value(ISA_BASE_LA64V100)
+
+
+;; ISA extensions / adjustments
+Enum
+Name(isa_ext_fpu) Type(int)
+FPU types of LoongArch:
+
+EnumValue
+Enum(isa_ext_fpu) String(@@STR_ISA_EXT_NOFPU@@) Value(ISA_EXT_NOFPU)
+
+EnumValue
+Enum(isa_ext_fpu) String(@@STR_ISA_EXT_FPU32@@) Value(ISA_EXT_FPU32)
+
+EnumValue
+Enum(isa_ext_fpu) String(@@STR_ISA_EXT_FPU64@@) Value(ISA_EXT_FPU64)
+
+m@@OPTSTR_ISA_EXT_FPU@@=
+Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPTION_NOT_SEEN)
+-m@@OPTSTR_ISA_EXT_FPU@@=FPU	Generate code for the given FPU.
+
+m@@OPTSTR_ISA_EXT_FPU@@=@@STR_ISA_EXT_FPU0@@
+Target RejectNegative Alias(m@@OPTSTR_ISA_EXT_FPU@@=,@@STR_ISA_EXT_NOFPU@@)
+
+m@@OPTSTR_SOFT_FLOAT@@
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_SOFTF) Negative(m@@OPTSTR_SINGLE_FLOAT@@)
+Prevent the use of all hardware floating-point instructions.
+
+m@@OPTSTR_SINGLE_FLOAT@@
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F32) Negative(m@@OPTSTR_DOUBLE_FLOAT@@)
+Restrict the use of hardware floating-point instructions to 32-bit operations.
+
+m@@OPTSTR_DOUBLE_FLOAT@@
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F64) Negative(m@@OPTSTR_SOFT_FLOAT@@)
+Allow hardware floating-point instructions to cover both 32-bit and 64-bit operations.
+
+
+;; Base target models (implies ISA & tune parameters)
+Enum
+Name(cpu_type) Type(int)
+LoongArch CPU types:
+
+EnumValue
+Enum(cpu_type) String(@@STR_CPU_NATIVE@@) Value(CPU_NATIVE)
+
+EnumValue
+Enum(cpu_type) String(@@STR_CPU_LOONGARCH64@@) Value(CPU_LOONGARCH64)
+
+EnumValue
+Enum(cpu_type) String(@@STR_CPU_LA464@@) Value(CPU_LA464)
+
+m@@OPTSTR_ARCH@@=
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPTION_NOT_SEEN)
+-m@@OPTSTR_ARCH@@=PROCESSOR	Generate code for the given PROCESSOR ISA.
+
+m@@OPTSTR_TUNE@@=
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPTION_NOT_SEEN)
+-m@@OPTSTR_TUNE@@=PROCESSOR	Generate optimized code for PROCESSOR.
+
+
+; ABI related options
+; (ISA constraints on ABI are handled dynamically)
+
+;; Base ABI
+Enum
+Name(abi_base) Type(int)
+Base ABI types for LoongArch:
+
+EnumValue
+Enum(abi_base) String(@@STR_ABI_BASE_LP64D@@) Value(ABI_BASE_LP64D)
+
+EnumValue
+Enum(abi_base) String(@@STR_ABI_BASE_LP64F@@) Value(ABI_BASE_LP64F)
+
+EnumValue
+Enum(abi_base) String(@@STR_ABI_BASE_LP64S@@) Value(ABI_BASE_LP64S)
+
+m@@OPTSTR_ABI_BASE@@=
+Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPTION_NOT_SEEN)
+-m@@OPTSTR_ABI_BASE@@=BASEABI	Generate code that conforms to the given BASEABI.
+
+;; ABI Extension
+Variable
+int la_opt_abi_ext = M_OPTION_NOT_SEEN
+
+
+mbranch-cost=
+Target RejectNegative Joined UInteger Var(loongarch_branch_cost)
+-mbranch-cost=COST	Set the cost of branches to roughly COST instructions.
+
+mcheck-zero-division
+Target Mask(CHECK_ZERO_DIV)
+Trap on integer divide by zero.
+
+mcond-move-int
+Target Var(TARGET_COND_MOVE_INT) Init(1)
+Conditional moves for integral are enabled.
+
+mcond-move-float
+Target Var(TARGET_COND_MOVE_FLOAT) Init(1)
+Conditional moves for float are enabled.
+
+mmemcpy
+Target Mask(MEMCPY)
+Don't optimize block moves.
+
+mlra
+Target Var(loongarch_lra_flag) Init(1) Save
+Use LRA instead of reload.
+
+noasmopt
+Driver
+
+mstrict-align
+Target Mask(STRICT_ALIGN) Save
+Do not generate unaligned memory accesses.
+
+mmax-inline-memcpy-size=
+Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024)
+-mmax-inline-memcpy-size=SIZE	Set the max size of memcpy to inline, default is 1024.
+
+; The code model option names for -mcmodel.
+Enum
+Name(cmodel) Type(int)
+The code model option names for -mcmodel:
+
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_NORMAL@@) Value(CMODEL_NORMAL)
+
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_TINY@@) Value(CMODEL_TINY)
+
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_TS@@) Value(CMODEL_TINY_STATIC)
+
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_LARGE@@) Value(CMODEL_LARGE)
+
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_EXTREME@@) Value(CMODEL_EXTREME)
+
+mcmodel=
+Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(CMODEL_NORMAL)
+Specify the code model.
diff --git a/gcc/config/loongarch/gnu-user.h b/gcc/config/loongarch/gnu-user.h
new file mode 100644
index 00000000000..6f3137f7eef
--- /dev/null
+++ b/gcc/config/loongarch/gnu-user.h
@@ -0,0 +1,78 @@
+/* Definitions for LoongArch systems using GNU userspace.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Define the size of the wide character type.  */
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+
+/* GNU-specific SPEC definitions.  */
+#define GNU_USER_LINK_EMULATION "elf" ABI_GRLEN_SPEC "loongarch"
+
+#define GLIBC_DYNAMIC_LINKER \
+  "/lib" ABI_GRLEN_SPEC "/ld-linux-loongarch-" ABI_SPEC ".so.1"
+
+#undef GNU_USER_TARGET_LINK_SPEC
+#define GNU_USER_TARGET_LINK_SPEC \
+  "%{G*} %{shared} -m " GNU_USER_LINK_EMULATION \
+  "%{!shared: %{static} %{!static: %{rdynamic:-export-dynamic} " \
+  "-dynamic-linker " GLIBC_DYNAMIC_LINKER "}}"
+
+
+/* Similar to standard Linux, but adding -ffast-math support.  */
+#undef GNU_USER_TARGET_MATHFILE_SPEC
+#define GNU_USER_TARGET_MATHFILE_SPEC \
+  "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s}"
+
+#undef LIB_SPEC
+#define LIB_SPEC GNU_USER_TARGET_LIB_SPEC
+
+#undef LINK_SPEC
+#define LINK_SPEC GNU_USER_TARGET_LINK_SPEC
+
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC \
+  GNU_USER_TARGET_MATHFILE_SPEC " " \
+  GNU_USER_TARGET_ENDFILE_SPEC
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}"
+
+/* A standard GNU/Linux mapping.  On most targets, it is included in
+   CC1_SPEC itself by config/linux.h, but loongarch.h overrides CC1_SPEC
+   and provides this hook instead.  */
+#undef SUBTARGET_CC1_SPEC
+#define SUBTARGET_CC1_SPEC GNU_USER_TARGET_CC1_SPEC
+
+#define TARGET_OS_CPP_BUILTINS() \
+  do \
+    { \
+      GNU_USER_TARGET_OS_CPP_BUILTINS (); \
+      /* The GNU C++ standard library requires this.  */ \
+      if (c_dialect_cxx ()) \
+       builtin_define ("_GNU_SOURCE"); \
+    } \
+  while (0)
+
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME loongarch_declare_object_name
diff --git a/gcc/config/loongarch/linux.h b/gcc/config/loongarch/linux.h
new file mode 100644
index 00000000000..453429fda97
--- /dev/null
+++ b/gcc/config/loongarch/linux.h
@@ -0,0 +1,50 @@
+/* Definitions for Linux-based systems with libraries in ELF format.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Default system library search paths.
+ * This ensures that a compiler configured with --disable-multilib
+ * can work in an multilib environment.  */
+
+#if defined(__DISABLE_MULTILIB) && defined(__DISABLE_MULTIARCH)
+
+  #if DEFAULT_ABI_INT == ABI_BASE_LP64D
+    #define ABI_LIBDIR "lib64"
+  #elif DEFAULT_ABI_INT == ABI_BASE_LP64F
+    #define ABI_LIBDIR "lib64/f32"
+  #elif DEFAULT_ABI_INT == ABI_BASE_LP64S
+    #define ABI_LIBDIR "lib64/sf"
+  #endif
+
+#endif
+
+#ifndef ABI_LIBDIR
+#define ABI_LIBDIR "lib"
+#endif
+
+#define STANDARD_STARTFILE_PREFIX_1 "/" ABI_LIBDIR "/"
+#define STANDARD_STARTFILE_PREFIX_2 "/usr/" ABI_LIBDIR "/"
+
+
+/* Define this to be nonzero if static stack checking is supported.  */
+#define STACK_CHECK_STATIC_BUILTIN 1
+
+/* The default value isn't sufficient in 64-bit mode.  */
+#define STACK_CHECK_PROTECT (TARGET_64BIT ? 16 * 1024 : 12 * 1024)
+
+#define TARGET_ASM_FILE_END file_end_indicate_exec_stack
diff --git a/gcc/config/loongarch/loongarch-cpu.c b/gcc/config/loongarch/loongarch-cpu.c
new file mode 100644
index 00000000000..384fb54392c
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-cpu.c
@@ -0,0 +1,206 @@
+/* Definitions for LoongArch CPU properties.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "diagnostic-core.h"
+
+#include "loongarch-opts.h"
+#include "loongarch-cpu.h"
+#include "loongarch-str.h"
+
+/* Native CPU detection with "cpucfg" */
+#define N_CPUCFG_WORDS 0x15
+static uint32_t cpucfg_cache[N_CPUCFG_WORDS] = { 0 };
+static const int cpucfg_useful_idx[] = {0, 1, 2, 16, 17, 18, 19};
+
+static uint32_t
+read_cpucfg_word (int wordno)
+{
+  /* To make cross-compiler shut up.  */
+  (void) wordno;
+  uint32_t ret = 0;
+
+  #ifdef __loongarch__
+  __asm__ __volatile__ ("cpucfg %0,%1\n\t"
+			:"=r"(ret)
+			:"r"(wordno)
+			:);
+  #endif
+
+  return ret;
+}
+
+void
+cache_cpucfg (void)
+{
+  for (unsigned int i = 0; i < sizeof (cpucfg_useful_idx) / sizeof (int); i++)
+    {
+      cpucfg_cache[cpucfg_useful_idx[i]]
+	= read_cpucfg_word (cpucfg_useful_idx[i]);
+    }
+}
+
+uint32_t
+get_native_prid (void)
+{
+  /* Fill loongarch_cpu_default_config[CPU_NATIVE] with cpucfg data,
+     see "Loongson Architecture Reference Manual"
+     (Volume 1, Section 2.2.10.5) */
+  return cpucfg_cache[0];
+}
+
+const char*
+get_native_prid_str (void)
+{
+  static char prid_str[9];
+  sprintf (prid_str, "%08x", cpucfg_cache[0]);
+  return (const char*) prid_str;
+}
+
+/* Fill property tables for CPU_NATIVE.  */
+unsigned int
+fill_native_cpu_config (int p_arch_native, int p_tune_native)
+{
+  int ret_cpu_type;
+
+  /* Nothing needs to be done unless "-march/tune=native"
+     is given or implied.  */
+  if (!(p_arch_native || p_tune_native))
+    return CPU_NATIVE;
+
+  /* Fill cpucfg_cache with the "cpucfg" instruction.  */
+  cache_cpucfg ();
+
+
+  /* Fill: loongarch_cpu_default_isa[CPU_NATIVE].base
+     With: base architecture (ARCH)
+     At:   cpucfg_words[1][1:0] */
+
+  #define NATIVE_BASE_ISA (loongarch_cpu_default_isa[CPU_NATIVE].base)
+  switch (cpucfg_cache[1] & 0x3)
+    {
+      case 0x02:
+	NATIVE_BASE_ISA = ISA_BASE_LA64V100;
+	break;
+
+      default:
+	if (p_arch_native)
+	  fatal_error (UNKNOWN_LOCATION,
+		       "unknown base architecture %<0x%x%>, %qs failed",
+		       (unsigned int) (cpucfg_cache[1] & 0x3),
+		       "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
+    }
+
+  /* Fill: loongarch_cpu_default_isa[CPU_NATIVE].fpu
+     With: FPU type (FP, FP_SP, FP_DP)
+     At:   cpucfg_words[2][2:0] */
+
+  #define NATIVE_FPU (loongarch_cpu_default_isa[CPU_NATIVE].fpu)
+  switch (cpucfg_cache[2] & 0x7)
+    {
+      case 0x07:
+	NATIVE_FPU = ISA_EXT_FPU64;
+	break;
+
+      case 0x03:
+	NATIVE_FPU = ISA_EXT_FPU32;
+	break;
+
+      case 0x00:
+	NATIVE_FPU = ISA_EXT_NOFPU;
+	break;
+
+      default:
+	if (p_arch_native)
+	  fatal_error (UNKNOWN_LOCATION,
+		       "unknown FPU type %<0x%x%>, %qs failed",
+		       (unsigned int) (cpucfg_cache[2] & 0x7),
+		       "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
+    }
+
+  /* Fill: loongarch_cpu_cache[CPU_NATIVE]
+     With: cache size info
+     At:   cpucfg_words[16:20][31:0] */
+
+  int l1d_present = 0, l1u_present = 0;
+  int l2d_present = 0;
+  uint32_t l1_szword, l2_szword;
+
+  l1u_present |= cpucfg_cache[16] & 3;	      /* bit[1:0]: unified l1 cache */
+  l1d_present |= cpucfg_cache[16] & 4;	      /* bit[2:2]: l1 dcache */
+  l1_szword = l1d_present ? 18 : (l1u_present ? 17 : 0);
+  l1_szword = l1_szword ? cpucfg_cache[l1_szword]: 0;
+
+  l2d_present |= cpucfg_cache[16] & 24;	      /* bit[4:3]: unified l2 cache */
+  l2d_present |= cpucfg_cache[16] & 128;      /* bit[7:7]: l2 dcache */
+  l2_szword = l2d_present ? cpucfg_cache[19]: 0;
+
+  loongarch_cpu_cache[CPU_NATIVE].l1d_line_size
+    = 1 << ((l1_szword & 0x7f000000) >> 24);  /* bit[30:24]: log2(linesize) */
+
+  loongarch_cpu_cache[CPU_NATIVE].l1d_size
+    = (1 << ((l1_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
+    * ((l1_szword & 0x0000ffff) + 1)	      /* bit[15:0]:  sets - 1 */
+    * (1 << ((l1_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(linesize) */
+    >> 10;				      /* in kilobytes */
+
+  loongarch_cpu_cache[CPU_NATIVE].l2d_size
+    = (1 << ((l2_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
+    * ((l2_szword & 0x0000ffff) + 1)	      /* bit[15:0]:  sets - 1 */
+    * (1 << ((l2_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(linesize) */
+    >> 10;				      /* in kilobytes */
+
+  /* Fill: ret_cpu_type
+     With: processor ID (PRID)
+     At:   cpucfg_words[0][31:0] */
+
+  switch (cpucfg_cache[0] & 0x00ffff00)
+  {
+    case 0x0014c000:   /* LA464 */
+      ret_cpu_type = CPU_LA464;
+      break;
+
+    default:
+      /* Unknown PRID.  This is generally harmless as long as
+	 the properties above can be obtained via "cpucfg".  */
+      if (p_tune_native)
+	inform (UNKNOWN_LOCATION, "unknown processor ID %<0x%x%>, "
+		"some tuning parameters will fall back to default",
+		cpucfg_cache[0]);
+      break;
+  }
+
+  /* Properties that cannot be looked up directly using cpucfg.  */
+  loongarch_cpu_issue_rate[CPU_NATIVE]
+    = loongarch_cpu_issue_rate[ret_cpu_type];
+
+  loongarch_cpu_multipass_dfa_lookahead[CPU_NATIVE]
+    = loongarch_cpu_multipass_dfa_lookahead[ret_cpu_type];
+
+  loongarch_cpu_rtx_cost_data[CPU_NATIVE]
+    = loongarch_cpu_rtx_cost_data[ret_cpu_type];
+
+  return ret_cpu_type;
+}
diff --git a/gcc/config/loongarch/loongarch-cpu.h b/gcc/config/loongarch/loongarch-cpu.h
new file mode 100644
index 00000000000..93d656f70f5
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-cpu.h
@@ -0,0 +1,30 @@
+/* Definitions for loongarch native cpu property detection routines.
+   Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_CPU_H
+#define LOONGARCH_CPU_H
+
+#include "system.h"
+
+void cache_cpucfg (void);
+unsigned int fill_native_cpu_config (int p_arch_native, int p_tune_native);
+uint32_t get_native_prid (void);
+const char* get_native_prid_str (void);
+
+#endif /* LOONGARCH_CPU_H */
diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c
new file mode 100644
index 00000000000..bc22a55edb0
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-def.c
@@ -0,0 +1,164 @@
+/* LoongArch static properties.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "loongarch-def.h"
+#include "loongarch-str.h"
+
+/* CPU property tables.  */
+const char*
+loongarch_cpu_strings[N_TUNE_TYPES] = {
+  [CPU_NATIVE]		  = STR_CPU_NATIVE,
+  [CPU_LOONGARCH64]	  = STR_CPU_LOONGARCH64,
+  [CPU_LA464]		  = STR_CPU_LA464,
+};
+
+struct loongarch_isa
+loongarch_cpu_default_isa[N_ARCH_TYPES] = {
+  [CPU_LOONGARCH64] = {
+      .base = ISA_BASE_LA64V100,
+      .fpu = ISA_EXT_FPU64,
+  },
+  [CPU_LA464] = {
+      .base = ISA_BASE_LA64V100,
+      .fpu = ISA_EXT_FPU64,
+  },
+};
+
+struct loongarch_cache
+loongarch_cpu_cache[N_TUNE_TYPES] = {
+  [CPU_LOONGARCH64] = {
+      .l1d_line_size = 64,
+      .l1d_size = 64,
+      .l2d_size = 256,
+  },
+  [CPU_LA464] = {
+      .l1d_line_size = 64,
+      .l1d_size = 64,
+      .l2d_size = 256,
+  },
+};
+
+/* The following properties cannot be looked up directly using "cpucfg".
+ So it is necessary to provide a default value for "unknown native"
+ tune targets (i.e. -mtune=native while PRID does not correspond to
+ any known "-mtune" type).  */
+
+struct loongarch_rtx_cost_data
+loongarch_cpu_rtx_cost_data[N_TUNE_TYPES] = {
+  [CPU_NATIVE] = {
+      DEFAULT_COSTS
+  },
+  [CPU_LOONGARCH64] = {
+      DEFAULT_COSTS
+  },
+  [CPU_LA464] = {
+      DEFAULT_COSTS
+  },
+};
+
+/* RTX costs to use when optimizing for size.  */
+extern const struct loongarch_rtx_cost_data
+loongarch_rtx_cost_optimize_size = {
+    .fp_add	      = 4,
+    .fp_mult_sf	      = 4,
+    .fp_mult_df	      = 4,
+    .fp_div_sf	      = 4,
+    .fp_div_df	      = 4,
+    .int_mult_si      = 4,
+    .int_mult_di      = 4,
+    .int_div_si	      = 4,
+    .int_div_di	      = 4,
+    .branch_cost      = 2,
+    .memory_latency   = 4,
+};
+
+int
+loongarch_cpu_issue_rate[N_TUNE_TYPES] = {
+  [CPU_NATIVE]	      = 4,
+  [CPU_LOONGARCH64]   = 4,
+  [CPU_LA464]	      = 4,
+};
+
+int
+loongarch_cpu_multipass_dfa_lookahead[N_TUNE_TYPES] = {
+  [CPU_NATIVE]	      = 4,
+  [CPU_LOONGARCH64]   = 4,
+  [CPU_LA464]	      = 4,
+};
+
+/* Wiring string definitions from loongarch-str.h to global arrays
+   with standard index values from loongarch-opts.h, so we can
+   print config-related messages and do ABI self-spec filtering
+   from the driver in a self-consistent manner.  */
+
+const char*
+loongarch_isa_base_strings[N_ISA_BASE_TYPES] = {
+  [ISA_BASE_LA64V100] = STR_ISA_BASE_LA64V100,
+};
+
+const char*
+loongarch_isa_ext_strings[N_ISA_EXT_TYPES] = {
+  [ISA_EXT_FPU64] = STR_ISA_EXT_FPU64,
+  [ISA_EXT_FPU32] = STR_ISA_EXT_FPU32,
+  [ISA_EXT_NOFPU] = STR_ISA_EXT_NOFPU,
+};
+
+const char*
+loongarch_abi_base_strings[N_ABI_BASE_TYPES] = {
+  [ABI_BASE_LP64D] = STR_ABI_BASE_LP64D,
+  [ABI_BASE_LP64F] = STR_ABI_BASE_LP64F,
+  [ABI_BASE_LP64S] = STR_ABI_BASE_LP64S,
+};
+
+const char*
+loongarch_abi_ext_strings[N_ABI_EXT_TYPES] = {
+  [ABI_EXT_BASE] = STR_ABI_EXT_BASE,
+};
+
+const char*
+loongarch_cmodel_strings[] = {
+  [CMODEL_NORMAL]	  = STR_CMODEL_NORMAL,
+  [CMODEL_TINY]		  = STR_CMODEL_TINY,
+  [CMODEL_TINY_STATIC]	  = STR_CMODEL_TS,
+  [CMODEL_LARGE]	  = STR_CMODEL_LARGE,
+  [CMODEL_EXTREME]	  = STR_CMODEL_EXTREME,
+};
+
+const char*
+loongarch_switch_strings[] = {
+  [SW_SOFT_FLOAT]	  = OPTSTR_SOFT_FLOAT,
+  [SW_SINGLE_FLOAT]	  = OPTSTR_SINGLE_FLOAT,
+  [SW_DOUBLE_FLOAT]	  = OPTSTR_DOUBLE_FLOAT,
+};
+
+
+/* ABI-related definitions.  */
+const struct loongarch_isa
+abi_minimal_isa[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES] = {
+  [ABI_BASE_LP64D] = {
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU64},
+  },
+  [ABI_BASE_LP64F] = {
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU32},
+  },
+  [ABI_BASE_LP64S] = {
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_NOFPU},
+  },
+};
diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h
new file mode 100644
index 00000000000..84c27271a69
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-def.h
@@ -0,0 +1,151 @@
+/* LoongArch definitions.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Definition of standard codes for:
+    - base architecture types	(isa_base),
+    - ISA extensions		(isa_ext),
+    - base ABI types		(abi_base),
+    - ABI extension types	(abi_ext).
+
+    - code models		      (cmodel)
+    - other command-line switches     (switch)
+
+   These values are primarily used for implementing option handling
+   logic in "loongarch.opt", "loongarch-driver.c" and "loongarch-opt.c".
+
+   As for the result of this option handling process, the following
+   scheme is adopted to represent the final configuration:
+
+    - The target ABI is encoded with a tuple (abi_base, abi_ext)
+      using the code defined below.
+
+    - The target ISA is encoded with a "struct loongarch_isa" defined
+      in loongarch-cpu.h.
+
+    - The target microarchitecture is represented with a cpu model
+      index defined in loongarch-cpu.h.
+*/
+
+#ifndef LOONGARCH_DEF_H
+#define LOONGARCH_DEF_H
+
+#include "loongarch-tune.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* enum isa_base */
+extern const char* loongarch_isa_base_strings[];
+#define ISA_BASE_LA64V100     0
+#define N_ISA_BASE_TYPES      1
+
+/* enum isa_ext_* */
+extern const char* loongarch_isa_ext_strings[];
+#define ISA_EXT_NOFPU	      0
+#define ISA_EXT_FPU32	      1
+#define ISA_EXT_FPU64	      2
+#define N_ISA_EXT_FPU_TYPES   3
+#define N_ISA_EXT_TYPES	      3
+
+/* enum abi_base */
+extern const char* loongarch_abi_base_strings[];
+#define ABI_BASE_LP64D	      0
+#define ABI_BASE_LP64F	      1
+#define ABI_BASE_LP64S	      2
+#define N_ABI_BASE_TYPES      3
+
+/* enum abi_ext */
+extern const char* loongarch_abi_ext_strings[];
+#define ABI_EXT_BASE	      0
+#define N_ABI_EXT_TYPES	      1
+
+/* enum cmodel */
+extern const char* loongarch_cmodel_strings[];
+#define CMODEL_NORMAL	      0
+#define CMODEL_TINY	      1
+#define CMODEL_TINY_STATIC    2
+#define CMODEL_LARGE	      3
+#define CMODEL_EXTREME	      4
+#define N_CMODEL_TYPES	      5
+
+/* enum switches */
+/* The "SW_" codes represent command-line switches (options that
+   accept no parameters). Definition for other switches that affects
+   the target ISA / ABI configuration will also be appended here
+   in the future.  */
+
+extern const char* loongarch_switch_strings[];
+#define SW_SOFT_FLOAT	      0
+#define SW_SINGLE_FLOAT	      1
+#define SW_DOUBLE_FLOAT	      2
+#define N_SWITCH_TYPES	      3
+
+/* The common default value for variables whose assignments
+   are triggered by command-line options.  */
+
+#define M_OPTION_NOT_SEEN -1
+#define M_OPT_ABSENT(opt_enum)  ((opt_enum) == M_OPTION_NOT_SEEN)
+
+
+/* Internal representation of the target.  */
+struct loongarch_isa
+{
+  unsigned char base;	    /* ISA_BASE_ */
+  unsigned char fpu;	    /* ISA_EXT_FPU_ */
+};
+
+struct loongarch_abi
+{
+  unsigned char base;	    /* ABI_BASE_ */
+  unsigned char ext;	    /* ABI_EXT_ */
+};
+
+struct loongarch_target
+{
+  struct loongarch_isa isa;
+  struct loongarch_abi abi;
+  unsigned char cpu_arch;   /* CPU_ */
+  unsigned char cpu_tune;   /* same */
+  unsigned char cpu_native; /* same */
+  unsigned char cmodel;	    /* CMODEL_ */
+};
+
+/* CPU properties.  */
+/* index */
+#define CPU_NATIVE	  0
+#define CPU_LOONGARCH64	  1
+#define CPU_LA464	  2
+#define N_ARCH_TYPES	  3
+#define N_TUNE_TYPES	  3
+
+/* parallel tables.  */
+extern const char* loongarch_cpu_strings[];
+extern struct loongarch_isa loongarch_cpu_default_isa[];
+extern int loongarch_cpu_issue_rate[];
+extern int loongarch_cpu_multipass_dfa_lookahead[];
+
+extern struct loongarch_cache loongarch_cpu_cache[];
+extern struct loongarch_rtx_cost_data loongarch_cpu_rtx_cost_data[];
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LOONGARCH_DEF_H */
diff --git a/gcc/config/loongarch/loongarch-driver.c b/gcc/config/loongarch/loongarch-driver.c
new file mode 100644
index 00000000000..8d78e2eb98b
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-driver.c
@@ -0,0 +1,187 @@
+/* Subroutines for the gcc driver.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "obstack.h"
+#include "diagnostic-core.h"
+
+#include "loongarch-opts.h"
+#include "loongarch-driver.h"
+
+static int
+  opt_arch_driver = M_OPTION_NOT_SEEN,
+  opt_tune_driver = M_OPTION_NOT_SEEN,
+  opt_fpu_driver = M_OPTION_NOT_SEEN,
+  opt_abi_base_driver = M_OPTION_NOT_SEEN,
+  opt_abi_ext_driver = M_OPTION_NOT_SEEN,
+  opt_cmodel_driver = M_OPTION_NOT_SEEN;
+
+int opt_switches = 0;
+
+/* This flag is set to 1 if we believe that the user might be avoiding
+   linking (implicitly) against something from the startfile search paths.  */
+static int no_link = 0;
+
+#define LARCH_DRIVER_SET_M_FLAG(OPTS_ARRAY, N_OPTS, FLAG, STR)	\
+  for (int i = 0; i < (N_OPTS); i++)				\
+  {								\
+    if ((OPTS_ARRAY)[i] != 0)					\
+      if (strcmp ((STR), (OPTS_ARRAY)[i]) == 0)			\
+	(FLAG) = i;						\
+  }
+
+/* Use the public obstack from the gcc driver (defined in gcc.c).
+   This is for allocating space for the returned string.  */
+extern struct obstack opts_obstack;
+
+#define APPEND_LTR(S)				      \
+  obstack_grow (&opts_obstack, (const void*) (S),     \
+		sizeof ((S)) / sizeof (char) -1)
+
+#define APPEND_VAL(S) \
+  obstack_grow (&opts_obstack, (const void*) (S), strlen ((S)))
+
+
+const char*
+driver_set_m_flag (int argc, const char **argv)
+{
+  int parm_off = 0;
+
+  if (argc != 1)
+    return "%eset_m_flag requires exactly 1 argument.";
+
+#undef PARM
+#define PARM (argv[0] + parm_off)
+
+/* Note: sizeof (OPTSTR_##NAME) equals the length of "<option>=".  */
+#undef MATCH_OPT
+#define MATCH_OPT(NAME) \
+  (strncmp (argv[0], OPTSTR_##NAME "=", \
+	    (parm_off = sizeof (OPTSTR_##NAME))) == 0)
+
+  if (strcmp (argv[0], "no_link") == 0)
+    {
+      no_link = 1;
+    }
+  else if (MATCH_OPT (ABI_BASE))
+    {
+      LARCH_DRIVER_SET_M_FLAG (
+	loongarch_abi_base_strings, N_ABI_BASE_TYPES,
+	opt_abi_base_driver, PARM)
+    }
+  else if (MATCH_OPT (ISA_EXT_FPU))
+    {
+      LARCH_DRIVER_SET_M_FLAG (loongarch_isa_ext_strings, N_ISA_EXT_FPU_TYPES,
+			       opt_fpu_driver, PARM)
+    }
+  else if (MATCH_OPT (ARCH))
+    {
+      LARCH_DRIVER_SET_M_FLAG (loongarch_cpu_strings, N_ARCH_TYPES,
+			       opt_arch_driver, PARM)
+    }
+  else if (MATCH_OPT (TUNE))
+    {
+      LARCH_DRIVER_SET_M_FLAG (loongarch_cpu_strings, N_TUNE_TYPES,
+			       opt_tune_driver, PARM)
+    }
+  else if (MATCH_OPT (CMODEL))
+    {
+      LARCH_DRIVER_SET_M_FLAG (loongarch_cmodel_strings, N_CMODEL_TYPES,
+			       opt_cmodel_driver, PARM)
+    }
+  else /* switches */
+    {
+      int switch_idx = M_OPTION_NOT_SEEN;
+
+      LARCH_DRIVER_SET_M_FLAG (loongarch_switch_strings, N_SWITCH_TYPES,
+			       switch_idx, argv[0])
+
+      if (switch_idx != M_OPTION_NOT_SEEN)
+	opt_switches |= loongarch_switch_mask[switch_idx];
+    }
+  return "";
+}
+
+const char*
+driver_get_normalized_m_opts (int argc, const char **argv)
+{
+  if (argc != 0)
+    {
+      (void) argv;  /* to make compiler shut up about unused argument */
+      return " %eget_normalized_m_opts requires no argument.\n";
+    }
+
+  loongarch_config_target (& la_target,
+			   opt_switches,
+			   opt_arch_driver,
+			   opt_tune_driver,
+			   opt_fpu_driver,
+			   opt_abi_base_driver,
+			   opt_abi_ext_driver,
+			   opt_cmodel_driver,
+			   !no_link /* follow_multilib_list */);
+
+  /* Output normalized option strings.  */
+  obstack_blank (&opts_obstack, 0);
+
+#undef APPEND_LTR
+#define APPEND_LTR(S) \
+  obstack_grow (&opts_obstack, (const void*) (S), \
+		sizeof ((S)) / sizeof (char) -1)
+
+#undef APPEND_VAL
+#define APPEND_VAL(S) \
+  obstack_grow (&opts_obstack, (const void*) (S), strlen ((S)))
+
+#undef APPEND_OPT
+#define APPEND_OPT(NAME) \
+   APPEND_LTR (" %<m" OPTSTR_##NAME "=* " \
+	       " -m" OPTSTR_##NAME "=")
+
+  for (int i = 0; i < N_SWITCH_TYPES; i++)
+    {
+      APPEND_LTR (" %<m");
+      APPEND_VAL (loongarch_switch_strings[i]);
+    }
+
+  APPEND_OPT (ABI_BASE);
+  APPEND_VAL (loongarch_abi_base_strings[la_target.abi.base]);
+
+  APPEND_OPT (ARCH);
+  APPEND_VAL (loongarch_cpu_strings[la_target.cpu_arch]);
+
+  APPEND_OPT (ISA_EXT_FPU);
+  APPEND_VAL (loongarch_isa_ext_strings[la_target.isa.fpu]);
+
+  APPEND_OPT (CMODEL);
+  APPEND_VAL (loongarch_cmodel_strings[la_target.cmodel]);
+
+  APPEND_OPT (TUNE);
+  APPEND_VAL (loongarch_cpu_strings[la_target.cpu_tune]);
+
+  obstack_1grow (&opts_obstack, '\0');
+
+  return XOBFINISH (&opts_obstack, const char *);
+}
diff --git a/gcc/config/loongarch/loongarch-driver.h b/gcc/config/loongarch/loongarch-driver.h
new file mode 100644
index 00000000000..ce0222748dd
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-driver.h
@@ -0,0 +1,69 @@
+/* Subroutine headers for the gcc driver.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_DRIVER_H
+#define LOONGARCH_DRIVER_H
+
+#include "loongarch-str.h"
+
+extern const char*
+driver_set_m_flag (int argc, const char **argv);
+
+extern const char*
+driver_get_normalized_m_opts (int argc, const char **argv);
+
+#define EXTRA_SPEC_FUNCTIONS \
+  { "set_m_flag", driver_set_m_flag  }, \
+  { "get_normalized_m_opts", driver_get_normalized_m_opts  },
+
+/* Pre-process ABI-related options.  */
+#define LA_SET_PARM_SPEC(NAME) \
+  " %{m" OPTSTR_##NAME  "=*: %:set_m_flag(" OPTSTR_##NAME "=%*)}" \
+
+#define LA_SET_FLAG_SPEC(NAME) \
+  " %{m" OPTSTR_##NAME  ": %:set_m_flag(" OPTSTR_##NAME ")}" \
+
+#define DRIVER_HANDLE_MACHINE_OPTIONS			      \
+  " %{c|S|E|nostdlib: %:set_m_flag(no_link)}"		      \
+  " %{nostartfiles: %{nodefaultlibs: %:set_m_flag(no_link)}}" \
+  LA_SET_PARM_SPEC (ABI_BASE)				      \
+  LA_SET_PARM_SPEC (ARCH)				      \
+  LA_SET_PARM_SPEC (TUNE)				      \
+  LA_SET_PARM_SPEC (ISA_EXT_FPU)			      \
+  LA_SET_PARM_SPEC (CMODEL)				      \
+  LA_SET_FLAG_SPEC (SOFT_FLOAT)				      \
+  LA_SET_FLAG_SPEC (SINGLE_FLOAT)			      \
+  LA_SET_FLAG_SPEC (DOUBLE_FLOAT)			      \
+  " %:get_normalized_m_opts()"
+
+#define DRIVER_SELF_SPECS \
+  DRIVER_HANDLE_MACHINE_OPTIONS
+
+/* ABI spec strings.  */
+#define ABI_GRLEN_SPEC \
+  "%{mabi=lp64*:64}"   \
+
+#define ABI_SPEC \
+  "%{mabi=lp64d:lp64d}" \
+  "%{mabi=lp64f:lp64f}" \
+  "%{mabi=lp64s:lp64s}" \
+
+#endif /* LOONGARCH_DRIVER_H */
+
diff --git a/gcc/config/loongarch/loongarch-opts.c b/gcc/config/loongarch/loongarch-opts.c
new file mode 100644
index 00000000000..fe9846b0cfb
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-opts.c
@@ -0,0 +1,582 @@
+/* Subroutines for loongarch-specific option handling.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "obstack.h"
+#include "diagnostic-core.h"
+
+#include "loongarch-cpu.h"
+#include "loongarch-opts.h"
+#include "loongarch-str.h"
+
+struct loongarch_target la_target;
+
+/* ABI-related configuration.  */
+#define ABI_COUNT (sizeof(abi_priority_list)/sizeof(struct loongarch_abi))
+static const struct loongarch_abi
+abi_priority_list[] = {
+    {ABI_BASE_LP64D, ABI_EXT_BASE},
+    {ABI_BASE_LP64F, ABI_EXT_BASE},
+    {ABI_BASE_LP64S, ABI_EXT_BASE},
+};
+
+/* Initialize enabled_abi_types from TM_MULTILIB_LIST.  */
+#ifdef __DISABLE_MULTILIB
+#define MULTILIB_LIST_LEN 1
+#else
+#define MULTILIB_LIST_LEN (sizeof (tm_multilib_list) / sizeof (int) / 2)
+static const int tm_multilib_list[] = { TM_MULTILIB_LIST };
+#endif
+static int enabled_abi_types[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES] = { 0 };
+
+#define isa_required(ABI) (abi_minimal_isa[(ABI).base][(ABI).ext])
+extern "C" const struct loongarch_isa
+abi_minimal_isa[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES];
+
+static inline int
+is_multilib_enabled (struct loongarch_abi abi)
+{
+  return enabled_abi_types[abi.base][abi.ext];
+}
+
+static void
+init_enabled_abi_types ()
+{
+#ifdef __DISABLE_MULTILIB
+  enabled_abi_types[DEFAULT_ABI_BASE][DEFAULT_ABI_EXT] = 1;
+#else
+  int abi_base, abi_ext;
+  for (unsigned int i = 0; i < MULTILIB_LIST_LEN; i++)
+    {
+      abi_base = tm_multilib_list[i << 1];
+      abi_ext = tm_multilib_list[(i << 1) + 1];
+      enabled_abi_types[abi_base][abi_ext] = 1;
+    }
+#endif
+}
+
+/* Switch masks.  */
+#undef M
+#define M(NAME) OPTION_MASK_##NAME
+const int loongarch_switch_mask[N_SWITCH_TYPES] = {
+  /* SW_SOFT_FLOAT */    M(FORCE_SOFTF),
+  /* SW_SINGLE_FLOAT */  M(FORCE_F32),
+  /* SW_DOUBLE_FLOAT */  M(FORCE_F64),
+};
+#undef M
+
+/* String processing.  */
+static struct obstack msg_obstack;
+#define APPEND_STRING(STR) obstack_grow (&msg_obstack, STR, strlen(STR));
+#define APPEND1(CH) obstack_1grow(&msg_obstack, CH);
+
+static const char* abi_str (struct loongarch_abi abi);
+static const char* isa_str (const struct loongarch_isa *isa, char separator);
+static const char* arch_str (const struct loongarch_target *target);
+static const char* multilib_enabled_abi_list ();
+
+/* Misc */
+static struct loongarch_abi isa_default_abi (const struct loongarch_isa *isa);
+static int isa_base_compat_p (const struct loongarch_isa *set1,
+			      const struct loongarch_isa *set2);
+static int isa_fpu_compat_p (const struct loongarch_isa *set1,
+			     const struct loongarch_isa *set2);
+static int abi_compat_p (const struct loongarch_isa *isa,
+			 struct loongarch_abi abi);
+static int abi_default_cpu_arch (struct loongarch_abi abi);
+
+/* Checking configure-time defaults.  */
+#ifndef DEFAULT_ABI_BASE
+#error missing definition of DEFAULT_ABI_BASE in ${tm_defines}.
+#endif
+
+#ifndef DEFAULT_ABI_EXT
+#error missing definition of DEFAULT_ABI_EXT in ${tm_defines}.
+#endif
+
+#ifndef DEFAULT_CPU_ARCH
+#error missing definition of DEFAULT_CPU_ARCH in ${tm_defines}.
+#endif
+
+#ifndef DEFAULT_ISA_EXT_FPU
+#error missing definition of DEFAULT_ISA_EXT_FPU in ${tm_defines}.
+#endif
+
+/* Handle combinations of -m machine option values
+   (see loongarch.opt and loongarch-opts.h).  */
+void
+loongarch_config_target (struct loongarch_target *target,
+			 HOST_WIDE_INT opt_switches,
+			 int opt_arch, int opt_tune, int opt_fpu,
+			 int opt_abi_base, int opt_abi_ext,
+			 int opt_cmodel, int follow_multilib_list)
+{
+  struct loongarch_target t;
+
+  if (!target)
+    return;
+
+  /* Initialization */
+  init_enabled_abi_types ();
+  obstack_init (&msg_obstack);
+
+  struct {
+    int arch, tune, fpu, abi_base, abi_ext, cmodel;
+  } constrained = {
+      M_OPT_ABSENT(opt_arch)     ? 0 : 1,
+      M_OPT_ABSENT(opt_tune)     ? 0 : 1,
+      M_OPT_ABSENT(opt_fpu)      ? 0 : 1,
+      M_OPT_ABSENT(opt_abi_base) ? 0 : 1,
+      M_OPT_ABSENT(opt_abi_ext)  ? 0 : 1,
+      M_OPT_ABSENT(opt_cmodel)   ? 0 : 1,
+  };
+
+#define on(NAME) ((loongarch_switch_mask[(SW_##NAME)] & opt_switches) \
+		  && (on_switch = (SW_##NAME), 1))
+  int on_switch;
+
+  /* 1.  Target ABI */
+  t.abi.base = constrained.abi_base ? opt_abi_base : DEFAULT_ABI_BASE;
+
+  t.abi.ext = constrained.abi_ext ? opt_abi_ext : DEFAULT_ABI_EXT;
+
+  /* Extra switch handling.  */
+  if (on (SOFT_FLOAT) || on (SINGLE_FLOAT) || on (DOUBLE_FLOAT))
+    {
+      switch (on_switch)
+	{
+	  case SW_SOFT_FLOAT:
+	    opt_fpu = ISA_EXT_NOFPU;
+	    break;
+
+	  case SW_SINGLE_FLOAT:
+	    opt_fpu = ISA_EXT_FPU32;
+	    break;
+
+	  case SW_DOUBLE_FLOAT:
+	    opt_fpu = ISA_EXT_FPU64;
+	    break;
+
+	  default:
+	    gcc_unreachable();
+	}
+      constrained.fpu = 1;
+
+      /* The target ISA is not ready yet, but (isa_required (t.abi)
+	 + forced fpu) is enough for computing the forced base ABI.  */
+      struct loongarch_isa default_isa = isa_required (t.abi);
+      struct loongarch_isa force_isa = default_isa;
+      struct loongarch_abi force_abi = t.abi;
+      force_isa.fpu = opt_fpu;
+      force_abi.base = isa_default_abi (&force_isa).base;
+
+      if (isa_fpu_compat_p (&default_isa, &force_isa));
+	/* Keep quiet if -m*-float does not promote the FP ABI.  */
+      else if (constrained.abi_base && (t.abi.base != force_abi.base))
+	inform (UNKNOWN_LOCATION,
+		"%<-m%s%> overrides %<-m%s=%s%>, promoting ABI to %qs",
+		loongarch_switch_strings[on_switch],
+		OPTSTR_ABI_BASE, loongarch_abi_base_strings[t.abi.base],
+		abi_str (force_abi));
+
+      t.abi.base = force_abi.base;
+    }
+
+#ifdef __DISABLE_MULTILIB
+  if (follow_multilib_list)
+    if (t.abi.base != DEFAULT_ABI_BASE || t.abi.ext != DEFAULT_ABI_EXT)
+      {
+	static const struct loongarch_abi default_abi
+	  = {DEFAULT_ABI_BASE, DEFAULT_ABI_EXT};
+
+	warning (0, "ABI changed (%qs -> %qs) while multilib is disabled",
+		 abi_str (default_abi), abi_str (t.abi));
+      }
+#endif
+
+  /* 2.  Target CPU */
+  t.cpu_arch = constrained.arch ? opt_arch : DEFAULT_CPU_ARCH;
+
+  t.cpu_tune = constrained.tune ? opt_tune
+    : (constrained.arch ? DEFAULT_CPU_ARCH : DEFAULT_CPU_TUNE);
+
+#ifdef __loongarch__
+  /* For native compilers, gather local CPU information
+     and fill the "CPU_NATIVE" index of arrays defined in
+     loongarch-cpu.c.  */
+
+  t.cpu_native = fill_native_cpu_config (t.cpu_arch == CPU_NATIVE,
+					 t.cpu_tune == CPU_NATIVE);
+
+#else
+  if (t.cpu_arch == CPU_NATIVE)
+    fatal_error (UNKNOWN_LOCATION,
+		 "%qs does not work on a cross compiler",
+		 "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
+
+  else if (t.cpu_tune == CPU_NATIVE)
+    fatal_error (UNKNOWN_LOCATION,
+		 "%qs does not work on a cross compiler",
+		 "-m" OPTSTR_TUNE "=" STR_CPU_NATIVE);
+#endif
+
+  /* 3.  Target ISA */
+config_target_isa:
+
+  /* Get default ISA from "-march" or its default value.  */
+  t.isa = loongarch_cpu_default_isa[__ACTUAL_ARCH];
+
+  /* Apply incremental changes.  */
+  /* "-march=native" overrides the default FPU type.  */
+  t.isa.fpu = constrained.fpu ? opt_fpu :
+    ((t.cpu_arch == CPU_NATIVE && constrained.arch) ?
+     t.isa.fpu : DEFAULT_ISA_EXT_FPU);
+
+
+  /* 4.  ABI-ISA compatibility */
+  /* Note:
+     - There IS a unique default -march value for each ABI type
+       (config.gcc: triplet -> abi -> default arch).
+
+     - If the base ABI is incompatible with the default arch,
+       try using the default -march it implies (and mark it
+       as "constrained" this time), then re-apply step 3.
+  */
+  struct loongarch_abi abi_tmp;
+  const struct loongarch_isa* isa_min;
+  struct loongarch_isa isa_tmp;
+
+  abi_tmp = t.abi;
+  isa_min = &isa_required (abi_tmp);
+  isa_tmp = t.isa;
+
+  if (isa_base_compat_p (&isa_tmp, isa_min)); /* OK */
+  else if (!constrained.arch)
+    {
+      /* Base architecture can only be implied by -march,
+	 so we adjust that first if it is not constrained.  */
+      int fallback_arch = abi_default_cpu_arch (t.abi);
+
+      if (t.cpu_arch == CPU_NATIVE)
+	warning (0, "your native CPU architecture (%qs) "
+		 "does not support %qs ABI, falling back to %<-m%s=%s%>",
+		 arch_str (&t), abi_str (t.abi), OPTSTR_ARCH,
+		 loongarch_cpu_strings[fallback_arch]);
+      else
+	warning (0, "default CPU architecture (%qs) "
+		 "does not support %qs ABI, falling back to %<-m%s=%s%>",
+		 arch_str (&t), abi_str (t.abi), OPTSTR_ARCH,
+		 loongarch_cpu_strings[fallback_arch]);
+
+      t.cpu_arch = fallback_arch;
+      constrained.arch = 1;
+      goto config_target_isa;
+    }
+  else if (!constrained.abi_base)
+    {
+      /* If -march is given while -mabi is not,
+	 try selecting another base ABI type.  */
+      abi_tmp.base = isa_default_abi (&isa_tmp).base;
+    }
+  else
+    goto fatal;
+
+  if (isa_fpu_compat_p (&isa_tmp, isa_min)); /* OK */
+  else if (!constrained.fpu)
+    isa_tmp.fpu = isa_min->fpu;
+  else if (!constrained.abi_base)
+      /* If -march is compatible with the default ABI
+	 while -mfpu is not.  */
+    abi_tmp.base = isa_default_abi (&isa_tmp).base;
+  else
+    goto fatal;
+
+  if (0)
+fatal:
+    fatal_error (UNKNOWN_LOCATION,
+		 "unable to implement ABI %qs with instruction set %qs",
+		 abi_str (t.abi), isa_str (&t.isa, '/'));
+
+
+  /* Using the fallback ABI.  */
+  if (abi_tmp.base != t.abi.base || abi_tmp.ext != t.abi.ext)
+    {
+      /* This flag is only set in the GCC driver.  */
+      if (follow_multilib_list)
+	{
+
+	  /* Continue falling back until we find a feasible ABI type
+	     enabled by TM_MULTILIB_LIST.  */
+	  if (!is_multilib_enabled (abi_tmp))
+	    {
+	      for (unsigned int i = 0; i < ABI_COUNT; i++)
+		{
+		  if (is_multilib_enabled (abi_priority_list[i])
+		      && abi_compat_p (&isa_tmp, abi_priority_list[i]))
+		    {
+		      abi_tmp = abi_priority_list[i];
+
+		      warning (0, "ABI %qs cannot be implemented due to "
+			       "limited instruction set %qs, "
+			       "falling back to %qs", abi_str (t.abi),
+			       isa_str (&t.isa, '/'), abi_str (abi_tmp));
+
+		      goto fallback;
+		    }
+		}
+
+	      /* Otherwise, keep using abi_tmp with a warning.  */
+#ifdef __DISABLE_MULTILIB
+	      warning (0, "instruction set %qs cannot implement "
+		       "default ABI %qs, falling back to %qs",
+		       isa_str (&t.isa, '/'), abi_str (t.abi),
+		       abi_str (abi_tmp));
+#else
+	      warning (0, "no multilib-enabled ABI (%qs) can be implemented "
+		       "with instruction set %qs, falling back to %qs",
+		       multilib_enabled_abi_list (),
+		       isa_str (&t.isa, '/'), abi_str (abi_tmp));
+#endif
+	    }
+	}
+
+fallback:
+      t.abi = abi_tmp;
+    }
+  else if (follow_multilib_list)
+    {
+      if (!is_multilib_enabled (t.abi))
+	{
+	  inform (UNKNOWN_LOCATION,
+		  "ABI %qs is not enabled at configure-time, "
+		  "the linker might report an error", abi_str (t.abi));
+
+	  inform (UNKNOWN_LOCATION, "ABI with startfiles: %s",
+		  multilib_enabled_abi_list ());
+	}
+    }
+
+
+  /* 5.  Target code model */
+  t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
+
+  /* cleanup and return */
+  obstack_free (&msg_obstack, NULL);
+  *target = t;
+}
+
+/* Returns the default ABI for the given instruction set.  */
+static inline struct loongarch_abi
+isa_default_abi (const struct loongarch_isa *isa)
+{
+  struct loongarch_abi abi;
+
+  switch (isa->fpu)
+    {
+      case ISA_EXT_FPU64:
+	if (isa->base == ISA_BASE_LA64V100)
+	  abi.base = ABI_BASE_LP64D;
+	break;
+
+      case ISA_EXT_FPU32:
+	if (isa->base == ISA_BASE_LA64V100)
+	  abi.base = ABI_BASE_LP64F;
+	break;
+
+      case ISA_EXT_NOFPU:
+	if (isa->base == ISA_BASE_LA64V100)
+	  abi.base = ABI_BASE_LP64S;
+	break;
+
+      default:
+	gcc_unreachable ();
+    }
+
+  abi.ext = ABI_EXT_BASE;
+  return abi;
+}
+
+/* Check if set2 is a subset of set1.  */
+static inline int
+isa_base_compat_p (const struct loongarch_isa *set1,
+		   const struct loongarch_isa *set2)
+{
+  switch (set2->base)
+    {
+      case ISA_BASE_LA64V100:
+	return (set1->base == ISA_BASE_LA64V100);
+
+      default:
+	gcc_unreachable ();
+    }
+}
+
+static inline int
+isa_fpu_compat_p (const struct loongarch_isa *set1,
+		  const struct loongarch_isa *set2)
+{
+  switch (set2->fpu)
+    {
+      case ISA_EXT_FPU64:
+	return set1->fpu == ISA_EXT_FPU64;
+
+      case ISA_EXT_FPU32:
+	return set1->fpu == ISA_EXT_FPU32 || set1->fpu == ISA_EXT_FPU64;
+
+      case ISA_EXT_NOFPU:
+	return 1;
+
+      default:
+	gcc_unreachable ();
+    }
+
+}
+
+static inline int
+abi_compat_p (const struct loongarch_isa *isa, struct loongarch_abi abi)
+{
+  int compatible = 1;
+  const struct loongarch_isa *isa2 = &isa_required (abi);
+
+  /* Append conditionals for new ISA components below.  */
+  compatible = compatible && isa_base_compat_p (isa, isa2);
+  compatible = compatible && isa_fpu_compat_p (isa, isa2);
+  return compatible;
+}
+
+/* The behavior of this function should be consistent
+   with config.gcc.  */
+static inline int
+abi_default_cpu_arch (struct loongarch_abi abi)
+{
+  switch (abi.base)
+    {
+      case ABI_BASE_LP64D:
+      case ABI_BASE_LP64F:
+      case ABI_BASE_LP64S:
+	if (abi.ext == ABI_EXT_BASE)
+	  return CPU_LOONGARCH64;
+    }
+  gcc_unreachable ();
+}
+
+static const char*
+abi_str (struct loongarch_abi abi)
+{
+  /* "/base" can be omitted */
+  if (abi.ext == ABI_EXT_BASE)
+    return (const char*)
+      obstack_copy0 (&msg_obstack, loongarch_abi_base_strings[abi.base],
+		     strlen (loongarch_abi_base_strings[abi.base]));
+  else
+    {
+      APPEND_STRING (loongarch_abi_base_strings[abi.base])
+      APPEND1 ('/')
+      APPEND_STRING (loongarch_abi_ext_strings[abi.ext])
+      APPEND1 ('\0')
+
+      return XOBFINISH (&msg_obstack, const char *);
+    }
+}
+
+static const char*
+isa_str (const struct loongarch_isa *isa, char separator)
+{
+  APPEND_STRING (loongarch_isa_base_strings[isa->base])
+  APPEND1 (separator)
+
+  if (isa->fpu == ISA_EXT_NOFPU)
+    {
+      APPEND_STRING ("no" OPTSTR_ISA_EXT_FPU)
+    }
+  else
+    {
+      APPEND_STRING (OPTSTR_ISA_EXT_FPU)
+      APPEND_STRING (loongarch_isa_ext_strings[isa->fpu])
+    }
+  APPEND1 ('\0')
+
+  /* Add more here.  */
+
+  return XOBFINISH (&msg_obstack, const char *);
+}
+
+static const char*
+arch_str (const struct loongarch_target *target)
+{
+  if (target->cpu_arch == CPU_NATIVE)
+    {
+      if (target->cpu_native == CPU_NATIVE)
+	{
+	  /* Describe a native CPU with unknown PRID.  */
+	  const char* isa_string = isa_str (&target->isa, ',');
+	  APPEND_STRING ("PRID: 0x")
+	  APPEND_STRING (get_native_prid_str ())
+	  APPEND_STRING (", ISA features: ")
+	  APPEND_STRING (isa_string)
+	  APPEND1 ('\0')
+	}
+      else
+	APPEND_STRING (loongarch_cpu_strings[target->cpu_native]);
+    }
+  else
+    APPEND_STRING (loongarch_cpu_strings[target->cpu_arch]);
+
+  APPEND1 ('\0')
+  return XOBFINISH (&msg_obstack, const char *);
+}
+
+static const char*
+multilib_enabled_abi_list ()
+{
+  int enabled_abi_idx[MULTILIB_LIST_LEN] = { 0 };
+  const char* enabled_abi_str[MULTILIB_LIST_LEN] = { NULL };
+  unsigned int j = 0;
+
+  for (unsigned int i = 0; i < ABI_COUNT && j < MULTILIB_LIST_LEN; i++)
+    {
+      if (enabled_abi_types[abi_priority_list[i].base]
+	  [abi_priority_list[i].ext])
+	{
+	  enabled_abi_idx[j++] = i;
+	}
+    }
+
+  for (unsigned int k = 0; k < j; k++)
+    {
+      enabled_abi_str[k] = abi_str (abi_priority_list[enabled_abi_idx[k]]);
+    }
+
+  for (unsigned int k = 0; k < j - 1; k++)
+    {
+      APPEND_STRING (enabled_abi_str[k])
+      APPEND1 (',')
+      APPEND1 (' ')
+    }
+  APPEND_STRING (enabled_abi_str[j - 1])
+  APPEND1 ('\0')
+
+  return XOBFINISH (&msg_obstack, const char *);
+}
diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h
new file mode 100644
index 00000000000..9c205e01980
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-opts.h
@@ -0,0 +1,86 @@
+/* Definitions for loongarch-specific option handling.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_OPTS_H
+#define LOONGARCH_OPTS_H
+
+
+/* Target configuration */
+extern struct loongarch_target la_target;
+
+/* Switch masks */
+extern const int loongarch_switch_mask[];
+
+#include "loongarch-def.h"
+
+#if !defined(IN_LIBGCC2) && !defined(IN_TARGET_LIBS) && !defined(IN_RTS)
+/* Handler for "-m" option combinations,
+   shared by the driver and the compiler proper.  */
+void
+loongarch_config_target (struct loongarch_target *target,
+  HOST_WIDE_INT opt_switches,
+  int opt_arch, int opt_tune, int opt_fpu,
+  int opt_abi_base, int opt_abi_ext,
+  int opt_cmodel, int follow_multilib_list);
+#endif
+
+
+/* Macros for common conditional expressions used in loongarch.{c,h,md} */
+#define TARGET_CMODEL_NORMAL	    (la_target.cmodel == CMODEL_NORMAL)
+#define TARGET_CMODEL_TINY	    (la_target.cmodel == CMODEL_TINY)
+#define TARGET_CMODEL_TINY_STATIC   (la_target.cmodel == CMODEL_TINY_STATIC)
+#define TARGET_CMODEL_LARGE	    (la_target.cmodel == CMODEL_LARGE)
+#define TARGET_CMODEL_EXTREME	    (la_target.cmodel == CMODEL_EXTREME)
+
+#define TARGET_HARD_FLOAT	    (la_target.isa.fpu != ISA_EXT_NOFPU)
+#define TARGET_HARD_FLOAT_ABI	    (la_target.abi.base == ABI_BASE_LP64D \
+				     || la_target.abi.base == ABI_BASE_LP64F)
+
+#define TARGET_SOFT_FLOAT	  (la_target.isa.fpu == ISA_EXT_NOFPU)
+#define TARGET_SOFT_FLOAT_ABI	  (la_target.abi.base == ABI_BASE_LP64S)
+#define TARGET_SINGLE_FLOAT	  (la_target.isa.fpu == ISA_EXT_FPU32)
+#define TARGET_SINGLE_FLOAT_ABI	  (la_target.abi.base == ABI_BASE_LP64F)
+#define TARGET_DOUBLE_FLOAT	  (la_target.isa.fpu == ISA_EXT_FPU64)
+#define TARGET_DOUBLE_FLOAT_ABI	  (la_target.abi.base == ABI_BASE_LP64D)
+
+#define TARGET_64BIT		  (la_target.isa.base == ISA_BASE_LA64V100)
+#define TARGET_ABI_LP64	  (la_target.abi.base == ABI_BASE_LP64D \
+				   || la_target.abi.base == ABI_BASE_LP64F \
+				   || la_target.abi.base == ABI_BASE_LP64S)
+
+#define TARGET_ARCH_NATIVE	  (la_target.cpu_arch == CPU_NATIVE)
+#define __ACTUAL_ARCH		  (TARGET_ARCH_NATIVE \
+				   ? (la_target.cpu_native < N_ARCH_TYPES \
+				      ? (la_target.cpu_native) : (CPU_NATIVE)) \
+				      : (la_target.cpu_arch))
+
+#define TARGET_TUNE_NATIVE	(la_target.cpu_tune == CPU_NATIVE)
+#define __ACTUAL_TUNE		(TARGET_TUNE_NATIVE \
+				 ? (la_target.cpu_native < N_TUNE_TYPES \
+				    ? (la_target.cpu_native) : (CPU_NATIVE)) \
+				    : (la_target.cpu_tune))
+
+#define TARGET_ARCH_LOONGARCH64	  (__ACTUAL_ARCH == CPU_LOONGARCH64)
+#define TARGET_ARCH_LA464	  (__ACTUAL_ARCH == CPU_LA464)
+
+#define TARGET_TUNE_LOONGARCH64	  (__ACTUAL_TUNE == CPU_LOONGARCH64)
+#define TARGET_TUNE_LA464	  (__ACTUAL_TUNE == CPU_LA464)
+
+#endif /* LOONGARCH_OPTS_H */
diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h
new file mode 100644
index 00000000000..4f0a76cdc28
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-str.h
@@ -0,0 +1,57 @@
+/* Generated automatically by "genstr" from "loongarch-strings".
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_STR_H
+#define LOONGARCH_STR_H
+
+#define OPTSTR_ARCH "arch"
+#define OPTSTR_TUNE "tune"
+
+#define STR_CPU_NATIVE "native"
+#define STR_CPU_LOONGARCH64 "loongarch64"
+#define STR_CPU_LA464 "la464"
+
+#define STR_ISA_BASE_LA64V100 "la64"
+
+#define OPTSTR_ISA_EXT_FPU "fpu"
+#define STR_ISA_EXT_NOFPU "none"
+#define STR_ISA_EXT_FPU0 "0"
+#define STR_ISA_EXT_FPU32 "32"
+#define STR_ISA_EXT_FPU64 "64"
+
+#define OPTSTR_SOFT_FLOAT "soft-float"
+#define OPTSTR_SINGLE_FLOAT "single-float"
+#define OPTSTR_DOUBLE_FLOAT "double-float"
+
+#define OPTSTR_ABI_BASE "abi"
+#define STR_ABI_BASE_LP64D "lp64d"
+#define STR_ABI_BASE_LP64F "lp64f"
+#define STR_ABI_BASE_LP64S "lp64s"
+
+#define STR_ABI_EXT_BASE "base"
+
+#define OPTSTR_CMODEL "cmodel"
+#define STR_CMODEL_NORMAL "normal"
+#define STR_CMODEL_TINY "tiny"
+#define STR_CMODEL_TS "tiny-static"
+#define STR_CMODEL_LARGE "large"
+#define STR_CMODEL_EXTREME "extreme"
+
+#endif /* LOONGARCH_STR_H */
diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
new file mode 100644
index 00000000000..9e0ea68f538
--- /dev/null
+++ b/gcc/config/loongarch/loongarch.opt
@@ -0,0 +1,189 @@
+; Generated by "genstr" from the template "loongarch.opt.in"
+; and definitions from "loongarch-strings".
+;
+; Copyright (C) 2021 Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC 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 3, or (at your option) any later
+; version.
+;
+; GCC 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 GCC; see the file COPYING3.  If not see
+; <http://www.gnu.org/licenses/>.
+;
+
+; Variables (macros) that should be exported by loongarch.opt:
+;   la_opt_switches,
+;   la_opt_abi_base, la_opt_abi_ext,
+;   la_opt_cpu_arch, la_opt_cpu_tune,
+;   la_opt_fpu,
+;   la_cmodel.
+
+HeaderInclude
+config/loongarch/loongarch-opts.h
+
+HeaderInclude
+config/loongarch/loongarch-str.h
+
+Variable
+HOST_WIDE_INT la_opt_switches = 0
+
+; ISA related options
+;; Base ISA
+Enum
+Name(isa_base) Type(int)
+Basic ISAs of LoongArch:
+
+EnumValue
+Enum(isa_base) String(la64) Value(ISA_BASE_LA64V100)
+
+
+;; ISA extensions / adjustments
+Enum
+Name(isa_ext_fpu) Type(int)
+FPU types of LoongArch:
+
+EnumValue
+Enum(isa_ext_fpu) String(none) Value(ISA_EXT_NOFPU)
+
+EnumValue
+Enum(isa_ext_fpu) String(32) Value(ISA_EXT_FPU32)
+
+EnumValue
+Enum(isa_ext_fpu) String(64) Value(ISA_EXT_FPU64)
+
+mfpu=
+Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPTION_NOT_SEEN)
+-mfpu=FPU	Generate code for the given FPU.
+
+mfpu=0
+Target RejectNegative Alias(mfpu=,none)
+
+msoft-float
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_SOFTF) Negative(msingle-float)
+Prevent the use of all hardware floating-point instructions.
+
+msingle-float
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F32) Negative(mdouble-float)
+Restrict the use of hardware floating-point instructions to 32-bit operations.
+
+mdouble-float
+Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F64) Negative(msoft-float)
+Allow hardware floating-point instructions to cover both 32-bit and 64-bit operations.
+
+
+;; Base target models (implies ISA & tune parameters)
+Enum
+Name(cpu_type) Type(int)
+LoongArch CPU types:
+
+EnumValue
+Enum(cpu_type) String(native) Value(CPU_NATIVE)
+
+EnumValue
+Enum(cpu_type) String(loongarch64) Value(CPU_LOONGARCH64)
+
+EnumValue
+Enum(cpu_type) String(la464) Value(CPU_LA464)
+
+march=
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPTION_NOT_SEEN)
+-march=PROCESSOR	Generate code for the given PROCESSOR ISA.
+
+mtune=
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPTION_NOT_SEEN)
+-mtune=PROCESSOR	Generate optimized code for PROCESSOR.
+
+
+; ABI related options
+; (ISA constraints on ABI are handled dynamically)
+
+;; Base ABI
+Enum
+Name(abi_base) Type(int)
+Base ABI types for LoongArch:
+
+EnumValue
+Enum(abi_base) String(lp64d) Value(ABI_BASE_LP64D)
+
+EnumValue
+Enum(abi_base) String(lp64f) Value(ABI_BASE_LP64F)
+
+EnumValue
+Enum(abi_base) String(lp64s) Value(ABI_BASE_LP64S)
+
+mabi=
+Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPTION_NOT_SEEN)
+-mabi=BASEABI	Generate code that conforms to the given BASEABI.
+
+;; ABI Extension
+Variable
+int la_opt_abi_ext = M_OPTION_NOT_SEEN
+
+
+mbranch-cost=
+Target RejectNegative Joined UInteger Var(loongarch_branch_cost)
+-mbranch-cost=COST	Set the cost of branches to roughly COST instructions.
+
+mcheck-zero-division
+Target Mask(CHECK_ZERO_DIV)
+Trap on integer divide by zero.
+
+mcond-move-int
+Target Var(TARGET_COND_MOVE_INT) Init(1)
+Conditional moves for integral are enabled.
+
+mcond-move-float
+Target Var(TARGET_COND_MOVE_FLOAT) Init(1)
+Conditional moves for float are enabled.
+
+mmemcpy
+Target Mask(MEMCPY)
+Don't optimize block moves.
+
+mlra
+Target Var(loongarch_lra_flag) Init(1) Save
+Use LRA instead of reload.
+
+noasmopt
+Driver
+
+mstrict-align
+Target Var(TARGET_STRICT_ALIGN) Init(1)
+Do not generate unaligned memory accesses.
+
+mmax-inline-memcpy-size=
+Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024)
+-mmax-inline-memcpy-size=SIZE	Set the max size of memcpy to inline, default is 1024.
+
+; The code model option names for -mcmodel.
+Enum
+Name(cmodel) Type(int)
+The code model option names for -mcmodel:
+
+EnumValue
+Enum(cmodel) String(normal) Value(CMODEL_NORMAL)
+
+EnumValue
+Enum(cmodel) String(tiny) Value(CMODEL_TINY)
+
+EnumValue
+Enum(cmodel) String(tiny-static) Value(CMODEL_TINY_STATIC)
+
+EnumValue
+Enum(cmodel) String(large) Value(CMODEL_LARGE)
+
+EnumValue
+Enum(cmodel) String(extreme) Value(CMODEL_EXTREME)
+
+mcmodel=
+Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(CMODEL_NORMAL)
+Specify the code model.
diff --git a/gcc/config/loongarch/t-linux b/gcc/config/loongarch/t-linux
new file mode 100644
index 00000000000..7be867d2351
--- /dev/null
+++ b/gcc/config/loongarch/t-linux
@@ -0,0 +1,53 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC 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 3, or (at your option)
+# any later version.
+#
+# GCC 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Multilib
+MULTILIB_OPTIONS = mabi=lp64d/mabi=lp64f/mabi=lp64s
+MULTILIB_DIRNAMES = base/lp64d base/lp64f base/lp64s
+
+# The GCC driver always gets all abi-related options on the command line.
+# (see loongarch-driver.c:driver_get_normalized_m_opts)
+comma=,
+MULTILIB_REQUIRED = $(subst $(comma), ,$(TM_MULTILIB_CONFIG))
+
+# Multiarch
+ifneq ($(call if_multiarch,yes),yes)
+    # Define __DISABLE_MULTIARCH if multiarch is disabled.
+    tm_defines += __DISABLE_MULTIARCH
+else
+    # Only define MULTIARCH_DIRNAME when multiarch is enabled,
+    # or it would always introduce ${target} into the search path.
+    MULTIARCH_DIRNAME = $(LA_MULTIARCH_TRIPLET)
+endif
+
+# Don't define MULTILIB_OSDIRNAMES if multilib is disabled.
+ifeq ($(filter __DISABLE_MULTILIB,$(tm_defines)),)
+
+    MULTILIB_OSDIRNAMES = \
+      mabi.lp64d=../lib64$\
+      $(call if_multiarch,:loongarch64-linux-gnuf64)
+
+    MULTILIB_OSDIRNAMES += \
+      mabi.lp64f=../lib64/f32$\
+      $(call if_multiarch,:loongarch64-linux-gnuf32)
+
+    MULTILIB_OSDIRNAMES += \
+      mabi.lp64s=../lib64/sf$\
+      $(call if_multiarch,:loongarch64-linux-gnusf)
+
+endif
diff --git a/gcc/config/loongarch/t-loongarch b/gcc/config/loongarch/t-loongarch
new file mode 100644
index 00000000000..0396a3a6d7c
--- /dev/null
+++ b/gcc/config/loongarch/t-loongarch
@@ -0,0 +1,59 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC 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 3, or (at your option)
+# any later version.
+#
+# GCC 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Canonical target triplet from config.gcc
+LA_MULTIARCH_TRIPLET = $(patsubst LA_MULTIARCH_TRIPLET=%,%,$\
+$(filter LA_MULTIARCH_TRIPLET=%,$(tm_defines)))
+
+# String definition header
+LA_STR_H = $(srcdir)/config/loongarch/loongarch-str.h
+
+loongarch-c.o: $(srcdir)/config/loongarch/loongarch-c.c $(CONFIG_H) $(SYSTEM_H) \
+	coretypes.h $(TM_H) $(TREE_H) output.h $(C_COMMON_H) $(TARGET_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+	$(srcdir)/config/loongarch/loongarch-c.c
+
+loongarch-builtins.o: $(srcdir)/config/loongarch/loongarch-builtins.c $(CONFIG_H) \
+	$(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) $(RECOG_H) langhooks.h \
+	$(DIAGNOSTIC_CORE_H) $(OPTABS_H) $(srcdir)/config/loongarch/loongarch-ftypes.def \
+	$(srcdir)/config/loongarch/loongarch-modes.def
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+	$(srcdir)/config/loongarch/loongarch-builtins.c
+
+loongarch-driver.o : $(srcdir)/config/loongarch/loongarch-driver.c $(LA_STR_H) \
+	$(CONFIG_H) $(SYSTEM_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+loongarch-opts.o: $(srcdir)/config/loongarch/loongarch-opts.c $(LA_STR_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+loongarch-cpu.o: $(srcdir)/config/loongarch/loongarch-cpu.c $(LA_STR_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+loongarch-def.o: $(srcdir)/config/loongarch/loongarch-def.c $(LA_STR_H)
+	$(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+$(srcdir)/config/loongarch/loongarch.opt: \
+	$(srcdir)/config/loongarch/genopts/genstr.sh \
+	$(srcdir)/config/loongarch/genopts/loongarch.opt.in
+	$(SHELL) $< opt > $@
+
+$(LA_STR_H): \
+	$(srcdir)/config/loongarch/genopts/genstr.sh \
+	$(srcdir)/config/loongarch/genopts/loongarch-strings
+	$(SHELL) $< header > $@
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 0db9c4cb027..b206c604d47 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -937,6 +937,9 @@ AC_ARG_ENABLE(fixed-point,
     mips*-*-*)
       enable_fixed_point=yes
       ;;
+    loongarch*-*-*)
+      enable_fixed_point=yes
+      ;;
     *)
       AC_MSG_WARN([fixed-point is not supported for this target, ignored])
       enable_fixed_point=no
@@ -3800,6 +3803,17 @@ foo:	data8	25
 	movl	r24 = @tprel(foo#)'
 	tls_as_opt=--fatal-warnings
 	;;
+  loongarch*-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:	.word 2
+	.text
+	la.tls.gd $a0,x
+	bl __tls_get_addr'
+	tls_first_major=0
+	tls_first_minor=0
+	tls_as_opt='--fatal-warnings'
+	;;
   microblaze*-*-*)
     conftest_s='
 	.section .tdata,"awT",@progbits
@@ -5260,6 +5274,17 @@ configured with --enable-newlib-nano-formatted-io.])
       [AC_DEFINE(HAVE_AS_MARCH_ZIFENCEI, 1,
 		 [Define if the assembler understands -march=rv*_zifencei.])])
     ;;
+  loongarch*-*-*)
+    gcc_GAS_CHECK_FEATURE([.dtprelword support],
+      gcc_cv_as_loongarch_dtprelword, [2,18,0],,
+      [.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+	.dtprelword x+0x8000],,
+      [AC_DEFINE(HAVE_AS_DTPRELWORD, 1,
+	  [Define if your assembler supports .dtprelword.])])
+    ;;
     s390*-*-*)
     gcc_GAS_CHECK_FEATURE([.gnu_attribute support],
       gcc_cv_as_s390_gnu_attribute,,
@@ -5293,11 +5318,11 @@ configured with --enable-newlib-nano-formatted-io.])
     ;;
 esac
 
-# Mips and HP-UX need the GNU assembler.
+# Mips, LoongArch and HP-UX need the GNU assembler.
 # Linux on IA64 might be able to use the Intel assembler.
 
 case "$target" in
-  mips*-*-* | *-*-hpux* )
+  mips*-*-* | loongarch*-*-* | *-*-hpux* )
     if test x$gas_flag = xyes \
        || test x"$host" != x"$build" \
        || test ! -x "$gcc_cv_as" \
@@ -5454,8 +5479,8 @@ esac
 # ??? Once 2.11 is released, probably need to add first known working
 # version to the per-target configury.
 case "$cpu_type" in
-  aarch64 | alpha | arc | arm | avr | bfin | cris | csky | i386 | m32c | m68k \
-  | microblaze | mips | nds32 | nios2 | pa | riscv | rs6000 | score | sparc \
+  aarch64 | alpha | arc | arm | avr | bfin | cris | csky | i386 | loongarch | m32c \
+  | m68k | microblaze | mips | nds32 | nios2 | pa | riscv | rs6000 | score | sparc \
   | tilegx | tilepro | visium | xstormy16 | xtensa)
     insn="nop"
     ;;
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 03/12] LoongArch Port: Regenerate gcc/configure.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
  2021-12-24  9:28 ` [PATCH v4 01/12] LoongArch Port: Regenerate configure chenglulu
  2021-12-24  9:28 ` [PATCH v4 02/12] LoongArch Port: gcc build chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 04/12] LoongArch Port: Machine Decsription files chenglulu
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

---
 gcc/configure | 85 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 6 deletions(-)

diff --git a/gcc/configure b/gcc/configure
index 992a9d70092..999557a3b51 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -5352,7 +5352,26 @@ else
   GDC="$ac_cv_prog_GDC"
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the D compiler works" >&5
+$as_echo_n "checking whether the D compiler works... " >&6; }
+if ${acx_cv_d_compiler_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.d <<EOF
+module conftest; int main() { return 0; }
+EOF
+acx_cv_d_compiler_works=no
 if test "x$GDC" != xno; then
+  errors=`(${GDC} -I"$srcdir"/d -c conftest.d) 2>&1 || echo failure`
+  if test x"$errors" = x && test -f conftest.$ac_objext; then
+    acx_cv_d_compiler_works=yes
+  fi
+  rm -f conftest.*
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_cv_d_compiler_works" >&5
+$as_echo "$acx_cv_d_compiler_works" >&6; }
+if test "x$GDC" != xno && test x$acx_cv_d_compiler_works != xno; then
   have_gdc=yes
 else
   have_gdc=no
@@ -5423,6 +5442,9 @@ case "${target}" in
 	# sets the default TLS model and affects inlining.
 	PICFLAG_FOR_TARGET=-fPIC
 	;;
+    loongarch*-*-*)
+	PICFLAG_FOR_TARGET=-fpic
+	;;
     mips-sgi-irix6*)
 	# PIC is the default.
 	;;
@@ -7865,6 +7887,9 @@ else
     mips*-*-*)
       enable_fixed_point=yes
       ;;
+    loongarch*-*-*)
+      enable_fixed_point=yes
+      ;;
     *)
       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: fixed-point is not supported for this target, ignored" >&5
 $as_echo "$as_me: WARNING: fixed-point is not supported for this target, ignored" >&2;}
@@ -19561,7 +19586,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19564 "configure"
+#line 19589 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -19667,7 +19692,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19670 "configure"
+#line 19695 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -25432,6 +25457,17 @@ foo:	data8	25
 	movl	r24 = @tprel(foo#)'
 	tls_as_opt=--fatal-warnings
 	;;
+  loongarch*-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:	.word 2
+	.text
+	la.tls.gd $a0,x
+	bl __tls_get_addr'
+	tls_first_major=0
+	tls_first_minor=0
+	tls_as_opt='--fatal-warnings'
+	;;
   microblaze*-*-*)
     conftest_s='
 	.section .tdata,"awT",@progbits
@@ -28654,6 +28690,43 @@ $as_echo "#define HAVE_AS_MARCH_ZIFENCEI 1" >>confdefs.h
 fi
 
     ;;
+  loongarch*-*-*)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .dtprelword support" >&5
+$as_echo_n "checking assembler for .dtprelword support... " >&6; }
+if ${gcc_cv_as_loongarch_dtprelword+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_loongarch_dtprelword=no
+  if test x$gcc_cv_as != x; then
+    $as_echo '' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags 2,18,0 -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+	.dtprelword x+0x8000
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_loongarch_dtprelword" >&5
+$as_echo "$gcc_cv_as_loongarch_dtprelword" >&6; }
+
+if test $gcc_cv_as_loongarch_dtprelword != yes; then
+
+$as_echo "#define HAVE_AS_DTPRELWORD 1" >>confdefs.h
+
+fi
+    ;;
     s390*-*-*)
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .gnu_attribute support" >&5
 $as_echo_n "checking assembler for .gnu_attribute support... " >&6; }
@@ -28817,11 +28890,11 @@ fi
     ;;
 esac
 
-# Mips and HP-UX need the GNU assembler.
+# Mips, LoongArch and HP-UX need the GNU assembler.
 # Linux on IA64 might be able to use the Intel assembler.
 
 case "$target" in
-  mips*-*-* | *-*-hpux* )
+  mips*-*-* | loongarch*-*-* | *-*-hpux* )
     if test x$gas_flag = xyes \
        || test x"$host" != x"$build" \
        || test ! -x "$gcc_cv_as" \
@@ -29258,8 +29331,8 @@ esac
 # ??? Once 2.11 is released, probably need to add first known working
 # version to the per-target configury.
 case "$cpu_type" in
-  aarch64 | alpha | arc | arm | avr | bfin | cris | csky | i386 | m32c | m68k \
-  | microblaze | mips | nds32 | nios2 | pa | riscv | rs6000 | score | sparc \
+  aarch64 | alpha | arc | arm | avr | bfin | cris | csky | i386 | loongarch | m32c \
+  | m68k | microblaze | mips | nds32 | nios2 | pa | riscv | rs6000 | score | sparc \
   | tilegx | tilepro | visium | xstormy16 | xtensa)
     insn="nop"
     ;;
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 04/12] LoongArch Port: Machine Decsription files.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (2 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 03/12] LoongArch Port: Regenerate gcc/configure chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2022-01-06 17:54   ` Xi Ruoyao
  2021-12-24  9:28 ` [PATCH v4 05/12] LoongArch Port: Machine description C files and .h files chenglulu
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/
	* config/loongarch/constraints.md: New file.
	* config/loongarch/generic.md: New file.
	* config/loongarch/la464.md: New file.
	* config/loongarch/loongarch-ftypes.def: New file.
	* config/loongarch/loongarch-modes.def: New file.
	* config/loongarch/loongarch.md: New file.
	* config/loongarch/predicates.md: New file.
	* config/loongarch/sync.md: New file.
---
 gcc/config/loongarch/constraints.md       |  212 ++
 gcc/config/loongarch/generic.md           |  132 +
 gcc/config/loongarch/la464.md             |  132 +
 gcc/config/loongarch/loongarch-ftypes.def |  106 +
 gcc/config/loongarch/loongarch-modes.def  |   29 +
 gcc/config/loongarch/loongarch.md         | 3829 +++++++++++++++++++++
 gcc/config/loongarch/predicates.md        |  553 +++
 gcc/config/loongarch/sync.md              |  574 +++
 8 files changed, 5567 insertions(+)
 create mode 100644 gcc/config/loongarch/constraints.md
 create mode 100644 gcc/config/loongarch/generic.md
 create mode 100644 gcc/config/loongarch/la464.md
 create mode 100644 gcc/config/loongarch/loongarch-ftypes.def
 create mode 100644 gcc/config/loongarch/loongarch-modes.def
 create mode 100644 gcc/config/loongarch/loongarch.md
 create mode 100644 gcc/config/loongarch/predicates.md
 create mode 100644 gcc/config/loongarch/sync.md

diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md
new file mode 100644
index 00000000000..db88a042c58
--- /dev/null
+++ b/gcc/config/loongarch/constraints.md
@@ -0,0 +1,212 @@
+;; Constraint definitions for LoongArch.
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;;
+;; This file is part of GCC.
+;;
+;; GCC 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 3, or (at your option)
+;; any later version.
+;;
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+;; Register constraints
+
+;; "a" "A constant call global and noplt address."
+;; "b" <-----unused
+;; "c" "A constant call local address."
+;; "d" <-----unused
+;; "e" JIRL_REGS
+;; "f" FP_REGS
+;; "g" <-----unused
+;; "h" "A constant call plt address."
+;; "i" "Matches a general integer constant." (Global non-architectural)
+;; "j" SIBCALL_REGS
+;; "k" <-----unused
+;; "l" "A signed 16-bit constant."
+;; "m" "A memory operand whose address is formed by a base register and offset
+;;      that is suitable for use in instructions with the same addressing mode
+;;      as @code{st.w} and @code{ld.w}."
+;; "n" "Matches a non-symbolic integer constant." (Global non-architectural)
+;; "o" "Matches an offsettable memory reference." (Global non-architectural)
+;; "p" "Matches a general address." (Global non-architectural)
+;; "q" CSR_REGS
+;; "r" GENERAL_REGS (Global non-architectural)
+;; "s" "Matches a symbolic integer constant." (Global non-architectural)
+;; "t" "A constant call weak address"
+;; "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)"
+;; "v" "A signed 64-bit constant and low 44-bit is zero (for logic instructions)."
+;; "w" "Matches any valid memory."
+;; "x" <-----unused
+;; "y" <-----unused
+;; "z" FCC_REGS
+;; "A" <-----unused
+;; "B" <-----unused
+;; "C" <-----unused
+;; "D" <-----unused
+;; "E" "Matches a floating-point constant." (Global non-architectural)
+;; "F" "Matches a floating-point constant." (Global non-architectural)
+;; "G" "Floating-point zero."
+;; "H" <-----unused
+;; "I" "A signed 12-bit constant (for arithmetic instructions)."
+;; "J" "Integer zero."
+;; "K" "An unsigned 12-bit constant (for logic instructions)."
+;; "L" <-----unused
+;; "M" <-----unused
+;; "N" <-----unused
+;; "O" <-----unused
+;; "P" <-----unused
+;; "Q" "A signed 12-bit constant"
+;; "R" <-----unused
+;; "S" <-----unused
+;; "T" <-----unused
+;; "U" <-----unused
+;; "V" "Matches a non-offsettable memory reference." (Global non-architectural)
+;; "W" "A memory address based on a member of @code{BASE_REG_CLASS}.  This is
+;;     true for all references."
+;; "X" "Matches anything." (Global non-architectural)
+;; "Y" -
+;;    "Yd"
+;;       "A constant @code{move_operand} that can be safely loaded using
+;;	  @code{la}."
+;;    "Yx"
+;; "Z" -
+;;    "ZC"
+;;      "A memory operand whose address is formed by a base register and offset
+;;       that is suitable for use in instructions with the same addressing mode
+;;       as @code{ll.w} and @code{sc.w}."
+;;    "ZB"
+;;      "An address that is held in a general-purpose register.
+;;      The offset is zero"
+;; "<" "Matches a pre-dec or post-dec operand." (Global non-architectural)
+;; ">" "Matches a pre-inc or post-inc operand." (Global non-architectural)
+
+(define_constraint "a"
+  "@internal
+   A constant call global and noplt address."
+  (match_operand 0 "is_const_call_global_noplt_symbol"))
+
+(define_constraint "c"
+  "@internal
+   A constant call local address."
+  (match_operand 0 "is_const_call_local_symbol"))
+
+(define_register_constraint "e" "JIRL_REGS"
+  "@internal")
+
+(define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS"
+  "A floating-point register (if available).")
+
+(define_constraint "h"
+  "@internal
+   A constant call plt address."
+  (match_operand 0 "is_const_call_plt_symbol"))
+
+(define_register_constraint "j" "SIBCALL_REGS"
+  "@internal")
+
+(define_constraint "l"
+"A signed 16-bit constant."
+(and (match_code "const_int")
+     (match_test "IMM16_OPERAND (ival)")))
+
+(define_memory_constraint "m"
+  "A memory operand whose address is formed by a base register and offset
+   that is suitable for use in instructions with the same addressing mode
+   as @code{st.w} and @code{ld.w}."
+  (and (match_code "mem")
+       (match_test "loongarch_12bit_offset_address_p (XEXP (op, 0), mode)")))
+
+(define_register_constraint "q" "CSR_REGS"
+  "A general-purpose register except for $r0 and $r1 for lcsr.")
+
+(define_constraint "t"
+  "@internal
+   A constant call weak address."
+  (match_operand 0 "is_const_call_weak_symbol"))
+
+(define_constraint "u"
+  "A signed 52bit constant and low 32-bit is zero (for logic instructions)."
+  (and (match_code "const_int")
+       (match_test "LU32I_OPERAND (ival)")))
+
+(define_constraint "v"
+  "A nsigned 64-bit constant and low 44-bit is zero (for logic instructions)."
+  (and (match_code "const_int")
+       (match_test "LU52I_OPERAND (ival)")))
+
+(define_register_constraint "z" "FCC_REGS"
+  "A floating-point condition code register.")
+
+;; Floating-point constraints
+
+(define_constraint "G"
+  "Floating-point zero."
+  (and (match_code "const_double")
+       (match_test "op == CONST0_RTX (mode)")))
+
+;; Integer constraints
+
+(define_constraint "I"
+  "A signed 12-bit constant (for arithmetic instructions)."
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND (ival)")))
+
+(define_constraint "J"
+  "Integer zero."
+  (and (match_code "const_int")
+       (match_test "ival == 0")))
+
+(define_constraint "K"
+  "An unsigned 12-bit constant (for logic instructions)."
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND_UNSIGNED (ival)")))
+
+;; General constraints
+
+(define_constraint "Q"
+  "@internal"
+  (match_operand 0 "const_arith_operand"))
+
+(define_memory_constraint "W"
+  "@internal
+   A memory address based on a member of @code{BASE_REG_CLASS}.  This is
+   true for allreferences."
+  (and (match_code "mem")
+       (match_operand 0 "memory_operand")
+	    (and (not (match_operand 0 "stack_operand"))
+		 (not (match_test "CONSTANT_P (XEXP (op, 0))")))))
+
+(define_constraint "Yd"
+  "@internal
+   A constant @code{move_operand} that can be safely loaded using
+   @code{la}."
+  (and (match_operand 0 "move_operand")
+       (match_test "CONSTANT_P (op)")))
+
+(define_constraint "Yx"
+   "@internal"
+   (match_operand 0 "low_bitmask_operand"))
+
+(define_memory_constraint "ZC"
+  "A memory operand whose address is formed by a base register and offset
+   that is suitable for use in instructions with the same addressing mode
+   as @code{ll.w} and @code{sc.w}."
+  (and (match_code "mem")
+       (match_test "loongarch_14bit_shifted_offset_address_p (XEXP (op, 0), mode)")))
+
+(define_memory_constraint "ZB"
+  "@internal
+  An address that is held in a general-purpose register.
+  The offset is zero"
+  (and (match_code "mem")
+       (match_test "GET_CODE (XEXP (op,0)) == REG")))
+
diff --git a/gcc/config/loongarch/generic.md b/gcc/config/loongarch/generic.md
new file mode 100644
index 00000000000..d1dc2d7fa0c
--- /dev/null
+++ b/gcc/config/loongarch/generic.md
@@ -0,0 +1,132 @@
+;; Generic DFA-based pipeline description for LoongArch targets
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;; Based on MIPS target for GNU compiler.
+
+;; This file is part of GCC.
+
+;; GCC 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 3, or (at your
+;; option) any later version.
+
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+
+;; Pipeline descriptions.
+;;
+;; generic.md provides a fallback for processors without a specific
+;; pipeline description.  It is derived from the old define_function_unit
+;; version and uses the "alu" and "imuldiv" units declared below.
+;;
+;; Some of the processor-specific files are also derived from old
+;; define_function_unit descriptions and simply override the parts of
+;; generic.md that don't apply.  The other processor-specific files
+;; are self-contained.
+(define_automaton "alu,imuldiv")
+
+(define_cpu_unit "alu" "alu")
+(define_cpu_unit "imuldiv" "imuldiv")
+
+;; Ghost instructions produce no real code and introduce no hazards.
+;; They exist purely to express an effect on dataflow.
+(define_insn_reservation "ghost" 0
+  (eq_attr "type" "ghost")
+  "nothing")
+
+;; This file is derived from the old define_function_unit description.
+;; Each reservation can be overridden on a processor-by-processor basis.
+
+(define_insn_reservation "generic_alu" 1
+  (eq_attr "type" "unknown,prefetch,prefetchx,condmove,const,arith,
+		   shift,slt,clz,trap,multi,nop,logical,signext,move")
+  "alu")
+
+(define_insn_reservation "generic_load" 3
+  (eq_attr "type" "load,fpload,fpidxload")
+  "alu")
+
+(define_insn_reservation "generic_store" 1
+  (eq_attr "type" "store,fpstore,fpidxstore")
+  "alu")
+
+(define_insn_reservation "generic_xfer" 2
+  (eq_attr "type" "mftg,mgtf")
+  "alu")
+
+(define_insn_reservation "generic_branch" 1
+  (eq_attr "type" "branch,jump,call")
+  "alu")
+
+(define_insn_reservation "generic_imul" 17
+  (eq_attr "type" "imul")
+  "imuldiv*17")
+
+(define_insn_reservation "generic_fcvt" 1
+  (eq_attr "type" "fcvt")
+  "alu")
+
+(define_insn_reservation "generic_fmove" 2
+  (eq_attr "type" "fabs,fneg,fmove")
+  "alu")
+
+(define_insn_reservation "generic_fcmp" 3
+  (eq_attr "type" "fcmp")
+  "alu")
+
+(define_insn_reservation "generic_fadd" 4
+  (eq_attr "type" "fadd")
+  "alu")
+
+(define_insn_reservation "generic_fmul_single" 7
+  (and (eq_attr "type" "fmul,fmadd")
+       (eq_attr "mode" "SF"))
+  "alu")
+
+(define_insn_reservation "generic_fmul_double" 8
+  (and (eq_attr "type" "fmul,fmadd")
+       (eq_attr "mode" "DF"))
+  "alu")
+
+(define_insn_reservation "generic_fdiv_single" 23
+  (and (eq_attr "type" "fdiv,frdiv")
+       (eq_attr "mode" "SF"))
+  "alu")
+
+(define_insn_reservation "generic_fdiv_double" 36
+  (and (eq_attr "type" "fdiv,frdiv")
+       (eq_attr "mode" "DF"))
+  "alu")
+
+(define_insn_reservation "generic_fsqrt_single" 54
+  (and (eq_attr "type" "fsqrt,frsqrt")
+       (eq_attr "mode" "SF"))
+  "alu")
+
+(define_insn_reservation "generic_fsqrt_double" 112
+  (and (eq_attr "type" "fsqrt,frsqrt")
+       (eq_attr "mode" "DF"))
+  "alu")
+
+(define_insn_reservation "generic_atomic" 10
+  (eq_attr "type" "atomic")
+  "alu")
+
+;; Sync loop consists of (in order)
+;; (1) optional sync,
+;; (2) LL instruction,
+;; (3) branch and 1-2 ALU instructions,
+;; (4) SC instruction,
+;; (5) branch and ALU instruction.
+;; The net result of this reservation is a big delay with a flush of
+;; ALU pipeline.
+(define_insn_reservation "generic_sync_loop" 40
+  (eq_attr "type" "syncloop")
+  "alu*39")
diff --git a/gcc/config/loongarch/la464.md b/gcc/config/loongarch/la464.md
new file mode 100644
index 00000000000..3a8cc6051ff
--- /dev/null
+++ b/gcc/config/loongarch/la464.md
@@ -0,0 +1,132 @@
+;; Pipeline model for LoongArch la464 cores.
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+
+;; This file is part of GCC.
+;;
+;; GCC 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 3, or (at your
+;; option) any later version.
+;;
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+;; Uncomment the following line to output automata for debugging.
+;; (automata_option "v")
+
+;; Automaton for integer instructions.
+(define_automaton "la464_a_alu")
+
+;; Automaton for floating-point instructions.
+(define_automaton "la464_a_falu")
+
+;; Automaton for memory operations.
+(define_automaton "la464_a_mem")
+
+;; Describe the resources.
+
+(define_cpu_unit "la464_alu1" "la464_a_alu")
+(define_cpu_unit "la464_alu2" "la464_a_alu")
+(define_cpu_unit "la464_mem1" "la464_a_mem")
+(define_cpu_unit "la464_mem2" "la464_a_mem")
+(define_cpu_unit "la464_falu1" "la464_a_falu")
+(define_cpu_unit "la464_falu2" "la464_a_falu")
+
+;; Describe instruction reservations.
+
+(define_insn_reservation "la464_arith" 1
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "arith,clz,const,logical,
+			move,nop,shift,signext,slt"))
+  "la464_alu1 | la464_alu2")
+
+(define_insn_reservation "la464_branch" 1
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "branch,jump,call,condmove,trap"))
+  "la464_alu1 | la464_alu2")
+
+(define_insn_reservation "la464_imul" 7
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "imul"))
+  "la464_alu1 | la464_alu2")
+
+(define_insn_reservation "la464_idiv_si" 12
+  (and (match_test "TARGET_ARCH_LA464")
+       (and (eq_attr "type" "idiv")
+	    (eq_attr "mode" "SI")))
+  "la464_alu1 | la464_alu2")
+
+(define_insn_reservation "la464_idiv_di" 25
+  (and (match_test "TARGET_ARCH_LA464")
+       (and (eq_attr "type" "idiv")
+	    (eq_attr "mode" "DI")))
+  "la464_alu1 | la464_alu2")
+
+(define_insn_reservation "la464_load" 4
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "load"))
+  "la464_mem1 | la464_mem2")
+
+(define_insn_reservation "la464_gpr_fp" 16
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "mftg,mgtf"))
+  "la464_mem1")
+
+(define_insn_reservation "la464_fpload" 4
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "fpload"))
+  "la464_mem1 | la464_mem2")
+
+(define_insn_reservation "la464_prefetch" 0
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "prefetch,prefetchx"))
+  "la464_mem1 | la464_mem2")
+
+(define_insn_reservation "la464_store" 0
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "store,fpstore,fpidxstore"))
+  "la464_mem1 | la464_mem2")
+
+(define_insn_reservation "la464_fadd" 4
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "fadd,fmul,fmadd"))
+  "la464_falu1 | la464_falu2")
+
+(define_insn_reservation "la464_fcmp" 2
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "fabs,fcmp,fmove,fneg"))
+  "la464_falu1 | la464_falu2")
+
+(define_insn_reservation "la464_fcvt" 4
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "fcvt"))
+  "la464_falu1 | la464_falu2")
+
+(define_insn_reservation "la464_fdiv_sf" 12
+  (and (match_test "TARGET_ARCH_LA464")
+       (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+	    (eq_attr "mode" "SF")))
+  "la464_falu1 | la464_falu2")
+
+(define_insn_reservation "la464_fdiv_df" 19
+  (and (match_test "TARGET_ARCH_LA464")
+       (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+	    (eq_attr "mode" "DF")))
+  "la464_falu1 | la464_falu2")
+
+;; Force single-dispatch for unknown or multi.
+(define_insn_reservation "la464_unknown" 1
+  (and (match_test "TARGET_ARCH_LA464")
+       (eq_attr "type" "unknown,multi,atomic,syncloop"))
+  "la464_alu1 + la464_alu2 + la464_falu1
+   + la464_falu2 + la464_mem1 + la464_mem2")
+
+;; End of DFA-based pipeline description for la464
diff --git a/gcc/config/loongarch/loongarch-ftypes.def b/gcc/config/loongarch/loongarch-ftypes.def
new file mode 100644
index 00000000000..e370337df1d
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-ftypes.def
@@ -0,0 +1,106 @@
+/* Definitions of prototypes for LoongArch built-in functions.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS target for GNU ckompiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Invoke DEF_LARCH_FTYPE (NARGS, LIST) for each prototype used by
+   LoongArch built-in functions, where:
+
+      NARGS is the number of arguments.
+      LIST contains the return-type code followed by the codes for each
+      argument type.
+
+   Argument- and return-type codes are either modes or one of the following:
+
+      VOID for void_type_node
+      INT for integer_type_node
+      POINTER for ptr_type_node
+
+   (we don't use PTR because that's a ANSI-compatibillity macro).
+
+   Please keep this list lexicographically sorted by the LIST argument.  */
+
+DEF_LARCH_FTYPE (1, (DF, DF))
+DEF_LARCH_FTYPE (2, (DF, DF, DF))
+
+DEF_LARCH_FTYPE (1, (DI, DI))
+DEF_LARCH_FTYPE (2, (DI, DI, DI))
+DEF_LARCH_FTYPE (3, (DI, DI, DI, QI))
+DEF_LARCH_FTYPE (2, (DI, DI, SI))
+DEF_LARCH_FTYPE (3, (DI, DI, SI, SI))
+DEF_LARCH_FTYPE (2, (DI, DI, UQI))
+DEF_LARCH_FTYPE (3, (DI, DI, USI, USI))
+DEF_LARCH_FTYPE (2, (DI, POINTER, SI))
+DEF_LARCH_FTYPE (1, (DI, SI))
+DEF_LARCH_FTYPE (2, (DI, SI, SI))
+DEF_LARCH_FTYPE (1, (DI, UQI))
+DEF_LARCH_FTYPE (2, (DI, USI, USI))
+
+DEF_LARCH_FTYPE (2, (HI, HI, HI))
+
+DEF_LARCH_FTYPE (2, (INT, DF, DF))
+DEF_LARCH_FTYPE (2, (INT, SF, SF))
+
+DEF_LARCH_FTYPE (2, (QI, QI, QI))
+
+DEF_LARCH_FTYPE (1, (SF, SF))
+DEF_LARCH_FTYPE (2, (SF, SF, SF))
+
+DEF_LARCH_FTYPE (2, (SI, DI, SI))
+DEF_LARCH_FTYPE (2, (SI, HI, SI))
+DEF_LARCH_FTYPE (2, (SI, POINTER, SI))
+DEF_LARCH_FTYPE (2, (SI, QI, SI))
+DEF_LARCH_FTYPE (1, (SI, SI))
+DEF_LARCH_FTYPE (2, (SI, SI, SI))
+DEF_LARCH_FTYPE (3, (SI, SI, SI, QI))
+DEF_LARCH_FTYPE (3, (SI, SI, SI, SI))
+DEF_LARCH_FTYPE (2, (SI, SI, UQI))
+DEF_LARCH_FTYPE (1, (SI, UDI))
+DEF_LARCH_FTYPE (1, (SI, UQI))
+DEF_LARCH_FTYPE (1, (SI, VOID))
+
+DEF_LARCH_FTYPE (2, (UDI, UDI, UDI))
+DEF_LARCH_FTYPE (3, (UDI, UDI, UDI, USI))
+DEF_LARCH_FTYPE (2, (UDI, UDI, USI))
+DEF_LARCH_FTYPE (1, (UDI, USI))
+
+DEF_LARCH_FTYPE (1, (UHI, USI))
+
+DEF_LARCH_FTYPE (1, (UQI, USI))
+
+DEF_LARCH_FTYPE (1, (USI, UQI))
+DEF_LARCH_FTYPE (1, (USI, USI))
+DEF_LARCH_FTYPE (2, (USI, USI, USI))
+DEF_LARCH_FTYPE (3, (USI, USI, USI, USI))
+DEF_LARCH_FTYPE (1, (USI, VOID))
+
+DEF_LARCH_FTYPE (2, (VOID, DI, DI))
+DEF_LARCH_FTYPE (2, (VOID, DI, UQI))
+DEF_LARCH_FTYPE (2, (VOID, SI, CVPOINTER))
+DEF_LARCH_FTYPE (2, (VOID, SI, SI))
+DEF_LARCH_FTYPE (2, (VOID, SI, UQI))
+DEF_LARCH_FTYPE (2, (VOID, UDI, USI))
+DEF_LARCH_FTYPE (2, (VOID, UHI, USI))
+DEF_LARCH_FTYPE (2, (VOID, UQI, SI))
+DEF_LARCH_FTYPE (2, (VOID, UQI, USI))
+DEF_LARCH_FTYPE (1, (VOID, USI))
+DEF_LARCH_FTYPE (3, (VOID, USI, UDI, SI))
+DEF_LARCH_FTYPE (2, (VOID, USI, UQI))
+DEF_LARCH_FTYPE (2, (VOID, USI, USI))
+DEF_LARCH_FTYPE (3, (VOID, USI, USI, SI))
diff --git a/gcc/config/loongarch/loongarch-modes.def b/gcc/config/loongarch/loongarch-modes.def
new file mode 100644
index 00000000000..b33e94504f1
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-modes.def
@@ -0,0 +1,29 @@
+/* LoongArch extra machine modes.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS target for GNU compiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+FLOAT_MODE (TF, 16, ieee_quad_format);
+
+VECTOR_MODES (FLOAT, 8);      /*       V4HF V2SF */
+
+/* For floating point conditions in FCC registers.  */
+CC_MODE (FCC);
+
+INT_MODE (OI, 32);
diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
new file mode 100644
index 00000000000..15611d60686
--- /dev/null
+++ b/gcc/config/loongarch/loongarch.md
@@ -0,0 +1,3829 @@
+;; Machine Description for LoongArch for GNU compiler.
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;; Based on MIPS target for GNU compiler.
+
+;; This file is part of GCC.
+
+;; GCC 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 3, or (at your option)
+;; any later version.
+
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_c_enum "unspec" [
+  ;; Integer operations that are too cumbersome to describe directly.
+  UNSPEC_REVB_2H
+  UNSPEC_REVB_4H
+  UNSPEC_REVH_D
+
+  ;; Floating-point moves.
+  UNSPEC_LOAD_LOW
+  UNSPEC_LOAD_HIGH
+  UNSPEC_STORE_WORD
+  UNSPEC_MOVGR2FRH
+  UNSPEC_MOVFRH2GR
+
+  ;; Floating-point environment.
+  UNSPEC_MOVFCSR2GR
+  UNSPEC_MOVGR2FCSR
+
+  ;; GP manipulation.
+  UNSPEC_EH_RETURN
+
+  ;;
+  UNSPEC_FRINT
+  UNSPEC_FCLASS
+  UNSPEC_BYTEPICK_W
+  UNSPEC_BYTEPICK_D
+  UNSPEC_BITREV_4B
+  UNSPEC_BITREV_8B
+
+  ;; Symbolic accesses.
+  UNSPEC_LOAD_CALL
+
+  ;; Blockage and synchronisation.
+  UNSPEC_BLOCKAGE
+  UNSPEC_DBAR
+  UNSPEC_IBAR
+
+  ;; CPUCFG
+  UNSPEC_CPUCFG
+  UNSPEC_ASRTLE_D
+  UNSPEC_ASRTGT_D
+
+  UNSPEC_CSRRD
+  UNSPEC_CSRWR
+  UNSPEC_CSRXCHG
+  UNSPEC_IOCSRRD
+  UNSPEC_IOCSRWR
+
+  ;; CACOP
+  UNSPEC_CACOP
+
+  ;; PTE
+  UNSPEC_LDDIR
+  UNSPEC_LDPTE
+
+  ;; Interrupt handling.
+  UNSPEC_ERTN
+
+  ;; Used in a call expression in place of args_size.  It's present for PIC
+  ;; indirect calls where it contains args_size and the function symbol.
+  UNSPEC_CALL_ATTR
+
+  ;; Stack checking.
+  UNSPEC_PROBE_STACK_RANGE
+
+  ;; TLS
+  UNSPEC_TLS_GD
+  UNSPEC_TLS_LD
+  UNSPEC_TLS_LE
+  UNSPEC_TLS_IE
+
+  ;; load immediate
+  UNSPEC_LU52I_D
+
+  UNSPEC_TIE
+
+  ;; Register save and restore.
+  UNSPECV_GPR_SAVE
+  UNSPECV_GPR_RESTORE
+
+  ;; CRC
+  UNSPEC_CRC
+  UNSPEC_CRCC
+])
+
+(define_constants
+  [(RETURN_ADDR_REGNUM		1)
+   (T0_REGNUM			12)
+   (T1_REGNUM			13)
+   (S0_REGNUM			23)
+
+   ;; PIC long branch sequences are never longer than 100 bytes.
+   (MAX_PIC_BRANCH_LENGTH	100)
+])
+
+(include "predicates.md")
+(include "constraints.md")
+\f
+;; ....................
+;;
+;;	Attributes
+;;
+;; ....................
+
+(define_attr "got" "unset,load"
+  (const_string "unset"))
+
+;; For jirl instructions, this attribute is DIRECT when the target address
+;; is symbolic and INDIRECT when it is a register.
+(define_attr "jirl" "unset,direct,indirect"
+  (const_string "unset"))
+
+
+;; Classification of moves, extensions and truncations.  Most values
+;; are as for "type" (see below) but there are also the following
+;; move-specific values:
+;;
+;; sll0		"slli.w DEST,SRC,0", which on 64-bit targets is guaranteed
+;;		to produce a sign-extended DEST, even if SRC is not
+;;		properly sign-extended
+;; pick_ins	BSTRPICK.W, BSTRPICK.D, BSTRINS.W or BSTRINS.D instruction
+;; andi		a single ANDI instruction
+;; shift_shift	a shift left followed by a shift right
+;;
+;; This attribute is used to determine the instruction's length and
+;; scheduling type.  For doubleword moves, the attribute always describes
+;; the split instructions; in some cases, it is more appropriate for the
+;; scheduling type to be "multi" instead.
+(define_attr "move_type"
+  "unknown,load,fpload,store,fpstore,mgtf,mftg,imul,move,fmove,
+   const,signext,pick_ins,logical,arith,sll0,andi,shift_shift"
+  (const_string "unknown"))
+
+(define_attr "alu_type" "unknown,add,sub,not,nor,and,or,xor"
+  (const_string "unknown"))
+
+;; Main data type used by the insn
+(define_attr "mode" "unknown,none,QI,HI,SI,DI,TI,OI,SF,DF,TF,FCC"
+  (const_string "unknown"))
+
+;; True if the main data type is twice the size of a word.
+(define_attr "dword_mode" "no,yes"
+  (cond [(and (eq_attr "mode" "DI,DF")
+	      (not (match_test "TARGET_64BIT")))
+	 (const_string "yes")
+
+	 (and (eq_attr "mode" "TI,TF")
+	      (match_test "TARGET_64BIT"))
+	 (const_string "yes")]
+	(const_string "no")))
+
+;; True if the main data type is four times of the size of a word.
+(define_attr "qword_mode" "no,yes"
+  (cond [(and (eq_attr "mode" "TI,TF")
+	      (not (match_test "TARGET_64BIT")))
+	 (const_string "yes")]
+	(const_string "no")))
+
+;; True if the main data type is eight times of the size of a word.
+(define_attr "oword_mode" "no,yes"
+  (cond [(and (eq_attr "mode" "OI")
+	      (not (match_test "TARGET_64BIT")))
+	 (const_string "yes")]
+	(const_string "no")))
+
+;; Classification of each insn.
+;; branch	conditional branch
+;; jump		unconditional jump
+;; call		unconditional call
+;; load		load instruction(s)
+;; fpload	floating point load
+;; fpidxload    floating point indexed load
+;; store	store instruction(s)
+;; fpstore	floating point store
+;; fpidxstore	floating point indexed store
+;; prefetch	memory prefetch (register + offset)
+;; prefetchx	memory indexed prefetch (register + register)
+;; condmove	conditional moves
+;; mgtf		move generate register to float register
+;; mftg		move float register to generate register
+;; const	load constant
+;; arith	integer arithmetic instructions
+;; logical      integer logical instructions
+;; shift	integer shift instructions
+;; slt		set less than instructions
+;; signext      sign extend instructions
+;; clz		the clz and clo instructions
+;; trap		trap if instructions
+;; imul		integer multiply
+;; idiv	integer divide
+;; move		integer register move ({,D}ADD{,U} with rt = 0)
+;; fmove	floating point register move
+;; fadd		floating point add/subtract
+;; fmul		floating point multiply
+;; fmadd	floating point multiply-add
+;; fdiv		floating point divide
+;; frdiv	floating point reciprocal divide
+;; fabs		floating point absolute value
+;; fneg		floating point negation
+;; fcmp		floating point compare
+;; fcvt		floating point convert
+;; fsqrt	floating point square root
+;; frsqrt       floating point reciprocal square root
+;; multi	multiword sequence (or user asm statements)
+;; atomic	atomic memory update instruction
+;; syncloop	memory atomic operation implemented as a sync loop
+;; nop		no operation
+;; ghost	an instruction that produces no real code
+(define_attr "type"
+  "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,
+   prefetch,prefetchx,condmove,mgtf,mftg,const,arith,logical,
+   shift,slt,signext,clz,trap,imul,idiv,move,
+   fmove,fadd,fmul,fmadd,fdiv,frdiv,fabs,fneg,fcmp,fcvt,fsqrt,
+   frsqrt,accext,accmod,multi,atomic,syncloop,nop,ghost"
+  (cond [(eq_attr "jirl" "!unset") (const_string "call")
+	 (eq_attr "got" "load") (const_string "load")
+
+	 (eq_attr "alu_type" "add,sub") (const_string "arith")
+
+	 (eq_attr "alu_type" "not,nor,and,or,xor") (const_string "logical")
+
+	 ;; If a doubleword move uses these expensive instructions,
+	 ;; it is usually better to schedule them in the same way
+	 ;; as the singleword form, rather than as "multi".
+	 (eq_attr "move_type" "load") (const_string "load")
+	 (eq_attr "move_type" "fpload") (const_string "fpload")
+	 (eq_attr "move_type" "store") (const_string "store")
+	 (eq_attr "move_type" "fpstore") (const_string "fpstore")
+	 (eq_attr "move_type" "mgtf") (const_string "mgtf")
+	 (eq_attr "move_type" "mftg") (const_string "mftg")
+
+	 ;; These types of move are always single insns.
+	 (eq_attr "move_type" "imul") (const_string "imul")
+	 (eq_attr "move_type" "fmove") (const_string "fmove")
+	 (eq_attr "move_type" "signext") (const_string "signext")
+	 (eq_attr "move_type" "pick_ins") (const_string "arith")
+	 (eq_attr "move_type" "arith") (const_string "arith")
+	 (eq_attr "move_type" "logical") (const_string "logical")
+	 (eq_attr "move_type" "sll0") (const_string "shift")
+	 (eq_attr "move_type" "andi") (const_string "logical")
+
+	 ;; These types of move are always split.
+	 (eq_attr "move_type" "shift_shift")
+	   (const_string "multi")
+
+	 ;; These types of move are split for octaword modes only.
+	 (and (eq_attr "move_type" "move,const")
+	      (eq_attr "oword_mode" "yes"))
+	   (const_string "multi")
+
+	 ;; These types of move are split for quadword modes only.
+	 (and (eq_attr "move_type" "move,const")
+	      (eq_attr "qword_mode" "yes"))
+	   (const_string "multi")
+
+	 ;; These types of move are split for doubleword modes only.
+	 (and (eq_attr "move_type" "move,const")
+	      (eq_attr "dword_mode" "yes"))
+	   (const_string "multi")
+	 (eq_attr "move_type" "move") (const_string "move")
+	 (eq_attr "move_type" "const") (const_string "const")]
+	(const_string "unknown")))
+
+;; Mode for conversion types (fcvt)
+;; I2S	integer to float single (SI/DI to SF)
+;; I2D	integer to float double (SI/DI to DF)
+;; S2I	float to integer (SF to SI/DI)
+;; D2I	float to integer (DF to SI/DI)
+;; D2S	double to float single
+;; S2D	float single to double
+
+(define_attr "cnv_mode" "unknown,I2S,I2D,S2I,D2I,D2S,S2D"
+  (const_string "unknown"))
+
+(define_attr "compression" "none,all"
+  (const_string "none"))
+
+;; The number of individual instructions that a non-branch pattern generates
+(define_attr "insn_count" ""
+  (cond [;; "Ghost" instructions occupy no space.
+	 (eq_attr "type" "ghost")
+	 (const_int 0)
+
+	 ;; Check for doubleword moves that are decomposed into two
+	 ;; instructions.
+	 (and (eq_attr "move_type" "mgtf,mftg,move")
+	      (eq_attr "dword_mode" "yes"))
+	 (const_int 2)
+
+	 ;; Check for quadword moves that are decomposed into four
+	 ;; instructions.
+	 (and (eq_attr "move_type" "mgtf,mftg,move")
+	      (eq_attr "qword_mode" "yes"))
+	 (const_int 4)
+
+	 ;; Check for Octaword moves that are decomposed into eight
+	 ;; instructions.
+	 (and (eq_attr "move_type" "mgtf,mftg,move")
+	      (eq_attr "oword_mode" "yes"))
+	 (const_int 8)
+
+	 ;; Constants, loads and stores are handled by external routines.
+	 (and (eq_attr "move_type" "const")
+	      (eq_attr "dword_mode" "yes"))
+	 (symbol_ref "loongarch_split_const_insns (operands[1])")
+	 (eq_attr "move_type" "const")
+	 (symbol_ref "loongarch_const_insns (operands[1])")
+	 (eq_attr "move_type" "load,fpload")
+	 (symbol_ref "loongarch_load_store_insns (operands[1], insn)")
+	 (eq_attr "move_type" "store,fpstore")
+	 (symbol_ref "loongarch_load_store_insns (operands[0], insn)")
+
+	 (eq_attr "type" "idiv")
+	 (symbol_ref "loongarch_idiv_insns (GET_MODE (PATTERN (insn)))")]
+(const_int 1)))
+
+;; Length of instruction in bytes.
+(define_attr "length" ""
+   (cond [
+	;; Branch instructions have a range of [-0x20000,0x1fffc].
+	;; If a branch is outside this range, we have a choice of two
+	;; sequences.
+	;;
+	;; For PIC, an out-of-range branch like:
+	;;
+	;;    bne     r1,r2,target
+	;;
+	;; becomes the equivalent of:
+	;;
+	;;    beq     r1,r2,1f
+	;;    la      rd,target
+	;;    jirl    $r0,rd,0
+	;; 1:
+	;;
+	;; The non-PIC case is similar except that we use a direct
+	;; jump instead of an la/jirl pair.  Since the target of this
+	;; jump is an absolute 28-bit bit address (the other bits
+	;; coming from the address of the delay slot) this form cannot
+	;; cross a 256MB boundary.  We could provide the option of
+	;; using la/jirl in this case too, but we do not do so at
+	;; present.
+	;;
+	;; from the shorten_branches reference address.
+	  (eq_attr "type" "branch")
+	  (cond [;; Any variant can handle the 17-bit range.
+		(and (le (minus (match_dup 0) (pc)) (const_int 65532))
+		     (le (minus (pc) (match_dup 0)) (const_int 65534)))
+		(const_int 4)
+
+		;; The non-PIC case: branch, and J.
+		(match_test "!flag_pic")
+		(const_int 8)]
+
+		;; Use MAX_PIC_BRANCH_LENGTH as a (gross) overestimate.
+		;; loongarch_adjust_insn_length substitutes the correct length.
+		;;
+		;; Note that we can't simply use (symbol_ref ...) here
+		;; because genattrtab needs to know the maximum length
+		;; of an insn.
+		(const_int MAX_PIC_BRANCH_LENGTH))]
+	(symbol_ref "get_attr_insn_count (insn) * 4")))
+
+;; Attribute describing the processor.
+;; The type of hardware hazard associated with this instruction.
+;; DELAY means that the next instruction cannot read the result
+;; of this one.
+(define_attr "hazard" "none,delay,forbidden_slot"
+  (const_string "none"))
+
+;; Can the instruction be put into a delay slot?
+(define_attr "can_delay" "no,yes"
+  (if_then_else (and (eq_attr "type" "!branch,call,jump")
+		     (eq_attr "hazard" "none")
+		     (match_test "get_attr_insn_count (insn) == 1"))
+		(const_string "yes")
+		(const_string "no")))
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+  [(set_attr "type" "multi")
+   (set_attr "can_delay" "no")])
+\f
+;; This mode iterator allows 32-bit and 64-bit GPR patterns to be generated
+;; from the same template.
+(define_mode_iterator GPR [SI (DI "TARGET_64BIT")])
+
+;; A copy of GPR that can be used when a pattern has two independent
+;; modes.
+(define_mode_iterator GPR2 [SI (DI "TARGET_64BIT")])
+
+;; Likewise, but for XLEN-sized quantities.
+(define_mode_iterator X [(SI "!TARGET_64BIT") (DI "TARGET_64BIT")])
+
+;; This mode iterator allows 16-bit and 32-bit GPR patterns and 32-bit 64-bit
+;; FPR patterns to be generated from the same template.
+(define_mode_iterator JOIN_MODE [HI
+				 SI
+				 (SF "TARGET_HARD_FLOAT")
+				 (DF "TARGET_DOUBLE_FLOAT")])
+
+;; This mode iterator allows :P to be used for patterns that operate on
+;; pointer-sized quantities.  Exactly one of the two alternatives will match.
+(define_mode_iterator P [(SI "Pmode == SImode") (DI "Pmode == DImode")])
+
+;; 64-bit modes for which we provide move patterns.
+(define_mode_iterator MOVE64 [DI DF])
+
+;; 128-bit modes for which we provide move patterns on 64-bit targets.
+(define_mode_iterator MOVE128 [TI TF])
+
+;; Iterator for sub-32-bit integer modes.
+(define_mode_iterator SHORT [QI HI])
+
+;; Likewise the 64-bit truncate-and-shift patterns.
+(define_mode_iterator SUBDI [QI HI SI])
+
+;; This mode iterator allows the QI HI SI and DI extension patterns to be
+(define_mode_iterator QHWD [QI HI SI (DI "TARGET_64BIT")])
+
+;; Iterator for hardware-supported floating-point modes.
+(define_mode_iterator ANYF [(SF "TARGET_HARD_FLOAT")
+			    (DF "TARGET_DOUBLE_FLOAT")])
+
+;; A floating-point mode for which moves involving FPRs may need to be split.
+(define_mode_iterator SPLITF
+  [(DF "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+   (DI "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+   (TF "TARGET_64BIT && TARGET_DOUBLE_FLOAT")])
+
+;; In GPR templates, a string like "mul.<d>" will expand to "mul" in the
+;; 32-bit "mul.w" and "mul.d" in the 64-bit version.
+(define_mode_attr d [(SI "w") (DI "d")])
+
+;; This attribute gives the length suffix for a load or store instruction.
+;; The same suffixes work for zero and sign extensions.
+(define_mode_attr size [(QI "b") (HI "h") (SI "w") (DI "d")])
+(define_mode_attr SIZE [(QI "B") (HI "H") (SI "W") (DI "D")])
+
+;; This attributes gives the mode mask of a SHORT.
+(define_mode_attr mask [(QI "0x00ff") (HI "0xffff")])
+
+;; This attributes gives the size (bits) of a SHORT.
+(define_mode_attr qi_hi [(QI "7") (HI "15")])
+
+;; Instruction names for stores.
+(define_mode_attr store [(QI "sb") (HI "sh") (SI "sw") (DI "sd")])
+
+;; Similarly for LoongArch indexed FPR loads and stores.
+(define_mode_attr floadx [(SF "fldx.s") (DF "fldx.d") (V2SF "fldx.d")])
+(define_mode_attr fstorex [(SF "fstx.s") (DF "fstx.d") (V2SF "fstx.d")])
+
+;; Similarly for LoongArch indexed GPR loads and stores.
+(define_mode_attr loadx [(QI "ldx.b")
+			 (HI "ldx.h")
+			 (SI "ldx.w")
+			 (DI "ldx.d")])
+(define_mode_attr storex [(QI "stx.b")
+			  (HI "stx.h")
+			  (SI "stx.w")
+			  (DI "stx.d")])
+
+;; This attribute gives the format suffix for floating-point operations.
+(define_mode_attr fmt [(SF "s") (DF "d")])
+
+;; This attribute gives the upper-case mode name for one unit of a
+;; floating-point mode or vector mode.
+(define_mode_attr UNITMODE [(SF "SF") (DF "DF") (V2SF "SF")])
+
+;; This attribute gives the integer mode that has half the size of
+;; the controlling mode.
+(define_mode_attr HALFMODE [(DF "SI") (DI "SI") (V2SF "SI") (TF "DI")])
+
+;; This attribute gives the integer prefix for some instructions templates.
+(define_mode_attr p [(SI "") (DI "d")])
+
+;; This code iterator allows signed and unsigned widening multiplications
+;; to use the same template.
+(define_code_iterator any_extend [sign_extend zero_extend])
+
+;; This code iterator allows the two right shift instructions to be
+;; generated from the same template.
+(define_code_iterator any_shiftrt [ashiftrt lshiftrt])
+
+;; This code iterator allows the three shift instructions to be generated
+;; from the same template.
+(define_code_iterator any_shift [ashift ashiftrt lshiftrt])
+
+;; This code iterator allows the three bitwise instructions to be generated
+;; from the same template.
+(define_code_iterator any_bitwise [and ior xor])
+
+;; This code iterator allows unsigned and signed division to be generated
+;; from the same template.
+(define_code_iterator any_div [div udiv mod umod])
+
+;; This code iterator allows all native floating-point comparisons to be
+;; generated from the same template.
+(define_code_iterator fcond [unordered uneq unlt unle eq lt le
+			     ordered ltgt ne ge gt unge ungt])
+
+;; Equality operators.
+(define_code_iterator equality_op [eq ne])
+
+;; These code iterators allow the signed and unsigned scc operations to use
+;; the same template.
+(define_code_iterator any_gt [gt gtu])
+(define_code_iterator any_ge [ge geu])
+(define_code_iterator any_lt [lt ltu])
+(define_code_iterator any_le [le leu])
+
+(define_code_iterator any_return [return simple_return])
+
+;; <u> expands to an empty string when doing a signed operation and
+;; "u" when doing an unsigned operation.
+(define_code_attr u [(sign_extend "") (zero_extend "u")
+		     (div "") (udiv "u")
+		     (mod "") (umod "u")
+		     (gt "") (gtu "u")
+		     (ge "") (geu "u")
+		     (lt "") (ltu "u")
+		     (le "") (leu "u")])
+
+;; <U> is like <u> except uppercase.
+(define_code_attr U [(sign_extend "") (zero_extend "U")])
+
+;; <su> is like <u>, but the signed form expands to "s" rather than "".
+(define_code_attr su [(sign_extend "s") (zero_extend "u")])
+
+;; <optab> expands to the name of the optab for a particular code.
+(define_code_attr optab [(ashift "ashl")
+			 (ashiftrt "ashr")
+			 (lshiftrt "lshr")
+			 (ior "ior")
+			 (xor "xor")
+			 (and "and")
+			 (plus "add")
+			 (minus "sub")
+			 (mult "mul")
+			 (div "div")
+			 (udiv "udiv")
+			 (mod "mod")
+			 (umod "umod")
+			 (return "return")
+			 (simple_return "simple_return")])
+
+;; <insn> expands to the name of the insn that implements a particular code.
+(define_code_attr insn [(ashift "sll")
+			(ashiftrt "sra")
+			(lshiftrt "srl")
+			(ior "or")
+			(xor "xor")
+			(and "and")
+			(plus "addu")
+			(minus "subu")
+			(div "div")
+			(udiv "div")
+			(mod "mod")
+			(umod "mod")])
+
+;; <fcond> is the c.cond.fmt condition associated with a particular code.
+(define_code_attr fcond [(unordered "cun")
+			 (uneq "cueq")
+			 (unlt "cult")
+			 (unle "cule")
+			 (eq "ceq")
+			 (lt "slt")
+			 (le "sle")
+			 (ordered "cor")
+			 (ltgt "sne")
+			 (ne "cune")
+			 (ge "sge")
+			 (gt "sgt")
+			 (unge "cuge")
+			 (ungt "cugt")])
+
+;; The sel mnemonic to use depending on the condition test.
+(define_code_attr sel [(eq "masknez") (ne "maskeqz")])
+(define_code_attr selinv [(eq "maskeqz") (ne "masknez")])
+
+;;
+;;  ....................
+;;
+;;	CONDITIONAL TRAPS
+;;
+;;  ....................
+;;
+
+(define_insn "trap"
+  [(trap_if (const_int 1) (const_int 0))]
+  ""
+{
+  return "break\t0";
+}
+  [(set_attr "type" "trap")])
+
+
+\f
+;;
+;;  ....................
+;;
+;;	ADDITION
+;;
+;;  ....................
+;;
+
+(define_insn "add<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(plus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		   (match_operand:ANYF 2 "register_operand" "f")))]
+  ""
+  "fadd.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(plus:GPR (match_operand:GPR 1 "register_operand" "r,r")
+		  (match_operand:GPR 2 "arith_operand" "r,Q")))]
+  ""
+  "add%i2.<d>\t%0,%1,%2";
+  [(set_attr "alu_type" "add")
+   (set_attr "compression" "*,*")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*addsi3_extended"
+  [(set (match_operand:DI 0 "register_operand" "=r,r")
+	(sign_extend:DI
+	     (plus:SI (match_operand:SI 1 "register_operand" "r,r")
+		      (match_operand:SI 2 "arith_operand" "r,Q"))))]
+  "TARGET_64BIT"
+  "add%i2.w\t%0,%1,%2"
+  [(set_attr "alu_type" "add")
+   (set_attr "mode" "SI")])
+
+(define_insn "*addsi3_extended2"
+  [(set (match_operand:DI 0 "register_operand" "=r,r")
+	(sign_extend:DI
+	  (subreg:SI (plus:DI (match_operand:DI 1 "register_operand" "r,r")
+			      (match_operand:DI 2 "arith_operand"    "r,Q"))
+		     0)))]
+  "TARGET_64BIT"
+  "add%i2.w\t%0,%1,%2"
+  [(set_attr "alu_type" "add")
+   (set_attr "mode" "SI")])
+
+\f
+;;
+;;  ....................
+;;
+;;	SUBTRACTION
+;;
+;;  ....................
+;;
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(minus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		    (match_operand:ANYF 2 "register_operand" "f")))]
+  ""
+  "fsub.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(minus:GPR (match_operand:GPR 1 "register_operand" "rJ")
+		   (match_operand:GPR 2 "register_operand" "r")))]
+  ""
+  "sub.<d>\t%0,%z1,%2"
+  [(set_attr "alu_type" "sub")
+   (set_attr "compression" "*")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*subsi3_extended"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	    (minus:SI (match_operand:SI 1 "register_operand" "rJ")
+		      (match_operand:SI 2 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "sub.w\t%0,%z1,%2"
+  [(set_attr "alu_type" "sub")
+   (set_attr "mode" "DI")])
+
+(define_insn "*subsi3_extended2"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	  (subreg:SI (minus:DI (match_operand:DI 1 "reg_or_0_operand" "rJ")
+			       (match_operand:DI 2 "register_operand" "r"))
+		     0)))]
+  "TARGET_64BIT"
+  "sub.w\t%0,%z1,%2"
+  [(set_attr "alu_type" "sub")
+   (set_attr "mode" "SI")])
+
+\f
+;;
+;;  ....................
+;;
+;;	MULTIPLICATION
+;;
+;;  ....................
+;;
+
+(define_insn "mul<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		   (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fmul.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmul")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "mul<mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(mult:GPR (match_operand:GPR 1 "register_operand" "r")
+		  (match_operand:GPR 2 "register_operand" "r")))]
+  ""
+  "mul.<d>\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "mulsidi3_64bit"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
+		 (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "mul.d\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "DI")])
+
+(define_insn "*mulsi3_extended"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	    (mult:SI (match_operand:SI 1 "register_operand" "r")
+		     (match_operand:SI 2 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "mul.w\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "SI")])
+
+(define_insn "*mulsi3_extended2"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	  (subreg:SI (mult:DI (match_operand:DI 1 "register_operand" "r")
+			      (match_operand:DI 2 "register_operand" "r"))
+		     0)))]
+  "TARGET_64BIT"
+  "mul.w\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "SI")])
+
+
+;;
+;;  ........................
+;;
+;;	MULTIPLICATION HIGH-PART
+;;
+;;  ........................
+;;
+
+
+(define_expand "<u>mulditi3"
+  [(set (match_operand:TI 0 "register_operand")
+	(mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
+		 (any_extend:TI (match_operand:DI 2 "register_operand"))))]
+  "TARGET_64BIT"
+{
+  rtx low = gen_reg_rtx (DImode);
+  emit_insn (gen_muldi3 (low, operands[1], operands[2]));
+
+  rtx high = gen_reg_rtx (DImode);
+  emit_insn (gen_<u>muldi3_highpart (high, operands[1], operands[2]));
+
+  emit_move_insn (gen_lowpart (DImode, operands[0]), low);
+  emit_move_insn (gen_highpart (DImode, operands[0]), high);
+  DONE;
+})
+
+(define_insn "<u>muldi3_highpart"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(truncate:DI
+	  (lshiftrt:TI
+	    (mult:TI (any_extend:TI
+		       (match_operand:DI 1 "register_operand" " r"))
+		     (any_extend:TI
+		       (match_operand:DI 2 "register_operand" " r")))
+	    (const_int 64))))]
+  "TARGET_64BIT"
+  "mulh.d<u>\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "DI")])
+
+(define_expand "<u>mulsidi3"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(mult:DI (any_extend:DI
+		   (match_operand:SI 1 "register_operand" " r"))
+		 (any_extend:DI
+		   (match_operand:SI 2 "register_operand" " r"))))]
+  "!TARGET_64BIT"
+{
+  rtx temp = gen_reg_rtx (SImode);
+  emit_insn (gen_mulsi3 (temp, operands[1], operands[2]));
+  emit_insn (gen_<u>mulsi3_highpart (loongarch_subword (operands[0], true),
+				     operands[1], operands[2]));
+  emit_insn (gen_movsi (loongarch_subword (operands[0], false), temp));
+  DONE;
+})
+
+(define_insn "<u>mulsi3_highpart"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(truncate:SI
+	  (lshiftrt:DI
+	    (mult:DI (any_extend:DI
+		       (match_operand:SI 1 "register_operand" " r"))
+		     (any_extend:DI
+		       (match_operand:SI 2 "register_operand" " r")))
+	    (const_int 32))))]
+  "!TARGET_64BIT"
+  "mulh.w<u>\t%0,%1,%2"
+  [(set_attr "type" "imul")
+   (set_attr "mode" "SI")])
+
+;;
+;;  ....................
+;;
+;;	DIVISION and REMAINDER
+;;
+;;  ....................
+;;
+
+;; Float division and modulus.
+(define_expand "div<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(div:ANYF (match_operand:ANYF 1 "reg_or_1_operand")
+		  (match_operand:ANYF 2 "register_operand")))]
+  "TARGET_HARD_FLOAT"
+{})
+
+(define_insn "*div<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(div:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fdiv.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fdiv")
+   (set_attr "mode" "<UNITMODE>")
+   (set_attr "insn_count" "1")])
+
+;; In 3A5000, the reciprocal operation is the same as the division operation.
+
+(define_insn "*recip<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+		  (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "frecip.<fmt>\t%0,%2"
+  [(set_attr "type" "frdiv")
+   (set_attr "mode" "<UNITMODE>")
+   (set_attr "insn_count" "1")])
+
+;; Integer division and modulus.
+
+(define_insn "<optab><mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")
+	(any_div:GPR (match_operand:GPR 1 "register_operand" "r")
+		     (match_operand:GPR 2 "register_operand" "r")))]
+  ""
+  {
+    return loongarch_output_division ("<insn>.<d><u>\t%0,%1,%2", operands);
+  }
+  [(set_attr "type" "idiv")
+   (set_attr "mode" "<MODE>")])
+
+
+;; Floating point multiply accumulate instructions.
+
+;; a * b + c
+(define_expand "fma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand")
+		  (match_operand:ANYF 2 "register_operand")
+		  (match_operand:ANYF 3 "register_operand")))]
+  "TARGET_HARD_FLOAT")
+
+(define_insn "*fma<mode>4_madd4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fmadd.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; a * b - c
+(define_insn "fms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
+  "TARGET_HARD_FLOAT"
+  "fmsub.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; fnma is defined in GCC as (fma (neg op1) op2 op3)
+;; (-op1 * op2) + op3 ==> -(op1 * op2) + op3 ==> -((op1 * op2) - op3)
+;; The loongarch nmsub instructions implement -((op1 * op2) - op3)
+;; This transformation means we may return the wrong signed zero
+;; so we check HONOR_SIGNED_ZEROS.
+
+;; -a * b + c
+(define_insn "fnma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && !HONOR_SIGNED_ZEROS (<MODE>mode)"
+  "fnmsub.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; fnms is defined as: (fma (neg op1) op2 (neg op3))
+;; ((-op1) * op2) - op3 ==> -(op1 * op2) - op3 ==> -((op1 * op2) + op3)
+;; The loongarch nmadd instructions implement -((op1 * op2) + op3)
+;; This transformation means we may return the wrong signed zero
+;; so we check HONOR_SIGNED_ZEROS.
+
+;; -a * b - c
+(define_insn "fnms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF
+	  (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+	  (match_operand:ANYF 2 "register_operand" "f")
+	  (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
+  "TARGET_HARD_FLOAT && !HONOR_SIGNED_ZEROS (<MODE>mode)"
+  "fnmadd.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; -(-a * b - c), modulo signed zeros
+(define_insn "*fma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(neg:ANYF
+	    (fma:ANYF
+		(neg:ANYF (match_operand:ANYF 1 "register_operand" " f"))
+		(match_operand:ANYF 2 "register_operand" " f")
+		(neg:ANYF (match_operand:ANYF 3 "register_operand" " f")))))]
+  "TARGET_HARD_FLOAT && !HONOR_SIGNED_ZEROS (<MODE>mode)"
+  "fmadd.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; -(-a * b + c), modulo signed zeros
+(define_insn "*fms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(neg:ANYF
+	    (fma:ANYF
+		(neg:ANYF (match_operand:ANYF 1 "register_operand" " f"))
+		(match_operand:ANYF 2 "register_operand" " f")
+		(match_operand:ANYF 3 "register_operand" " f"))))]
+  "TARGET_HARD_FLOAT && !HONOR_SIGNED_ZEROS (<MODE>mode)"
+  "fmsub.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; -(a * b + c)
+(define_insn "*fnms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(neg:ANYF
+	    (fma:ANYF
+		(match_operand:ANYF 1 "register_operand" " f")
+		(match_operand:ANYF 2 "register_operand" " f")
+		(match_operand:ANYF 3 "register_operand" " f"))))]
+  "TARGET_HARD_FLOAT"
+  "fnmadd.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
+;; -(a * b - c)
+(define_insn "*fnma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(neg:ANYF
+	    (fma:ANYF
+		(match_operand:ANYF 1 "register_operand" " f")
+		(match_operand:ANYF 2 "register_operand" " f")
+		(neg:ANYF (match_operand:ANYF 3 "register_operand" " f")))))]
+  "TARGET_HARD_FLOAT"
+  "fnmsub.<fmt>\t%0,%1,%2,%3"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+\f
+;;
+;;  ....................
+;;
+;;	SQUARE ROOT
+;;
+;;  ....................
+
+(define_insn "sqrt<mode>2"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(sqrt:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fsqrt.<fmt>\t%0,%1"
+  [(set_attr "type" "fsqrt")
+   (set_attr "mode" "<UNITMODE>")
+   (set_attr "insn_count" "1")])
+
+(define_insn "*rsqrt<mode>a"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+		  (sqrt:ANYF (match_operand:ANYF 2 "register_operand" "f"))))]
+  "TARGET_HARD_FLOAT && flag_unsafe_math_optimizations"
+  "frsqrt.<fmt>\t%0,%2"
+  [(set_attr "type" "frsqrt")
+   (set_attr "mode" "<UNITMODE>")
+   (set_attr "insn_count" "1")])
+
+(define_insn "*rsqrt<mode>b"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(sqrt:ANYF (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+			     (match_operand:ANYF 2 "register_operand" "f"))))]
+  "TARGET_HARD_FLOAT && flag_unsafe_math_optimizations"
+  "frsqrt.<fmt>\t%0,%2"
+  [(set_attr "type" "frsqrt")
+   (set_attr "mode" "<UNITMODE>")
+   (set_attr "insn_count" "1")])
+\f
+;;
+;;  ....................
+;;
+;;	ABSOLUTE VALUE
+;;
+;;  ....................
+
+(define_insn "abs<mode>2"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(abs:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fabs.<fmt>\t%0,%1"
+  [(set_attr "type" "fabs")
+   (set_attr "mode" "<UNITMODE>")])
+\f
+;;
+;;  ...................
+;;
+;;  Count leading zeroes.
+;;
+;;  ...................
+;;
+
+(define_insn "clz<mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(clz:GPR (match_operand:GPR 1 "register_operand" "r")))]
+  ""
+  "clz.<d>\t%0,%1"
+  [(set_attr "type" "clz")
+   (set_attr "mode" "<MODE>")])
+
+;;
+;;  ...................
+;;
+;;  Count trailing zeroes.
+;;
+;;  ...................
+;;
+
+(define_insn "ctz<mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(ctz:GPR (match_operand:GPR 1 "register_operand" "r")))]
+  ""
+  "ctz.<d>\t%0,%1"
+  [(set_attr "type" "clz")
+   (set_attr "mode" "<MODE>")])
+
+;;
+;;  ....................
+;;
+;;	MIN/MAX
+;;
+;;  ....................
+
+(define_insn "smax<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+       (smax:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fmax.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "smin<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+       (smin:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fmin.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "smaxa<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+       (if_then_else:ANYF
+	      (gt (abs:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+		  (abs:ANYF (match_operand:ANYF 2 "register_operand" "f")))
+	      (match_dup 1)
+	      (match_dup 2)))]
+  "TARGET_HARD_FLOAT"
+  "fmaxa.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "smina<mode>3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+       (if_then_else:ANYF
+		(lt (abs:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+		    (abs:ANYF (match_operand:ANYF 2 "register_operand" "f")))
+		(match_dup 1)
+		(match_dup 2)))]
+  "TARGET_HARD_FLOAT"
+  "fmina.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<MODE>")])
+\f
+;;
+;;  ....................
+;;
+;;	NEGATION and ONE'S COMPLEMENT
+;;
+;;  ....................
+
+(define_insn "neg<mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(neg:GPR (match_operand:GPR 1 "register_operand" "r")))]
+  ""
+  "sub.<d>\t%0,%.,%1"
+  [(set_attr "alu_type"	"sub")
+   (set_attr "mode"	"<MODE>")])
+
+(define_insn "one_cmpl<mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(not:GPR (match_operand:GPR 1 "register_operand" "r")))]
+  ""
+  "nor\t%0,%.,%1"
+  [(set_attr "alu_type" "not")
+   (set_attr "compression" "*")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "neg<mode>2"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(neg:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fneg.<fmt>\t%0,%1"
+  [(set_attr "type" "fneg")
+   (set_attr "mode" "<UNITMODE>")])
+\f
+
+;;
+;;  ....................
+;;
+;;	LOGICAL
+;;
+;;  ....................
+;;
+
+(define_insn "<optab><mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(any_bitwise:GPR (match_operand:GPR 1 "register_operand" "r,r")
+		 (match_operand:GPR 2 "uns_arith_operand" "r,K")))]
+  ""
+  "<insn>%i2\t%0,%1,%2"
+  [(set_attr "type" "logical")
+   (set_attr "compression" "*,*")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "and<mode>3_extended"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(and:GPR (match_operand:GPR 1 "nonimmediate_operand" "r")
+		 (match_operand:GPR 2 "low_bitmask_operand" "Yx")))]
+  ""
+{
+  int len;
+
+  len = low_bitmask_len (<MODE>mode, INTVAL (operands[2]));
+  operands[2] = GEN_INT (len-1);
+  return "bstrpick.<d>\t%0,%1,%2,0";
+}
+  [(set_attr "move_type" "pick_ins")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*iorhi3"
+  [(set (match_operand:HI 0 "register_operand" "=r,r")
+	(ior:HI (match_operand:HI 1 "register_operand" "r,r")
+		(match_operand:HI 2 "uns_arith_operand" "r,K")))]
+  ""
+  "or%i2\t%0,%1,%2"
+  [(set_attr "type" "logical")
+   (set_attr "mode" "HI")])
+
+(define_insn "*nor<mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(and:GPR (not:GPR (match_operand:GPR 1 "register_operand" "r"))
+		 (not:GPR (match_operand:GPR 2 "register_operand" "r"))))]
+  ""
+  "nor\t%0,%1,%2"
+  [(set_attr "type" "logical")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "andn<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(and:GPR
+	  (not:GPR (match_operand:GPR 1 "register_operand" "r"))
+	  (match_operand:GPR 2 "register_operand" "r")))]
+  ""
+  "andn\t%0,%2,%1"
+  [(set_attr "type" "logical")])
+
+(define_insn "orn<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(ior:GPR
+	  (not:GPR (match_operand:GPR 1 "register_operand" "r"))
+	  (match_operand:GPR 2 "register_operand" "r")))]
+  ""
+  "orn\t%0,%2,%1"
+  [(set_attr "type" "logical")])
+
+\f
+;;
+;;  ....................
+;;
+;;	TRUNCATION
+;;
+;;  ....................
+
+(define_insn "truncdfsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+	(float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "fcvt.s.d\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "cnv_mode"	"D2S")
+   (set_attr "mode" "SF")])
+
+;; Integer truncation patterns.  Truncating SImode values to smaller
+;; modes is a no-op, as it is for most other GCC ports.  Truncating
+;; DImode values to SImode is not a no-op for TARGET_64BIT since we
+;; need to make sure that the lower 32 bits are properly sign-extended
+;; (see TARGET_TRULY_NOOP_TRUNCATION).  Truncating DImode values into modes
+;; smaller than SImode is equivalent to two separate truncations:
+;;
+;;			  A       B
+;;    DI ---> HI  ==  DI ---> SI ---> HI
+;;    DI ---> QI  ==  DI ---> SI ---> QI
+;;
+;; Step A needs a real instruction but step B does not.
+
+(define_insn "truncdi<mode>2"
+  [(set (match_operand:SUBDI 0 "nonimmediate_operand" "=r,m")
+	(truncate:SUBDI (match_operand:DI 1 "register_operand" "r,r")))]
+  "TARGET_64BIT"
+  "@
+    slli.w\t%0,%1,0
+    st.<size>\t%1,%0"
+  [(set_attr "move_type" "sll0,store")
+   (set_attr "mode" "SI")])
+
+(define_insn "truncdisi2_extended"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=ZC")
+	(truncate:SI (match_operand:DI 1 "register_operand" "r")))]
+  "TARGET_64BIT"
+  "stptr.w\t%1,%0"
+  [(set_attr "move_type" "store")
+   (set_attr "mode" "SI")])
+
+;; Combiner patterns to optimize shift/truncate combinations.
+
+(define_insn "*ashr_trunc<mode>"
+  [(set (match_operand:SUBDI 0 "register_operand" "=r")
+	(truncate:SUBDI
+	  (ashiftrt:DI (match_operand:DI 1 "register_operand" "r")
+		       (match_operand:DI 2 "const_arith_operand" ""))))]
+  "TARGET_64BIT && IN_RANGE (INTVAL (operands[2]), 32, 63)"
+  "srai.d\t%0,%1,%2"
+  [(set_attr "type" "shift")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*lshr32_trunc<mode>"
+  [(set (match_operand:SUBDI 0 "register_operand" "=r")
+	(truncate:SUBDI
+	  (lshiftrt:DI (match_operand:DI 1 "register_operand" "r")
+		       (const_int 32))))]
+  "TARGET_64BIT"
+  "srai.d\t%0,%1,32"
+  [(set_attr "type" "shift")
+   (set_attr "mode" "<MODE>")])
+\f
+;;
+;;  ....................
+;;
+;;	ZERO EXTENSION
+;;
+;;  ....................
+
+(define_insn "zero_extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
+	(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,ZC,W")))]
+  "TARGET_64BIT"
+  "@
+   bstrpick.d\t%0,%1,31,0
+   ldptr.w\t%0,%1\n\tlu32i.d\t%0,0
+   ld.wu\t%0,%1"
+  [(set_attr "move_type" "arith,load,load")
+   (set_attr "mode" "DI")
+   (set_attr "insn_count" "1,2,1")])
+
+(define_insn "*zero_extendsidi2_internal"
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
+	(subreg:DI (match_operand:SI 1 "nonimmediate_operand" "r,ZC,W") 0))]
+  "TARGET_64BIT"
+  "@
+   bstrpick.d\t%0,%1,31,0
+   ldptr.w\t%0,%1\n\tlu32i.d\t%0,0
+   ld.wu\t%0,%1"
+  [(set_attr "move_type" "arith,load,load")
+   (set_attr "mode" "DI")
+   (set_attr "insn_count" "1,2,1")])
+
+;; See the comment before the *and<mode>3 pattern why this is generated by
+;; combine.
+
+(define_insn "zero_extend<SHORT:mode><GPR:mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(zero_extend:GPR
+	     (match_operand:SHORT 1 "nonimmediate_operand" "r,m")))]
+  ""
+  "@
+   bstrpick.<d>\t%0,%1,<SHORT:qi_hi>,0
+   ld.<SHORT:size>u\t%0,%1"
+  [(set_attr "move_type" "pick_ins,load")
+   (set_attr "compression" "*,*")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=r,r")
+	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "r,m")))]
+  ""
+  "@
+   andi\t%0,%1,0xff
+   ld.bu\t%0,%1"
+  [(set_attr "move_type" "andi,load")
+   (set_attr "mode" "HI")])
+
+;; Combiner patterns to optimize truncate/zero_extend combinations.
+
+(define_insn "*zero_extend<GPR:mode>_trunc<SHORT:mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(zero_extend:GPR
+	    (truncate:SHORT (match_operand:DI 1 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "bstrpick.<d>\t%0,%1,<SHORT:qi_hi>,0"
+  [(set_attr "move_type" "pick_ins")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*zero_extendhi_truncqi"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+	(zero_extend:HI
+	    (truncate:QI (match_operand:DI 1 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "andi\t%0,%1,0xff"
+  [(set_attr "alu_type" "and")
+   (set_attr "mode" "HI")])
+\f
+;;
+;;  ....................
+;;
+;;	SIGN EXTENSION
+;;
+;;  ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+;; When TARGET_64BIT, all SImode integer and accumulator registers
+;; should already be in sign-extended form (see TARGET_TRULY_NOOP_TRUNCATION
+;; and truncdisi2).  We can therefore get rid of register->register
+;; instructions if we constrain the source to be in the same register as
+;; the destination.
+;;
+;; Only the pre-reload scheduler sees the type of the register alternatives;
+;; we split them into nothing before the post-reload scheduler runs.
+;; These alternatives therefore have type "move" in order to reflect
+;; what happens if the two pre-reload operands cannot be tied, and are
+;; instead allocated two separate GPRs.  We don't distinguish between
+;; the GPR and LO cases because we don't usually know during pre-reload
+;; scheduling whether an operand will be LO or not.
+(define_insn_and_split "extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
+	(sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "0,ZC,m")))]
+  "TARGET_64BIT"
+  "@
+   #
+   ldptr.w\t%0,%1
+   ld.w\t%0,%1"
+  "&& reload_completed && register_operand (operands[1], VOIDmode)"
+  [(const_int 0)]
+{
+  emit_note (NOTE_INSN_DELETED);
+  DONE;
+}
+  [(set_attr "move_type" "move,load,load")
+   (set_attr "mode" "DI")])
+
+(define_insn "extend<SHORT:mode><GPR:mode>2"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(sign_extend:GPR
+	     (match_operand:SHORT 1 "nonimmediate_operand" "r,m")))]
+  ""
+  "@
+   ext.w.<SHORT:size>\t%0,%1
+   ld.<SHORT:size>\t%0,%1"
+  [(set_attr "move_type" "signext,load")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=r,r")
+	(sign_extend:HI
+	     (match_operand:QI 1 "nonimmediate_operand" "r,m")))]
+  ""
+  "@
+   ext.w.b\t%0,%1
+   ld.b\t%0,%1"
+  [(set_attr "move_type" "signext,load")
+   (set_attr "mode" "SI")])
+
+;; Combiner patterns for truncate/sign_extend combinations.  The SI versions
+;; use the shift/truncate patterns.
+
+(define_insn_and_split "*extenddi_truncate<mode>"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	    (truncate:SHORT (match_operand:DI 1 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2)
+	(ashift:DI (match_dup 1)
+		   (match_dup 3)))
+   (set (match_dup 0)
+	(ashiftrt:DI (match_dup 2)
+		     (match_dup 3)))]
+{
+  operands[2] = gen_lowpart (DImode, operands[0]);
+  operands[3] = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (<MODE>mode));
+}
+  [(set_attr "move_type" "shift_shift")
+   (set_attr "mode" "DI")])
+
+(define_insn_and_split "*extendsi_truncate<mode>"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(sign_extend:SI
+	    (truncate:SHORT (match_operand:DI 1 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2)
+	(ashift:DI (match_dup 1)
+		   (match_dup 3)))
+   (set (match_dup 0)
+	(truncate:SI (ashiftrt:DI (match_dup 2)
+				  (match_dup 3))))]
+{
+  operands[2] = gen_lowpart (DImode, operands[0]);
+  operands[3] = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (<MODE>mode));
+}
+  [(set_attr "move_type" "shift_shift")
+   (set_attr "mode" "SI")])
+
+(define_insn_and_split "*extendhi_truncateqi"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+	(sign_extend:HI
+	    (truncate:QI (match_operand:DI 1 "register_operand" "r"))))]
+  "TARGET_64BIT"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2)
+	(ashift:DI (match_dup 1)
+		   (const_int 56)))
+   (set (match_dup 0)
+	(truncate:HI (ashiftrt:DI (match_dup 2)
+				  (const_int 56))))]
+{
+  operands[2] = gen_lowpart (DImode, operands[0]);
+}
+  [(set_attr "move_type" "shift_shift")
+   (set_attr "mode" "SI")])
+
+(define_insn "extendsfdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+	(float_extend:DF (match_operand:SF 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "fcvt.d.s\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "cnv_mode"	"S2D")
+   (set_attr "mode" "DF")])
+\f
+;;
+;;  ....................
+;;
+;;	CONVERSIONS
+;;
+;;  ....................
+
+;; conversion of a floating-point value to a integer
+
+(define_insn "fix_truncdfsi2"
+  [(set (match_operand:SI 0 "register_operand" "=f")
+	(fix:SI (match_operand:DF 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ftintrz.w.d %0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "DF")
+   (set_attr "cnv_mode"	"D2I")])
+
+(define_insn "fix_truncsfsi2"
+  [(set (match_operand:SI 0 "register_operand" "=f")
+	(fix:SI (match_operand:SF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "ftintrz.w.s %0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "SF")
+   (set_attr "cnv_mode"	"S2I")])
+
+
+(define_insn "fix_truncdfdi2"
+  [(set (match_operand:DI 0 "register_operand" "=f")
+	(fix:DI (match_operand:DF 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ftintrz.l.d %0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "DF")
+   (set_attr "cnv_mode"	"D2I")])
+
+
+(define_insn "fix_truncsfdi2"
+  [(set (match_operand:DI 0 "register_operand" "=f")
+	(fix:DI (match_operand:SF 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ftintrz.l.s %0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "SF")
+   (set_attr "cnv_mode"	"S2I")])
+
+;; conversion of an integeral (or boolean) value to a floating-point value
+
+(define_insn "floatsidf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+	(float:DF (match_operand:SI 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ffint.d.w\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "DF")
+   (set_attr "cnv_mode"	"I2D")])
+
+
+(define_insn "floatdidf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+	(float:DF (match_operand:DI 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ffint.d.l\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "DF")
+   (set_attr "cnv_mode" "I2D")])
+
+
+(define_insn "floatsisf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+	(float:SF (match_operand:SI 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "ffint.s.w\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "SF")
+   (set_attr "cnv_mode"	"I2S")])
+
+
+(define_insn "floatdisf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+	(float:SF (match_operand:DI 1 "register_operand" "f")))]
+  "TARGET_DOUBLE_FLOAT"
+  "ffint.s.l\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "SF")
+   (set_attr "cnv_mode"	"I2S")])
+
+;; floating point value by converting to value to an unsigned integer
+
+(define_expand "fixuns_truncdfsi2"
+  [(set (match_operand:SI 0 "register_operand")
+	(unsigned_fix:SI (match_operand:DF 1 "register_operand")))]
+  "TARGET_DOUBLE_FLOAT"
+{
+  rtx reg1 = gen_reg_rtx (DFmode);
+  rtx reg2 = gen_reg_rtx (DFmode);
+  rtx reg3 = gen_reg_rtx (SImode);
+  rtx_code_label *label1 = gen_label_rtx ();
+  rtx_code_label *label2 = gen_label_rtx ();
+  rtx test;
+  REAL_VALUE_TYPE offset;
+
+  real_2expN (&offset, 31, DFmode);
+
+  if (reg1)		      /* Turn off complaints about unreached code.  */
+    {
+      loongarch_emit_move (reg1,
+			   const_double_from_real_value (offset, DFmode));
+      do_pending_stack_adjust ();
+
+      test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+      emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+      emit_insn (gen_fix_truncdfsi2 (operands[0], operands[1]));
+      emit_jump_insn (gen_rtx_SET (pc_rtx,
+				   gen_rtx_LABEL_REF (VOIDmode, label2)));
+      emit_barrier ();
+
+      emit_label (label1);
+      loongarch_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+      loongarch_emit_move (reg3, GEN_INT (trunc_int_for_mode
+				     (BITMASK_HIGH, SImode)));
+
+      emit_insn (gen_fix_truncdfsi2 (operands[0], reg2));
+      emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+      emit_label (label2);
+
+      /* Allow REG_NOTES to be set on last insn (labels don't have enough
+	 fields, and can't be used for REG_NOTES anyway).  */
+      emit_use (stack_pointer_rtx);
+      DONE;
+    }
+})
+
+
+(define_expand "fixuns_truncdfdi2"
+  [(set (match_operand:DI 0 "register_operand")
+	(unsigned_fix:DI (match_operand:DF 1 "register_operand")))]
+  "TARGET_DOUBLE_FLOAT"
+{
+  rtx reg1 = gen_reg_rtx (DFmode);
+  rtx reg2 = gen_reg_rtx (DFmode);
+  rtx reg3 = gen_reg_rtx (DImode);
+  rtx_code_label *label1 = gen_label_rtx ();
+  rtx_code_label *label2 = gen_label_rtx ();
+  rtx test;
+  REAL_VALUE_TYPE offset;
+
+  real_2expN (&offset, 63, DFmode);
+
+  loongarch_emit_move (reg1, const_double_from_real_value (offset, DFmode));
+  do_pending_stack_adjust ();
+
+  test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+  emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+  emit_insn (gen_fix_truncdfdi2 (operands[0], operands[1]));
+  emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+  emit_barrier ();
+
+  emit_label (label1);
+  loongarch_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+  loongarch_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+  emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+  emit_insn (gen_fix_truncdfdi2 (operands[0], reg2));
+  emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+  emit_label (label2);
+
+  /* Allow REG_NOTES to be set on last insn (labels don't have enough
+     fields, and can't be used for REG_NOTES anyway).  */
+  emit_use (stack_pointer_rtx);
+  DONE;
+})
+
+
+(define_expand "fixuns_truncsfsi2"
+  [(set (match_operand:SI 0 "register_operand")
+	(unsigned_fix:SI (match_operand:SF 1 "register_operand")))]
+  "TARGET_HARD_FLOAT"
+{
+  rtx reg1 = gen_reg_rtx (SFmode);
+  rtx reg2 = gen_reg_rtx (SFmode);
+  rtx reg3 = gen_reg_rtx (SImode);
+  rtx_code_label *label1 = gen_label_rtx ();
+  rtx_code_label *label2 = gen_label_rtx ();
+  rtx test;
+  REAL_VALUE_TYPE offset;
+
+  real_2expN (&offset, 31, SFmode);
+
+  loongarch_emit_move (reg1, const_double_from_real_value (offset, SFmode));
+  do_pending_stack_adjust ();
+
+  test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+  emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+  emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
+  emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+  emit_barrier ();
+
+  emit_label (label1);
+  loongarch_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+  loongarch_emit_move (reg3, GEN_INT (trunc_int_for_mode
+				 (BITMASK_HIGH, SImode)));
+
+  emit_insn (gen_fix_truncsfsi2 (operands[0], reg2));
+  emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+  emit_label (label2);
+
+  /* Allow REG_NOTES to be set on last insn (labels don't have enough
+     fields, and can't be used for REG_NOTES anyway).  */
+  emit_use (stack_pointer_rtx);
+  DONE;
+})
+
+
+(define_expand "fixuns_truncsfdi2"
+  [(set (match_operand:DI 0 "register_operand")
+	(unsigned_fix:DI (match_operand:SF 1 "register_operand")))]
+  "TARGET_DOUBLE_FLOAT"
+{
+  rtx reg1 = gen_reg_rtx (SFmode);
+  rtx reg2 = gen_reg_rtx (SFmode);
+  rtx reg3 = gen_reg_rtx (DImode);
+  rtx_code_label *label1 = gen_label_rtx ();
+  rtx_code_label *label2 = gen_label_rtx ();
+  rtx test;
+  REAL_VALUE_TYPE offset;
+
+  real_2expN (&offset, 63, SFmode);
+
+  loongarch_emit_move (reg1, const_double_from_real_value (offset, SFmode));
+  do_pending_stack_adjust ();
+
+  test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+  emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+  emit_insn (gen_fix_truncsfdi2 (operands[0], operands[1]));
+  emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+  emit_barrier ();
+
+  emit_label (label1);
+  loongarch_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+  loongarch_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+  emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+  emit_insn (gen_fix_truncsfdi2 (operands[0], reg2));
+  emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+  emit_label (label2);
+
+  /* Allow REG_NOTES to be set on last insn (labels don't have enough
+     fields, and can't be used for REG_NOTES anyway).  */
+  emit_use (stack_pointer_rtx);
+  DONE;
+})
+\f
+;;
+;;  ....................
+;;
+;;	EXTRACT AND INSERT
+;;
+;;  ....................
+
+(define_expand "extzv<mode>"
+  [(set (match_operand:GPR 0 "register_operand")
+	(zero_extract:GPR (match_operand:GPR 1 "register_operand")
+			  (match_operand 2 "const_int_operand")
+			  (match_operand 3 "const_int_operand")))]
+  ""
+{
+  if (!loongarch_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+			   INTVAL (operands[3])))
+    FAIL;
+})
+
+(define_insn "*extzv<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(zero_extract:GPR (match_operand:GPR 1 "register_operand" "r")
+			  (match_operand 2 "const_int_operand" "")
+			  (match_operand 3 "const_int_operand" "")))]
+  "loongarch_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+		       INTVAL (operands[3]))"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1);
+  return "bstrpick.<d>\t%0,%1,%2,%3";
+}
+  [(set_attr "type" "arith")
+   (set_attr "mode" "<MODE>")])
+
+(define_expand "insv<mode>"
+  [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand")
+			  (match_operand 1 "const_int_operand")
+			  (match_operand 2 "const_int_operand"))
+	(match_operand:GPR 3 "reg_or_0_operand"))]
+  ""
+{
+  if (!loongarch_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+			   INTVAL (operands[2])))
+    FAIL;
+})
+
+(define_insn "*insv<mode>"
+  [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand" "+r")
+			  (match_operand:SI 1 "const_int_operand" "")
+			  (match_operand:SI 2 "const_int_operand" ""))
+	(match_operand:GPR 3 "reg_or_0_operand" "rJ"))]
+  "loongarch_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+		       INTVAL (operands[2]))"
+{
+  operands[1] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1);
+  return "bstrins.<d>\t%0,%z3,%1,%2";
+}
+  [(set_attr "type" "arith")
+   (set_attr "mode" "<MODE>")])
+
+\f
+;;
+;;  ....................
+;;
+;;	DATA MOVEMENT
+;;
+;;  ....................
+
+;; Allow combine to split complex const_int load sequences, using operand 2
+;; to store the intermediate results.  See move_operand for details.
+(define_split
+  [(set (match_operand:GPR 0 "register_operand")
+	(match_operand:GPR 1 "splittable_const_int_operand"))
+   (clobber (match_operand:GPR 2 "register_operand"))]
+  ""
+  [(const_int 0)]
+{
+  loongarch_move_integer (operands[2], operands[0], INTVAL (operands[1]));
+  DONE;
+})
+
+;; 64-bit integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+
+(define_expand "movdi"
+  [(set (match_operand:DI 0 "")
+	(match_operand:DI 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (DImode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movdi_32bit"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,ZC,r,m,*f,*f,*r,*m")
+       (match_operand:DI 1 "move_operand" "r,i,ZC,r,m,r,*J*r,*m,*f,*f"))]
+  "!TARGET_64BIT
+   && (register_operand (operands[0], DImode)
+       || reg_or_0_operand (operands[1], DImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,load,store,load,store,mgtf,fpload,mftg,fpstore")
+   (set_attr "mode" "DI")])
+
+(define_insn "*movdi_64bit"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,ZC,r,m,*f,*f,*r,*m")
+	(match_operand:DI 1 "move_operand" "r,Yd,ZC,rJ,m,rJ,*r*J,*m,*f,*f"))]
+  "TARGET_64BIT
+   && (register_operand (operands[0], DImode)
+       || reg_or_0_operand (operands[1], DImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,load,store,load,store,mgtf,fpload,mftg,fpstore")
+   (set_attr "mode" "DI")])
+
+;; 32-bit Integer moves
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "")
+	(match_operand:SI 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (SImode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movsi_internal"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,ZC,r,m,*f,*f,*r,*m,*r,*z")
+	(match_operand:SI 1 "move_operand" "r,Yd,ZC,rJ,m,rJ,*r*J,*m,*f,*f,*z,*r"))]
+  "(register_operand (operands[0], SImode)
+       || reg_or_0_operand (operands[1], SImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,load,store,load,store,mgtf,fpload,mftg,fpstore,mftg,mgtf")
+   (set_attr "compression" "all,*,*,*,*,*,*,*,*,*,*,*")
+   (set_attr "mode" "SI")])
+
+;; 16-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movhi"
+  [(set (match_operand:HI 0 "")
+	(match_operand:HI 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (HImode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movhi_internal"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,r,m")
+	(match_operand:HI 1 "move_operand" "r,Yd,I,m,rJ"))]
+  "(register_operand (operands[0], HImode)
+       || reg_or_0_operand (operands[1], HImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,const,load,store")
+   (set_attr "compression" "all,all,*,*,*")
+   (set_attr "mode" "HI")])
+
+;; 8-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movqi"
+  [(set (match_operand:QI 0 "")
+	(match_operand:QI 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (QImode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movqi_internal"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r,m")
+	(match_operand:QI 1 "move_operand" "r,I,m,rJ"))]
+  "(register_operand (operands[0], QImode)
+       || reg_or_0_operand (operands[1], QImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,load,store")
+   (set_attr "compression" "all,*,*,*")
+   (set_attr "mode" "QI")])
+
+;; 32-bit floating point moves
+
+(define_expand "movsf"
+  [(set (match_operand:SF 0 "")
+	(match_operand:SF 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (SFmode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movsf_hardfloat"
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,*r,*r,*m")
+	(match_operand:SF 1 "move_operand" "f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
+  "TARGET_HARD_FLOAT
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "fmove,mgtf,fpload,fpstore,store,mgtf,mftg,move,load,store")
+   (set_attr "mode" "SF")])
+
+(define_insn "*movsf_softfloat"
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m")
+	(match_operand:SF 1 "move_operand" "Gr,m,r"))]
+  "TARGET_SOFT_FLOAT
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,load,store")
+   (set_attr "mode" "SF")])
+
+
+;; 64-bit floating point moves
+
+(define_expand "movdf"
+  [(set (match_operand:DF 0 "")
+	(match_operand:DF 1 ""))]
+  ""
+{
+  if (loongarch_legitimize_move (DFmode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movdf_hardfloat"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,*r,*r,*m")
+	(match_operand:DF 1 "move_operand" "f,G,m,f,G,*r,*f,*r*G,*m,*r"))]
+  "TARGET_DOUBLE_FLOAT
+   && (register_operand (operands[0], DFmode)
+       || reg_or_0_operand (operands[1], DFmode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "fmove,mgtf,fpload,fpstore,store,mgtf,mftg,move,load,store")
+   (set_attr "mode" "DF")])
+
+(define_insn "*movdf_softfloat"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,m")
+	(match_operand:DF 1 "move_operand" "rG,m,rG"))]
+  "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
+   && (register_operand (operands[0], DFmode)
+       || reg_or_0_operand (operands[1], DFmode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,load,store")
+   (set_attr "mode" "DF")])
+
+
+;; 128-bit integer moves
+
+(define_expand "movti"
+  [(set (match_operand:TI 0)
+	(match_operand:TI 1))]
+  "TARGET_64BIT"
+{
+  if (loongarch_legitimize_move (TImode, operands[0], operands[1]))
+    DONE;
+})
+
+(define_insn "*movti"
+  [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,r,m")
+	(match_operand:TI 1 "move_operand" "r,i,m,rJ"))]
+  "TARGET_64BIT
+   && (register_operand (operands[0], TImode)
+       || reg_or_0_operand (operands[1], TImode))"
+  { return loongarch_output_move (operands[0], operands[1]); }
+  [(set_attr "move_type" "move,const,load,store")
+   (set (attr "mode")
+    (if_then_else (eq_attr "move_type" "imul")
+		      (const_string "SI")
+		      (const_string "TI")))])
+
+
+;; 128-bit floating point moves
+
+(define_expand "movtf"
+  [(set (match_operand:TF 0)
+	(match_operand:TF 1))]
+  "TARGET_64BIT"
+{
+  if (loongarch_legitimize_move (TFmode, operands[0], operands[1]))
+    DONE;
+})
+
+;; This pattern handles both hard- and soft-float cases.
+(define_insn "*movtf"
+  [(set (match_operand:TF 0 "nonimmediate_operand" "=r,r,m,f,r,f,m")
+	(match_operand:TF 1 "move_operand" "rG,m,rG,rG,f,m,f"))]
+  "TARGET_64BIT
+   && (register_operand (operands[0], TFmode)
+       || reg_or_0_operand (operands[1], TFmode))"
+  "#"
+  [(set_attr "move_type" "move,load,store,mgtf,mftg,fpload,fpstore")
+   (set_attr "mode" "TF")])
+
+
+(define_split
+  [(set (match_operand:MOVE64 0 "nonimmediate_operand")
+	(match_operand:MOVE64 1 "move_operand"))]
+  "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1], insn)"
+  [(const_int 0)]
+{
+  loongarch_split_move_insn (operands[0], operands[1], curr_insn);
+  DONE;
+})
+
+(define_split
+  [(set (match_operand:MOVE128 0 "nonimmediate_operand")
+	(match_operand:MOVE128 1 "move_operand"))]
+  "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1], insn)"
+  [(const_int 0)]
+{
+  loongarch_split_move_insn (operands[0], operands[1], curr_insn);
+  DONE;
+})
+
+;; Emit a doubleword move in which exactly one of the operands is
+;; a floating-point register.  We can't just emit two normal moves
+;; because of the constraints imposed by the FPU register model;
+;; see loongarch_cannot_change_mode_class for details.  Instead, we keep
+;; the FPR whole and use special patterns to refer to each word of
+;; the other operand.
+
+(define_expand "move_doubleword_fpr<mode>"
+  [(set (match_operand:SPLITF 0)
+	(match_operand:SPLITF 1))]
+  ""
+{
+  if (FP_REG_RTX_P (operands[0]))
+    {
+      rtx low = loongarch_subword (operands[1], 0);
+      rtx high = loongarch_subword (operands[1], 1);
+      emit_insn (gen_load_low<mode> (operands[0], low));
+      if (!TARGET_64BIT)
+       emit_insn (gen_movgr2frh<mode> (operands[0], high, operands[0]));
+      else
+       emit_insn (gen_load_high<mode> (operands[0], high, operands[0]));
+    }
+  else
+    {
+      rtx low = loongarch_subword (operands[0], 0);
+      rtx high = loongarch_subword (operands[0], 1);
+      emit_insn (gen_store_word<mode> (low, operands[1], const0_rtx));
+      if (!TARGET_64BIT)
+       emit_insn (gen_movfrh2gr<mode> (high, operands[1]));
+      else
+       emit_insn (gen_store_word<mode> (high, operands[1], const1_rtx));
+    }
+  DONE;
+})
+
+;; Conditional move instructions.
+
+(define_insn "*sel<code><GPR:mode>_using_<GPR2:mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(if_then_else:GPR
+	 (equality_op:GPR2 (match_operand:GPR2 1 "register_operand" "r,r")
+			   (const_int 0))
+	 (match_operand:GPR 2 "reg_or_0_operand" "r,J")
+	 (match_operand:GPR 3 "reg_or_0_operand" "J,r")))]
+  "register_operand (operands[2], <GPR:MODE>mode)
+       != register_operand (operands[3], <GPR:MODE>mode)"
+  "@
+   <sel>\t%0,%2,%1
+   <selinv>\t%0,%3,%1"
+  [(set_attr "type" "condmove")
+   (set_attr "mode" "<GPR:MODE>")])
+
+;; sel.fmt copies the 3rd argument when the 1st is non-zero and the 2nd
+;; argument if the 1st is zero.  This means operand 2 and 3 are
+;; inverted in the instruction.
+
+(define_insn "*sel<mode>"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(if_then_else:ANYF
+	 (ne:FCC (match_operand:FCC 1 "register_operand" "z")
+		 (const_int 0))
+	 (match_operand:ANYF 2 "reg_or_0_operand" "f")
+	 (match_operand:ANYF 3 "reg_or_0_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fsel\t%0,%3,%2,%1"
+  [(set_attr "type" "condmove")
+   (set_attr "mode" "<ANYF:MODE>")])
+
+;; These are the main define_expand's used to make conditional moves.
+
+(define_expand "mov<mode>cc"
+  [(set (match_operand:GPR 0 "register_operand")
+	(if_then_else:GPR (match_operator 1 "comparison_operator"
+			 [(match_operand:GPR 2 "reg_or_0_operand")
+			  (match_operand:GPR 3 "reg_or_0_operand")])))]
+  "TARGET_COND_MOVE_INT"
+{
+  if (!INTEGRAL_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+    FAIL;
+
+  loongarch_expand_conditional_move (operands);
+  DONE;
+})
+
+(define_expand "mov<mode>cc"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(if_then_else:ANYF (match_operator 1 "comparison_operator"
+			  [(match_operand:ANYF 2 "reg_or_0_operand")
+			   (match_operand:ANYF 3 "reg_or_0_operand")])))]
+  "TARGET_COND_MOVE_FLOAT"
+{
+  if (!FLOAT_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+    FAIL;
+
+  loongarch_expand_conditional_move (operands);
+  DONE;
+})
+;; lu32i.d
+(define_insn "lu32i_d"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(ior:DI
+	  (zero_extend:DI
+	    (subreg:SI (match_operand:DI 1 "register_operand" "0") 0))
+	  (match_operand:DI 2 "const_lu32i_operand" "u")))]
+  "TARGET_64BIT"
+  "lu32i.d\t%0,%X2>>32"
+  [(set_attr "type" "arith")
+   (set_attr "mode" "DI")])
+
+;; lu52i.d
+
+(define_insn "lu52i_d"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(ior:DI
+	  (and:DI (match_operand:DI 1 "register_operand" "r")
+		  (match_operand 2 "lu52i_mask_operand"))
+	  (match_operand 3 "const_lu52i_operand" "v")))]
+    "TARGET_64BIT"
+    "lu52i.d\t%0,%1,%X3>>52"
+    [(set_attr "type" "arith")
+     (set_attr "mode" "DI")])
+
+;; Convert floating-point numbers to integers
+
+(define_insn "frint_<fmt>"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+		      UNSPEC_FRINT))]
+  "TARGET_HARD_FLOAT"
+  "frint.<fmt>\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "<MODE>")])
+
+;; LoongArch supports loading and storing a floating point register from
+;; the sum of two general registers.  We use two versions for each of
+;; these four instructions: one where the two general registers are
+;; SImode, and one where they are DImode.  This is because general
+;; registers will be in SImode when they hold 32-bit values, but,
+;; since the 32-bit values are always sign extended, the f{ld/st}x.{s/d}
+;; instructions will still work correctly.
+
+;; ??? Perhaps it would be better to support these instructions by
+;; modifying TARGET_LEGITIMATE_ADDRESS_P and friends.  However, since
+;; these instructions can only be used to load and store floating
+;; point registers, that would probably cause trouble in reload.
+
+(define_insn "*<ANYF:floadx>_<P:mode>"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(mem:ANYF (plus:P (match_operand:P 1 "register_operand" "r")
+			  (match_operand:P 2 "register_operand" "r"))))]
+  "TARGET_HARD_FLOAT"
+  "<ANYF:floadx>\t%0,%1,%2"
+  [(set_attr "type" "fpidxload")
+   (set_attr "mode" "<ANYF:UNITMODE>")])
+
+(define_insn "*<ANYF:fstorex>_<P:mode>"
+  [(set (mem:ANYF (plus:P (match_operand:P 1 "register_operand" "r")
+			  (match_operand:P 2 "register_operand" "r")))
+	(match_operand:ANYF 0 "register_operand" "f"))]
+  "TARGET_HARD_FLOAT"
+  "<ANYF:fstorex>\t%0,%1,%2"
+  [(set_attr "type" "fpidxstore")
+   (set_attr "mode" "<ANYF:UNITMODE>")])
+
+;; loading and storing a integer register from the sum of two general
+;; registers.
+
+(define_insn "*<GPR:loadx>_<P:mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(mem:GPR
+	    (plus:P (match_operand:P 1 "register_operand" "r")
+		    (match_operand:P 2 "register_operand" "r"))))]
+  ""
+  "<GPR:loadx>\t%0,%1,%2"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*<GPR:storex>_<P:mode>"
+  [(set (mem:GPR (plus:P (match_operand:P 1 "register_operand" "r")
+			 (match_operand:P 2 "register_operand" "r")))
+	(match_operand:GPR 0 "register_operand" "r"))]
+  ""
+  "<GPR:storex>\t%0,%1,%2"
+  [(set_attr "type" "store")
+   (set_attr "mode" "<GPR:MODE>")])
+
+;; SHORT mode sign_extend.
+(define_insn "*extend_<SHORT:loadx>_<GPR:mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(sign_extend:GPR
+	  (mem:SHORT
+	    (plus:P (match_operand:P 1 "register_operand" "r")
+		    (match_operand:P 2 "register_operand" "r")))))]
+  ""
+  "<SHORT:loadx>\t%0,%1,%2"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*extend_<SHORT:storex>"
+  [(set (mem:SHORT (plus:P (match_operand:P 1 "register_operand" "r")
+			   (match_operand:P 2 "register_operand" "r")))
+	(match_operand:SHORT 0 "register_operand" "r"))]
+  ""
+  "<SHORT:storex>\t%0,%1,%2"
+  [(set_attr "type" "store")
+   (set_attr "mode" "SI")])
+
+;; Load the low word of operand 0 with operand 1.
+(define_insn "load_low<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+	(unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")]
+		       UNSPEC_LOAD_LOW))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[0] = loongarch_subword (operands[0], 0);
+  return loongarch_output_move (operands[0], operands[1]);
+}
+  [(set_attr "move_type" "mgtf,fpload")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Load the high word of operand 0 from operand 1, preserving the value
+;; in the low word.
+(define_insn "load_high<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+	(unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")
+			(match_operand:SPLITF 2 "register_operand" "0,0")]
+		       UNSPEC_LOAD_HIGH))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[0] = loongarch_subword (operands[0], 1);
+  return loongarch_output_move (operands[0], operands[1]);
+}
+  [(set_attr "move_type" "mgtf,fpload")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Store one word of operand 1 in operand 0.  Operand 2 is 1 to store the
+;; high word and 0 to store the low word.
+(define_insn "store_word<mode>"
+  [(set (match_operand:<HALFMODE> 0 "nonimmediate_operand" "=r,m")
+	(unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f,f")
+			    (match_operand 2 "const_int_operand")]
+			   UNSPEC_STORE_WORD))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[1] = loongarch_subword (operands[1], INTVAL (operands[2]));
+  return loongarch_output_move (operands[0], operands[1]);
+}
+  [(set_attr "move_type" "mftg,fpstore")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Thread-Local Storage
+
+(define_insn "got_load_tls_gd<mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec:P
+	    [(match_operand:P 1 "symbolic_operand" "")]
+	    UNSPEC_TLS_GD))]
+  ""
+  "la.tls.gd\t%0,%1"
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "got_load_tls_ld<mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec:P
+	    [(match_operand:P 1 "symbolic_operand" "")]
+	    UNSPEC_TLS_LD))]
+  ""
+  "la.tls.ld\t%0,%1"
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "got_load_tls_le<mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec:P
+	    [(match_operand:P 1 "symbolic_operand" "")]
+	    UNSPEC_TLS_LE))]
+  ""
+  "la.tls.le\t%0,%1"
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "got_load_tls_ie<mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec:P
+	    [(match_operand:P 1 "symbolic_operand" "")]
+	    UNSPEC_TLS_IE))]
+  ""
+  "la.tls.ie\t%0,%1"
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
+
+;; Move operand 1 to the high word of operand 0 using movgr2frh, preserving the
+;; value in the low word.
+(define_insn "movgr2frh<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f")
+	(unspec:SPLITF [(match_operand:<HALFMODE> 1 "reg_or_0_operand" "rJ")
+			(match_operand:SPLITF 2 "register_operand" "0")]
+			UNSPEC_MOVGR2FRH))]
+  "TARGET_DOUBLE_FLOAT"
+  "movgr2frh.w\t%z1,%0"
+  [(set_attr "move_type" "mgtf")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Move high word of operand 1 to operand 0 using movfrh2gr.
+(define_insn "movfrh2gr<mode>"
+  [(set (match_operand:<HALFMODE> 0 "register_operand" "=r")
+	(unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f")]
+			    UNSPEC_MOVFRH2GR))]
+  "TARGET_DOUBLE_FLOAT"
+  "movfrh2gr.s\t%0,%1"
+  [(set_attr "move_type" "mftg")
+   (set_attr "mode" "<HALFMODE>")])
+
+\f
+;; Expand in-line code to clear the instruction cache between operand[0] and
+;; operand[1].
+(define_expand "clear_cache"
+  [(match_operand 0 "pmode_register_operand")
+   (match_operand 1 "pmode_register_operand")]
+  ""
+  "
+{
+  emit_insn (gen_ibar (const0_rtx));
+  DONE;
+}")
+
+(define_insn "ibar"
+  [(unspec_volatile:SI [(match_operand 0 "const_uimm15_operand")] UNSPEC_IBAR)]
+  ""
+  "ibar\t%0")
+
+(define_insn "dbar"
+  [(unspec_volatile:SI [(match_operand 0 "const_uimm15_operand")] UNSPEC_DBAR)]
+  ""
+  "dbar\t%0")
+
+\f
+
+;; Privileged state instruction
+
+(define_insn "cpucfg"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "r")]
+			     UNSPEC_CPUCFG))]
+  ""
+  "cpucfg\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "SI")])
+
+(define_insn "asrtle_d"
+	[(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "r")
+			      (match_operand:DI 1 "register_operand" "r")]
+			      UNSPEC_ASRTLE_D)]
+  "TARGET_64BIT"
+  "asrtle.d\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "DI")])
+
+(define_insn "asrtgt_d"
+	[(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "r")
+			      (match_operand:DI 1 "register_operand" "r")]
+			      UNSPEC_ASRTGT_D)]
+  "TARGET_64BIT"
+  "asrtgt.d\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "DI")])
+
+(define_insn "<p>csrrd"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(unspec_volatile:GPR [(match_operand  1 "const_uimm14_operand")]
+			     UNSPEC_CSRRD))]
+  ""
+  "csrrd\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "<p>csrwr"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	  (unspec_volatile:GPR
+	  [(match_operand:GPR 1 "register_operand" "0")
+	   (match_operand 2 "const_uimm14_operand")]
+	  UNSPEC_CSRWR))]
+  ""
+  "csrwr\t%0,%2"
+  [(set_attr "type" "store")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "<p>csrxchg"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	  (unspec_volatile:GPR
+	  [(match_operand:GPR 1 "register_operand" "0")
+	   (match_operand:GPR 2 "register_operand" "q")
+	   (match_operand 3 "const_uimm14_operand")]
+	  UNSPEC_CSRXCHG))]
+  ""
+  "csrxchg\t%0,%2,%3"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "iocsrrd_<size>"
+  [(set (match_operand:QHWD 0 "register_operand" "=r")
+	(unspec_volatile:QHWD [(match_operand:SI 1 "register_operand" "r")]
+			      UNSPEC_IOCSRRD))]
+  ""
+  "iocsrrd.<size>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "iocsrwr_<size>"
+  [(unspec_volatile:QHWD [(match_operand:QHWD 0 "register_operand" "r")
+			  (match_operand:SI 1 "register_operand" "r")]
+			UNSPEC_IOCSRWR)]
+  ""
+  "iocsrwr.<size>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "<p>cacop"
+  [(unspec_volatile:X [(match_operand 0 "const_uimm5_operand")
+			 (match_operand:X 1 "register_operand" "r")
+			 (match_operand 2 "const_imm12_operand")]
+			 UNSPEC_CACOP)]
+  ""
+  "cacop\t%0,%1,%2"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "<p>lddir"
+  [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+			 (match_operand:X 1 "register_operand" "r")
+			 (match_operand 2 "const_uimm5_operand")]
+			 UNSPEC_LDDIR)]
+  ""
+  "lddir\t%0,%1,%2"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "<p>ldpte"
+  [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+			 (match_operand 1 "const_uimm5_operand")]
+			 UNSPEC_LDPTE)]
+  ""
+  "ldpte\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+\f
+;; Block moves, see loongarch.c for more details.
+;; Argument 0 is the destination
+;; Argument 1 is the source
+;; Argument 2 is the length
+;; Argument 3 is the alignment
+
+(define_expand "cpymemsi"
+  [(parallel [(set (match_operand:BLK 0 "general_operand")
+		   (match_operand:BLK 1 "general_operand"))
+	      (use (match_operand:SI 2 ""))
+	      (use (match_operand:SI 3 "const_int_operand"))])]
+  " !TARGET_MEMCPY"
+{
+  if (loongarch_expand_block_move (operands[0], operands[1], operands[2]))
+    DONE;
+  else
+    FAIL;
+})
+\f
+;;
+;;  ....................
+;;
+;;	SHIFTS
+;;
+;;  ....................
+
+(define_insn "<optab><mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(any_shift:GPR (match_operand:GPR 1 "register_operand" "r")
+		       (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+{
+  if (CONST_INT_P (operands[2]))
+    operands[2] = GEN_INT (INTVAL (operands[2])
+			   & (GET_MODE_BITSIZE (<MODE>mode) - 1));
+
+  return "<insn>%i2.<d>\t%0,%1,%2";
+}
+  [(set_attr "type" "shift")
+   (set_attr "compression" "none")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*<optab>si3_extend"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(sign_extend:DI
+	   (any_shift:SI (match_operand:SI 1 "register_operand" "r")
+			 (match_operand:SI 2 "arith_operand" "rI"))))]
+  "TARGET_64BIT"
+{
+  if (CONST_INT_P (operands[2]))
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+  return "<insn>%i2.w\t%0,%1,%2";
+}
+  [(set_attr "type" "shift")
+   (set_attr "mode" "SI")])
+
+(define_insn "rotr<mode>3"
+  [(set (match_operand:GPR 0 "register_operand" "=r,r")
+	(rotatert:GPR (match_operand:GPR 1 "register_operand" "r,r")
+		      (match_operand:SI 2 "arith_operand" "r,I")))]
+  ""
+  "rotr%i2.<d>\t%0,%1,%2"
+  [(set_attr "type" "shift,shift")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "zero_extend_ashift1"
+ [ (set (match_operand:DI 0 "register_operand" "=r")
+	(and:DI (ashift:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0)
+			   (match_operand 2 "const_immlsa_operand" ""))
+		(match_operand 3 "shift_mask_operand" "")))]
+"TARGET_64BIT"
+"bstrpick.d\t%0,%1,31,0\n\talsl.d\t%0,%0,$r0,%2"
+[(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "insn_count" "2")])
+
+(define_insn "zero_extend_ashift2"
+ [ (set (match_operand:DI 0 "register_operand" "=r")
+	(and:DI (ashift:DI (match_operand:DI 1 "register_operand" "r")
+			   (match_operand 2 "const_immlsa_operand" ""))
+		(match_operand 3 "shift_mask_operand" "")))]
+"TARGET_64BIT"
+"bstrpick.d\t%0,%1,31,0\n\talsl.d\t%0,%0,$r0,%2"
+[(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "insn_count" "2")])
+
+(define_insn "alsl_paired1"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+	(plus:DI (and:DI (ashift:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0)
+			   (match_operand 2 "const_immlsa_operand" ""))
+			(match_operand 3 "shift_mask_operand" ""))
+		(match_operand:DI 4 "register_operand" "r")))]
+ "TARGET_64BIT"
+ "bstrpick.d\t%0,%1,31,0\n\talsl.d\t%0,%0,%4,%2"
+ [(set_attr "type" "arith")
+  (set_attr "mode" "DI")
+  (set_attr "insn_count" "2")])
+
+(define_insn "alsl_paired2"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+	(plus:DI (match_operand:DI 1 "register_operand" "r")
+		(and:DI (ashift:DI (match_operand:DI 2 "register_operand" "r")
+			   (match_operand 3 "const_immlsa_operand" ""))
+			(match_operand 4 "shift_mask_operand" ""))))]
+ "TARGET_64BIT"
+ "bstrpick.d\t%0,%2,31,0\n\talsl.d\t%0,%0,%1,%3"
+ [(set_attr "type" "arith")
+  (set_attr "mode" "DI")
+  (set_attr "insn_count" "2")])
+
+(define_insn "alsl<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+	(plus:GPR (ashift:GPR (match_operand:GPR 1 "register_operand" "r")
+			     (match_operand 2 "const_immlsa_operand" ""))
+		(match_operand:GPR 3 "register_operand" "r")))]
+ ""
+ "alsl.<d>\t%0,%1,%3,%2"
+ [(set_attr "type" "arith")
+  (set_attr "mode" "<MODE>")])
+
+\f
+
+;; Reverse the order of bytes of operand 1 and store the result in operand 0.
+
+(define_insn "bswaphi2"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+	(bswap:HI (match_operand:HI 1 "register_operand" "r")))]
+  ""
+  "revb.2h\t%0,%1"
+  [(set_attr "type" "shift")])
+
+(define_insn_and_split "bswapsi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(bswap:SI (match_operand:SI 1 "register_operand" "r")))]
+  ""
+  "#"
+  ""
+  [(set (match_dup 0) (unspec:SI [(match_dup 1)] UNSPEC_REVB_2H))
+   (set (match_dup 0) (rotatert:SI (match_dup 0) (const_int 16)))]
+  ""
+  [(set_attr "insn_count" "2")])
+
+(define_insn_and_split "bswapdi2"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(bswap:DI (match_operand:DI 1 "register_operand" "r")))]
+  "TARGET_64BIT"
+  "#"
+  ""
+  [(set (match_dup 0) (unspec:DI [(match_dup 1)] UNSPEC_REVB_4H))
+   (set (match_dup 0) (unspec:DI [(match_dup 0)] UNSPEC_REVH_D))]
+  ""
+  [(set_attr "insn_count" "2")])
+
+(define_insn "revb_2h"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "r")] UNSPEC_REVB_2H))]
+  ""
+  "revb.2h\t%0,%1"
+  [(set_attr "type" "shift")])
+
+(define_insn "revb_4h"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_REVB_4H))]
+  "TARGET_64BIT"
+  "revb.4h\t%0,%1"
+  [(set_attr "type" "shift")])
+
+(define_insn "revh_d"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_REVH_D))]
+  "TARGET_64BIT"
+  "revh.d\t%0,%1"
+  [(set_attr "type" "shift")])
+\f
+;;
+;;  ....................
+;;
+;;	CONDITIONAL BRANCHES
+;;
+;;  ....................
+
+;; Conditional branches
+
+(define_insn "*branch_fp_FCCmode"
+  [(set (pc)
+	(if_then_else
+	  (match_operator 1 "equality_operator"
+	      [(match_operand:FCC 2 "register_operand" "z")
+		(const_int 0)])
+	  (label_ref (match_operand 0 "" ""))
+	(pc)))]
+  "TARGET_HARD_FLOAT"
+{
+  return loongarch_output_conditional_branch (insn, operands,
+					      LARCH_BRANCH ("b%F1", "%Z2%0"),
+					      LARCH_BRANCH ("b%W1", "%Z2%0"));
+}
+  [(set_attr "type" "branch")])
+
+(define_insn "*branch_fp_inverted_FCCmode"
+  [(set (pc)
+	(if_then_else
+	  (match_operator 1 "equality_operator"
+	    [(match_operand:FCC 2 "register_operand" "z")
+	    (const_int 0)])
+	    (pc)
+	  (label_ref (match_operand 0 "" ""))))]
+  "TARGET_HARD_FLOAT"
+{
+  return loongarch_output_conditional_branch (insn, operands,
+					      LARCH_BRANCH ("b%W1", "%Z2%0"),
+					      LARCH_BRANCH ("b%F1", "%Z2%0"));
+}
+  [(set_attr "type" "branch")])
+
+;; Conditional branches on ordered comparisons with zero.
+
+(define_insn "*branch_order<mode>"
+  [(set (pc)
+	(if_then_else
+	 (match_operator 1 "order_operator"
+			 [(match_operand:GPR 2 "register_operand" "r,r")
+			  (match_operand:GPR 3 "reg_or_0_operand" "J,r")])
+	 (label_ref (match_operand 0 "" ""))
+	 (pc)))]
+  ""
+  { return loongarch_output_order_conditional_branch (insn, operands, false); }
+  [(set_attr "type" "branch")
+   (set_attr "hazard" "forbidden_slot")])
+
+(define_insn "*branch_order<mode>_inverted"
+  [(set (pc)
+	(if_then_else
+	 (match_operator 1 "order_operator"
+			 [(match_operand:GPR 2 "register_operand" "r,r")
+			  (match_operand:GPR 3 "reg_or_0_operand" "J,r")])
+	 (pc)
+	 (label_ref (match_operand 0 "" ""))))]
+  ""
+  { return loongarch_output_order_conditional_branch (insn, operands, true); }
+  [(set_attr "type" "branch")
+   (set_attr "hazard" "forbidden_slot")])
+
+;; Conditional branch on equality comparison.
+
+(define_insn "*branch_equality<mode>"
+  [(set (pc)
+	(if_then_else
+	 (match_operator 1 "equality_operator"
+			 [(match_operand:GPR 2 "register_operand" "r")
+			  (match_operand:GPR 3 "reg_or_0_operand" "rJ")])
+	 (label_ref (match_operand 0 "" ""))
+	 (pc)))]
+  ""
+  { return loongarch_output_equal_conditional_branch (insn, operands, false); }
+  [(set_attr "type" "branch")
+   (set_attr "hazard" "forbidden_slot")])
+
+
+(define_insn "*branch_equality<mode>_inverted"
+  [(set (pc)
+	(if_then_else
+	 (match_operator 1 "equality_operator"
+			 [(match_operand:GPR 2 "register_operand" "r")
+			  (match_operand:GPR 3 "reg_or_0_operand" "rJ")])
+	 (pc)
+	 (label_ref (match_operand 0 "" ""))))]
+  ""
+  { return loongarch_output_equal_conditional_branch (insn, operands, true); }
+  [(set_attr "type" "branch")
+   (set_attr "hazard" "forbidden_slot")])
+
+
+(define_expand "cbranch<mode>4"
+  [(set (pc)
+	(if_then_else (match_operator 0 "comparison_operator"
+		      [(match_operand:GPR 1 "register_operand")
+			(match_operand:GPR 2 "nonmemory_operand")])
+		      (label_ref (match_operand 3 ""))
+		      (pc)))]
+  ""
+{
+  loongarch_expand_conditional_branch (operands);
+  DONE;
+})
+
+(define_expand "cbranch<mode>4"
+  [(set (pc)
+	(if_then_else (match_operator 0 "comparison_operator"
+			[(match_operand:ANYF 1 "register_operand")
+			(match_operand:ANYF 2 "register_operand")])
+		      (label_ref (match_operand 3 ""))
+		      (pc)))]
+  ""
+{
+  loongarch_expand_conditional_branch (operands);
+  DONE;
+})
+
+;; Used to implement built-in functions.
+(define_expand "condjump"
+  [(set (pc)
+	(if_then_else (match_operand 0)
+		      (label_ref (match_operand 1))
+		      (pc)))])
+
+
+\f
+;;
+;;  ....................
+;;
+;;	SETTING A REGISTER FROM A COMPARISON
+;;
+;;  ....................
+
+;; Destination is always set in SI mode.
+
+(define_expand "cstore<mode>4"
+  [(set (match_operand:SI 0 "register_operand")
+	(match_operator:SI 1 "loongarch_cstore_operator"
+	 [(match_operand:GPR 2 "register_operand")
+	  (match_operand:GPR 3 "nonmemory_operand")]))]
+  ""
+{
+  loongarch_expand_scc (operands);
+  DONE;
+})
+
+(define_insn "*seq_zero_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(eq:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		 (const_int 0)))]
+  ""
+  "sltui\t%0,%1,1"
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+
+(define_insn "*sne_zero_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(ne:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		 (const_int 0)))]
+  ""
+  "sltu\t%0,%.,%1"
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sgt<u>_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(any_gt:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		     (match_operand:GPR 2 "reg_or_0_operand" "rJ")))]
+  ""
+  "slt<u>\t%0,%z2,%1"
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sge<u>_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(any_ge:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		     (const_int 1)))]
+  ""
+  "slt<u>i\t%0,%.,%1"
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*slt<u>_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(any_lt:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		     (match_operand:GPR 2 "arith_operand" "rI")))]
+  ""
+  "slt<u>%i2\t%0,%1,%2";
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sle<u>_<GPR:mode><GPR2:mode>"
+  [(set (match_operand:GPR2 0 "register_operand" "=r")
+	(any_le:GPR2 (match_operand:GPR 1 "register_operand" "r")
+		     (match_operand:GPR 2 "sle_operand" "")))]
+  ""
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+  return "slt<u>i\t%0,%1,%2";
+}
+  [(set_attr "type" "slt")
+   (set_attr "mode" "<GPR:MODE>")])
+
+\f
+;;
+;;  ....................
+;;
+;;	FLOATING POINT COMPARISONS
+;;
+;;  ....................
+
+(define_insn "s<code>_<ANYF:mode>_using_FCCmode"
+  [(set (match_operand:FCC 0 "register_operand" "=z")
+	(fcond:FCC (match_operand:ANYF 1 "register_operand" "f")
+		    (match_operand:ANYF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "fcmp.<fcond>.<fmt>\t%Z0%1,%2"
+  [(set_attr "type" "fcmp")
+   (set_attr "mode" "FCC")])
+
+\f
+;;
+;;  ....................
+;;
+;;	UNCONDITIONAL BRANCHES
+;;
+;;  ....................
+
+;; Unconditional branches.
+
+(define_expand "jump"
+  [(set (pc)
+	(label_ref (match_operand 0)))])
+
+(define_insn "*jump_absolute"
+  [(set (pc)
+	(label_ref (match_operand 0)))]
+  "!flag_pic"
+{
+  return "b\t%l0";
+}
+  [(set_attr "type" "branch")])
+
+(define_insn "*jump_pic"
+  [(set (pc)
+	(label_ref (match_operand 0)))]
+  "flag_pic"
+{
+  return "b\t%0";
+}
+  [(set_attr "type" "branch")])
+
+(define_expand "indirect_jump"
+  [(set (pc) (match_operand 0 "register_operand"))]
+  ""
+{
+  operands[0] = force_reg (Pmode, operands[0]);
+  emit_jump_insn (PMODE_INSN (gen_indirect_jump, (operands[0])));
+  DONE;
+})
+
+(define_insn "indirect_jump_<mode>"
+  [(set (pc) (match_operand:P 0 "register_operand" "r"))]
+  ""
+  {
+    return "jr\t%0";
+  }
+  [(set_attr "type" "jump")
+   (set_attr "mode" "none")])
+
+(define_expand "tablejump"
+  [(set (pc)
+	(match_operand 0 "register_operand"))
+   (use (label_ref (match_operand 1 "")))]
+  ""
+{
+  if (flag_pic)
+      operands[0] = expand_simple_binop (Pmode, PLUS, operands[0],
+					 gen_rtx_LABEL_REF (Pmode,
+							    operands[1]),
+					 NULL_RTX, 0, OPTAB_DIRECT);
+  emit_jump_insn (PMODE_INSN (gen_tablejump, (operands[0], operands[1])));
+  DONE;
+})
+
+(define_insn "tablejump_<mode>"
+  [(set (pc)
+	(match_operand:P 0 "register_operand" "r"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  {
+    return "jr\t%0";
+  }
+  [(set_attr "type" "jump")
+   (set_attr "mode" "none")])
+
+
+\f
+;;
+;;  ....................
+;;
+;;	Function prologue/epilogue
+;;
+;;  ....................
+;;
+
+(define_expand "prologue"
+  [(const_int 1)]
+  ""
+{
+  loongarch_expand_prologue ();
+  DONE;
+})
+
+;; Block any insns from being moved before this point, since the
+;; profiling call to mcount can use various registers that aren't
+;; saved or used to pass arguments.
+
+(define_insn "blockage"
+  [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
+  ""
+  ""
+  [(set_attr "type" "ghost")
+   (set_attr "mode" "none")])
+
+(define_insn "probe_stack_range_<P:mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec_volatile:P [(match_operand:P 1 "register_operand" "0")
+			    (match_operand:P 2 "register_operand" "r")
+			    (match_operand:P 3 "register_operand" "r")]
+			    UNSPEC_PROBE_STACK_RANGE))]
+  ""
+{
+  return loongarch_output_probe_stack_range (operands[0],
+					     operands[2],
+					     operands[3]);
+}
+  [(set_attr "type" "unknown")
+   (set_attr "can_delay" "no")
+   (set_attr "mode" "<MODE>")])
+
+(define_expand "epilogue"
+  [(const_int 2)]
+  ""
+{
+  loongarch_expand_epilogue (false);
+  DONE;
+})
+
+(define_expand "sibcall_epilogue"
+  [(const_int 2)]
+  ""
+{
+  loongarch_expand_epilogue (true);
+  DONE;
+})
+
+;; Trivial return.  Make it look like a normal return insn as that
+;; allows jump optimizations to work better.
+
+(define_expand "return"
+  [(simple_return)]
+  "loongarch_can_use_return_insn ()"
+  { })
+
+(define_expand "simple_return"
+  [(simple_return)]
+  ""
+  { })
+
+(define_insn "*<optab>"
+  [(any_return)]
+  ""
+  {
+    operands[0] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
+    return "jr\t%0";
+  }
+  [(set_attr "type"	"jump")
+   (set_attr "mode"	"none")])
+
+;; Normal return.
+
+(define_insn "<optab>_internal"
+  [(any_return)
+   (use (match_operand 0 "pmode_register_operand" ""))]
+  ""
+  {
+    return "jr\t%0";
+  }
+  [(set_attr "type"	"jump")
+   (set_attr "mode"	"none")])
+
+;; Exception return.
+(define_insn "loongarch_ertn"
+  [(return)
+   (unspec_volatile [(const_int 0)] UNSPEC_ERTN)]
+  ""
+  "ertn"
+  [(set_attr "type"	"trap")
+   (set_attr "mode"	"none")])
+
+;; This is used in compiling the unwind routines.
+(define_expand "eh_return"
+  [(use (match_operand 0 "general_operand"))]
+  ""
+{
+  if (GET_MODE (operands[0]) != word_mode)
+    operands[0] = convert_to_mode (word_mode, operands[0], 0);
+  if (TARGET_64BIT)
+    emit_insn (gen_eh_set_ra_di (operands[0]));
+  else
+    emit_insn (gen_eh_set_ra_si (operands[0]));
+  DONE;
+})
+
+;; Clobber the return address on the stack.  We can't expand this
+;; until we know where it will be put in the stack frame.
+
+(define_insn "eh_set_ra_si"
+  [(unspec [(match_operand:SI 0 "register_operand" "r")] UNSPEC_EH_RETURN)
+   (clobber (match_scratch:SI 1 "=&r"))]
+  "! TARGET_64BIT"
+  "#")
+
+(define_insn "eh_set_ra_di"
+  [(unspec [(match_operand:DI 0 "register_operand" "r")] UNSPEC_EH_RETURN)
+   (clobber (match_scratch:DI 1 "=&r"))]
+  "TARGET_64BIT"
+  "#")
+
+(define_split
+  [(unspec [(match_operand 0 "register_operand")] UNSPEC_EH_RETURN)
+   (clobber (match_scratch 1))]
+  "reload_completed"
+  [(const_int 0)]
+{
+  loongarch_set_return_address (operands[0], operands[1]);
+  DONE;
+})
+
+
+\f
+;;
+;;  ....................
+;;
+;;	FUNCTION CALLS
+;;
+;;  ....................
+
+;; Sibling calls.  All these patterns use jump instructions.
+
+(define_expand "sibcall"
+  [(parallel [(call (match_operand 0 "")
+		    (match_operand 1 ""))
+	      (use (match_operand 2 ""))	;; next_arg_reg
+	      (use (match_operand 3 ""))])]	;; struct_value_size_rtx
+  ""
+{
+  rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
+
+  emit_call_insn (gen_sibcall_internal (target, operands[1]));
+  DONE;
+})
+
+(define_insn "sibcall_internal"
+  [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,a,t,h"))
+	 (match_operand 1 "" ""))]
+  "SIBLING_CALL_P (insn)"
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return "jr\t%0";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,(%%pcrel(%0+0x20000))>>18\n\t"
+	       "jirl\t$r0,$r12,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r12,$r13,%0\n\tjr\t$r12";
+      else
+	return "b\t%0";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "b\t%0";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%0\n\tjr\t$r12";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%0\n\tjr\t$r12";
+    case 4:
+      if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return "b\t%%plt(%0)";
+      else if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,(%%plt(%0)+0x20000)>>18\n\t"
+	       "jirl\t$r0,$r12,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
+      else
+	{
+	  sorry ("cmodel extreme and tiny static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+    }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_expand "sibcall_value"
+  [(parallel [(set (match_operand 0 "")
+		   (call (match_operand 1 "")
+			 (match_operand 2 "")))
+	      (use (match_operand 3 ""))])]		;; next_arg_reg
+  ""
+{
+  rtx target = loongarch_legitimize_call_address (XEXP (operands[1], 0));
+
+ /*  Handle return values created by loongarch_return_fpr_pair.  */
+  if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 2)
+    {
+      rtx arg1 = XEXP (XVECEXP (operands[0],0, 0), 0);
+      rtx arg2 = XEXP (XVECEXP (operands[0],0, 1), 0);
+
+      emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
+							   operands[2],
+							   arg2));
+    }
+   else
+    {
+      /*  Handle return values created by loongarch_return_fpr_single.  */
+      if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
+      operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+      emit_call_insn (gen_sibcall_value_internal (operands[0], target,
+						  operands[2]));
+    }
+  DONE;
+})
+
+(define_insn "sibcall_value_internal"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+	      (match_operand 2 "" "")))]
+  "SIBLING_CALL_P (insn)"
+{
+  switch (which_alternative)
+  {
+    case 0:
+      return "jr\t%1";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
+	       "jirl\t$r0,$r12,%%pcrel(%1+4)-((%%pcrel(%1+4+0x20000))>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "b\t%1";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "b\t%1";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%1\n\tjr\t$r12";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%1\n\tjr\t$r12";
+    case 4:
+      if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return " b\t%%plt(%1)";
+      else if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
+	       "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+      else
+	{
+	  sorry ("loongarch cmodel extreme and tiny-static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+  }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_insn "sibcall_value_multiple_internal"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+	      (match_operand 2 "" "")))
+   (set (match_operand 3 "register_operand" "")
+	(call (mem:SI (match_dup 1))
+	      (match_dup 2)))]
+  "SIBLING_CALL_P (insn)"
+{
+  switch (which_alternative)
+  {
+    case 0:
+      return "jr\t%1";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
+	       "jirl\t$r0,$r12,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "b\t%1";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "b\t%1";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%1\n\tjr\t$r12";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+      else
+	return "la.global\t$r12,%1\n\tjr\t$r12";
+    case 4:
+      if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return "b\t%%plt(%1)";
+      else if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
+	       "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+      else
+	{
+	  sorry ("loongarch cmodel extreme and tiny-static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+  }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_expand "call"
+  [(parallel [(call (match_operand 0 "")
+		    (match_operand 1 ""))
+	      (use (match_operand 2 ""))	;; next_arg_reg
+	      (use (match_operand 3 ""))])]	;; struct_value_size_rtx
+  ""
+{
+  rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
+
+  emit_call_insn (gen_call_internal (target, operands[1]));
+  DONE;
+})
+
+(define_insn "call_internal"
+  [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,a,t,h"))
+	 (match_operand 1 "" ""))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  ""
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return "jirl\t$r1,%0,0";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,%%pcrel(%0+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+      else
+	return "bl\t%0";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "bl\t%0";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+      else
+	return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+      else
+	return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
+    case 4:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,(%%plt(%0)+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
+      else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return "bl\t%%plt(%0)";
+      else
+	{
+	  sorry ("cmodel extreme and tiny-static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+    }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+   (set_attr "insn_count" "1,2,3,3,2")])
+
+(define_expand "call_value"
+  [(parallel [(set (match_operand 0 "")
+		   (call (match_operand 1 "")
+			 (match_operand 2 "")))
+	      (use (match_operand 3 ""))])]		;; next_arg_reg
+  ""
+{
+  rtx target = loongarch_legitimize_call_address (XEXP (operands[1], 0));
+ /*  Handle return values created by loongarch_return_fpr_pair.  */
+  if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 2)
+    {
+      rtx arg1 = XEXP (XVECEXP (operands[0], 0, 0), 0);
+      rtx arg2 = XEXP (XVECEXP (operands[0], 0, 1), 0);
+
+      emit_call_insn (gen_call_value_multiple_internal (arg1, target,
+							operands[2], arg2));
+    }
+   else
+    {
+      /*  Handle return values created by loongarch_return_fpr_single.  */
+      if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
+      operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+      emit_call_insn (gen_call_value_internal (operands[0], target,
+					       operands[2]));
+    }
+  DONE;
+})
+
+;; See comment for call_internal.
+(define_insn "call_value_internal"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+	      (match_operand 2 "" "")))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  ""
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return "jirl\t$r1,%1,0";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+      else
+	return "bl\t%1";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "bl\t%1";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+      else
+	return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+      else
+	return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+    case 4:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+      else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return "bl\t%%plt(%1)";
+      else
+	{
+	  sorry ("loongarch cmodel extreme and tiny-static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+    }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+   (set_attr "insn_count" "1,2,3,3,2")])
+
+;; See comment for call_internal.
+(define_insn "call_value_multiple_internal"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+	      (match_operand 2 "" "")))
+   (set (match_operand 3 "register_operand" "")
+	(call (mem:SI (match_dup 1))
+	      (match_dup 2)))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  ""
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return "jirl\t$r1,%1,0";
+    case 1:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+      else
+	return "bl\t%1";
+    case 2:
+      if (TARGET_CMODEL_TINY_STATIC)
+	return "bl\t%1";
+      else if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0 ";
+      else
+	return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+    case 3:
+      if (TARGET_CMODEL_EXTREME)
+	return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+      else
+	return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+    case 4:
+      if (TARGET_CMODEL_LARGE)
+	return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
+	       "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+      else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+	return "bl\t%%plt(%1)";
+      else
+	{
+	  sorry ("loongarch cmodel extreme and tiny-static not support plt");
+	  return "";  /* GCC complains about may fall through.  */
+	}
+    default:
+      gcc_unreachable ();
+    }
+}
+  [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+   (set_attr "insn_count" "1,2,3,3,2")])
+
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+  [(parallel [(call (match_operand 0 "")
+		    (const_int 0))
+	      (match_operand 1 "")
+	      (match_operand 2 "")])]
+  ""
+{
+  int i;
+
+  emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx));
+
+  for (i = 0; i < XVECLEN (operands[2], 0); i++)
+    {
+      rtx set = XVECEXP (operands[2], 0, i);
+      loongarch_emit_move (SET_DEST (set), SET_SRC (set));
+    }
+
+  emit_insn (gen_blockage ());
+  DONE;
+})
+\f
+;;
+;;  ....................
+;;
+;;	MISC.
+;;
+;;  ....................
+;;
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop"
+  [(set_attr "type"	"nop")
+   (set_attr "mode"	"none")])
+
+;; __builtin_loongarch_movfcsr2gr: move the FCSR into operand 0.
+(define_insn "loongarch_movfcsr2gr"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+    (unspec_volatile:SI [(match_operand 1 "const_uimm5_operand")]
+    UNSPEC_MOVFCSR2GR))]
+  "TARGET_HARD_FLOAT"
+  "movfcsr2gr\t%0,$r%1")
+
+;; __builtin_loongarch_movgr2fcsr: move operand 0 into the FCSR.
+(define_insn "loongarch_movgr2fcsr"
+  [(unspec_volatile [(match_operand 0 "const_uimm5_operand")
+		     (match_operand:SI 1 "register_operand" "r")]
+	  UNSPEC_MOVGR2FCSR)]
+  "TARGET_HARD_FLOAT"
+  "movgr2fcsr\t$r%0,%1")
+
+(define_insn "fclass_<fmt>"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+       (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+		UNSPEC_FCLASS))]
+  "TARGET_HARD_FLOAT"
+  "fclass.<fmt>\t%0,%1"
+  [(set_attr "type" "unknown")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "bytepick_w"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "r")
+		   (match_operand:SI 2 "register_operand" "r")
+		   (match_operand:SI 3 "const_0_to_3_operand" "n")]
+	      UNSPEC_BYTEPICK_W))]
+  ""
+  "bytepick.w\t%0,%1,%2,%z3"
+  [(set_attr "mode"    "SI")])
+
+(define_insn "bytepick_d"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")
+		   (match_operand:DI 2 "register_operand" "r")
+		   (match_operand:DI 3 "const_0_to_7_operand" "n")]
+	      UNSPEC_BYTEPICK_D))]
+  ""
+  "bytepick.d\t%0,%1,%2,%z3"
+  [(set_attr "mode"    "DI")])
+
+(define_insn "bitrev_4b"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "r")]
+	    UNSPEC_BITREV_4B))]
+  ""
+  "bitrev.4b\t%0,%1"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "SI")])
+
+(define_insn "bitrev_8b"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")]
+	    UNSPEC_BITREV_8B))]
+  ""
+  "bitrev.8b\t%0,%1"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "DI")])
+
+(define_insn "stack_tie<mode>"
+  [(set (mem:BLK (scratch))
+	(unspec:BLK [(match_operand:GPR 0 "register_operand" "r")
+		     (match_operand:GPR 1 "register_operand" "r")]
+		    UNSPEC_TIE))]
+  ""
+  ""
+  [(set_attr "length" "0")]
+)
+
+(define_insn "gpr_restore_return"
+  [(return)
+   (use (match_operand 0 "pmode_register_operand" ""))
+   (const_int 0)]
+  ""
+  "")
+\f
+(define_split
+  [(match_operand 0 "small_data_pattern")]
+  "reload_completed"
+  [(match_dup 0)]
+  { operands[0] = loongarch_rewrite_small_data (operands[0]); })
+
+
+;; Match paired HI/SI/SF/DFmode load/stores.
+(define_insn "*join2_load_store<JOIN_MODE:mode>"
+  [(set (match_operand:JOIN_MODE 0 "nonimmediate_operand" "=r,f,m,m,r,ZC")
+	(match_operand:JOIN_MODE 1 "nonimmediate_operand" "m,m,r,f,ZC,r"))
+   (set (match_operand:JOIN_MODE 2 "nonimmediate_operand" "=r,f,m,m,r,ZC")
+	(match_operand:JOIN_MODE 3 "nonimmediate_operand" "m,m,r,f,ZC,r"))]
+  "reload_completed"
+  {
+    bool load_p = (which_alternative == 0 || which_alternative == 1);
+    /* Reg-renaming pass reuses base register if it is dead after bonded loads.
+       Hardware does not bond those loads, even when they are consecutive.
+       However, order of the loads need to be checked for correctness.  */
+    if (!load_p || !reg_overlap_mentioned_p (operands[0], operands[1]))
+      {
+	output_asm_insn (loongarch_output_move (operands[0], operands[1]),
+			 operands);
+	output_asm_insn (loongarch_output_move (operands[2], operands[3]),
+			 &operands[2]);
+      }
+    else
+      {
+	output_asm_insn (loongarch_output_move (operands[2], operands[3]),
+			 &operands[2]);
+	output_asm_insn (loongarch_output_move (operands[0], operands[1]),
+			 operands);
+      }
+    return "";
+  }
+  [(set_attr "move_type" "load,fpload,store,fpstore,load,store")
+   (set_attr "insn_count" "2,2,2,2,2,2")])
+
+;; 2 HI/SI/SF/DF loads are bonded.
+(define_peephole2
+  [(set (match_operand:JOIN_MODE 0 "register_operand")
+	(match_operand:JOIN_MODE 1 "non_volatile_mem_operand"))
+   (set (match_operand:JOIN_MODE 2 "register_operand")
+	(match_operand:JOIN_MODE 3 "non_volatile_mem_operand"))]
+  "loongarch_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, true)"
+  [(parallel [(set (match_dup 0)
+		   (match_dup 1))
+	      (set (match_dup 2)
+		   (match_dup 3))])]
+  "")
+
+;; 2 HI/SI/SF/DF stores are bonded.
+(define_peephole2
+  [(set (match_operand:JOIN_MODE 0 "memory_operand")
+	(match_operand:JOIN_MODE 1 "register_operand"))
+   (set (match_operand:JOIN_MODE 2 "memory_operand")
+	(match_operand:JOIN_MODE 3 "register_operand"))]
+  "loongarch_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, false)"
+  [(parallel [(set (match_dup 0)
+		   (match_dup 1))
+	      (set (match_dup 2)
+		   (match_dup 3))])]
+  "")
+
+;; Match paired HImode loads.
+(define_insn "*join2_loadhi"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand" "m")))
+   (set (match_operand:SI 2 "register_operand" "=r")
+	(any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand" "m")))]
+  "reload_completed"
+  {
+    /* Reg-renaming pass reuses base register if it is dead after bonded loads.
+       Hardware does not bond those loads, even when they are consecutive.
+       However, order of the loads need to be checked for correctness.  */
+    if (!reg_overlap_mentioned_p (operands[0], operands[1]))
+      {
+	output_asm_insn ("ld.h<u>\t%0,%1", operands);
+	output_asm_insn ("ld.h<u>\t%2,%3", operands);
+      }
+    else
+      {
+	output_asm_insn ("ld.h<u>\t%2,%3", operands);
+	output_asm_insn ("ld.h<u>\t%0,%1", operands);
+      }
+
+    return "";
+  }
+  [(set_attr "move_type" "load")
+   (set_attr "insn_count" "2")])
+
+
+;; 2 HI loads are bonded.
+(define_peephole2
+  [(set (match_operand:SI 0 "register_operand")
+	(any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand")))
+   (set (match_operand:SI 2 "register_operand")
+	(any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand")))]
+  "loongarch_load_store_bonding_p (operands, HImode, true)"
+  [(parallel [(set (match_dup 0)
+		   (any_extend:SI (match_dup 1)))
+	      (set (match_dup 2)
+		   (any_extend:SI (match_dup 3)))])]
+  "")
+
+\f
+
+(define_mode_iterator QHSD [QI HI SI DI])
+
+(define_insn "crc_w_<size>_w"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(match_operand:QHSD 1 "register_operand" "r")
+		   (match_operand:SI 2 "register_operand" "r")]
+		     UNSPEC_CRC))]
+  ""
+  "crc.w.<size>.w\t%0,%1,%2"
+  [(set_attr "type" "unknown")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "crcc_w_<size>_w"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(match_operand:QHSD 1 "register_operand" "r")
+		   (match_operand:SI 2 "register_operand" "r")]
+		     UNSPEC_CRCC))]
+  ""
+  "crcc.w.<size>.w\t%0,%1,%2"
+  [(set_attr "type" "unknown")
+   (set_attr "mode" "<MODE>")])
+
+;; Synchronization instructions.
+
+(include "sync.md")
+
+(include "generic.md")
+(include "la464.md")
+
+(define_c_enum "unspec" [
+  UNSPEC_ADDRESS_FIRST
+])
diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
new file mode 100644
index 00000000000..681ee93ec32
--- /dev/null
+++ b/gcc/config/loongarch/predicates.md
@@ -0,0 +1,553 @@
+;; Predicate definitions for LoongArch target.
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;; Based on MIPS target for GNU compiler.
+;;
+;; This file is part of GCC.
+;;
+;; GCC 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 3, or (at your option)
+;; any later version.
+;;
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_predicate "const_uns_arith_operand"
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND_UNSIGNED (INTVAL (op))")))
+
+(define_predicate "uns_arith_operand"
+  (ior (match_operand 0 "const_uns_arith_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "const_lu32i_operand"
+  (and (match_code "const_int")
+       (match_test "LU32I_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_lu52i_operand"
+  (and (match_code "const_int")
+       (match_test "LU52I_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_arith_operand"
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_imm16_operand"
+  (and (match_code "const_int")
+       (match_test "IMM16_OPERAND (INTVAL (op))")))
+
+(define_predicate "arith_operand"
+  (ior (match_operand 0 "const_arith_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "const_immlsa_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 1, 4)")))
+
+(define_predicate "const_uimm3_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 7)")))
+
+(define_predicate "const_uimm4_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 15)")))
+
+(define_predicate "const_uimm5_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 31)")))
+
+(define_predicate "const_uimm6_operand"
+  (and (match_code "const_int")
+       (match_test "UIMM6_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_uimm7_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 127)")))
+
+(define_predicate "const_uimm8_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 255)")))
+
+(define_predicate "const_uimm14_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 16383)")))
+
+(define_predicate "const_uimm15_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 32767)")))
+
+(define_predicate "const_imm5_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), -16, 15)")))
+
+(define_predicate "const_imm10_operand"
+  (and (match_code "const_int")
+       (match_test "IMM10_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_imm12_operand"
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND (INTVAL (op))")))
+
+(define_predicate "reg_imm10_operand"
+  (ior (match_operand 0 "const_imm10_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "aq8b_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 0)")))
+
+(define_predicate "aq8h_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 1)")))
+
+(define_predicate "aq8w_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 2)")))
+
+(define_predicate "aq8d_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 3)")))
+
+(define_predicate "aq10b_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 10, 0)")))
+
+(define_predicate "aq10h_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 10, 1)")))
+
+(define_predicate "aq10w_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 10, 2)")))
+
+(define_predicate "aq10d_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 10, 3)")))
+
+(define_predicate "aq12b_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 12, 0)")))
+
+(define_predicate "aq12h_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 11, 1)")))
+
+(define_predicate "aq12w_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 10, 2)")))
+
+(define_predicate "aq12d_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 9, 3)")))
+
+(define_predicate "sle_operand"
+  (and (match_code "const_int")
+       (match_test "IMM12_OPERAND (INTVAL (op) + 1)")))
+
+(define_predicate "sleu_operand"
+  (and (match_operand 0 "sle_operand")
+       (match_test "INTVAL (op) + 1 != 0")))
+
+(define_predicate "const_0_operand"
+  (and (match_code "const_int,const_double,const_vector")
+       (match_test "op == CONST0_RTX (GET_MODE (op))")))
+
+(define_predicate "const_m1_operand"
+  (and (match_code "const_int,const_double,const_vector")
+       (match_test "op == CONSTM1_RTX (GET_MODE (op))")))
+
+(define_predicate "reg_or_m1_operand"
+  (ior (match_operand 0 "const_m1_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "reg_or_0_operand"
+  (ior (match_operand 0 "const_0_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "const_1_operand"
+  (and (match_code "const_int,const_double,const_vector")
+       (match_test "op == CONST1_RTX (GET_MODE (op))")))
+
+(define_predicate "reg_or_1_operand"
+  (ior (match_operand 0 "const_1_operand")
+       (match_operand 0 "register_operand")))
+
+;; These are used in vec_merge, hence accept bitmask as const_int.
+(define_predicate "const_exp_2_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (exact_log2 (INTVAL (op)), 0, 1)")))
+
+(define_predicate "const_exp_4_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (exact_log2 (INTVAL (op)), 0, 3)")))
+
+(define_predicate "const_exp_8_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (exact_log2 (INTVAL (op)), 0, 7)")))
+
+(define_predicate "const_exp_16_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (exact_log2 (INTVAL (op)), 0, 15)")))
+
+(define_predicate "const_exp_32_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (exact_log2 (INTVAL (op)), 0, 31)")))
+
+;; This is used for indexing into vectors, and hence only accepts const_int.
+(define_predicate "const_0_or_1_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 1)")))
+
+(define_predicate "const_2_or_3_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 2, 3)")))
+
+(define_predicate "const_0_to_3_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 3)")))
+
+(define_predicate "const_0_to_7_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 7)")))
+
+(define_predicate "const_4_to_7_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 4, 7)")))
+
+(define_predicate "const_8_to_15_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 7)")))
+
+(define_predicate "const_16_to_31_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 7)")))
+
+(define_predicate "qi_mask_operand"
+  (and (match_code "const_int")
+       (match_test "UINTVAL (op) == 0xff")))
+
+(define_predicate "hi_mask_operand"
+  (and (match_code "const_int")
+       (match_test "UINTVAL (op) == 0xffff")))
+
+(define_predicate "lu52i_mask_operand"
+  (and (match_code "const_int")
+       (match_test "UINTVAL (op) == 0xfffffffffffff")))
+
+(define_predicate "shift_mask_operand"
+  (and (match_code "const_int")
+       (ior (match_test "UINTVAL (op) == 0x3fffffffc")
+	    (match_test "UINTVAL (op) == 0x1fffffffe")
+	    (match_test "UINTVAL (op) == 0x7fffffff8")
+	    (match_test "UINTVAL (op) == 0xffffffff0"))))
+
+(define_predicate "si_mask_operand"
+  (and (match_code "const_int")
+       (match_test "UINTVAL (op) == 0xffffffff")))
+
+(define_predicate "and_load_operand"
+  (ior (match_operand 0 "qi_mask_operand")
+       (match_operand 0 "hi_mask_operand")
+       (match_operand 0 "si_mask_operand")))
+
+(define_predicate "low_bitmask_operand"
+  (and (match_code "const_int")
+       (match_test "low_bitmask_len (mode, INTVAL (op)) > 12")))
+
+(define_predicate "and_reg_operand"
+  (ior (match_operand 0 "register_operand")
+       (match_operand 0 "const_uns_arith_operand")
+       (match_operand 0 "low_bitmask_operand")
+       (match_operand 0 "si_mask_operand")))
+
+(define_predicate "and_operand"
+  (ior (match_operand 0 "and_load_operand")
+       (match_operand 0 "and_reg_operand")))
+
+(define_predicate "d_operand"
+  (and (match_code "reg")
+       (match_test "GP_REG_P (REGNO (op))")))
+
+(define_predicate "db4_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op) + 1, 4, 0)")))
+
+(define_predicate "db7_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op) + 1, 7, 0)")))
+
+(define_predicate "db8_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op) + 1, 8, 0)")))
+
+(define_predicate "ib3_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op) - 1, 3, 0)")))
+
+(define_predicate "sb4_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 4, 0)")))
+
+(define_predicate "sb5_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 5, 0)")))
+
+(define_predicate "sb8_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 0)")))
+
+(define_predicate "sd8_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_signed_immediate_p (INTVAL (op), 8, 3)")))
+
+(define_predicate "ub4_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 4, 0)")))
+
+(define_predicate "ub8_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 8, 0)")))
+
+(define_predicate "uh4_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 4, 1)")))
+
+(define_predicate "uw4_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 4, 2)")))
+
+(define_predicate "uw5_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 5, 2)")))
+
+(define_predicate "uw6_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 6, 2)")))
+
+(define_predicate "uw8_operand"
+  (and (match_code "const_int")
+       (match_test "loongarch_unsigned_immediate_p (INTVAL (op), 8, 2)")))
+
+(define_predicate "addiur2_operand"
+  (and (match_code "const_int")
+	(ior (match_test "INTVAL (op) == -1")
+	     (match_test "INTVAL (op) == 1")
+	     (match_test "INTVAL (op) == 4")
+	     (match_test "INTVAL (op) == 8")
+	     (match_test "INTVAL (op) == 12")
+	     (match_test "INTVAL (op) == 16")
+	     (match_test "INTVAL (op) == 20")
+	     (match_test "INTVAL (op) == 24"))))
+
+(define_predicate "addiusp_operand"
+  (and (match_code "const_int")
+       (ior (match_test "(IN_RANGE (INTVAL (op), 2, 257))")
+	    (match_test "(IN_RANGE (INTVAL (op), -258, -3))"))))
+
+(define_predicate "andi16_operand"
+  (and (match_code "const_int")
+	(ior (match_test "IN_RANGE (INTVAL (op), 1, 4)")
+	     (match_test "IN_RANGE (INTVAL (op), 7, 8)")
+	     (match_test "IN_RANGE (INTVAL (op), 15, 16)")
+	     (match_test "IN_RANGE (INTVAL (op), 31, 32)")
+	     (match_test "IN_RANGE (INTVAL (op), 63, 64)")
+	     (match_test "INTVAL (op) == 255")
+	     (match_test "INTVAL (op) == 32768")
+	     (match_test "INTVAL (op) == 65535"))))
+
+(define_predicate "movep_src_register"
+  (and (match_code "reg")
+       (ior (match_test ("IN_RANGE (REGNO (op), 2, 3)"))
+	    (match_test ("IN_RANGE (REGNO (op), 16, 20)")))))
+
+(define_predicate "movep_src_operand"
+  (ior (match_operand 0 "const_0_operand")
+       (match_operand 0 "movep_src_register")))
+
+(define_predicate "fcc_reload_operand"
+  (and (match_code "reg,subreg")
+       (match_test "FCC_REG_P (true_regnum (op))")))
+
+(define_predicate "muldiv_target_operand"
+		(match_operand 0 "register_operand"))
+
+(define_predicate "const_call_insn_operand"
+  (match_code "const,symbol_ref,label_ref")
+{
+  enum loongarch_symbol_type symbol_type;
+
+  if (!loongarch_symbolic_constant_p (op, SYMBOL_CONTEXT_CALL, &symbol_type))
+    return false;
+
+  switch (symbol_type)
+    {
+    case SYMBOL_GOT_DISP:
+      /* Without explicit relocs, there is no special syntax for
+	 loading the address of a call destination into a register.
+	 Using "la.global JIRL_REGS,foo; jirl JIRL_REGS" would prevent the lazy
+	 binding of "foo", so keep the address of global symbols with the jirl
+	 macro.  */
+      return 1;
+
+    default:
+      return false;
+    }
+})
+
+(define_predicate "call_insn_operand"
+  (ior (match_operand 0 "const_call_insn_operand")
+       (match_operand 0 "register_operand")))
+
+(define_predicate "is_const_call_local_symbol"
+  (and (match_operand 0 "const_call_insn_operand")
+       (ior (match_test "loongarch_global_symbol_p (op) == 0")
+       (match_test "loongarch_symbol_binds_local_p (op) != 0"))
+       (match_test "CONSTANT_P (op)")))
+
+(define_predicate "is_const_call_weak_symbol"
+  (and (match_operand 0 "const_call_insn_operand")
+       (not (match_operand 0 "is_const_call_local_symbol"))
+       (match_test "loongarch_weak_symbol_p (op) != 0")
+       (match_test "CONSTANT_P (op)")))
+
+(define_predicate "is_const_call_plt_symbol"
+  (and (match_operand 0 "const_call_insn_operand")
+       (match_test "flag_plt != 0")
+       (match_test "loongarch_global_symbol_noweak_p (op) != 0")
+       (match_test "CONSTANT_P (op)")))
+
+(define_predicate "is_const_call_global_noplt_symbol"
+  (and (match_operand 0 "const_call_insn_operand")
+       (match_test "flag_plt == 0")
+       (match_test "loongarch_global_symbol_noweak_p (op) != 0")
+       (match_test "CONSTANT_P (op)")))
+
+;; A legitimate CONST_INT operand that takes more than one instruction
+;; to load.
+(define_predicate "splittable_const_int_operand"
+  (match_code "const_int")
+{
+  /* Don't handle multi-word moves this way; we don't want to introduce
+     the individual word-mode moves until after reload.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+    return false;
+
+  /* Otherwise check whether the constant can be loaded in a single
+     instruction.  */
+  return !LU12I_INT (op) && !IMM12_INT (op) && !IMM12_INT_UNSIGNED (op)
+	 && !LU52I_INT (op);
+})
+
+(define_predicate "move_operand"
+  (match_operand 0 "general_operand")
+{
+  enum loongarch_symbol_type symbol_type;
+
+  /* The thinking here is as follows:
+
+     (1) The move expanders should split complex load sequences into
+	 individual instructions.  Those individual instructions can
+	 then be optimized by all rtl passes.
+
+     (2) The target of pre-reload load sequences should not be used
+	 to store temporary results.  If the target register is only
+	 assigned one value, reload can rematerialize that value
+	 on demand, rather than spill it to the stack.
+
+     (3) If we allowed pre-reload passes like combine and cse to recreate
+	 complex load sequences, we would want to be able to split the
+	 sequences before reload as well, so that the pre-reload scheduler
+	 can see the individual instructions.  This falls foul of (2);
+	 the splitter would be forced to reuse the target register for
+	 intermediate results.
+
+     (4) We want to define complex load splitters for combine.  These
+	 splitters can request a temporary scratch register, which avoids
+	 the problem in (2).  They allow things like:
+
+	      (set (reg T1) (high SYM))
+	      (set (reg T2) (low (reg T1) SYM))
+	      (set (reg X) (plus (reg T2) (const_int OFFSET)))
+
+	 to be combined into:
+
+	      (set (reg T3) (high SYM+OFFSET))
+	      (set (reg X) (lo_sum (reg T3) SYM+OFFSET))
+
+	 if T2 is only used this once.  */
+  switch (GET_CODE (op))
+    {
+    case CONST_INT:
+      return !splittable_const_int_operand (op, mode);
+
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return (loongarch_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA,
+					     &symbol_type));
+    default:
+      return true;
+    }
+})
+
+(define_predicate "consttable_operand"
+  (match_test "CONSTANT_P (op)"))
+
+(define_predicate "symbolic_operand"
+  (match_code "const,symbol_ref,label_ref")
+{
+  enum loongarch_symbol_type type;
+  return loongarch_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type);
+})
+
+(define_predicate "got_disp_operand"
+  (match_code "const,symbol_ref,label_ref")
+{
+  enum loongarch_symbol_type type;
+  return (loongarch_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type)
+	  && type == SYMBOL_GOT_DISP);
+})
+
+(define_predicate "symbol_ref_operand"
+  (match_code "symbol_ref"))
+
+(define_predicate "stack_operand"
+  (and (match_code "mem")
+       (match_test "loongarch_stack_address_p (XEXP (op, 0), GET_MODE (op))")))
+
+(define_predicate "equality_operator"
+  (match_code "eq,ne"))
+
+(define_predicate "extend_operator"
+  (match_code "zero_extend,sign_extend"))
+
+(define_predicate "trap_comparison_operator"
+  (match_code "eq,ne,lt,ltu,ge,geu"))
+
+(define_predicate "order_operator"
+  (match_code "lt,ltu,le,leu,ge,geu,gt,gtu"))
+
+;; For NE, cstore uses sltu instructions in which the first operand is $0.
+
+(define_predicate "loongarch_cstore_operator"
+  (match_code "ne,eq,gt,gtu,ge,geu,lt,ltu,le,leu"))
+
+(define_predicate "small_data_pattern"
+  (and (match_code "set,parallel,unspec,unspec_volatile,prefetch")
+       (match_test "loongarch_small_data_pattern_p (op)")))
+
+(define_predicate "mem_noofs_operand"
+  (and (match_code "mem")
+       (match_code "reg" "0")))
+
+;; Return 1 if the operand is in non-volatile memory.
+(define_predicate "non_volatile_mem_operand"
+  (and (match_operand 0 "memory_operand")
+       (not (match_test "MEM_VOLATILE_P (op)"))))
diff --git a/gcc/config/loongarch/sync.md b/gcc/config/loongarch/sync.md
new file mode 100644
index 00000000000..69e9c137ac8
--- /dev/null
+++ b/gcc/config/loongarch/sync.md
@@ -0,0 +1,574 @@
+;; Machine description for LoongArch atomic operations.
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;; Based on MIPS and RISC-V target for GNU compiler.
+
+;; This file is part of GCC.
+
+;; GCC 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 3, or (at your option)
+;; any later version.
+
+;; GCC 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 GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_c_enum "unspec" [
+  UNSPEC_COMPARE_AND_SWAP
+  UNSPEC_COMPARE_AND_SWAP_ADD
+  UNSPEC_COMPARE_AND_SWAP_SUB
+  UNSPEC_COMPARE_AND_SWAP_AND
+  UNSPEC_COMPARE_AND_SWAP_XOR
+  UNSPEC_COMPARE_AND_SWAP_OR
+  UNSPEC_COMPARE_AND_SWAP_NAND
+  UNSPEC_SYNC_OLD_OP
+  UNSPEC_SYNC_EXCHANGE
+  UNSPEC_ATOMIC_STORE
+  UNSPEC_MEMORY_BARRIER
+])
+
+(define_code_iterator any_atomic [plus ior xor and])
+(define_code_attr atomic_optab
+  [(plus "add") (ior "or") (xor "xor") (and "and")])
+
+;; This attribute gives the format suffix for atomic memory operations.
+(define_mode_attr amo [(SI "w") (DI "d")])
+
+;; <amop> expands to the name of the atomic operand that implements a
+;; particular code.
+(define_code_attr amop [(ior "or") (xor "xor") (and "and") (plus "add")])
+
+;; Memory barriers.
+
+(define_expand "mem_thread_fence"
+  [(match_operand:SI 0 "const_int_operand" "")] ;; model
+  ""
+{
+  if (INTVAL (operands[0]) != MEMMODEL_RELAXED)
+    {
+      rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+      MEM_VOLATILE_P (mem) = 1;
+      emit_insn (gen_mem_thread_fence_1 (mem, operands[0]));
+    }
+  DONE;
+})
+
+;; Until the LoongArch memory model (hence its mapping from C++) is finalized,
+;; conservatively emit a full FENCE.
+(define_insn "mem_thread_fence_1"
+  [(set (match_operand:BLK 0 "" "")
+	(unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))
+   (match_operand:SI 1 "const_int_operand" "")] ;; model
+  ""
+  "dbar\t0")
+
+;; Atomic memory operations.
+
+;; Implement atomic stores with amoswap.  Fall back to fences for atomic loads.
+(define_insn "atomic_store<mode>"
+  [(set (match_operand:GPR 0 "memory_operand" "+ZB")
+    (unspec_volatile:GPR
+      [(match_operand:GPR 1 "reg_or_0_operand" "rJ")
+       (match_operand:SI 2 "const_int_operand")]      ;; model
+      UNSPEC_ATOMIC_STORE))]
+  ""
+  "amswap%A2.<amo>\t$zero,%z1,%0"
+  [(set (attr "length") (const_int 8))])
+
+(define_insn "atomic_<atomic_optab><mode>"
+  [(set (match_operand:GPR 0 "memory_operand" "+ZB")
+	(unspec_volatile:GPR
+	  [(any_atomic:GPR (match_dup 0)
+			   (match_operand:GPR 1 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 2 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+  "am<amop>%A2.<amo>\t$zero,%z1,%0"
+  [(set (attr "length") (const_int 8))])
+
+(define_insn "atomic_fetch_<atomic_optab><mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")
+	(match_operand:GPR 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR
+	  [(any_atomic:GPR (match_dup 1)
+		     (match_operand:GPR 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+  "am<amop>%A3.<amo>\t%0,%z2,%1"
+  [(set (attr "length") (const_int 8))])
+
+(define_insn "atomic_exchange<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")
+	(unspec_volatile:GPR
+	  [(match_operand:GPR 1 "memory_operand" "+ZB")
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	  UNSPEC_SYNC_EXCHANGE))
+   (set (match_dup 1)
+	(match_operand:GPR 2 "register_operand" "r"))]
+  ""
+  "amswap%A3.<amo>\t%0,%z2,%1"
+  [(set (attr "length") (const_int 8))])
+
+(define_insn "atomic_cas_value_strong<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")
+			      (match_operand:SI 4 "const_int_operand")  ;; mod_s
+			      (match_operand:SI 5 "const_int_operand")] ;; mod_f
+	 UNSPEC_COMPARE_AND_SWAP))
+   (clobber (match_scratch:GPR 6 "=&r"))]
+  ""
+{
+  return "%G5\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "bne\\t%0,%z2,2f\\n\\t"
+	 "or%i3\\t%6,$zero,%3\\n\\t"
+	 "sc.<amo>\\t%6,%1\\n\\t"
+	 "beq\\t$zero,%6,1b\\n\\t"
+	 "b\\t3f\\n\\t"
+	 "2:\\n\\t"
+	 "dbar\\t0x700\\n\\t"
+	 "3:\\n\\t";
+}
+  [(set (attr "length") (const_int 32))])
+
+(define_expand "atomic_compare_and_swap<mode>"
+  [(match_operand:SI 0 "register_operand" "")   ;; bool output
+   (match_operand:GPR 1 "register_operand" "")  ;; val output
+   (match_operand:GPR 2 "memory_operand" "")    ;; memory
+   (match_operand:GPR 3 "reg_or_0_operand" "")  ;; expected value
+   (match_operand:GPR 4 "reg_or_0_operand" "")  ;; desired value
+   (match_operand:SI 5 "const_int_operand" "")  ;; is_weak
+   (match_operand:SI 6 "const_int_operand" "")  ;; mod_s
+   (match_operand:SI 7 "const_int_operand" "")] ;; mod_f
+  ""
+{
+  emit_insn (gen_atomic_cas_value_strong<mode> (operands[1], operands[2],
+						operands[3], operands[4],
+						operands[6], operands[7]));
+
+  rtx compare = operands[1];
+  if (operands[3] != const0_rtx)
+    {
+      rtx difference = gen_rtx_MINUS (<MODE>mode, operands[1], operands[3]);
+      compare = gen_reg_rtx (<MODE>mode);
+      emit_insn (gen_rtx_SET (compare, difference));
+    }
+
+  if (word_mode != <MODE>mode)
+    {
+      rtx reg = gen_reg_rtx (word_mode);
+      emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
+      compare = reg;
+    }
+
+  emit_insn (gen_rtx_SET (operands[0],
+			  gen_rtx_EQ (SImode, compare, const0_rtx)));
+  DONE;
+})
+
+(define_expand "atomic_test_and_set"
+  [(match_operand:QI 0 "register_operand" "")     ;; bool output
+   (match_operand:QI 1 "memory_operand" "+ZB")    ;; memory
+   (match_operand:SI 2 "const_int_operand" "")]   ;; model
+  ""
+{
+  /* We have no QImode atomics, so use the address LSBs to form a mask,
+     then use an aligned SImode atomic.  */
+  rtx result = operands[0];
+  rtx mem = operands[1];
+  rtx model = operands[2];
+  rtx addr = force_reg (Pmode, XEXP (mem, 0));
+  rtx tmp_reg = gen_reg_rtx (Pmode);
+  rtx zero_reg = gen_rtx_REG (Pmode, 0);
+
+  rtx aligned_addr = gen_reg_rtx (Pmode);
+  emit_move_insn (tmp_reg, gen_rtx_PLUS (Pmode, zero_reg, GEN_INT (-4)));
+  emit_move_insn (aligned_addr, gen_rtx_AND (Pmode, addr, tmp_reg));
+
+  rtx aligned_mem = change_address (mem, SImode, aligned_addr);
+  set_mem_alias_set (aligned_mem, 0);
+
+  rtx offset = gen_reg_rtx (SImode);
+  emit_move_insn (offset, gen_rtx_AND (SImode, gen_lowpart (SImode, addr),
+				       GEN_INT (3)));
+
+  rtx tmp = gen_reg_rtx (SImode);
+  emit_move_insn (tmp, GEN_INT (1));
+
+  rtx shmt = gen_reg_rtx (SImode);
+  emit_move_insn (shmt, gen_rtx_ASHIFT (SImode, offset, GEN_INT (3)));
+
+  rtx word = gen_reg_rtx (SImode);
+  emit_move_insn (word, gen_rtx_ASHIFT (SImode, tmp, shmt));
+
+  tmp = gen_reg_rtx (SImode);
+  emit_insn (gen_atomic_fetch_orsi (tmp, aligned_mem, word, model));
+
+  emit_move_insn (gen_lowpart (SImode, result),
+		  gen_rtx_LSHIFTRT (SImode, tmp, shmt));
+  DONE;
+})
+
+(define_insn "atomic_cas_value_cmp_and_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")
+			      (match_operand:SI 6 "const_int_operand")] ;; model
+	 UNSPEC_COMPARE_AND_SWAP))
+   (clobber (match_scratch:GPR 7 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%2\\n\\t"
+	 "bne\\t%7,%z4,2f\\n\\t"
+	 "and\\t%7,%0,%z3\\n\\t"
+	 "or%i5\\t%7,%7,%5\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b\\n\\t"
+	 "b\\t3f\\n\\t"
+	 "2:\\n\\t"
+	 "dbar\\t0x700\\n\\t"
+	 "3:\\n\\t";
+}
+  [(set (attr "length") (const_int 40))])
+
+(define_expand "atomic_compare_and_swap<mode>"
+  [(match_operand:SI 0 "register_operand" "")   ;; bool output
+   (match_operand:SHORT 1 "register_operand" "")  ;; val output
+   (match_operand:SHORT 2 "memory_operand" "")    ;; memory
+   (match_operand:SHORT 3 "reg_or_0_operand" "")  ;; expected value
+   (match_operand:SHORT 4 "reg_or_0_operand" "")  ;; desired value
+   (match_operand:SI 5 "const_int_operand" "")  ;; is_weak
+   (match_operand:SI 6 "const_int_operand" "")  ;; mod_s
+   (match_operand:SI 7 "const_int_operand" "")] ;; mod_f
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_cmp_and_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[1], operands[2],
+				operands[3], operands[4], operands[7]);
+
+  rtx compare = operands[1];
+  if (operands[3] != const0_rtx)
+    {
+      machine_mode mode = GET_MODE (operands[3]);
+      rtx op1 = convert_modes (SImode, mode, operands[1], true);
+      rtx op3 = convert_modes (SImode, mode, operands[3], true);
+      rtx difference = gen_rtx_MINUS (SImode, op1, op3);
+      compare = gen_reg_rtx (SImode);
+      emit_insn (gen_rtx_SET (compare, difference));
+    }
+
+  if (word_mode != <MODE>mode)
+    {
+      rtx reg = gen_reg_rtx (word_mode);
+      emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
+      compare = reg;
+    }
+
+  emit_insn (gen_rtx_SET (operands[0],
+			  gen_rtx_EQ (SImode, compare, const0_rtx)));
+  DONE;
+})
+
+(define_insn "atomic_cas_value_add_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_ADD))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "add.w\\t%8,%0,%z5\\n\\t"
+	 "and\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+
+  [(set (attr "length") (const_int 32))])
+
+(define_insn "atomic_cas_value_sub_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_SUB))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "sub.w\\t%8,%0,%z5\\n\\t"
+	 "and\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+  [(set (attr "length") (const_int 32))])
+
+(define_insn "atomic_cas_value_and_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_AND))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "and\\t%8,%0,%z5\\n\\t"
+	 "and\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+  [(set (attr "length") (const_int 32))])
+
+(define_insn "atomic_cas_value_xor_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_XOR))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "xor\\t%8,%0,%z5\\n\\t"
+	 "and\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+
+  [(set (attr "length") (const_int 32))])
+
+(define_insn "atomic_cas_value_or_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_OR))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "or\\t%8,%0,%z5\\n\\t"
+	 "and\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+
+  [(set (attr "length") (const_int 32))])
+
+(define_insn "atomic_cas_value_nand_7_<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
+	(match_operand:GPR 1 "memory_operand" "+ZC"))
+   (set (match_dup 1)
+	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
+			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
+			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
+			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
+			      (match_operand:SI 6 "const_int_operand")]		;; model
+	 UNSPEC_COMPARE_AND_SWAP_NAND))
+   (clobber (match_scratch:GPR 7 "=&r"))
+   (clobber (match_scratch:GPR 8 "=&r"))]
+  ""
+{
+  return "%G6\\n\\t"
+	 "1:\\n\\t"
+	 "ll.<amo>\\t%0,%1\\n\\t"
+	 "and\\t%7,%0,%3\\n\\t"
+	 "and\\t%8,%0,%z5\\n\\t"
+	 "xor\\t%8,%8,%z2\\n\\t"
+	 "or%i8\\t%7,%7,%8\\n\\t"
+	 "sc.<amo>\\t%7,%1\\n\\t"
+	 "beq\\t$zero,%7,1b";
+}
+  [(set (attr "length") (const_int 32))])
+
+(define_expand "atomic_exchange<mode>"
+  [(set (match_operand:SHORT 0 "register_operand")
+	(unspec_volatile:SHORT
+	  [(match_operand:SHORT 1 "memory_operand")
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	  UNSPEC_SYNC_EXCHANGE))
+   (set (match_dup 1)
+	(match_operand:SHORT 2 "register_operand"))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_cmp_and_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_add<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(plus:SHORT (match_dup 1)
+		       (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_add_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_sub<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(minus:SHORT (match_dup 1)
+			(match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_sub_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_and<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(and:SHORT (match_dup 1)
+		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_and_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_xor<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(xor:SHORT (match_dup 1)
+		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_xor_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_or<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(ior:SHORT (match_dup 1)
+		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_or_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "atomic_fetch_nand<mode>"
+  [(set (match_operand:SHORT 0 "register_operand" "=&r")
+	(match_operand:SHORT 1 "memory_operand" "+ZB"))
+   (set (match_dup 1)
+	(unspec_volatile:SHORT
+	  [(not:SHORT (and:SHORT (match_dup 1)
+				 (match_operand:SHORT 2 "reg_or_0_operand" "rJ")))
+	   (match_operand:SI 3 "const_int_operand")] ;; model
+	 UNSPEC_SYNC_OLD_OP))]
+  ""
+{
+  union loongarch_gen_fn_ptrs generator;
+  generator.fn_7 = gen_atomic_cas_value_nand_7_si;
+  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
+				operands[1], operands[2], operands[3]);
+  DONE;
+})
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 05/12] LoongArch Port: Machine description C files and .h files.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (3 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 04/12] LoongArch Port: Machine Decsription files chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 06/12] LoongArch Port: Builtin functions chenglulu
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/
	* config/host-linux.c: Add LoongArch support.
	* config/loongarch/loongarch-protos.h: New file.
	* config/loongarch/loongarch-tune.h: Likewise.
	* config/loongarch/loongarch.c: Likewise.
	* config/loongarch/loongarch.h: Likewise.
---
 gcc/config/host-linux.c                 |    2 +
 gcc/config/loongarch/loongarch-protos.h |  242 +
 gcc/config/loongarch/loongarch-tune.h   |   72 +
 gcc/config/loongarch/loongarch.c        | 6461 +++++++++++++++++++++++
 gcc/config/loongarch/loongarch.h        | 1291 +++++
 5 files changed, 8068 insertions(+)
 create mode 100644 gcc/config/loongarch/loongarch-protos.h
 create mode 100644 gcc/config/loongarch/loongarch-tune.h
 create mode 100644 gcc/config/loongarch/loongarch.c
 create mode 100644 gcc/config/loongarch/loongarch.h

diff --git a/gcc/config/host-linux.c b/gcc/config/host-linux.c
index 0481ecff3f2..cbbc8d13533 100644
--- a/gcc/config/host-linux.c
+++ b/gcc/config/host-linux.c
@@ -98,6 +98,8 @@
 # define TRY_EMPTY_VM_SPACE	0x60000000
 #elif defined(__riscv) && defined (__LP64__)
 # define TRY_EMPTY_VM_SPACE	0x1000000000
+#elif defined(__loongarch__) && defined(__LP64__)
+# define TRY_EMPTY_VM_SPACE	0x8000000000
 #else
 # define TRY_EMPTY_VM_SPACE	0
 #endif
diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h
new file mode 100644
index 00000000000..58b5d72fc85
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -0,0 +1,242 @@
+/* Prototypes of target machine for GNU compiler.  LoongArch version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS target for GNU compiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LOONGARCH_PROTOS_H
+#define GCC_LOONGARCH_PROTOS_H
+
+/* Describes how a symbol is used.
+
+   SYMBOL_CONTEXT_CALL
+       The symbol is used as the target of a call instruction.
+
+   SYMBOL_CONTEXT_LEA
+       The symbol is used in a load-address operation.
+
+   SYMBOL_CONTEXT_MEM
+       The symbol is used as the address in a MEM.  */
+enum loongarch_symbol_context {
+  SYMBOL_CONTEXT_CALL,
+  SYMBOL_CONTEXT_LEA,
+  SYMBOL_CONTEXT_MEM
+};
+
+/* Classifies a SYMBOL_REF, LABEL_REF or UNSPEC address.
+
+   SYMBOL_GOT_DISP
+       The symbol's value will be loaded directly from the GOT.
+
+   SYMBOL_TLS
+       A thread-local symbol.
+
+   SYMBOL_TLSGD
+   SYMBOL_TLSLDM
+       UNSPEC wrappers around SYMBOL_TLS, corresponding to the
+       thread-local storage relocation operators.
+   */
+enum loongarch_symbol_type {
+  SYMBOL_GOT_DISP,
+  SYMBOL_TLS,
+  SYMBOL_TLSGD,
+  SYMBOL_TLSLDM,
+};
+#define NUM_SYMBOL_TYPES (SYMBOL_TLSLDM + 1)
+
+/* Classifies a type of call.
+
+   LARCH_CALL_NORMAL
+	A normal call or call_value pattern.
+
+   LARCH_CALL_SIBCALL
+	A sibcall or sibcall_value pattern.
+
+   LARCH_CALL_EPILOGUE
+	A call inserted in the epilogue.  */
+enum loongarch_call_type {
+  LARCH_CALL_NORMAL,
+  LARCH_CALL_SIBCALL,
+  LARCH_CALL_EPILOGUE
+};
+
+/* Controls the conditions under which certain instructions are split.
+
+   SPLIT_IF_NECESSARY
+	Only perform splits that are necessary for correctness
+	(because no unsplit version exists).
+
+   SPLIT_FOR_SPEED
+	Perform splits that are necessary for correctness or
+	beneficial for code speed.
+
+   SPLIT_FOR_SIZE
+	Perform splits that are necessary for correctness or
+	beneficial for code size.  */
+enum loongarch_split_type {
+  SPLIT_IF_NECESSARY,
+  SPLIT_FOR_SPEED,
+  SPLIT_FOR_SIZE
+};
+
+extern const char *const loongarch_fp_conditions[16];
+
+/* Routines implemented in loongarch.c.  */
+extern rtx loongarch_emit_move (rtx, rtx);
+extern HOST_WIDE_INT loongarch_initial_elimination_offset (int, int);
+extern void loongarch_expand_prologue (void);
+extern void loongarch_expand_epilogue (bool);
+extern bool loongarch_can_use_return_insn (void);
+\f
+extern bool loongarch_symbolic_constant_p (rtx, enum loongarch_symbol_context,
+					   enum loongarch_symbol_type *);
+extern int loongarch_regno_mode_ok_for_base_p (int, machine_mode, bool);
+extern bool loongarch_stack_address_p (rtx, machine_mode);
+extern int loongarch_address_insns (rtx, machine_mode, bool);
+extern int loongarch_const_insns (rtx);
+extern int loongarch_split_const_insns (rtx);
+extern int loongarch_split_128bit_const_insns (rtx);
+extern int loongarch_load_store_insns (rtx, rtx_insn *);
+extern int loongarch_idiv_insns (machine_mode);
+#ifdef RTX_CODE
+extern void loongarch_emit_binary (enum rtx_code, rtx, rtx, rtx);
+#endif
+extern bool loongarch_split_symbol (rtx, rtx, machine_mode, rtx *);
+extern rtx loongarch_unspec_address (rtx, enum loongarch_symbol_type);
+extern rtx loongarch_strip_unspec_address (rtx);
+extern void loongarch_move_integer (rtx, rtx, unsigned HOST_WIDE_INT);
+extern bool loongarch_legitimize_move (machine_mode, rtx, rtx);
+extern rtx loongarch_legitimize_call_address (rtx);
+
+extern rtx loongarch_subword (rtx, bool);
+extern bool loongarch_split_move_p (rtx, rtx, enum loongarch_split_type);
+extern void loongarch_split_move (rtx, rtx, enum loongarch_split_type, rtx);
+extern bool loongarch_split_move_insn_p (rtx, rtx, rtx);
+extern void loongarch_split_move_insn (rtx, rtx, rtx);
+extern const char *loongarch_output_move (rtx, rtx);
+extern bool loongarch_cfun_has_cprestore_slot_p (void);
+#ifdef RTX_CODE
+extern void loongarch_expand_scc (rtx *);
+extern void loongarch_expand_conditional_branch (rtx *);
+extern void loongarch_expand_conditional_move (rtx *);
+extern void loongarch_expand_conditional_trap (rtx);
+#endif
+extern void loongarch_set_return_address (rtx, rtx);
+extern bool loongarch_move_by_pieces_p (unsigned HOST_WIDE_INT, unsigned int);
+extern bool loongarch_store_by_pieces_p (unsigned HOST_WIDE_INT, unsigned int);
+extern bool loongarch_expand_block_move (rtx, rtx, rtx);
+
+extern bool loongarch_expand_ext_as_unaligned_load (rtx, rtx, HOST_WIDE_INT,
+						    HOST_WIDE_INT, bool);
+extern bool loongarch_expand_ins_as_unaligned_store (rtx, rtx, HOST_WIDE_INT,
+						     HOST_WIDE_INT);
+extern HOST_WIDE_INT loongarch_debugger_offset (rtx, HOST_WIDE_INT);
+
+extern void loongarch_output_external (FILE *, tree, const char *);
+extern void loongarch_output_ascii (FILE *, const char *, size_t);
+extern void loongarch_output_aligned_decl_common (FILE *, tree, const char *,
+						  unsigned HOST_WIDE_INT,
+						  unsigned int);
+extern void loongarch_declare_common_object (FILE *, const char *,
+					     const char *,
+					     unsigned HOST_WIDE_INT,
+					     unsigned int, bool);
+extern void loongarch_declare_object (FILE *, const char *, const char *,
+				      const char *, ...) ATTRIBUTE_PRINTF_4;
+extern void loongarch_declare_object_name (FILE *, const char *, tree);
+extern void loongarch_finish_declare_object (FILE *, tree, int, int);
+extern void loongarch_set_text_contents_type (FILE *, const char *,
+					      unsigned long, bool);
+
+extern bool loongarch_small_data_pattern_p (rtx);
+extern rtx loongarch_rewrite_small_data (rtx);
+extern rtx loongarch_return_addr (int, rtx);
+
+extern enum reg_class loongarch_secondary_reload_class (enum reg_class,
+							machine_mode,
+							rtx, bool);
+extern int loongarch_class_max_nregs (enum reg_class, machine_mode);
+
+extern machine_mode loongarch_hard_regno_caller_save_mode (unsigned int,
+							   unsigned int,
+							   machine_mode);
+extern int loongarch_adjust_insn_length (rtx_insn *, int);
+extern const char *loongarch_output_conditional_branch (rtx_insn *, rtx *,
+							const char *,
+							const char *);
+extern const char *loongarch_output_order_conditional_branch (rtx_insn *,
+							      rtx *,
+							      bool);
+extern const char *loongarch_output_equal_conditional_branch (rtx_insn *,
+							      rtx *,
+							      bool);
+extern const char *loongarch_output_division (const char *, rtx *);
+extern const char *loongarch_output_probe_stack_range (rtx, rtx, rtx);
+extern bool loongarch_hard_regno_rename_ok (unsigned int, unsigned int);
+extern int loongarch_dspalu_bypass_p (rtx, rtx);
+extern rtx loongarch_prefetch_cookie (rtx, rtx);
+
+extern bool loongarch_global_symbol_p (const_rtx);
+extern bool loongarch_global_symbol_noweak_p (const_rtx);
+extern bool loongarch_weak_symbol_p (const_rtx);
+extern bool loongarch_symbol_binds_local_p (const_rtx);
+
+extern const char *current_section_name (void);
+extern unsigned int current_section_flags (void);
+extern bool loongarch_use_ins_ext_p (rtx, HOST_WIDE_INT, HOST_WIDE_INT);
+
+union loongarch_gen_fn_ptrs
+{
+  rtx (*fn_8) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_7) (rtx, rtx, rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_6) (rtx, rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_5) (rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_4) (rtx, rtx, rtx, rtx);
+};
+
+extern void loongarch_expand_atomic_qihi (union loongarch_gen_fn_ptrs,
+					  rtx, rtx, rtx, rtx, rtx);
+
+extern bool loongarch_signed_immediate_p (unsigned HOST_WIDE_INT, int, int);
+extern bool loongarch_unsigned_immediate_p (unsigned HOST_WIDE_INT, int, int);
+extern bool loongarch_12bit_offset_address_p (rtx, machine_mode);
+extern bool loongarch_14bit_shifted_offset_address_p (rtx, machine_mode);
+extern rtx loongarch_expand_thread_pointer (rtx);
+
+extern bool loongarch_eh_uses (unsigned int);
+extern bool loongarch_epilogue_uses (unsigned int);
+extern bool loongarch_load_store_bonding_p (rtx *, machine_mode, bool);
+extern bool loongarch_split_symbol_type (enum loongarch_symbol_type);
+
+typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx);
+
+extern void loongarch_register_frame_header_opt (void);
+
+extern void loongarch_declare_function_name (FILE *, const char *, tree);
+
+/* Routines implemented in loongarch-c.c.  */
+void loongarch_cpu_cpp_builtins (cpp_reader *);
+
+extern void loongarch_init_builtins (void);
+extern void loongarch_atomic_assign_expand_fenv (tree *, tree *, tree *);
+extern tree loongarch_builtin_decl (unsigned int, bool);
+extern rtx loongarch_expand_builtin (tree, rtx, rtx subtarget ATTRIBUTE_UNUSED,
+				     machine_mode, int);
+extern tree loongarch_build_builtin_va_list (void);
+
+#endif /* ! GCC_LOONGARCH_PROTOS_H */
diff --git a/gcc/config/loongarch/loongarch-tune.h b/gcc/config/loongarch/loongarch-tune.h
new file mode 100644
index 00000000000..1ff99e9c28b
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-tune.h
@@ -0,0 +1,72 @@
+/* Definitions for microarchitecture-related data structures.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_TUNE_H
+#define LOONGARCH_TUNE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RTX costs of various operations on the different architectures.  */
+struct loongarch_rtx_cost_data
+{
+  unsigned short fp_add;
+  unsigned short fp_mult_sf;
+  unsigned short fp_mult_df;
+  unsigned short fp_div_sf;
+  unsigned short fp_div_df;
+  unsigned short int_mult_si;
+  unsigned short int_mult_di;
+  unsigned short int_div_si;
+  unsigned short int_div_di;
+  unsigned short branch_cost;
+  unsigned short memory_latency;
+};
+
+/* Default RTX cost initializer.  */
+#define COSTS_N_INSNS(N) ((N) * 4)
+#define DEFAULT_COSTS				\
+    .fp_add		= COSTS_N_INSNS (1),	\
+    .fp_mult_sf		= COSTS_N_INSNS (2),	\
+    .fp_mult_df		= COSTS_N_INSNS (2),	\
+    .fp_div_sf		= COSTS_N_INSNS (4),	\
+    .fp_div_df		= COSTS_N_INSNS (4),	\
+    .int_mult_si	= COSTS_N_INSNS (1),	\
+    .int_mult_di	= COSTS_N_INSNS (1),	\
+    .int_div_si		= COSTS_N_INSNS (1),	\
+    .int_div_di		= COSTS_N_INSNS (1),	\
+    .branch_cost	= 2,			\
+    .memory_latency	= 4
+
+/* Costs to use when optimizing for size.  */
+extern const struct loongarch_rtx_cost_data loongarch_rtx_cost_optimize_size;
+
+/* Cache size record of known processor models.  */
+struct loongarch_cache {
+    int l1d_line_size;  /* bytes */
+    int l1d_size;       /* KiB */
+    int l2d_size;       /* kiB */
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LOONGARCH_TUNE_H */
diff --git a/gcc/config/loongarch/loongarch.c b/gcc/config/loongarch/loongarch.c
new file mode 100644
index 00000000000..a3501dd505c
--- /dev/null
+++ b/gcc/config/loongarch/loongarch.c
@@ -0,0 +1,6461 @@
+/* Subroutines used for LoongArch code generation.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS and RISC-V target for GNU compiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "gimple.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "optabs.h"
+#include "regs.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "insn-attr.h"
+#include "output.h"
+#include "alias.h"
+#include "fold-const.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "explow.h"
+#include "expr.h"
+#include "libfuncs.h"
+#include "reload.h"
+#include "common/common-target.h"
+#include "langhooks.h"
+#include "cfgrtl.h"
+#include "cfganal.h"
+#include "sched-int.h"
+#include "gimplify.h"
+#include "target-globals.h"
+#include "tree-pass.h"
+#include "context.h"
+#include "builtins.h"
+#include "rtl-iter.h"
+
+/* This file should be included last.  */
+#include "target-def.h"
+
+/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF.  */
+#define UNSPEC_ADDRESS_P(X)					\
+  (GET_CODE (X) == UNSPEC					\
+   && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST			\
+   && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES)
+
+/* Extract the symbol or label from UNSPEC wrapper X.  */
+#define UNSPEC_ADDRESS(X) XVECEXP (X, 0, 0)
+
+/* Extract the symbol type from UNSPEC wrapper X.  */
+#define UNSPEC_ADDRESS_TYPE(X) \
+  ((enum loongarch_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
+
+/* True if INSN is a loongarch.md pattern or asm statement.  */
+/* ???	This test exists through the compiler, perhaps it should be
+   moved to rtl.h.  */
+#define USEFUL_INSN_P(INSN)						\
+  (NONDEBUG_INSN_P (INSN)						\
+   && GET_CODE (PATTERN (INSN)) != USE					\
+   && GET_CODE (PATTERN (INSN)) != CLOBBER)
+
+/* True if bit BIT is set in VALUE.  */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
+/* Classifies an address.
+
+   ADDRESS_REG
+       A natural register + offset address.  The register satisfies
+       loongarch_valid_base_register_p and the offset is a const_arith_operand.
+
+   ADDRESS_CONST_INT
+       A signed 16-bit constant address.
+
+   ADDRESS_SYMBOLIC:
+       A constant symbolic address.  */
+enum loongarch_address_type
+{
+  ADDRESS_REG,
+  ADDRESS_CONST_INT,
+  ADDRESS_SYMBOLIC
+};
+
+
+/* Information about an address described by loongarch_address_type.
+
+   ADDRESS_CONST_INT
+       No fields are used.
+
+   ADDRESS_REG
+       REG is the base register and OFFSET is the constant offset.
+
+   ADDRESS_SYMBOLIC
+       SYMBOL_TYPE is the type of symbol that the address references.  */
+struct loongarch_address_info
+{
+  enum loongarch_address_type type;
+  rtx reg;
+  rtx offset;
+  enum loongarch_symbol_type symbol_type;
+};
+
+/* Method of loading instant numbers:
+
+   METHOD_NORMAL:
+     Load 0-31 bit of the immediate number.
+
+   METHOD_LU32I:
+     Load 32-51 bit of the immediate number.
+
+   METHOD_LU52I:
+     Load 52-63 bit of the immediate number.
+
+   METHOD_INSV:
+     immediate like 0xfff00000fffffxxx
+   */
+enum loongarch_load_imm_method
+{
+  METHOD_NORMAL,
+  METHOD_LU32I,
+  METHOD_LU52I,
+  METHOD_INSV
+};
+
+/* One stage in a constant building sequence.  These sequences have
+   the form:
+
+	A = VALUE[0]
+	A = A CODE[1] VALUE[1]
+	A = A CODE[2] VALUE[2]
+	...
+
+   where A is an accumulator, each CODE[i] is a binary rtl operation
+   and each VALUE[i] is a constant integer.  CODE[0] is undefined.  */
+struct loongarch_integer_op
+{
+  enum rtx_code code;
+  unsigned HOST_WIDE_INT value;
+  enum loongarch_load_imm_method method;
+};
+
+/* The largest number of operations needed to load an integer constant.
+   The worst accepted case for 64-bit constants is LU12I.W,LU32I.D,LU52I.D,ORI
+   or LU12I.W,LU32I.D,LU52I.D,ADDI.D DECL_ASSEMBLER_NAME.  */
+#define LARCH_MAX_INTEGER_OPS 4
+
+/* Arrays that map GCC register numbers to debugger register numbers.  */
+int loongarch_dwarf_regno[FIRST_PSEUDO_REGISTER];
+
+/* Index [M][R] is true if register R is allowed to hold a value of mode M.  */
+static bool loongarch_hard_regno_mode_ok_p[MAX_MACHINE_MODE]
+					  [FIRST_PSEUDO_REGISTER];
+
+/* Index C is true if character C is a valid PRINT_OPERAND punctation
+   character.  */
+static bool loongarch_print_operand_punct[256];
+
+/* Cached value of can_issue_more.  This is cached in loongarch_variable_issue
+   hook and returned from loongarch_sched_reorder2.  */
+static int cached_can_issue_more;
+
+/* Index R is the smallest register class that contains register R.  */
+const enum reg_class loongarch_regno_to_class[FIRST_PSEUDO_REGISTER] = {
+    GR_REGS,	     GR_REGS,	      GR_REGS,	       GR_REGS,
+    JIRL_REGS,       JIRL_REGS,       JIRL_REGS,       JIRL_REGS,
+    JIRL_REGS,       JIRL_REGS,       JIRL_REGS,       JIRL_REGS,
+    SIBCALL_REGS,    SIBCALL_REGS,    SIBCALL_REGS,    SIBCALL_REGS,
+    SIBCALL_REGS,    SIBCALL_REGS,    SIBCALL_REGS,    SIBCALL_REGS,
+    SIBCALL_REGS,    GR_REGS,	      GR_REGS,	       JIRL_REGS,
+    JIRL_REGS,       JIRL_REGS,       JIRL_REGS,       JIRL_REGS,
+    JIRL_REGS,       JIRL_REGS,       JIRL_REGS,       JIRL_REGS,
+
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
+    FCC_REGS,	FCC_REGS,	FCC_REGS,	FCC_REGS,
+    FCC_REGS,	FCC_REGS,	FCC_REGS,	FCC_REGS,
+    FRAME_REGS,	FRAME_REGS
+};
+
+/* The value of TARGET_ATTRIBUTE_TABLE.  */
+static const struct attribute_spec loongarch_attribute_table[] = {
+    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+       affects_type_identity, handler, exclude }  */
+      { NULL, 0, 0, false, false, false, false, NULL, NULL }
+};
+
+/* Which cost information to use.  */
+static const struct loongarch_rtx_cost_data *loongarch_cost;
+
+/* Information about a single argument.  */
+struct loongarch_arg_info
+{
+  /* True if the argument is at least partially passed on the stack.  */
+  bool stack_p;
+
+  /* The number of integer registers allocated to this argument.  */
+  unsigned int num_gprs;
+
+  /* The offset of the first register used, provided num_gprs is nonzero.
+     If passed entirely on the stack, the value is MAX_ARGS_IN_REGISTERS.  */
+  unsigned int gpr_offset;
+
+  /* The number of floating-point registers allocated to this argument.  */
+  unsigned int num_fprs;
+
+  /* The offset of the first register used, provided num_fprs is nonzero.  */
+  unsigned int fpr_offset;
+};
+
+/* Implement TARGET_FUNCTION_ARG_BOUNDARY.  Every parameter gets at
+   least PARM_BOUNDARY bits of alignment, but will be given anything up
+   to PREFERRED_STACK_BOUNDARY bits if the type requires it.  */
+
+static unsigned int
+loongarch_function_arg_boundary (machine_mode mode, const_tree type)
+{
+  unsigned int alignment;
+
+  /* Use natural alignment if the type is not aggregate data.  */
+  if (type && !AGGREGATE_TYPE_P (type))
+    alignment = TYPE_ALIGN (TYPE_MAIN_VARIANT (type));
+  else
+    alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
+
+  return MIN (PREFERRED_STACK_BOUNDARY, MAX (PARM_BOUNDARY, alignment));
+}
+
+/* If MODE represents an argument that can be passed or returned in
+   floating-point registers, return the number of registers, else 0.  */
+
+static unsigned
+loongarch_pass_mode_in_fpr_p (machine_mode mode)
+{
+  if (GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FP_ARG)
+    {
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+	return 1;
+
+      if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+	return 2;
+    }
+
+  return 0;
+}
+
+typedef struct
+{
+  const_tree type;
+  HOST_WIDE_INT offset;
+} loongarch_aggregate_field;
+
+/* Identify subfields of aggregates that are candidates for passing in
+   floating-point registers.  */
+
+static int
+loongarch_flatten_aggregate_field (const_tree type,
+				   loongarch_aggregate_field fields[2], int n,
+				   HOST_WIDE_INT offset)
+{
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      /* Can't handle incomplete types nor sizes that are not fixed.  */
+      if (!COMPLETE_TYPE_P (type)
+	  || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+	  || !tree_fits_uhwi_p (TYPE_SIZE (type)))
+	return -1;
+
+      for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f))
+	if (TREE_CODE (f) == FIELD_DECL)
+	  {
+	    if (!TYPE_P (TREE_TYPE (f)))
+	      return -1;
+
+	    HOST_WIDE_INT pos = offset + int_byte_position (f);
+	    n = loongarch_flatten_aggregate_field (TREE_TYPE (f), fields, n,
+						   pos);
+	    if (n < 0)
+	      return -1;
+	  }
+      return n;
+
+    case ARRAY_TYPE:
+      {
+	HOST_WIDE_INT n_elts;
+	loongarch_aggregate_field subfields[2];
+	tree index = TYPE_DOMAIN (type);
+	tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
+	int n_subfields = loongarch_flatten_aggregate_field (TREE_TYPE (type),
+							     subfields, 0,
+							     offset);
+
+	/* Can't handle incomplete types nor sizes that are not fixed.  */
+	if (n_subfields <= 0
+	    || !COMPLETE_TYPE_P (type)
+	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+	    || !index
+	    || !TYPE_MAX_VALUE (index)
+	    || !tree_fits_uhwi_p (TYPE_MAX_VALUE (index))
+	    || !TYPE_MIN_VALUE (index)
+	    || !tree_fits_uhwi_p (TYPE_MIN_VALUE (index))
+	    || !tree_fits_uhwi_p (elt_size))
+	  return -1;
+
+	n_elts = 1 + tree_to_uhwi (TYPE_MAX_VALUE (index))
+		 - tree_to_uhwi (TYPE_MIN_VALUE (index));
+	gcc_assert (n_elts >= 0);
+
+	for (HOST_WIDE_INT i = 0; i < n_elts; i++)
+	  for (int j = 0; j < n_subfields; j++)
+	    {
+	      if (n >= 2)
+		return -1;
+
+	      fields[n] = subfields[j];
+	      fields[n++].offset += i * tree_to_uhwi (elt_size);
+	    }
+
+	return n;
+      }
+
+    case COMPLEX_TYPE:
+      {
+	/* Complex type need consume 2 field, so n must be 0.  */
+	if (n != 0)
+	  return -1;
+
+	HOST_WIDE_INT elt_size = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (type)));
+
+	if (elt_size <= UNITS_PER_FP_ARG)
+	  {
+	    fields[0].type = TREE_TYPE (type);
+	    fields[0].offset = offset;
+	    fields[1].type = TREE_TYPE (type);
+	    fields[1].offset = offset + elt_size;
+
+	    return 2;
+	  }
+
+	return -1;
+      }
+
+    default:
+      if (n < 2
+	  && ((SCALAR_FLOAT_TYPE_P (type)
+	       && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FP_ARG)
+	      || (INTEGRAL_TYPE_P (type)
+		  && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_WORD)))
+	{
+	  fields[n].type = type;
+	  fields[n].offset = offset;
+	  return n + 1;
+	}
+      else
+	return -1;
+    }
+}
+
+/* Identify candidate aggregates for passing in floating-point registers.
+   Candidates have at most two fields after flattening.  */
+
+static int
+loongarch_flatten_aggregate_argument (const_tree type,
+				      loongarch_aggregate_field fields[2])
+{
+  if (!type || TREE_CODE (type) != RECORD_TYPE)
+    return -1;
+
+  return loongarch_flatten_aggregate_field (type, fields, 0, 0);
+}
+
+/* See whether TYPE is a record whose fields should be returned in one or
+   two floating-point registers.  If so, populate FIELDS accordingly.  */
+
+static unsigned
+loongarch_pass_aggregate_in_fpr_pair_p (const_tree type,
+					loongarch_aggregate_field fields[2])
+{
+  int n = loongarch_flatten_aggregate_argument (type, fields);
+
+  for (int i = 0; i < n; i++)
+    if (!SCALAR_FLOAT_TYPE_P (fields[i].type))
+      return 0;
+
+  return n > 0 ? n : 0;
+}
+
+/* See whether TYPE is a record whose fields should be returned in one or
+   floating-point register and one integer register.  If so, populate
+   FIELDS accordingly.  */
+
+static bool
+loongarch_pass_aggregate_in_fpr_and_gpr_p (const_tree type,
+					   loongarch_aggregate_field fields[2])
+{
+  unsigned num_int = 0, num_float = 0;
+  int n = loongarch_flatten_aggregate_argument (type, fields);
+
+  for (int i = 0; i < n; i++)
+    {
+      num_float += SCALAR_FLOAT_TYPE_P (fields[i].type);
+      num_int += INTEGRAL_TYPE_P (fields[i].type);
+    }
+
+  return num_int == 1 && num_float == 1;
+}
+
+/* Return the representation of an argument passed or returned in an FPR
+   when the value has mode VALUE_MODE and the type has TYPE_MODE.  The
+   two modes may be different for structures like:
+
+   struct __attribute__((packed)) foo { float f; }
+
+   where the SFmode value "f" is passed in REGNO but the struct itself
+   has mode BLKmode.  */
+
+static rtx
+loongarch_pass_fpr_single (machine_mode type_mode, unsigned regno,
+			   machine_mode value_mode)
+{
+  rtx x = gen_rtx_REG (value_mode, regno);
+
+  if (type_mode != value_mode)
+    {
+      x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+      x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+    }
+  return x;
+}
+
+/* Pass or return a composite value in the FPR pair REGNO and REGNO + 1.
+   MODE is the mode of the composite.  MODE1 and OFFSET1 are the mode and
+   byte offset for the first value, likewise MODE2 and OFFSET2 for the
+   second value.  */
+
+static rtx
+loongarch_pass_fpr_pair (machine_mode mode, unsigned regno1,
+			 machine_mode mode1, HOST_WIDE_INT offset1,
+			 unsigned regno2, machine_mode mode2,
+			 HOST_WIDE_INT offset2)
+{
+  return gen_rtx_PARALLEL (
+    mode, gen_rtvec (2,
+		     gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode1, regno1),
+					GEN_INT (offset1)),
+		     gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode2, regno2),
+					GEN_INT (offset2))));
+}
+
+/* Fill INFO with information about a single argument, and return an
+   RTL pattern to pass or return the argument.  CUM is the cumulative
+   state for earlier arguments.  MODE is the mode of this argument and
+   TYPE is its type (if known).  NAMED is true if this is a named
+   (fixed) argument rather than a variable one.  RETURN_P is true if
+   returning the argument, or false if passing the argument.  */
+
+static rtx
+loongarch_get_arg_info (struct loongarch_arg_info *info,
+			const CUMULATIVE_ARGS *cum, machine_mode mode,
+			const_tree type, bool named, bool return_p)
+{
+  unsigned num_bytes, num_words;
+  unsigned fpr_base = return_p ? FP_RETURN : FP_ARG_FIRST;
+  unsigned gpr_base = return_p ? GP_RETURN : GP_ARG_FIRST;
+  unsigned alignment = loongarch_function_arg_boundary (mode, type);
+
+  memset (info, 0, sizeof (*info));
+  info->gpr_offset = cum->num_gprs;
+  info->fpr_offset = cum->num_fprs;
+
+  if (named)
+    {
+      loongarch_aggregate_field fields[2];
+      unsigned fregno = fpr_base + info->fpr_offset;
+      unsigned gregno = gpr_base + info->gpr_offset;
+
+      /* Pass one- or two-element floating-point aggregates in FPRs.  */
+      if ((info->num_fprs
+	   = loongarch_pass_aggregate_in_fpr_pair_p (type, fields))
+	  && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS)
+	switch (info->num_fprs)
+	  {
+	  case 1:
+	    return loongarch_pass_fpr_single (mode, fregno,
+					      TYPE_MODE (fields[0].type));
+
+	  case 2:
+	    return loongarch_pass_fpr_pair (mode, fregno,
+					    TYPE_MODE (fields[0].type),
+					    fields[0].offset,
+					    fregno + 1,
+					    TYPE_MODE (fields[1].type),
+					    fields[1].offset);
+
+	  default:
+	    gcc_unreachable ();
+	  }
+
+      /* Pass real and complex floating-point numbers in FPRs.  */
+      if ((info->num_fprs = loongarch_pass_mode_in_fpr_p (mode))
+	  && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS)
+	switch (GET_MODE_CLASS (mode))
+	  {
+	  case MODE_FLOAT:
+	    return gen_rtx_REG (mode, fregno);
+
+	  case MODE_COMPLEX_FLOAT:
+	    return loongarch_pass_fpr_pair (mode, fregno,
+					    GET_MODE_INNER (mode), 0,
+					    fregno + 1, GET_MODE_INNER (mode),
+					    GET_MODE_UNIT_SIZE (mode));
+
+	  default:
+	    gcc_unreachable ();
+	  }
+
+      /* Pass structs with one float and one integer in an FPR and a GPR.  */
+      if (loongarch_pass_aggregate_in_fpr_and_gpr_p (type, fields)
+	  && info->gpr_offset < MAX_ARGS_IN_REGISTERS
+	  && info->fpr_offset < MAX_ARGS_IN_REGISTERS)
+	{
+	  info->num_gprs = 1;
+	  info->num_fprs = 1;
+
+	  if (!SCALAR_FLOAT_TYPE_P (fields[0].type))
+	    std::swap (fregno, gregno);
+
+	  return loongarch_pass_fpr_pair (mode, fregno,
+					  TYPE_MODE (fields[0].type),
+					  fields[0].offset, gregno,
+					  TYPE_MODE (fields[1].type),
+					  fields[1].offset);
+	}
+    }
+
+  /* Work out the size of the argument.  */
+  num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+  num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+  /* Doubleword-aligned varargs start on an even register boundary.  */
+  if (!named && num_bytes != 0 && alignment > BITS_PER_WORD)
+    info->gpr_offset += info->gpr_offset & 1;
+
+  /* Partition the argument between registers and stack.  */
+  info->num_fprs = 0;
+  info->num_gprs = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset);
+  info->stack_p = (num_words - info->num_gprs) != 0;
+
+  if (info->num_gprs || return_p)
+    return gen_rtx_REG (mode, gpr_base + info->gpr_offset);
+
+  return NULL_RTX;
+}
+
+/* Implement TARGET_FUNCTION_ARG.  */
+
+static rtx
+loongarch_function_arg (cumulative_args_t cum_v, const function_arg_info &arg)
+{
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+  struct loongarch_arg_info info;
+
+  if (arg.end_marker_p ())
+    return NULL;
+
+  return loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named,
+				 false);
+}
+
+/* Implement TARGET_FUNCTION_ARG_ADVANCE.  */
+
+static void
+loongarch_function_arg_advance (cumulative_args_t cum_v,
+				const function_arg_info &arg)
+{
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+  struct loongarch_arg_info info;
+
+  loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false);
+
+  /* Advance the register count.  This has the effect of setting
+     num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
+     argument required us to skip the final GPR and pass the whole
+     argument on the stack.  */
+  cum->num_fprs = info.fpr_offset + info.num_fprs;
+  cum->num_gprs = info.gpr_offset + info.num_gprs;
+}
+
+/* Implement TARGET_ARG_PARTIAL_BYTES.  */
+
+static int
+loongarch_arg_partial_bytes (cumulative_args_t cum,
+			     const function_arg_info &generic_arg)
+{
+  struct loongarch_arg_info arg;
+
+  loongarch_get_arg_info (&arg, get_cumulative_args (cum), generic_arg.mode,
+			  generic_arg.type, generic_arg.named, false);
+  return arg.stack_p ? arg.num_gprs * UNITS_PER_WORD : 0;
+}
+
+/* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
+   VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
+   VALTYPE is null and MODE is the mode of the return value.  */
+
+static rtx
+loongarch_function_value_1 (const_tree type, const_tree func,
+			    machine_mode mode)
+{
+  struct loongarch_arg_info info;
+  CUMULATIVE_ARGS args;
+
+  if (type)
+    {
+      int unsigned_p = TYPE_UNSIGNED (type);
+
+      mode = TYPE_MODE (type);
+
+      /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes,
+	 return values, promote the mode here too.  */
+      mode = promote_function_mode (type, mode, &unsigned_p, func, 1);
+    }
+
+  memset (&args, 0, sizeof (args));
+  return loongarch_get_arg_info (&info, &args, mode, type, true, true);
+}
+
+
+/* Implement TARGET_FUNCTION_VALUE.  */
+
+static rtx
+loongarch_function_value (const_tree valtype, const_tree fn_decl_or_type,
+			  bool outgoing ATTRIBUTE_UNUSED)
+{
+  return loongarch_function_value_1 (valtype, fn_decl_or_type, VOIDmode);
+}
+
+/* Implement TARGET_LIBCALL_VALUE.  */
+
+static rtx
+loongarch_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+  return loongarch_function_value_1 (NULL_TREE, NULL_TREE, mode);
+}
+
+
+/* Implement TARGET_PASS_BY_REFERENCE.  */
+
+static bool
+loongarch_pass_by_reference (cumulative_args_t cum_v,
+			     const function_arg_info &arg)
+{
+  HOST_WIDE_INT size = arg.type_size_in_bytes ();
+  struct loongarch_arg_info info;
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+  /* ??? std_gimplify_va_arg_expr passes NULL for cum.  Fortunately, we
+     never pass variadic arguments in floating-point registers, so we can
+     avoid the call to loongarch_get_arg_info in this case.  */
+  if (cum != NULL)
+    {
+      /* Don't pass by reference if we can use a floating-point register.  */
+      loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named,
+			      false);
+      if (info.num_fprs)
+	return false;
+    }
+
+  /* Pass by reference if the data do not fit in two integer registers.  */
+  return !IN_RANGE (size, 0, 2 * UNITS_PER_WORD);
+}
+
+/* Implement TARGET_RETURN_IN_MEMORY.  */
+
+static bool
+loongarch_return_in_memory (const_tree type,
+			    const_tree fndecl ATTRIBUTE_UNUSED)
+{
+  CUMULATIVE_ARGS args;
+  cumulative_args_t cum = pack_cumulative_args (&args);
+
+  /* The rules for returning in memory are the same as for passing the
+     first named argument by reference.  */
+  memset (&args, 0, sizeof (args));
+  function_arg_info arg (const_cast<tree> (type), /*named=*/true);
+  return loongarch_pass_by_reference (cum, arg);
+}
+
+/* Implement TARGET_SETUP_INCOMING_VARARGS.  */
+
+static void
+loongarch_setup_incoming_varargs (cumulative_args_t cum,
+				  const function_arg_info &arg,
+				  int *pretend_size ATTRIBUTE_UNUSED,
+				  int no_rtl)
+{
+  CUMULATIVE_ARGS local_cum;
+  int gp_saved;
+
+  /* The caller has advanced CUM up to, but not beyond, the last named
+     argument.  Advance a local copy of CUM past the last "real" named
+     argument, to find out how many registers are left over.  */
+  local_cum = *get_cumulative_args (cum);
+  loongarch_function_arg_advance (pack_cumulative_args (&local_cum), arg);
+
+  /* Found out how many registers we need to save.  */
+  gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
+
+  if (!no_rtl && gp_saved > 0)
+    {
+      rtx ptr = plus_constant (Pmode, virtual_incoming_args_rtx,
+			       REG_PARM_STACK_SPACE (cfun->decl)
+				 - gp_saved * UNITS_PER_WORD);
+      rtx mem = gen_frame_mem (BLKmode, ptr);
+      set_mem_alias_set (mem, get_varargs_alias_set ());
+
+      move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, mem, gp_saved);
+    }
+  if (REG_PARM_STACK_SPACE (cfun->decl) == 0)
+    cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD;
+}
+
+/* Make the last instruction frame-related and note that it performs
+   the operation described by FRAME_PATTERN.  */
+
+static void
+loongarch_set_frame_expr (rtx frame_pattern)
+{
+  rtx insn;
+
+  insn = get_last_insn ();
+  RTX_FRAME_RELATED_P (insn) = 1;
+  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, frame_pattern,
+				      REG_NOTES (insn));
+}
+
+/* Return a frame-related rtx that stores REG at MEM.
+   REG must be a single register.  */
+
+static rtx
+loongarch_frame_set (rtx mem, rtx reg)
+{
+  rtx set = gen_rtx_SET (mem, reg);
+  RTX_FRAME_RELATED_P (set) = 1;
+  return set;
+}
+
+/* Return true if the current function must save register REGNO.  */
+
+static bool
+loongarch_save_reg_p (unsigned int regno)
+{
+  bool call_saved = !global_regs[regno] && !call_used_regs[regno];
+  bool might_clobber
+    = crtl->saves_all_registers || df_regs_ever_live_p (regno);
+
+  if (call_saved && might_clobber)
+    return true;
+
+  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+    return true;
+
+  if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return)
+    return true;
+
+  return false;
+}
+
+/* Determine which GPR save/restore routine to call.  */
+
+static unsigned
+loongarch_save_libcall_count (unsigned mask)
+{
+  for (unsigned n = GP_REG_LAST; n > GP_REG_FIRST; n--)
+    if (BITSET_P (mask, n))
+      return CALLEE_SAVED_REG_NUMBER (n) + 1;
+  abort ();
+}
+
+/* Populate the current function's loongarch_frame_info structure.
+
+   LoongArch stack frames grown downward.  High addresses are at the top.
+
+     +-------------------------------+
+     |				     |
+     |  incoming stack arguments     |
+     |				     |
+     +-------------------------------+ <-- incoming stack pointer
+     |				     |
+     |  callee-allocated save area   |
+     |  for arguments that are       |
+     |  split between registers and  |
+     |  the stack		     |
+     |				     |
+     +-------------------------------+ <-- arg_pointer_rtx (virtual)
+     |				     |
+     |  callee-allocated save area   |
+     |  for register varargs	     |
+     |				     |
+     +-------------------------------+ <-- hard_frame_pointer_rtx;
+     |				     |     stack_pointer_rtx + gp_sp_offset
+     |  GPR save area		     |       + UNITS_PER_WORD
+     |				     |
+     +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset
+     |				     |       + UNITS_PER_HWVALUE
+     |  FPR save area		     |
+     |				     |
+     +-------------------------------+ <-- frame_pointer_rtx (virtual)
+     |				     |
+     |  local variables		     |
+     |				     |
+   P +-------------------------------+
+     |				     |
+     |  outgoing stack arguments     |
+     |				     |
+     +-------------------------------+ <-- stack_pointer_rtx
+
+   Dynamic stack allocations such as alloca insert data at point P.
+   They decrease stack_pointer_rtx but leave frame_pointer_rtx and
+   hard_frame_pointer_rtx unchanged.  */
+
+static void
+loongarch_compute_frame_info (void)
+{
+  struct loongarch_frame_info *frame;
+  HOST_WIDE_INT offset;
+  unsigned int regno, i, num_x_saved = 0, num_f_saved = 0;
+
+  frame = &cfun->machine->frame;
+  memset (frame, 0, sizeof (*frame));
+
+  /* Find out which GPRs we need to save.  */
+  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    if (loongarch_save_reg_p (regno))
+      frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
+
+  /* If this function calls eh_return, we must also save and restore the
+     EH data registers.  */
+  if (crtl->calls_eh_return)
+    for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++)
+      frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
+
+  /* Find out which FPRs we need to save.  This loop must iterate over
+     the same space as its companion in loongarch_for_each_saved_reg.  */
+  if (TARGET_HARD_FLOAT)
+    for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+      if (loongarch_save_reg_p (regno))
+	frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++;
+
+  /* At the bottom of the frame are any outgoing stack arguments.  */
+  offset = LARCH_STACK_ALIGN (crtl->outgoing_args_size);
+  /* Next are local stack variables.  */
+  offset += LARCH_STACK_ALIGN (get_frame_size ());
+  /* The virtual frame pointer points above the local variables.  */
+  frame->frame_pointer_offset = offset;
+  /* Next are the callee-saved FPRs.  */
+  if (frame->fmask)
+    offset += LARCH_STACK_ALIGN (num_f_saved * UNITS_PER_FP_REG);
+  frame->fp_sp_offset = offset - UNITS_PER_FP_REG;
+  /* Next are the callee-saved GPRs.  */
+  if (frame->mask)
+    {
+      unsigned x_save_size = LARCH_STACK_ALIGN (num_x_saved * UNITS_PER_WORD);
+      unsigned num_save_restore
+	= 1 + loongarch_save_libcall_count (frame->mask);
+
+      /* Only use save/restore routines if they don't alter the stack size.  */
+      if (LARCH_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) == x_save_size)
+	frame->save_libcall_adjustment = x_save_size;
+
+      offset += x_save_size;
+    }
+  frame->gp_sp_offset = offset - UNITS_PER_WORD;
+  /* The hard frame pointer points above the callee-saved GPRs.  */
+  frame->hard_frame_pointer_offset = offset;
+  /* Above the hard frame pointer is the callee-allocated varags save area.  */
+  offset += LARCH_STACK_ALIGN (cfun->machine->varargs_size);
+  /* Next is the callee-allocated area for pretend stack arguments.  */
+  offset += LARCH_STACK_ALIGN (crtl->args.pretend_args_size);
+  /* Arg pointer must be below pretend args, but must be above alignment
+     padding.  */
+  frame->arg_pointer_offset = offset - crtl->args.pretend_args_size;
+  frame->total_size = offset;
+  /* Next points the incoming stack pointer and any incoming arguments.  */
+
+  /* Only use save/restore routines when the GPRs are atop the frame.  */
+  if (frame->hard_frame_pointer_offset != frame->total_size)
+    frame->save_libcall_adjustment = 0;
+}
+
+/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame pointer
+   or argument pointer.  TO is either the stack pointer or hard frame
+   pointer.  */
+
+HOST_WIDE_INT
+loongarch_initial_elimination_offset (int from, int to)
+{
+  HOST_WIDE_INT src, dest;
+
+  loongarch_compute_frame_info ();
+
+  if (to == HARD_FRAME_POINTER_REGNUM)
+    dest = cfun->machine->frame.hard_frame_pointer_offset;
+  else if (to == STACK_POINTER_REGNUM)
+    dest = 0; /* The stack pointer is the base of all offsets, hence 0.  */
+  else
+    gcc_unreachable ();
+
+  if (from == FRAME_POINTER_REGNUM)
+    src = cfun->machine->frame.frame_pointer_offset;
+  else if (from == ARG_POINTER_REGNUM)
+    src = cfun->machine->frame.arg_pointer_offset;
+  else
+    gcc_unreachable ();
+
+  return src - dest;
+}
+
+/* A function to save or store a register.  The first argument is the
+   register and the second is the stack slot.  */
+typedef void (*loongarch_save_restore_fn) (rtx, rtx);
+
+/* Use FN to save or restore register REGNO.  MODE is the register's
+   mode and OFFSET is the offset of its save slot from the current
+   stack pointer.  */
+
+static void
+loongarch_save_restore_reg (machine_mode mode, int regno, HOST_WIDE_INT offset,
+			    loongarch_save_restore_fn fn)
+{
+  rtx mem;
+
+  mem = gen_frame_mem (mode, plus_constant (Pmode, stack_pointer_rtx, offset));
+  fn (gen_rtx_REG (mode, regno), mem);
+}
+
+/* Call FN for each register that is saved by the current function.
+   SP_OFFSET is the offset of the current stack pointer from the start
+   of the frame.  */
+
+static void
+loongarch_for_each_saved_reg (HOST_WIDE_INT sp_offset,
+			      loongarch_save_restore_fn fn)
+{
+  HOST_WIDE_INT offset;
+
+  /* Save the link register and s-registers.  */
+  offset = cfun->machine->frame.gp_sp_offset - sp_offset;
+  for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
+      {
+	loongarch_save_restore_reg (word_mode, regno, offset, fn);
+	offset -= UNITS_PER_WORD;
+      }
+
+  /* This loop must iterate over the same space as its companion in
+     loongarch_compute_frame_info.  */
+  offset = cfun->machine->frame.fp_sp_offset - sp_offset;
+  for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+    if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+      {
+	machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode;
+
+	loongarch_save_restore_reg (mode, regno, offset, fn);
+	offset -= GET_MODE_SIZE (mode);
+      }
+}
+
+/* Emit a move from SRC to DEST.  Assume that the move expanders can
+   handle all moves if !can_create_pseudo_p ().  The distinction is
+   important because, unlike emit_move_insn, the move expanders know
+   how to force Pmode objects into the constant pool even when the
+   constant pool address is not itself legitimate.  */
+
+rtx
+loongarch_emit_move (rtx dest, rtx src)
+{
+  return (can_create_pseudo_p () ? emit_move_insn (dest, src)
+				 : emit_move_insn_1 (dest, src));
+}
+
+/* Save register REG to MEM.  Make the instruction frame-related.  */
+
+static void
+loongarch_save_reg (rtx reg, rtx mem)
+{
+  loongarch_emit_move (mem, reg);
+  loongarch_set_frame_expr (loongarch_frame_set (mem, reg));
+}
+
+/* Restore register REG from MEM.  */
+
+static void
+loongarch_restore_reg (rtx reg, rtx mem)
+{
+  rtx insn = loongarch_emit_move (reg, mem);
+  rtx dwarf = NULL_RTX;
+  dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+  REG_NOTES (insn) = dwarf;
+
+  RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* For stack frames that can't be allocated with a single ADDI instruction,
+   compute the best value to initially allocate.  It must at a minimum
+   allocate enough space to spill the callee-saved registers.  */
+
+static HOST_WIDE_INT
+loongarch_first_stack_step (struct loongarch_frame_info *frame)
+{
+  if (IMM12_OPERAND (frame->total_size))
+    return frame->total_size;
+
+  HOST_WIDE_INT min_first_step
+    = LARCH_STACK_ALIGN (frame->total_size - frame->fp_sp_offset);
+  HOST_WIDE_INT max_first_step = IMM_REACH / 2 - PREFERRED_STACK_BOUNDARY / 8;
+  HOST_WIDE_INT min_second_step = frame->total_size - max_first_step;
+  gcc_assert (min_first_step <= max_first_step);
+
+  /* As an optimization, use the least-significant bits of the total frame
+     size, so that the second adjustment step is just LU12I + ADD.  */
+  if (!IMM12_OPERAND (min_second_step)
+      && frame->total_size % IMM_REACH < IMM_REACH / 2
+      && frame->total_size % IMM_REACH >= min_first_step)
+    return frame->total_size % IMM_REACH;
+
+  return max_first_step;
+}
+
+static void
+loongarch_emit_stack_tie (void)
+{
+  if (Pmode == SImode)
+    emit_insn (gen_stack_tiesi (stack_pointer_rtx, hard_frame_pointer_rtx));
+  else
+    emit_insn (gen_stack_tiedi (stack_pointer_rtx, hard_frame_pointer_rtx));
+}
+
+#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP)
+
+#if PROBE_INTERVAL > 16384
+#error Cannot use indexed addressing mode for stack probing
+#endif
+
+/* Emit code to probe a range of stack addresses from FIRST to FIRST+SIZE,
+   inclusive.  These are offsets from the current stack pointer.  */
+
+static void
+loongarch_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
+{
+  /* See if we have a constant small number of probes to generate.  If so,
+     that's the easy case.  */
+  if ((TARGET_64BIT && (first + size <= 32768))
+      || (!TARGET_64BIT && (first + size <= 2048)))
+    {
+      HOST_WIDE_INT i;
+
+      /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until
+	 it exceeds SIZE.  If only one probe is needed, this will not
+	 generate any code.  Then probe at FIRST + SIZE.  */
+      for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
+	emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+					 -(first + i)));
+
+      emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+				       -(first + size)));
+    }
+
+  /* Otherwise, do the same as above, but in a loop.  Note that we must be
+     extra careful with variables wrapping around because we might be at
+     the very top (or the very bottom) of the address space and we have
+     to be able to handle this case properly; in particular, we use an
+     equality test for the loop condition.  */
+  else
+    {
+      HOST_WIDE_INT rounded_size;
+      rtx r13 = LARCH_PROLOGUE_TEMP (Pmode);
+      rtx r12 = LARCH_PROLOGUE_TEMP2 (Pmode);
+      rtx r14 = LARCH_PROLOGUE_TEMP3 (Pmode);
+
+      /* Sanity check for the addressing mode we're going to use.  */
+      gcc_assert (first <= 16384);
+
+
+      /* Step 1: round SIZE to the previous multiple of the interval.  */
+
+      rounded_size = ROUND_DOWN (size, PROBE_INTERVAL);
+
+      /* TEST_ADDR = SP + FIRST */
+      if (first != 0)
+	{
+	  emit_move_insn (r14, GEN_INT (first));
+	  emit_insn (gen_rtx_SET (r13, gen_rtx_MINUS (Pmode,
+						      stack_pointer_rtx,
+						      r14)));
+	}
+      else
+	emit_move_insn (r13, stack_pointer_rtx);
+
+      /* Step 2: compute initial and final value of the loop counter.  */
+
+      emit_move_insn (r14, GEN_INT (PROBE_INTERVAL));
+      /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE.  */
+      if (rounded_size == 0)
+	emit_move_insn (r12, r13);
+      else
+	{
+	  emit_move_insn (r12, GEN_INT (rounded_size));
+	  emit_insn (gen_rtx_SET (r12, gen_rtx_MINUS (Pmode, r13, r12)));
+	  /* Step 3: the loop
+
+	     do
+	     {
+	     TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
+	     probe at TEST_ADDR
+	     }
+	     while (TEST_ADDR != LAST_ADDR)
+
+	     probes at FIRST + N * PROBE_INTERVAL for values of N from 1
+	     until it is equal to ROUNDED_SIZE.  */
+
+	  emit_insn (PMODE_INSN (gen_probe_stack_range, (r13, r13, r12, r14)));
+	}
+
+      /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
+	 that SIZE is equal to ROUNDED_SIZE.  */
+
+      if (size != rounded_size)
+	{
+	  if (TARGET_64BIT)
+	    emit_stack_probe (plus_constant (Pmode, r12, rounded_size - size));
+	  else
+	    {
+	      HOST_WIDE_INT i;
+	      for (i = 2048; i < (size - rounded_size); i += 2048)
+		{
+		  emit_stack_probe (plus_constant (Pmode, r12, -i));
+		  emit_insn (gen_rtx_SET (r12,
+					  plus_constant (Pmode, r12, -2048)));
+		}
+	      rtx r1 = plus_constant (Pmode, r12,
+				      -(size - rounded_size - i + 2048));
+	      emit_stack_probe (r1);
+	    }
+	}
+    }
+
+  /* Make sure nothing is scheduled before we are done.  */
+  emit_insn (gen_blockage ());
+}
+
+/* Probe a range of stack addresses from REG1 to REG2 inclusive.  These are
+   absolute addresses.  */
+const char *
+loongarch_output_probe_stack_range (rtx reg1, rtx reg2, rtx reg3)
+{
+  static int labelno = 0;
+  char loop_lab[32], tmp[64];
+  rtx xops[3];
+
+  ASM_GENERATE_INTERNAL_LABEL (loop_lab, "LPSRL", labelno++);
+
+  /* Loop.  */
+  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, loop_lab);
+
+  /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL.  */
+  xops[0] = reg1;
+  xops[1] = GEN_INT (-PROBE_INTERVAL);
+  xops[2] = reg3;
+  if (TARGET_64BIT)
+    output_asm_insn ("sub.d\t%0,%0,%2", xops);
+  else
+    output_asm_insn ("sub.w\t%0,%0,%2", xops);
+
+  /* Probe at TEST_ADDR, test if TEST_ADDR == LAST_ADDR and branch.  */
+  xops[1] = reg2;
+  strcpy (tmp, "bne\t%0,%1,");
+  if (TARGET_64BIT)
+    output_asm_insn ("st.d\t$r0,%0,0", xops);
+  else
+    output_asm_insn ("st.w\t$r0,%0,0", xops);
+  output_asm_insn (strcat (tmp, &loop_lab[1]), xops);
+
+  return "";
+}
+
+/* Expand the "prologue" pattern.  */
+
+void
+loongarch_expand_prologue (void)
+{
+  struct loongarch_frame_info *frame = &cfun->machine->frame;
+  HOST_WIDE_INT size = frame->total_size;
+  HOST_WIDE_INT tmp;
+  unsigned mask = frame->mask;
+  rtx insn;
+
+  if (flag_stack_usage_info)
+    current_function_static_stack_size = size;
+
+  if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+      || flag_stack_clash_protection)
+    {
+      if (crtl->is_leaf && !cfun->calls_alloca)
+	{
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    {
+	      tmp = size - get_stack_check_protect ();
+	      loongarch_emit_probe_stack_range (get_stack_check_protect (),
+						tmp);
+	    }
+	}
+      else if (size > 0)
+	loongarch_emit_probe_stack_range (get_stack_check_protect (), size);
+    }
+
+  /* Save the registers.  */
+  if ((frame->mask | frame->fmask) != 0)
+    {
+      HOST_WIDE_INT step1 = MIN (size, loongarch_first_stack_step (frame));
+
+      insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+			    GEN_INT (-step1));
+      RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+      size -= step1;
+      loongarch_for_each_saved_reg (size, loongarch_save_reg);
+    }
+
+  frame->mask = mask; /* Undo the above fib.  */
+
+  /* Set up the frame pointer, if we're using one.  */
+  if (frame_pointer_needed)
+    {
+      insn = gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx,
+			    GEN_INT (frame->hard_frame_pointer_offset - size));
+      RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+
+      loongarch_emit_stack_tie ();
+    }
+
+  /* Allocate the rest of the frame.  */
+  if (size > 0)
+    {
+      if (IMM12_OPERAND (-size))
+	{
+	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+				GEN_INT (-size));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	}
+      else
+	{
+	  loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), GEN_INT (-size));
+	  emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+				    LARCH_PROLOGUE_TEMP (Pmode)));
+
+	  /* Describe the effect of the previous instructions.  */
+	  insn = plus_constant (Pmode, stack_pointer_rtx, -size);
+	  insn = gen_rtx_SET (stack_pointer_rtx, insn);
+	  loongarch_set_frame_expr (insn);
+	}
+    }
+}
+
+/* Return nonzero if this function is known to have a null epilogue.
+   This allows the optimizer to omit jumps to jumps if no stack
+   was created.  */
+
+bool
+loongarch_can_use_return_insn (void)
+{
+  return reload_completed && cfun->machine->frame.total_size == 0;
+}
+
+/* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P
+   says which.  */
+
+void
+loongarch_expand_epilogue (bool sibcall_p)
+{
+  /* Split the frame into two.  STEP1 is the amount of stack we should
+     deallocate before restoring the registers.  STEP2 is the amount we
+     should deallocate afterwards.
+
+     Start off by assuming that no registers need to be restored.  */
+  struct loongarch_frame_info *frame = &cfun->machine->frame;
+  HOST_WIDE_INT step1 = frame->total_size;
+  HOST_WIDE_INT step2 = 0;
+  rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
+  rtx insn;
+
+  /* We need to add memory barrier to prevent read from deallocated stack.  */
+  bool need_barrier_p
+    = (get_frame_size () + cfun->machine->frame.arg_pointer_offset) != 0;
+
+  if (!sibcall_p && loongarch_can_use_return_insn ())
+    {
+      emit_jump_insn (gen_return ());
+      return;
+    }
+
+  /* Move past any dynamic stack allocations.  */
+  if (cfun->calls_alloca)
+    {
+      /* Emit a barrier to prevent loads from a deallocated stack.  */
+      loongarch_emit_stack_tie ();
+      need_barrier_p = false;
+
+      rtx adjust = GEN_INT (-frame->hard_frame_pointer_offset);
+      if (!IMM12_OPERAND (INTVAL (adjust)))
+	{
+	  loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), adjust);
+	  adjust = LARCH_PROLOGUE_TEMP (Pmode);
+	}
+
+      insn = emit_insn (gen_add3_insn (stack_pointer_rtx,
+				       hard_frame_pointer_rtx,
+				       adjust));
+
+      rtx dwarf = NULL_RTX;
+      rtx minus_offset = NULL_RTX;
+      minus_offset = GEN_INT (-frame->hard_frame_pointer_offset);
+      rtx cfa_adjust_value = gen_rtx_PLUS (Pmode,
+					   hard_frame_pointer_rtx,
+					   minus_offset);
+
+      rtx cfa_adjust_rtx = gen_rtx_SET (stack_pointer_rtx, cfa_adjust_value);
+      dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, cfa_adjust_rtx, dwarf);
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      REG_NOTES (insn) = dwarf;
+    }
+
+  /* If we need to restore registers, deallocate as much stack as
+     possible in the second step without going out of range.  */
+  if ((frame->mask | frame->fmask) != 0)
+    {
+      step2 = loongarch_first_stack_step (frame);
+      step1 -= step2;
+    }
+
+  /* Set TARGET to BASE + STEP1.  */
+  if (step1 > 0)
+    {
+      /* Emit a barrier to prevent loads from a deallocated stack.  */
+      loongarch_emit_stack_tie ();
+      need_barrier_p = false;
+
+      /* Get an rtx for STEP1 that we can add to BASE.  */
+      rtx adjust = GEN_INT (step1);
+      if (!IMM12_OPERAND (step1))
+	{
+	  loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), adjust);
+	  adjust = LARCH_PROLOGUE_TEMP (Pmode);
+	}
+
+      insn = emit_insn (gen_add3_insn (stack_pointer_rtx,
+				       stack_pointer_rtx,
+				       adjust));
+
+      rtx dwarf = NULL_RTX;
+      rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+					 GEN_INT (step2));
+
+      dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      REG_NOTES (insn) = dwarf;
+    }
+
+  /* Restore the registers.  */
+  loongarch_for_each_saved_reg (frame->total_size - step2,
+				loongarch_restore_reg);
+
+  if (need_barrier_p)
+    loongarch_emit_stack_tie ();
+
+  /* Deallocate the final bit of the frame.  */
+  if (step2 > 0)
+    {
+      insn = emit_insn (gen_add3_insn (stack_pointer_rtx,
+				       stack_pointer_rtx,
+				       GEN_INT (step2)));
+
+      rtx dwarf = NULL_RTX;
+      rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, const0_rtx);
+      dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      REG_NOTES (insn) = dwarf;
+    }
+
+  /* Add in the __builtin_eh_return stack adjustment.  */
+  if (crtl->calls_eh_return)
+    emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+			      EH_RETURN_STACKADJ_RTX));
+
+  if (!sibcall_p)
+    emit_jump_insn (gen_simple_return_internal (ra));
+}
+
+#define LU32I_B (0xfffffUL << 32)
+#define LU52I_B (0xfffUL << 52)
+
+static unsigned int
+loongarch_build_integer (struct loongarch_integer_op *codes,
+			 HOST_WIDE_INT value)
+
+{
+  unsigned int cost = 0;
+
+  HOST_WIDE_INT low_part = TARGET_64BIT ? value << 32 >> 32 : value;
+
+  if (IMM12_OPERAND (low_part) || IMM12_OPERAND_UNSIGNED (low_part))
+    {
+      codes[0].code = UNKNOWN;
+      codes[0].method = METHOD_NORMAL;
+      codes[0].value = low_part;
+      cost++;
+    }
+  else
+    {
+      codes[0].code = UNKNOWN;
+      codes[0].method = METHOD_NORMAL;
+      codes[0].value = low_part & ~(IMM_REACH - 1);
+      cost++;
+      HOST_WIDE_INT iorv = low_part & (IMM_REACH - 1);
+      if (iorv != 0)
+	{
+	  codes[1].code = IOR;
+	  codes[1].method = METHOD_NORMAL;
+	  codes[1].value = iorv;
+	  cost++;
+	}
+    }
+
+  if (TARGET_64BIT)
+    {
+      bool lu32i[2] = {(value & LU32I_B) == 0, (value & LU32I_B) == LU32I_B};
+      bool lu52i[2] = {(value & LU52I_B) == 0, (value & LU52I_B) == LU52I_B};
+
+      int sign31 = (value & (1UL << 31)) >> 31;
+      if (lu32i[sign31] && lu52i[sign31])
+	return cost;
+      else if (lu32i[sign31])
+	{
+	  codes[cost].method = METHOD_LU52I;
+	  codes[cost].value = (value >> 52) << 52;
+	  return cost + 1;
+	}
+
+      codes[cost].method = METHOD_LU32I;
+      codes[cost].value = ((value << 12) >> 44) << 32;
+      cost++;
+
+      if (!lu52i[(value & (1UL << 51)) >> 51])
+	{
+	  codes[cost].method = METHOD_LU52I;
+	  codes[cost].value = (value >> 52) << 52;
+	  cost++;
+	}
+    }
+
+  gcc_assert (cost <= LARCH_MAX_INTEGER_OPS);
+
+  return cost;
+}
+
+/* Fill CODES with a sequence of rtl operations to load VALUE.
+   Return the number of operations needed.
+   Split interger in loongarch_output_move.  */
+
+static unsigned int
+loongarch_integer_cost (HOST_WIDE_INT value)
+{
+  struct loongarch_integer_op codes[LARCH_MAX_INTEGER_OPS];
+  return loongarch_build_integer (codes, value);
+}
+
+/* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
+
+static bool
+loongarch_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+  return loongarch_const_insns (x) > 0;
+}
+
+/* Return true if X is a thread-local symbol.  */
+
+static bool
+loongarch_tls_symbol_p (rtx x)
+{
+  return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
+}
+
+/* Return true if SYMBOL_REF X is associated with a global symbol
+   (in the STB_GLOBAL sense).  */
+
+bool
+loongarch_global_symbol_p (const_rtx x)
+{
+  if (GET_CODE (x) == LABEL_REF)
+    return false;
+
+  const_tree decl = SYMBOL_REF_DECL (x);
+
+  if (!decl)
+    return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
+
+  /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+     or weak symbols.  Relocations in the object file will be against
+     the target symbol, so it's that symbol's binding that matters here.  */
+  return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
+}
+
+bool
+loongarch_global_symbol_noweak_p (const_rtx x)
+{
+  if (GET_CODE (x) == LABEL_REF)
+    return false;
+
+  const_tree decl = SYMBOL_REF_DECL (x);
+
+  if (!decl)
+    return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
+
+  /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+     or weak symbols.  Relocations in the object file will be against
+     the target symbol, so it's that symbol's binding that matters here.  */
+  return DECL_P (decl) && TREE_PUBLIC (decl);
+}
+
+bool
+loongarch_weak_symbol_p (const_rtx x)
+{
+  const_tree decl;
+  if (GET_CODE (x) == LABEL_REF || !(decl = SYMBOL_REF_DECL (x)))
+    return false;
+  return DECL_P (decl) && DECL_WEAK (decl);
+}
+
+/* Return true if SYMBOL_REF X binds locally.  */
+
+bool
+loongarch_symbol_binds_local_p (const_rtx x)
+{
+  if (GET_CODE (x) == LABEL_REF)
+    return false;
+
+  return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+			      : SYMBOL_REF_LOCAL_P (x));
+}
+
+/* Return true if rtx constants of mode MODE should be put into a small
+   data section.  */
+
+static bool
+loongarch_rtx_constant_in_small_data_p (machine_mode mode)
+{
+  return (GET_MODE_SIZE (mode) <= g_switch_value);
+}
+
+/* Return the method that should be used to access SYMBOL_REF or
+   LABEL_REF X in context CONTEXT.  */
+
+static enum loongarch_symbol_type
+loongarch_classify_symbol (const_rtx x,
+			   enum loongarch_symbol_context context  \
+			   ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (x) == LABEL_REF)
+    {
+      return SYMBOL_GOT_DISP;
+    }
+
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
+
+  if (SYMBOL_REF_TLS_MODEL (x))
+    return SYMBOL_TLS;
+
+  if (GET_CODE (x) == SYMBOL_REF)
+    return SYMBOL_GOT_DISP;
+
+  return SYMBOL_GOT_DISP;
+}
+
+/* Return true if X is a symbolic constant that can be used in context
+   CONTEXT.  If it is, store the type of the symbol in *SYMBOL_TYPE.  */
+
+bool
+loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_context context,
+			       enum loongarch_symbol_type *symbol_type)
+{
+  rtx offset;
+
+  split_const (x, &x, &offset);
+  if (UNSPEC_ADDRESS_P (x))
+    {
+      *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+      x = UNSPEC_ADDRESS (x);
+    }
+  else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+    {
+      *symbol_type = loongarch_classify_symbol (x, context);
+      if (*symbol_type == SYMBOL_TLS)
+	return true;
+    }
+  else
+    return false;
+
+  if (offset == const0_rtx)
+    return true;
+
+  /* Check whether a nonzero offset is valid for the underlying
+     relocations.  */
+  switch (*symbol_type)
+    {
+      /* Fall through.  */
+
+    case SYMBOL_GOT_DISP:
+    case SYMBOL_TLSGD:
+    case SYMBOL_TLSLDM:
+    case SYMBOL_TLS:
+      return false;
+    }
+  gcc_unreachable ();
+}
+
+/* Like loongarch_symbol_insns We rely on the fact that, in the worst case.  */
+
+static int
+loongarch_symbol_insns_1 (enum loongarch_symbol_type type, machine_mode mode)
+{
+  switch (type)
+    {
+    case SYMBOL_GOT_DISP:
+      /* The constant will have to be loaded from the GOT before it
+	 is used in an address.  */
+      if (mode != MAX_MACHINE_MODE)
+	return 0;
+
+      /* Fall through.  */
+
+      return 3;
+
+    case SYMBOL_TLSGD:
+    case SYMBOL_TLSLDM:
+      return 1;
+
+    case SYMBOL_TLS:
+      /* We don't treat a bare TLS symbol as a constant.  */
+      return 0;
+    }
+  gcc_unreachable ();
+}
+
+/* If MODE is MAX_MACHINE_MODE, return the number of instructions needed
+   to load symbols of type TYPE into a register.  Return 0 if the given
+   type of symbol cannot be used as an immediate operand.
+
+   Otherwise, return the number of instructions needed to load or store
+   values of mode MODE to or from addresses of type TYPE.  Return 0 if
+   the given type of symbol is not valid in addresses.
+
+   In both cases, instruction counts are based off BASE_INSN_LENGTH.  */
+
+static int
+loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode)
+{
+  return loongarch_symbol_insns_1 (type, mode) * (1);
+}
+
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM.  */
+
+static bool
+loongarch_cannot_force_const_mem (machine_mode mode, rtx x)
+{
+  enum loongarch_symbol_type type;
+  rtx base, offset;
+
+  /* As an optimization, reject constants that loongarch_legitimize_move
+     can expand inline.
+
+     Suppose we have a multi-instruction sequence that loads constant C
+     into register R.  If R does not get allocated a hard register, and
+     R is used in an operand that allows both registers and memory
+     references, reload will consider forcing C into memory and using
+     one of the instruction's memory alternatives.  Returning false
+     here will force it to use an input reload instead.  */
+  if (CONST_INT_P (x) && loongarch_legitimate_constant_p (mode, x))
+    return true;
+
+  split_const (x, &base, &offset);
+  if (loongarch_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type))
+    {
+      /* The same optimization as for CONST_INT.  */
+      if (IMM12_INT (offset)
+	  && loongarch_symbol_insns (type, MAX_MACHINE_MODE) > 0)
+	return true;
+    }
+
+  /* TLS symbols must be computed by loongarch_legitimize_move.  */
+  if (tls_referenced_p (x))
+    return true;
+
+  return false;
+}
+
+/* Return true if register REGNO is a valid base register for mode MODE.
+   STRICT_P is true if REG_OK_STRICT is in effect.  */
+
+int
+loongarch_regno_mode_ok_for_base_p (int regno,
+				    machine_mode mode ATTRIBUTE_UNUSED,
+				    bool strict_p)
+{
+  if (!HARD_REGISTER_NUM_P (regno))
+    {
+      if (!strict_p)
+	return true;
+      regno = reg_renumber[regno];
+    }
+
+  /* These fake registers will be eliminated to either the stack or
+     hard frame pointer, both of which are usually valid base registers.
+     Reload deals with the cases where the eliminated form isn't valid.  */
+  if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
+    return true;
+
+  return GP_REG_P (regno);
+}
+
+/* Return true if X is a valid base register for mode MODE.
+   STRICT_P is true if REG_OK_STRICT is in effect.  */
+
+static bool
+loongarch_valid_base_register_p (rtx x, machine_mode mode, bool strict_p)
+{
+  if (!strict_p && GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+
+  return (REG_P (x)
+	  && loongarch_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p));
+}
+
+/* Return true if, for every base register BASE_REG, (plus BASE_REG X)
+   can address a value of mode MODE.  */
+
+static bool
+loongarch_valid_offset_p (rtx x, machine_mode mode)
+{
+  /* Check that X is a signed 12-bit number,
+   * or check that X is a signed 16-bit number
+   * and offset 4 byte aligned.  */
+  if (!(const_arith_operand (x, Pmode)
+	|| ((mode == E_SImode || mode == E_DImode)
+	    && const_imm16_operand (x, Pmode)
+	    && (loongarch_signed_immediate_p (INTVAL (x), 14, 2)))))
+    return false;
+
+  /* We may need to split multiword moves, so make sure that every word
+     is accessible.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && !IMM12_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD))
+    return false;
+
+  return true;
+}
+
+/* Return true if X is a valid address for machine mode MODE.  If it is,
+   fill in INFO appropriately.  STRICT_P is true if REG_OK_STRICT is in
+   effect.  */
+
+static bool
+loongarch_classify_address (struct loongarch_address_info *info, rtx x,
+			    machine_mode mode, bool strict_p)
+{
+  switch (GET_CODE (x))
+    {
+    case REG:
+    case SUBREG:
+      info->type = ADDRESS_REG;
+      info->reg = x;
+      info->offset = const0_rtx;
+      return loongarch_valid_base_register_p (info->reg, mode, strict_p);
+
+    case PLUS:
+      info->type = ADDRESS_REG;
+      info->reg = XEXP (x, 0);
+      info->offset = XEXP (x, 1);
+      return (loongarch_valid_base_register_p (info->reg, mode, strict_p)
+	      && loongarch_valid_offset_p (info->offset, mode));
+
+    default:
+      return false;
+    }
+}
+
+/* Implement TARGET_LEGITIMATE_ADDRESS_P.  */
+
+static bool
+loongarch_legitimate_address_p (machine_mode mode, rtx x, bool strict_p)
+{
+  struct loongarch_address_info addr;
+
+  return loongarch_classify_address (&addr, x, mode, strict_p);
+}
+
+/* Return true if X is a legitimate $sp-based address for mode MODE.  */
+
+bool
+loongarch_stack_address_p (rtx x, machine_mode mode)
+{
+  struct loongarch_address_info addr;
+
+  return (loongarch_classify_address (&addr, x, mode, false)
+	  && addr.type == ADDRESS_REG
+	  && addr.reg == stack_pointer_rtx);
+}
+
+/* Return true if ADDR matches the pattern for the L{B,H,W,D}{,U}X load
+   indexed address instruction.  Note that such addresses are
+   not considered legitimate in the TARGET_LEGITIMATE_ADDRESS_P
+   sense, because their use is so restricted.  */
+
+static bool
+loongarch_lx_address_p (rtx addr, machine_mode mode ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (addr) != PLUS
+      || !REG_P (XEXP (addr, 0))
+      || !REG_P (XEXP (addr, 1)))
+    return false;
+  return false;
+}
+
+/* Return the number of instructions needed to load or store a value
+   of mode MODE at address X, assuming that BASE_INSN_LENGTH is the
+   length of one instruction.  Return 0 if X isn't valid for MODE.
+   Assume that multiword moves may need to be split into word moves
+   if MIGHT_SPLIT_P, otherwise assume that a single load or store is
+   enough.  */
+
+int
+loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p)
+{
+  struct loongarch_address_info addr;
+  int factor;
+
+  if (!loongarch_classify_address (&addr, x, mode, false))
+    return 0;
+
+  /* BLKmode is used for single unaligned loads and stores and should
+     not count as a multiword mode.  (GET_MODE_SIZE (BLKmode) is pretty
+     meaningless, so we have to single it out as a special case one way
+     or the other.)  */
+  if (mode != BLKmode && might_split_p)
+    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  else
+    factor = 1;
+
+  if (loongarch_classify_address (&addr, x, mode, false))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+	return factor;
+
+      case ADDRESS_CONST_INT:
+	return factor;
+
+      case ADDRESS_SYMBOLIC:
+	return factor * loongarch_symbol_insns (addr.symbol_type, mode);
+      }
+  return 0;
+}
+
+/* Return true if X fits within an unsigned field of BITS bits that is
+   shifted left SHIFT bits before being used.  */
+
+bool
+loongarch_unsigned_immediate_p (unsigned HOST_WIDE_INT x, int bits,
+				int shift = 0)
+{
+  return (x & ((1 << shift) - 1)) == 0 && x < ((unsigned) 1 << (shift + bits));
+}
+
+/* Return true if X fits within a signed field of BITS bits that is
+   shifted left SHIFT bits before being used.  */
+
+bool
+loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits,
+			      int shift = 0)
+{
+  x += 1 << (bits + shift - 1);
+  return loongarch_unsigned_immediate_p (x, bits, shift);
+}
+
+/* Return true if X is a legitimate address with a 12-bit offset.
+   MODE is the mode of the value being accessed.  */
+
+bool
+loongarch_12bit_offset_address_p (rtx x, machine_mode mode)
+{
+  struct loongarch_address_info addr;
+
+  return (loongarch_classify_address (&addr, x, mode, false)
+	  && addr.type == ADDRESS_REG
+	  && CONST_INT_P (addr.offset)
+	  && LARCH_U12BIT_OFFSET_P (INTVAL (addr.offset)));
+}
+
+/* Return true if X is a legitimate address with a 14-bit offset shifted 2.
+   MODE is the mode of the value being accessed.  */
+
+bool
+loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode)
+{
+  struct loongarch_address_info addr;
+
+  return (loongarch_classify_address (&addr, x, mode, false)
+	  && addr.type == ADDRESS_REG
+	  && CONST_INT_P (addr.offset)
+	  && LARCH_16BIT_OFFSET_P (INTVAL (addr.offset))
+	  && LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset)));
+}
+
+/* Return the number of instructions needed to load constant X,
+   assuming that BASE_INSN_LENGTH is the length of one instruction.
+   Return 0 if X isn't a valid constant.  */
+
+int
+loongarch_const_insns (rtx x)
+{
+  enum loongarch_symbol_type symbol_type;
+  rtx offset;
+
+  switch (GET_CODE (x))
+    {
+    case CONST_INT:
+      return loongarch_integer_cost (INTVAL (x));
+
+    case CONST_VECTOR:
+      /* Fall through.  */
+    case CONST_DOUBLE:
+      /* Allow zeros for normal mode, where we can use $0.  */
+      return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
+
+    case CONST:
+      /* See if we can refer to X directly.  */
+      if (loongarch_symbolic_constant_p (x, SYMBOL_CONTEXT_LEA, &symbol_type))
+	return loongarch_symbol_insns (symbol_type, MAX_MACHINE_MODE);
+
+      /* Otherwise try splitting the constant into a base and offset.
+	 If the offset is a 12-bit value, we can load the base address
+	 into a register and then use ADDI.{W/D} to add in the offset.
+	 If the offset is larger, we can load the base and offset
+	 into separate registers and add them together with ADD.{W/D}.
+	 However, the latter is only possible before reload; during
+	 and after reload, we must have the option of forcing the
+	 constant into the pool instead.  */
+      split_const (x, &x, &offset);
+      if (offset != 0)
+	{
+	  int n = loongarch_const_insns (x);
+	  if (n != 0)
+	    {
+	      if (IMM12_INT (offset))
+		return n + 1;
+	      else if (!targetm.cannot_force_const_mem (GET_MODE (x), x))
+		return n + 1 + loongarch_integer_cost (INTVAL (offset));
+	    }
+	}
+      return 0;
+
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return loongarch_symbol_insns (
+	loongarch_classify_symbol (x, SYMBOL_CONTEXT_LEA), MAX_MACHINE_MODE);
+
+    default:
+      return 0;
+    }
+}
+
+/* X is a doubleword constant that can be handled by splitting it into
+   two words and loading each word separately.  Return the number of
+   instructions required to do this, assuming that BASE_INSN_LENGTH
+   is the length of one instruction.  */
+
+int
+loongarch_split_const_insns (rtx x)
+{
+  unsigned int low, high;
+
+  low = loongarch_const_insns (loongarch_subword (x, false));
+  high = loongarch_const_insns (loongarch_subword (x, true));
+  gcc_assert (low > 0 && high > 0);
+  return low + high;
+}
+
+/* Return the number of instructions needed to implement INSN,
+   given that it loads from or stores to MEM.  Assume that
+   BASE_INSN_LENGTH is the length of one instruction.  */
+
+int
+loongarch_load_store_insns (rtx mem, rtx_insn *insn)
+{
+  machine_mode mode;
+  bool might_split_p;
+  rtx set;
+
+  gcc_assert (MEM_P (mem));
+  mode = GET_MODE (mem);
+
+  /* Try to prove that INSN does not need to be split.  */
+  might_split_p = GET_MODE_SIZE (mode) > UNITS_PER_WORD;
+  if (might_split_p)
+    {
+      set = single_set (insn);
+      if (set
+	  && !loongarch_split_move_insn_p (SET_DEST (set), SET_SRC (set),
+					   insn))
+	might_split_p = false;
+    }
+
+  return loongarch_address_insns (XEXP (mem, 0), mode, might_split_p);
+}
+
+/* Return the number of instructions needed for an integer division,
+   assuming that BASE_INSN_LENGTH is the length of one instruction.  */
+
+int
+loongarch_idiv_insns (machine_mode mode ATTRIBUTE_UNUSED)
+{
+  int count;
+
+  count = 1;
+  if (TARGET_CHECK_ZERO_DIV)
+    count += 2;
+
+  return count;
+}
+
+/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)).  */
+
+void
+loongarch_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
+{
+  emit_insn (gen_rtx_SET (target, gen_rtx_fmt_ee (code, GET_MODE (target),
+						  op0, op1)));
+}
+
+/* Compute (CODE OP0 OP1) and store the result in a new register
+   of mode MODE.  Return that new register.  */
+
+static rtx
+loongarch_force_binary (machine_mode mode, enum rtx_code code, rtx op0,
+			rtx op1)
+{
+  rtx reg;
+
+  reg = gen_reg_rtx (mode);
+  loongarch_emit_binary (code, reg, op0, op1);
+  return reg;
+}
+
+/* Copy VALUE to a register and return that register.  If new pseudos
+   are allowed, copy it into a new register, otherwise use DEST.  */
+
+static rtx
+loongarch_force_temporary (rtx dest, rtx value)
+{
+  if (can_create_pseudo_p ())
+    return force_reg (Pmode, value);
+  else
+    {
+      loongarch_emit_move (dest, value);
+      return dest;
+    }
+}
+
+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
+   then add CONST_INT OFFSET to the result.  */
+
+static rtx
+loongarch_unspec_address_offset (rtx base, rtx offset,
+				 enum loongarch_symbol_type symbol_type)
+{
+  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+			 UNSPEC_ADDRESS_FIRST + symbol_type);
+  if (offset != const0_rtx)
+    base = gen_rtx_PLUS (Pmode, base, offset);
+  return gen_rtx_CONST (Pmode, base);
+}
+
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+   type SYMBOL_TYPE.  */
+
+rtx
+loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type)
+{
+  rtx base, offset;
+
+  split_const (address, &base, &offset);
+  return loongarch_unspec_address_offset (base, offset, symbol_type);
+}
+
+/* If OP is an UNSPEC address, return the address to which it refers,
+   otherwise return OP itself.  */
+
+rtx
+loongarch_strip_unspec_address (rtx op)
+{
+  rtx base, offset;
+
+  split_const (op, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    op = plus_constant (Pmode, UNSPEC_ADDRESS (base), INTVAL (offset));
+  return op;
+}
+
+/* Return a legitimate address for REG + OFFSET.  TEMP is as for
+   loongarch_force_temporary; it is only needed when OFFSET is not a
+   IMM12_OPERAND.  */
+
+static rtx
+loongarch_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
+{
+  if (!IMM12_OPERAND (offset))
+    {
+      rtx high;
+
+      /* Leave OFFSET as a 12-bit offset and put the excess in HIGH.
+	 The addition inside the macro CONST_HIGH_PART may cause an
+	 overflow, so we need to force a sign-extension check.  */
+      high = gen_int_mode (CONST_HIGH_PART (offset), Pmode);
+      offset = CONST_LOW_PART (offset);
+      high = loongarch_force_temporary (temp, high);
+      reg = loongarch_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg));
+    }
+  return plus_constant (Pmode, reg, offset);
+}
+
+/* The __tls_get_attr symbol.  */
+static GTY (()) rtx loongarch_tls_symbol;
+
+/* Load an entry from the GOT for a TLS GD access.  */
+
+static rtx
+loongarch_got_load_tls_gd (rtx dest, rtx sym)
+{
+  if (Pmode == DImode)
+    return gen_got_load_tls_gddi (dest, sym);
+  else
+    return gen_got_load_tls_gdsi (dest, sym);
+}
+
+/* Load an entry from the GOT for a TLS LD access.  */
+
+static rtx
+loongarch_got_load_tls_ld (rtx dest, rtx sym)
+{
+  if (Pmode == DImode)
+    return gen_got_load_tls_lddi (dest, sym);
+  else
+    return gen_got_load_tls_ldsi (dest, sym);
+}
+
+/* Load an entry from the GOT for a TLS IE access.  */
+
+static rtx
+loongarch_got_load_tls_ie (rtx dest, rtx sym)
+{
+  if (Pmode == DImode)
+    return gen_got_load_tls_iedi (dest, sym);
+  else
+    return gen_got_load_tls_iesi (dest, sym);
+}
+
+/* Add in the thread pointer for a TLS LE access.  */
+
+static rtx
+loongarch_got_load_tls_le (rtx dest, rtx sym)
+{
+  if (Pmode == DImode)
+    return gen_got_load_tls_ledi (dest, sym);
+  else
+    return gen_got_load_tls_lesi (dest, sym);
+}
+
+/* Return an instruction sequence that calls __tls_get_addr.  SYM is
+   the TLS symbol we are referencing and TYPE is the symbol type to use
+   (either global dynamic or local dynamic).  V0 is an RTX for the
+   return value location.  */
+
+static rtx_insn *
+loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
+{
+  rtx loc, a0;
+  rtx_insn *insn;
+
+  a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  if (!loongarch_tls_symbol)
+    loongarch_tls_symbol = init_one_libfunc ("__tls_get_addr");
+
+  loc = loongarch_unspec_address (sym, type);
+
+  start_sequence ();
+
+  if (type == SYMBOL_TLSLDM)
+    emit_insn (loongarch_got_load_tls_ld (a0, loc));
+  else if (type == SYMBOL_TLSGD)
+    emit_insn (loongarch_got_load_tls_gd (a0, loc));
+  else
+    gcc_unreachable ();
+
+  insn = emit_call_insn (gen_call_value_internal (v0, loongarch_tls_symbol,
+						  const0_rtx));
+  RTL_CONST_CALL_P (insn) = 1;
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
+  insn = get_insns ();
+
+  end_sequence ();
+
+  return insn;
+}
+
+/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return
+   its address.  The return value will be both a valid address and a valid
+   SET_SRC (either a REG or a LO_SUM).  */
+
+static rtx
+loongarch_legitimize_tls_address (rtx loc)
+{
+  rtx dest, tp, tmp;
+  enum tls_model model = SYMBOL_REF_TLS_MODEL (loc);
+  rtx_insn *insn;
+
+  switch (model)
+    {
+    case TLS_MODEL_LOCAL_DYNAMIC:
+      tmp = gen_rtx_REG (Pmode, GP_RETURN);
+      dest = gen_reg_rtx (Pmode);
+      insn = loongarch_call_tls_get_addr (loc, SYMBOL_TLSLDM, tmp);
+      emit_libcall_block (insn, dest, tmp, loc);
+      break;
+
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+      tmp = gen_rtx_REG (Pmode, GP_RETURN);
+      dest = gen_reg_rtx (Pmode);
+      insn = loongarch_call_tls_get_addr (loc, SYMBOL_TLSGD, tmp);
+      emit_libcall_block (insn, dest, tmp, loc);
+      break;
+
+    case TLS_MODEL_INITIAL_EXEC:
+      /* la.tls.ie; tp-relative add  */
+      tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
+      tmp = gen_reg_rtx (Pmode);
+      emit_insn (loongarch_got_load_tls_ie (tmp, loc));
+      dest = gen_reg_rtx (Pmode);
+      emit_insn (gen_add3_insn (dest, tmp, tp));
+      break;
+
+    case TLS_MODEL_LOCAL_EXEC:
+      /* la.tls.le; tp-relative add  */
+      tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
+      tmp = gen_reg_rtx (Pmode);
+      emit_insn (loongarch_got_load_tls_le (tmp, loc));
+      dest = gen_reg_rtx (Pmode);
+      emit_insn (gen_add3_insn (dest, tmp, tp));
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+  return dest;
+}
+
+rtx
+loongarch_legitimize_call_address (rtx addr)
+{
+  if (!call_insn_operand (addr, VOIDmode))
+    {
+      rtx reg = gen_reg_rtx (Pmode);
+      loongarch_emit_move (reg, addr);
+      return reg;
+    }
+  return addr;
+}
+
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+   and *OFFSET_PTR.  Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise.  */
+
+static void
+loongarch_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
+{
+  if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+    {
+      *base_ptr = XEXP (x, 0);
+      *offset_ptr = INTVAL (XEXP (x, 1));
+    }
+  else
+    {
+      *base_ptr = x;
+      *offset_ptr = 0;
+    }
+}
+
+/* If X is not a valid address for mode MODE, force it into a register.  */
+
+static rtx
+loongarch_force_address (rtx x, machine_mode mode)
+{
+  if (!loongarch_legitimate_address_p (mode, x, false))
+    x = force_reg (Pmode, x);
+  return x;
+}
+
+/* This function is used to implement LEGITIMIZE_ADDRESS.  If X can
+   be legitimized in a way that the generic machinery might not expect,
+   return a new address, otherwise return NULL.  MODE is the mode of
+   the memory being accessed.  */
+
+static rtx
+loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+			      machine_mode mode)
+{
+  rtx base, addr;
+  HOST_WIDE_INT offset;
+
+  if (loongarch_tls_symbol_p (x))
+    return loongarch_legitimize_tls_address (x);
+
+  /* Handle BASE + OFFSET using loongarch_add_offset.  */
+  loongarch_split_plus (x, &base, &offset);
+  if (offset != 0)
+    {
+      if (!loongarch_valid_base_register_p (base, mode, false))
+	base = copy_to_mode_reg (Pmode, base);
+      addr = loongarch_add_offset (NULL, base, offset);
+      return loongarch_force_address (addr, mode);
+    }
+
+  return x;
+}
+
+/* Load VALUE into DEST.  TEMP is as for loongarch_force_temporary.  */
+
+void
+loongarch_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value)
+{
+  struct loongarch_integer_op codes[LARCH_MAX_INTEGER_OPS];
+  machine_mode mode;
+  unsigned int i, num_ops;
+  rtx x;
+
+  mode = GET_MODE (dest);
+  num_ops = loongarch_build_integer (codes, value);
+
+  /* Apply each binary operation to X.  Invariant: X is a legitimate
+     source operand for a SET pattern.  */
+  x = GEN_INT (codes[0].value);
+  for (i = 1; i < num_ops; i++)
+    {
+      if (!can_create_pseudo_p ())
+	{
+	  emit_insn (gen_rtx_SET (temp, x));
+	  x = temp;
+	}
+      else
+	x = force_reg (mode, x);
+
+      switch (codes[i].method)
+	{
+	case METHOD_NORMAL:
+	  x = gen_rtx_fmt_ee (codes[i].code, mode, x,
+			      GEN_INT (codes[i].value));
+	  break;
+	case METHOD_LU32I:
+	  emit_insn (
+	    gen_rtx_SET (x,
+			 gen_rtx_IOR (DImode,
+				      gen_rtx_ZERO_EXTEND (
+					DImode, gen_rtx_SUBREG (SImode, x, 0)),
+				      GEN_INT (codes[i].value))));
+	  break;
+	case METHOD_LU52I:
+	  emit_insn (gen_lu52i_d (x, x, GEN_INT (0xfffffffffffff),
+				  GEN_INT (codes[i].value)));
+	  break;
+	case METHOD_INSV:
+	  emit_insn (
+	    gen_rtx_SET (gen_rtx_ZERO_EXTRACT (DImode, x, GEN_INT (20),
+					       GEN_INT (32)),
+			 gen_rtx_REG (DImode, 0)));
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  emit_insn (gen_rtx_SET (dest, x));
+}
+
+/* Subroutine of loongarch_legitimize_move.  Move constant SRC into register
+   DEST given that SRC satisfies immediate_operand but doesn't satisfy
+   move_operand.  */
+
+static void
+loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
+{
+  rtx base, offset;
+
+  /* Split moves of big integers into smaller pieces.  */
+  if (splittable_const_int_operand (src, mode))
+    {
+      loongarch_move_integer (dest, dest, INTVAL (src));
+      return;
+    }
+
+  /* Generate the appropriate access sequences for TLS symbols.  */
+  if (loongarch_tls_symbol_p (src))
+    {
+      loongarch_emit_move (dest, loongarch_legitimize_tls_address (src));
+      return;
+    }
+
+  /* If we have (const (plus symbol offset)), and that expression cannot
+     be forced into memory, load the symbol first and add in the offset.
+     prefer to do this even if the constant _can_ be forced into memory,
+     as it usually produces better code.  */
+  split_const (src, &base, &offset);
+  if (offset != const0_rtx
+      && (targetm.cannot_force_const_mem (mode, src)
+	  || (can_create_pseudo_p ())))
+    {
+      base = loongarch_force_temporary (dest, base);
+      loongarch_emit_move (dest,
+			   loongarch_add_offset (NULL, base, INTVAL (offset)));
+      return;
+    }
+
+  src = force_const_mem (mode, src);
+
+  loongarch_emit_move (dest, src);
+}
+
+/* If (set DEST SRC) is not a valid move instruction, emit an equivalent
+   sequence that is valid.  */
+
+bool
+loongarch_legitimize_move (machine_mode mode, rtx dest, rtx src)
+{
+  if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
+    {
+      loongarch_emit_move (dest, force_reg (mode, src));
+      return true;
+    }
+
+  /* Both src and dest are non-registers;  one special case is supported where
+     the source is (const_int 0) and the store can source the zero register.
+     */
+  if (!register_operand (dest, mode) && !register_operand (src, mode)
+      && !const_0_operand (src, mode))
+    {
+      loongarch_emit_move (dest, force_reg (mode, src));
+      return true;
+    }
+
+  /* We need to deal with constants that would be legitimate
+     immediate_operands but aren't legitimate move_operands.  */
+  if (CONSTANT_P (src) && !move_operand (src, mode))
+    {
+      loongarch_legitimize_const_move (mode, dest, src);
+      set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src));
+      return true;
+    }
+  return false;
+}
+
+/* Return true if OP refers to small data symbols directly, not through
+   a LO_SUM.  CONTEXT is the context in which X appears.  */
+
+static int
+loongarch_small_data_pattern_1 (rtx x,
+				enum loongarch_symbol_context context \
+				ATTRIBUTE_UNUSED)
+{
+  subrtx_var_iterator::array_type array;
+  FOR_EACH_SUBRTX_VAR (iter, array, x, ALL)
+    {
+      rtx x = *iter;
+
+      /* We make no particular guarantee about which symbolic constants are
+	 acceptable as asm operands versus which must be forced into a GPR.  */
+      if (GET_CODE (x) == ASM_OPERANDS)
+	iter.skip_subrtxes ();
+      else if (MEM_P (x))
+	{
+	  if (loongarch_small_data_pattern_1 (XEXP (x, 0), SYMBOL_CONTEXT_MEM))
+	    return true;
+	  iter.skip_subrtxes ();
+	}
+    }
+  return false;
+}
+
+/* Return true if OP refers to small data symbols directly, not through
+   a LO_SUM.  */
+
+bool
+loongarch_small_data_pattern_p (rtx op)
+{
+  return loongarch_small_data_pattern_1 (op, SYMBOL_CONTEXT_LEA);
+}
+
+/* Rewrite *LOC so that it refers to small data using explicit
+   relocations.  CONTEXT is the context in which *LOC appears.  */
+
+static void
+loongarch_rewrite_small_data_1 (rtx *loc,
+				enum loongarch_symbol_context context \
+				ATTRIBUTE_UNUSED)
+{
+  subrtx_ptr_iterator::array_type array;
+  FOR_EACH_SUBRTX_PTR (iter, array, loc, ALL)
+    {
+      rtx *loc = *iter;
+      if (MEM_P (*loc))
+	{
+	  loongarch_rewrite_small_data_1 (&XEXP (*loc, 0), SYMBOL_CONTEXT_MEM);
+	  iter.skip_subrtxes ();
+	}
+    }
+}
+
+/* Rewrite instruction pattern PATTERN so that it refers to small data
+   using explicit relocations.  */
+
+rtx
+loongarch_rewrite_small_data (rtx pattern)
+{
+  pattern = copy_insn (pattern);
+  loongarch_rewrite_small_data_1 (&pattern, SYMBOL_CONTEXT_LEA);
+  return pattern;
+}
+
+/* The cost of loading values from the constant pool.  It should be
+   larger than the cost of any constant we want to synthesize inline.  */
+#define CONSTANT_POOL_COST COSTS_N_INSNS (8)
+
+/* Return true if there is a instruction that implements CODE
+   and if that instruction accepts X as an immediate operand.  */
+
+static int
+loongarch_immediate_operand_p (int code, HOST_WIDE_INT x)
+{
+  switch (code)
+    {
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      /* All shift counts are truncated to a valid constant.  */
+      return true;
+
+    case ROTATE:
+    case ROTATERT:
+      /* Likewise rotates, if the target supports rotates at all.  */
+      return true;
+
+    case AND:
+    case IOR:
+    case XOR:
+      /* These instructions take 12-bit unsigned immediates.  */
+      return IMM12_OPERAND_UNSIGNED (x);
+
+    case PLUS:
+    case LT:
+    case LTU:
+      /* These instructions take 12-bit signed immediates.  */
+      return IMM12_OPERAND (x);
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+      /* The "immediate" forms of these instructions are really
+	 implemented as comparisons with register 0.  */
+      return x == 0;
+
+    case GE:
+    case GEU:
+      /* Likewise, meaning that the only valid immediate operand is 1.  */
+      return x == 1;
+
+    case LE:
+      /* We add 1 to the immediate and use SLT.  */
+      return IMM12_OPERAND (x + 1);
+
+    case LEU:
+      /* Likewise SLTU, but reject the always-true case.  */
+      return IMM12_OPERAND (x + 1) && x + 1 != 0;
+
+    case SIGN_EXTRACT:
+    case ZERO_EXTRACT:
+      /* The bit position and size are immediate operands.  */
+      return 1;
+
+    default:
+      /* By default assume that $0 can be used for 0.  */
+      return x == 0;
+    }
+}
+
+/* Return the cost of binary operation X, given that the instruction
+   sequence for a word-sized or smaller operation has cost SINGLE_COST
+   and that the sequence of a double-word operation has cost DOUBLE_COST.
+   If SPEED is true, optimize for speed otherwise optimize for size.  */
+
+static int
+loongarch_binary_cost (rtx x, int single_cost, int double_cost, bool speed)
+{
+  int cost;
+
+  if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2)
+    cost = double_cost;
+  else
+    cost = single_cost;
+  return (cost
+	  + set_src_cost (XEXP (x, 0), GET_MODE (x), speed)
+	  + rtx_cost (XEXP (x, 1), GET_MODE (x), GET_CODE (x), 1, speed));
+}
+
+/* Return the cost of floating-point multiplications of mode MODE.  */
+
+static int
+loongarch_fp_mult_cost (machine_mode mode)
+{
+  return mode == DFmode ? loongarch_cost->fp_mult_df
+			: loongarch_cost->fp_mult_sf;
+}
+
+/* Return the cost of floating-point divisions of mode MODE.  */
+
+static int
+loongarch_fp_div_cost (machine_mode mode)
+{
+  return mode == DFmode ? loongarch_cost->fp_div_df
+			: loongarch_cost->fp_div_sf;
+}
+
+/* Return the cost of sign-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+loongarch_sign_extend_cost (machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* A sign extension from SImode to DImode in 64-bit mode is free.  */
+    return 0;
+
+  return COSTS_N_INSNS (1);
+}
+
+/* Return the cost of zero-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+loongarch_zero_extend_cost (machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* We need a shift left by 32 bits and a shift right by 32 bits.  */
+    return COSTS_N_INSNS (2);
+
+  /* We can use ANDI.  */
+  return COSTS_N_INSNS (1);
+}
+
+/* Return the cost of moving between two registers of mode MODE,
+   assuming that the move will be in pieces of at most UNITS bytes.  */
+
+static int
+loongarch_set_reg_reg_piece_cost (machine_mode mode, unsigned int units)
+{
+  return COSTS_N_INSNS ((GET_MODE_SIZE (mode) + units - 1) / units);
+}
+
+/* Return the cost of moving between two registers of mode MODE.  */
+
+static int
+loongarch_set_reg_reg_cost (machine_mode mode)
+{
+  switch (GET_MODE_CLASS (mode))
+    {
+    case MODE_CC:
+      return loongarch_set_reg_reg_piece_cost (mode, GET_MODE_SIZE (CCmode));
+
+    case MODE_FLOAT:
+    case MODE_COMPLEX_FLOAT:
+    case MODE_VECTOR_FLOAT:
+      if (TARGET_HARD_FLOAT)
+	return loongarch_set_reg_reg_piece_cost (mode, UNITS_PER_HWFPVALUE);
+      /* Fall through.  */
+
+    default:
+      return loongarch_set_reg_reg_piece_cost (mode, UNITS_PER_WORD);
+    }
+}
+
+/* Implement TARGET_RTX_COSTS.  */
+
+static bool
+loongarch_rtx_costs (rtx x, machine_mode mode, int outer_code,
+		     int opno ATTRIBUTE_UNUSED, int *total, bool speed)
+{
+  int code = GET_CODE (x);
+  bool float_mode_p = FLOAT_MODE_P (mode);
+  int cost;
+  rtx addr;
+
+  if (outer_code == COMPARE)
+    {
+      gcc_assert (CONSTANT_P (x));
+      *total = 0;
+      return true;
+    }
+
+  switch (code)
+    {
+    case CONST_INT:
+      if (TARGET_64BIT && outer_code == AND && UINTVAL (x) == 0xffffffff)
+	{
+	  *total = 0;
+	  return true;
+	}
+
+      /* When not optimizing for size, we care more about the cost
+	 of hot code, and hot code is often in a loop.  If a constant
+	 operand needs to be forced into a register, we will often be
+	 able to hoist the constant load out of the loop, so the load
+	 should not contribute to the cost.  */
+      if (speed || loongarch_immediate_operand_p (outer_code, INTVAL (x)))
+	{
+	  *total = 0;
+	  return true;
+	}
+      /* Fall through.  */
+
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST_DOUBLE:
+      cost = loongarch_const_insns (x);
+      if (cost > 0)
+	{
+	  if (cost == 1 && outer_code == SET
+	      && !(float_mode_p && TARGET_HARD_FLOAT))
+	    cost = 0;
+	  else if ((outer_code == SET || GET_MODE (x) == VOIDmode))
+	    cost = 1;
+	  *total = COSTS_N_INSNS (cost);
+	  return true;
+	}
+      /* The value will need to be fetched from the constant pool.  */
+      *total = CONSTANT_POOL_COST;
+      return true;
+
+    case MEM:
+      /* If the address is legitimate, return the number of
+	 instructions it needs.  */
+      addr = XEXP (x, 0);
+      cost = loongarch_address_insns (addr, mode, true);
+      if (cost > 0)
+	{
+	  *total = COSTS_N_INSNS (cost + 1);
+	  return true;
+	}
+      /* Check for a scaled indexed address.  */
+      if (loongarch_lx_address_p (addr, mode))
+	{
+	  *total = COSTS_N_INSNS (2);
+	  return true;
+	}
+      /* Otherwise use the default handling.  */
+      return false;
+
+    case FFS:
+      *total = COSTS_N_INSNS (6);
+      return false;
+
+    case NOT:
+      *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1);
+      return false;
+
+    case AND:
+      /* Check for a *clear_upper32 pattern and treat it like a zero
+	 extension.  See the pattern's comment for details.  */
+      if (TARGET_64BIT && mode == DImode && CONST_INT_P (XEXP (x, 1))
+	  && UINTVAL (XEXP (x, 1)) == 0xffffffff)
+	{
+	  *total = (loongarch_zero_extend_cost (mode, XEXP (x, 0))
+		    + set_src_cost (XEXP (x, 0), mode, speed));
+	  return true;
+	}
+      /* (AND (NOT op0) (NOT op1) is a nor operation that can be done in
+	 a single instruction.  */
+      if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT)
+	{
+	  cost = GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1;
+	  *total = (COSTS_N_INSNS (cost)
+		    + set_src_cost (XEXP (XEXP (x, 0), 0), mode, speed)
+		    + set_src_cost (XEXP (XEXP (x, 1), 0), mode, speed));
+	  return true;
+	}
+
+      /* Fall through.  */
+
+    case IOR:
+    case XOR:
+      /* Double-word operations use two single-word operations.  */
+      *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+				      speed);
+      return true;
+
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ROTATE:
+    case ROTATERT:
+      if (CONSTANT_P (XEXP (x, 1)))
+	*total = loongarch_binary_cost (x, COSTS_N_INSNS (1),
+					COSTS_N_INSNS (4), speed);
+      else
+	*total = loongarch_binary_cost (x, COSTS_N_INSNS (1),
+					COSTS_N_INSNS (12), speed);
+      return true;
+
+    case ABS:
+      if (float_mode_p)
+	*total = loongarch_cost->fp_add;
+      else
+	*total = COSTS_N_INSNS (4);
+      return false;
+
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case EQ:
+    case NE:
+    case UNORDERED:
+    case LTGT:
+    case UNGE:
+    case UNGT:
+    case UNLE:
+    case UNLT:
+      /* Branch comparisons have VOIDmode, so use the first operand's
+	 mode instead.  */
+      mode = GET_MODE (XEXP (x, 0));
+      if (FLOAT_MODE_P (mode))
+	{
+	  *total = loongarch_cost->fp_add;
+	  return false;
+	}
+      *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+				      speed);
+      return true;
+
+    case MINUS:
+    case PLUS:
+      if (float_mode_p)
+	{
+	  *total = loongarch_cost->fp_add;
+	  return false;
+	}
+
+      /* If it's an add + mult (which is equivalent to shift left) and
+	 it's immediate operand satisfies const_immlsa_operand predicate.  */
+      if ((mode == SImode || (TARGET_64BIT && mode == DImode))
+	  && GET_CODE (XEXP (x, 0)) == MULT)
+	{
+	  rtx op2 = XEXP (XEXP (x, 0), 1);
+	  if (const_immlsa_operand (op2, mode))
+	    {
+	      *total = (COSTS_N_INSNS (1)
+			+ set_src_cost (XEXP (XEXP (x, 0), 0), mode, speed)
+			+ set_src_cost (XEXP (x, 1), mode, speed));
+	      return true;
+	    }
+	}
+
+      /* Double-word operations require three single-word operations and
+	 an SLTU.  */
+      *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+				      speed);
+      return true;
+
+    case NEG:
+      if (float_mode_p)
+	*total = loongarch_cost->fp_add;
+      else
+	*total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
+      return false;
+
+    case FMA:
+      *total = loongarch_fp_mult_cost (mode);
+      return false;
+
+    case MULT:
+      if (float_mode_p)
+	*total = loongarch_fp_mult_cost (mode);
+      else if (mode == DImode && !TARGET_64BIT)
+	*total = (speed
+		  ? loongarch_cost->int_mult_si * 3 + 6
+		  : COSTS_N_INSNS (7));
+      else if (!speed)
+	*total = COSTS_N_INSNS (1) + 1;
+      else if (mode == DImode)
+	*total = loongarch_cost->int_mult_di;
+      else
+	*total = loongarch_cost->int_mult_si;
+      return false;
+
+    case DIV:
+      /* Check for a reciprocal.  */
+      if (float_mode_p
+	  && flag_unsafe_math_optimizations
+	  && XEXP (x, 0) == CONST1_RTX (mode))
+	{
+	  if (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT)
+	    /* An rsqrt<mode>a or rsqrt<mode>b pattern.  Count the
+	       division as being free.  */
+	    *total = set_src_cost (XEXP (x, 1), mode, speed);
+	  else
+	    *total = (loongarch_fp_div_cost (mode)
+		      + set_src_cost (XEXP (x, 1), mode, speed));
+	  return true;
+	}
+      /* Fall through.  */
+
+    case SQRT:
+    case MOD:
+      if (float_mode_p)
+	{
+	  *total = loongarch_fp_div_cost (mode);
+	  return false;
+	}
+      /* Fall through.  */
+
+    case UDIV:
+    case UMOD:
+      if (!speed)
+	{
+	  *total = COSTS_N_INSNS (loongarch_idiv_insns (mode));
+	}
+      else if (mode == DImode)
+	*total = loongarch_cost->int_div_di;
+      else
+	*total = loongarch_cost->int_div_si;
+      return false;
+
+    case SIGN_EXTEND:
+      *total = loongarch_sign_extend_cost (mode, XEXP (x, 0));
+      return false;
+
+    case ZERO_EXTEND:
+      *total = loongarch_zero_extend_cost (mode, XEXP (x, 0));
+      return false;
+    case TRUNCATE:
+      /* Costings for highpart multiplies.  Matching patterns of the form:
+
+	 (lshiftrt:DI (mult:DI (sign_extend:DI (...)
+			       (sign_extend:DI (...))
+		      (const_int 32)
+      */
+      if ((GET_CODE (XEXP (x, 0)) == ASHIFTRT
+	   || GET_CODE (XEXP (x, 0)) == LSHIFTRT)
+	  && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+	  && ((INTVAL (XEXP (XEXP (x, 0), 1)) == 32
+	       && GET_MODE (XEXP (x, 0)) == DImode)
+	      || (TARGET_64BIT
+		  && INTVAL (XEXP (XEXP (x, 0), 1)) == 64
+		  && GET_MODE (XEXP (x, 0)) == TImode))
+	  && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
+	  && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND
+	       && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == SIGN_EXTEND)
+	      || (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND
+		  && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1))
+		      == ZERO_EXTEND))))
+	{
+	  if (!speed)
+	    *total = COSTS_N_INSNS (1) + 1;
+	  else if (mode == DImode)
+	    *total = loongarch_cost->int_mult_di;
+	  else
+	    *total = loongarch_cost->int_mult_si;
+
+	  /* Sign extension is free, zero extension costs for DImode when
+	     on a 64bit core / when DMUL is present.  */
+	  for (int i = 0; i < 2; ++i)
+	    {
+	      rtx op = XEXP (XEXP (XEXP (x, 0), 0), i);
+	      if (TARGET_64BIT
+		  && GET_CODE (op) == ZERO_EXTEND
+		  && GET_MODE (op) == DImode)
+		*total += rtx_cost (op, DImode, MULT, i, speed);
+	      else
+		*total += rtx_cost (XEXP (op, 0), VOIDmode, GET_CODE (op), 0,
+				    speed);
+	    }
+
+	  return true;
+	}
+      return false;
+
+    case FLOAT:
+    case UNSIGNED_FLOAT:
+    case FIX:
+    case FLOAT_EXTEND:
+    case FLOAT_TRUNCATE:
+      *total = loongarch_cost->fp_add;
+      return false;
+
+    case SET:
+      if (register_operand (SET_DEST (x), VOIDmode)
+	  && reg_or_0_operand (SET_SRC (x), VOIDmode))
+	{
+	  *total = loongarch_set_reg_reg_cost (GET_MODE (SET_DEST (x)));
+	  return true;
+	}
+      return false;
+
+    default:
+      return false;
+    }
+}
+
+/* Implement TARGET_ADDRESS_COST.  */
+
+static int
+loongarch_address_cost (rtx addr, machine_mode mode,
+			addr_space_t as ATTRIBUTE_UNUSED,
+			bool speed ATTRIBUTE_UNUSED)
+{
+  return loongarch_address_insns (addr, mode, false);
+}
+
+/* Return one word of double-word value OP, taking into account the fixed
+   endianness of certain registers.  HIGH_P is true to select the high part,
+   false to select the low part.  */
+
+rtx
+loongarch_subword (rtx op, bool high_p)
+{
+  unsigned int byte, offset;
+  machine_mode mode;
+
+  mode = GET_MODE (op);
+  if (mode == VOIDmode)
+    mode = TARGET_64BIT ? TImode : DImode;
+
+  if (high_p)
+    byte = UNITS_PER_WORD;
+  else
+    byte = 0;
+
+  if (FP_REG_RTX_P (op))
+    {
+      /* Paired FPRs are always ordered little-endian.  */
+      offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+      return gen_rtx_REG (word_mode, REGNO (op) + offset);
+    }
+
+  if (MEM_P (op))
+    return loongarch_rewrite_small_data (adjust_address (op, word_mode, byte));
+
+  return simplify_gen_subreg (word_mode, op, mode, byte);
+}
+
+/* Return true if a move from SRC to DEST should be split into two.
+   SPLIT_TYPE describes the split condition.  */
+
+bool
+loongarch_split_move_p (rtx dest, rtx src,
+			enum loongarch_split_type split_type ATTRIBUTE_UNUSED)
+{
+  /* FPR-to-FPR moves can be done in a single instruction, if they're
+     allowed at all.  */
+  unsigned int size = GET_MODE_SIZE (GET_MODE (dest));
+  if (size == 8 && FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
+    return false;
+
+  /* Check for floating-point loads and stores.  */
+  if (size == 8)
+    {
+      if (FP_REG_RTX_P (dest) && MEM_P (src))
+	return false;
+      if (FP_REG_RTX_P (src) && MEM_P (dest))
+	return false;
+    }
+  /* Otherwise split all multiword moves.  */
+  return size > UNITS_PER_WORD;
+}
+
+/* Split a move from SRC to DEST, given that loongarch_split_move_p holds.
+   SPLIT_TYPE describes the split condition.  */
+
+void
+loongarch_split_move (rtx dest, rtx src, enum loongarch_split_type split_type,
+		      rtx insn_)
+{
+  rtx low_dest;
+
+  gcc_checking_assert (loongarch_split_move_p (dest, src, split_type));
+  if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
+    {
+      if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+	emit_insn (gen_move_doubleword_fprdi (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+	emit_insn (gen_move_doubleword_fprdf (dest, src));
+      else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+	emit_insn (gen_move_doubleword_fprtf (dest, src));
+      else
+	gcc_unreachable ();
+    }
+  else
+    {
+      /* The operation can be split into two normal moves.  Decide in
+	 which order to do them.  */
+      low_dest = loongarch_subword (dest, false);
+      if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src))
+	{
+	  loongarch_emit_move (loongarch_subword (dest, true),
+			       loongarch_subword (src, true));
+	  loongarch_emit_move (low_dest, loongarch_subword (src, false));
+	}
+      else
+	{
+	  loongarch_emit_move (low_dest, loongarch_subword (src, false));
+	  loongarch_emit_move (loongarch_subword (dest, true),
+			       loongarch_subword (src, true));
+	}
+    }
+
+  /* This is a hack.  See if the next insn uses DEST and if so, see if we
+     can forward SRC for DEST.  This is most useful if the next insn is a
+     simple store.  */
+  rtx_insn *insn = (rtx_insn *) insn_;
+  struct loongarch_address_info addr = {};
+  if (insn)
+    {
+      rtx_insn *next = next_nonnote_nondebug_insn_bb (insn);
+      if (next)
+	{
+	  rtx set = single_set (next);
+	  if (set && SET_SRC (set) == dest)
+	    {
+	      if (MEM_P (src))
+		{
+		  rtx tmp = XEXP (src, 0);
+		  loongarch_classify_address (&addr, tmp, GET_MODE (tmp),
+					      true);
+		  if (addr.reg && !reg_overlap_mentioned_p (dest, addr.reg))
+		    validate_change (next, &SET_SRC (set), src, false);
+		}
+	      else
+		validate_change (next, &SET_SRC (set), src, false);
+	    }
+	}
+    }
+}
+
+/* Return the split type for instruction INSN.  */
+
+static enum loongarch_split_type
+loongarch_insn_split_type (rtx insn)
+{
+  basic_block bb = BLOCK_FOR_INSN (insn);
+  if (bb)
+    {
+      if (optimize_bb_for_speed_p (bb))
+	return SPLIT_FOR_SPEED;
+      else
+	return SPLIT_FOR_SIZE;
+    }
+  /* Once CFG information has been removed, we should trust the optimization
+     decisions made by previous passes and only split where necessary.  */
+  return SPLIT_IF_NECESSARY;
+}
+
+/* Return true if a move from SRC to DEST in INSN should be split.  */
+
+bool
+loongarch_split_move_insn_p (rtx dest, rtx src, rtx insn)
+{
+  return loongarch_split_move_p (dest, src, loongarch_insn_split_type (insn));
+}
+
+/* Split a move from SRC to DEST in INSN, given that
+   loongarch_split_move_insn_p holds.  */
+
+void
+loongarch_split_move_insn (rtx dest, rtx src, rtx insn)
+{
+  loongarch_split_move (dest, src, loongarch_insn_split_type (insn), insn);
+}
+
+/* Forward declaration.  Used below.  */
+static HOST_WIDE_INT
+loongarch_constant_alignment (const_tree exp, HOST_WIDE_INT align);
+
+/* Return the appropriate instructions to move SRC into DEST.  Assume
+   that SRC is operand 1 and DEST is operand 0.  */
+
+const char *
+loongarch_output_move (rtx dest, rtx src)
+{
+  enum rtx_code dest_code = GET_CODE (dest);
+  enum rtx_code src_code = GET_CODE (src);
+  machine_mode mode = GET_MODE (dest);
+  bool dbl_p = (GET_MODE_SIZE (mode) == 8);
+
+  if (loongarch_split_move_p (dest, src, SPLIT_IF_NECESSARY))
+    return "#";
+
+  if ((src_code == REG && GP_REG_P (REGNO (src)))
+      || (src == CONST0_RTX (mode)))
+    {
+      if (dest_code == REG)
+	{
+	  if (GP_REG_P (REGNO (dest)))
+	    return "or\t%0,%z1,$r0";
+
+	  if (FP_REG_P (REGNO (dest)))
+	    return dbl_p ? "movgr2fr.d\t%0,%z1" : "movgr2fr.w\t%0,%z1";
+	}
+      if (dest_code == MEM)
+	{
+	  rtx offset = XEXP (dest, 0);
+	  if (GET_CODE (offset) == PLUS)
+	    offset = XEXP (offset, 1);
+	  switch (GET_MODE_SIZE (mode))
+	    {
+	    case 1:
+	      return "st.b\t%z1,%0";
+	    case 2:
+	      return "st.h\t%z1,%0";
+	    case 4:
+	      if (const_arith_operand (offset, Pmode))
+		return "st.w\t%z1,%0";
+	      else
+		return "stptr.w\t%z1,%0";
+	    case 8:
+	      if (const_arith_operand (offset, Pmode))
+		return "st.d\t%z1,%0";
+	      else
+		return "stptr.d\t%z1,%0";
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
+    }
+  if (dest_code == REG && GP_REG_P (REGNO (dest)))
+    {
+      if (src_code == REG)
+	if (FP_REG_P (REGNO (src)))
+	  return dbl_p ? "movfr2gr.d\t%0,%1" : "movfr2gr.s\t%0,%1";
+
+      if (src_code == MEM)
+	{
+	  rtx offset = XEXP (src, 0);
+	  if (GET_CODE (offset) == PLUS)
+	    offset = XEXP (offset, 1);
+	  switch (GET_MODE_SIZE (mode))
+	    {
+	    case 1:
+	      return "ld.bu\t%0,%1";
+	    case 2:
+	      return "ld.hu\t%0,%1";
+	    case 4:
+	      if (const_arith_operand (offset, Pmode))
+		return "ld.w\t%0,%1";
+	      else
+		return "ldptr.w\t%0,%1";
+	    case 8:
+	      if (const_arith_operand (offset, Pmode))
+		return "ld.d\t%0,%1";
+	      else
+		return "ldptr.d\t%0,%1";
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
+
+      if (src_code == CONST_INT)
+	{
+	  if (LU12I_INT (src))
+	    return "lu12i.w\t%0,%1>>12\t\t\t# %X1";
+	  else if (IMM12_INT (src))
+	    return "addi.w\t%0,$r0,%1\t\t\t# %X1";
+	  else if (IMM12_INT_UNSIGNED (src))
+	    return "ori\t%0,$r0,%1\t\t\t# %X1";
+	  else if (LU52I_INT (src))
+	    return "lu52i.d\t%0,$r0,%X1>>52\t\t\t# %1";
+	  else
+	    gcc_unreachable ();
+	}
+
+      if (symbolic_operand (src, VOIDmode))
+	{
+	  if ((TARGET_CMODEL_TINY && (!loongarch_global_symbol_p (src)
+				      || loongarch_symbol_binds_local_p (src)))
+	      || (TARGET_CMODEL_TINY_STATIC && !loongarch_weak_symbol_p (src)))
+	    {
+	      /* The symbol must be aligned to 4 byte.  */
+	      unsigned int align;
+
+	      if (GET_CODE (src) == LABEL_REF)
+		align = 128 /* Whatever.  */;
+	      else if (CONSTANT_POOL_ADDRESS_P (src))
+		align = GET_MODE_ALIGNMENT (get_pool_mode (src));
+	      else if (TREE_CONSTANT_POOL_ADDRESS_P (src))
+		{
+		  tree exp = SYMBOL_REF_DECL (src);
+		  align = TYPE_ALIGN (TREE_TYPE (exp));
+		  align = loongarch_constant_alignment (exp, align);
+		}
+	      else if (SYMBOL_REF_DECL (src))
+		align = DECL_ALIGN (SYMBOL_REF_DECL (src));
+	      else if (SYMBOL_REF_HAS_BLOCK_INFO_P (src)
+		       && SYMBOL_REF_BLOCK (src) != NULL)
+		align = SYMBOL_REF_BLOCK (src)->alignment;
+	      else
+		align = BITS_PER_UNIT;
+
+	      if (align % (4 * 8) == 0)
+		return "pcaddi\t%0,%%pcrel(%1)>>2";
+	    }
+	  if (TARGET_CMODEL_TINY
+	      || TARGET_CMODEL_TINY_STATIC
+	      || TARGET_CMODEL_NORMAL
+	      || TARGET_CMODEL_LARGE)
+	    {
+	      if (!loongarch_global_symbol_p (src)
+		  || loongarch_symbol_binds_local_p (src))
+		return "la.local\t%0,%1";
+	      else
+		return "la.global\t%0,%1";
+	    }
+	  if (TARGET_CMODEL_EXTREME)
+	    {
+	      sorry ("Normal symbol loading not implemented in extreme mode.");
+	      /* GCC complains.  */
+	      /* return ""; */
+	    }
+	}
+    }
+  if (src_code == REG && FP_REG_P (REGNO (src)))
+    {
+      if (dest_code == REG && FP_REG_P (REGNO (dest)))
+	return dbl_p ? "fmov.d\t%0,%1" : "fmov.s\t%0,%1";
+
+      if (dest_code == MEM)
+	return dbl_p ? "fst.d\t%1,%0" : "fst.s\t%1,%0";
+    }
+  if (dest_code == REG && FP_REG_P (REGNO (dest)))
+    {
+      if (src_code == MEM)
+	return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1";
+    }
+  gcc_unreachable ();
+}
+
+/* Return true if CMP1 is a suitable second operand for integer ordering
+   test CODE.  See also the *sCC patterns in loongarch.md.  */
+
+static bool
+loongarch_int_order_operand_ok_p (enum rtx_code code, rtx cmp1)
+{
+  switch (code)
+    {
+    case GT:
+    case GTU:
+      return reg_or_0_operand (cmp1, VOIDmode);
+
+    case GE:
+    case GEU:
+      return cmp1 == const1_rtx;
+
+    case LT:
+    case LTU:
+      return arith_operand (cmp1, VOIDmode);
+
+    case LE:
+      return sle_operand (cmp1, VOIDmode);
+
+    case LEU:
+      return sleu_operand (cmp1, VOIDmode);
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return true if *CMP1 (of mode MODE) is a valid second operand for
+   integer ordering test *CODE, or if an equivalent combination can
+   be formed by adjusting *CODE and *CMP1.  When returning true, update
+ *CODE and *CMP1 with the chosen code and operand, otherwise leave
+ them alone.  */
+
+static bool
+loongarch_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1,
+				       machine_mode mode)
+{
+  HOST_WIDE_INT plus_one;
+
+  if (loongarch_int_order_operand_ok_p (*code, *cmp1))
+    return true;
+
+  if (CONST_INT_P (*cmp1))
+    switch (*code)
+      {
+      case LE:
+	plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+	if (INTVAL (*cmp1) < plus_one)
+	  {
+	    *code = LT;
+	    *cmp1 = force_reg (mode, GEN_INT (plus_one));
+	    return true;
+	  }
+	break;
+
+      case LEU:
+	plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+	if (plus_one != 0)
+	  {
+	    *code = LTU;
+	    *cmp1 = force_reg (mode, GEN_INT (plus_one));
+	    return true;
+	  }
+	break;
+
+      default:
+	break;
+      }
+  return false;
+}
+
+/* Compare CMP0 and CMP1 using ordering test CODE and store the result
+   in TARGET.  CMP0 and TARGET are register_operands.  If INVERT_PTR
+   is nonnull, it's OK to set TARGET to the inverse of the result and
+   flip *INVERT_PTR instead.  */
+
+static void
+loongarch_emit_int_order_test (enum rtx_code code, bool *invert_ptr,
+			       rtx target, rtx cmp0, rtx cmp1)
+{
+  machine_mode mode;
+
+  /* First see if there is a LoongArch instruction that can do this operation.
+     If not, try doing the same for the inverse operation.  If that also
+     fails, force CMP1 into a register and try again.  */
+  mode = GET_MODE (cmp0);
+  if (loongarch_canonicalize_int_order_test (&code, &cmp1, mode))
+    loongarch_emit_binary (code, target, cmp0, cmp1);
+  else
+    {
+      enum rtx_code inv_code = reverse_condition (code);
+      if (!loongarch_canonicalize_int_order_test (&inv_code, &cmp1, mode))
+	{
+	  cmp1 = force_reg (mode, cmp1);
+	  loongarch_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1);
+	}
+      else if (invert_ptr == 0)
+	{
+	  rtx inv_target;
+
+	  inv_target = loongarch_force_binary (GET_MODE (target),
+					       inv_code, cmp0, cmp1);
+	  loongarch_emit_binary (XOR, target, inv_target, const1_rtx);
+	}
+      else
+	{
+	  *invert_ptr = !*invert_ptr;
+	  loongarch_emit_binary (inv_code, target, cmp0, cmp1);
+	}
+    }
+}
+
+/* Return a register that is zero if CMP0 and CMP1 are equal.
+   The register will have the same mode as CMP0.  */
+
+static rtx
+loongarch_zero_if_equal (rtx cmp0, rtx cmp1)
+{
+  if (cmp1 == const0_rtx)
+    return cmp0;
+
+  if (uns_arith_operand (cmp1, VOIDmode))
+    return expand_binop (GET_MODE (cmp0), xor_optab, cmp0, cmp1, 0, 0,
+			 OPTAB_DIRECT);
+
+  return expand_binop (GET_MODE (cmp0), sub_optab, cmp0, cmp1, 0, 0,
+		       OPTAB_DIRECT);
+}
+
+/* Allocate a floating-point condition-code register of mode MODE.  */
+
+static rtx
+loongarch_allocate_fcc (machine_mode mode)
+{
+  unsigned int regno, count;
+
+  gcc_assert (TARGET_HARD_FLOAT);
+
+  if (mode == FCCmode)
+    count = 1;
+  else
+    gcc_unreachable ();
+
+  cfun->machine->next_fcc += -cfun->machine->next_fcc & (count - 1);
+  if (cfun->machine->next_fcc > FCC_REG_LAST - FCC_REG_FIRST)
+    cfun->machine->next_fcc = 0;
+
+  regno = FCC_REG_FIRST + cfun->machine->next_fcc;
+  cfun->machine->next_fcc += count;
+  return gen_rtx_REG (mode, regno);
+}
+
+/* Sign- or zero-extend OP0 and OP1 for integer comparisons.  */
+
+static void
+loongarch_extend_comparands (rtx_code code, rtx *op0, rtx *op1)
+{
+  /* Comparisons consider all XLEN bits, so extend sub-XLEN values.  */
+  if (GET_MODE_SIZE (word_mode) > GET_MODE_SIZE (GET_MODE (*op0)))
+    {
+      /* TODO: checkout It is more profitable to zero-extend QImode values.  */
+      if (unsigned_condition (code) == code && GET_MODE (*op0) == QImode)
+	{
+	  *op0 = gen_rtx_ZERO_EXTEND (word_mode, *op0);
+	  if (CONST_INT_P (*op1))
+	    *op1 = GEN_INT ((uint8_t) INTVAL (*op1));
+	  else
+	    *op1 = gen_rtx_ZERO_EXTEND (word_mode, *op1);
+	}
+      else
+	{
+	  *op0 = gen_rtx_SIGN_EXTEND (word_mode, *op0);
+	  if (*op1 != const0_rtx)
+	    *op1 = gen_rtx_SIGN_EXTEND (word_mode, *op1);
+	}
+    }
+}
+
+/* Convert a comparison into something that can be used in a branch.  On
+   entry, *OP0 and *OP1 are the values being compared and *CODE is the code
+   used to compare them.  Update them to describe the final comparison.  */
+
+static void
+loongarch_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1)
+{
+  static const enum rtx_code
+  mag_comparisons[][2] = {{LEU, LTU}, {GTU, GEU}, {LE, LT}, {GT, GE}};
+
+  if (splittable_const_int_operand (*op1, VOIDmode))
+    {
+      HOST_WIDE_INT rhs = INTVAL (*op1);
+
+      if (*code == EQ || *code == NE)
+	{
+	  /* Convert e.g. OP0 == 2048 into OP0 - 2048 == 0.  */
+	  if (IMM12_OPERAND (-rhs))
+	    {
+	      *op0 = loongarch_force_binary (GET_MODE (*op0), PLUS, *op0,
+					     GEN_INT (-rhs));
+	      *op1 = const0_rtx;
+	    }
+	}
+      else
+	{
+	  /* Convert e.g. (OP0 <= 0xFFF) into (OP0 < 0x1000).  */
+	  for (size_t i = 0; i < ARRAY_SIZE (mag_comparisons); i++)
+	    {
+	      HOST_WIDE_INT new_rhs;
+	      bool increment = *code == mag_comparisons[i][0];
+	      bool decrement = *code == mag_comparisons[i][1];
+	      if (!increment && !decrement)
+		continue;
+
+	      new_rhs = rhs + (increment ? 1 : -1);
+	      if (loongarch_integer_cost (new_rhs)
+		    < loongarch_integer_cost (rhs)
+		  && (rhs < 0) == (new_rhs < 0))
+		{
+		  *op1 = GEN_INT (new_rhs);
+		  *code = mag_comparisons[i][increment];
+		}
+	      break;
+	    }
+	}
+    }
+
+  loongarch_extend_comparands (*code, op0, op1);
+
+  *op0 = force_reg (word_mode, *op0);
+  if (*op1 != const0_rtx)
+    *op1 = force_reg (word_mode, *op1);
+}
+
+/* Like riscv_emit_int_compare, but for floating-point comparisons.  */
+
+static void
+loongarch_emit_float_compare (enum rtx_code *code, rtx *op0, rtx *op1)
+{
+  rtx cmp_op0 = *op0;
+  rtx cmp_op1 = *op1;
+
+  /* Floating-point tests use a separate FCMP.cond.fmt
+     comparison to set a register.  The branch or conditional move will
+     then compare that register against zero.
+
+     Set CMP_CODE to the code of the comparison instruction and
+   *CODE to the code that the branch or move should use.  */
+  enum rtx_code cmp_code = *code;
+  /* Three FP conditions cannot be implemented by reversing the
+     operands for FCMP.cond.fmt, instead a reversed condition code is
+     required and a test for false.  */
+  *code = NE;
+  *op0 = loongarch_allocate_fcc (FCCmode);
+
+  *op1 = const0_rtx;
+  loongarch_emit_binary (cmp_code, *op0, cmp_op0, cmp_op1);
+}
+
+/* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2]
+   and OPERAND[3].  Store the result in OPERANDS[0].
+
+   On 64-bit targets, the mode of the comparison and target will always be
+   SImode, thus possibly narrower than that of the comparison's operands.  */
+
+void
+loongarch_expand_scc (rtx operands[])
+{
+  rtx target = operands[0];
+  enum rtx_code code = GET_CODE (operands[1]);
+  rtx op0 = operands[2];
+  rtx op1 = operands[3];
+
+  gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT);
+
+  if (code == EQ || code == NE)
+    {
+      rtx zie = loongarch_zero_if_equal (op0, op1);
+      loongarch_emit_binary (code, target, zie, const0_rtx);
+    }
+  else
+    loongarch_emit_int_order_test (code, 0, target, op0, op1);
+}
+
+/* Compare OPERANDS[1] with OPERANDS[2] using comparison code
+   CODE and jump to OPERANDS[3] if the condition holds.  */
+
+void
+loongarch_expand_conditional_branch (rtx *operands)
+{
+  enum rtx_code code = GET_CODE (operands[0]);
+  rtx op0 = operands[1];
+  rtx op1 = operands[2];
+  rtx condition;
+
+  if (FLOAT_MODE_P (GET_MODE (op1)))
+    loongarch_emit_float_compare (&code, &op0, &op1);
+  else
+    loongarch_emit_int_compare (&code, &op0, &op1);
+
+  condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
+  emit_jump_insn (gen_condjump (condition, operands[3]));
+}
+
+/* Perform the comparison in OPERANDS[1].  Move OPERANDS[2] into OPERANDS[0]
+   if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0].  */
+
+void
+loongarch_expand_conditional_move (rtx *operands)
+{
+  enum rtx_code code = GET_CODE (operands[1]);
+  rtx op0 = XEXP (operands[1], 0);
+  rtx op1 = XEXP (operands[1], 1);
+
+  if (FLOAT_MODE_P (GET_MODE (op1)))
+    loongarch_emit_float_compare (&code, &op0, &op1);
+  else
+    {
+      /* See test-cbranchqi4.c and pr53645.c.  */
+      loongarch_extend_comparands (code, &op0, &op1);
+
+      op0 = force_reg (word_mode, op0);
+
+      if (code == EQ || code == NE) /* See test-mask-1.c && test-mask-5.c.  */
+	{
+	  op0 = loongarch_zero_if_equal (op0, op1);
+	  op1 = const0_rtx;
+	}
+      else /* See test-mask-2.c.  */
+	{
+	  /* The comparison needs a separate scc instruction.  Store the
+	     result of the scc in *OP0 and compare it against zero.  */
+	  bool invert = false;
+	  rtx target = gen_reg_rtx (GET_MODE (op0));
+	  loongarch_emit_int_order_test (code, &invert, target, op0, op1);
+	  code = invert ? EQ : NE;
+	  op0 = target;
+	  op1 = const0_rtx;
+	}
+    }
+
+  rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+  /* There is no direct support for general conditional GP move involving
+     two registers using SEL.  See test-mask-3.c.  */
+  if (INTEGRAL_MODE_P (GET_MODE (operands[2]))
+      && register_operand (operands[2], VOIDmode)
+      && register_operand (operands[3], VOIDmode))
+    {
+      machine_mode mode = GET_MODE (operands[0]);
+      rtx temp = gen_reg_rtx (mode);
+      rtx temp2 = gen_reg_rtx (mode);
+
+      emit_insn (gen_rtx_SET (temp,
+			      gen_rtx_IF_THEN_ELSE (mode, cond,
+						    operands[2], const0_rtx)));
+
+      /* Flip the test for the second operand.  */
+      cond = gen_rtx_fmt_ee ((code == EQ) ? NE : EQ, GET_MODE (op0), op0, op1);
+
+      emit_insn (gen_rtx_SET (temp2,
+			      gen_rtx_IF_THEN_ELSE (mode, cond,
+						    operands[3], const0_rtx)));
+
+      /* Merge the two results, at least one is guaranteed to be zero.  */
+      emit_insn (gen_rtx_SET (operands[0], gen_rtx_IOR (mode, temp, temp2)));
+    }
+  else
+    emit_insn (gen_rtx_SET (operands[0],
+			    gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
+						  operands[2], operands[3])));
+}
+
+/* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
+
+static void
+loongarch_va_start (tree valist, rtx nextarg)
+{
+  nextarg = plus_constant (Pmode, nextarg, -cfun->machine->varargs_size);
+  std_expand_builtin_va_start (valist, nextarg);
+}
+
+/* Start a definition of function NAME.  */
+
+static void
+loongarch_start_function_definition (const char *name)
+{
+  ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, name, "function");
+
+  /* Start the definition proper.  */
+  assemble_name (asm_out_file, name);
+  fputs (":\n", asm_out_file);
+}
+
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
+
+static bool
+loongarch_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
+				   tree exp ATTRIBUTE_UNUSED)
+{
+  /* Always OK.  */
+  return true;
+}
+
+/* Implement TARGET_USE_BY_PIECES_INFRASTRUCTURE_P.  */
+
+bool
+loongarch_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size,
+					  unsigned int align,
+					  enum by_pieces_operation op,
+					  bool speed_p)
+{
+  if (op == STORE_BY_PIECES)
+    return loongarch_store_by_pieces_p (size, align);
+  if (op == MOVE_BY_PIECES && HAVE_cpymemsi)
+    {
+      /* cpymemsi is meant to generate code that is at least as good as
+	 move_by_pieces.  However, cpymemsi effectively uses a by-pieces
+	 implementation both for moves smaller than a word and for
+	 word-aligned moves of no more than LARCH_MAX_MOVE_BYTES_STRAIGHT
+	 bytes.  We should allow the tree-level optimisers to do such
+	 moves by pieces, as it often exposes other optimization
+	 opportunities.  We might as well continue to use cpymemsi at
+	 the rtl level though, as it produces better code when
+	 scheduling is disabled (such as at -O).  */
+      if (currently_expanding_to_rtl)
+	return false;
+      if (align < BITS_PER_WORD)
+	return size < UNITS_PER_WORD;
+      return size <= LARCH_MAX_MOVE_BYTES_STRAIGHT;
+    }
+
+  return default_use_by_pieces_infrastructure_p (size, align, op, speed_p);
+}
+
+/* Implement a handler for STORE_BY_PIECES operations
+   for TARGET_USE_BY_PIECES_INFRASTRUCTURE_P.  */
+
+bool
+loongarch_store_by_pieces_p (unsigned HOST_WIDE_INT size, unsigned int align)
+{
+  /* Storing by pieces involves moving constants into registers
+     of size MIN (ALIGN, BITS_PER_WORD), then storing them.
+     We need to decide whether it is cheaper to load the address of
+     constant data into a register and use a block move instead.  */
+
+  /* If the data is only byte aligned, then:
+
+     (a1) A block move of less than 4 bytes would involve three 3 LD.Bs and
+	  3 ST.Bs.  We might as well use 3 single-instruction LIs and 3 SD.Bs
+	  instead.
+
+     (a2) A block move of 4 bytes from aligned source data can use an
+	  LD.W/ST.W sequence.  This is often better than the 4 LIs and
+	  4 SD.Bs that we would generate when storing by pieces.  */
+  if (align <= BITS_PER_UNIT)
+    return size < 4;
+
+  /* If the data is 2-byte aligned, then:
+
+     (b1) A block move of less than 4 bytes would use a combination of LD.Bs,
+	  LD.Hs, SD.Bs and SD.Hs.  We get better code by using
+     single-instruction LIs, SD.Bs and SD.Hs instead.
+
+     (b2) A block move of 4 bytes from aligned source data would again use
+	  an LD.W/ST.W sequence.  In most cases, loading the address of
+	  the source data would require at least one extra instruction.
+	  It is often more efficient to use 2 single-instruction LIs and
+	  2 SHs instead.
+
+     (b3) A block move of up to 3 additional bytes would be like (b1).
+
+     (b4) A block move of 8 bytes from aligned source data can use two
+	  LD.W/ST.W sequences.  Both sequences are better than the 4 LIs
+	  and 4 ST.Hs that we'd generate when storing by pieces.
+
+     The reasoning for higher alignments is similar:
+
+     (c1) A block move of less than 4 bytes would be the same as (b1).
+
+     (c2) A block move of 4 bytes would use an LD.W/ST.W sequence.  Again,
+	  loading the address of the source data would typically require
+	  at least one extra instruction.  It is generally better to use
+	  LU12I/ORI/SW instead.
+
+     (c3) A block move of up to 3 additional bytes would be like (b1).
+
+     (c4) A block move of 8 bytes can use two LD.W/ST.W sequences or a single
+	  LD.D/ST.D sequence, and in these cases we've traditionally preferred
+	  the memory copy over the more bulky constant moves.  */
+  return size < 8;
+}
+
+/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
+   Assume that the areas do not overlap.  */
+
+static void
+loongarch_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
+{
+  HOST_WIDE_INT offset, delta;
+  unsigned HOST_WIDE_INT bits;
+  int i;
+  machine_mode mode;
+  rtx *regs;
+
+  bits = MIN (BITS_PER_WORD, MIN (MEM_ALIGN (src), MEM_ALIGN (dest)));
+
+  mode = int_mode_for_size (bits, 0).require ();
+  delta = bits / BITS_PER_UNIT;
+
+  /* Allocate a buffer for the temporary registers.  */
+  regs = XALLOCAVEC (rtx, length / delta);
+
+  /* Load as many BITS-sized chunks as possible.  Use a normal load if
+     the source has enough alignment, otherwise use left/right pairs.  */
+  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+    {
+      regs[i] = gen_reg_rtx (mode);
+      loongarch_emit_move (regs[i], adjust_address (src, mode, offset));
+    }
+
+  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+    loongarch_emit_move (adjust_address (dest, mode, offset), regs[i]);
+
+  /* Mop up any left-over bytes.  */
+  if (offset < length)
+    {
+      src = adjust_address (src, BLKmode, offset);
+      dest = adjust_address (dest, BLKmode, offset);
+      move_by_pieces (dest, src, length - offset,
+		      MIN (MEM_ALIGN (src), MEM_ALIGN (dest)),
+		      (enum memop_ret) 0);
+    }
+}
+
+/* Helper function for doing a loop-based block operation on memory
+   reference MEM.  Each iteration of the loop will operate on LENGTH
+   bytes of MEM.
+
+   Create a new base register for use within the loop and point it to
+   the start of MEM.  Create a new memory reference that uses this
+   register.  Store them in *LOOP_REG and *LOOP_MEM respectively.  */
+
+static void
+loongarch_adjust_block_mem (rtx mem, HOST_WIDE_INT length, rtx *loop_reg,
+			    rtx *loop_mem)
+{
+  *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
+
+  /* Although the new mem does not refer to a known location,
+     it does keep up to LENGTH bytes of alignment.  */
+  *loop_mem = change_address (mem, BLKmode, *loop_reg);
+  set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
+}
+
+/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER
+   bytes at a time.  LENGTH must be at least BYTES_PER_ITER.  Assume that
+   the memory regions do not overlap.  */
+
+static void
+loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
+			   HOST_WIDE_INT bytes_per_iter)
+{
+  rtx_code_label *label;
+  rtx src_reg, dest_reg, final_src, test;
+  HOST_WIDE_INT leftover;
+
+  leftover = length % bytes_per_iter;
+  length -= leftover;
+
+  /* Create registers and memory references for use within the loop.  */
+  loongarch_adjust_block_mem (src, bytes_per_iter, &src_reg, &src);
+  loongarch_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest);
+
+  /* Calculate the value that SRC_REG should have after the last iteration
+     of the loop.  */
+  final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length), 0,
+				   0, OPTAB_WIDEN);
+
+  /* Emit the start of the loop.  */
+  label = gen_label_rtx ();
+  emit_label (label);
+
+  /* Emit the loop body.  */
+  loongarch_block_move_straight (dest, src, bytes_per_iter);
+
+  /* Move on to the next block.  */
+  loongarch_emit_move (src_reg,
+		       plus_constant (Pmode, src_reg, bytes_per_iter));
+  loongarch_emit_move (dest_reg,
+		       plus_constant (Pmode, dest_reg, bytes_per_iter));
+
+  /* Emit the loop condition.  */
+  test = gen_rtx_NE (VOIDmode, src_reg, final_src);
+  if (Pmode == DImode)
+    emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label));
+  else
+    emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label));
+
+  /* Mop up any left-over bytes.  */
+  if (leftover)
+    loongarch_block_move_straight (dest, src, leftover);
+  else
+    /* Temporary fix for PR79150.  */
+    emit_insn (gen_nop ());
+}
+
+/* Expand a cpymemsi instruction, which copies LENGTH bytes from
+   memory reference SRC to memory reference DEST.  */
+
+bool
+loongarch_expand_block_move (rtx dest, rtx src, rtx length)
+{
+  int max_move_bytes = LARCH_MAX_MOVE_BYTES_STRAIGHT;
+
+  if (CONST_INT_P (length)
+      && INTVAL (length) <= loongarch_max_inline_memcpy_size)
+    {
+      if (INTVAL (length) <= max_move_bytes)
+	{
+	  loongarch_block_move_straight (dest, src, INTVAL (length));
+	  return true;
+	}
+      else if (optimize)
+	{
+	  loongarch_block_move_loop (dest, src, INTVAL (length),
+				     LARCH_MAX_MOVE_BYTES_PER_LOOP_ITER);
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Expand a QI or HI mode atomic memory operation.
+
+   GENERATOR contains a pointer to the gen_* function that generates
+   the SI mode underlying atomic operation using masks that we
+   calculate.
+
+   RESULT is the return register for the operation.  Its value is NULL
+   if unused.
+
+   MEM is the location of the atomic access.
+
+   OLDVAL is the first operand for the operation.
+
+   NEWVAL is the optional second operand for the operation.  Its value
+   is NULL if unused.  */
+
+void
+loongarch_expand_atomic_qihi (union loongarch_gen_fn_ptrs generator,
+			      rtx result, rtx mem, rtx oldval, rtx newval,
+			      rtx model)
+{
+  rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
+  rtx unshifted_mask_reg, mask, inverted_mask, si_op;
+  rtx res = NULL;
+  machine_mode mode;
+
+  mode = GET_MODE (mem);
+
+  /* Compute the address of the containing SImode value.  */
+  orig_addr = force_reg (Pmode, XEXP (mem, 0));
+  memsi_addr = loongarch_force_binary (Pmode, AND, orig_addr,
+				       force_reg (Pmode, GEN_INT (-4)));
+
+  /* Create a memory reference for it.  */
+  memsi = gen_rtx_MEM (SImode, memsi_addr);
+  set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
+  MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
+
+  /* Work out the byte offset of the QImode or HImode value,
+     counting from the least significant byte.  */
+  shift = loongarch_force_binary (Pmode, AND, orig_addr, GEN_INT (3));
+  /* Multiply by eight to convert the shift value from bytes to bits.  */
+  loongarch_emit_binary (ASHIFT, shift, shift, GEN_INT (3));
+
+  /* Make the final shift an SImode value, so that it can be used in
+     SImode operations.  */
+  shiftsi = force_reg (SImode, gen_lowpart (SImode, shift));
+
+  /* Set MASK to an inclusive mask of the QImode or HImode value.  */
+  unshifted_mask = GEN_INT (GET_MODE_MASK (mode));
+  unshifted_mask_reg = force_reg (SImode, unshifted_mask);
+  mask = loongarch_force_binary (SImode, ASHIFT, unshifted_mask_reg, shiftsi);
+
+  /* Compute the equivalent exclusive mask.  */
+  inverted_mask = gen_reg_rtx (SImode);
+  emit_insn (gen_rtx_SET (inverted_mask, gen_rtx_NOT (SImode, mask)));
+
+  /* Shift the old value into place.  */
+  if (oldval != const0_rtx)
+    {
+      oldval = convert_modes (SImode, mode, oldval, true);
+      oldval = force_reg (SImode, oldval);
+      oldval = loongarch_force_binary (SImode, ASHIFT, oldval, shiftsi);
+    }
+
+  /* Do the same for the new value.  */
+  if (newval && newval != const0_rtx)
+    {
+      newval = convert_modes (SImode, mode, newval, true);
+      newval = force_reg (SImode, newval);
+      newval = loongarch_force_binary (SImode, ASHIFT, newval, shiftsi);
+    }
+
+  /* Do the SImode atomic access.  */
+  if (result)
+    res = gen_reg_rtx (SImode);
+
+  if (newval)
+    si_op = generator.fn_7 (res, memsi, mask, inverted_mask, oldval, newval,
+			    model);
+  else if (result)
+    si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, model);
+  else
+    si_op = generator.fn_5 (memsi, mask, inverted_mask, oldval, model);
+
+  emit_insn (si_op);
+
+  if (result)
+    {
+      /* Shift and convert the result.  */
+      loongarch_emit_binary (AND, res, res, mask);
+      loongarch_emit_binary (LSHIFTRT, res, res, shiftsi);
+      loongarch_emit_move (result, gen_lowpart (GET_MODE (result), res));
+    }
+}
+
+/* Return true if (zero_extract OP WIDTH BITPOS) can be used as the
+   source of an "ext" instruction or the destination of an "ins"
+   instruction.  OP must be a register operand and the following
+   conditions must hold:
+
+   0 <= BITPOS < GET_MODE_BITSIZE (GET_MODE (op))
+   0 < WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+   0 < BITPOS + WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+
+   Also reject lengths equal to a word as they are better handled
+   by the move patterns.  */
+
+bool
+loongarch_use_ins_ext_p (rtx op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos)
+{
+  if (!register_operand (op, VOIDmode)
+      || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
+    return false;
+
+  if (!IN_RANGE (width, 1, GET_MODE_BITSIZE (GET_MODE (op)) - 1))
+    return false;
+
+  if (bitpos < 0 || bitpos + width > GET_MODE_BITSIZE (GET_MODE (op)))
+    return false;
+
+  return true;
+}
+
+/* Print the text for PRINT_OPERAND punctation character CH to FILE.
+   The punctuation characters are:
+
+   '.'	Print the name of the register with a hard-wired zero (zero or $r0).
+   '$'	Print the name of the stack pointer register (sp or $r3).
+
+   See also loongarch_init_print_operand_punct.  */
+
+static void
+loongarch_print_operand_punctuation (FILE *file, int ch)
+{
+  switch (ch)
+    {
+    case '.':
+      fputs (reg_names[GP_REG_FIRST + 0], file);
+      break;
+
+    case '$':
+      fputs (reg_names[STACK_POINTER_REGNUM], file);
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Initialize loongarch_print_operand_punct.  */
+
+static void
+loongarch_init_print_operand_punct (void)
+{
+  const char *p;
+
+  for (p = ".$"; *p; p++)
+    loongarch_print_operand_punct[(unsigned char) *p] = true;
+}
+
+/* PRINT_OPERAND prefix LETTER refers to the integer branch instruction
+   associated with condition CODE.  Print the condition part of the
+   opcode to FILE.  */
+
+static void
+loongarch_print_int_branch_condition (FILE *file, enum rtx_code code,
+				      int letter)
+{
+  switch (code)
+    {
+    case EQ:
+    case NE:
+    case GT:
+    case GE:
+    case LT:
+    case LE:
+    case GTU:
+    case GEU:
+    case LTU:
+    case LEU:
+      /* Conveniently, the LoongArch names for these conditions are the same
+	 as their RTL equivalents.  */
+      fputs (GET_RTX_NAME (code), file);
+      break;
+
+    default:
+      output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+      break;
+    }
+}
+
+/* Likewise floating-point branches.  */
+
+static void
+loongarch_print_float_branch_condition (FILE *file, enum rtx_code code,
+					int letter)
+{
+  switch (code)
+    {
+    case EQ:
+      fputs ("ceqz", file);
+      break;
+
+    case NE:
+      fputs ("cnez", file);
+      break;
+
+    default:
+      output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+      break;
+    }
+}
+
+/* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P.  */
+
+static bool
+loongarch_print_operand_punct_valid_p (unsigned char code)
+{
+  return loongarch_print_operand_punct[code];
+}
+
+/* Return true if a FENCE should be emitted to before a memory access to
+   implement the release portion of memory model MODEL.  */
+
+static bool
+loongarch_memmodel_needs_release_fence (enum memmodel model)
+{
+  switch (model)
+    {
+    case MEMMODEL_ACQ_REL:
+    case MEMMODEL_SEQ_CST:
+    case MEMMODEL_SYNC_SEQ_CST:
+    case MEMMODEL_RELEASE:
+    case MEMMODEL_SYNC_RELEASE:
+      return true;
+
+    case MEMMODEL_ACQUIRE:
+    case MEMMODEL_CONSUME:
+    case MEMMODEL_SYNC_ACQUIRE:
+    case MEMMODEL_RELAXED:
+      return false;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Implement TARGET_PRINT_OPERAND.  The LoongArch-specific operand codes are:
+
+   'X'	Print CONST_INT OP in hexadecimal format.
+   'x'	Print the low 16 bits of CONST_INT OP in hexadecimal format.
+   'd'	Print CONST_INT OP in decimal.
+   'm'	Print one less than CONST_INT OP in decimal.
+   'y'	Print exact log2 of CONST_INT OP in decimal.
+   'C'	Print the integer branch condition for comparison OP.
+   'N'	Print the inverse of the integer branch condition for comparison OP.
+   'F'	Print the FPU branch condition for comparison OP.
+   'W'	Print the inverse of the FPU branch condition for comparison OP.
+   'T'	Print 'f' for (eq:CC ...), 't' for (ne:CC ...),
+	      'z' for (eq:?I ...), 'n' for (ne:?I ...).
+   't'	Like 'T', but with the EQ/NE cases reversed
+   'Y'	Print loongarch_fp_conditions[INTVAL (OP)]
+   'Z'	Print OP and a comma for 8CC, otherwise print nothing.
+   'z'	Print $0 if OP is zero, otherwise print OP normally.
+   'b'	Print the address of a memory operand, without offset.
+   'V'	Print exact log2 of CONST_INT OP element 0 of a replicated
+	  CONST_VECTOR in decimal.
+   'A'	Print a _DB suffix if the memory model requires a release.
+   'G'	Print a DBAR insn if the memory model requires a release.
+   'i'	Print i if the operand is not a register.  */
+
+static void
+loongarch_print_operand (FILE *file, rtx op, int letter)
+{
+  enum rtx_code code;
+
+  if (loongarch_print_operand_punct_valid_p (letter))
+    {
+      loongarch_print_operand_punctuation (file, letter);
+      return;
+    }
+
+  gcc_assert (op);
+  code = GET_CODE (op);
+
+  switch (letter)
+    {
+    case 'X':
+      if (CONST_INT_P (op))
+	fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'x':
+      if (CONST_INT_P (op))
+	fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'd':
+      if (CONST_INT_P (op))
+	fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'm':
+      if (CONST_INT_P (op))
+	fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'y':
+      if (CONST_INT_P (op))
+	{
+	  int val = exact_log2 (INTVAL (op));
+	  if (val != -1)
+	    fprintf (file, "%d", val);
+	  else
+	    output_operand_lossage ("invalid use of '%%%c'", letter);
+	}
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'V':
+      if (GET_CODE (op) == CONST_VECTOR)
+	{
+	  machine_mode mode = GET_MODE_INNER (GET_MODE (op));
+	  unsigned HOST_WIDE_INT val = UINTVAL (CONST_VECTOR_ELT (op, 0));
+	  int vlog2 = exact_log2 (val & GET_MODE_MASK (mode));
+	  if (vlog2 != -1)
+	    fprintf (file, "%d", vlog2);
+	  else
+	    output_operand_lossage ("invalid use of '%%%c'", letter);
+	}
+      else
+	output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'C':
+      loongarch_print_int_branch_condition (file, code, letter);
+      break;
+
+    case 'N':
+      loongarch_print_int_branch_condition (file, reverse_condition (code),
+					    letter);
+      break;
+
+    case 'F':
+      loongarch_print_float_branch_condition (file, code, letter);
+      break;
+
+    case 'W':
+      loongarch_print_float_branch_condition (file, reverse_condition (code),
+					      letter);
+      break;
+
+    case 'T':
+    case 't':
+      {
+	int truth = (code == NE) == (letter == 'T');
+	fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file);
+      }
+      break;
+
+    case 'Y':
+      if (code == CONST_INT
+	  && UINTVAL (op) < ARRAY_SIZE (loongarch_fp_conditions))
+	fputs (loongarch_fp_conditions[UINTVAL (op)], file);
+      else
+	output_operand_lossage ("'%%%c' is not a valid operand prefix",
+				letter);
+      break;
+
+    case 'Z':
+      loongarch_print_operand (file, op, 0);
+      fputc (',', file);
+      break;
+
+    case 'A':
+      if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op)))
+	fputs ("_db", file);
+      break;
+
+    case 'G':
+      if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op)))
+	fputs ("dbar\t0", file);
+      break;
+
+    case 'i':
+      if (code != REG)
+	fputs ("i", file);
+      break;
+
+    default:
+      switch (code)
+	{
+	case REG:
+	  {
+	    unsigned int regno = REGNO (op);
+	    if (letter && letter != 'z')
+	      output_operand_lossage ("invalid use of '%%%c'", letter);
+	    fprintf (file, "%s", reg_names[regno]);
+	  }
+	  break;
+
+	case MEM:
+	  if (letter == 'D')
+	    output_address (GET_MODE (op),
+			    plus_constant (Pmode, XEXP (op, 0), 4));
+	  else if (letter == 'b')
+	    {
+	      gcc_assert (REG_P (XEXP (op, 0)));
+	      loongarch_print_operand (file, XEXP (op, 0), 0);
+	    }
+	  else if (letter && letter != 'z')
+	    output_operand_lossage ("invalid use of '%%%c'", letter);
+	  else
+	    output_address (GET_MODE (op), XEXP (op, 0));
+	  break;
+
+	default:
+	  if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
+	    fputs (reg_names[GP_REG_FIRST], file);
+	  else if (letter && letter != 'z')
+	    output_operand_lossage ("invalid use of '%%%c'", letter);
+	  else
+	    output_addr_const (file, loongarch_strip_unspec_address (op));
+	  break;
+	}
+    }
+}
+
+/* Implement TARGET_PRINT_OPERAND_ADDRESS.  */
+
+static void
+loongarch_print_operand_address (FILE *file, machine_mode /* mode  */, rtx x)
+{
+  struct loongarch_address_info addr;
+
+  if (loongarch_classify_address (&addr, x, word_mode, true))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+	fprintf (file, "%s,", reg_names[REGNO (addr.reg)]);
+	loongarch_print_operand (file, addr.offset, 0);
+	return;
+
+      case ADDRESS_CONST_INT:
+	fprintf (file, "%s,", reg_names[GP_REG_FIRST]);
+	output_addr_const (file, x);
+	return;
+
+      case ADDRESS_SYMBOLIC:
+	output_addr_const (file, loongarch_strip_unspec_address (x));
+	return;
+      }
+  if (GET_CODE (x) == CONST_INT)
+    output_addr_const (file, x);
+  else
+    gcc_unreachable ();
+}
+
+/* Implement TARGET_ASM_SELECT_RTX_SECTION.  */
+
+static section *
+loongarch_select_rtx_section (machine_mode mode, rtx x,
+			      unsigned HOST_WIDE_INT align)
+{
+  /* ??? Consider using mergeable small data sections.  */
+  if (loongarch_rtx_constant_in_small_data_p (mode))
+    return get_named_section (NULL, ".sdata", 0);
+
+  return default_elf_select_rtx_section (mode, x, align);
+}
+
+/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
+
+   The complication here is that, with the combination
+   !TARGET_ABSOLUTE_ABICALLS , jump tables will use
+   absolute addresses, and should therefore not be included in the
+   read-only part of a DSO.  Handle such cases by selecting a normal
+   data section instead of a read-only one.  The logic apes that in
+   default_function_rodata_section.  */
+
+static section *
+loongarch_function_rodata_section (tree decl, bool)
+{
+  return default_function_rodata_section (decl, false);
+}
+
+/* Implement TARGET_IN_SMALL_DATA_P.  */
+
+static bool
+loongarch_in_small_data_p (const_tree decl)
+{
+  int size;
+
+  if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
+    return false;
+
+  if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+    {
+      const char *name;
+
+      /* Reject anything that isn't in a known small-data section.  */
+      name = DECL_SECTION_NAME (decl);
+      if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0)
+	return false;
+
+      /* If a symbol is defined externally, the assembler will use the
+	 usual -G rules when deciding how to implement macros.  */
+      if (!DECL_EXTERNAL (decl))
+	return true;
+    }
+
+  /* We have traditionally not treated zero-sized objects as small data,
+     so this is now effectively part of the ABI.  */
+  size = int_size_in_bytes (TREE_TYPE (decl));
+  return size > 0 && size <= g_switch_value;
+}
+
+/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P.  We don't want to use
+   anchors for small data: the GP register acts as an anchor in that
+   case.  We also don't want to use them for PC-relative accesses,
+   where the PC acts as an anchor.  */
+
+static bool
+loongarch_use_anchors_for_symbol_p (const_rtx symbol)
+{
+  return default_use_anchors_for_symbol_p (symbol);
+}
+
+/* The LoongArch debug format wants all automatic variables and arguments
+   to be in terms of the virtual frame pointer (stack pointer before
+   any adjustment in the function), while the LoongArch linker wants
+   the frame pointer to be the stack pointer after the initial
+   adjustment.  So, we do the adjustment here.  The arg pointer (which
+   is eliminated) points to the virtual frame pointer, while the frame
+   pointer (which may be eliminated) points to the stack pointer after
+   the initial adjustments.  */
+
+HOST_WIDE_INT
+loongarch_debugger_offset (rtx addr, HOST_WIDE_INT offset)
+{
+  rtx offset2 = const0_rtx;
+  rtx reg = eliminate_constant_term (addr, &offset2);
+
+  if (offset == 0)
+    offset = INTVAL (offset2);
+
+  if (reg == stack_pointer_rtx
+      || reg == frame_pointer_rtx
+      || reg == hard_frame_pointer_rtx)
+    {
+      offset -= cfun->machine->frame.total_size;
+      if (reg == hard_frame_pointer_rtx)
+	offset += cfun->machine->frame.hard_frame_pointer_offset;
+    }
+
+  return offset;
+}
+
+/* Implement ASM_OUTPUT_EXTERNAL.  */
+
+void
+loongarch_output_external (FILE *file, tree decl, const char *name)
+{
+  default_elf_asm_output_external (file, decl, name);
+
+  /* We output the name if and only if TREE_SYMBOL_REFERENCED is
+     set in order to avoid putting out names that are never really
+     used.  */
+  if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
+    {
+      if (loongarch_in_small_data_p (decl))
+	{
+	  /* When using assembler macros, emit .extern directives for
+	     all small-data externs so that the assembler knows how
+	     big they are.
+
+	     In most cases it would be safe (though pointless) to emit
+	     .externs for other symbols too.  One exception is when an
+	     object is within the -G limit but declared by the user to
+	     be in a section other than .sbss or .sdata.  */
+	  fputs ("\t.extern\t", file);
+	  assemble_name (file, name);
+	  fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+		   int_size_in_bytes (TREE_TYPE (decl)));
+	}
+    }
+}
+
+/* Implement TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
+
+static void ATTRIBUTE_UNUSED
+loongarch_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  switch (size)
+    {
+    case 4:
+      fputs ("\t.dtprelword\t", file);
+      break;
+
+    case 8:
+      fputs ("\t.dtpreldword\t", file);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+  output_addr_const (file, x);
+  fputs ("+0x8000", file);
+}
+
+/* Implement TARGET_DWARF_FRAME_REG_MODE.  */
+
+static machine_mode
+loongarch_dwarf_frame_reg_mode (int regno)
+{
+  machine_mode mode = default_dwarf_frame_reg_mode (regno);
+
+  return mode;
+}
+
+/* Implement ASM_OUTPUT_ASCII.  */
+
+void
+loongarch_output_ascii (FILE *stream, const char *string, size_t len)
+{
+  size_t i;
+  int cur_pos;
+
+  cur_pos = 17;
+  fprintf (stream, "\t.ascii\t\"");
+  for (i = 0; i < len; i++)
+    {
+      int c;
+
+      c = (unsigned char) string[i];
+      if (ISPRINT (c))
+	{
+	  if (c == '\\' || c == '\"')
+	    {
+	      putc ('\\', stream);
+	      cur_pos++;
+	    }
+	  putc (c, stream);
+	  cur_pos++;
+	}
+      else
+	{
+	  fprintf (stream, "\\%03o", c);
+	  cur_pos += 4;
+	}
+
+      if (cur_pos > 72 && i + 1 < len)
+	{
+	  cur_pos = 17;
+	  fprintf (stream, "\"\n\t.ascii\t\"");
+	}
+    }
+  fprintf (stream, "\"\n");
+}
+
+/* Emit either a label, .comm, or .lcomm directive.  When using assembler
+   macros, mark the symbol as written so that loongarch_asm_output_external
+   won't emit an .extern for it.  STREAM is the output file, NAME is the
+   name of the symbol, INIT_STRING is the string that should be written
+   before the symbol and FINAL_STRING is the string that should be
+   written after it.  FINAL_STRING is a printf format that consumes the
+   remaining arguments.  */
+
+void
+loongarch_declare_object (FILE *stream, const char *name,
+			  const char *init_string, const char *final_string,
+			  ...)
+{
+  va_list ap;
+
+  fputs (init_string, stream);
+  assemble_name (stream, name);
+  va_start (ap, final_string);
+  vfprintf (stream, final_string, ap);
+  va_end (ap);
+
+  tree name_tree = get_identifier (name);
+  TREE_ASM_WRITTEN (name_tree) = 1;
+}
+
+/* Declare a common object of SIZE bytes using asm directive INIT_STRING.
+   NAME is the name of the object and ALIGN is the required alignment
+   in bytes.  TAKES_ALIGNMENT_P is true if the directive takes a third
+   alignment argument.  */
+
+void
+loongarch_declare_common_object (FILE *stream, const char *name,
+				 const char *init_string,
+				 unsigned HOST_WIDE_INT size,
+				 unsigned int align, bool takes_alignment_p)
+{
+  if (!takes_alignment_p)
+    {
+      size += (align / BITS_PER_UNIT) - 1;
+      size -= size % (align / BITS_PER_UNIT);
+      loongarch_declare_object (stream, name, init_string,
+				"," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size);
+    }
+  else
+    loongarch_declare_object (stream, name, init_string,
+			      "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n", size,
+			      align / BITS_PER_UNIT);
+}
+
+/* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON.  This is usually the same as the
+   elfos.h version.  */
+
+void
+loongarch_output_aligned_decl_common (FILE *stream,
+				      tree decl ATTRIBUTE_UNUSED,
+				      const char *name,
+				      unsigned HOST_WIDE_INT size,
+				      unsigned int align)
+{
+  loongarch_declare_common_object (stream, name, "\n\t.comm\t", size, align,
+				   true);
+}
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+extern int size_directive_output;
+
+/* Implement ASM_DECLARE_OBJECT_NAME.  This is like most of the standard ELF
+   definitions except that it uses loongarch_declare_object to emit the label.
+*/
+
+void
+loongarch_declare_object_name (FILE *stream, const char *name,
+			       tree decl ATTRIBUTE_UNUSED)
+{
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+#ifdef USE_GNU_UNIQUE_OBJECT
+  /* As in elfos.h.  */
+  if (USE_GNU_UNIQUE_OBJECT && DECL_ONE_ONLY (decl)
+      && (!DECL_ARTIFICIAL (decl) || !TREE_READONLY (decl)))
+    ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "gnu_unique_object");
+  else
+#endif
+    ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
+
+  size_directive_output = 0;
+  if (!flag_inhibit_size_directive && DECL_SIZE (decl))
+    {
+      HOST_WIDE_INT size;
+
+      size_directive_output = 1;
+      size = int_size_in_bytes (TREE_TYPE (decl));
+      ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+    }
+
+  loongarch_declare_object (stream, name, "", ":\n");
+}
+
+/* Implement ASM_FINISH_DECLARE_OBJECT.  This is generic ELF stuff.  */
+
+void
+loongarch_finish_declare_object (FILE *stream, tree decl, int top_level,
+				 int at_end)
+{
+  const char *name;
+
+  name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+  if (!flag_inhibit_size_directive
+      && DECL_SIZE (decl) != 0
+      && !at_end
+      && top_level
+      && DECL_INITIAL (decl) == error_mark_node
+      && !size_directive_output)
+    {
+      HOST_WIDE_INT size;
+
+      size_directive_output = 1;
+      size = int_size_in_bytes (TREE_TYPE (decl));
+      ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+    }
+}
+#endif
+
+/* Mark text contents as code or data, mainly for the purpose of correct
+   disassembly.  Emit a local symbol and set its type appropriately for
+   that purpose.  */
+
+void
+loongarch_set_text_contents_type (FILE *file ATTRIBUTE_UNUSED,
+				  const char *prefix ATTRIBUTE_UNUSED,
+				  unsigned long num ATTRIBUTE_UNUSED,
+				  bool function_p ATTRIBUTE_UNUSED)
+{
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+  char buf[(sizeof (num) * 10) / 4 + 2];
+  const char *fnname;
+  char *sname;
+  rtx symbol;
+
+  sprintf (buf, "%lu", num);
+  symbol = XEXP (DECL_RTL (current_function_decl), 0);
+  fnname = targetm.strip_name_encoding (XSTR (symbol, 0));
+  sname = ACONCAT ((prefix, fnname, "_", buf, NULL));
+
+  ASM_OUTPUT_TYPE_DIRECTIVE (file, sname, function_p ? "function" : "object");
+  assemble_name (file, sname);
+  fputs (":\n", file);
+#endif
+}
+
+/* Implement TARGET_ASM_FILE_START.  */
+
+static void
+loongarch_file_start (void)
+{
+  default_file_start ();
+}
+
+
+/* Implement TARGET_FRAME_POINTER_REQUIRED.  */
+
+static bool
+loongarch_frame_pointer_required (void)
+{
+  /* If the function contains dynamic stack allocations, we need to
+     use the frame pointer to access the static parts of the frame.  */
+  if (cfun->calls_alloca)
+    return true;
+
+  return false;
+}
+
+/* Implement TARGET_CAN_ELIMINATE.  Make sure that we're not trying
+   to eliminate to the wrong hard frame pointer.  */
+
+static bool
+loongarch_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+  return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM);
+}
+
+/* Implement RETURN_ADDR_RTX.  We do not support moving back to a
+   previous frame.  */
+
+rtx
+loongarch_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+  if (count != 0)
+    return const0_rtx;
+
+  return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM);
+}
+
+/* Emit code to change the current function's return address to
+   ADDRESS.  SCRATCH is available as a scratch register, if needed.
+   ADDRESS and SCRATCH are both word-mode GPRs.  */
+
+void
+loongarch_set_return_address (rtx address, rtx scratch)
+{
+  rtx slot_address;
+
+  gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM));
+
+  if (frame_pointer_needed)
+    slot_address = loongarch_add_offset (scratch, hard_frame_pointer_rtx,
+					 -UNITS_PER_WORD);
+  else
+    slot_address = loongarch_add_offset (scratch, stack_pointer_rtx,
+					 cfun->machine->frame.gp_sp_offset);
+
+  loongarch_emit_move (gen_frame_mem (GET_MODE (address), slot_address),
+		       address);
+}
+
+/* Implement ASM_DECLARE_FUNCTION_NAME.  */
+
+void
+loongarch_declare_function_name (FILE *stream ATTRIBUTE_UNUSED,
+				 const char *name,
+				 tree fndecl ATTRIBUTE_UNUSED)
+{
+  loongarch_start_function_definition (name);
+}
+
+/* Return true if register REGNO can store a value of mode MODE.
+   The result of this function is cached in loongarch_hard_regno_mode_ok.  */
+
+static bool
+loongarch_hard_regno_mode_ok_uncached (unsigned int regno, machine_mode mode)
+{
+  unsigned int size;
+  enum mode_class mclass;
+
+  if (mode == FCCmode)
+    return FCC_REG_P (regno);
+
+  size = GET_MODE_SIZE (mode);
+  mclass = GET_MODE_CLASS (mode);
+
+  if (GP_REG_P (regno))
+    return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
+
+  if (FP_REG_P (regno)
+      && (((regno - FP_REG_FIRST) % MAX_FPRS_PER_FMT) == 0
+	  || (MIN_FPRS_PER_FMT == 1 && size <= UNITS_PER_FPREG)))
+    {
+      if (mclass == MODE_FLOAT
+	  || mclass == MODE_COMPLEX_FLOAT
+	  || mclass == MODE_VECTOR_FLOAT)
+	return size <= UNITS_PER_FPVALUE;
+
+      /* Allow integer modes that fit into a single register.  We need
+	 to put integers into FPRs when using instructions like CVT
+	 and TRUNC.  There's no point allowing sizes smaller than a word,
+	 because the FPU has no appropriate load/store instructions.  */
+      if (mclass == MODE_INT)
+	return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
+    }
+
+  return false;
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK.  */
+
+static bool
+loongarch_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
+{
+  return loongarch_hard_regno_mode_ok_p[mode][regno];
+}
+
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
+
+bool
+loongarch_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+				unsigned int new_reg ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+/* Implement TARGET_HARD_REGNO_NREGS.  */
+
+static unsigned int
+loongarch_hard_regno_nregs (unsigned int regno, machine_mode mode)
+{
+  if (FCC_REG_P (regno))
+    /* The size of FP status registers is always 4, because they only hold
+       FCCmode values, and FCCmode is always considered to be 4 bytes wide.  */
+    return (GET_MODE_SIZE (mode) + 3) / 4;
+
+  if (FP_REG_P (regno))
+    return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
+
+  /* All other registers are word-sized.  */
+  return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Implement CLASS_MAX_NREGS, taking the maximum of the cases
+   in loongarch_hard_regno_nregs.  */
+
+int
+loongarch_class_max_nregs (enum reg_class rclass, machine_mode mode)
+{
+  int size;
+  HARD_REG_SET left;
+
+  size = 0x8000;
+  left = reg_class_contents[rclass];
+  if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FCC_REGS]))
+    {
+      if (loongarch_hard_regno_mode_ok (FCC_REG_FIRST, mode))
+	size = MIN (size, 4);
+
+      left &= ~reg_class_contents[FCC_REGS];
+    }
+  if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FP_REGS]))
+    {
+      if (loongarch_hard_regno_mode_ok (FP_REG_FIRST, mode))
+	size = MIN (size, UNITS_PER_FPREG);
+
+      left &= ~reg_class_contents[FP_REGS];
+    }
+  if (!hard_reg_set_empty_p (left))
+    size = MIN (size, UNITS_PER_WORD);
+  return (GET_MODE_SIZE (mode) + size - 1) / size;
+}
+
+/* Implement TARGET_CAN_CHANGE_MODE_CLASS.  */
+
+static bool
+loongarch_can_change_mode_class (machine_mode from, machine_mode to,
+				 reg_class_t rclass)
+{
+  /* Allow conversions between different Loongson integer vectors,
+     and between those vectors and DImode.  */
+  if (GET_MODE_SIZE (from) == 8 && GET_MODE_SIZE (to) == 8
+      && INTEGRAL_MODE_P (from) && INTEGRAL_MODE_P (to))
+    return true;
+
+  return !reg_classes_intersect_p (FP_REGS, rclass);
+}
+
+/* Return true if moves in mode MODE can use the FPU's fmov.fmt instruction,
+*/
+
+static bool
+loongarch_mode_ok_for_mov_fmt_p (machine_mode mode)
+{
+  switch (mode)
+    {
+    case E_FCCmode:
+    case E_SFmode:
+      return TARGET_HARD_FLOAT;
+
+    case E_DFmode:
+      return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
+
+    case E_V2SFmode:
+      return 0;
+
+    default:
+      return 0;
+    }
+}
+
+/* Implement TARGET_MODES_TIEABLE_P.  */
+
+static bool
+loongarch_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+  /* FPRs allow no mode punning, so it's not worth tying modes if we'd
+     prefer to put one of them in FPRs.  */
+  return (mode1 == mode2
+	  || (!loongarch_mode_ok_for_mov_fmt_p (mode1)
+	      && !loongarch_mode_ok_for_mov_fmt_p (mode2)));
+}
+
+/* Implement TARGET_PREFERRED_RELOAD_CLASS.  */
+
+static reg_class_t
+loongarch_preferred_reload_class (rtx x, reg_class_t rclass)
+{
+  if (reg_class_subset_p (FP_REGS, rclass)
+      && loongarch_mode_ok_for_mov_fmt_p (GET_MODE (x)))
+    return FP_REGS;
+
+  if (reg_class_subset_p (GR_REGS, rclass))
+    rclass = GR_REGS;
+
+  return rclass;
+}
+
+/* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
+   Return a "canonical" class to represent it in later calculations.  */
+
+static reg_class_t
+loongarch_canonicalize_move_class (reg_class_t rclass)
+{
+  if (reg_class_subset_p (rclass, GENERAL_REGS))
+    rclass = GENERAL_REGS;
+
+  return rclass;
+}
+
+/* Return the cost of moving a value from a register of class FROM to a GPR.
+   Return 0 for classes that are unions of other classes handled by this
+   function.  */
+
+static int
+loongarch_move_to_gpr_cost (reg_class_t from)
+{
+  switch (from)
+    {
+    case GENERAL_REGS:
+      /* MOVE macro.  */
+      return 2;
+
+    case FP_REGS:
+      /* MOVFR2GR, etc.  */
+      return 4;
+
+    default:
+      return 0;
+    }
+}
+
+/* Return the cost of moving a value from a GPR to a register of class TO.
+   Return 0 for classes that are unions of other classes handled by this
+   function.  */
+
+static int
+loongarch_move_from_gpr_cost (reg_class_t to)
+{
+  switch (to)
+    {
+    case GENERAL_REGS:
+      /*MOVE macro.  */
+      return 2;
+
+    case FP_REGS:
+      /* MOVGR2FR, etc.  */
+      return 4;
+
+    default:
+      return 0;
+    }
+}
+
+/* Implement TARGET_REGISTER_MOVE_COST.  Return 0 for classes that are the
+   maximum of the move costs for subclasses; regclass will work out
+   the maximum for us.  */
+
+static int
+loongarch_register_move_cost (machine_mode mode, reg_class_t from,
+			      reg_class_t to)
+{
+  reg_class_t dregs;
+  int cost1, cost2;
+
+  from = loongarch_canonicalize_move_class (from);
+  to = loongarch_canonicalize_move_class (to);
+
+  /* Handle moves that can be done without using general-purpose registers.  */
+  if (from == FP_REGS)
+    {
+      if (to == FP_REGS && loongarch_mode_ok_for_mov_fmt_p (mode))
+	/* FMOV.FMT.  */
+	return 4;
+    }
+
+  /* Handle cases in which only one class deviates from the ideal.  */
+  dregs = GENERAL_REGS;
+  if (from == dregs)
+    return loongarch_move_from_gpr_cost (to);
+  if (to == dregs)
+    return loongarch_move_to_gpr_cost (from);
+
+  /* Handles cases that require a GPR temporary.  */
+  cost1 = loongarch_move_to_gpr_cost (from);
+  if (cost1 != 0)
+    {
+      cost2 = loongarch_move_from_gpr_cost (to);
+      if (cost2 != 0)
+	return cost1 + cost2;
+    }
+
+  return 0;
+}
+
+/* Implement TARGET_MEMORY_MOVE_COST.  */
+
+static int
+loongarch_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in)
+{
+  return (loongarch_cost->memory_latency
+	  + memory_move_secondary_cost (mode, rclass, in));
+}
+
+/* Implement TARGET_SECONDARY_MEMORY_NEEDED.
+
+   This can be achieved using MOVFRH2GR.S/MOVGR2FRH.W when these instructions
+   are available but otherwise moves must go via memory.  Using
+   MOVGR2FR/MOVFR2GR to access the lower-half of these registers would require
+   a forbidden single-precision access.  We require all double-word moves to
+   use memory because adding even and odd floating-point registers classes
+   would have a significant impact on the backend.  */
+
+static bool
+loongarch_secondary_memory_needed (machine_mode mode ATTRIBUTE_UNUSED,
+				   reg_class_t class1, reg_class_t class2)
+{
+  /* Ignore spilled pseudos.  */
+  if (lra_in_progress && (class1 == NO_REGS || class2 == NO_REGS))
+    return false;
+
+  return false;
+}
+
+/* Return the register class required for a secondary register when
+   copying between one of the registers in RCLASS and value X, which
+   has mode MODE.  X is the source of the move if IN_P, otherwise it
+   is the destination.  Return NO_REGS if no secondary register is
+   needed.  */
+
+static reg_class_t
+loongarch_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x,
+			    reg_class_t rclass, machine_mode mode,
+			    secondary_reload_info *sri ATTRIBUTE_UNUSED)
+{
+  int regno;
+
+  regno = true_regnum (x);
+
+  /* Copying from accumulator registers to anywhere other than a general
+     register requires a temporary general register.  */
+  if (reg_class_subset_p (rclass, FP_REGS))
+    {
+      if (regno < 0
+	  || (MEM_P (x)
+	      && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8)))
+	/* In this case we can use fld.s, fst.s, fld.d or fst.d.  */
+	return NO_REGS;
+
+      if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+	/* In this case we can use movgr2fr.s, movfr2gr.s, movgr2fr.d or
+	 * movfr2gr.d.  */
+	return NO_REGS;
+
+      if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (mode, x))
+	/* We can force the constant to memory and use fld.s
+	   and fld.d.  As above, we will use pairs of lwc1s if
+	   ldc1 is not supported.  */
+	return NO_REGS;
+
+      if (FP_REG_P (regno) && loongarch_mode_ok_for_mov_fmt_p (mode))
+	/* In this case we can use fmov.{s/d}.  */
+	return NO_REGS;
+
+      /* Otherwise, we need to reload through an integer register.  */
+      return GR_REGS;
+    }
+  if (FP_REG_P (regno))
+    return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
+
+  return NO_REGS;
+}
+
+/* Implement TARGET_VALID_POINTER_MODE.  */
+
+static bool
+loongarch_valid_pointer_mode (scalar_int_mode mode)
+{
+  return mode == SImode || (TARGET_64BIT && mode == DImode);
+}
+
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.  */
+
+static bool
+loongarch_scalar_mode_supported_p (scalar_mode mode)
+{
+  if (ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
+    return true;
+
+  return default_scalar_mode_supported_p (mode);
+}
+
+/* Implement TARGET_VECTORIZE_PREFERRED_SIMD_MODE.  */
+
+static machine_mode
+loongarch_preferred_simd_mode (scalar_mode mode ATTRIBUTE_UNUSED)
+{
+  return word_mode;
+}
+
+/* Return the length of INSN.  LENGTH is the initial length computed by
+   attributes in the machine-description file.  */
+
+int
+loongarch_adjust_insn_length (rtx_insn *insn, int length)
+{
+  /* loongarch.md uses MAX_PIC_BRANCH_LENGTH as a placeholder for the length
+     of a PIC long-branch sequence.  Substitute the correct value.  */
+  if (length == MAX_PIC_BRANCH_LENGTH
+      && JUMP_P (insn)
+      && INSN_CODE (insn) >= 0
+      && get_attr_type (insn) == TYPE_BRANCH)
+    {
+      /* Add the branch-over instruction and its delay slot, if this
+	 is a conditional branch.  */
+      length = simplejump_p (insn) ? 0 : 8;
+
+      /* Add the length of an indirect jump, ignoring the delay slot.  */
+      length += 4;
+    }
+
+  /* A unconditional jump has an unfilled delay slot if it is not part
+     of a sequence.  A conditional jump normally has a delay slot.  */
+  if (CALL_P (insn) || (JUMP_P (insn)))
+    length += 4;
+
+  /* See how many nops might be needed to avoid hardware hazards.  */
+  if (!cfun->machine->ignore_hazard_length_p
+      && INSN_P (insn)
+      && INSN_CODE (insn) >= 0)
+    switch (get_attr_hazard (insn))
+      {
+      case HAZARD_NONE:
+	break;
+
+      case HAZARD_DELAY:
+      case HAZARD_FORBIDDEN_SLOT:
+	/* nop insn length is 4 bytes.  */
+	length += 4;
+	break;
+      }
+
+  return length;
+}
+
+/* Return the assembly code for INSN, which has the operands given by
+   OPERANDS, and which branches to OPERANDS[0] if some condition is true.
+   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0]
+   is in range of a direct branch.  BRANCH_IF_FALSE is an inverted
+   version of BRANCH_IF_TRUE.  */
+
+const char *
+loongarch_output_conditional_branch (rtx_insn *insn, rtx *operands,
+				     const char *branch_if_true,
+				     const char *branch_if_false)
+{
+  unsigned int length;
+  rtx taken;
+
+  gcc_assert (LABEL_P (operands[0]));
+
+  length = get_attr_length (insn);
+  if (length <= 12)
+    {
+      return branch_if_true;
+    }
+
+  /* Generate a reversed branch around a direct jump.  */
+  rtx_code_label *not_taken = gen_label_rtx ();
+  taken = operands[0];
+
+  /* Generate the reversed branch to NOT_TAKEN.  */
+  operands[0] = not_taken;
+  output_asm_insn (branch_if_false, operands);
+
+  /* If INSN has a delay slot, we must provide delay slots for both the
+     branch to NOT_TAKEN and the conditional jump.  We must also ensure
+     that INSN's delay slot is executed in the appropriate cases.  */
+  if (final_sequence)
+    {
+      /* This first delay slot will always be executed, so use INSN's
+	 delay slot if is not annulled.  */
+      if (!INSN_ANNULLED_BRANCH_P (insn))
+	{
+	  final_scan_insn (final_sequence->insn (1), asm_out_file, optimize, 1,
+			   NULL);
+	  final_sequence->insn (1)->set_deleted ();
+	}
+      fprintf (asm_out_file, "\n");
+    }
+
+  output_asm_insn ("b\t%0", &taken);
+
+  /* Now deal with its delay slot; see above.  */
+  if (final_sequence)
+    {
+      /* This delay slot will only be executed if the branch is taken.
+	 Use INSN's delay slot if is annulled.  */
+      if (INSN_ANNULLED_BRANCH_P (insn))
+	{
+	  final_scan_insn (final_sequence->insn (1), asm_out_file, optimize, 1,
+			   NULL);
+	  final_sequence->insn (1)->set_deleted ();
+	}
+      fprintf (asm_out_file, "\n");
+    }
+
+  /* Output NOT_TAKEN.  */
+  targetm.asm_out.internal_label (asm_out_file, "L",
+				  CODE_LABEL_NUMBER (not_taken));
+  return "";
+}
+
+/* Return the assembly code for INSN, which branches to OPERANDS[0]
+   if some equality condition is true.  The condition is given by
+   OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
+   OPERANDS[1].  OPERANDS[2] is the comparison's first operand;
+   OPERANDS[3] is the second operand and may be zero or a register.  */
+
+const char *
+loongarch_output_equal_conditional_branch (rtx_insn *insn, rtx *operands,
+					   bool inverted_p)
+{
+  const char *branch[2];
+  if (operands[3] == const0_rtx)
+    {
+      branch[!inverted_p] = LARCH_BRANCH ("b%C1z", "%2,%0");
+      branch[inverted_p] = LARCH_BRANCH ("b%N1z", "%2,%0");
+    }
+  else
+    {
+      branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,%z3,%0");
+      branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,%z3,%0");
+    }
+
+  return loongarch_output_conditional_branch (insn, operands, branch[1],
+					      branch[0]);
+}
+
+/* Return the assembly code for INSN, which branches to OPERANDS[0]
+   if some ordering condition is true.  The condition is given by
+   OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
+   OPERANDS[1].  OPERANDS[2] is the comparison's first operand;
+   OPERANDS[3] is the second operand and may be zero or a register.  */
+
+const char *
+loongarch_output_order_conditional_branch (rtx_insn *insn, rtx *operands,
+					   bool inverted_p)
+{
+  const char *branch[2];
+
+  /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true.
+     Make BRANCH[0] branch on the inverse condition.  */
+  if (operands[3] != const0_rtx)
+    {
+      /* Handle degenerate cases that should not, but do, occur.  */
+      if (REGNO (operands[2]) == REGNO (operands[3]))
+	{
+	  switch (GET_CODE (operands[1]))
+	    {
+	    case LT:
+	    case LTU:
+	      inverted_p = !inverted_p;
+	      /* Fall through.  */
+	    case GE:
+	    case GEU:
+	      branch[!inverted_p] = LARCH_BRANCH ("b", "%0");
+	      branch[inverted_p] = "\t# branch never";
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
+      else
+	{
+	  switch (GET_CODE (operands[1]))
+	    {
+	    case LE:
+	    case LEU:
+	    case GT:
+	    case GTU:
+	    case LT:
+	    case LTU:
+	    case GE:
+	    case GEU:
+	      branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,%3,%0");
+	      branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,%3,%0");
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
+    }
+  else
+    {
+      switch (GET_CODE (operands[1]))
+	{
+	  /* These cases are equivalent to comparisons against zero.  */
+	case LEU:
+	case GTU:
+	case LTU:
+	case GEU:
+	case LE:
+	case GT:
+	case LT:
+	case GE:
+	  branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,$r0,%0");
+	  branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,$r0,%0");
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+    }
+  return loongarch_output_conditional_branch (insn, operands, branch[1],
+					      branch[0]);
+}
+
+/* Return the assembly code for DIV.{W/D} instruction DIVISION, which has
+   the operands given by OPERANDS.  Add in a divide-by-zero check if needed.
+   */
+
+const char *
+loongarch_output_division (const char *division, rtx *operands)
+{
+  const char *s;
+
+  s = division;
+  if (TARGET_CHECK_ZERO_DIV)
+    {
+      output_asm_insn (s, operands);
+      s = "bne\t%2,%.,1f\n\tbreak\t7\n1:";
+    }
+  return s;
+}
+
+/* Implement TARGET_SCHED_ADJUST_COST.  We assume that anti and output
+   dependencies have no cost.  */
+
+static int
+loongarch_adjust_cost (rtx_insn *, int dep_type, rtx_insn *, int cost,
+		       unsigned int)
+{
+  if (dep_type != 0 && (dep_type != REG_DEP_OUTPUT))
+    return 0;
+  return cost;
+}
+
+/* Return the number of instructions that can be issued per cycle.  */
+
+static int
+loongarch_issue_rate (void)
+{
+  if ((unsigned long) __ACTUAL_TUNE < N_TUNE_TYPES)
+    return loongarch_cpu_issue_rate[__ACTUAL_TUNE];
+  else
+    return 1;
+}
+
+/* Implement TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD.  This should
+   be as wide as the scheduling freedom in the DFA.  */
+
+static int
+loongarch_multipass_dfa_lookahead (void)
+{
+  if ((unsigned long) __ACTUAL_TUNE < N_ARCH_TYPES)
+    return loongarch_cpu_multipass_dfa_lookahead[__ACTUAL_TUNE];
+  else
+    return 0;
+}
+
+/* Implement TARGET_SCHED_REORDER.  */
+
+static int
+loongarch_sched_reorder (FILE *file ATTRIBUTE_UNUSED,
+			 int verbose ATTRIBUTE_UNUSED,
+			 rtx_insn **ready ATTRIBUTE_UNUSED,
+			 int *nreadyp ATTRIBUTE_UNUSED,
+			 int cycle ATTRIBUTE_UNUSED)
+{
+  return loongarch_issue_rate ();
+}
+
+/* Implement TARGET_SCHED_REORDER2.  */
+
+static int
+loongarch_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED,
+			  int verbose ATTRIBUTE_UNUSED,
+			  rtx_insn **ready ATTRIBUTE_UNUSED,
+			  int *nreadyp ATTRIBUTE_UNUSED,
+			  int cycle ATTRIBUTE_UNUSED)
+{
+  return cached_can_issue_more;
+}
+
+/* Implement TARGET_SCHED_INIT.  */
+
+static void
+loongarch_sched_init (FILE *file ATTRIBUTE_UNUSED,
+		      int verbose ATTRIBUTE_UNUSED,
+		      int max_ready ATTRIBUTE_UNUSED)
+{}
+
+/* Implement TARGET_SCHED_VARIABLE_ISSUE.  */
+
+static int
+loongarch_variable_issue (FILE *file ATTRIBUTE_UNUSED,
+			  int verbose ATTRIBUTE_UNUSED, rtx_insn *insn,
+			  int more)
+{
+  /* Ignore USEs and CLOBBERs; don't count them against the issue rate.  */
+  if (USEFUL_INSN_P (insn))
+    {
+      if (get_attr_type (insn) != TYPE_GHOST)
+	more--;
+    }
+
+  /* Instructions of type 'multi' should all be split before
+     the second scheduling pass.  */
+  gcc_assert (!reload_completed
+	      || recog_memoized (insn) < 0
+	      || get_attr_type (insn) != TYPE_MULTI);
+
+  cached_can_issue_more = more;
+  return more;
+}
+
+/* A structure representing the state of the processor pipeline.
+   Used by the loongarch_sim_* family of functions.  */
+
+struct loongarch_sim
+{
+  /* The maximum number of instructions that can be issued in a cycle.
+     (Caches loongarch_issue_rate.)  */
+  unsigned int issue_rate;
+
+  /* The current simulation time.  */
+  unsigned int time;
+
+  /* How many more instructions can be issued in the current cycle.  */
+  unsigned int insns_left;
+
+  /* LAST_SET[X].INSN is the last instruction to set register X.
+     LAST_SET[X].TIME is the time at which that instruction was issued.
+     INSN is null if no instruction has yet set register X.  */
+  struct
+  {
+    rtx_insn *insn;
+    unsigned int time;
+  } last_set[FIRST_PSEUDO_REGISTER];
+
+  /* The pipeline's current DFA state.  */
+  state_t dfa_state;
+};
+
+/* Reset STATE to the initial simulation state.  */
+
+static void
+loongarch_sim_reset (struct loongarch_sim *state)
+{
+  curr_state = state->dfa_state;
+
+  state->time = 0;
+  state->insns_left = state->issue_rate;
+  memset (&state->last_set, 0, sizeof (state->last_set));
+  state_reset (curr_state);
+
+  targetm.sched.init (0, false, 0);
+  advance_state (curr_state);
+}
+
+/* Initialize STATE before its first use.  DFA_STATE points to an
+   allocated but uninitialized DFA state.  */
+
+static void
+loongarch_sim_init (struct loongarch_sim *state, state_t dfa_state)
+{
+  if (targetm.sched.init_dfa_pre_cycle_insn)
+    targetm.sched.init_dfa_pre_cycle_insn ();
+
+  if (targetm.sched.init_dfa_post_cycle_insn)
+    targetm.sched.init_dfa_post_cycle_insn ();
+
+  state->issue_rate = loongarch_issue_rate ();
+  state->dfa_state = dfa_state;
+  loongarch_sim_reset (state);
+}
+
+/* Set up costs based on the current architecture and tuning settings.  */
+
+static void
+loongarch_set_tuning_info (void)
+{
+  dfa_start ();
+
+  struct loongarch_sim state;
+  loongarch_sim_init (&state, alloca (state_size ()));
+
+  dfa_finish ();
+}
+
+/* Implement TARGET_EXPAND_TO_RTL_HOOK.  */
+
+static void
+loongarch_expand_to_rtl_hook (void)
+{
+  /* We need to call this at a point where we can safely create sequences
+     of instructions, so TARGET_OVERRIDE_OPTIONS is too early.  We also
+     need to call it at a point where the DFA infrastructure is not
+     already in use, so we can't just call it lazily on demand.
+
+     At present, loongarch_tuning_info is only needed during post-expand
+     RTL passes such as split_insns, so this hook should be early enough.
+     We may need to move the call elsewhere if loongarch_tuning_info starts
+     to be used for other things (such as rtx_costs, or expanders that
+     could be called during gimple optimization).  */
+
+  loongarch_set_tuning_info ();
+}
+
+
+/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
+   return the first operand of the associated PREF or PREFX insn.  */
+
+rtx
+loongarch_prefetch_cookie (rtx write, rtx locality)
+{
+  /* store_streamed / load_streamed.  */
+  if (INTVAL (locality) <= 0)
+    return GEN_INT (INTVAL (write) + 4);
+
+  /* store / load.  */
+  if (INTVAL (locality) <= 2)
+    return write;
+
+  /* store_retained / load_retained.  */
+  return GEN_INT (INTVAL (write) + 6);
+}
+
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
+   in order to avoid duplicating too much logic from elsewhere.  */
+
+static void
+loongarch_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+			   HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+			   tree function)
+{
+  const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
+  rtx this_rtx, temp1, temp2, fnaddr;
+  rtx_insn *insn;
+  bool use_sibcall_p;
+
+  /* Pretend to be a post-reload pass while generating rtl.  */
+  reload_completed = 1;
+
+  /* Mark the end of the (empty) prologue.  */
+  emit_note (NOTE_INSN_PROLOGUE_END);
+
+  /* Determine if we can use a sibcall to call FUNCTION directly.  */
+  fnaddr = XEXP (DECL_RTL (function), 0);
+  use_sibcall_p = const_call_insn_operand (fnaddr, Pmode);
+
+  /* We need two temporary registers in some cases.  */
+  temp1 = gen_rtx_REG (Pmode, 12);
+  temp2 = gen_rtx_REG (Pmode, 13);
+
+  /* Find out which register contains the "this" pointer.  */
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+    this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+  else
+    this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  /* Add DELTA to THIS_RTX.  */
+  if (delta != 0)
+    {
+      rtx offset = GEN_INT (delta);
+      if (!IMM12_OPERAND (delta))
+	{
+	  loongarch_emit_move (temp1, offset);
+	  offset = temp1;
+	}
+      emit_insn (gen_add3_insn (this_rtx, this_rtx, offset));
+    }
+
+  /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX.  */
+  if (vcall_offset != 0)
+    {
+      rtx addr;
+
+      /* Set TEMP1 to *THIS_RTX.  */
+      loongarch_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx));
+
+      /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET.  */
+      addr = loongarch_add_offset (temp2, temp1, vcall_offset);
+
+      /* Load the offset and add it to THIS_RTX.  */
+      loongarch_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
+      emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1));
+    }
+
+  /* Jump to the target function.  Use a sibcall if direct jumps are
+     allowed, otherwise load the address into a register first.  */
+  if (use_sibcall_p)
+    {
+      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+      SIBLING_CALL_P (insn) = 1;
+    }
+  else
+    {
+      loongarch_emit_move (temp1, fnaddr);
+      emit_jump_insn (gen_indirect_jump (temp1));
+    }
+
+  /* Run just enough of rest_of_compilation.  This sequence was
+     "borrowed" from alpha.c.  */
+  insn = get_insns ();
+  split_all_insns_noflow ();
+  shorten_branches (insn);
+  assemble_start_function (thunk_fndecl, fnname);
+  final_start_function (insn, file, 1);
+  final (insn, file, 1);
+  final_end_function ();
+  assemble_end_function (thunk_fndecl, fnname);
+
+  /* Clean up the vars set above.  Note that final_end_function resets
+     the global pointer for us.  */
+  reload_completed = 0;
+}
+
+/* Allocate a chunk of memory for per-function machine-dependent data.  */
+
+static struct machine_function *
+loongarch_init_machine_status (void)
+{
+  return ggc_cleared_alloc<machine_function> ();
+}
+
+static void
+loongarch_option_override_internal (struct gcc_options *opts,
+				    struct gcc_options *opts_set);
+
+/* Implement TARGET_OPTION_OVERRIDE.  */
+
+static void
+loongarch_option_override (void)
+{
+  loongarch_option_override_internal (&global_options, &global_options_set);
+}
+
+static void
+loongarch_option_override_internal (struct gcc_options *opts,
+				    struct gcc_options *opts_set)
+{
+  int i, regno, mode;
+
+  (void) opts_set;
+
+  if (flag_pic)
+    g_switch_value = 0;
+
+  /* Handle target-specific options: compute defaults/conflicts etc.  */
+  loongarch_config_target (&la_target, la_opt_switches,
+			   la_opt_cpu_arch, la_opt_cpu_tune, la_opt_fpu,
+			   la_opt_abi_base, la_opt_abi_ext, la_opt_cmodel, 0);
+
+  /* End of code shared with GAS.  */
+  if (TARGET_ABI_LP64)
+    flag_pcc_struct_return = 0;
+
+  /* Decide which rtx_costs structure to use.  */
+  if (optimize_size)
+    loongarch_cost = &loongarch_rtx_cost_optimize_size;
+  else
+    loongarch_cost = &loongarch_cpu_rtx_cost_data[__ACTUAL_TUNE];
+
+  /* If the user hasn't specified a branch cost, use the processor's
+     default.  */
+  if (loongarch_branch_cost == 0)
+    loongarch_branch_cost = loongarch_cost->branch_cost;
+
+
+  switch (la_target.cmodel)
+    {
+      case CMODEL_TINY_STATIC:
+      case CMODEL_EXTREME:
+	if (opts->x_flag_plt)
+	  error ("code model %qs and %qs not support %s mode",
+		 "tiny-static", "extreme", "plt");
+	break;
+
+      case CMODEL_NORMAL:
+      case CMODEL_TINY:
+      case CMODEL_LARGE:
+	break;
+
+      default:
+	gcc_unreachable ();
+    }
+
+  /* .cfi_* directives generate a read-only section, so fall back on
+     manual .eh_frame creation if we need the section to be writable.  */
+  if (flag_pic)
+    flag_dwarf2_cfi_asm = 0;
+
+  loongarch_init_print_operand_punct ();
+
+  /* Set up array to map GCC register number to debug register number.
+     Ignore the special purpose register numbers.  */
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      if (GP_REG_P (i) || FP_REG_P (i))
+	loongarch_dwarf_regno[i] = i;
+      else
+	loongarch_dwarf_regno[i] = INVALID_REGNUM;
+    }
+
+  /* Set up loongarch_hard_regno_mode_ok.  */
+  for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+    for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+      loongarch_hard_regno_mode_ok_p[mode][regno]
+	= loongarch_hard_regno_mode_ok_uncached (regno, (machine_mode) mode);
+
+  /* Function to allocate machine-dependent function status.  */
+  init_machine_status = &loongarch_init_machine_status;
+}
+
+/* Implement TARGET_CONDITIONAL_REGISTER_USAGE.  */
+
+static void
+loongarch_conditional_register_usage (void)
+{
+  if (!TARGET_HARD_FLOAT)
+    accessible_reg_set &= ~(reg_class_contents[FP_REGS]
+			    | reg_class_contents[FCC_REGS]);
+}
+
+/* Implement EH_USES.  */
+
+bool
+loongarch_eh_uses (unsigned int regno ATTRIBUTE_UNUSED)
+{
+  return false;
+}
+
+/* Implement EPILOGUE_USES.  */
+
+bool
+loongarch_epilogue_uses (unsigned int regno)
+{
+  /* Say that the epilogue uses the return address register.  Note that
+     in the case of sibcalls, the values "used by the epilogue" are
+     considered live at the start of the called function.  */
+  if (regno == RETURN_ADDR_REGNUM)
+    return true;
+
+  return false;
+}
+
+bool
+loongarch_load_store_bonding_p (rtx *operands, machine_mode mode, bool load_p)
+{
+  rtx reg1, reg2, mem1, mem2, base1, base2;
+  enum reg_class rc1, rc2;
+  HOST_WIDE_INT offset1, offset2;
+
+  if (load_p)
+    {
+      reg1 = operands[0];
+      reg2 = operands[2];
+      mem1 = operands[1];
+      mem2 = operands[3];
+    }
+  else
+    {
+      reg1 = operands[1];
+      reg2 = operands[3];
+      mem1 = operands[0];
+      mem2 = operands[2];
+    }
+
+  if (loongarch_address_insns (XEXP (mem1, 0), mode, false) == 0
+      || loongarch_address_insns (XEXP (mem2, 0), mode, false) == 0)
+    return false;
+
+  loongarch_split_plus (XEXP (mem1, 0), &base1, &offset1);
+  loongarch_split_plus (XEXP (mem2, 0), &base2, &offset2);
+
+  /* Base regs do not match.  */
+  if (!REG_P (base1) || !rtx_equal_p (base1, base2))
+    return false;
+
+  /* Either of the loads is clobbering base register.  It is legitimate to bond
+     loads if second load clobbers base register.  However, hardware does not
+     support such bonding.  */
+  if (load_p
+      && (REGNO (reg1) == REGNO (base1) || (REGNO (reg2) == REGNO (base1))))
+    return false;
+
+  /* Loading in same registers.  */
+  if (load_p && REGNO (reg1) == REGNO (reg2))
+    return false;
+
+  /* The loads/stores are not of same type.  */
+  rc1 = REGNO_REG_CLASS (REGNO (reg1));
+  rc2 = REGNO_REG_CLASS (REGNO (reg2));
+  if (rc1 != rc2 && !reg_class_subset_p (rc1, rc2)
+      && !reg_class_subset_p (rc2, rc1))
+    return false;
+
+  if (abs (offset1 - offset2) != GET_MODE_SIZE (mode))
+    return false;
+
+  return true;
+}
+
+/* Implement TARGET_TRAMPOLINE_INIT.  */
+
+static void
+loongarch_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+  rtx addr, end_addr, mem;
+  rtx trampoline[8];
+  unsigned int i, j;
+  HOST_WIDE_INT end_addr_offset, static_chain_offset, target_function_offset;
+
+  /* Work out the offsets of the pointers from the start of the
+     trampoline code.  */
+  end_addr_offset = TRAMPOLINE_CODE_SIZE;
+  static_chain_offset = end_addr_offset;
+  target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
+
+  /* Get pointers to the beginning and end of the code block.  */
+  addr = force_reg (Pmode, XEXP (m_tramp, 0));
+  end_addr
+    = loongarch_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
+
+#define OP(X) gen_int_mode (X, SImode)
+
+  /* Build up the code in TRAMPOLINE.  */
+  i = 0;
+  /*pcaddi $static_chain,0
+    ld.[dw] $tmp,$static_chain,target_function_offset
+    ld.[dw] $static_chain,$static_chain,static_chain_offset
+    jirl $r0,$tmp,0  */
+  trampoline[i++] = OP (0x18000000 | (STATIC_CHAIN_REGNUM - GP_REG_FIRST));
+  trampoline[i++] = OP ((ptr_mode == DImode ? 0x28c00000 : 0x28800000)
+			| 19 /* $t7  */
+			| ((STATIC_CHAIN_REGNUM - GP_REG_FIRST) << 5)
+			| ((target_function_offset & 0xfff) << 10));
+  trampoline[i++] = OP ((ptr_mode == DImode ? 0x28c00000 : 0x28800000)
+			| (STATIC_CHAIN_REGNUM - GP_REG_FIRST)
+			| ((STATIC_CHAIN_REGNUM - GP_REG_FIRST) << 5)
+			| ((static_chain_offset & 0xfff) << 10));
+  trampoline[i++] = OP (0x4c000000 | (19 << 5));
+#undef OP
+
+  for (j = 0; j < i; j++)
+   {
+     mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
+     loongarch_emit_move (mem, trampoline[j]);
+   }
+
+  /* Set up the static chain pointer field.  */
+  mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
+  loongarch_emit_move (mem, chain_value);
+
+  /* Set up the target function field.  */
+  mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
+  loongarch_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
+
+  /* Flush the code part of the trampoline.  */
+  emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE)));
+  emit_insn (gen_clear_cache (addr, end_addr));
+}
+
+/* Implement TARGET_SHIFT_TRUNCATION_MASK.  We want to keep the default
+   behavior of TARGET_SHIFT_TRUNCATION_MASK for non-vector modes even
+   when TARGET_LOONGSON_MMI is true.  */
+
+static unsigned HOST_WIDE_INT
+loongarch_shift_truncation_mask (machine_mode mode)
+{
+  return GET_MODE_BITSIZE (mode) - 1;
+}
+
+/* Implement TARGET_SCHED_REASSOCIATION_WIDTH.  */
+
+static int
+loongarch_sched_reassociation_width (unsigned int opc ATTRIBUTE_UNUSED,
+				     machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
+/* Implement HARD_REGNO_CALLER_SAVE_MODE.  */
+
+machine_mode
+loongarch_hard_regno_caller_save_mode (unsigned int regno, unsigned int nregs,
+				       machine_mode mode)
+{
+  /* For performance, avoid saving/restoring upper parts of a register
+     by returning MODE as save mode when the mode is known.  */
+  if (mode == VOIDmode)
+    return choose_hard_reg_mode (regno, nregs, NULL);
+  else
+    return mode;
+}
+
+/* Implement TARGET_CASE_VALUES_THRESHOLD.  */
+
+unsigned int
+loongarch_case_values_threshold (void)
+{
+  return default_case_values_threshold ();
+}
+
+/* Implement TARGET_SPILL_CLASS.  */
+
+static reg_class_t
+loongarch_spill_class (reg_class_t rclass ATTRIBUTE_UNUSED,
+		       machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return NO_REGS;
+}
+
+/* Implement TARGET_LRA_P.  */
+
+static bool
+loongarch_lra_p (void)
+{
+  return loongarch_lra_flag;
+}
+
+/* Implement TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS.  */
+
+static reg_class_t
+loongarch_ira_change_pseudo_allocno_class (int regno,
+					   reg_class_t allocno_class,
+					   reg_class_t best_class     \
+					   ATTRIBUTE_UNUSED)
+{
+  /* LRA will allocate an FPR for an integer mode pseudo instead of spilling
+     to memory if an FPR is present in the allocno class.  It is rare that
+     we actually need to place an integer mode value in an FPR so where
+     possible limit the allocation to GR_REGS.  This will slightly pessimize
+     code that involves integer to/from float conversions as these will have
+     to reload into FPRs in LRA.  Such reloads are sometimes eliminated and
+     sometimes only partially eliminated.  We choose to take this penalty
+     in order to eliminate usage of FPRs in code that does not use floating
+     point data.
+
+     This change has a similar effect to increasing the cost of FPR->GPR
+     register moves for integer modes so that they are higher than the cost
+     of memory but changing the allocno class is more reliable.
+
+     This is also similar to forbidding integer mode values in FPRs entirely
+     but this would lead to an inconsistency in the integer to/from float
+     instructions that say integer mode values must be placed in FPRs.  */
+  if (INTEGRAL_MODE_P (PSEUDO_REGNO_MODE (regno)) && allocno_class == ALL_REGS)
+    return GR_REGS;
+  return allocno_class;
+}
+
+/* Implement TARGET_PROMOTE_FUNCTION_MODE.  */
+
+/* This function is equivalent to default_promote_function_mode_always_promote
+   except that it returns a promoted mode even if type is NULL_TREE.  This is
+   needed by libcalls which have no type (only a mode) such as fixed conversion
+   routines that take a signed or unsigned char/short argument and convert it
+   to a fixed type.  */
+
+static machine_mode
+loongarch_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
+				 machine_mode mode,
+				 int *punsignedp ATTRIBUTE_UNUSED,
+				 const_tree fntype ATTRIBUTE_UNUSED,
+				 int for_return ATTRIBUTE_UNUSED)
+{
+  int unsignedp;
+
+  if (type != NULL_TREE)
+    return promote_mode (type, mode, punsignedp);
+
+  unsignedp = *punsignedp;
+  PROMOTE_MODE (mode, unsignedp, type);
+  *punsignedp = unsignedp;
+  return mode;
+}
+
+/* Implement TARGET_TRULY_NOOP_TRUNCATION.  */
+
+static bool
+loongarch_truly_noop_truncation (poly_uint64 outprec, poly_uint64 inprec)
+{
+  return !TARGET_64BIT || inprec <= 32 || outprec > 32;
+}
+
+/* Implement TARGET_CONSTANT_ALIGNMENT.  */
+
+static HOST_WIDE_INT
+loongarch_constant_alignment (const_tree exp, HOST_WIDE_INT align)
+{
+  if (TREE_CODE (exp) == STRING_CST || TREE_CODE (exp) == CONSTRUCTOR)
+    return MAX (align, BITS_PER_WORD);
+  return align;
+}
+
+/* Implement TARGET_STARTING_FRAME_OFFSET.  See loongarch_compute_frame_info
+   for details about the frame layout.  */
+
+static HOST_WIDE_INT
+loongarch_starting_frame_offset (void)
+{
+  if (FRAME_GROWS_DOWNWARD)
+    return 0;
+  return crtl->outgoing_args_size;
+}
+
+/* Initialize the GCC target structure.  */
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE loongarch_option_override
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS loongarch_legitimize_address
+
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION loongarch_select_rtx_section
+#undef TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION loongarch_function_rodata_section
+
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT loongarch_sched_init
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER loongarch_sched_reorder
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 loongarch_sched_reorder2
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE loongarch_variable_issue
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST loongarch_adjust_cost
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE loongarch_issue_rate
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+  loongarch_multipass_dfa_lookahead
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL loongarch_function_ok_for_sibcall
+
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE loongarch_valid_pointer_mode
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST loongarch_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST loongarch_memory_move_cost
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS loongarch_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST loongarch_address_cost
+
+#undef TARGET_IN_SMALL_DATA_P
+#define TARGET_IN_SMALL_DATA_P loongarch_in_small_data_p
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS loongarch_preferred_reload_class
+
+#undef TARGET_EXPAND_TO_RTL_HOOK
+#define TARGET_EXPAND_TO_RTL_HOOK loongarch_expand_to_rtl_hook
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START loongarch_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START loongarch_va_start
+
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE loongarch_promote_function_mode
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY loongarch_return_in_memory
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE loongarch_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE loongarch_libcall_value
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK loongarch_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK \
+  hook_bool_const_tree_hwi_hwi_const_tree_true
+
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND loongarch_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS loongarch_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P \
+  loongarch_print_operand_punct_valid_p
+
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS loongarch_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE loongarch_pass_by_reference
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES loongarch_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG loongarch_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE loongarch_function_arg_advance
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY loongarch_function_arg_boundary
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P loongarch_scalar_mode_supported_p
+
+#undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
+#define TARGET_VECTORIZE_PREFERRED_SIMD_MODE loongarch_preferred_simd_mode
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS loongarch_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL loongarch_builtin_decl
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN loongarch_expand_builtin
+
+/* The generic ELF target does not always have TLS support.  */
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+#endif
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM loongarch_cannot_force_const_mem
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P loongarch_legitimate_constant_p
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE loongarch_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+   be compiled or called.  They don't in themselves prevent inlining.  */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+
+#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
+#define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_true
+#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
+#define TARGET_USE_ANCHORS_FOR_SYMBOL_P loongarch_use_anchors_for_symbol_p
+
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL loongarch_output_dwarf_dtprel
+#endif
+#undef TARGET_DWARF_FRAME_REG_MODE
+#define TARGET_DWARF_FRAME_REG_MODE loongarch_dwarf_frame_reg_mode
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P loongarch_legitimate_address_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED loongarch_frame_pointer_required
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE loongarch_can_eliminate
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE loongarch_conditional_register_usage
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init
+
+#undef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK loongarch_shift_truncation_mask
+
+#undef TARGET_SCHED_REASSOCIATION_WIDTH
+#define TARGET_SCHED_REASSOCIATION_WIDTH loongarch_sched_reassociation_width
+
+#undef TARGET_CASE_VALUES_THRESHOLD
+#define TARGET_CASE_VALUES_THRESHOLD loongarch_case_values_threshold
+
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv
+
+#undef TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS
+#define TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS true
+
+#undef TARGET_USE_BY_PIECES_INFRASTRUCTURE_P
+#define TARGET_USE_BY_PIECES_INFRASTRUCTURE_P \
+  loongarch_use_by_pieces_infrastructure_p
+
+#undef TARGET_SPILL_CLASS
+#define TARGET_SPILL_CLASS loongarch_spill_class
+#undef TARGET_LRA_P
+#define TARGET_LRA_P loongarch_lra_p
+#undef TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS
+#define TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS \
+  loongarch_ira_change_pseudo_allocno_class
+
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS loongarch_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK loongarch_hard_regno_mode_ok
+
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P loongarch_modes_tieable_p
+
+#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS
+#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 2
+
+#undef TARGET_SECONDARY_MEMORY_NEEDED
+#define TARGET_SECONDARY_MEMORY_NEEDED loongarch_secondary_memory_needed
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS loongarch_can_change_mode_class
+
+#undef TARGET_TRULY_NOOP_TRUNCATION
+#define TARGET_TRULY_NOOP_TRUNCATION loongarch_truly_noop_truncation
+
+#undef TARGET_CONSTANT_ALIGNMENT
+#define TARGET_CONSTANT_ALIGNMENT loongarch_constant_alignment
+
+#undef TARGET_STARTING_FRAME_OFFSET
+#define TARGET_STARTING_FRAME_OFFSET loongarch_starting_frame_offset
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD loongarch_secondary_reload
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+#include "gt-loongarch.h"
diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h
new file mode 100644
index 00000000000..f829ae53f21
--- /dev/null
+++ b/gcc/config/loongarch/loongarch.h
@@ -0,0 +1,1291 @@
+/* Definitions of target machine for GNU compiler.  LoongArch version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS and RISC-V target for GNU compiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* LoongArch external variables defined in loongarch.c.  */
+
+#include "config/loongarch/loongarch-opts.h"
+
+/* Macros to silence warnings about numbers being signed in traditional
+   C and unsigned in ISO C when compiled on 32-bit hosts.  */
+
+#define BITMASK_HIGH (((unsigned long) 1) << 31) /* 0x80000000  */
+
+/* Run-time compilation parameters selecting different hardware subsets.  */
+
+/* Target CPU builtins.  */
+#define TARGET_CPU_CPP_BUILTINS() loongarch_cpu_cpp_builtins (pfile)
+
+/* Default target_flags if no switches are specified.  */
+
+#ifdef IN_LIBGCC2
+#undef TARGET_64BIT
+/* Make this compile time constant for libgcc2.  */
+#ifdef __loongarch64
+#define TARGET_64BIT 1
+#else
+#define TARGET_64BIT 0
+#endif
+#endif /* IN_LIBGCC2  */
+
+#define TARGET_LIBGCC_SDATA_SECTION ".sdata"
+
+/* Support for a compile-time default CPU, et cetera.  The rules are:
+   --with-divide is ignored if -mdivide-traps or -mdivide-breaks are
+     specified.  */
+#define OPTION_DEFAULT_SPECS \
+  {"divide", "%{!mdivide-traps:%{!mdivide-breaks:-mdivide-%(VALUE)}}"},
+
+/* Driver native functions for SPEC processing in the GCC driver.  */
+#include "loongarch-driver.h"
+
+/* This definition replaces the formerly used 'm' constraint with a
+   different constraint letter in order to avoid changing semantics of
+   the 'm' constraint when accepting new address formats in
+   TARGET_LEGITIMATE_ADDRESS_P.  The constraint letter defined here
+   must not be used in insn definitions or inline assemblies.  */
+#define TARGET_MEM_CONSTRAINT 'w'
+
+/* Tell collect what flags to pass to nm.  */
+#ifndef NM_FLAGS
+#define NM_FLAGS "-Bn"
+#endif
+
+/* SUBTARGET_ASM_DEBUGGING_SPEC handles passing debugging options to
+   the assembler.  It may be overridden by subtargets.  */
+
+#ifndef SUBTARGET_ASM_DEBUGGING_SPEC
+#define SUBTARGET_ASM_DEBUGGING_SPEC "\
+%{g} %{g0} %{g1} %{g2} %{g3} \
+%{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \
+%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \
+%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3}"
+#endif
+
+/* SUBTARGET_ASM_SPEC is always passed to the assembler.  It may be
+   overridden by subtargets.  */
+
+#ifndef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC ""
+#endif
+
+#undef ASM_SPEC
+#define ASM_SPEC "%{mabi=*} %{subtarget_asm_spec}"
+
+/* Extra switches sometimes passed to the linker.  */
+
+#ifndef LINK_SPEC
+#define LINK_SPEC ""
+#endif /* LINK_SPEC defined  */
+
+/* Specs for the compiler proper.  */
+
+/* CC1_SPEC is the set of arguments to pass to the compiler proper.  */
+
+#undef CC1_SPEC
+#define CC1_SPEC "\
+%{G*} \
+%(subtarget_cc1_spec)"
+
+/* Preprocessor specs.  */
+
+/* SUBTARGET_CPP_SPEC is passed to the preprocessor.  It may be
+   overridden by subtargets.  */
+#ifndef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC ""
+#endif
+
+#define CPP_SPEC "%(subtarget_cpp_spec)"
+
+/* This macro defines names of additional specifications to put in the specs
+   that can be used in various specifications like CC1_SPEC.  Its definition
+   is an initializer with a subgrouping for each command option.
+
+   Each subgrouping contains a string constant, that defines the
+   specification name, and a string constant that used by the GCC driver
+   program.
+
+   Do not define this macro if it does not need to do anything.  */
+
+#define EXTRA_SPECS \
+  {"subtarget_cc1_spec", SUBTARGET_CC1_SPEC}, \
+  {"subtarget_cpp_spec", SUBTARGET_CPP_SPEC}, \
+  {"subtarget_asm_debugging_spec", SUBTARGET_ASM_DEBUGGING_SPEC}, \
+  {"subtarget_asm_spec", SUBTARGET_ASM_SPEC},
+
+/* Registers may have a prefix which can be ignored when matching
+   user asm and register definitions.  */
+#ifndef REGISTER_PREFIX
+#define REGISTER_PREFIX "$"
+#endif
+
+/* Local compiler-generated symbols must have a prefix that the assembler
+   understands.  */
+
+#define LOCAL_LABEL_PREFIX "."
+
+/* By default on the loongarch, external symbols do not have an underscore
+   prepended.  */
+
+#define USER_LABEL_PREFIX ""
+
+#ifndef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+#endif
+
+/* The size of DWARF addresses should be the same as the size of symbols
+   in the target file format.  */
+#define DWARF2_ADDR_SIZE (TARGET_64BIT ? 8 : 4)
+
+/* By default, turn on GDB extensions.  */
+#define DEFAULT_GDB_EXTENSIONS 1
+
+/* By default, produce dwarf version 2 format debugging output in response
+   to the ‘-g’ option.  */
+#define DWARF2_DEBUGGING_INFO 1
+
+/* The mapping from gcc register number to DWARF 2 CFA column number.  */
+#define DWARF_FRAME_REGNUM(REGNO) loongarch_dwarf_regno[REGNO]
+
+/* The DWARF 2 CFA column which tracks the return address.  */
+#define DWARF_FRAME_RETURN_COLUMN RETURN_ADDR_REGNUM
+
+/* Before the prologue, RA lives in r1.  */
+#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM)
+
+/* Describe how we implement __builtin_eh_return.  */
+#define EH_RETURN_DATA_REGNO(N) \
+  ((N) < (4) ? (N) + GP_ARG_FIRST : INVALID_REGNUM)
+
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_ARG_FIRST + 4)
+
+#define EH_USES(N) loongarch_eh_uses (N)
+
+/* Offsets recorded in opcodes are a multiple of this alignment factor.
+   The default for this in 64-bit mode is 8, which causes problems with
+   SFmode register saves.  */
+#define DWARF_CIE_DATA_ALIGNMENT -4
+
+/* Target machine storage layout.  */
+
+#define BITS_BIG_ENDIAN 0
+#define BYTES_BIG_ENDIAN 0
+#define WORDS_BIG_ENDIAN 0
+
+#define MAX_BITS_PER_WORD 64
+
+/* Width of a word, in units (bytes).  */
+#define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4)
+#ifndef IN_LIBGCC2
+#define MIN_UNITS_PER_WORD 4
+#endif
+
+/* For LARCH, width of a floating point register.  */
+#define UNITS_PER_FPREG (TARGET_DOUBLE_FLOAT ? 8 : 4)
+
+/* The number of consecutive floating-point registers needed to store the
+   largest format supported by the FPU.  */
+#define MAX_FPRS_PER_FMT 1
+
+/* The number of consecutive floating-point registers needed to store the
+   smallest format supported by the FPU.  */
+#define MIN_FPRS_PER_FMT 1
+
+/* The largest size of value that can be held in floating-point
+   registers and moved with a single instruction.  */
+#define UNITS_PER_HWFPVALUE \
+  (TARGET_SOFT_FLOAT ? 0 : MAX_FPRS_PER_FMT * UNITS_PER_FPREG)
+
+/* The largest size of value that can be held in floating-point
+   registers.  */
+#define UNITS_PER_FPVALUE \
+  (TARGET_SOFT_FLOAT ? 0 \
+   : TARGET_SINGLE_FLOAT ? UNITS_PER_FPREG \
+			 : LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT)
+
+/* The number of bytes in a double.  */
+#define UNITS_PER_DOUBLE (TYPE_PRECISION (double_type_node) / BITS_PER_UNIT)
+
+/* Set the sizes of the core types.  */
+#define SHORT_TYPE_SIZE 16
+#define INT_TYPE_SIZE 32
+#define LONG_TYPE_SIZE (TARGET_64BIT ? 64 : 32)
+#define LONG_LONG_TYPE_SIZE 64
+
+#define FLOAT_TYPE_SIZE 32
+#define DOUBLE_TYPE_SIZE 64
+#define LONG_DOUBLE_TYPE_SIZE (TARGET_64BIT ? 128 : 64)
+
+/* Define the sizes of fixed-point types.  */
+#define SHORT_FRACT_TYPE_SIZE 8
+#define FRACT_TYPE_SIZE 16
+#define LONG_FRACT_TYPE_SIZE 32
+#define LONG_LONG_FRACT_TYPE_SIZE 64
+
+#define SHORT_ACCUM_TYPE_SIZE 16
+#define ACCUM_TYPE_SIZE 32
+#define LONG_ACCUM_TYPE_SIZE 64
+/* FIXME.  LONG_LONG_ACCUM_TYPE_SIZE should be 128 bits, but GCC
+   doesn't support 128-bit integers for loongarch32 currently.  */
+#define LONG_LONG_ACCUM_TYPE_SIZE (TARGET_64BIT ? 128 : 64)
+
+/* long double is not a fixed mode, but the idea is that, if we
+   support long double, we also want a 128-bit integer type.  */
+#define MAX_FIXED_MODE_SIZE LONG_DOUBLE_TYPE_SIZE
+
+/* Width in bits of a pointer.  */
+#ifndef POINTER_SIZE
+#define POINTER_SIZE (TARGET_64BIT ? 64 : 32)
+#endif
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list.  */
+#define PARM_BOUNDARY BITS_PER_WORD
+
+/* Allocation boundary (in *bits*) for the code of a function.  */
+#define FUNCTION_BOUNDARY 32
+
+/* Alignment of field after `int : 0' in a structure.  */
+#define EMPTY_FIELD_BOUNDARY 32
+
+/* Number of bits which any structure or union's size must be a multiple of.
+   Each structure or union's size is rounded up to a multiple of this.  */
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+/* There is no point aligning anything to a rounder boundary than
+   LONG_DOUBLE_TYPE_SIZE.  */
+#define BIGGEST_ALIGNMENT (LONG_DOUBLE_TYPE_SIZE)
+
+/* All accesses must be aligned.  */
+#define STRICT_ALIGNMENT (TARGET_STRICT_ALIGN)
+
+/* Define this if you wish to imitate the way many other C compilers
+   handle alignment of bitfields and the structures that contain
+   them.
+
+   The behavior is that the type written for a bit-field (`int',
+   `short', or other integer type) imposes an alignment for the
+   entire structure, as if the structure really did contain an
+   ordinary field of that type.  In addition, the bit-field is placed
+   within the structure so that it would fit within such a field,
+   not crossing a boundary for it.
+
+   Thus, on most machines, a bit-field whose type is written as `int'
+   would not cross a four-byte boundary, and would force four-byte
+   alignment for the whole structure.  (The alignment used may not
+   be four bytes; it is controlled by the other alignment
+   parameters.)
+
+   If the macro is defined, its definition should be a C expression;
+   a nonzero value for the expression enables this behavior.  */
+
+#define PCC_BITFIELD_TYPE_MATTERS 1
+
+/* If defined, a C expression to compute the alignment for a static
+   variable.  TYPE is the data type, and ALIGN is the alignment that
+   the object would ordinarily have.  The value of this macro is used
+   instead of that alignment to align the object.
+
+   If this macro is not defined, then ALIGN is used.
+
+   One use of this macro is to increase alignment of medium-size
+   data to make it all fit in fewer cache lines.  Another is to
+   cause character arrays to be word-aligned so that `strcpy' calls
+   that copy constants to character arrays can be done inline.  */
+
+#undef DATA_ALIGNMENT
+#define DATA_ALIGNMENT(TYPE, ALIGN)					\
+  ((((ALIGN) < BITS_PER_WORD)						\
+    && (TREE_CODE (TYPE) == ARRAY_TYPE					\
+	|| TREE_CODE (TYPE) == UNION_TYPE				\
+	|| TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN))
+
+/* We need this for the same reason as DATA_ALIGNMENT, namely to cause
+   character arrays to be word-aligned so that `strcpy' calls that copy
+   constants to character arrays can be done inline, and 'strcmp' can be
+   optimised to use word loads.  */
+#define LOCAL_ALIGNMENT(TYPE, ALIGN) DATA_ALIGNMENT (TYPE, ALIGN)
+
+/* Define if operations between registers always perform the operation
+   on the full register even if a narrower mode is specified.  */
+#define WORD_REGISTER_OPERATIONS 1
+
+/* When in 64-bit mode, move insns will sign extend SImode and FCCmode
+   moves.  All other references are zero extended.  */
+#define LOAD_EXTEND_OP(MODE) \
+  (TARGET_64BIT && ((MODE) == SImode || (MODE) == FCCmode) ? SIGN_EXTEND \
+							   : ZERO_EXTEND)
+
+/* Define this macro if it is advisable to hold scalars in registers
+   in a wider mode than that declared by the program.  In such cases,
+   the value is constrained to be within the bounds of the declared
+   type, but kept valid in the wider mode.  The signedness of the
+   extension may differ from that of the type.  */
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+  if (GET_MODE_CLASS (MODE) == MODE_INT \
+      && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \
+    { \
+      if ((MODE) == SImode) \
+	(UNSIGNEDP) = 0; \
+      (MODE) = Pmode; \
+    }
+
+/* Pmode is always the same as ptr_mode, but not always the same as word_mode.
+   Extensions of pointers to word_mode must be signed.  */
+#define POINTERS_EXTEND_UNSIGNED false
+
+/* Define if loading short immediate values into registers sign extends.  */
+#define SHORT_IMMEDIATES_SIGN_EXTEND 1
+
+/* The clz.{w/d} instructions have the natural values at 0.  */
+
+#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) \
+  ((VALUE) = GET_MODE_UNIT_BITSIZE (MODE), 2)
+
+/* Standard register usage.  */
+
+/* Number of hardware registers.  We have:
+
+   - 32 integer registers
+   - 32 floating point registers
+   - 8 condition code registers
+   - 2 fake registers:
+	- ARG_POINTER_REGNUM
+	- FRAME_POINTER_REGNUM
+*/
+
+#define FIRST_PSEUDO_REGISTER 74
+
+/* zero, tp, sp and x are fixed.  */
+#define FIXED_REGISTERS							\
+{ /* General registers.  */						\
+  1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  /* Floating-point registers.  */					\
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  /* Others.  */							\
+  0, 0, 0, 0, 0, 0, 0, 0, 1, 1}
+
+/* The call RTLs themselves clobber ra.  */
+#define CALL_USED_REGISTERS						\
+{ /* General registers.  */						\
+  1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,			\
+  1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  /* Floating-point registers.  */					\
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,			\
+  1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,			\
+  /* Others.  */							\
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+
+/* Internal macros to classify a register number as to whether it's a
+   general purpose register, a floating point register, or a status
+   register.  */
+
+#define GP_REG_FIRST 0
+#define GP_REG_LAST 31
+#define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1)
+
+#define FP_REG_FIRST 32
+#define FP_REG_LAST 63
+#define FP_REG_NUM (FP_REG_LAST - FP_REG_FIRST + 1)
+
+/* The DWARF 2 CFA column which tracks the return address from a
+   signal handler context.  This means that to maintain backwards
+   compatibility, no hard register can be assigned this column if it
+   would need to be handled by the DWARF unwinder.  */
+#define DWARF_ALT_FRAME_RETURN_COLUMN 72
+
+#define FCC_REG_FIRST 64
+#define FCC_REG_LAST 71
+#define FCC_REG_NUM (FCC_REG_LAST - FCC_REG_FIRST + 1)
+
+#define GP_REG_P(REGNO) \
+  ((unsigned int) ((int) (REGNO) - GP_REG_FIRST) < GP_REG_NUM)
+#define FP_REG_P(REGNO) \
+  ((unsigned int) ((int) (REGNO) - FP_REG_FIRST) < FP_REG_NUM)
+#define FCC_REG_P(REGNO) \
+  ((unsigned int) ((int) (REGNO) - FCC_REG_FIRST) < FCC_REG_NUM)
+
+#define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
+
+#define HARD_REGNO_RENAME_OK(OLD_REG, NEW_REG) \
+  loongarch_hard_regno_rename_ok (OLD_REG, NEW_REG)
+
+/* Select a register mode required for caller save of hard regno REGNO.  */
+#define HARD_REGNO_CALLER_SAVE_MODE(REGNO, NREGS, MODE) \
+  loongarch_hard_regno_caller_save_mode (REGNO, NREGS, MODE)
+
+/* Register to use for pushing function arguments.  */
+#define STACK_POINTER_REGNUM (GP_REG_FIRST + 3)
+
+/* These two registers don't really exist: they get eliminated to either
+   the stack or hard frame pointer.  */
+#define ARG_POINTER_REGNUM 72
+#define FRAME_POINTER_REGNUM 73
+
+#define HARD_FRAME_POINTER_REGNUM (GP_REG_FIRST + 22)
+
+#define HARD_FRAME_POINTER_IS_FRAME_POINTER 0
+#define HARD_FRAME_POINTER_IS_ARG_POINTER 0
+
+/* Register in which static-chain is passed to a function.  */
+#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 20) /* $t8  */
+
+#define GP_TEMP_FIRST (GP_REG_FIRST + 12)
+#define LARCH_PROLOGUE_TEMP_REGNUM (GP_TEMP_FIRST + 1)
+#define LARCH_PROLOGUE_TEMP2_REGNUM (GP_TEMP_FIRST)
+#define LARCH_PROLOGUE_TEMP3_REGNUM (GP_TEMP_FIRST + 2)
+#define LARCH_EPILOGUE_TEMP_REGNUM (GP_TEMP_FIRST)
+
+#define CALLEE_SAVED_REG_NUMBER(REGNO) \
+  ((REGNO) >= 22 && (REGNO) <= 31 ? (REGNO) -22 : -1)
+
+#define LARCH_PROLOGUE_TEMP(MODE) \
+  gen_rtx_REG (MODE, LARCH_PROLOGUE_TEMP_REGNUM)
+#define LARCH_PROLOGUE_TEMP2(MODE) \
+  gen_rtx_REG (MODE, LARCH_PROLOGUE_TEMP2_REGNUM)
+#define LARCH_PROLOGUE_TEMP3(MODE) \
+  gen_rtx_REG (MODE, LARCH_PROLOGUE_TEMP3_REGNUM)
+#define LARCH_EPILOGUE_TEMP(MODE) \
+  gen_rtx_REG (MODE, LARCH_EPILOGUE_TEMP_REGNUM)
+
+/* Define this macro if it is as good or better to call a constant
+   function address than to call an address kept in a register.  */
+#define NO_FUNCTION_CSE 1
+
+#define THREAD_POINTER_REGNUM (GP_REG_FIRST + 2)
+
+/* Define the classes of registers for register constraints in the
+   machine description.  Also define ranges of constants.
+
+   One of the classes must always be named ALL_REGS and include all hard regs.
+   If there is more than one class, another class must be named NO_REGS
+   and contain no registers.
+
+   The name GENERAL_REGS must be the name of a class (or an alias for
+   another name such as ALL_REGS).  This is the class of registers
+   that is allowed by "r" in a register constraint.
+   Also, registers outside this class are allocated only when
+   instructions express preferences for them.
+
+   The classes must be numbered in nondecreasing order; that is,
+   a larger-numbered class must never be contained completely
+   in a smaller-numbered class.
+
+   For any two classes, it is very desirable that there be another
+   class that represents their union.  */
+
+enum reg_class
+{
+  NO_REGS,	  /* no registers in set  */
+  SIBCALL_REGS,	  /* SIBCALL_REGS  */
+  JIRL_REGS,	  /* JIRL_REGS  */
+  GR_REGS,	  /* integer registers  */
+  CSR_REGS,	  /* integer registers except for $r0 and $r1 for lcsr.  */
+  FP_REGS,	  /* floating point registers  */
+  FCC_REGS,	  /* status registers (fp status)  */
+  FRAME_REGS,	  /* arg pointer and frame pointer  */
+  ALL_REGS,	  /* all registers  */
+  LIM_REG_CLASSES /* max value + 1  */
+};
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+#define GENERAL_REGS GR_REGS
+
+/* An initializer containing the names of the register classes as C
+   string constants.  These names are used in writing some of the
+   debugging dumps.  */
+
+#define REG_CLASS_NAMES							\
+{									\
+  "NO_REGS",								\
+  "SIBCALL_REGS",							\
+  "JIRL_REGS",								\
+  "GR_REGS",								\
+  "CSR_REGS",								\
+  "FP_REGS",								\
+  "FCC_REGS",								\
+  "FRAME_REGS",								\
+  "ALL_REGS"								\
+}
+
+/* An initializer containing the contents of the register classes,
+   as integers which are bit masks.  The Nth integer specifies the
+   contents of class N.  The way the integer MASK is interpreted is
+   that register R is in the class if `MASK & (1 << R)' is 1.
+
+   When the machine has more than 32 registers, an integer does not
+   suffice.  Then the integers are replaced by sub-initializers,
+   braced groupings containing several integers.  Each
+   sub-initializer must be suitable as an initializer for the type
+   `HARD_REG_SET' which is defined in `hard-reg-set.h'.  */
+
+#define REG_CLASS_CONTENTS						\
+{									\
+  { 0x00000000, 0x00000000, 0x00000000 },	/* NO_REGS  */		\
+  { 0x001ff000, 0x00000000, 0x00000000 },	/* SIBCALL_REGS  */	\
+  { 0xff9ffff0, 0x00000000, 0x00000000 },	/* JIRL_REGS  */	\
+  { 0xffffffff, 0x00000000, 0x00000000 },	/* GR_REGS  */		\
+  { 0xfffffffc, 0x00000000, 0x00000000 },	/* CSR_REGS  */		\
+  { 0x00000000, 0xffffffff, 0x00000000 },	/* FP_REGS  */		\
+  { 0x00000000, 0x00000000, 0x000000ff },	/* FCC_REGS  */		\
+  { 0x00000000, 0x00000000, 0x00000300 },	/* FRAME_REGS  */	\
+  { 0xffffffff, 0xffffffff, 0x000003ff }	/* ALL_REGS  */		\
+}
+
+/* A C expression whose value is a register class containing hard
+   register REGNO.  In general there is more that one such class;
+   choose a class which is "minimal", meaning that no smaller class
+   also contains the register.  */
+
+#define REGNO_REG_CLASS(REGNO) loongarch_regno_to_class[(REGNO)]
+
+/* A macro whose definition is the name of the class to which a
+   valid base register must belong.  A base register is one used in
+   an address which is the register value plus a displacement.  */
+
+#define BASE_REG_CLASS (GR_REGS)
+
+/* A macro whose definition is the name of the class to which a
+   valid index register must belong.  An index register is one used
+   in an address where its value is either multiplied by a scale
+   factor or added to another register (as well as added to a
+   displacement).  */
+
+#define INDEX_REG_CLASS NO_REGS
+
+/* We generally want to put call-clobbered registers ahead of
+   call-saved ones.  (IRA expects this.)  */
+
+#define REG_ALLOC_ORDER							\
+{ /* Call-clobbered GPRs.  */						\
+  12, 13, 14, 15, 16, 17, 18, 19, 20, 4, 5, 6, 7, 8, 9, 10, 11, 1,	\
+  /* Call-saved GPRs.  */						\
+  23, 24, 25, 26, 27, 28, 29, 30, 31,					\
+  /* GPRs that can never be exposed to the register allocator.  */	\
+  0, 2, 3, 21, 22, 							\
+  /* Call-clobbered FPRs.  */						\
+  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,					\
+  /* None of the remaining classes have defined call-saved		\
+     registers.  */							\
+  64, 65, 66, 67, 68, 69, 70, 71, 72, 73}
+
+#define IMM_BITS 12
+#define IMM_REACH (1LL << IMM_BITS)
+
+/* True if VALUE is an unsigned 6-bit number.  */
+
+#define UIMM6_OPERAND(VALUE) (((VALUE) & ~(unsigned HOST_WIDE_INT) 0x3f) == 0)
+
+/* True if VALUE is a signed 10-bit number.  */
+
+#define IMM10_OPERAND(VALUE) ((unsigned HOST_WIDE_INT) (VALUE) + 0x200 < 0x400)
+
+/* True if VALUE is a signed 12-bit number.  */
+
+#define IMM12_OPERAND(VALUE) \
+  ((unsigned HOST_WIDE_INT) (VALUE) + IMM_REACH / 2 < IMM_REACH)
+
+/* True if VALUE is a signed 16-bit number.  */
+
+#define IMM16_OPERAND(VALUE) \
+  ((unsigned HOST_WIDE_INT) (VALUE) + 0x8000 < 0x10000)
+
+/* True if VALUE is an unsigned 12-bit number.  */
+
+#define IMM12_OPERAND_UNSIGNED(VALUE) \
+  (((VALUE) & ~(unsigned HOST_WIDE_INT) (IMM_REACH - 1)) == 0)
+
+/* True if VALUE can be loaded into a register using LU12I.  */
+
+#define LU12I_OPERAND(VALUE) \
+  (((VALUE) | ((1UL << 31) - IMM_REACH)) == ((1UL << 31) - IMM_REACH) \
+   || ((VALUE) | ((1UL << 31) - IMM_REACH)) + IMM_REACH == 0)
+
+/* True if VALUE can be loaded into a register using LU32I.  */
+
+#define LU32I_OPERAND(VALUE) \
+  (((VALUE) | (((1UL << 19) - 1) << 32)) == (((1UL << 19) - 1) << 32) \
+   || ((VALUE) | (((1UL << 19) - 1) << 32)) + (1UL << 32) == 0)
+
+/* True if VALUE can be loaded into a register using LU52I.  */
+
+#define LU52I_OPERAND(VALUE) (((VALUE) | (0xfffUL << 52)) == (0xfffUL << 52))
+
+/* Return a value X with the low 12 bits clear, and such that
+   VALUE - X is a signed 12-bit value.  */
+
+#define CONST_HIGH_PART(VALUE) (((VALUE) + (IMM_REACH / 2)) & ~(IMM_REACH - 1))
+
+#define CONST_LOW_PART(VALUE) ((VALUE) -CONST_HIGH_PART (VALUE))
+
+#define IMM12_INT(X) IMM12_OPERAND (INTVAL (X))
+#define IMM12_INT_UNSIGNED(X) IMM12_OPERAND_UNSIGNED (INTVAL (X))
+#define LU12I_INT(X) LU12I_OPERAND (INTVAL (X))
+#define LU32I_INT(X) LU32I_OPERAND (INTVAL (X))
+#define LU52I_INT(X) LU52I_OPERAND (INTVAL (X))
+#define LARCH_U12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047))
+#define LARCH_9BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -256, 255))
+#define LARCH_16BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -32768, 32767))
+#define LARCH_SHIFT_2_OFFSET_P(OFFSET) (((OFFSET) & 0x3) == 0)
+
+/* Return the maximum number of consecutive registers
+   needed to represent mode MODE in a register of class CLASS.  */
+
+#define CLASS_MAX_NREGS(CLASS, MODE) loongarch_class_max_nregs (CLASS, MODE)
+
+/* Stack layout; function entry, exit and calling.  */
+
+#define STACK_GROWS_DOWNWARD 1
+
+#define FRAME_GROWS_DOWNWARD 1
+
+#define RETURN_ADDR_RTX loongarch_return_addr
+
+/* Similarly, don't use the least-significant bit to tell pointers to
+   code from vtable index.  */
+
+#define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_delta
+
+#define ELIMINABLE_REGS \
+  { \
+    {ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+      {ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
+      {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+      {FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
+  }
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+  (OFFSET) = loongarch_initial_elimination_offset ((FROM), (TO))
+
+/* Allocate stack space for arguments at the beginning of each function.  */
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+/* The argument pointer always points to the first argument.  */
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+#define REG_PARM_STACK_SPACE(FNDECL) 0
+
+/* Define this if it is the responsibility of the caller to
+   allocate the area reserved for arguments passed in registers.
+   If `ACCUMULATE_OUTGOING_ARGS' is also defined, the only effect
+   of this macro is to determine whether the space is included in
+   `crtl->outgoing_args_size'.  */
+#define OUTGOING_REG_PARM_STACK_SPACE(FNTYPE) 1
+
+#define STACK_BOUNDARY (TARGET_ABI_LP64 ? 128 : 64)
+
+/* Symbolic macros for the registers used to return integer and floating
+   point values.  */
+
+#define GP_RETURN (GP_REG_FIRST + 4)
+#define FP_RETURN ((TARGET_SOFT_FLOAT) ? GP_RETURN : (FP_REG_FIRST + 0))
+
+#define MAX_ARGS_IN_REGISTERS 8
+
+/* Symbolic macros for the first/last argument registers.  */
+
+#define GP_ARG_FIRST (GP_REG_FIRST + 4)
+#define GP_ARG_LAST (GP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+#define FP_ARG_FIRST (FP_REG_FIRST + 0)
+#define FP_ARG_LAST (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+
+/* 1 if N is a possible register number for function argument passing.
+   We have no FP argument registers when soft-float.  */
+
+/* Accept arguments in a0-a7, and in fa0-fa7 if permitted by the ABI.  */
+#define FUNCTION_ARG_REGNO_P(N) \
+  (IN_RANGE ((N), GP_ARG_FIRST, GP_ARG_LAST) \
+   || (UNITS_PER_FP_ARG && IN_RANGE ((N), FP_ARG_FIRST, FP_ARG_LAST)))
+
+/* This structure has to cope with two different argument allocation
+   schemes.  Most LoongArch ABIs view the arguments as a structure, of which
+   the first N words go in registers and the rest go on the stack.  If I
+   < N, the Ith word might go in Ith integer argument register or in a
+   floating-point register.  For these ABIs, we only need to remember
+   the offset of the current argument into the structure.
+
+   So for the standard ABIs, the first N words are allocated to integer
+   registers, and loongarch_function_arg decides on an argument-by-argument
+   basis whether that argument should really go in an integer register,
+   or in a floating-point one.  */
+
+typedef struct loongarch_args
+{
+  /* Number of integer registers used so far, up to MAX_ARGS_IN_REGISTERS.  */
+  unsigned int num_gprs;
+
+  /* Number of floating-point registers used so far, likewise.  */
+  unsigned int num_fprs;
+
+} CUMULATIVE_ARGS;
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
+
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
+  memset (&(CUM), 0, sizeof (CUM))
+
+#define EPILOGUE_USES(REGNO) loongarch_epilogue_uses (REGNO)
+
+/* Treat LOC as a byte offset from the stack pointer and round it up
+   to the next fully-aligned offset.  */
+#define LARCH_STACK_ALIGN(LOC) \
+  (TARGET_ABI_LP64 ? ROUND_UP ((LOC), 16) : ROUND_UP ((LOC), 8))
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
+
+#define MCOUNT_NAME "_mcount"
+
+/* Emit rtl for profiling.  Output assembler code to FILE
+   to call "_mcount" for profiling a function entry.  */
+#define PROFILE_HOOK(LABEL) \
+  { \
+    rtx fun, ra; \
+    ra = get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM); \
+    fun = gen_rtx_SYMBOL_REF (Pmode, MCOUNT_NAME); \
+    emit_library_call (fun, LCT_NORMAL, VOIDmode, ra, Pmode); \
+  }
+
+/* All the work done in PROFILE_HOOK, but still required.  */
+#define FUNCTION_PROFILER(STREAM, LABELNO) do { } while (0)
+
+#define NO_PROFILE_COUNTERS 1
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+   the stack pointer does not matter.  The value is tested only in
+   functions that have frame pointers.
+   No definition is equivalent to always zero.  */
+
+#define EXIT_IGNORE_STACK 1
+
+/* Trampolines are a block of code followed by two pointers.  */
+
+#define TRAMPOLINE_CODE_SIZE 16
+#define TRAMPOLINE_SIZE \
+  ((Pmode == SImode) ? TRAMPOLINE_CODE_SIZE \
+		     : (TRAMPOLINE_CODE_SIZE + POINTER_SIZE * 2))
+#define TRAMPOLINE_ALIGNMENT POINTER_SIZE
+
+/* loongarch_trampoline_init calls this library function to flush
+   program and data caches.  */
+
+#ifndef CACHE_FLUSH_FUNC
+#define CACHE_FLUSH_FUNC "_flush_cache"
+#endif
+
+/* Addressing modes, and classification of registers for them.  */
+
+#define REGNO_OK_FOR_INDEX_P(REGNO) 0
+#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \
+  loongarch_regno_mode_ok_for_base_p (REGNO, MODE, 1)
+
+/* Maximum number of registers that can appear in a valid memory address.  */
+
+#define MAX_REGS_PER_ADDRESS 1
+
+/* Check for constness inline but use loongarch_legitimate_address_p
+   to check whether a constant really is an address.  */
+
+#define CONSTANT_ADDRESS_P(X) (CONSTANT_P (X) && memory_address_p (SImode, X))
+
+/* This handles the magic '..CURRENT_FUNCTION' symbol, which means
+   'the start of the function that this code is output in'.  */
+
+#define ASM_OUTPUT_LABELREF(FILE, NAME) \
+  do \
+    { \
+      if (strcmp (NAME, "..CURRENT_FUNCTION") == 0) \
+	asm_fprintf ((FILE), "%U%s", \
+		     XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \
+      else \
+	asm_fprintf ((FILE), "%U%s", (NAME)); \
+    } \
+  while (0)
+
+#define CASE_VECTOR_MODE Pmode
+
+/* Only use short offsets if their range will not overflow.  */
+#define CASE_VECTOR_SHORTEN_MODE(MIN, MAX, BODY) Pmode
+
+/* Define this as 1 if `char' should by default be signed; else as 0.  */
+#ifndef DEFAULT_SIGNED_CHAR
+#define DEFAULT_SIGNED_CHAR 1
+#endif
+
+/* The SPARC port says:
+   The maximum number of bytes that a single instruction
+   can move quickly between memory and registers or between
+   two memory locations.  */
+#define MOVE_MAX UNITS_PER_WORD
+#define MAX_MOVE_MAX 8
+
+/* The SPARC port says:
+   Nonzero if access to memory by bytes is slow and undesirable.
+   For RISC chips, it means that access to memory by bytes is no
+   better than access by words when possible, so grab a whole word
+   and maybe make use of that.  */
+#define SLOW_BYTE_ACCESS 1
+
+/* Standard LoongArch integer shifts truncate the shift amount to the
+   width of the shifted operand.  */
+#define SHIFT_COUNT_TRUNCATED 1
+
+/* Specify the machine mode that pointers have.
+   After generation of rtl, the compiler makes no further distinction
+   between pointers and any other objects of this machine mode.  */
+
+#ifndef Pmode
+#define Pmode (TARGET_64BIT ? DImode : SImode)
+#endif
+
+/* Give call MEMs SImode since it is the "most permissive" mode
+   for both 32-bit and 64-bit targets.  */
+
+#define FUNCTION_MODE SImode
+
+/* We allocate $fcc registers by hand and can't cope with moves of
+   CCmode registers to and from pseudos (or memory).  */
+#define AVOID_CCMODE_COPIES
+
+/* A C expression for the cost of a branch instruction.  A value of
+   1 is the default; other values are interpreted relative to that.  */
+
+#define BRANCH_COST(speed_p, predictable_p) loongarch_branch_cost
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
+/* If defined, modifies the length assigned to instruction INSN as a
+   function of the context in which it is used.  LENGTH is an lvalue
+   that contains the initially computed length of the insn and should
+   be updated with the correct length of the insn.  */
+#define ADJUST_INSN_LENGTH(INSN, LENGTH) \
+  ((LENGTH) = loongarch_adjust_insn_length ((INSN), (LENGTH)))
+
+/* Return the asm template for a conditional branch instruction.
+   OPCODE is the opcode's mnemonic and OPERANDS is the asm template for
+   its operands.  */
+#define LARCH_BRANCH(OPCODE, OPERANDS) OPCODE "\t" OPERANDS
+
+/* Control the assembler format that we output.  */
+
+/* Output to assembler file text saying following lines
+   may contain character constants, extra white space, comments, etc.  */
+
+#ifndef ASM_APP_ON
+#define ASM_APP_ON " #APP\n"
+#endif
+
+/* Output to assembler file text saying following lines
+   no longer contain unusual constructs.  */
+
+#ifndef ASM_APP_OFF
+#define ASM_APP_OFF " #NO_APP\n"
+#endif
+
+#define REGISTER_NAMES							  \
+{ "$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",	  \
+  "$arg", "$frame"}
+
+/* This macro defines additional names for hard registers.  */
+
+#define ADDITIONAL_REGISTER_NAMES					\
+{									\
+  { "zero",	 0 + GP_REG_FIRST },					\
+  { "ra",	 1 + GP_REG_FIRST },					\
+  { "tp",	 2 + GP_REG_FIRST },					\
+  { "sp",	 3 + GP_REG_FIRST },					\
+  { "a0",	 4 + GP_REG_FIRST },					\
+  { "a1",	 5 + GP_REG_FIRST },					\
+  { "a2",	 6 + GP_REG_FIRST },					\
+  { "a3",	 7 + GP_REG_FIRST },					\
+  { "a4",	 8 + GP_REG_FIRST },					\
+  { "a5",	 9 + GP_REG_FIRST },					\
+  { "a6",	10 + GP_REG_FIRST },					\
+  { "a7",	11 + GP_REG_FIRST },					\
+  { "t0",	12 + GP_REG_FIRST },					\
+  { "t1",	13 + GP_REG_FIRST },					\
+  { "t2",	14 + GP_REG_FIRST },					\
+  { "t3",	15 + GP_REG_FIRST },					\
+  { "t4",	16 + GP_REG_FIRST },					\
+  { "t5",	17 + GP_REG_FIRST },					\
+  { "t6",	18 + GP_REG_FIRST },					\
+  { "t7",	19 + GP_REG_FIRST },					\
+  { "t8",	20 + GP_REG_FIRST },					\
+  { "x",	21 + GP_REG_FIRST },					\
+  { "fp",	22 + GP_REG_FIRST },					\
+  { "s0",	23 + GP_REG_FIRST },					\
+  { "s1",	24 + GP_REG_FIRST },					\
+  { "s2",	25 + GP_REG_FIRST },					\
+  { "s3",	26 + GP_REG_FIRST },					\
+  { "s4",	27 + GP_REG_FIRST },					\
+  { "s5",	28 + GP_REG_FIRST },					\
+  { "s6",	29 + GP_REG_FIRST },					\
+  { "s7",	30 + GP_REG_FIRST },					\
+  { "s8",	31 + GP_REG_FIRST },					\
+  { "v0",	 4 + GP_REG_FIRST },					\
+  { "v1",	 5 + GP_REG_FIRST }					\
+}
+
+#define DBR_OUTPUT_SEQEND(STREAM) \
+  do \
+    { \
+      /* Emit a blank line after the delay slot for emphasis.  */ \
+      fputs ("\n", STREAM); \
+    } \
+  while (0)
+
+/* The LoongArch implementation uses some labels for its own purpose.  The
+   following lists what labels are created, and are all formed by the
+   pattern $L[a-z].*.  The machine independent portion of GCC creates
+   labels matching:  $L[A-Z][0-9]+ and $L[0-9]+.
+
+	LM[0-9]+	Silicon Graphics/ECOFF stabs label before each stmt.
+	$Lb[0-9]+	Begin blocks for LoongArch debug support
+	$Lc[0-9]+	Label for use in s<xx> operation.
+	$Le[0-9]+	End blocks for LoongArch debug support.  */
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \
+  loongarch_declare_object (STREAM, NAME, "", ":\n")
+
+/* Globalizing directive for a label.  */
+#define GLOBAL_ASM_OP "\t.globl\t"
+
+/* This says how to define a global common symbol.  */
+
+#define ASM_OUTPUT_ALIGNED_DECL_COMMON loongarch_output_aligned_decl_common
+
+/* This says how to define a local common symbol (i.e., not visible to
+   linker).  */
+
+#ifndef ASM_OUTPUT_ALIGNED_LOCAL
+#define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \
+  loongarch_declare_common_object (STREAM, NAME, "\n\t.lcomm\t", SIZE, ALIGN, \
+				   false)
+#endif
+
+/* This says how to output an external.  It would be possible not to
+   output anything and let undefined symbol become external.  However
+   the assembler uses length information on externals to allocate in
+   data/sdata bss/sbss, thereby saving exec time.  */
+
+#undef ASM_OUTPUT_EXTERNAL
+#define ASM_OUTPUT_EXTERNAL(STREAM, DECL, NAME) \
+  loongarch_output_external (STREAM, DECL, NAME)
+
+/* This is how to declare a function name.  The actual work of
+   emitting the label is moved to function_prologue, so that we can
+   get the line number correctly emitted before the .ent directive,
+   and after any .file directives.  Define as empty so that the function
+   is not declared before the .ent directive elsewhere.  */
+
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \
+  loongarch_declare_function_name (STREAM, NAME, DECL)
+
+/* This is how to store into the string LABEL
+   the symbol_ref name of an internal numbered label where
+   PREFIX is the class of label and NUM is the number within the class.
+   This is suitable for output with `assemble_name'.  */
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
+  sprintf ((LABEL), "*%s%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long) (NUM))
+
+/* Print debug labels as "foo = ." rather than "foo:" because they should
+   represent a byte pointer rather than an ISA-encoded address.  This is
+   particularly important for code like:
+
+	$LFBxxx = .
+		.cfi_startproc
+		...
+		.section .gcc_except_table,...
+		...
+		.uleb128 foo-$LFBxxx
+
+   The .uleb128 requies $LFBxxx to match the FDE start address, which is
+   likewise a byte pointer rather than an ISA-encoded address.
+
+   At the time of writing, this hook is not used for the function end
+   label:
+
+	$LFExxx:
+		.end foo
+
+   */
+
+#define ASM_OUTPUT_DEBUG_LABEL(FILE, PREFIX, NUM) \
+  fprintf (FILE, "%s%s%d = .\n", LOCAL_LABEL_PREFIX, PREFIX, NUM)
+
+/* This is how to output an element of a case-vector that is absolute.  */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \
+  fprintf (STREAM, "\t%s\t%sL%d\n", ptr_mode == DImode ? ".dword" : ".word", \
+	   LOCAL_LABEL_PREFIX, VALUE)
+
+/* This is how to output an element of a case-vector.  */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \
+  do \
+    { \
+      fprintf (STREAM, "\t%s\t%sL%d-%sL%d\n", \
+	       ptr_mode == DImode ? ".dword" : ".word", LOCAL_LABEL_PREFIX, \
+	       VALUE, LOCAL_LABEL_PREFIX, REL); \
+    } \
+  while (0)
+
+#define JUMP_TABLES_IN_TEXT_SECTION 0
+
+/* This is how to output an assembler line
+   that says to advance the location counter
+   to a multiple of 2**LOG bytes.  */
+
+#define ASM_OUTPUT_ALIGN(STREAM, LOG) fprintf (STREAM, "\t.align\t%d\n", (LOG))
+
+/* "nop" instruction 54525952 (andi $r0,$r0,0) is
+   used for padding.  */
+#define ASM_OUTPUT_ALIGN_WITH_NOP(STREAM, LOG) \
+  fprintf (STREAM, "\t.align\t%d,54525952,4\n", (LOG))
+
+/* This is how to output an assembler line to advance the location
+   counter by SIZE bytes.  */
+
+#undef ASM_OUTPUT_SKIP
+#define ASM_OUTPUT_SKIP(STREAM, SIZE) \
+  fprintf (STREAM, "\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n", (SIZE))
+
+/* This is how to output a string.  */
+#undef ASM_OUTPUT_ASCII
+#define ASM_OUTPUT_ASCII loongarch_output_ascii
+
+/* Define the strings to put out for each section in the object file.  */
+#define TEXT_SECTION_ASM_OP "\t.text" /* instructions  */
+#define DATA_SECTION_ASM_OP "\t.data" /* large data  */
+
+#undef READONLY_DATA_SECTION_ASM_OP
+#define READONLY_DATA_SECTION_ASM_OP "\t.section\t.rodata" /* read-only data */
+
+#define ASM_OUTPUT_REG_PUSH(STREAM, REGNO)	\
+  do \
+    { \
+      fprintf (STREAM, "\t%s\t%s,%s,-8\n\t%s\t%s,%s,0\n", \
+	       TARGET_64BIT ? "addi.d" : "addi.w", \
+	       reg_names[STACK_POINTER_REGNUM], \
+	       reg_names[STACK_POINTER_REGNUM], \
+	       TARGET_64BIT ? "st.d" : "st.w", reg_names[REGNO], \
+	       reg_names[STACK_POINTER_REGNUM]); \
+    } \
+  while (0)
+
+#define ASM_OUTPUT_REG_POP(STREAM, REGNO) \
+  do \
+    { \
+      fprintf (STREAM, "\t%s\t%s,%s,0\n\t%s\t%s,%s,8\n", \
+	       TARGET_64BIT ? "ld.d" : "ld.w", reg_names[REGNO], \
+	       reg_names[STACK_POINTER_REGNUM], \
+	       TARGET_64BIT ? "addi.d" : "addi.w", \
+	       reg_names[STACK_POINTER_REGNUM], \
+	       reg_names[STACK_POINTER_REGNUM]); \
+    } \
+  while (0)
+
+/* How to start an assembler comment.
+   The leading space is important (the loongarch native assembler requires it).
+ */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START " #"
+#endif
+
+#undef SIZE_TYPE
+#define SIZE_TYPE (POINTER_SIZE == 64 ? "long unsigned int" : "unsigned int")
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE (POINTER_SIZE == 64 ? "long int" : "int")
+
+/* The maximum number of bytes that can be copied by one iteration of
+   a cpymemsi loop; see loongarch_block_move_loop.  */
+#define LARCH_MAX_MOVE_BYTES_PER_LOOP_ITER (UNITS_PER_WORD * 4)
+
+/* The maximum number of bytes that can be copied by a straight-line
+   implementation of cpymemsi; see loongarch_block_move_straight.  We want
+   to make sure that any loop-based implementation will iterate at
+   least twice.  */
+#define LARCH_MAX_MOVE_BYTES_STRAIGHT (LARCH_MAX_MOVE_BYTES_PER_LOOP_ITER * 2)
+
+/* The base cost of a memcpy call, for MOVE_RATIO and friends.  These
+   values were determined experimentally by benchmarking with CSiBE.
+*/
+#define LARCH_CALL_RATIO 8
+
+/* Any loop-based implementation of cpymemsi will have at least
+   LARCH_MAX_MOVE_BYTES_STRAIGHT / UNITS_PER_WORD memory-to-memory
+   moves, so allow individual copies of fewer elements.
+
+   When cpymemsi is not available, use a value approximating
+   the length of a memcpy call sequence, so that move_by_pieces
+   will generate inline code if it is shorter than a function call.
+   Since move_by_pieces_ninsns counts memory-to-memory moves, but
+   we'll have to generate a load/store pair for each, halve the
+   value of LARCH_CALL_RATIO to take that into account.  */
+
+#define MOVE_RATIO(speed) \
+  (HAVE_cpymemsi ? LARCH_MAX_MOVE_BYTES_STRAIGHT / MOVE_MAX \
+		 : LARCH_CALL_RATIO / 2)
+
+/* For CLEAR_RATIO, when optimizing for size, give a better estimate
+   of the length of a memset call, but use the default otherwise.  */
+
+#define CLEAR_RATIO(speed) ((speed) ? 15 : LARCH_CALL_RATIO)
+
+/* This is similar to CLEAR_RATIO, but for a non-zero constant, so when
+   optimizing for size adjust the ratio to account for the overhead of
+   loading the constant and replicating it across the word.  */
+
+#define SET_RATIO(speed) ((speed) ? 15 : LARCH_CALL_RATIO - 2)
+
+#ifndef USED_FOR_TARGET
+extern const enum reg_class loongarch_regno_to_class[];
+extern int loongarch_dwarf_regno[];
+
+/* Information about a function's frame layout.  */
+struct GTY (()) loongarch_frame_info
+{
+  /* The size of the frame in bytes.  */
+  HOST_WIDE_INT total_size;
+
+  /* Bit X is set if the function saves or restores GPR X.  */
+  unsigned int mask;
+
+  /* Likewise FPR X.  */
+  unsigned int fmask;
+
+  /* How much the GPR save/restore routines adjust sp (or 0 if unused).  */
+  unsigned save_libcall_adjustment;
+
+  /* Offsets of fixed-point and floating-point save areas from frame
+     bottom.  */
+  HOST_WIDE_INT gp_sp_offset;
+  HOST_WIDE_INT fp_sp_offset;
+
+  /* Offset of virtual frame pointer from stack pointer/frame bottom.  */
+  HOST_WIDE_INT frame_pointer_offset;
+
+  /* Offset of hard frame pointer from stack pointer/frame bottom.  */
+  HOST_WIDE_INT hard_frame_pointer_offset;
+
+  /* The offset of arg_pointer_rtx from the bottom of the frame.  */
+  HOST_WIDE_INT arg_pointer_offset;
+};
+
+struct GTY (()) machine_function
+{
+  /* The next floating-point condition-code register to allocate
+     for 8CC targets, relative to FCC_REG_FIRST.  */
+  unsigned int next_fcc;
+
+  /* The number of extra stack bytes taken up by register varargs.
+     This area is allocated by the callee at the very top of the frame.  */
+  int varargs_size;
+
+  /* The current frame information, calculated by loongarch_compute_frame_info.
+   */
+  struct loongarch_frame_info frame;
+
+  /* True if loongarch_adjust_insn_length should ignore an instruction's
+     hazard attribute.  */
+  bool ignore_hazard_length_p;
+
+  /* True if at least one of the formal parameters to a function must be
+     written to the frame header (probably so its address can be taken).  */
+  bool does_not_use_frame_header;
+
+  /* True if none of the functions that are called by this function need
+     stack space allocated for their arguments.  */
+  bool optimize_call_stack;
+
+  /* True if one of the functions calling this function may not allocate
+     a frame header.  */
+  bool callers_may_not_allocate_frame;
+};
+#endif
+
+/* As on most targets, we want the .eh_frame section to be read-only where
+   possible.  And as on most targets, this means two things:
+
+     (a) Non-locally-binding pointers must have an indirect encoding,
+	 so that the addresses in the .eh_frame section itself become
+	 locally-binding.
+
+     (b) A shared library's .eh_frame section must encode locally-binding
+	 pointers in a relative (relocation-free) form.
+
+   However, LoongArch has traditionally not allowed directives like:
+
+	.long	x-.
+
+   in cases where "x" is in a different section, or is not defined in the
+   same assembly file.  We are therefore unable to emit the PC-relative
+   form required by (b) at assembly time.
+
+   Fortunately, the linker is able to convert absolute addresses into
+   PC-relative addresses on our behalf.  Unfortunately, only certain
+   versions of the linker know how to do this for indirect pointers,
+   and for personality data.  We must fall back on using writable
+   .eh_frame sections for shared libraries if the linker does not
+   support this feature.  */
+#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \
+  (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr)
+
+/* Several named LoongArch patterns depend on Pmode.  These patterns have the
+   form <NAME>_si for Pmode == SImode and <NAME>_di for Pmode == DImode.
+   Add the appropriate suffix to generator function NAME and invoke it
+   with arguments ARGS.  */
+#define PMODE_INSN(NAME, ARGS) \
+  (Pmode == SImode ? NAME##_si ARGS : NAME##_di ARGS)
+
+/* Do emit .note.GNU-stack by default.  */
+#ifndef NEED_INDICATE_EXEC_STACK
+#define NEED_INDICATE_EXEC_STACK 1
+#endif
+
+/* The `Q' extension is not yet supported.  */
+/* TODO: according to march.  */
+#define UNITS_PER_FP_REG (TARGET_DOUBLE_FLOAT ? 8 : 4)
+
+/* The largest type that can be passed in floating-point registers.  */
+/* TODO: according to mabi.  */
+#define UNITS_PER_FP_ARG  \
+  (TARGET_HARD_FLOAT ? (TARGET_DOUBLE_FLOAT ? 8 : 4) : 0)
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN || (N) == FP_RETURN)
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 06/12] LoongArch Port: Builtin functions.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (4 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 05/12] LoongArch Port: Machine description C files and .h files chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 07/12] LoongArch Port: Builtin macros chenglulu
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/

	* config/loongarch/larchintrin.h: New file.
	* config/loongarch/loongarch-builtins.c: New file.
---
 gcc/config/loongarch/larchintrin.h        | 413 +++++++++++++++++
 gcc/config/loongarch/loongarch-builtins.c | 511 ++++++++++++++++++++++
 2 files changed, 924 insertions(+)
 create mode 100644 gcc/config/loongarch/larchintrin.h
 create mode 100644 gcc/config/loongarch/loongarch-builtins.c

diff --git a/gcc/config/loongarch/larchintrin.h b/gcc/config/loongarch/larchintrin.h
new file mode 100644
index 00000000000..fcf5043841d
--- /dev/null
+++ b/gcc/config/loongarch/larchintrin.h
@@ -0,0 +1,413 @@
+/* Intrinsics for LoongArch BASE operations.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your
+option) any later version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef _GCC_LOONGARCH_BASE_INTRIN_H
+#define _GCC_LOONGARCH_BASE_INTRIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct drdtime
+{
+  unsigned long dvalue;
+  unsigned long dtimeid;
+} __drdtime_t;
+
+typedef struct rdtime
+{
+  unsigned int value;
+  unsigned int timeid;
+} __rdtime_t;
+
+#ifdef __loongarch64
+extern __inline __drdtime_t
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_rdtime_d (void)
+{
+  __drdtime_t drdtime;
+  __asm__ volatile (
+    "rdtime.d\t%[val],%[tid]\n\t"
+    : [val]"=&r"(drdtime.dvalue),[tid]"=&r"(drdtime.dtimeid)
+    :);
+  return drdtime;
+}
+#define __rdtime_d __builtin_loongarch_rdtime_d
+#endif
+
+extern __inline __rdtime_t
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_rdtimeh_w (void)
+{
+  __rdtime_t rdtime;
+  __asm__ volatile (
+    "rdtimeh.w\t%[val],%[tid]\n\t"
+    : [val]"=&r"(rdtime.value),[tid]"=&r"(rdtime.timeid)
+    :);
+  return rdtime;
+}
+#define __rdtimel_w __builtin_loongarch_rdtimel_w
+
+extern __inline __rdtime_t
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_rdtimel_w (void)
+{
+  __rdtime_t rdtime;
+  __asm__ volatile (
+    "rdtimel.w\t%[val],%[tid]\n\t"
+    : [val]"=&r"(rdtime.value),[tid]"=&r"(rdtime.timeid)
+    :);
+  return rdtime;
+}
+#define __rdtimeh_w __builtin_loongarch_rdtimeh_w
+
+/* Assembly instruction format:	rj, fcsr.  */
+/* Data types in instruction templates:  USI, UQI.  */
+#define __movfcsr2gr(/*ui5*/ _1) __builtin_loongarch_movfcsr2gr ((_1));
+
+/* Assembly instruction format:	0, fcsr, rj.  */
+/* Data types in instruction templates:  VOID, UQI, USI.  */
+#define __movgr2fcsr(/*ui5*/ _1, _2) \
+  __builtin_loongarch_movgr2fcsr ((unsigned short) _1, (unsigned int) _2);
+
+#if defined __loongarch64
+/* Assembly instruction format:	ui5, rj, si12.  */
+/* Data types in instruction templates:  VOID, USI, UDI, SI.  */
+#define __dcacop(/*ui5*/ _1, /*unsigned long int*/ _2, /*si12*/ _3) \
+  ((void) __builtin_loongarch_dcacop ((_1), (unsigned long int) (_2), (_3)))
+#else
+#error "Don't support this ABI."
+#endif
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  USI, USI.  */
+extern __inline unsigned int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__cpucfg (unsigned int _1)
+{
+  return (unsigned int) __builtin_loongarch_cpucfg ((unsigned int) _1);
+}
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  DI, DI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__asrtle_d (long int _1, long int _2)
+{
+  __builtin_loongarch_asrtle_d ((long int) _1, (long int) _2);
+}
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  DI, DI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__asrtgt_d (long int _1, long int _2)
+{
+  __builtin_loongarch_asrtgt_d ((long int) _1, (long int) _2);
+}
+#endif
+
+#if defined __loongarch64
+/* Assembly instruction format:	rd, rj, ui5.  */
+/* Data types in instruction templates:  DI, DI, UQI.  */
+#define __dlddir(/*long int*/ _1, /*ui5*/ _2) \
+  ((long int) __builtin_loongarch_dlddir ((long int) (_1), (_2)))
+#else
+#error "Don't support this ABI."
+#endif
+
+#if defined __loongarch64
+/* Assembly instruction format:	rj, ui5.  */
+/* Data types in instruction templates:  VOID, DI, UQI.  */
+#define __dldpte(/*long int*/ _1, /*ui5*/ _2) \
+  ((void) __builtin_loongarch_dldpte ((long int) (_1), (_2)))
+#else
+#error "Don't support this ABI."
+#endif
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, QI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crc_w_b_w (char _1, int _2)
+{
+  return (int) __builtin_loongarch_crc_w_b_w ((char) _1, (int) _2);
+}
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, HI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crc_w_h_w (short _1, int _2)
+{
+  return (int) __builtin_loongarch_crc_w_h_w ((short) _1, (int) _2);
+}
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, SI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crc_w_w_w (int _1, int _2)
+{
+  return (int) __builtin_loongarch_crc_w_w_w ((int) _1, (int) _2);
+}
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, DI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crc_w_d_w (long int _1, int _2)
+{
+  return (int) __builtin_loongarch_crc_w_d_w ((long int) _1, (int) _2);
+}
+#endif
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, QI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crcc_w_b_w (char _1, int _2)
+{
+  return (int) __builtin_loongarch_crcc_w_b_w ((char) _1, (int) _2);
+}
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, HI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crcc_w_h_w (short _1, int _2)
+{
+  return (int) __builtin_loongarch_crcc_w_h_w ((short) _1, (int) _2);
+}
+
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, SI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crcc_w_w_w (int _1, int _2)
+{
+  return (int) __builtin_loongarch_crcc_w_w_w ((int) _1, (int) _2);
+}
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, rj, rk.  */
+/* Data types in instruction templates:  SI, DI, SI.  */
+extern __inline int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__crcc_w_d_w (long int _1, int _2)
+{
+  return (int) __builtin_loongarch_crcc_w_d_w ((long int) _1, (int) _2);
+}
+#endif
+
+/* Assembly instruction format:	rd, ui14.  */
+/* Data types in instruction templates:  USI, USI.  */
+#define __csrrd(/*ui14*/ _1) ((unsigned int) __builtin_loongarch_csrrd ((_1)))
+
+/* Assembly instruction format:	rd, ui14.  */
+/* Data types in instruction templates:  USI, USI, USI.  */
+#define __csrwr(/*unsigned int*/ _1, /*ui14*/ _2) \
+  ((unsigned int) __builtin_loongarch_csrwr ((unsigned int) (_1), (_2)))
+
+/* Assembly instruction format:	rd, rj, ui14.  */
+/* Data types in instruction templates:  USI, USI, USI, USI.  */
+#define __csrxchg(/*unsigned int*/ _1, /*unsigned int*/ _2, /*ui14*/ _3) \
+  ((unsigned int) __builtin_loongarch_csrxchg ((unsigned int) (_1), \
+					       (unsigned int) (_2), (_3)))
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, ui14.  */
+/* Data types in instruction templates:  UDI, USI.  */
+#define __dcsrrd(/*ui14*/ _1) \
+  ((unsigned long int) __builtin_loongarch_dcsrrd ((_1)))
+
+/* Assembly instruction format:	rd, ui14.  */
+/* Data types in instruction templates:  UDI, UDI, USI.  */
+#define __dcsrwr(/*unsigned long int*/ _1, /*ui14*/ _2) \
+  ((unsigned long int) __builtin_loongarch_dcsrwr ((unsigned long int) (_1), \
+						   (_2)))
+
+/* Assembly instruction format:	rd, rj, ui14.  */
+/* Data types in instruction templates:  UDI, UDI, UDI, USI.  */
+#define __dcsrxchg(/*unsigned long int*/ _1, /*unsigned long int*/ _2, \
+		   /*ui14*/ _3) \
+  ((unsigned long int) __builtin_loongarch_dcsrxchg ( \
+    (unsigned long int) (_1), (unsigned long int) (_2), (_3)))
+#endif
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  UQI, USI.  */
+extern __inline unsigned char
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrrd_b (unsigned int _1)
+{
+  return (unsigned char) __builtin_loongarch_iocsrrd_b ((unsigned int) _1);
+}
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  UHI, USI.  */
+extern __inline unsigned char
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrrd_h (unsigned int _1)
+{
+  return (unsigned short) __builtin_loongarch_iocsrrd_h ((unsigned int) _1);
+}
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  USI, USI.  */
+extern __inline unsigned int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrrd_w (unsigned int _1)
+{
+  return (unsigned int) __builtin_loongarch_iocsrrd_w ((unsigned int) _1);
+}
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  UDI, USI.  */
+extern __inline unsigned long int
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrrd_d (unsigned int _1)
+{
+  return (unsigned long int) __builtin_loongarch_iocsrrd_d ((unsigned int) _1);
+}
+#endif
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  VOID, UQI, USI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrwr_b (unsigned char _1, unsigned int _2)
+{
+  return (void) __builtin_loongarch_iocsrwr_b ((unsigned char) _1,
+					       (unsigned int) _2);
+}
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  VOID, UHI, USI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrwr_h (unsigned short _1, unsigned int _2)
+{
+  return (void) __builtin_loongarch_iocsrwr_h ((unsigned short) _1,
+					       (unsigned int) _2);
+}
+
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  VOID, USI, USI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrwr_w (unsigned int _1, unsigned int _2)
+{
+  return (void) __builtin_loongarch_iocsrwr_w ((unsigned int) _1,
+					       (unsigned int) _2);
+}
+
+#ifdef __loongarch64
+/* Assembly instruction format:	rd, rj.  */
+/* Data types in instruction templates:  VOID, UDI, USI.  */
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__iocsrwr_d (unsigned long int _1, unsigned int _2)
+{
+  return (void) __builtin_loongarch_iocsrwr_d ((unsigned long int) _1,
+					       (unsigned int) _2);
+}
+#endif
+
+/* Assembly instruction format:	ui15.  */
+/* Data types in instruction templates:  UQI.  */
+#define __dbar(/*ui15*/ _1) __builtin_loongarch_dbar ((_1))
+
+/* Assembly instruction format:	ui15.  */
+/* Data types in instruction templates:  UQI.  */
+#define __ibar(/*ui15*/ _1) __builtin_loongarch_ibar ((_1))
+
+#define __builtin_loongarch_syscall(a) \
+  { \
+    __asm__ volatile ("syscall %0\n\t" ::"I"(a)); \
+  }
+#define __syscall __builtin_loongarch_syscall
+
+#define __builtin_loongarch_break(a) \
+  { \
+    __asm__ volatile ("break %0\n\t" ::"I"(a)); \
+  }
+#define __break __builtin_loongarch_break
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbsrch (void)
+{
+  __asm__ volatile ("tlbsrch\n\t");
+}
+#define __tlbsrch __builtin_loongarch_tlbsrch
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbrd (void)
+{
+  __asm__ volatile ("tlbrd\n\t");
+}
+#define __tlbrd __builtin_loongarch_tlbrd
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbwr (void)
+{
+  __asm__ volatile ("tlbwr\n\t");
+}
+#define __tlbwr __builtin_loongarch_tlbwr
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbfill (void)
+{
+  __asm__ volatile ("tlbfill\n\t");
+}
+#define __tlbfill __builtin_loongarch_tlbfill
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbclr (void)
+{
+  __asm__ volatile ("tlbclr\n\t");
+}
+#define __tlbclr __builtin_loongarch_tlbclr
+
+extern __inline void
+__attribute__ ((__gnu_inline__, __always_inline__, __artificial__))
+__builtin_loongarch_tlbflush (void)
+{
+  __asm__ volatile ("tlbflush\n\t");
+}
+#define __tlbflush __builtin_loongarch_tlbflush
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _GCC_LOONGARCH_BASE_INTRIN_H */
diff --git a/gcc/config/loongarch/loongarch-builtins.c b/gcc/config/loongarch/loongarch-builtins.c
new file mode 100644
index 00000000000..53112f63b8a
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-builtins.c
@@ -0,0 +1,511 @@
+/* Subroutines used for expanding LoongArch builtins.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "gimple.h"
+#include "tm_p.h"
+#include "optabs.h"
+#include "recog.h"
+#include "diagnostic.h"
+#include "fold-const.h"
+#include "expr.h"
+#include "langhooks.h"
+
+/* Macros to create an enumeration identifier for a function prototype.  */
+#define LARCH_FTYPE_NAME1(A, B) LARCH_##A##_FTYPE_##B
+#define LARCH_FTYPE_NAME2(A, B, C) LARCH_##A##_FTYPE_##B##_##C
+#define LARCH_FTYPE_NAME3(A, B, C, D) LARCH_##A##_FTYPE_##B##_##C##_##D
+#define LARCH_FTYPE_NAME4(A, B, C, D, E) \
+  LARCH_##A##_FTYPE_##B##_##C##_##D##_##E
+
+/* Classifies the prototype of a built-in function.  */
+enum loongarch_function_type
+{
+#define DEF_LARCH_FTYPE(NARGS, LIST) LARCH_FTYPE_NAME##NARGS LIST,
+#include "config/loongarch/loongarch-ftypes.def"
+#undef DEF_LARCH_FTYPE
+  LARCH_MAX_FTYPE_MAX
+};
+
+/* Specifies how a built-in function should be converted into rtl.  */
+enum loongarch_builtin_type
+{
+  /* The function corresponds directly to an .md pattern.  The return
+     value is mapped to operand 0 and the arguments are mapped to
+     operands 1 and above.  */
+  LARCH_BUILTIN_DIRECT,
+
+  /* The function corresponds directly to an .md pattern.  There is no return
+     value and the arguments are mapped to operands 0 and above.  */
+  LARCH_BUILTIN_DIRECT_NO_TARGET,
+
+};
+
+/* Invoke MACRO (COND) for each fcmp.cond.{s/d} condition.  */
+#define LARCH_FP_CONDITIONS(MACRO) \
+  MACRO (f),	\
+  MACRO (un),	\
+  MACRO (eq),	\
+  MACRO (ueq),	\
+  MACRO (olt),	\
+  MACRO (ult),	\
+  MACRO (ole),	\
+  MACRO (ule),	\
+  MACRO (sf),	\
+  MACRO (ngle),	\
+  MACRO (seq),	\
+  MACRO (ngl),	\
+  MACRO (lt),	\
+  MACRO (nge),	\
+  MACRO (le),	\
+  MACRO (ngt)
+
+/* Enumerates the codes above as LARCH_FP_COND_<X>.  */
+#define DECLARE_LARCH_COND(X) LARCH_FP_COND_##X
+enum loongarch_fp_condition
+{
+  LARCH_FP_CONDITIONS (DECLARE_LARCH_COND)
+};
+#undef DECLARE_LARCH_COND
+
+/* Index X provides the string representation of LARCH_FP_COND_<X>.  */
+#define STRINGIFY(X) #X
+const char *const
+loongarch_fp_conditions[16]= {LARCH_FP_CONDITIONS (STRINGIFY)};
+#undef STRINGIFY
+
+/* Declare an availability predicate for built-in functions that require
+ * COND to be true.  NAME is the main part of the predicate's name.  */
+#define AVAIL_ALL(NAME, COND) \
+  static unsigned int \
+  loongarch_builtin_avail_##NAME (void) \
+  { \
+    return (COND) ? 1 : 0; \
+  }
+
+static unsigned int
+loongarch_builtin_avail_default (void)
+{
+  return 1;
+}
+/* This structure describes a single built-in function.  */
+struct loongarch_builtin_description
+{
+  /* The code of the main .md file instruction.  See loongarch_builtin_type
+     for more information.  */
+  enum insn_code icode;
+
+  /* The floating-point comparison code to use with ICODE, if any.  */
+  enum loongarch_fp_condition cond;
+
+  /* The name of the built-in function.  */
+  const char *name;
+
+  /* Specifies how the function should be expanded.  */
+  enum loongarch_builtin_type builtin_type;
+
+  /* The function's prototype.  */
+  enum loongarch_function_type function_type;
+
+  /* Whether the function is available.  */
+  unsigned int (*avail) (void);
+};
+
+AVAIL_ALL (hard_float, TARGET_HARD_FLOAT_ABI)
+
+/* Construct a loongarch_builtin_description from the given arguments.
+
+   INSN is the name of the associated instruction pattern, without the
+   leading CODE_FOR_loongarch_.
+
+   CODE is the floating-point condition code associated with the
+   function.  It can be 'f' if the field is not applicable.
+
+   NAME is the name of the function itself, without the leading
+   "__builtin_loongarch_".
+
+   BUILTIN_TYPE and FUNCTION_TYPE are loongarch_builtin_description fields.
+
+   AVAIL is the name of the availability predicate, without the leading
+   loongarch_builtin_avail_.  */
+#define LARCH_BUILTIN(INSN, COND, NAME, BUILTIN_TYPE, FUNCTION_TYPE, AVAIL) \
+  { \
+    CODE_FOR_loongarch_##INSN, LARCH_FP_COND_##COND, \
+      "__builtin_loongarch_" NAME, BUILTIN_TYPE, FUNCTION_TYPE, \
+      loongarch_builtin_avail_##AVAIL \
+  }
+
+/* Define __builtin_loongarch_<INSN>, which is a LARCH_BUILTIN_DIRECT function
+   mapped to instruction CODE_FOR_loongarch_<INSN>,  FUNCTION_TYPE and AVAIL
+   are as for LARCH_BUILTIN.  */
+#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+  LARCH_BUILTIN (INSN, f, #INSN, LARCH_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_loongarch_<INSN>, which is a LARCH_BUILTIN_DIRECT_NO_TARGET
+   function mapped to instruction CODE_FOR_loongarch_<INSN>,  FUNCTION_TYPE
+   and AVAIL are as for LARCH_BUILTIN.  */
+#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+  LARCH_BUILTIN (INSN, f, #INSN, LARCH_BUILTIN_DIRECT_NO_TARGET, \
+		 FUNCTION_TYPE, AVAIL)
+
+/* Loongson support crc.  */
+#define CODE_FOR_loongarch_crc_w_b_w CODE_FOR_crc_w_b_w
+#define CODE_FOR_loongarch_crc_w_h_w CODE_FOR_crc_w_h_w
+#define CODE_FOR_loongarch_crc_w_w_w CODE_FOR_crc_w_w_w
+#define CODE_FOR_loongarch_crc_w_d_w CODE_FOR_crc_w_d_w
+#define CODE_FOR_loongarch_crcc_w_b_w CODE_FOR_crcc_w_b_w
+#define CODE_FOR_loongarch_crcc_w_h_w CODE_FOR_crcc_w_h_w
+#define CODE_FOR_loongarch_crcc_w_w_w CODE_FOR_crcc_w_w_w
+#define CODE_FOR_loongarch_crcc_w_d_w CODE_FOR_crcc_w_d_w
+
+/* Privileged state instruction.  */
+#define CODE_FOR_loongarch_cpucfg CODE_FOR_cpucfg
+#define CODE_FOR_loongarch_asrtle_d CODE_FOR_asrtle_d
+#define CODE_FOR_loongarch_asrtgt_d CODE_FOR_asrtgt_d
+#define CODE_FOR_loongarch_csrrd CODE_FOR_csrrd
+#define CODE_FOR_loongarch_dcsrrd CODE_FOR_dcsrrd
+#define CODE_FOR_loongarch_csrwr CODE_FOR_csrwr
+#define CODE_FOR_loongarch_dcsrwr CODE_FOR_dcsrwr
+#define CODE_FOR_loongarch_csrxchg CODE_FOR_csrxchg
+#define CODE_FOR_loongarch_dcsrxchg CODE_FOR_dcsrxchg
+#define CODE_FOR_loongarch_iocsrrd_b CODE_FOR_iocsrrd_b
+#define CODE_FOR_loongarch_iocsrrd_h CODE_FOR_iocsrrd_h
+#define CODE_FOR_loongarch_iocsrrd_w CODE_FOR_iocsrrd_w
+#define CODE_FOR_loongarch_iocsrrd_d CODE_FOR_iocsrrd_d
+#define CODE_FOR_loongarch_iocsrwr_b CODE_FOR_iocsrwr_b
+#define CODE_FOR_loongarch_iocsrwr_h CODE_FOR_iocsrwr_h
+#define CODE_FOR_loongarch_iocsrwr_w CODE_FOR_iocsrwr_w
+#define CODE_FOR_loongarch_iocsrwr_d CODE_FOR_iocsrwr_d
+#define CODE_FOR_loongarch_lddir CODE_FOR_lddir
+#define CODE_FOR_loongarch_dlddir CODE_FOR_dlddir
+#define CODE_FOR_loongarch_ldpte CODE_FOR_ldpte
+#define CODE_FOR_loongarch_dldpte CODE_FOR_dldpte
+#define CODE_FOR_loongarch_cacop CODE_FOR_cacop
+#define CODE_FOR_loongarch_dcacop CODE_FOR_dcacop
+#define CODE_FOR_loongarch_dbar CODE_FOR_dbar
+#define CODE_FOR_loongarch_ibar CODE_FOR_ibar
+
+static const struct loongarch_builtin_description loongarch_builtins[] = {
+#define LARCH_MOVFCSR2GR 0
+  DIRECT_BUILTIN (movfcsr2gr, LARCH_USI_FTYPE_UQI, hard_float),
+#define LARCH_MOVGR2FCSR 1
+  DIRECT_NO_TARGET_BUILTIN (movgr2fcsr, LARCH_VOID_FTYPE_UQI_USI, hard_float),
+
+  DIRECT_NO_TARGET_BUILTIN (cacop, LARCH_VOID_FTYPE_USI_USI_SI, default),
+  DIRECT_NO_TARGET_BUILTIN (dcacop, LARCH_VOID_FTYPE_USI_UDI_SI, default),
+  DIRECT_NO_TARGET_BUILTIN (dbar, LARCH_VOID_FTYPE_USI, default),
+  DIRECT_NO_TARGET_BUILTIN (ibar, LARCH_VOID_FTYPE_USI, default),
+
+  DIRECT_BUILTIN (cpucfg, LARCH_USI_FTYPE_USI, default),
+  DIRECT_BUILTIN (asrtle_d, LARCH_VOID_FTYPE_DI_DI, default),
+  DIRECT_BUILTIN (asrtgt_d, LARCH_VOID_FTYPE_DI_DI, default),
+  DIRECT_BUILTIN (dlddir, LARCH_DI_FTYPE_DI_UQI, default),
+  DIRECT_BUILTIN (lddir, LARCH_SI_FTYPE_SI_UQI, default),
+  DIRECT_NO_TARGET_BUILTIN (dldpte, LARCH_VOID_FTYPE_DI_UQI, default),
+  DIRECT_NO_TARGET_BUILTIN (ldpte, LARCH_VOID_FTYPE_SI_UQI, default),
+
+  /* CRC Instrinsic */
+
+  DIRECT_BUILTIN (crc_w_b_w, LARCH_SI_FTYPE_QI_SI, default),
+  DIRECT_BUILTIN (crc_w_h_w, LARCH_SI_FTYPE_HI_SI, default),
+  DIRECT_BUILTIN (crc_w_w_w, LARCH_SI_FTYPE_SI_SI, default),
+  DIRECT_BUILTIN (crc_w_d_w, LARCH_SI_FTYPE_DI_SI, default),
+  DIRECT_BUILTIN (crcc_w_b_w, LARCH_SI_FTYPE_QI_SI, default),
+  DIRECT_BUILTIN (crcc_w_h_w, LARCH_SI_FTYPE_HI_SI, default),
+  DIRECT_BUILTIN (crcc_w_w_w, LARCH_SI_FTYPE_SI_SI, default),
+  DIRECT_BUILTIN (crcc_w_d_w, LARCH_SI_FTYPE_DI_SI, default),
+
+  DIRECT_BUILTIN (csrrd, LARCH_USI_FTYPE_USI, default),
+  DIRECT_BUILTIN (dcsrrd, LARCH_UDI_FTYPE_USI, default),
+  DIRECT_BUILTIN (csrwr, LARCH_USI_FTYPE_USI_USI, default),
+  DIRECT_BUILTIN (dcsrwr, LARCH_UDI_FTYPE_UDI_USI, default),
+  DIRECT_BUILTIN (csrxchg, LARCH_USI_FTYPE_USI_USI_USI, default),
+  DIRECT_BUILTIN (dcsrxchg, LARCH_UDI_FTYPE_UDI_UDI_USI, default),
+  DIRECT_BUILTIN (iocsrrd_b, LARCH_UQI_FTYPE_USI, default),
+  DIRECT_BUILTIN (iocsrrd_h, LARCH_UHI_FTYPE_USI, default),
+  DIRECT_BUILTIN (iocsrrd_w, LARCH_USI_FTYPE_USI, default),
+  DIRECT_BUILTIN (iocsrrd_d, LARCH_UDI_FTYPE_USI, default),
+  DIRECT_NO_TARGET_BUILTIN (iocsrwr_b, LARCH_VOID_FTYPE_UQI_USI, default),
+  DIRECT_NO_TARGET_BUILTIN (iocsrwr_h, LARCH_VOID_FTYPE_UHI_USI, default),
+  DIRECT_NO_TARGET_BUILTIN (iocsrwr_w, LARCH_VOID_FTYPE_USI_USI, default),
+  DIRECT_NO_TARGET_BUILTIN (iocsrwr_d, LARCH_VOID_FTYPE_UDI_USI, default),
+};
+
+/* Index I is the function declaration for loongarch_builtins[I], or null if
+   the function isn't defined on this target.  */
+static GTY (()) tree loongarch_builtin_decls[ARRAY_SIZE (loongarch_builtins)];
+/* Get the index I of the function declaration for loongarch_builtin_decls[I]
+   using the instruction code or return null if not defined for the target.  */
+static GTY (()) int loongarch_get_builtin_decl_index[NUM_INSN_CODES];
+
+/* Return a type for 'const volatile void *'.  */
+
+static tree
+loongarch_build_cvpointer_type (void)
+{
+  static tree cache;
+
+  if (cache == NULL_TREE)
+    cache = build_pointer_type (build_qualified_type (void_type_node,
+						      TYPE_QUAL_CONST
+						       | TYPE_QUAL_VOLATILE));
+  return cache;
+}
+
+/* Source-level argument types.  */
+#define LARCH_ATYPE_VOID void_type_node
+#define LARCH_ATYPE_INT integer_type_node
+#define LARCH_ATYPE_POINTER ptr_type_node
+#define LARCH_ATYPE_CVPOINTER loongarch_build_cvpointer_type ()
+
+/* Standard mode-based argument types.  */
+#define LARCH_ATYPE_QI intQI_type_node
+#define LARCH_ATYPE_UQI unsigned_intQI_type_node
+#define LARCH_ATYPE_HI intHI_type_node
+#define LARCH_ATYPE_UHI unsigned_intHI_type_node
+#define LARCH_ATYPE_SI intSI_type_node
+#define LARCH_ATYPE_USI unsigned_intSI_type_node
+#define LARCH_ATYPE_DI intDI_type_node
+#define LARCH_ATYPE_UDI unsigned_intDI_type_node
+#define LARCH_ATYPE_SF float_type_node
+#define LARCH_ATYPE_DF double_type_node
+
+/* LARCH_FTYPE_ATYPESN takes N LARCH_FTYPES-like type codes and lists
+   their associated LARCH_ATYPEs.  */
+#define LARCH_FTYPE_ATYPES1(A, B) LARCH_ATYPE_##A, LARCH_ATYPE_##B
+
+#define LARCH_FTYPE_ATYPES2(A, B, C) \
+  LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C
+
+#define LARCH_FTYPE_ATYPES3(A, B, C, D) \
+  LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C, LARCH_ATYPE_##D
+
+#define LARCH_FTYPE_ATYPES4(A, B, C, D, E) \
+  LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C, LARCH_ATYPE_##D, \
+  LARCH_ATYPE_##E
+
+/* Return the function type associated with function prototype TYPE.  */
+
+static tree
+loongarch_build_function_type (enum loongarch_function_type type)
+{
+  static tree types[(int) LARCH_MAX_FTYPE_MAX];
+
+  if (types[(int) type] == NULL_TREE)
+    switch (type)
+      {
+#define DEF_LARCH_FTYPE(NUM, ARGS) \
+  case LARCH_FTYPE_NAME##NUM ARGS: \
+    types[(int) type] \
+      = build_function_type_list (LARCH_FTYPE_ATYPES##NUM ARGS, NULL_TREE); \
+    break;
+#include "config/loongarch/loongarch-ftypes.def"
+#undef DEF_LARCH_FTYPE
+      default:
+	gcc_unreachable ();
+      }
+
+  return types[(int) type];
+}
+
+/* Implement TARGET_INIT_BUILTINS.  */
+
+void
+loongarch_init_builtins (void)
+{
+  const struct loongarch_builtin_description *d;
+  unsigned int i;
+  tree type;
+
+  /* Iterate through all of the bdesc arrays, initializing all of the
+     builtin functions.  */
+  for (i = 0; i < ARRAY_SIZE (loongarch_builtins); i++)
+    {
+      d = &loongarch_builtins[i];
+      if (d->avail ())
+	{
+	  type = loongarch_build_function_type (d->function_type);
+	  loongarch_builtin_decls[i]
+	    = add_builtin_function (d->name, type, i, BUILT_IN_MD, NULL,
+				    NULL);
+	  loongarch_get_builtin_decl_index[d->icode] = i;
+	}
+    }
+}
+
+/* Implement TARGET_BUILTIN_DECL.  */
+
+tree
+loongarch_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+  if (code >= ARRAY_SIZE (loongarch_builtins))
+    return error_mark_node;
+  return loongarch_builtin_decls[code];
+}
+
+/* Take argument ARGNO from EXP's argument list and convert it into
+   an expand operand.  Store the operand in *OP.  */
+
+static void
+loongarch_prepare_builtin_arg (struct expand_operand *op, tree exp,
+			       unsigned int argno)
+{
+  tree arg;
+  rtx value;
+
+  arg = CALL_EXPR_ARG (exp, argno);
+  value = expand_normal (arg);
+  create_input_operand (op, value, TYPE_MODE (TREE_TYPE (arg)));
+}
+
+/* Expand instruction ICODE as part of a built-in function sequence.
+   Use the first NOPS elements of OPS as the instruction's operands.
+   HAS_TARGET_P is true if operand 0 is a target; it is false if the
+   instruction has no target.
+
+   Return the target rtx if HAS_TARGET_P, otherwise return const0_rtx.  */
+
+static rtx
+loongarch_expand_builtin_insn (enum insn_code icode, unsigned int nops,
+			       struct expand_operand *ops, bool has_target_p)
+{
+  if (!maybe_expand_insn (icode, nops, ops))
+    {
+      error ("invalid argument to built-in function");
+      return has_target_p ? gen_reg_rtx (ops[0].mode) : const0_rtx;
+    }
+  return has_target_p ? ops[0].value : const0_rtx;
+}
+
+/* Expand a LARCH_BUILTIN_DIRECT or LARCH_BUILTIN_DIRECT_NO_TARGET function;
+   HAS_TARGET_P says which.  EXP is the CALL_EXPR that calls the function
+   and ICODE is the code of the associated .md pattern.  TARGET, if nonnull,
+   suggests a good place to put the result.  */
+
+static rtx
+loongarch_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+				 bool has_target_p)
+{
+  struct expand_operand ops[MAX_RECOG_OPERANDS];
+  int opno, argno;
+
+  /* Map any target to operand 0.  */
+  opno = 0;
+  if (has_target_p)
+    create_output_operand (&ops[opno++], target, TYPE_MODE (TREE_TYPE (exp)));
+
+  /* Map the arguments to the other operands.  */
+  gcc_assert (opno + call_expr_nargs (exp)
+	      == insn_data[icode].n_generator_args);
+  for (argno = 0; argno < call_expr_nargs (exp); argno++)
+    loongarch_prepare_builtin_arg (&ops[opno++], exp, argno);
+
+  return loongarch_expand_builtin_insn (icode, opno, ops, has_target_p);
+}
+
+/* Implement TARGET_EXPAND_BUILTIN.  */
+
+rtx
+loongarch_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+			  machine_mode mode ATTRIBUTE_UNUSED,
+			  int ignore ATTRIBUTE_UNUSED)
+{
+  tree fndecl;
+  unsigned int fcode, avail;
+  const struct loongarch_builtin_description *d;
+
+  fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  fcode = DECL_MD_FUNCTION_CODE (fndecl);
+  gcc_assert (fcode < ARRAY_SIZE (loongarch_builtins));
+  d = &loongarch_builtins[fcode];
+  avail = d->avail ();
+  gcc_assert (avail != 0);
+  switch (d->builtin_type)
+    {
+    case LARCH_BUILTIN_DIRECT:
+      return loongarch_expand_builtin_direct (d->icode, target, exp, true);
+
+    case LARCH_BUILTIN_DIRECT_NO_TARGET:
+      return loongarch_expand_builtin_direct (d->icode, target, exp, false);
+    }
+  gcc_unreachable ();
+}
+
+/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV.  */
+
+void
+loongarch_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+  if (!TARGET_HARD_FLOAT_ABI)
+    return;
+  tree exceptions_var = create_tmp_var_raw (LARCH_ATYPE_USI);
+  tree fcsr_orig_var = create_tmp_var_raw (LARCH_ATYPE_USI);
+  tree fcsr_mod_var = create_tmp_var_raw (LARCH_ATYPE_USI);
+  tree const0 = build_int_cst (LARCH_ATYPE_UQI, 0);
+  tree get_fcsr = loongarch_builtin_decls[LARCH_MOVFCSR2GR];
+  tree set_fcsr = loongarch_builtin_decls[LARCH_MOVGR2FCSR];
+  tree get_fcsr_hold_call = build_call_expr (get_fcsr, 1, const0);
+  tree hold_assign_orig = build4 (TARGET_EXPR, LARCH_ATYPE_USI,
+				  fcsr_orig_var, get_fcsr_hold_call,
+				  NULL, NULL);
+  tree hold_mod_val = build2 (BIT_AND_EXPR, LARCH_ATYPE_USI, fcsr_orig_var,
+			      build_int_cst (LARCH_ATYPE_USI, 0xffe0ffe0));
+  tree hold_assign_mod = build4 (TARGET_EXPR, LARCH_ATYPE_USI,
+				 fcsr_mod_var, hold_mod_val, NULL, NULL);
+  tree set_fcsr_hold_call = build_call_expr (set_fcsr, 2, const0,
+					     fcsr_mod_var);
+  tree hold_all = build2 (COMPOUND_EXPR, LARCH_ATYPE_USI, hold_assign_orig,
+			  hold_assign_mod);
+  *hold = build2 (COMPOUND_EXPR, void_type_node, hold_all, set_fcsr_hold_call);
+
+  *clear = build_call_expr (set_fcsr, 2, const0, fcsr_mod_var);
+
+  tree get_fcsr_update_call = build_call_expr (get_fcsr, 1, const0);
+  *update = build4 (TARGET_EXPR, LARCH_ATYPE_USI, exceptions_var,
+		    get_fcsr_update_call, NULL, NULL);
+  tree set_fcsr_update_call = build_call_expr (set_fcsr, 2, const0,
+					       fcsr_orig_var);
+  *update = build2 (COMPOUND_EXPR, void_type_node, *update,
+		    set_fcsr_update_call);
+  tree atomic_feraiseexcept
+    = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
+  tree int_exceptions_var = fold_convert (integer_type_node, exceptions_var);
+  tree atomic_feraiseexcept_call = build_call_expr (atomic_feraiseexcept, 1,
+						    int_exceptions_var);
+  *update = build2 (COMPOUND_EXPR, void_type_node, *update,
+		    atomic_feraiseexcept_call);
+}
+
+/* Implement TARGET_BUILTIN_VA_LIST.  */
+
+tree
+loongarch_build_builtin_va_list (void)
+{
+  return ptr_type_node;
+}
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 07/12] LoongArch Port: Builtin macros.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (5 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 06/12] LoongArch Port: Builtin functions chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 08/12] LoongArch Port: libgcc chenglulu
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/

	*config/loongarch/loongarch-c.c
---
 gcc/config/loongarch/loongarch-c.c | 111 +++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)
 create mode 100644 gcc/config/loongarch/loongarch-c.c

diff --git a/gcc/config/loongarch/loongarch-c.c b/gcc/config/loongarch/loongarch-c.c
new file mode 100644
index 00000000000..b577b2f952c
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-c.c
@@ -0,0 +1,111 @@
+/* LoongArch-specific code for C family languages.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "c-family/c-common.h"
+#include "cpplib.h"
+
+#define preprocessing_asm_p() (cpp_get_options (pfile)->lang == CLK_ASM)
+#define builtin_define(TXT) cpp_define (pfile, TXT)
+#define builtin_assert(TXT) cpp_assert (pfile, TXT)
+
+/* Define preprocessor macros for the -march and -mtune options.
+   PREFIX is either _LOONGARCH_ARCH or _LOONGARCH_TUNE, INFO is
+   the selected processor.  If INFO's canonical name is "foo",
+   define PREFIX to be "foo", and define an additional macro
+   PREFIX_FOO.  */
+#define LARCH_CPP_SET_PROCESSOR(PREFIX, CPU_TYPE)			\
+  do									\
+    {									\
+      char *macro, *p;							\
+      int cpu_type = (CPU_TYPE);					\
+									\
+      macro = concat ((PREFIX), "_",					\
+		      loongarch_cpu_strings[cpu_type], NULL);		\
+      for (p = macro; *p != 0; p++)					\
+	*p = TOUPPER (*p);						\
+									\
+      builtin_define (macro);						\
+      builtin_define_with_value ((PREFIX),				\
+				 loongarch_cpu_strings[cpu_type], 1);	\
+      free (macro);							\
+    }									\
+  while (0)
+
+/* TODO: what is the pfile technique ??? !!! */
+
+void
+loongarch_cpu_cpp_builtins (cpp_reader *pfile)
+{
+  builtin_assert ("machine=loongarch");
+  builtin_assert ("cpu=loongarch");
+  builtin_define ("__loongarch__");
+
+  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_ARCH", __ACTUAL_ARCH);
+  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_TUNE", __ACTUAL_TUNE);
+
+  /* Base architecture / ABI.  */
+  if (TARGET_64BIT)
+    {
+      builtin_define ("__loongarch_grlen=64");
+      builtin_define ("__loongarch64");
+    }
+
+  if (TARGET_ABI_LP64)
+    {
+      builtin_define ("_ABILP64=3");
+      builtin_define ("_LOONGARCH_SIM=_ABILP64");
+      builtin_define ("__loongarch_lp64");
+    }
+
+  /* These defines reflect the ABI in use, not whether the
+     FPU is directly accessible.  */
+  if (TARGET_DOUBLE_FLOAT_ABI)
+    builtin_define ("__loongarch_double_float=1");
+  else if (TARGET_SINGLE_FLOAT_ABI)
+    builtin_define ("__loongarch_single_float=1");
+
+  if (TARGET_DOUBLE_FLOAT_ABI || TARGET_SINGLE_FLOAT_ABI)
+    builtin_define ("__loongarch_hard_float=1");
+  else
+    builtin_define ("__loongarch_soft_float=1");
+
+
+  /* ISA Extensions.  */
+  if (TARGET_DOUBLE_FLOAT)
+    builtin_define ("__loongarch_frlen=64");
+  else if (TARGET_SINGLE_FLOAT)
+    builtin_define ("__loongarch_frlen=32");
+  else
+    builtin_define ("__loongarch_frlen=0");
+
+  /* Native Data Sizes.  */
+  builtin_define_with_int_value ("_LOONGARCH_SZINT", INT_TYPE_SIZE);
+  builtin_define_with_int_value ("_LOONGARCH_SZLONG", LONG_TYPE_SIZE);
+  builtin_define_with_int_value ("_LOONGARCH_SZPTR", POINTER_SIZE);
+  builtin_define_with_int_value ("_LOONGARCH_FPSET", 32 / MAX_FPRS_PER_FMT);
+  builtin_define_with_int_value ("_LOONGARCH_SPFPSET", 32);
+
+}
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 08/12] LoongArch Port: libgcc
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (6 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 07/12] LoongArch Port: Builtin macros chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 09/12] LoongArch Port: Regenerate libgcc/configure chenglulu
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

libgcc/

        * config/loongarch/crtfastmath.c: New file.
        * config/loongarch/crti.S: Like wise.
        * config/loongarch/crtn.S: Like wise.
        * config/loongarch/lib2funcs.c: Like wise.
        * config/loongarch/linux-unwind.h: Like wise.
        * config/loongarch/sfp-machine.h: Like wise.
        * config/loongarch/t-crtstuff: Like wise.
        * config/loongarch/t-elf: Like wise.
        * config/loongarch/t-loongarch: Like wise.
        * config/loongarch/t-loongarch64: Like wise.
        * config/loongarch/t-softfp-tf: Like wise.
        * config.host: Add LoongArch tuples.
        * configure.ac: Add LoongArch support.
---
 libgcc/config.host                     |  28 ++++-
 libgcc/config/loongarch/crtfastmath.c  |  52 +++++++++
 libgcc/config/loongarch/crti.S         |  43 +++++++
 libgcc/config/loongarch/crtn.S         |  39 +++++++
 libgcc/config/loongarch/lib2funcs.c    |   0
 libgcc/config/loongarch/linux-unwind.h |  80 +++++++++++++
 libgcc/config/loongarch/sfp-machine.h  | 152 +++++++++++++++++++++++++
 libgcc/config/loongarch/t-crtstuff     |   2 +
 libgcc/config/loongarch/t-elf          |   3 +
 libgcc/config/loongarch/t-loongarch    |   9 ++
 libgcc/config/loongarch/t-loongarch64  |   1 +
 libgcc/config/loongarch/t-softfp-tf    |   3 +
 libgcc/configure.ac                    |   2 +-
 13 files changed, 412 insertions(+), 2 deletions(-)
 create mode 100644 libgcc/config/loongarch/crtfastmath.c
 create mode 100644 libgcc/config/loongarch/crti.S
 create mode 100644 libgcc/config/loongarch/crtn.S
 create mode 100644 libgcc/config/loongarch/lib2funcs.c
 create mode 100644 libgcc/config/loongarch/linux-unwind.h
 create mode 100644 libgcc/config/loongarch/sfp-machine.h
 create mode 100644 libgcc/config/loongarch/t-crtstuff
 create mode 100644 libgcc/config/loongarch/t-elf
 create mode 100644 libgcc/config/loongarch/t-loongarch
 create mode 100644 libgcc/config/loongarch/t-loongarch64
 create mode 100644 libgcc/config/loongarch/t-softfp-tf

diff --git a/libgcc/config.host b/libgcc/config.host
index ad0cdb284c7..7e9da09cb80 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -138,6 +138,22 @@ hppa*-*-*)
 lm32*-*-*)
 	cpu_type=lm32
 	;;
+loongarch*-*-*)
+	cpu_type=loongarch
+	tmake_file="loongarch/t-loongarch"
+	if test "${libgcc_cv_loongarch_hard_float}" = yes; then
+		tmake_file="${tmake_file} t-hardfp-sfdf t-hardfp"
+	else
+		tmake_file="${tmake_file} t-softfp-sfdf"
+	fi
+	if test "${ac_cv_sizeof_long_double}" = 16; then
+		tmake_file="${tmake_file} loongarch/t-softfp-tf"
+	fi
+	if test "${host_address}" = 64; then
+		tmake_file="${tmake_file} loongarch/t-loongarch64"
+	fi
+	tmake_file="${tmake_file} t-softfp"
+	;;
 m32r*-*-*)
         cpu_type=m32r
         ;;
@@ -925,7 +941,17 @@ lm32-*-rtems*)
 lm32-*-uclinux*)
         extra_parts="$extra_parts crtbegin.o crtendS.o crtbeginT.o"
         tmake_file="lm32/t-lm32 lm32/t-uclinux t-libgcc-pic t-softfp-sfdf t-softfp"
-	;;	
+	;;
+loongarch*-*-linux*)
+	extra_parts="$extra_parts crtfastmath.o"
+	tmake_file="${tmake_file} t-crtfm"
+	case ${host} in
+	  *)
+	    tmake_file="${tmake_file} t-slibgcc-libgcc"
+	    ;;
+	esac
+	md_unwind_header=loongarch/linux-unwind.h
+	;;
 m32r-*-elf*)
 	tmake_file="$tmake_file m32r/t-m32r t-fdpbit"
 	extra_parts="$extra_parts crtinit.o crtfini.o"
diff --git a/libgcc/config/loongarch/crtfastmath.c b/libgcc/config/loongarch/crtfastmath.c
new file mode 100644
index 00000000000..3523d531e2b
--- /dev/null
+++ b/libgcc/config/loongarch/crtfastmath.c
@@ -0,0 +1,52 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on MIPS target for GNU compiler.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License
+and a copy of the GCC Runtime Library Exception along with this
+program; see the files COPYING3 and COPYING.RUNTIME respectively.
+If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef __loongarch_hard_float
+
+/* Rounding control.  */
+#define _FPU_RC_NEAREST 0x000     /* RECOMMENDED.  */
+#define _FPU_RC_ZERO    0x100
+#define _FPU_RC_UP      0x200
+#define _FPU_RC_DOWN    0x300
+
+/* Enable interrupts for IEEE exceptions.  */
+#define _FPU_IEEE     0x0000001F
+
+/* Macros for accessing the hardware control word.  */
+#define _FPU_GETCW(cw) __asm__ volatile ("movfcsr2gr %0,$r0" : "=r" (cw))
+#define _FPU_SETCW(cw) __asm__ volatile ("movgr2fcsr $r0,%0" : : "r" (cw))
+
+static void __attribute__((constructor))
+set_fast_math (void)
+{
+  unsigned int fcr;
+
+  /* Flush to zero, round to nearest, IEEE exceptions disabled.  */
+  fcr = _FPU_RC_NEAREST;
+
+  _FPU_SETCW (fcr);
+}
+
+#endif /* __loongarch_hard_float  */
diff --git a/libgcc/config/loongarch/crti.S b/libgcc/config/loongarch/crti.S
new file mode 100644
index 00000000000..bac029874b5
--- /dev/null
+++ b/libgcc/config/loongarch/crti.S
@@ -0,0 +1,43 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option) any later
+version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* 4 slots for argument spill area.  1 for cpreturn, 1 for stack.
+   Return spill offset of 8.  Aligned to 16 bytes for lp64.  */
+
+	.section .init,"ax",@progbits
+	.globl	_init
+	.type	_init,@function
+_init:
+	addi.d   $r3,$r3,-16
+	st.d      $r1,$r3,8
+	addi.d   $r3,$r3,16
+	jirl	$r0,$r1,0
+
+	.section .fini,"ax",@progbits
+	.globl	_fini
+	.type	_fini,@function
+_fini:
+	addi.d   $r3,$r3,-16
+	st.d      $r1,$r3,8
+	addi.d   $r3,$r3,16
+	jirl	$r0,$r1,0
diff --git a/libgcc/config/loongarch/crtn.S b/libgcc/config/loongarch/crtn.S
new file mode 100644
index 00000000000..7af6728690f
--- /dev/null
+++ b/libgcc/config/loongarch/crtn.S
@@ -0,0 +1,39 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option) any later
+version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* 4 slots for argument spill area.  1 for cpreturn, 1 for stack.
+   Return spill offset of 8.  Aligned to 16 bytes for lp64.  */
+
+
+	.section .init,"ax",@progbits
+init:
+	ld.d      $r1,$r3,8
+	addi.d	$r3,$r3,16
+	jirl	$r0,$r1,0
+
+	.section .fini,"ax",@progbits
+fini:
+	ld.d	$r1,$r3,8
+	addi.d	$r3,$r3,16
+	jirl	$r0,$r1,0
+
diff --git a/libgcc/config/loongarch/lib2funcs.c b/libgcc/config/loongarch/lib2funcs.c
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libgcc/config/loongarch/linux-unwind.h b/libgcc/config/loongarch/linux-unwind.h
new file mode 100644
index 00000000000..8c9dfa56a6d
--- /dev/null
+++ b/libgcc/config/loongarch/linux-unwind.h
@@ -0,0 +1,80 @@
+/* DWARF2 EH unwinding support for LoongArch Linux.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option)
+any later version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef inhibit_libc
+/* Do code reading to identify a signal frame, and set the frame
+   state data appropriately.  See unwind-dw2.c for the structs.  */
+
+#include <signal.h>
+#include <sys/syscall.h>
+#include <sys/ucontext.h>
+
+#define MD_FALLBACK_FRAME_STATE_FOR loongarch_fallback_frame_state
+
+static _Unwind_Reason_Code
+loongarch_fallback_frame_state (struct _Unwind_Context *context,
+				_Unwind_FrameState *fs)
+{
+  u_int32_t *pc = (u_int32_t *) context->ra;
+  struct sigcontext *sc;
+  _Unwind_Ptr new_cfa;
+  int i;
+
+  /* 03822c0b dli a7, 0x8b (sigreturn)  */
+  /* 002b0000 syscall 0  */
+  if (pc[1] != 0x002b0000)
+    return _URC_END_OF_STACK;
+  if (pc[0] == 0x03822c0b)
+    {
+      struct rt_sigframe
+      {
+	siginfo_t info;
+	ucontext_t uc;
+      } *rt_ = context->cfa;
+      sc = &rt_->uc.uc_mcontext;
+    }
+  else
+    return _URC_END_OF_STACK;
+
+  new_cfa = (_Unwind_Ptr) sc;
+  fs->regs.cfa_how = CFA_REG_OFFSET;
+  fs->regs.cfa_reg = __LIBGCC_STACK_POINTER_REGNUM__;
+  fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;
+
+  for (i = 0; i < 32; i++)
+    {
+      fs->regs.reg[i].how = REG_SAVED_OFFSET;
+      fs->regs.reg[i].loc.offset = (_Unwind_Ptr) & (sc->sc_regs[i]) - new_cfa;
+    }
+
+  fs->signal_frame = 1;
+  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].how
+    = REG_SAVED_VAL_OFFSET;
+  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset
+    = (_Unwind_Ptr) (sc->sc_pc) - new_cfa;
+  fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__;
+
+  return _URC_NO_REASON;
+}
+#endif
diff --git a/libgcc/config/loongarch/sfp-machine.h b/libgcc/config/loongarch/sfp-machine.h
new file mode 100644
index 00000000000..cb5b04feede
--- /dev/null
+++ b/libgcc/config/loongarch/sfp-machine.h
@@ -0,0 +1,152 @@
+/* softfp machine description for LoongArch.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option) any later
+version.
+
+GCC 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifdef __loongarch64
+#define _FP_W_TYPE_SIZE 64
+#define _FP_W_TYPE unsigned long long
+#define _FP_WS_TYPE signed long long
+#define _FP_I_TYPE long long
+
+typedef int TItype __attribute__ ((mode (TI)));
+typedef unsigned int UTItype __attribute__ ((mode (TI)));
+#define TI_BITS (__CHAR_BIT__ * (int) sizeof (TItype))
+
+#define _FP_MUL_MEAT_S(R, X, Y) \
+  _FP_MUL_MEAT_1_wide (_FP_WFRACBITS_S, R, X, Y, umul_ppmm)
+#define _FP_MUL_MEAT_D(R, X, Y) \
+  _FP_MUL_MEAT_1_wide (_FP_WFRACBITS_D, R, X, Y, umul_ppmm)
+#define _FP_MUL_MEAT_Q(R, X, Y) \
+  _FP_MUL_MEAT_2_wide (_FP_WFRACBITS_Q, R, X, Y, umul_ppmm)
+
+#define _FP_DIV_MEAT_S(R, X, Y) _FP_DIV_MEAT_1_udiv_norm (S, R, X, Y)
+#define _FP_DIV_MEAT_D(R, X, Y) _FP_DIV_MEAT_1_udiv_norm (D, R, X, Y)
+#define _FP_DIV_MEAT_Q(R, X, Y) _FP_DIV_MEAT_2_udiv (Q, R, X, Y)
+
+#define _FP_NANFRAC_S ((_FP_QNANBIT_S << 1) - 1)
+#define _FP_NANFRAC_D ((_FP_QNANBIT_D << 1) - 1)
+#define _FP_NANFRAC_Q ((_FP_QNANBIT_Q << 1) - 1), -1
+#else
+#define _FP_W_TYPE_SIZE 32
+#define _FP_W_TYPE unsigned int
+#define _FP_WS_TYPE signed int
+#define _FP_I_TYPE int
+
+#define _FP_MUL_MEAT_S(R, X, Y) \
+  _FP_MUL_MEAT_1_wide (_FP_WFRACBITS_S, R, X, Y, umul_ppmm)
+#define _FP_MUL_MEAT_D(R, X, Y) \
+  _FP_MUL_MEAT_2_wide (_FP_WFRACBITS_D, R, X, Y, umul_ppmm)
+#define _FP_MUL_MEAT_Q(R, X, Y) \
+  _FP_MUL_MEAT_4_wide (_FP_WFRACBITS_Q, R, X, Y, umul_ppmm)
+
+#define _FP_DIV_MEAT_S(R, X, Y) _FP_DIV_MEAT_1_udiv_norm (S, R, X, Y)
+#define _FP_DIV_MEAT_D(R, X, Y) _FP_DIV_MEAT_2_udiv (D, R, X, Y)
+#define _FP_DIV_MEAT_Q(R, X, Y) _FP_DIV_MEAT_4_udiv (Q, R, X, Y)
+
+#define _FP_NANFRAC_S ((_FP_QNANBIT_S << 1) - 1)
+#define _FP_NANFRAC_D ((_FP_QNANBIT_D << 1) - 1), -1
+#define _FP_NANFRAC_Q ((_FP_QNANBIT_Q << 1) - 1), -1, -1, -1
+#endif
+
+/* The type of the result of a floating point comparison.  This must
+   match __libgcc_cmp_return__ in GCC for the target.  */
+typedef int __gcc_CMPtype __attribute__ ((mode (__libgcc_cmp_return__)));
+#define CMPtype __gcc_CMPtype
+
+#define _FP_NANSIGN_S 0
+#define _FP_NANSIGN_D 0
+#define _FP_NANSIGN_Q 0
+
+#define _FP_KEEPNANFRACP 1
+#define _FP_QNANNEGATEDP 0
+
+/* NaN payloads should be preserved for NAN2008.  */
+#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \
+  do \
+    { \
+      R##_s = X##_s; \
+      _FP_FRAC_COPY_##wc (R, X); \
+      R##_c = FP_CLS_NAN; \
+    } \
+  while (0)
+
+#ifdef __loongarch_hard_float
+#define FP_EX_INVALID 0x100000
+#define FP_EX_DIVZERO 0x080000
+#define FP_EX_OVERFLOW 0x040000
+#define FP_EX_UNDERFLOW 0x020000
+#define FP_EX_INEXACT 0x010000
+#define FP_EX_ALL \
+  (FP_EX_INVALID | FP_EX_DIVZERO | FP_EX_OVERFLOW | FP_EX_UNDERFLOW \
+   | FP_EX_INEXACT)
+
+#define FP_EX_ENABLE_SHIFT 16
+#define FP_EX_CAUSE_SHIFT 8
+
+#define FP_RND_NEAREST 0x000
+#define FP_RND_ZERO 0x100
+#define FP_RND_PINF 0x200
+#define FP_RND_MINF 0x300
+#define FP_RND_MASK 0x300
+
+#define _FP_DECL_EX \
+  unsigned long int _fcsr __attribute__ ((unused)) = FP_RND_NEAREST
+
+#define FP_INIT_ROUNDMODE \
+  do \
+    { \
+      _fcsr = __builtin_loongarch_movfcsr2gr (0); \
+    } \
+  while (0)
+
+#define FP_ROUNDMODE (_fcsr & FP_RND_MASK)
+
+#define FP_TRAPPING_EXCEPTIONS ((_fcsr << FP_EX_ENABLE_SHIFT) & FP_EX_ALL)
+
+#define FP_HANDLE_EXCEPTIONS \
+  do \
+    { \
+      _fcsr &= ~(FP_EX_ALL << FP_EX_CAUSE_SHIFT); \
+      _fcsr |= _fex | (_fex << FP_EX_CAUSE_SHIFT); \
+      __builtin_loongarch_movgr2fcsr (0, _fcsr); \
+    } \
+  while (0)
+
+#else
+#define FP_EX_INVALID (1 << 4)
+#define FP_EX_DIVZERO (1 << 3)
+#define FP_EX_OVERFLOW (1 << 2)
+#define FP_EX_UNDERFLOW (1 << 1)
+#define FP_EX_INEXACT (1 << 0)
+#endif
+
+#define _FP_TININESS_AFTER_ROUNDING 1
+
+#define __LITTLE_ENDIAN 1234
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+/* Define ALIASNAME as a strong alias for NAME.  */
+#define strong_alias(name, aliasname) _strong_alias (name, aliasname)
+#define _strong_alias(name, aliasname) \
+  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
diff --git a/libgcc/config/loongarch/t-crtstuff b/libgcc/config/loongarch/t-crtstuff
new file mode 100644
index 00000000000..d8ab43be2ae
--- /dev/null
+++ b/libgcc/config/loongarch/t-crtstuff
@@ -0,0 +1,2 @@
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
diff --git a/libgcc/config/loongarch/t-elf b/libgcc/config/loongarch/t-elf
new file mode 100644
index 00000000000..651f10a5303
--- /dev/null
+++ b/libgcc/config/loongarch/t-elf
@@ -0,0 +1,3 @@
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+HOST_LIBGCC2_CFLAGS += -G 0
diff --git a/libgcc/config/loongarch/t-loongarch b/libgcc/config/loongarch/t-loongarch
new file mode 100644
index 00000000000..9508cb2fcae
--- /dev/null
+++ b/libgcc/config/loongarch/t-loongarch
@@ -0,0 +1,9 @@
+LIB2_SIDITI_CONV_FUNCS = yes
+
+softfp_float_modes :=
+softfp_int_modes := si di
+softfp_extensions :=
+softfp_truncations :=
+softfp_exclude_libgcc2 := n
+
+LIB2ADD_ST += $(srcdir)/config/loongarch/lib2funcs.c
diff --git a/libgcc/config/loongarch/t-loongarch64 b/libgcc/config/loongarch/t-loongarch64
new file mode 100644
index 00000000000..a1e3513e288
--- /dev/null
+++ b/libgcc/config/loongarch/t-loongarch64
@@ -0,0 +1 @@
+softfp_int_modes += ti
diff --git a/libgcc/config/loongarch/t-softfp-tf b/libgcc/config/loongarch/t-softfp-tf
new file mode 100644
index 00000000000..306677b1255
--- /dev/null
+++ b/libgcc/config/loongarch/t-softfp-tf
@@ -0,0 +1,3 @@
+softfp_float_modes += tf
+softfp_extensions += sftf dftf
+softfp_truncations += tfsf tfdf
diff --git a/libgcc/configure.ac b/libgcc/configure.ac
index 13a80b2551b..b17d788dbd0 100644
--- a/libgcc/configure.ac
+++ b/libgcc/configure.ac
@@ -317,7 +317,7 @@ AC_CACHE_CHECK([whether assembler supports CFI directives], [libgcc_cv_cfi],
 # word size rather than the address size.
 cat > conftest.c <<EOF
 #if defined(__x86_64__) || (!defined(__i386__) && defined(__LP64__)) \
-    || defined(__mips64)
+    || defined(__mips64) || defined(__loongarch64)
 host_address=64
 #else
 host_address=32
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 09/12] LoongArch Port: Regenerate libgcc/configure.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (7 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 08/12] LoongArch Port: libgcc chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 10/12] LoongArch Port: libgomp chenglulu
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

---
 libgcc/configure | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libgcc/configure b/libgcc/configure
index 4919a56f518..ce04c4f529f 100755
--- a/libgcc/configure
+++ b/libgcc/configure
@@ -2412,6 +2412,9 @@ case "${host}" in
 	# sets the default TLS model and affects inlining.
 	PICFLAG=-fPIC
 	;;
+    loongarch*-*-*)
+	PICFLAG=-fpic
+	;;
     mips-sgi-irix6*)
 	# PIC is the default.
 	;;
@@ -5066,7 +5069,7 @@ $as_echo "$libgcc_cv_cfi" >&6; }
 # word size rather than the address size.
 cat > conftest.c <<EOF
 #if defined(__x86_64__) || (!defined(__i386__) && defined(__LP64__)) \
-    || defined(__mips64)
+    || defined(__mips64) || defined(__loongarch64)
 host_address=64
 #else
 host_address=32
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 10/12] LoongArch Port: libgomp
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (8 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 09/12] LoongArch Port: Regenerate libgcc/configure chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 11/12] LoongArch Port: gcc/testsuite chenglulu
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

libgomp/

        * configure.tgt: Add LoongArch triplet.
---
 libgomp/configure.tgt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libgomp/configure.tgt b/libgomp/configure.tgt
index d4f1e741b5a..2cd7272fcd8 100644
--- a/libgomp/configure.tgt
+++ b/libgomp/configure.tgt
@@ -56,6 +56,10 @@ if test x$enable_linux_futex = xyes; then
 	config_path="linux/ia64 linux posix"
 	;;
 
+    loongarch*-*-linux*)
+	config_path="linux posix"
+	;;
+
     mips*-*-linux*)
 	config_path="linux/mips linux posix"
 	;;
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 11/12] LoongArch Port: gcc/testsuite
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (9 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 10/12] LoongArch Port: libgomp chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2021-12-24  9:28 ` [PATCH v4 12/12] LoongArch Port: Add doc chenglulu
  2022-01-05 14:13 ` [PATCH v4 00/12] Add LoongArch support Paul Hua
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

gcc/testsuite/

        * g++.dg/cpp0x/constexpr-rom.C: Add build options for LoongArch.
        * g++.old-deja/g++.abi/ptrmem.C: Add LoongArch support.
        * g++.old-deja/g++.pt/ptrmem6.C: xfail for LoongArch.
        * gcc.dg/20020312-2.c: Add LoongArch support.
        * gcc.dg/loop-8.c: Skip on LoongArch.
        * gcc.dg/torture/stackalign/builtin-apply-2.c: Likewise.
        * gcc.dg/tree-ssa/ssa-fre-3.c: Likewise.
        * go.test/go-test.exp: Define the LoongArch target.
        * lib/target-supports.exp: Like wise.
        * gcc.target/loongarch/loongarch.exp: New file.
        * gcc.target/loongarch/tst-asm-const.c: Like wise.
---
 gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C    |  2 +-
 gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C   |  2 +-
 gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C   |  2 +-
 gcc/testsuite/gcc.dg/20020312-2.c             |  2 +
 gcc/testsuite/gcc.dg/loop-8.c                 |  2 +-
 .../torture/stackalign/builtin-apply-2.c      |  2 +-
 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c     |  2 +-
 .../gcc.target/loongarch/loongarch.exp        | 40 +++++++++++++++++++
 .../gcc.target/loongarch/tst-asm-const.c      | 16 ++++++++
 gcc/testsuite/go.test/go-test.exp             |  3 ++
 gcc/testsuite/lib/target-supports.exp         | 14 +++++++
 11 files changed, 81 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/loongarch/loongarch.exp
 create mode 100644 gcc/testsuite/gcc.target/loongarch/tst-asm-const.c

diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C
index 2e0ef685f36..424979a604b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C
@@ -1,6 +1,6 @@
 // PR c++/49673: check that test_data goes into .rodata
 // { dg-do compile { target c++11 } }
-// { dg-additional-options -G0 { target { { alpha*-*-* frv*-*-* ia64-*-* lm32*-*-* m32r*-*-* microblaze*-*-* mips*-*-* nios2-*-* powerpc*-*-* rs6000*-*-* } && { ! { *-*-darwin* *-*-aix* alpha*-*-*vms* } } } } }
+// { dg-additional-options -G0 { target { { alpha*-*-* frv*-*-* ia64-*-* lm32*-*-* m32r*-*-* microblaze*-*-* mips*-*-* loongarch*-*-* nios2-*-* powerpc*-*-* rs6000*-*-* } && { ! { *-*-darwin* *-*-aix* alpha*-*-*vms* } } } } }
 // { dg-final { scan-assembler "\\.rdata" { target mips*-*-* } } }
 // { dg-final { scan-assembler "rodata" { target { { *-*-linux-gnu *-*-gnu* *-*-elf } && { ! { mips*-*-* riscv*-*-* } } } } } }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C b/gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C
index bda7960d8a2..f69000e9081 100644
--- a/gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C
+++ b/gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C
@@ -7,7 +7,7 @@
    function.  However, some platforms use all bits to encode a
    function pointer.  Such platforms use the lowest bit of the delta,
    that is shifted left by one bit.  */
-#if defined __MN10300__ || defined __SH5__ || defined __arm__ || defined __thumb__ || defined __mips__ || defined __aarch64__ || defined __PRU__
+#if defined __MN10300__ || defined __SH5__ || defined __arm__ || defined __thumb__ || defined __mips__ || defined __aarch64__ || defined __PRU__ || defined __loongarch__
 #define ADJUST_PTRFN(func, virt) ((void (*)())(func))
 #define ADJUST_DELTA(delta, virt) (((delta) << 1) + !!(virt))
 #else
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C b/gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C
index 9f4bbe43f89..8f8f7017ab7 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C
@@ -25,7 +25,7 @@ int main() {
   h<&B::j>(); // { dg-error "" } 
   g<(void (A::*)()) &A::f>(); // { dg-error "" "" { xfail c++11 } }
   h<(int A::*) &A::i>(); // { dg-error "" "" { xfail c++11 } }
-  g<(void (A::*)()) &B::f>(); // { dg-error "" "" { xfail { c++11 && { aarch64*-*-* arm*-*-* mips*-*-* } } } }
+  g<(void (A::*)()) &B::f>(); // { dg-error "" "" { xfail { c++11 && { aarch64*-*-* arm*-*-* mips*-*-* loongarch*-*-* } } } }
   h<(int A::*) &B::j>(); // { dg-error "" } 
   g<(void (A::*)()) 0>(); // { dg-error "" "" { target { ! c++11 } } }
   h<(int A::*) 0>(); // { dg-error "" "" { target { ! c++11 } } }
diff --git a/gcc/testsuite/gcc.dg/20020312-2.c b/gcc/testsuite/gcc.dg/20020312-2.c
index 52c33d09b90..92bc150df0f 100644
--- a/gcc/testsuite/gcc.dg/20020312-2.c
+++ b/gcc/testsuite/gcc.dg/20020312-2.c
@@ -37,6 +37,8 @@ extern void abort (void);
 /* PIC register is r1, but is used even without -fpic.  */
 #elif defined(__lm32__)
 /* No pic register.  */
+#elif defined(__loongarch__)
+/* No pic register.  */
 #elif defined(__M32R__)
 /* No pic register.  */
 #elif defined(__m68k__)
diff --git a/gcc/testsuite/gcc.dg/loop-8.c b/gcc/testsuite/gcc.dg/loop-8.c
index a685fc25056..8e5f2087831 100644
--- a/gcc/testsuite/gcc.dg/loop-8.c
+++ b/gcc/testsuite/gcc.dg/loop-8.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-options "-O1 -fdump-rtl-loop2_invariant" } */
-/* { dg-skip-if "unexpected IV" { "hppa*-*-* mips*-*-* visium-*-* powerpc*-*-* riscv*-*-* mmix-*-* vax-*-*" } } */
+/* { dg-skip-if "unexpected IV" { "hppa*-*-* mips*-*-* visium-*-* powerpc*-*-* riscv*-*-* mmix-*-* vax-*-* loongarch*-*-*" } } */
 /* Load immediate on condition is available from z13 on and prevents moving
    the load out of the loop, so always run this test with -march=zEC12 that
    does not have load immediate on condition.  */
diff --git a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
index 5ec05587dba..552ca1433f4 100644
--- a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
+++ b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
@@ -9,7 +9,7 @@
 /* arm_hf_eabi: Variadic funcs use Base AAPCS.  Normal funcs use VFP variant.
    avr: Variadic funcs don't pass arguments in registers, while normal funcs
         do.  */
-/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { csky*-*-* avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* } } } */
+/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { csky*-*-* avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* loongarch*-*-* } } } */
 /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { nds32*-*-* } { v850*-*-* } } */
 /* { dg-require-effective-target untyped_assembly } */
    
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c
index 6b6255b9713..224dd4f72ef 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c
@@ -5,7 +5,7 @@
 
    When the condition is true, we distribute "(int) (a + b)" as
    "(int) a + (int) b", otherwise we keep the original.  */
-/* { dg-do compile { target { ! mips64 } } } */
+/* { dg-do compile { target { ! mips64 } && { ! loongarch64 } } } */
 /* { dg-options "-O -fno-tree-forwprop -fno-tree-ccp -fwrapv -fdump-tree-fre1-details" } */
 
 /* From PR14844.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/loongarch.exp b/gcc/testsuite/gcc.target/loongarch/loongarch.exp
new file mode 100644
index 00000000000..9f374a9bc73
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/loongarch.exp
@@ -0,0 +1,40 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+
+# 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 3 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Exit immediately if this isn't a LoongArch target.
+if ![istarget loongarch*-*-*] then {
+  return
+}
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " "
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+	"" $DEFAULT_CFLAGS
+# All done.
+dg-finish
diff --git a/gcc/testsuite/gcc.target/loongarch/tst-asm-const.c b/gcc/testsuite/gcc.target/loongarch/tst-asm-const.c
new file mode 100644
index 00000000000..2e04b99e301
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/tst-asm-const.c
@@ -0,0 +1,16 @@
+/* Test asm const. */
+/* { dg-do compile } */
+/* { dg-final { scan-assembler-times "foo:.*\\.long 1061109567.*\\.long 52" 1 } } */
+int foo ()
+{
+  __asm__ volatile (
+          "foo:"
+          "\n\t"
+	  ".long %a0\n\t"
+	  ".long %a1\n\t"
+	  :
+	  :"i"(0x3f3f3f3f), "i"(52)
+	  :
+	  );
+}
+
diff --git a/gcc/testsuite/go.test/go-test.exp b/gcc/testsuite/go.test/go-test.exp
index f16754513e4..63755aed106 100644
--- a/gcc/testsuite/go.test/go-test.exp
+++ b/gcc/testsuite/go.test/go-test.exp
@@ -232,6 +232,9 @@ proc go-set-goarch { } {
 	"riscv64-*-*" {
 	    set goarch "riscv64"
 	}
+	"loongarch64-*-*" {
+	    set goarch "loongarch64"
+	}
 	"s390*-*-*" {
 	    if [check_effective_target_ilp32] {
 		set goarch "s390"
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 039125c866a..6899b317b8c 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -284,6 +284,10 @@ proc check_configured_with { pattern } {
 proc check_weak_available { } {
     global target_cpu
 
+    if { [ string first "loongarch" $target_cpu ] >= 0 } {
+        return 1
+    }
+
     # All mips targets should support it
 
     if { [ string first "mips" $target_cpu ] >= 0 } {
@@ -1295,6 +1299,14 @@ proc check_effective_target_mpaired_single { } {
 # Return true if the target has access to FPU instructions.
 
 proc check_effective_target_hard_float { } {
+    if { [istarget loongarch*-*-*] } {
+	return [check_no_compiler_messages hard_float assembly {
+		#if (defined __loongarch_soft_float)
+		#error __loongarch_soft_float
+		#endif
+	}]
+    }
+
     if { [istarget mips*-*-*] } {
 	return [check_no_compiler_messages hard_float assembly {
 		#if (defined __mips_soft_float || defined __mips16)
@@ -8585,6 +8597,7 @@ proc check_effective_target_sync_char_short { } {
 	     || [istarget cris-*-*]
 	     || ([istarget sparc*-*-*] && [check_effective_target_sparc_v9])
 	     || ([istarget arc*-*-*] && [check_effective_target_arc_atomic])
+	     || [istarget loongarch*-*-*]
 	     || [check_effective_target_mips_llsc] }}]
 }
 
@@ -10677,6 +10690,7 @@ proc check_effective_target_branch_cost {} {
 	 || [istarget epiphany*-*-*]
 	 || [istarget frv*-*-*]
 	 || [istarget i?86-*-*] || [istarget x86_64-*-*]
+	 || [istarget loongarch*-*-*]
 	 || [istarget mips*-*-*]
 	 || [istarget s390*-*-*]
 	 || [istarget riscv*-*-*]
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 12/12] LoongArch Port: Add doc.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (10 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 11/12] LoongArch Port: gcc/testsuite chenglulu
@ 2021-12-24  9:28 ` chenglulu
  2022-01-05 14:13 ` [PATCH v4 00/12] Add LoongArch support Paul Hua
  12 siblings, 0 replies; 16+ messages in thread
From: chenglulu @ 2021-12-24  9:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: xuchenghua, yangyujie, chenglulu

	* contrib/config-list.mk: Add LoongArch triplet.
	* gcc/doc/install.texi: Add LoongArch options section.
	* gcc/doc/invoke.texi: Add LoongArch options section.
	* gcc/doc/md.texi: Add LoongArch options section.
---
 contrib/config-list.mk |   5 +-
 gcc/doc/install.texi   |  47 +++++++++-
 gcc/doc/invoke.texi    | 201 +++++++++++++++++++++++++++++++++++++++++
 gcc/doc/md.texi        |  55 +++++++++++
 4 files changed, 302 insertions(+), 6 deletions(-)

diff --git a/contrib/config-list.mk b/contrib/config-list.mk
index 3e1d1321861..ba6f12e4693 100644
--- a/contrib/config-list.mk
+++ b/contrib/config-list.mk
@@ -57,7 +57,10 @@ LIST = aarch64-elf aarch64-linux-gnu aarch64-rtems \
   i686-wrs-vxworksae \
   i686-cygwinOPT-enable-threads=yes i686-mingw32crt ia64-elf \
   ia64-freebsd6 ia64-linux ia64-hpux ia64-hp-vms iq2000-elf lm32-elf \
-  lm32-rtems lm32-uclinux m32c-rtems m32c-elf m32r-elf m32rle-elf \
+  lm32-rtems lm32-uclinux \
+  loongarch64-linux-gnu loongarch64-linux-gnuf64 \
+  loongarch64-linux-gnuf32 loongarch64-linux-gnusf \
+  m32c-rtems m32c-elf m32r-elf m32rle-elf \
   m68k-elf m68k-netbsdelf \
   m68k-uclinux m68k-linux m68k-rtems \
   mcore-elf microblaze-linux microblaze-elf \
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 549da22aa16..4e0cf20347e 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -752,9 +752,9 @@ Here are the possible CPU types:
 @quotation
 aarch64, aarch64_be, alpha, alpha64, amdgcn, arc, arceb, arm, armeb, avr, bfin,
 bpf, cr16, cris, csky, epiphany, fido, fr30, frv, ft32, h8300, hppa, hppa2.0,
-hppa64, i486, i686, ia64, iq2000, lm32, m32c, m32r, m32rle, m68k, mcore,
-microblaze, microblazeel, mips, mips64, mips64el, mips64octeon, mips64orion,
-mips64vr, mipsel, mipsisa32, mipsisa32r2, mipsisa64, mipsisa64r2,
+hppa64, i486, i686, ia64, iq2000, lm32, loongarch64, m32c, m32r, m32rle, m68k,
+mcore, microblaze, microblazeel, mips, mips64, mips64el, mips64octeon,
+mips64orion, mips64vr, mipsel, mipsisa32, mipsisa32r2, mipsisa64, mipsisa64r2,
 mipsisa64r2el, mipsisa64sb1, mipsisa64sr71k, mipstx39, mmix, mn10300, moxie,
 msp430, nds32be, nds32le, nios2, nvptx, or1k, pdp11, powerpc, powerpc64,
 powerpc64le, powerpcle, pru, riscv32, riscv32be, riscv64, riscv64be, rl78, rx,
@@ -1171,8 +1171,9 @@ sysv, aix.
 @itemx --without-multilib-list
 Specify what multilibs to build.  @var{list} is a comma separated list of
 values, possibly consisting of a single value.  Currently only implemented
-for aarch64*-*-*, arm*-*-*, riscv*-*-*, sh*-*-* and x86-64-*-linux*.  The
-accepted values and meaning for each target is given below.
+for aarch64*-*-*, arm*-*-*, loongarch64-*-*, riscv*-*-*, sh*-*-* and
+x86-64-*-linux*.  The accepted values and meaning for each target is given
+below.
 
 @table @code
 @item aarch64*-*-*
@@ -1259,6 +1260,14 @@ profile.  The union of these options is considered when specifying both
 @code{-mfloat-abi=hard}
 @end multitable
 
+@item loongarch*-*-*
+@var{list} is a comma-separated list of the following ABI identifiers:
+@code{lp64d[/base]} @code{lp64f[/base]} @code{lp64d[/base]}, where the
+@code{/base} suffix may be omitted, to enable their respective run-time
+libraries.  If @var{list} is empty, @code{default}
+or @option{--with-multilib-list} is not specified, then the default ABI
+as specified by @option{--with-abi} or implied by @option{--target} is selected.
+
 @item riscv*-*-*
 @var{list} is a single ABI name.  The target architecture must be either
 @code{rv32gc} or @code{rv64gc}.  This will build a single multilib for the
@@ -4430,6 +4439,34 @@ This configuration is intended for embedded systems.
 Lattice Mico32 processor.
 This configuration is intended for embedded systems running uClinux.
 
+@html
+<hr />
+@end html
+@anchor{loongarch}
+@heading LoongArch
+LoongArch processor.
+The following LoongArch targets are available:
+@table @code
+@item loongarch64-linux-gnu*
+LoongArch processor running GNU/Linux.  This target triplet may be coupled
+with a small set of possible suffixes to identify their default ABI type:
+@table @code
+@item f64
+Uses @code{lp64d/base} ABI by default.
+@item f32
+Uses @code{lp64f/base} ABI by default.
+@item sf
+Uses @code{lp64s/base} ABI by default.
+@end table
+
+@item loongarch64-linux-gnu
+Same as @code{loongarch64-linux-gnuf64}, but may be used with
+@option{--with-abi=*} to configure the default ABI type.
+@end table
+
+More information about LoongArch can be found at
+@uref{https://github.com/loongson/LoongArch-Documentation}.
+
 @html
 <hr />
 @end html
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ca621577432..2cf226604f7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -991,6 +991,16 @@ Objective-C and Objective-C++ Dialects}.
 @gccoptlist{-mbarrel-shift-enabled  -mdivide-enabled  -mmultiply-enabled @gol
 -msign-extend-enabled  -muser-enabled}
 
+@emph{LoongArch Options}
+@gccoptlist{-march=@var{cpu-type}  -mtune=@var{cpu-type} -mabi=@var{base-abi-type} @gol
+-mfpu=@var{fpu-type} -msoft-float -msingle-float -mdouble-float @gol
+-mbranch-cost=@var{n}  -mcheck-zero-division -mno-check-zero-division @gol
+-mcond-move-int  -mno-cond-move-int @gol
+-mcond-move-float  -mno-cond-move-float @gol
+-memcpy  -mno-memcpy -mstrict-align @gol
+-mmax-inline-memcpy-size=@var{n} @gol
+-mlra -mcmodel=@var{code-model}}
+
 @emph{M32R/D Options}
 @gccoptlist{-m32r2  -m32rx  -m32r @gol
 -mdebug @gol
@@ -18638,6 +18648,7 @@ platform.
 * HPPA Options::
 * IA-64 Options::
 * LM32 Options::
+* LoongArch Options::
 * M32C Options::
 * M32R/D Options::
 * M680x0 Options::
@@ -24134,6 +24145,196 @@ Enable user-defined instructions.
 
 @end table
 
+@node LoongArch Options
+@subsection LoongArch Options
+@cindex LoongArch Options
+
+These command-line options are defined for LoongArch targets:
+
+@table @gcctabopt
+@item -march=@var{cpu-type}
+@opindex -march
+Generate instructions for the machine type @var{cpu-type}.  In contrast to
+@option{-mtune=@var{cpu-type}}, which merely tunes the generated code
+for the specified @var{cpu-type}, @option{-march=@var{cpu-type}} allows GCC
+to generate code that may not run at all on processors other than the one
+indicated.  Specifying @option{-march=@var{cpu-type}} implies
+@option{-mtune=@var{cpu-type}}, except where noted otherwise.
+
+The choices for @var{cpu-type} are:
+
+@table @samp
+@item native
+This selects the CPU to generate code for at compilation time by determining
+the processor type of the compiling machine.  Using @option{-march=native}
+enables all instruction subsets supported by the local machine (hence
+the result might not run on different machines).  Using @option{-mtune=native}
+produces code optimized for the local machine under the constraints
+of the selected instruction set.
+@item loongarch64
+A generic CPU with 64-bit extensions.
+@item la464
+LoongArch LA464 CPU with LBT, LSX, LASX, LVZ.
+@end table
+
+
+@item -mtune=@var{cpu-type}
+@opindex mtune
+Optimize the output for the given processor, specified by microarchitecture
+name.
+
+@item -mabi=@var{base-abi-type}
+@opindex mabi
+Generate code for the specified calling convention. @gol
+Set base ABI to one of: @gol
+@table @samp
+@item lp64d
+Uses 64-bit general purpose registers and 32/64-bit floating-point
+registers for parameter passing.  Data model is LP64, where int
+is 32 bits, while long int and pointers are 64 bits.
+@item lp64f
+Uses 64-bit general purpose registers and 32-bit floating-point
+registers for parameter passing.  Data model is LP64, where int
+is 32 bits, while long int and pointers are 64 bits.
+@item lp64s
+Uses 64-bit general purpose registers and no floating-point
+registers for parameter passing.  Data model is LP64, where int
+is 32 bits, while long int and pointers are 64 bits.
+@end table
+
+
+@item -mfpu=@var{fpu-type}
+@opindex mfpu
+Generating code for the specified FPU type: @gol
+@table @samp
+@item 64
+Allow the use of hardware floating-point instructions for 32-bit
+and 64-bit operations.
+@item 32
+Allow the use of hardware floating-point instructions for 32-bit
+operations.
+@item none
+@item 0
+Prevent the use of hardware floating-point instructions.
+@end table
+
+
+@item -msoft-float
+@opindex msoft-float
+Force @option{-mfpu=none} and prevents the use of floating-point
+registers for parameter passing.  This option may change the target
+ABI.
+
+@item -msingle-float
+@opindex -msingle-float
+Force @option{-mfpu=32} and allow the use of 32-bit floating-point
+registers for parameter passing.  This option may change the target
+ABI.
+
+@item -mdouble-float
+@opindex -mdouble-float
+Force @option{-mfpu=64} and allow the use of 32/64-bit floating-point
+registers for parameter passing.  This option may change the target
+ABI.
+
+
+@item -mbranch-cost=@var{n}
+@opindex -mbranch-cost
+Set the cost of branches to roughly n instructions.
+
+@item -mcheck-zero-division
+@itemx -mno-check-zero-divison
+@opindex -mcheck-zero-division
+Trap (do not trap) on integer division by zero. The default is '-mcheck-zero-
+division'.
+
+
+@item -mcond-move-int
+@itemx -mno-cond-move-int
+@opindex -mcond-move-int
+Conditional moves for floating point are enabled (disabled). The default is
+'-mcond-move-float'.
+
+@item -mmemcpy
+@itemx -mno-memcpy
+@opindex -mmemcpy
+Force (do not force) the use of memcpy for non-trivial block moves. The default
+is '-mno-memcpy', which allows GCC to inline most constant-sized copies.
+
+
+@item -mlra
+@opindex -mlra
+Use the new LRA register allocator. By default, the LRA is used.
+
+@item -mstrict-align
+@itemx -mno-strict-align
+@opindex -mstrict-align
+Avoid or allow generating memory accesses that may not be aligned on a natural
+object boundary as described in the architecture specification.
+
+@item -msmall-data-limit=@var{number}
+@opindex -msmall-data-limit
+Put global and static data smaller than @code{number} bytes into a special section (on some targets).
+Default value is 0.
+
+
+@item -mmax-inline-memcpy-size=@var{n}
+@opindex -mmax-inline-memcpy-size
+Set the max size n of memcpy to inline, default n is 1024.
+
+@item -mcmodel=@var{code-model}
+Default code model is normal.
+Set the code model to one of:
+@table @samp
+@item tiny-static
+@itemize @bullet
+@item
+local symbol and global strong symbol: The data section must be within +/-2MiB addressing space.
+The text section must be within +/-128MiB addressing space.
+@item
+global weak symbol: The got table must be within +/-2GiB addressing space.
+@end itemize
+
+@item tiny
+@itemize @bullet
+@item
+local symbol: The data section must be within +/-2MiB addressing space.
+The text section must be within +/-128MiB
+addressing space.
+@item
+global symbol: The got table must be within +/-2GiB addressing space.
+@end itemize
+
+@item normal
+@itemize @bullet
+@item
+local symbol: The data section must be within +/-2GiB addressing space.
+The text section must be within +/-128MiB addressing space.
+@item
+global symbol: The got table must be within +/-2GiB addressing space.
+@end itemize
+
+@item large
+@itemize @bullet
+@item
+local symbol: The data section must be within +/-2GiB addressing space.
+The text section must be within +/-128GiB addressing space.
+@item
+global symbol: The got table must be within +/-2GiB addressing space.
+@end itemize
+
+@item extreme(Not implemented yet)
+@itemize @bullet
+@item
+local symbol: The data and text section must be within +/-8EiB addressing space.
+@item
+global symbol: The data got table must be within +/-8EiB addressing space.
+@end itemize
+@end table
+@end table
+
+
+
 @node M32C Options
 @subsection M32C Options
 @cindex M32C options
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 9ec051e94e1..8f8ab195c5f 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -2747,6 +2747,61 @@ Memory addressed using the small base register ($sb).
 $r1h
 @end table
 
+@item LoongArch---@file{config/loongarch/constraints.md}
+@table @code
+@item a
+A constant call global and noplt address.
+@item c
+A constant call local address.
+@item e
+A register that is used as function call.
+@item f
+A floating-point register (if available).
+@item h
+A constant call plt address.
+@item j
+A rester that is used as sibing call.
+@item l
+A signed 16-bit constant.
+@item m
+A memory operand whose address is formed by a base register and offset
+that is suitable for use in instructions with the same addressing mode
+as @code{st.w} and @code{ld.w}.
+@item q
+A general-purpose register except for $r0 and $r1 for csr instructions.
+@item t
+A constant call weak address.
+@item u
+A signed 52bit constant and low 32-bit is zero (for logic instructions).
+@item v
+A nsigned 64-bit constant and low 44-bit is zero (for logic instructions).
+@item z
+A floating-point condition code register.
+@item G
+Floating-point zero.
+@item I
+A signed 12-bit constant (for arithmetic instructions).
+@item J
+Integer zero.
+@item K
+An unsigned 12-bit constant (for logic instructions).
+@item Q
+A 12-bit constant used for logical operations.
+@item W
+A memory address based on a member of @code{BASE_REG_CLASS}.  This is
+true for allreferences.
+@item Yd
+A constant @code{move_operand} that can be safely loaded using
+@code{la}.
+@item ZB
+An address that is held in a general-purpose register.
+The offset is zero.
+@item ZC
+A memory operand whose address is formed by a base register and offset
+that is suitable for use in instructions with the same addressing mode
+as @code{ll.w} and @code{sc.w}.
+@end table
+
 @item MicroBlaze---@file{config/microblaze/constraints.md}
 @table @code
 @item d
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 00/12] Add LoongArch support.
  2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
                   ` (11 preceding siblings ...)
  2021-12-24  9:28 ` [PATCH v4 12/12] LoongArch Port: Add doc chenglulu
@ 2022-01-05 14:13 ` Paul Hua
  12 siblings, 0 replies; 16+ messages in thread
From: Paul Hua @ 2022-01-05 14:13 UTC (permalink / raw)
  To: chenglulu; +Cc: gcc-patches, Xu Chenghua, yangyujie

Hi all,

Ping?

By the way, the LoongArch machine is already connected to the Cfarm
and will be announced soon.
You can login through ssh.
ssh -l your-cfarm-user-name -p 25469 114.242.206.180


On Fri, Dec 24, 2021 at 5:28 PM chenglulu <chenglulu@loongson.cn> wrote:
>
> The LoongArch architecture (LoongArch) is an Instruction Set
> Architecture (ISA) that has a Reduced Instruction Set Computer (RISC)
> style.
> The documents are on
> https://loongson.github.io/LoongArch-Documentation/README-EN.html
>
> The ELF ABI Documents are on:
> https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
>
> The binutils has been merged into trunk:
> https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=560b3fe208255ae909b4b1c88ba9c28b09043307
>
> Note: We split -mabi= into -mabi=lp64d/f/s, the new options not support by upstream binutils yet,
> this GCC port requires the following patch applied to binutils to build.
> https://github.com/loongson/binutils-gdb/commit/aacb0bf860f02aa5a7dcb76dd0e392bf871c7586
> (will be submitted to upstream after gcc side comfirmed)
>
>
> changelog:
>
> v1 -> v2
> 1. Split patch set.
> 2. Change some code style.
> 3. Add -mabi=lp64d/f/s options.
> 4. Change GLIBC_DYNAMIC_LINKER_LP64 name.
>
> v2 -> v3
> 1. Change some code style.
> 2. Bug fix.
>
> v3 -> v4
> 1. Change some code style.
> 2. Bug fix.
> 3. Delete some builtin macros.
>
> Add LoongArch support.
>
> chenglulu (12):
>   LoongArch Port: Regenerate configure
>   LoongArch Port: gcc build
>   LoongArch Port: Regenerate gcc/configure.
>   LoongArch Port: Machine Decsription files.
>   LoongArch Port: Machine description C files and .h files.
>   LoongArch Port: Builtin functions.
>   LoongArch Port: Builtin macros.
>   LoongArch Port: libgcc
>   LoongArch Port: Regenerate libgcc/configure.
>   LoongArch Port: libgomp
>   LoongArch Port: gcc/testsuite
>   LoongArch Port: Add doc.
>
>  config/picflag.m4                             |    3 +
>  configure                                     |   10 +-
>  configure.ac                                  |   10 +-
>  contrib/config-list.mk                        |    5 +-
>  .../config/loongarch/loongarch-common.c       |   63 +
>  gcc/config.gcc                                |  400 +-
>  gcc/config/host-linux.c                       |    2 +
>  gcc/config/loongarch/constraints.md           |  212 +
>  gcc/config/loongarch/generic.md               |  132 +
>  gcc/config/loongarch/genopts/genstr.sh        |   91 +
>  .../loongarch/genopts/loongarch-strings       |   58 +
>  gcc/config/loongarch/genopts/loongarch.opt.in |  189 +
>  gcc/config/loongarch/gnu-user.h               |   78 +
>  gcc/config/loongarch/la464.md                 |  132 +
>  gcc/config/loongarch/larchintrin.h            |  413 ++
>  gcc/config/loongarch/linux.h                  |   50 +
>  gcc/config/loongarch/loongarch-builtins.c     |  511 ++
>  gcc/config/loongarch/loongarch-c.c            |  111 +
>  gcc/config/loongarch/loongarch-cpu.c          |  206 +
>  gcc/config/loongarch/loongarch-cpu.h          |   30 +
>  gcc/config/loongarch/loongarch-def.c          |  164 +
>  gcc/config/loongarch/loongarch-def.h          |  151 +
>  gcc/config/loongarch/loongarch-driver.c       |  187 +
>  gcc/config/loongarch/loongarch-driver.h       |   69 +
>  gcc/config/loongarch/loongarch-ftypes.def     |  106 +
>  gcc/config/loongarch/loongarch-modes.def      |   29 +
>  gcc/config/loongarch/loongarch-opts.c         |  582 ++
>  gcc/config/loongarch/loongarch-opts.h         |   86 +
>  gcc/config/loongarch/loongarch-protos.h       |  242 +
>  gcc/config/loongarch/loongarch-str.h          |   57 +
>  gcc/config/loongarch/loongarch-tune.h         |   72 +
>  gcc/config/loongarch/loongarch.c              | 6461 +++++++++++++++++
>  gcc/config/loongarch/loongarch.h              | 1291 ++++
>  gcc/config/loongarch/loongarch.md             | 3829 ++++++++++
>  gcc/config/loongarch/loongarch.opt            |  189 +
>  gcc/config/loongarch/predicates.md            |  553 ++
>  gcc/config/loongarch/sync.md                  |  574 ++
>  gcc/config/loongarch/t-linux                  |   53 +
>  gcc/config/loongarch/t-loongarch              |   59 +
>  gcc/configure                                 |   85 +-
>  gcc/configure.ac                              |   33 +-
>  gcc/doc/install.texi                          |   47 +-
>  gcc/doc/invoke.texi                           |  201 +
>  gcc/doc/md.texi                               |   55 +
>  gcc/testsuite/g++.dg/cpp0x/constexpr-rom.C    |    2 +-
>  gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C   |    2 +-
>  gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C   |    2 +-
>  gcc/testsuite/gcc.dg/20020312-2.c             |    2 +
>  gcc/testsuite/gcc.dg/loop-8.c                 |    2 +-
>  .../torture/stackalign/builtin-apply-2.c      |    2 +-
>  gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-3.c     |    2 +-
>  .../gcc.target/loongarch/loongarch.exp        |   40 +
>  .../gcc.target/loongarch/tst-asm-const.c      |   16 +
>  gcc/testsuite/go.test/go-test.exp             |    3 +
>  gcc/testsuite/lib/target-supports.exp         |   14 +
>  libgcc/config.host                            |   28 +-
>  libgcc/config/loongarch/crtfastmath.c         |   52 +
>  libgcc/config/loongarch/crti.S                |   43 +
>  libgcc/config/loongarch/crtn.S                |   39 +
>  libgcc/config/loongarch/lib2funcs.c           |    0
>  libgcc/config/loongarch/linux-unwind.h        |   80 +
>  libgcc/config/loongarch/sfp-machine.h         |  152 +
>  libgcc/config/loongarch/t-crtstuff            |    2 +
>  libgcc/config/loongarch/t-elf                 |    3 +
>  libgcc/config/loongarch/t-loongarch           |    9 +
>  libgcc/config/loongarch/t-loongarch64         |    1 +
>  libgcc/config/loongarch/t-softfp-tf           |    3 +
>  libgcc/configure                              |    5 +-
>  libgcc/configure.ac                           |    2 +-
>  libgomp/configure.tgt                         |    4 +
>  70 files changed, 18363 insertions(+), 28 deletions(-)
>  create mode 100644 gcc/common/config/loongarch/loongarch-common.c
>  create mode 100644 gcc/config/loongarch/constraints.md
>  create mode 100644 gcc/config/loongarch/generic.md
>  create mode 100755 gcc/config/loongarch/genopts/genstr.sh
>  create mode 100644 gcc/config/loongarch/genopts/loongarch-strings
>  create mode 100644 gcc/config/loongarch/genopts/loongarch.opt.in
>  create mode 100644 gcc/config/loongarch/gnu-user.h
>  create mode 100644 gcc/config/loongarch/la464.md
>  create mode 100644 gcc/config/loongarch/larchintrin.h
>  create mode 100644 gcc/config/loongarch/linux.h
>  create mode 100644 gcc/config/loongarch/loongarch-builtins.c
>  create mode 100644 gcc/config/loongarch/loongarch-c.c
>  create mode 100644 gcc/config/loongarch/loongarch-cpu.c
>  create mode 100644 gcc/config/loongarch/loongarch-cpu.h
>  create mode 100644 gcc/config/loongarch/loongarch-def.c
>  create mode 100644 gcc/config/loongarch/loongarch-def.h
>  create mode 100644 gcc/config/loongarch/loongarch-driver.c
>  create mode 100644 gcc/config/loongarch/loongarch-driver.h
>  create mode 100644 gcc/config/loongarch/loongarch-ftypes.def
>  create mode 100644 gcc/config/loongarch/loongarch-modes.def
>  create mode 100644 gcc/config/loongarch/loongarch-opts.c
>  create mode 100644 gcc/config/loongarch/loongarch-opts.h
>  create mode 100644 gcc/config/loongarch/loongarch-protos.h
>  create mode 100644 gcc/config/loongarch/loongarch-str.h
>  create mode 100644 gcc/config/loongarch/loongarch-tune.h
>  create mode 100644 gcc/config/loongarch/loongarch.c
>  create mode 100644 gcc/config/loongarch/loongarch.h
>  create mode 100644 gcc/config/loongarch/loongarch.md
>  create mode 100644 gcc/config/loongarch/loongarch.opt
>  create mode 100644 gcc/config/loongarch/predicates.md
>  create mode 100644 gcc/config/loongarch/sync.md
>  create mode 100644 gcc/config/loongarch/t-linux
>  create mode 100644 gcc/config/loongarch/t-loongarch
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/loongarch.exp
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/tst-asm-const.c
>  create mode 100644 libgcc/config/loongarch/crtfastmath.c
>  create mode 100644 libgcc/config/loongarch/crti.S
>  create mode 100644 libgcc/config/loongarch/crtn.S
>  create mode 100644 libgcc/config/loongarch/lib2funcs.c
>  create mode 100644 libgcc/config/loongarch/linux-unwind.h
>  create mode 100644 libgcc/config/loongarch/sfp-machine.h
>  create mode 100644 libgcc/config/loongarch/t-crtstuff
>  create mode 100644 libgcc/config/loongarch/t-elf
>  create mode 100644 libgcc/config/loongarch/t-loongarch
>  create mode 100644 libgcc/config/loongarch/t-loongarch64
>  create mode 100644 libgcc/config/loongarch/t-softfp-tf
>
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 04/12] LoongArch Port: Machine Decsription files.
  2021-12-24  9:28 ` [PATCH v4 04/12] LoongArch Port: Machine Decsription files chenglulu
@ 2022-01-06 17:54   ` Xi Ruoyao
  2022-01-07 11:36     ` 程璐璐
  0 siblings, 1 reply; 16+ messages in thread
From: Xi Ruoyao @ 2022-01-06 17:54 UTC (permalink / raw)
  To: chenglulu, gcc-patches; +Cc: xuchenghua, yangyujie

[-- Attachment #1: Type: text/plain, Size: 1659 bytes --]

On Fri, 2021-12-24 at 17:28 +0800, chenglulu wrote:
> +(define_insn "*zero_extendsidi2_internal"
> +  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
> +       (subreg:DI (match_operand:SI 1 "nonimmediate_operand" "r,ZC,W") 0))]
> +  "TARGET_64BIT"
> +  "@
> +   bstrpick.d\t%0,%1,31,0
> +   ldptr.w\t%0,%1\n\tlu32i.d\t%0,0
> +   ld.wu\t%0,%1"
> +  [(set_attr "move_type" "arith,load,load")
> +   (set_attr "mode" "DI")
> +   (set_attr "insn_count" "1,2,1")])

This pattern is generating wrong code, causing

FAIL: gcc.dg/compat/scalar-by-value-3 c_compat_x_tst.o-c_compat_y_tst.o execute

This failure is a real bug, the reduced testcase is attached.  In the
assembly:

        # ...
        bstrins.d       $r5,$r13,31,0
        addi.d  $r12,$r22,-228
        bstrpick.d      $r12,$r12,31,0
        bstrins.d       $r5,$r12,63,32
        addi.w  $r4,$r0,14                      # 0xe
        bl      testvaci

This obviously does not make any sense: it calculates the *address* of
g[0]'s imaginary part, truncates it to 32-bit, then pass it as the
*value* of the imaginary part to testvaci().

The problem is: before the reload pass, the compiler consider g[0] a
(virtual) register.  It only becomes MEM after the reload pass.  Adding
reload_completed as the condition of this insn seems able to fix the
issue.  However I'm not sure if other insns need the change too.

> +;; See the comment before the *and<mode>3 pattern why this is generated by
> +;; combine.

A minor issue: the comment before 'and<mode>3' does not exist.
-- 
Xi Ruoyao <xry111@mengyan1223.wang>
School of Aerospace Science and Technology, Xidian University

[-- Attachment #2: bug.c --]
[-- Type: text/x-csrc, Size: 608 bytes --]

#include <stdarg.h>
#include <assert.h>

extern void testvaci (int n, ...);

int main() {
        int i;
        volatile _Complex int g[14];
        for (i = 0; i < 14; i++)
                g[i] = i + 3 + 3i;
        testvaci (14, g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7], g[8], g[9], g[10], g[11], g[12], g[13]); 
        return 0;
}

void testvaci (int n, ...) {
        int i;
        va_list ap;
        va_start(ap, n);
        for (i = 0; i < n; i++) {
                _Complex int t = __builtin_va_arg(ap, _Complex int);
                assert(t == i + 3 + 3i);
        }
        va_end(ap);
}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 04/12] LoongArch Port: Machine Decsription files.
  2022-01-06 17:54   ` Xi Ruoyao
@ 2022-01-07 11:36     ` 程璐璐
  0 siblings, 0 replies; 16+ messages in thread
From: 程璐璐 @ 2022-01-07 11:36 UTC (permalink / raw)
  To: Xi Ruoyao, gcc-patches; +Cc: xuchenghua, yangyujie

在 2022/1/7 上午1:54, Xi Ruoyao 写道:
> On Fri, 2021-12-24 at 17:28 +0800, chenglulu wrote:
>> +(define_insn "*zero_extendsidi2_internal"
>> +  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
>> +       (subreg:DI (match_operand:SI 1 "nonimmediate_operand" "r,ZC,W") 0))]
>> +  "TARGET_64BIT"
>> +  "@
>> +   bstrpick.d\t%0,%1,31,0
>> +   ldptr.w\t%0,%1\n\tlu32i.d\t%0,0
>> +   ld.wu\t%0,%1"
>> +  [(set_attr "move_type" "arith,load,load")
>> +   (set_attr "mode" "DI")
>> +   (set_attr "insn_count" "1,2,1")])
> This pattern is generating wrong code, causing
>
> FAIL: gcc.dg/compat/scalar-by-value-3 c_compat_x_tst.o-c_compat_y_tst.o execute
>
> This failure is a real bug, the reduced testcase is attached.  In the
> assembly:
>
>          # ...
>          bstrins.d       $r5,$r13,31,0
>          addi.d  $r12,$r22,-228
>          bstrpick.d      $r12,$r12,31,0
>          bstrins.d       $r5,$r12,63,32
>          addi.w  $r4,$r0,14                      # 0xe
>          bl      testvaci
>
> This obviously does not make any sense: it calculates the *address* of
> g[0]'s imaginary part, truncates it to 32-bit, then pass it as the
> *value* of the imaginary part to testvaci().
>
> The problem is: before the reload pass, the compiler consider g[0] a
> (virtual) register.  It only becomes MEM after the reload pass.  Adding
> reload_completed as the condition of this insn seems able to fix the
> issue.  However I'm not sure if other insns need the change too.
>
>> +;; See the comment before the *and<mode>3 pattern why this is generated by
>> +;; combine.
> A minor issue: the comment before 'and<mode>3' does not exist.

Thanks, we are working on this. In addition, we will check other 
instruction templates.



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2022-01-07 11:36 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-24  9:28 [PATCH v4 00/12] Add LoongArch support chenglulu
2021-12-24  9:28 ` [PATCH v4 01/12] LoongArch Port: Regenerate configure chenglulu
2021-12-24  9:28 ` [PATCH v4 02/12] LoongArch Port: gcc build chenglulu
2021-12-24  9:28 ` [PATCH v4 03/12] LoongArch Port: Regenerate gcc/configure chenglulu
2021-12-24  9:28 ` [PATCH v4 04/12] LoongArch Port: Machine Decsription files chenglulu
2022-01-06 17:54   ` Xi Ruoyao
2022-01-07 11:36     ` 程璐璐
2021-12-24  9:28 ` [PATCH v4 05/12] LoongArch Port: Machine description C files and .h files chenglulu
2021-12-24  9:28 ` [PATCH v4 06/12] LoongArch Port: Builtin functions chenglulu
2021-12-24  9:28 ` [PATCH v4 07/12] LoongArch Port: Builtin macros chenglulu
2021-12-24  9:28 ` [PATCH v4 08/12] LoongArch Port: libgcc chenglulu
2021-12-24  9:28 ` [PATCH v4 09/12] LoongArch Port: Regenerate libgcc/configure chenglulu
2021-12-24  9:28 ` [PATCH v4 10/12] LoongArch Port: libgomp chenglulu
2021-12-24  9:28 ` [PATCH v4 11/12] LoongArch Port: gcc/testsuite chenglulu
2021-12-24  9:28 ` [PATCH v4 12/12] LoongArch Port: Add doc chenglulu
2022-01-05 14:13 ` [PATCH v4 00/12] Add LoongArch support Paul Hua

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).