From fc73a9edc11014a9ad258046fc39099cd34654cb Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 10 Aug 2023 10:22:42 +0800 Subject: [PATCH] LoongArch: add new symbol types and backports Signed-off-by: Peng Fan --- 0001-LoongArch-backports-from-upstream.patch | 8600 +++++++++++++++++ ...-link-objects-contained-same-element.patch | 43 + gcc.spec | 15 +- 3 files changed, 8657 insertions(+), 1 deletion(-) create mode 100644 0001-LoongArch-backports-from-upstream.patch create mode 100644 0002-libjccjit-do-not-link-objects-contained-same-element.patch diff --git a/0001-LoongArch-backports-from-upstream.patch b/0001-LoongArch-backports-from-upstream.patch new file mode 100644 index 0000000..a756dc1 --- /dev/null +++ b/0001-LoongArch-backports-from-upstream.patch @@ -0,0 +1,8600 @@ +From d516c8dcac4d0ccd3f0178f3dc523ea9504d83fb Mon Sep 17 00:00:00 2001 +From: Peng Fan +Date: Wed, 26 Jul 2023 09:54:41 +0800 +Subject: [PATCH] LoongArch: backports from upstream + +Signed-off-by: Peng Fan +--- + .../config/loongarch/loongarch-common.cc | 1 + + gcc/config.gcc | 14 +- + gcc/config/host-linux.cc | 2 +- + gcc/config/loongarch/constraints.md | 80 +- + .../loongarch/genopts/loongarch-strings | 1 + + gcc/config/loongarch/genopts/loongarch.opt.in | 11 + + gcc/config/loongarch/gnu-user.h | 15 +- + gcc/config/loongarch/linux.h | 3 + + gcc/config/loongarch/loongarch-def.c | 15 + + gcc/config/loongarch/loongarch-def.h | 8 +- + gcc/config/loongarch/loongarch-opts.cc | 19 + + gcc/config/loongarch/loongarch-opts.h | 1 + + gcc/config/loongarch/loongarch-protos.h | 21 +- + gcc/config/loongarch/loongarch-str.h | 1 + + gcc/config/loongarch/loongarch-tune.h | 9 + + gcc/config/loongarch/loongarch.cc | 1770 +++++++++++++---- + gcc/config/loongarch/loongarch.h | 44 +- + gcc/config/loongarch/loongarch.md | 924 +++++---- + gcc/config/loongarch/loongarch.opt | 11 + + gcc/config/loongarch/predicates.md | 116 +- + gcc/config/loongarch/t-linux | 2 +- + gcc/configure | 34 + + gcc/configure.ac | 8 + + gcc/doc/extend.texi | 197 +- + gcc/doc/invoke.texi | 81 +- + gcc/testsuite/g++.target/loongarch/bytepick.C | 32 + + gcc/testsuite/g++.target/loongarch/pr106828.C | 4 + + .../gcc.target/loongarch/add-const.c | 45 + + .../gcc.target/loongarch/attr-model-1.c | 6 + + .../gcc.target/loongarch/attr-model-2.c | 6 + + .../gcc.target/loongarch/attr-model-diag.c | 7 + + .../gcc.target/loongarch/attr-model-test.c | 25 + + .../gcc.target/loongarch/direct-extern-1.c | 6 + + .../gcc.target/loongarch/direct-extern-2.c | 6 + + gcc/testsuite/gcc.target/loongarch/div-1.c | 9 + + gcc/testsuite/gcc.target/loongarch/div-2.c | 9 + + gcc/testsuite/gcc.target/loongarch/div-3.c | 9 + + gcc/testsuite/gcc.target/loongarch/div-4.c | 9 + + .../gcc.target/loongarch/fcopysign.c | 16 + + gcc/testsuite/gcc.target/loongarch/flogb.c | 18 + + .../gcc.target/loongarch/flt-abi-isa-1.c | 14 + + .../gcc.target/loongarch/flt-abi-isa-2.c | 10 + + .../gcc.target/loongarch/flt-abi-isa-3.c | 9 + + .../gcc.target/loongarch/flt-abi-isa-4.c | 10 + + .../gcc.target/loongarch/fmax-fmin.c | 30 + + gcc/testsuite/gcc.target/loongarch/frint.c | 16 + + gcc/testsuite/gcc.target/loongarch/fscaleb.c | 48 + + .../gcc.target/loongarch/ftint-no-inexact.c | 44 + + gcc/testsuite/gcc.target/loongarch/ftint.c | 44 + + .../gcc.target/loongarch/func-call-1.c | 32 + + .../gcc.target/loongarch/func-call-2.c | 32 + + .../gcc.target/loongarch/func-call-3.c | 32 + + .../gcc.target/loongarch/func-call-4.c | 32 + + .../gcc.target/loongarch/func-call-5.c | 33 + + .../gcc.target/loongarch/func-call-6.c | 33 + + .../gcc.target/loongarch/func-call-7.c | 34 + + .../gcc.target/loongarch/func-call-8.c | 33 + + .../loongarch/func-call-extreme-1.c | 32 + + .../loongarch/func-call-extreme-2.c | 32 + + .../gcc.target/loongarch/func-call-medium-1.c | 41 + + .../gcc.target/loongarch/func-call-medium-2.c | 41 + + .../gcc.target/loongarch/func-call-medium-3.c | 41 + + .../gcc.target/loongarch/func-call-medium-4.c | 41 + + .../gcc.target/loongarch/func-call-medium-5.c | 42 + + .../gcc.target/loongarch/func-call-medium-6.c | 42 + + .../gcc.target/loongarch/func-call-medium-7.c | 43 + + .../gcc.target/loongarch/func-call-medium-8.c | 43 + + gcc/testsuite/gcc.target/loongarch/imm-load.c | 10 + + .../gcc.target/loongarch/imm-load1.c | 26 + + .../loongarch/{tst-asm-const.c => pr107731.c} | 6 +- + .../gcc.target/loongarch/pr109465-1.c | 9 + + .../gcc.target/loongarch/pr109465-2.c | 9 + + .../gcc.target/loongarch/pr109465-3.c | 12 + + .../gcc.target/loongarch/prolog-opt.c | 2 +- + .../loongarch/relocs-symbol-noaddend.c | 23 + + .../gcc.target/loongarch/shrink-wrap.c | 19 + + .../loongarch/stack-check-alloca-1.c | 15 + + .../loongarch/stack-check-alloca-2.c | 12 + + .../loongarch/stack-check-alloca-3.c | 12 + + .../loongarch/stack-check-alloca-4.c | 12 + + .../loongarch/stack-check-alloca-5.c | 13 + + .../loongarch/stack-check-alloca-6.c | 13 + + .../gcc.target/loongarch/stack-check-alloca.h | 15 + + .../gcc.target/loongarch/stack-check-cfa-1.c | 12 + + .../gcc.target/loongarch/stack-check-cfa-2.c | 12 + + .../loongarch/stack-check-prologue-1.c | 11 + + .../loongarch/stack-check-prologue-2.c | 11 + + .../loongarch/stack-check-prologue-3.c | 11 + + .../loongarch/stack-check-prologue-4.c | 11 + + .../loongarch/stack-check-prologue-5.c | 12 + + .../loongarch/stack-check-prologue-6.c | 11 + + .../loongarch/stack-check-prologue-7.c | 12 + + .../loongarch/stack-check-prologue.h | 5 + + .../gcc.target/loongarch/tls-gd-noplt.c | 12 + + gcc/testsuite/gcc.target/loongarch/va_arg.c | 24 + + gcc/testsuite/lib/target-supports.exp | 7 +- + include/longlong.h | 12 + + include/vtv-change-permission.h | 4 + + libitm/config/loongarch/asm.h | 54 + + libitm/config/loongarch/sjlj.S | 130 ++ + libitm/config/loongarch/target.h | 50 + + libitm/configure.tgt | 2 + + libsanitizer/asan/asan_interceptors.h | 2 +- + libsanitizer/asan/asan_interceptors_vfork.S | 1 + + libsanitizer/asan/asan_mapping.h | 9 + + libsanitizer/configure.tgt | 2 + + libsanitizer/lsan/lsan_common.cpp | 3 + + libsanitizer/lsan/lsan_common.h | 2 + + .../sanitizer_common/sanitizer_common.h | 3 + + ...ommon_interceptors_vfork_loongarch64.inc.S | 57 + + .../sanitizer_common_syscalls.inc | 4 +- + .../sanitizer_common/sanitizer_linux.cpp | 113 +- + .../sanitizer_common/sanitizer_linux.h | 2 +- + .../sanitizer_linux_libcdep.cpp | 19 +- + .../sanitizer_common/sanitizer_platform.h | 10 +- + .../sanitizer_platform_interceptors.h | 2 +- + .../sanitizer_platform_limits_linux.cpp | 3 +- + .../sanitizer_platform_limits_posix.cpp | 13 +- + .../sanitizer_platform_limits_posix.h | 7 +- + .../sanitizer_common/sanitizer_stacktrace.cpp | 4 +- + .../sanitizer_stoptheworld_linux_libcdep.cpp | 11 +- + .../sanitizer_symbolizer_libcdep.cpp | 2 + + .../sanitizer_syscall_linux_loongarch64.inc | 171 ++ + libsanitizer/tsan/tsan_interceptors_posix.cpp | 2 + + libsanitizer/tsan/tsan_platform.h | 35 + + libsanitizer/tsan/tsan_platform_linux.cpp | 21 +- + libsanitizer/tsan/tsan_rtl.h | 3 +- + libsanitizer/tsan/tsan_rtl_loongarch64.S | 196 ++ + libvtv/configure.tgt | 3 + + 130 files changed, 4909 insertions(+), 883 deletions(-) + create mode 100644 gcc/testsuite/g++.target/loongarch/bytepick.C + create mode 100644 gcc/testsuite/g++.target/loongarch/pr106828.C + create mode 100644 gcc/testsuite/gcc.target/loongarch/add-const.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/attr-model-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/attr-model-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/attr-model-diag.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/attr-model-test.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/direct-extern-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/direct-extern-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/div-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/div-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/div-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/div-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/fcopysign.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/flogb.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/flt-abi-isa-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/flt-abi-isa-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/flt-abi-isa-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/flt-abi-isa-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/fmax-fmin.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/frint.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/fscaleb.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/ftint-no-inexact.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/ftint.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-5.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-6.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-7.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-8.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-extreme-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-extreme-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/imm-load.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/imm-load1.c + rename gcc/testsuite/gcc.target/loongarch/{tst-asm-const.c => pr107731.c} (78%) + create mode 100644 gcc/testsuite/gcc.target/loongarch/pr109465-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/pr109465-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/pr109465-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/shrink-wrap.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/stack-check-prologue.h + create mode 100644 gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c + create mode 100644 gcc/testsuite/gcc.target/loongarch/va_arg.c + create mode 100644 libitm/config/loongarch/asm.h + create mode 100644 libitm/config/loongarch/sjlj.S + create mode 100644 libitm/config/loongarch/target.h + create mode 100644 libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S + create mode 100644 libsanitizer/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc + create mode 100644 libsanitizer/tsan/tsan_rtl_loongarch64.S + +diff --git a/gcc/common/config/loongarch/loongarch-common.cc b/gcc/common/config/loongarch/loongarch-common.cc +index ed3730fce..f8b4660fa 100644 +--- a/gcc/common/config/loongarch/loongarch-common.cc ++++ b/gcc/common/config/loongarch/loongarch-common.cc +@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see + static const struct default_options loongarch_option_optimization_table[] = + { + { OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 }, ++ { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +diff --git a/gcc/config.gcc b/gcc/config.gcc +index c5064dd37..673523788 100644 +--- a/gcc/config.gcc ++++ b/gcc/config.gcc +@@ -4971,20 +4971,16 @@ case "${target}" in + case ${target} in + loongarch64-*-*-*f64) + abi_pattern="lp64d" +- triplet_abi="f64" + ;; + loongarch64-*-*-*f32) + abi_pattern="lp64f" +- triplet_abi="f32" + ;; + loongarch64-*-*-*sf) + abi_pattern="lp64s" +- triplet_abi="sf" + ;; + loongarch64-*-*-*) + abi_pattern="lp64[dfs]" + abi_default="lp64d" +- triplet_abi="" + ;; + *) + echo "Unsupported target ${target}." 1>&2 +@@ -5005,9 +5001,6 @@ case "${target}" in + ;; + esac + +- la_canonical_triplet="loongarch64-${triplet_os}${triplet_abi}" +- +- + # Perform initial sanity checks on --with-* options. + case ${with_arch} in + "" | loongarch64 | la464) ;; # OK, append here. +@@ -5078,6 +5071,13 @@ case "${target}" in + ;; + esac + ++ case ${with_abi} in ++ "lp64d") triplet_abi="";; ++ "lp64f") triplet_abi="f32";; ++ "lp64s") triplet_abi="sf";; ++ esac ++ la_canonical_triplet="loongarch64-${triplet_os}${triplet_abi}" ++ + # Set default value for with_abiext (internal) + case ${with_abiext} in + "") +diff --git a/gcc/config/host-linux.cc b/gcc/config/host-linux.cc +index 817d3c087..d93cfc064 100644 +--- a/gcc/config/host-linux.cc ++++ b/gcc/config/host-linux.cc +@@ -99,7 +99,7 @@ + #elif defined(__riscv) && defined (__LP64__) + # define TRY_EMPTY_VM_SPACE 0x1000000000 + #elif defined(__loongarch__) && defined(__LP64__) +-# define TRY_EMPTY_VM_SPACE 0x8000000000 ++# define TRY_EMPTY_VM_SPACE 0x1000000000 + #else + # define TRY_EMPTY_VM_SPACE 0 + #endif +diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md +index d0bfddbd5..25f3cda35 100644 +--- a/gcc/config/loongarch/constraints.md ++++ b/gcc/config/loongarch/constraints.md +@@ -20,14 +20,14 @@ + + ;; Register constraints + +-;; "a" "A constant call global and noplt address." +-;; "b" <-----unused ++;; "a" <-----unused ++;; "b" "A constant call not local address." + ;; "c" "A constant call local address." + ;; "d" <-----unused + ;; "e" JIRL_REGS + ;; "f" FP_REGS + ;; "g" <-----unused +-;; "h" "A constant call plt address." ++;; "h" <-----unused + ;; "i" "Matches a general integer constant." (Global non-architectural) + ;; "j" SIBCALL_REGS + ;; "k" "A memory operand whose address is formed by a base register and +@@ -42,7 +42,7 @@ + ;; "q" CSR_REGS + ;; "r" GENERAL_REGS (Global non-architectural) + ;; "s" "Matches a symbolic integer constant." (Global non-architectural) +-;; "t" "A constant call weak address" ++;; "t" <-----unused + ;; "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." +@@ -60,7 +60,22 @@ + ;; "I" "A signed 12-bit constant (for arithmetic instructions)." + ;; "J" "Integer zero." + ;; "K" "An unsigned 12-bit constant (for logic instructions)." +-;; "L" <-----unused ++;; "L" - ++;; "La" ++;; "A signed constant in [-4096, 2048) or (2047, 4094]." ++;; "Lb" ++;; "A signed 32-bit constant and low 16-bit is zero, which can be ++;; added onto a register with addu16i.d. It matches nothing if ++;; the addu16i.d instruction is not available." ++;; "Lc" ++;; "A signed 64-bit constant can be expressed as Lb + I, but not a ++;; single Lb or I." ++;; "Ld" ++;; "A signed 64-bit constant can be expressed as Lb + Lb, but not a ++;; single Lb." ++;; "Le" ++;; "A signed 32-bit constant can be expressed as Lb + I, but not a ++;; single Lb or I." + ;; "M" <-----unused + ;; "N" <-----unused + ;; "O" <-----unused +@@ -86,13 +101,17 @@ + ;; "ZB" + ;; "An address that is held in a general-purpose register. + ;; The offset is zero" ++;; "ZD" ++;; "An address 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{preld}." + ;; "<" "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" ++(define_constraint "b" + "@internal +- A constant call global and noplt address." +- (match_operand 0 "is_const_call_global_noplt_symbol")) ++ A constant call no local address." ++ (match_operand 0 "is_const_call_no_local_symbol")) + + (define_constraint "c" + "@internal +@@ -105,11 +124,6 @@ + (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") + +@@ -134,11 +148,6 @@ + (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") +@@ -176,6 +185,35 @@ + (and (match_code "const_int") + (match_test "IMM12_OPERAND_UNSIGNED (ival)"))) + ++(define_constraint "La" ++ "A signed constant in [-4096, 2048) or (2047, 4094]." ++ (and (match_code "const_int") ++ (match_test "DUAL_IMM12_OPERAND (ival)"))) ++ ++(define_constraint "Lb" ++ "A signed 32-bit constant and low 16-bit is zero, which can be added ++ onto a register with addu16i.d." ++ (and (match_code "const_int") ++ (match_test "ADDU16I_OPERAND (ival)"))) ++ ++(define_constraint "Lc" ++ "A signed 64-bit constant can be expressed as Lb + I, but not a single Lb ++ or I." ++ (and (match_code "const_int") ++ (match_test "loongarch_addu16i_imm12_operand_p (ival, DImode)"))) ++ ++(define_constraint "Ld" ++ "A signed 64-bit constant can be expressed as Lb + Lb, but not a single ++ Lb." ++ (and (match_code "const_int") ++ (match_test "DUAL_ADDU16I_OPERAND (ival)"))) ++ ++(define_constraint "Le" ++ "A signed 32-bit constant can be expressed as Lb + I, but not a single Lb ++ or I." ++ (and (match_code "const_int") ++ (match_test "loongarch_addu16i_imm12_operand_p (ival, SImode)"))) ++ + (define_constraint "Yd" + "@internal + A constant @code{move_operand} that can be safely loaded using +@@ -200,3 +238,9 @@ + The offset is zero" + (and (match_code "mem") + (match_test "REG_P (XEXP (op, 0))"))) ++ ++(define_address_constraint "ZD" ++ "An address 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{preld}." ++ (match_test "loongarch_12bit_offset_address_p (op, mode)")) +diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings +index cb88ed56b..44ebb7ab1 100644 +--- a/gcc/config/loongarch/genopts/loongarch-strings ++++ b/gcc/config/loongarch/genopts/loongarch-strings +@@ -54,5 +54,6 @@ OPTSTR_CMODEL cmodel + STR_CMODEL_NORMAL normal + STR_CMODEL_TINY tiny + STR_CMODEL_TS tiny-static ++STR_CMODEL_MEDIUM medium + 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 +index 61e7d72a0..a56a867ff 100644 +--- a/gcc/config/loongarch/genopts/loongarch.opt.in ++++ b/gcc/config/loongarch/genopts/loongarch.opt.in +@@ -154,6 +154,10 @@ 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. + ++mexplicit-relocs ++Target Var(TARGET_EXPLICIT_RELOCS) Init(1) ++Use %reloc() assembly operators. ++ + ; The code model option names for -mcmodel. + Enum + Name(cmodel) Type(int) +@@ -168,6 +172,9 @@ 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_MEDIUM@@) Value(CMODEL_MEDIUM) ++ + EnumValue + Enum(cmodel) String(@@STR_CMODEL_LARGE@@) Value(CMODEL_LARGE) + +@@ -177,3 +184,7 @@ 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. ++ ++mdirect-extern-access ++Target Var(TARGET_DIRECT_EXTERN_ACCESS) Init(0) ++Avoid using the GOT to access external symbols. +diff --git a/gcc/config/loongarch/gnu-user.h b/gcc/config/loongarch/gnu-user.h +index 664dc9206..44e4f2575 100644 +--- a/gcc/config/loongarch/gnu-user.h ++++ b/gcc/config/loongarch/gnu-user.h +@@ -33,21 +33,28 @@ along with GCC; see the file COPYING3. If not see + #define GLIBC_DYNAMIC_LINKER \ + "/lib" ABI_GRLEN_SPEC "/ld-linux-loongarch-" ABI_SPEC ".so.1" + ++#define MUSL_ABI_SPEC \ ++ "%{mabi=lp64d:-lp64d}" \ ++ "%{mabi=lp64f:-lp64f}" \ ++ "%{mabi=lp64s:-lp64s}" ++ + #undef MUSL_DYNAMIC_LINKER + #define MUSL_DYNAMIC_LINKER \ +- "/lib" ABI_GRLEN_SPEC "/ld-musl-loongarch-" ABI_SPEC ".so.1" ++ "/lib/ld-musl-loongarch" ABI_GRLEN_SPEC MUSL_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 " GNU_USER_DYNAMIC_LINKER "}}" ++ "%{!shared: %{static} " \ ++ "%{!static: %{!static-pie: %{rdynamic:-export-dynamic} " \ ++ "-dynamic-linker " GNU_USER_DYNAMIC_LINKER "}} " \ ++ "%{static-pie: -static -pie --no-dynamic-linker -z text}}" + + + /* 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}" ++ "%{Ofast|ffast-math|funsafe-math-optimizations:%{!shared:crtfastmath.o%s}}" + + #undef LIB_SPEC + #define LIB_SPEC GNU_USER_TARGET_LIB_SPEC +diff --git a/gcc/config/loongarch/linux.h b/gcc/config/loongarch/linux.h +index 110d0fab9..00039ac18 100644 +--- a/gcc/config/loongarch/linux.h ++++ b/gcc/config/loongarch/linux.h +@@ -48,3 +48,6 @@ along with GCC; see the file COPYING3. If not see + #define STACK_CHECK_PROTECT (TARGET_64BIT ? 16 * 1024 : 12 * 1024) + + #define TARGET_ASM_FILE_END file_end_indicate_exec_stack ++ ++/* The stack pointer needs to be moved while checking the stack. */ ++#define STACK_CHECK_MOVING_SP 1 +diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c +index c8769b7d6..74d422ce0 100644 +--- a/gcc/config/loongarch/loongarch-def.c ++++ b/gcc/config/loongarch/loongarch-def.c +@@ -62,11 +62,25 @@ loongarch_cpu_cache[N_TUNE_TYPES] = { + .l1d_line_size = 64, + .l1d_size = 64, + .l2d_size = 256, ++ .simultaneous_prefetches = 4, + }, + [CPU_LA464] = { + .l1d_line_size = 64, + .l1d_size = 64, + .l2d_size = 256, ++ .simultaneous_prefetches = 4, ++ }, ++}; ++ ++struct loongarch_align ++loongarch_cpu_align[N_TUNE_TYPES] = { ++ [CPU_LOONGARCH64] = { ++ .function = "32", ++ .label = "16", ++ }, ++ [CPU_LA464] = { ++ .function = "32", ++ .label = "16", + }, + }; + +@@ -152,6 +166,7 @@ loongarch_cmodel_strings[] = { + [CMODEL_NORMAL] = STR_CMODEL_NORMAL, + [CMODEL_TINY] = STR_CMODEL_TINY, + [CMODEL_TINY_STATIC] = STR_CMODEL_TS, ++ [CMODEL_MEDIUM] = STR_CMODEL_MEDIUM, + [CMODEL_LARGE] = STR_CMODEL_LARGE, + [CMODEL_EXTREME] = STR_CMODEL_EXTREME, + }; +diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h +index c2c35b6ba..eb87a79a5 100644 +--- a/gcc/config/loongarch/loongarch-def.h ++++ b/gcc/config/loongarch/loongarch-def.h +@@ -82,9 +82,10 @@ 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 ++#define CMODEL_MEDIUM 3 ++#define CMODEL_LARGE 4 ++#define CMODEL_EXTREME 5 ++#define N_CMODEL_TYPES 6 + + /* enum switches */ + /* The "SW_" codes represent command-line switches (options that +@@ -143,6 +144,7 @@ extern int loongarch_cpu_issue_rate[]; + extern int loongarch_cpu_multipass_dfa_lookahead[]; + + extern struct loongarch_cache loongarch_cpu_cache[]; ++extern struct loongarch_align loongarch_cpu_align[]; + extern struct loongarch_rtx_cost_data loongarch_cpu_rtx_cost_data[]; + + #ifdef __cplusplus +diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc +index eb9c2a52f..e13eafb58 100644 +--- a/gcc/config/loongarch/loongarch-opts.cc ++++ b/gcc/config/loongarch/loongarch-opts.cc +@@ -377,6 +377,25 @@ fallback: + /* 5. Target code model */ + t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL; + ++ switch (t.cmodel) ++ { ++ case CMODEL_TINY: ++ case CMODEL_TINY_STATIC: ++ case CMODEL_LARGE: ++ warning (0, "%qs is not supported, now cmodel is set to %qs", ++ loongarch_cmodel_strings[t.cmodel], "normal"); ++ t.cmodel = CMODEL_NORMAL; ++ break; ++ ++ case CMODEL_NORMAL: ++ case CMODEL_MEDIUM: ++ case CMODEL_EXTREME: ++ break; ++ ++ default: ++ gcc_unreachable (); ++ } ++ + /* Cleanup and return. */ + obstack_free (&msg_obstack, NULL); + *target = t; +diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h +index eaa6fc074..165143188 100644 +--- a/gcc/config/loongarch/loongarch-opts.h ++++ b/gcc/config/loongarch/loongarch-opts.h +@@ -46,6 +46,7 @@ loongarch_config_target (struct loongarch_target *target, + #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_MEDIUM (la_target.cmodel == CMODEL_MEDIUM) + #define TARGET_CMODEL_LARGE (la_target.cmodel == CMODEL_LARGE) + #define TARGET_CMODEL_EXTREME (la_target.cmodel == CMODEL_EXTREME) + +diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h +index 2144c2421..3ac3b5e19 100644 +--- a/gcc/config/loongarch/loongarch-protos.h ++++ b/gcc/config/loongarch/loongarch-protos.h +@@ -27,9 +27,18 @@ along with GCC; see the file COPYING3. If not see + SYMBOL_GOT_DISP + The symbol's value will be loaded directly from the GOT. + ++ SYMBOL_PCREL ++ The symbol's value will be loaded directly from data section within ++ +/- 2GiB range. ++ ++ SYMBOL_PCREL64 ++ The symbol's value will be loaded directly from data section within ++ +/- 8EiB range. ++ + SYMBOL_TLS + A thread-local symbol. + ++ SYMBOL_TLS_IE + SYMBOL_TLSGD + SYMBOL_TLSLDM + UNSPEC wrappers around SYMBOL_TLS, corresponding to the +@@ -37,7 +46,11 @@ along with GCC; see the file COPYING3. If not see + */ + enum loongarch_symbol_type { + SYMBOL_GOT_DISP, ++ SYMBOL_PCREL, ++ SYMBOL_PCREL64, + SYMBOL_TLS, ++ SYMBOL_TLS_IE, ++ SYMBOL_TLS_LE, + SYMBOL_TLSGD, + SYMBOL_TLSLDM, + }; +@@ -61,7 +74,6 @@ 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); +@@ -71,8 +83,8 @@ extern rtx loongarch_legitimize_call_address (rtx); + extern rtx loongarch_subword (rtx, bool); + extern bool loongarch_split_move_p (rtx, rtx); + extern void loongarch_split_move (rtx, rtx, rtx); +-extern bool loongarch_split_move_insn_p (rtx, rtx); +-extern void loongarch_split_move_insn (rtx, rtx, rtx); ++extern bool loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT, machine_mode); ++extern void loongarch_split_plus_constant (rtx *, machine_mode); + extern const char *loongarch_output_move (rtx, rtx); + extern bool loongarch_cfun_has_cprestore_slot_p (void); + #ifdef RTX_CODE +@@ -83,7 +95,7 @@ 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_expand_block_move (rtx, rtx, rtx); ++extern bool loongarch_expand_block_move (rtx, rtx, rtx, rtx); + extern bool loongarch_do_optimize_block_move_p (void); + + extern bool loongarch_expand_ext_as_unaligned_load (rtx, rtx, HOST_WIDE_INT, +@@ -130,6 +142,7 @@ 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); ++extern bool loongarch_check_zero_div_p (void); + + union loongarch_gen_fn_ptrs + { +diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h +index 0e8889b8c..9f1b0989c 100644 +--- a/gcc/config/loongarch/loongarch-str.h ++++ b/gcc/config/loongarch/loongarch-str.h +@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3. If not see + #define STR_CMODEL_NORMAL "normal" + #define STR_CMODEL_TINY "tiny" + #define STR_CMODEL_TS "tiny-static" ++#define STR_CMODEL_MEDIUM "medium" + #define STR_CMODEL_LARGE "large" + #define STR_CMODEL_EXTREME "extreme" + +diff --git a/gcc/config/loongarch/loongarch-tune.h b/gcc/config/loongarch/loongarch-tune.h +index 6f3530f5c..d961963f0 100644 +--- a/gcc/config/loongarch/loongarch-tune.h ++++ b/gcc/config/loongarch/loongarch-tune.h +@@ -45,6 +45,15 @@ struct loongarch_cache { + int l1d_line_size; /* bytes */ + int l1d_size; /* KiB */ + int l2d_size; /* kiB */ ++ int simultaneous_prefetches; /* number of parallel prefetch */ ++}; ++ ++/* Alignment for functions and labels for best performance. For new uarchs ++ the value should be measured via benchmarking. See the documentation for ++ -falign-functions and -falign-labels in invoke.texi for the format. */ ++struct loongarch_align { ++ const char *function; /* default value for -falign-functions */ ++ const char *label; /* default value for -falign-labels */ + }; + + #endif /* LOONGARCH_TUNE_H */ +diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc +index 22901cb61..ad129be2f 100644 +--- a/gcc/config/loongarch/loongarch.cc ++++ b/gcc/config/loongarch/loongarch.cc +@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3. If not see + #include "context.h" + #include "builtins.h" + #include "rtl-iter.h" ++#include "opts.h" ++#include "function-abi.h" + + /* This file should be included last. */ + #include "target-def.h" +@@ -100,6 +102,10 @@ along with GCC; see the file COPYING3. If not see + ADDRESS_REG_REG + A base register indexed by (optionally scaled) register. + ++ ADDRESS_LO_SUM ++ A LO_SUM rtx. The first operand is a valid base register and the second ++ operand is a symbolic address. ++ + ADDRESS_CONST_INT + A signed 16-bit constant address. + +@@ -109,24 +115,13 @@ enum loongarch_address_type + { + ADDRESS_REG, + ADDRESS_REG_REG, ++ ADDRESS_LO_SUM, + 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_REG_REG +- A base register indexed by (optionally scaled) register. +- +- ADDRESS_SYMBOLIC +- SYMBOL_TYPE is the type of symbol that the address references. */ ++/* Information about an address described by loongarch_address_type. */ + struct loongarch_address_info + { + enum loongarch_address_type type; +@@ -145,22 +140,21 @@ struct loongarch_address_info + + 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 ++ METHOD_LU52I + }; + + struct loongarch_integer_op + { + enum rtx_code code; + HOST_WIDE_INT value; ++ /* Represent the result of the immediate count of the load instruction at ++ each step. */ ++ HOST_WIDE_INT curr_value; + enum loongarch_load_imm_method method; + }; + +@@ -264,6 +258,10 @@ const char *const + loongarch_fp_conditions[16]= {LARCH_FP_CONDITIONS (STRINGIFY)}; + #undef STRINGIFY + ++/* Size of guard page. */ ++#define STACK_CLASH_PROTECTION_GUARD_SIZE \ ++ (1 << param_stack_clash_protection_guard_size) ++ + /* 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. */ +@@ -766,7 +764,9 @@ loongarch_setup_incoming_varargs (cumulative_args_t 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; ++ gp_saved = cfun->va_list_gpr_size / UNITS_PER_WORD; ++ if (gp_saved > (int) (MAX_ARGS_IN_REGISTERS - local_cum.num_gprs)) ++ gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs; + + if (!no_rtl && gp_saved > 0) + { +@@ -1017,19 +1017,23 @@ loongarch_for_each_saved_reg (HOST_WIDE_INT 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); ++ if (!cfun->machine->reg_is_wrapped_separately[regno]) ++ 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; ++ machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode; ++ + 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; ++ if (!cfun->machine->reg_is_wrapped_separately[regno]) ++ loongarch_save_restore_reg (word_mode, regno, offset, fn); + +- loongarch_save_restore_reg (mode, regno, offset, fn); + offset -= GET_MODE_SIZE (mode); + } + } +@@ -1076,11 +1080,20 @@ loongarch_restore_reg (rtx reg, rtx mem) + static HOST_WIDE_INT + loongarch_first_stack_step (struct loongarch_frame_info *frame) + { ++ HOST_WIDE_INT min_first_step ++ = LARCH_STACK_ALIGN (frame->total_size - frame->fp_sp_offset); ++ ++ /* When stack checking is required, if the sum of frame->total_size ++ and stack_check_protect is greater than stack clash protection guard ++ size, then return min_first_step. */ ++ if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK ++ || (flag_stack_clash_protection ++ && frame->total_size > STACK_CLASH_PROTECTION_GUARD_SIZE)) ++ return min_first_step; ++ + 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); +@@ -1113,103 +1126,109 @@ loongarch_emit_stack_tie (void) + 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; ++ HOST_WIDE_INT rounded_size; ++ HOST_WIDE_INT interval; ++ ++ if (flag_stack_clash_protection) ++ interval = STACK_CLASH_PROTECTION_GUARD_SIZE; ++ else ++ interval = PROBE_INTERVAL; + +- /* 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))); ++ rtx r12 = LARCH_PROLOGUE_TEMP2 (Pmode); ++ rtx r14 = LARCH_PROLOGUE_TEMP3 (Pmode); + +- emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx, +- -(first + size))); +- } ++ size = size + first; + +- /* 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); + +- /* 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, interval); + +- /* Step 1: round SIZE to the previous multiple of the interval. */ ++ /* Step 2: compute initial and final value of the loop counter. */ + +- rounded_size = ROUND_DOWN (size, PROBE_INTERVAL); ++ emit_move_insn (r14, GEN_INT (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); ++ /* If rounded_size is zero, it means that the space requested by ++ the local variable is less than the interval, and there is no ++ need to display and detect the allocated space. */ ++ if (rounded_size != 0) ++ { ++ /* Step 3: the loop + +- /* Step 2: compute initial and final value of the loop counter. */ ++ do ++ { ++ TEST_ADDR = TEST_ADDR + PROBE_INTERVAL ++ probe at TEST_ADDR ++ } ++ while (TEST_ADDR != LAST_ADDR) + +- emit_move_insn (r14, GEN_INT (PROBE_INTERVAL)); +- /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */ +- if (rounded_size == 0) +- emit_move_insn (r12, r13); ++ probes at FIRST + N * PROBE_INTERVAL for values of N from 1 ++ until it is equal to ROUNDED_SIZE. */ ++ ++ if (rounded_size <= STACK_CLASH_MAX_UNROLL_PAGES * interval) ++ { ++ for (HOST_WIDE_INT i = 0; i < rounded_size; i += interval) ++ { ++ emit_insn (gen_rtx_SET (stack_pointer_rtx, ++ gen_rtx_MINUS (Pmode, ++ stack_pointer_rtx, ++ r14))); ++ emit_move_insn (gen_rtx_MEM (Pmode, ++ gen_rtx_PLUS (Pmode, ++ stack_pointer_rtx, ++ const0_rtx)), ++ const0_rtx); ++ emit_insn (gen_blockage ()); ++ } ++ dump_stack_clash_frame_info (PROBE_INLINE, size != rounded_size); ++ } + 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 (gen_probe_stack_range (Pmode, r13, r13, r12, r14)); ++ emit_insn (gen_rtx_SET (r12, ++ gen_rtx_MINUS (Pmode, ++ stack_pointer_rtx, ++ r12))); ++ ++ emit_insn (gen_probe_stack_range (Pmode, stack_pointer_rtx, ++ stack_pointer_rtx, r12, r14)); ++ emit_insn (gen_blockage ()); ++ dump_stack_clash_frame_info (PROBE_LOOP, size != rounded_size); + } ++ } ++ else ++ dump_stack_clash_frame_info (NO_PROBE_SMALL_FRAME, true); + +- /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time +- that SIZE is equal to ROUNDED_SIZE. */ + +- if (size != rounded_size) ++ /* 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 (size - rounded_size >= 2048) + { +- 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); +- } ++ emit_move_insn (r14, GEN_INT (size - rounded_size)); ++ emit_insn (gen_rtx_SET (stack_pointer_rtx, ++ gen_rtx_MINUS (Pmode, ++ stack_pointer_rtx, ++ r14))); + } ++ else ++ emit_insn (gen_rtx_SET (stack_pointer_rtx, ++ gen_rtx_PLUS (Pmode, ++ stack_pointer_rtx, ++ GEN_INT (rounded_size - size)))); + } + ++ if (first) ++ { ++ emit_move_insn (r12, GEN_INT (first)); ++ emit_insn (gen_rtx_SET (stack_pointer_rtx, ++ gen_rtx_PLUS (Pmode, ++ stack_pointer_rtx, r12))); ++ } + /* Make sure nothing is scheduled before we are done. */ + emit_insn (gen_blockage ()); + } +@@ -1230,7 +1249,6 @@ loongarch_output_probe_stack_range (rtx reg1, rtx reg2, rtx reg3) + + /* 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); +@@ -1256,28 +1274,11 @@ loongarch_expand_prologue (void) + { + struct loongarch_frame_info *frame = &cfun->machine->frame; + HOST_WIDE_INT size = frame->total_size; +- HOST_WIDE_INT tmp; + 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) + { +@@ -1290,7 +1291,6 @@ loongarch_expand_prologue (void) + loongarch_for_each_saved_reg (size, loongarch_save_reg); + } + +- + /* Set up the frame pointer, if we're using one. */ + if (frame_pointer_needed) + { +@@ -1301,7 +1301,45 @@ loongarch_expand_prologue (void) + loongarch_emit_stack_tie (); + } + +- /* Allocate the rest of the frame. */ ++ if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK ++ || flag_stack_clash_protection) ++ { ++ HOST_WIDE_INT first = get_stack_check_protect (); ++ ++ if (frame->total_size == 0) ++ { ++ /* do nothing. */ ++ dump_stack_clash_frame_info (NO_PROBE_NO_FRAME, false); ++ return; ++ } ++ ++ if (crtl->is_leaf && !cfun->calls_alloca) ++ { ++ HOST_WIDE_INT interval; ++ ++ if (flag_stack_clash_protection) ++ interval = STACK_CLASH_PROTECTION_GUARD_SIZE; ++ else ++ interval = PROBE_INTERVAL; ++ ++ if (size > interval && size > first) ++ loongarch_emit_probe_stack_range (first, size - first); ++ else ++ loongarch_emit_probe_stack_range (first, size); ++ } ++ else ++ loongarch_emit_probe_stack_range (first, size); ++ ++ if (size > 0) ++ { ++ /* 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; ++ } ++ + if (size > 0) + { + if (IMM12_OPERAND (-size)) +@@ -1312,7 +1350,8 @@ loongarch_expand_prologue (void) + } + else + { +- loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), GEN_INT (-size)); ++ 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))); + +@@ -1480,24 +1519,27 @@ loongarch_build_integer (struct loongarch_integer_op *codes, + { + /* The value of the lower 32 bit be loaded with one instruction. + lu12i.w. */ +- codes[0].code = UNKNOWN; +- codes[0].method = METHOD_NORMAL; +- codes[0].value = low_part; ++ codes[cost].code = UNKNOWN; ++ codes[cost].method = METHOD_NORMAL; ++ codes[cost].value = low_part; ++ codes[cost].curr_value = low_part; + cost++; + } + else + { + /* lu12i.w + ior. */ +- codes[0].code = UNKNOWN; +- codes[0].method = METHOD_NORMAL; +- codes[0].value = low_part & ~(IMM_REACH - 1); ++ codes[cost].code = UNKNOWN; ++ codes[cost].method = METHOD_NORMAL; ++ codes[cost].value = low_part & ~(IMM_REACH - 1); ++ codes[cost].curr_value = codes[cost].value; + 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; ++ codes[cost].code = IOR; ++ codes[cost].method = METHOD_NORMAL; ++ codes[cost].value = iorv; ++ codes[cost].curr_value = low_part; + cost++; + } + } +@@ -1520,11 +1562,14 @@ loongarch_build_integer (struct loongarch_integer_op *codes, + { + codes[cost].method = METHOD_LU52I; + codes[cost].value = value & LU52I_B; ++ codes[cost].curr_value = value; + return cost + 1; + } + + codes[cost].method = METHOD_LU32I; + codes[cost].value = (value & LU32I_B) | (sign51 ? LU52I_B : 0); ++ codes[cost].curr_value = (value & 0xfffffffffffff) ++ | (sign51 ? LU52I_B : 0); + cost++; + + /* Determine whether the 52-61 bits are sign-extended from the low order, +@@ -1533,6 +1578,7 @@ loongarch_build_integer (struct loongarch_integer_op *codes, + { + codes[cost].method = METHOD_LU52I; + codes[cost].value = value & LU52I_B; ++ codes[cost].curr_value = value; + cost++; + } + } +@@ -1617,11 +1663,15 @@ loongarch_weak_symbol_p (const_rtx x) + bool + loongarch_symbol_binds_local_p (const_rtx x) + { +- if (LABEL_REF_P (x)) +- return false; ++ if (TARGET_DIRECT_EXTERN_ACCESS) ++ return true; + +- return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) +- : SYMBOL_REF_LOCAL_P (x)); ++ if (SYMBOL_REF_P (x)) ++ return (SYMBOL_REF_DECL (x) ++ ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) ++ : SYMBOL_REF_LOCAL_P (x)); ++ else ++ return false; + } + + /* Return true if rtx constants of mode MODE should be put into a small +@@ -1639,18 +1689,55 @@ loongarch_rtx_constant_in_small_data_p (machine_mode mode) + static enum loongarch_symbol_type + loongarch_classify_symbol (const_rtx x) + { +- if (LABEL_REF_P (x)) +- return SYMBOL_GOT_DISP; ++ enum loongarch_symbol_type pcrel = ++ TARGET_CMODEL_EXTREME ? SYMBOL_PCREL64 : SYMBOL_PCREL; + +- gcc_assert (SYMBOL_REF_P (x)); ++ if (!SYMBOL_REF_P (x)) ++ return pcrel; + + if (SYMBOL_REF_TLS_MODEL (x)) + return SYMBOL_TLS; + +- if (SYMBOL_REF_P (x)) ++ if (!loongarch_symbol_binds_local_p (x)) + return SYMBOL_GOT_DISP; + +- return SYMBOL_GOT_DISP; ++ tree t = SYMBOL_REF_DECL (x); ++ if (!t) ++ return pcrel; ++ ++ t = lookup_attribute ("model", DECL_ATTRIBUTES (t)); ++ if (!t) ++ return pcrel; ++ ++ t = TREE_VALUE (TREE_VALUE (t)); ++ ++ /* loongarch_handle_model_attribute should reject other values. */ ++ gcc_assert (TREE_CODE (t) == STRING_CST); ++ ++ const char *model = TREE_STRING_POINTER (t); ++ if (strcmp (model, "normal") == 0) ++ return SYMBOL_PCREL; ++ if (strcmp (model, "extreme") == 0) ++ return SYMBOL_PCREL64; ++ ++ /* loongarch_handle_model_attribute should reject unknown model ++ name. */ ++ gcc_unreachable (); ++} ++ ++/* Classify the base of symbolic expression X, given that X appears in ++ context CONTEXT. */ ++ ++static enum loongarch_symbol_type ++loongarch_classify_symbolic_expression (rtx x) ++{ ++ rtx offset; ++ ++ split_const (x, &x, &offset); ++ if (UNSPEC_ADDRESS_P (x)) ++ return UNSPEC_ADDRESS_TYPE (x); ++ ++ return loongarch_classify_symbol (x); + } + + /* Return true if X is a symbolic constant. If it is, +@@ -1683,9 +1770,16 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type) + relocations. */ + switch (*symbol_type) + { +- case SYMBOL_GOT_DISP: ++ case SYMBOL_TLS_IE: ++ case SYMBOL_TLS_LE: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: ++ case SYMBOL_PCREL: ++ case SYMBOL_PCREL64: ++ /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */ ++ return sext_hwi (INTVAL (offset), 32) == INTVAL (offset); ++ ++ case SYMBOL_GOT_DISP: + case SYMBOL_TLS: + return false; + } +@@ -1702,14 +1796,22 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode) + 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) ++ if (!TARGET_EXPLICIT_RELOCS && mode != MAX_MACHINE_MODE) + return 0; + + return 3; + ++ case SYMBOL_PCREL: ++ case SYMBOL_TLS_IE: ++ case SYMBOL_TLS_LE: ++ return 2; ++ + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: +- return 1; ++ return 3; ++ ++ case SYMBOL_PCREL64: ++ return 5; + + case SYMBOL_TLS: + /* We don't treat a bare TLS symbol as a constant. */ +@@ -1815,6 +1917,85 @@ loongarch_valid_offset_p (rtx x, machine_mode mode) + return true; + } + ++/* Should a symbol of type SYMBOL_TYPE should be split in two or more? */ ++ ++bool ++loongarch_split_symbol_type (enum loongarch_symbol_type symbol_type) ++{ ++ switch (symbol_type) ++ { ++ case SYMBOL_PCREL: ++ case SYMBOL_PCREL64: ++ case SYMBOL_GOT_DISP: ++ case SYMBOL_TLS_IE: ++ case SYMBOL_TLS_LE: ++ case SYMBOL_TLSGD: ++ case SYMBOL_TLSLDM: ++ return true; ++ ++ case SYMBOL_TLS: ++ return false; ++ ++ default: ++ gcc_unreachable (); ++ } ++} ++ ++/* Return true if a LO_SUM can address a value of mode MODE when the ++ LO_SUM symbol has type SYMBOL_TYPE. */ ++ ++static bool ++loongarch_valid_lo_sum_p (enum loongarch_symbol_type symbol_type, ++ machine_mode mode, rtx x) ++{ ++ int align, size; ++ ++ /* Check that symbols of type SYMBOL_TYPE can be used to access values ++ of mode MODE. */ ++ if (loongarch_symbol_insns (symbol_type, mode) == 0) ++ return false; ++ ++ /* Check that there is a known low-part relocation. */ ++ if (!loongarch_split_symbol_type (symbol_type)) ++ return false; ++ ++ /* We can't tell size or alignment when we have BLKmode, so try extracing a ++ decl from the symbol if possible. */ ++ if (mode == BLKmode) ++ { ++ rtx offset; ++ ++ /* Extract the symbol from the LO_SUM operand, if any. */ ++ split_const (x, &x, &offset); ++ ++ /* Might be a CODE_LABEL. We can compute align but not size for that, ++ so don't bother trying to handle it. */ ++ if (!SYMBOL_REF_P (x)) ++ return false; ++ ++ /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ ++ align = (SYMBOL_REF_DECL (x) ++ ? DECL_ALIGN (SYMBOL_REF_DECL (x)) ++ : 1); ++ size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) ++ ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) ++ : 2*BITS_PER_WORD); ++ } ++ else ++ { ++ align = GET_MODE_ALIGNMENT (mode); ++ size = GET_MODE_BITSIZE (mode); ++ } ++ ++ /* We may need to split multiword moves, so make sure that each word ++ can be accessed without inducing a carry. */ ++ if (size > BITS_PER_WORD ++ && (!TARGET_STRICT_ALIGN || size > align)) ++ return false; ++ ++ return true; ++} ++ + static bool + loongarch_valid_index_p (struct loongarch_address_info *info, rtx x, + machine_mode mode, bool strict_p) +@@ -1881,6 +2062,31 @@ loongarch_classify_address (struct loongarch_address_info *info, rtx x, + info->offset = XEXP (x, 1); + return (loongarch_valid_base_register_p (info->reg, mode, strict_p) + && loongarch_valid_offset_p (info->offset, mode)); ++ ++ case LO_SUM: ++ info->type = ADDRESS_LO_SUM; ++ info->reg = XEXP (x, 0); ++ info->offset = XEXP (x, 1); ++ /* We have to trust the creator of the LO_SUM to do something vaguely ++ sane. Target-independent code that creates a LO_SUM should also ++ create and verify the matching HIGH. Target-independent code that ++ adds an offset to a LO_SUM must prove that the offset will not ++ induce a carry. Failure to do either of these things would be ++ a bug, and we are not required to check for it here. The MIPS ++ backend itself should only create LO_SUMs for valid symbolic ++ constants, with the high part being either a HIGH or a copy ++ of _gp. */ ++ info->symbol_type ++ = loongarch_classify_symbolic_expression (info->offset); ++ return (loongarch_valid_base_register_p (info->reg, mode, strict_p) ++ && loongarch_valid_lo_sum_p (info->symbol_type, mode, ++ info->offset)); ++ case CONST_INT: ++ /* Small-integer addresses don't occur very often, but they ++ are legitimate if $r0 is a valid base register. */ ++ info->type = ADDRESS_CONST_INT; ++ return IMM12_OPERAND (INTVAL (x)); ++ + default: + return false; + } +@@ -1937,14 +2143,13 @@ loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p) + switch (addr.type) + { + case ADDRESS_REG: +- return factor; +- + case ADDRESS_REG_REG: +- return factor; +- + case ADDRESS_CONST_INT: + return factor; + ++ case ADDRESS_LO_SUM: ++ return factor + 1; ++ + case ADDRESS_SYMBOLIC: + return factor * loongarch_symbol_insns (addr.symbol_type, mode); + } +@@ -1972,7 +2177,8 @@ loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits, + return loongarch_unsigned_immediate_p (x, bits, shift); + } + +-/* Return true if X is a legitimate address with a 12-bit offset. ++/* Return true if X is a legitimate address with a 12-bit offset ++ or addr.type is ADDRESS_LO_SUM. + MODE is the mode of the value being accessed. */ + + bool +@@ -1981,9 +2187,10 @@ 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))); ++ && ((addr.type == ADDRESS_REG ++ && CONST_INT_P (addr.offset) ++ && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset))) ++ || addr.type == ADDRESS_LO_SUM)); + } + + /* Return true if X is a legitimate address with a 14-bit offset shifted 2. +@@ -2001,6 +2208,9 @@ loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode) + && LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset))); + } + ++/* Return true if X is a legitimate address with base and index. ++ MODE is the mode of the value being accessed. */ ++ + bool + loongarch_base_index_address_p (rtx x, machine_mode mode) + { +@@ -2022,6 +2232,14 @@ loongarch_const_insns (rtx x) + + switch (GET_CODE (x)) + { ++ case HIGH: ++ if (!loongarch_symbolic_constant_p (XEXP (x, 0), &symbol_type) ++ || !loongarch_split_symbol_type (symbol_type)) ++ return 0; ++ ++ /* This is simply a PCALAU12I. */ ++ return 1; ++ + case CONST_INT: + return loongarch_integer_cost (INTVAL (x)); + +@@ -2082,6 +2300,8 @@ loongarch_split_const_insns (rtx x) + return low + high; + } + ++static bool loongarch_split_move_insn_p (rtx dest, rtx src); ++ + /* Return the number of instructions needed to implement INSN, + given that it loads from or stores to MEM. */ + +@@ -2110,7 +2330,7 @@ loongarch_load_store_insns (rtx mem, rtx_insn *insn) + + /* Return true if we need to trap on division by zero. */ + +-static bool ++bool + loongarch_check_zero_div_p (void) + { + /* if -m[no-]check-zero-division is given explicitly. */ +@@ -2199,6 +2419,15 @@ loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type) + return loongarch_unspec_address_offset (base, offset, symbol_type); + } + ++/* Emit an instruction of the form (set TARGET SRC). */ ++ ++static rtx ++loongarch_emit_set (rtx target, rtx src) ++{ ++ emit_insn (gen_rtx_SET (target, src)); ++ return target; ++} ++ + /* If OP is an UNSPEC address, return the address to which it refers, + otherwise return OP itself. */ + +@@ -2280,6 +2509,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) + { + rtx loc, a0; + rtx_insn *insn; ++ rtx tmp = gen_reg_rtx (Pmode); + + a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); + +@@ -2290,15 +2520,129 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) + + 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)); ++ if (TARGET_EXPLICIT_RELOCS) ++ { ++ /* Split tls symbol to high and low. */ ++ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (loc)); ++ high = loongarch_force_temporary (tmp, high); ++ ++ if (TARGET_CMODEL_EXTREME) ++ { ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ rtx tmp1 = gen_reg_rtx (Pmode); ++ emit_insn (gen_tls_low (Pmode, tmp1, gen_rtx_REG (Pmode, 0), loc)); ++ emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loc)); ++ emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loc)); ++ emit_move_insn (a0, gen_rtx_PLUS (Pmode, high, tmp1)); ++ } ++ else ++ emit_insn (gen_tls_low (Pmode, a0, high, loc)); ++ } ++ else ++ { ++ 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 (); ++ } ++ ++ if (flag_plt) ++ { ++ switch (la_opt_cmodel) ++ { ++ case CMODEL_NORMAL: ++ insn = emit_call_insn (gen_call_value_internal (v0, ++ loongarch_tls_symbol, ++ const0_rtx)); ++ break; ++ ++ case CMODEL_MEDIUM: ++ { ++ rtx reg = gen_reg_rtx (Pmode); ++ if (TARGET_EXPLICIT_RELOCS) ++ { ++ emit_insn (gen_pcalau12i (Pmode, reg, loongarch_tls_symbol)); ++ rtx call = gen_call_value_internal_1 (Pmode, v0, reg, ++ loongarch_tls_symbol, ++ const0_rtx); ++ insn = emit_call_insn (call); ++ } ++ else ++ { ++ emit_move_insn (reg, loongarch_tls_symbol); ++ insn = emit_call_insn (gen_call_value_internal (v0, ++ reg, ++ const0_rtx)); ++ } ++ break; ++ } ++ ++ /* code model extreme not support plt. */ ++ case CMODEL_EXTREME: ++ case CMODEL_LARGE: ++ case CMODEL_TINY: ++ case CMODEL_TINY_STATIC: ++ default: ++ gcc_unreachable (); ++ } ++ } + else +- gcc_unreachable (); ++ { ++ rtx dest = gen_reg_rtx (Pmode); ++ ++ switch (la_opt_cmodel) ++ { ++ case CMODEL_NORMAL: ++ case CMODEL_MEDIUM: ++ { ++ if (TARGET_EXPLICIT_RELOCS) ++ { ++ rtx high = gen_reg_rtx (Pmode); ++ loongarch_emit_move (high, ++ gen_rtx_HIGH (Pmode, ++ loongarch_tls_symbol)); ++ emit_insn (gen_ld_from_got (Pmode, dest, high, ++ loongarch_tls_symbol)); ++ } ++ else ++ loongarch_emit_move (dest, loongarch_tls_symbol); ++ break; ++ } ++ ++ case CMODEL_EXTREME: ++ { ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ rtx tmp1 = gen_reg_rtx (Pmode); ++ rtx high = gen_reg_rtx (Pmode); ++ ++ loongarch_emit_move (high, ++ gen_rtx_HIGH (Pmode, loongarch_tls_symbol)); ++ loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode, ++ gen_rtx_REG (Pmode, 0), ++ loongarch_tls_symbol)); ++ emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol)); ++ emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol)); ++ loongarch_emit_move (dest, ++ gen_rtx_MEM (Pmode, ++ gen_rtx_PLUS (Pmode, ++ high, tmp1))); ++ } ++ break; ++ ++ case CMODEL_LARGE: ++ case CMODEL_TINY: ++ case CMODEL_TINY_STATIC: ++ default: ++ gcc_unreachable (); ++ } ++ ++ insn = emit_call_insn (gen_call_value_internal (v0, dest, const0_rtx)); ++ } + +- 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 (); +@@ -2315,7 +2659,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) + static rtx + loongarch_legitimize_tls_address (rtx loc) + { +- rtx dest, tp, tmp; ++ rtx dest, tp, tmp, tmp1, tmp2, tmp3; + enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); + rtx_insn *insn; + +@@ -2336,28 +2680,75 @@ loongarch_legitimize_tls_address (rtx 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)); ++ { ++ /* la.tls.ie; tp-relative add. */ ++ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); ++ tmp1 = gen_reg_rtx (Pmode); ++ dest = gen_reg_rtx (Pmode); ++ if (TARGET_EXPLICIT_RELOCS) ++ { ++ tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_IE); ++ tmp3 = gen_reg_rtx (Pmode); ++ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); ++ high = loongarch_force_temporary (tmp3, high); ++ ++ if (TARGET_CMODEL_EXTREME) ++ { ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ rtx tmp3 = gen_reg_rtx (Pmode); ++ emit_insn (gen_tls_low (Pmode, tmp3, ++ gen_rtx_REG (Pmode, 0), tmp2)); ++ emit_insn (gen_lui_h_lo20 (tmp3, tmp3, tmp2)); ++ emit_insn (gen_lui_h_hi12 (tmp3, tmp3, tmp2)); ++ emit_move_insn (tmp1, ++ gen_rtx_MEM (Pmode, ++ gen_rtx_PLUS (Pmode, ++ high, tmp3))); ++ } ++ else ++ emit_insn (gen_ld_from_got (Pmode, tmp1, high, tmp2)); ++ } ++ else ++ emit_insn (loongarch_got_load_tls_ie (tmp1, loc)); ++ emit_insn (gen_add3_insn (dest, tmp1, 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; ++ { ++ /* la.tls.le; tp-relative add. */ ++ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); ++ tmp1 = gen_reg_rtx (Pmode); ++ dest = gen_reg_rtx (Pmode); + +- default: +- gcc_unreachable (); +- } +- return dest; +-} ++ if (TARGET_EXPLICIT_RELOCS) ++ { ++ tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_LE); ++ tmp3 = gen_reg_rtx (Pmode); ++ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); ++ high = loongarch_force_temporary (tmp3, high); ++ emit_insn (gen_ori_l_lo12 (Pmode, tmp1, high, tmp2)); ++ ++ if (TARGET_CMODEL_EXTREME) ++ { ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ emit_insn (gen_lui_h_lo20 (tmp1, tmp1, tmp2)); ++ emit_insn (gen_lui_h_hi12 (tmp1, tmp1, tmp2)); ++ } ++ } ++ else ++ emit_insn (loongarch_got_load_tls_le (tmp1, loc)); ++ emit_insn (gen_add3_insn (dest, tmp1, tp)); ++ } ++ break; ++ ++ default: ++ gcc_unreachable (); ++ } ++ return dest; ++} + + rtx + loongarch_legitimize_call_address (rtx addr) +@@ -2368,6 +2759,24 @@ loongarch_legitimize_call_address (rtx addr) + loongarch_emit_move (reg, addr); + return reg; + } ++ ++ enum loongarch_symbol_type symbol_type = loongarch_classify_symbol (addr); ++ ++ /* Split function call insn 'bl sym' or 'bl %plt(sym)' to : ++ pcalau12i $rd, %pc_hi20(sym) ++ jr $rd, %pc_lo12(sym). */ ++ ++ if (TARGET_CMODEL_MEDIUM ++ && TARGET_EXPLICIT_RELOCS ++ && (SYMBOL_REF_P (addr) || LABEL_REF_P (addr)) ++ && (symbol_type == SYMBOL_PCREL ++ || (symbol_type == SYMBOL_GOT_DISP && flag_plt))) ++ { ++ rtx reg = gen_reg_rtx (Pmode); ++ emit_insn (gen_pcalau12i (Pmode, reg, addr)); ++ return gen_rtx_LO_SUM (Pmode, reg, addr); ++ } ++ + return addr; + } + +@@ -2399,6 +2808,107 @@ loongarch_force_address (rtx x, machine_mode mode) + return x; + } + ++static bool ++loongarch_symbol_extreme_p (enum loongarch_symbol_type type) ++{ ++ switch (type) ++ { ++ case SYMBOL_PCREL: ++ return false; ++ case SYMBOL_PCREL64: ++ return true; ++ default: ++ return TARGET_CMODEL_EXTREME; ++ } ++} ++ ++/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise ++ it appears in a MEM of that mode. Return true if ADDR is a legitimate ++ constant in that context and can be split into high and low parts. ++ If so, and if LOW_OUT is nonnull, emit the high part and store the ++ low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise. ++ ++ Return false if build with '-mno-explicit-relocs'. ++ ++ TEMP is as for loongarch_force_temporary and is used to load the high ++ part into a register. ++ ++ When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be ++ a legitimize SET_SRC for an .md pattern, otherwise the low part ++ is guaranteed to be a legitimate address for mode MODE. */ ++ ++bool ++loongarch_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out) ++{ ++ enum loongarch_symbol_type symbol_type; ++ ++ /* If build with '-mno-explicit-relocs', don't split symbol. */ ++ if (!TARGET_EXPLICIT_RELOCS) ++ return false; ++ ++ if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE) ++ || !loongarch_symbolic_constant_p (addr, &symbol_type) ++ || loongarch_symbol_insns (symbol_type, mode) == 0 ++ || !loongarch_split_symbol_type (symbol_type)) ++ return false; ++ ++ rtx high, temp1 = NULL; ++ ++ if (temp == NULL) ++ temp = gen_reg_rtx (Pmode); ++ ++ /* Get the 12-31 bits of the address. */ ++ high = gen_rtx_HIGH (Pmode, copy_rtx (addr)); ++ high = loongarch_force_temporary (temp, high); ++ ++ if (loongarch_symbol_extreme_p (symbol_type) && can_create_pseudo_p ()) ++ { ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ temp1 = gen_reg_rtx (Pmode); ++ emit_move_insn (temp1, gen_rtx_LO_SUM (Pmode, gen_rtx_REG (Pmode, 0), ++ addr)); ++ emit_insn (gen_lui_h_lo20 (temp1, temp1, addr)); ++ emit_insn (gen_lui_h_hi12 (temp1, temp1, addr)); ++ } ++ ++ if (low_out) ++ switch (symbol_type) ++ { ++ case SYMBOL_PCREL64: ++ if (can_create_pseudo_p ()) ++ { ++ *low_out = gen_rtx_PLUS (Pmode, high, temp1); ++ break; ++ } ++ /* fall through */ ++ case SYMBOL_PCREL: ++ *low_out = gen_rtx_LO_SUM (Pmode, high, addr); ++ break; ++ ++ case SYMBOL_GOT_DISP: ++ /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */ ++ { ++ if (TARGET_CMODEL_EXTREME && can_create_pseudo_p ()) ++ *low_out = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, high, temp1)); ++ else ++ { ++ rtx low = gen_rtx_LO_SUM (Pmode, high, addr); ++ rtx mem = gen_rtx_MEM (Pmode, low); ++ *low_out = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, mem), ++ UNSPEC_LOAD_FROM_GOT); ++ } ++ ++ break; ++ } ++ ++ default: ++ gcc_unreachable (); ++ } ++ ++ return true; ++} ++ + /* 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 +@@ -2414,6 +2924,10 @@ loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, + if (loongarch_tls_symbol_p (x)) + return loongarch_legitimize_tls_address (x); + ++ /* See if the address can split into a high part and a LO_SUM. */ ++ if (loongarch_split_symbol (NULL, x, mode, &addr)) ++ return loongarch_force_address (addr, mode); ++ + /* Handle BASE + OFFSET using loongarch_add_offset. */ + loongarch_split_plus (x, &base, &offset); + if (offset != 0) +@@ -2453,6 +2967,9 @@ loongarch_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value) + else + x = force_reg (mode, x); + ++ set_unique_reg_note (get_last_insn (), REG_EQUAL, ++ GEN_INT (codes[i-1].curr_value)); ++ + switch (codes[i].method) + { + case METHOD_NORMAL: +@@ -2460,22 +2977,17 @@ loongarch_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value) + 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)))); ++ gcc_assert (mode == DImode); ++ 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))); ++ gcc_assert (mode == DImode); ++ x = gen_rtx_IOR (DImode, ++ gen_rtx_AND (DImode, x, GEN_INT (0xfffffffffffff)), ++ GEN_INT (codes[i].value)); + break; + default: + gcc_unreachable (); +@@ -2501,6 +3013,13 @@ loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src) + return; + } + ++ /* Split moves of symbolic constants into high and low. */ ++ if (loongarch_split_symbol (dest, src, MAX_MACHINE_MODE, &src)) ++ { ++ loongarch_emit_set (dest, src); ++ return; ++ } ++ + /* Generate the appropriate access sequences for TLS symbols. */ + if (loongarch_tls_symbol_p (src)) + { +@@ -3241,21 +3760,56 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_) + } + } + +-/* Return true if a move from SRC to DEST in INSN should be split. */ ++/* Check if adding an integer constant value for a specific mode can be ++ performed with an addu16i.d instruction and an addi.{w/d} ++ instruction. */ + + bool +-loongarch_split_move_insn_p (rtx dest, rtx src) ++loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT value, machine_mode mode) + { +- return loongarch_split_move_p (dest, src); ++ /* Not necessary, but avoid unnecessary calculation if !TARGET_64BIT. */ ++ if (!TARGET_64BIT) ++ return false; ++ ++ if ((value & 0xffff) == 0) ++ return false; ++ ++ if (IMM12_OPERAND (value)) ++ return false; ++ ++ value = (value & ~HWIT_UC_0xFFF) + ((value & 0x800) << 1); ++ return ADDU16I_OPERAND (trunc_int_for_mode (value, mode)); + } + +-/* Split a move from SRC to DEST in INSN, given that +- loongarch_split_move_insn_p holds. */ ++/* Split one integer constant op[0] into two (op[1] and op[2]) for constant ++ plus operation in a specific mode. The splitted constants can be added ++ onto a register with a single instruction (addi.{d/w} or addu16i.d). */ + + void +-loongarch_split_move_insn (rtx dest, rtx src, rtx insn) ++loongarch_split_plus_constant (rtx *op, machine_mode mode) + { +- loongarch_split_move (dest, src, insn); ++ HOST_WIDE_INT v = INTVAL (op[0]), a; ++ ++ if (DUAL_IMM12_OPERAND (v)) ++ a = (v > 0 ? 2047 : -2048); ++ else if (loongarch_addu16i_imm12_operand_p (v, mode)) ++ a = (v & ~HWIT_UC_0xFFF) + ((v & 0x800) << 1); ++ else if (mode == DImode && DUAL_ADDU16I_OPERAND (v)) ++ a = (v > 0 ? 0x7fff : -0x8000) << 16; ++ else ++ gcc_unreachable (); ++ ++ op[1] = gen_int_mode (a, mode); ++ v = v - (unsigned HOST_WIDE_INT) a; ++ op[2] = gen_int_mode (v, mode); ++} ++ ++/* Return true if a move from SRC to DEST in INSN should be split. */ ++ ++static bool ++loongarch_split_move_insn_p (rtx dest, rtx src) ++{ ++ return loongarch_split_move_p (dest, src); + } + + /* Implement TARGET_CONSTANT_ALIGNMENT. */ +@@ -3371,12 +3925,16 @@ loongarch_output_move (rtx dest, rtx src) + case 2: + return "st.h\t%z1,%0"; + case 4: +- if (const_arith_operand (offset, Pmode)) ++ /* Matching address type with a 12bit offset and ++ ADDRESS_LO_SUM. */ ++ if (const_arith_operand (offset, Pmode) ++ || GET_CODE (offset) == LO_SUM) + return "st.w\t%z1,%0"; + else + return "stptr.w\t%z1,%0"; + case 8: +- if (const_arith_operand (offset, Pmode)) ++ if (const_arith_operand (offset, Pmode) ++ || GET_CODE (offset) == LO_SUM) + return "st.d\t%z1,%0"; + else + return "stptr.d\t%z1,%0"; +@@ -3409,12 +3967,16 @@ loongarch_output_move (rtx dest, rtx src) + case 2: + return "ld.hu\t%0,%1"; + case 4: +- if (const_arith_operand (offset, Pmode)) ++ /* Matching address type with a 12bit offset and ++ ADDRESS_LO_SUM. */ ++ if (const_arith_operand (offset, Pmode) ++ || GET_CODE (offset) == LO_SUM) + return "ld.w\t%0,%1"; + else + return "ldptr.w\t%0,%1"; + case 8: +- if (const_arith_operand (offset, Pmode)) ++ if (const_arith_operand (offset, Pmode) ++ || GET_CODE (offset) == LO_SUM) + return "ld.d\t%0,%1"; + else + return "ldptr.d\t%0,%1"; +@@ -3423,6 +3985,21 @@ loongarch_output_move (rtx dest, rtx src) + } + } + ++ if (src_code == HIGH) ++ { ++ rtx offset, x; ++ split_const (XEXP (src, 0), &x, &offset); ++ enum loongarch_symbol_type type = SYMBOL_PCREL; ++ ++ if (UNSPEC_ADDRESS_P (x)) ++ type = UNSPEC_ADDRESS_TYPE (x); ++ ++ if (type == SYMBOL_TLS_LE) ++ return "lu12i.w\t%0,%h1"; ++ else ++ return "pcalau12i\t%0,%h1"; ++ } ++ + if (src_code == CONST_INT) + { + if (LU12I_INT (src)) +@@ -3436,56 +4013,17 @@ loongarch_output_move (rtx dest, rtx src) + 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 (LABEL_REF_P (src)) +- align = 32 /* 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_unreachable (); +- } +- +- } ++ if (!TARGET_EXPLICIT_RELOCS ++ && dest_code == REG && symbolic_operand (src, VOIDmode)) ++ { ++ if (loongarch_classify_symbol (src) == SYMBOL_PCREL) ++ return "la.local\t%0,%1"; ++ else ++ return "la.global\t%0,%1"; + } ++ + if (src_code == REG && FP_REG_P (REGNO (src))) + { + if (dest_code == REG && FP_REG_P (REGNO (dest))) +@@ -3503,6 +4041,7 @@ loongarch_output_move (rtx dest, rtx src) + 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) +@@ -3517,6 +4056,7 @@ loongarch_output_move (rtx dest, rtx src) + return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1"; + } + } ++ + gcc_unreachable (); + } + +@@ -3737,10 +4277,13 @@ loongarch_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1) + if (!increment && !decrement) + continue; + ++ if ((increment && rhs == HOST_WIDE_INT_MAX) ++ || (decrement && rhs == HOST_WIDE_INT_MIN)) ++ break; ++ + new_rhs = rhs + (increment ? 1 : -1); + if (loongarch_integer_cost (new_rhs) +- < loongarch_integer_cost (rhs) +- && (rhs < 0) == (new_rhs < 0)) ++ < loongarch_integer_cost (rhs)) + { + *op1 = GEN_INT (new_rhs); + *code = mag_comparisons[i][increment]; +@@ -3920,41 +4463,46 @@ loongarch_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, + Assume that the areas do not overlap. */ + + static void +-loongarch_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length) ++loongarch_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length, ++ HOST_WIDE_INT delta) + { +- HOST_WIDE_INT offset, delta; +- unsigned HOST_WIDE_INT bits; ++ HOST_WIDE_INT offs, delta_cur; + 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; ++ /* Calculate how many registers we'll need for the block move. ++ We'll emit length / delta move operations with delta as the size ++ first. Then we may still have length % delta bytes not copied. ++ We handle these remaining bytes by move operations with smaller ++ (halfed) sizes. For example, if length = 21 and delta = 8, we'll ++ emit two ld.d/st.d pairs, one ld.w/st.w pair, and one ld.b/st.b ++ pair. For each load/store pair we use a dedicated register to keep ++ the pipeline as populated as possible. */ ++ HOST_WIDE_INT num_reg = length / delta; ++ for (delta_cur = delta / 2; delta_cur != 0; delta_cur /= 2) ++ num_reg += !!(length & delta_cur); + + /* Allocate a buffer for the temporary registers. */ +- regs = XALLOCAVEC (rtx, length / delta); ++ regs = XALLOCAVEC (rtx, num_reg); + +- /* 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++) ++ for (delta_cur = delta, i = 0, offs = 0; offs < length; delta_cur /= 2) + { +- regs[i] = gen_reg_rtx (mode); +- loongarch_emit_move (regs[i], adjust_address (src, mode, offset)); +- } ++ mode = int_mode_for_size (delta_cur * BITS_PER_UNIT, 0).require (); + +- for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) +- loongarch_emit_move (adjust_address (dest, mode, offset), regs[i]); ++ for (; offs + delta_cur <= length; offs += delta_cur, i++) ++ { ++ regs[i] = gen_reg_rtx (mode); ++ loongarch_emit_move (regs[i], adjust_address (src, mode, offs)); ++ } ++ } + +- /* Mop up any left-over bytes. */ +- if (offset < length) ++ for (delta_cur = delta, i = 0, offs = 0; offs < length; delta_cur /= 2) + { +- 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); ++ mode = int_mode_for_size (delta_cur * BITS_PER_UNIT, 0).require (); ++ ++ for (; offs + delta_cur <= length; offs += delta_cur, i++) ++ loongarch_emit_move (adjust_address (dest, mode, offs), regs[i]); + } + } + +@@ -3984,10 +4532,11 @@ loongarch_adjust_block_mem (rtx mem, HOST_WIDE_INT length, rtx *loop_reg, + + static void + loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, +- HOST_WIDE_INT bytes_per_iter) ++ HOST_WIDE_INT align) + { + rtx_code_label *label; + rtx src_reg, dest_reg, final_src, test; ++ HOST_WIDE_INT bytes_per_iter = align * LARCH_MAX_MOVE_OPS_PER_LOOP_ITER; + HOST_WIDE_INT leftover; + + leftover = length % bytes_per_iter; +@@ -4007,7 +4556,7 @@ loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, + emit_label (label); + + /* Emit the loop body. */ +- loongarch_block_move_straight (dest, src, bytes_per_iter); ++ loongarch_block_move_straight (dest, src, bytes_per_iter, align); + + /* Move on to the next block. */ + loongarch_emit_move (src_reg, +@@ -4024,7 +4573,7 @@ loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, + + /* Mop up any left-over bytes. */ + if (leftover) +- loongarch_block_move_straight (dest, src, leftover); ++ loongarch_block_move_straight (dest, src, leftover, align); + else + /* Temporary fix for PR79150. */ + emit_insn (gen_nop ()); +@@ -4034,25 +4583,32 @@ loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, + memory reference SRC to memory reference DEST. */ + + bool +-loongarch_expand_block_move (rtx dest, rtx src, rtx length) ++loongarch_expand_block_move (rtx dest, rtx src, rtx r_length, rtx r_align) + { +- int max_move_bytes = LARCH_MAX_MOVE_BYTES_STRAIGHT; ++ if (!CONST_INT_P (r_length)) ++ return false; ++ ++ HOST_WIDE_INT length = INTVAL (r_length); ++ if (length > loongarch_max_inline_memcpy_size) ++ return false; + +- if (CONST_INT_P (length) +- && INTVAL (length) <= loongarch_max_inline_memcpy_size) ++ HOST_WIDE_INT align = INTVAL (r_align); ++ ++ if (!TARGET_STRICT_ALIGN || align > UNITS_PER_WORD) ++ align = UNITS_PER_WORD; ++ ++ if (length <= align * LARCH_MAX_MOVE_OPS_STRAIGHT) + { +- 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; +- } ++ loongarch_block_move_straight (dest, src, length, align); ++ return true; + } ++ ++ if (optimize) ++ { ++ loongarch_block_move_loop (dest, src, length, align); ++ return true; ++ } ++ + return false; + } + +@@ -4345,29 +4901,131 @@ loongarch_memmodel_needs_release_fence (enum memmodel model) + } + } + ++/* Print symbolic operand OP, which is part of a HIGH or LO_SUM ++ in context CONTEXT. HI_RELOC indicates a high-part reloc. */ ++ ++static void ++loongarch_print_operand_reloc (FILE *file, rtx op, bool hi64_part, ++ bool hi_reloc) ++{ ++ const char *reloc; ++ enum loongarch_symbol_type symbol_type = ++ loongarch_classify_symbolic_expression (op); ++ ++ if (loongarch_symbol_extreme_p (symbol_type)) ++ gcc_assert (TARGET_EXPLICIT_RELOCS); ++ ++ switch (symbol_type) ++ { ++ case SYMBOL_PCREL64: ++ if (hi64_part) ++ { ++ reloc = hi_reloc ? "%pc64_hi12" : "%pc64_lo20"; ++ break; ++ } ++ /* fall through */ ++ case SYMBOL_PCREL: ++ reloc = hi_reloc ? "%pc_hi20" : "%pc_lo12"; ++ break; ++ ++ case SYMBOL_GOT_DISP: ++ if (hi64_part) ++ { ++ if (TARGET_CMODEL_EXTREME) ++ reloc = hi_reloc ? "%got64_pc_hi12" : "%got64_pc_lo20"; ++ else ++ gcc_unreachable (); ++ } ++ else ++ reloc = hi_reloc ? "%got_pc_hi20" : "%got_pc_lo12"; ++ break; ++ ++ case SYMBOL_TLS_IE: ++ if (hi64_part) ++ { ++ if (TARGET_CMODEL_EXTREME) ++ reloc = hi_reloc ? "%ie64_pc_hi12" : "%ie64_pc_lo20"; ++ else ++ gcc_unreachable (); ++ } ++ else ++ reloc = hi_reloc ? "%ie_pc_hi20" : "%ie_pc_lo12"; ++ break; ++ ++ case SYMBOL_TLS_LE: ++ if (hi64_part) ++ { ++ if (TARGET_CMODEL_EXTREME) ++ reloc = hi_reloc ? "%le64_hi12" : "%le64_lo20"; ++ else ++ gcc_unreachable (); ++ } ++ else ++ reloc = hi_reloc ? "%le_hi20" : "%le_lo12"; ++ break; ++ ++ case SYMBOL_TLSGD: ++ if (hi64_part) ++ { ++ if (TARGET_CMODEL_EXTREME) ++ reloc = hi_reloc ? "%got64_pc_hi12" : "%got64_pc_lo20"; ++ else ++ gcc_unreachable (); ++ } ++ else ++ reloc = hi_reloc ? "%gd_pc_hi20" : "%got_pc_lo12"; ++ break; ++ ++ case SYMBOL_TLSLDM: ++ if (hi64_part) ++ { ++ if (TARGET_CMODEL_EXTREME) ++ reloc = hi_reloc ? "%got64_pc_hi12" : "%got64_pc_lo20"; ++ else ++ gcc_unreachable (); ++ } ++ else ++ reloc = hi_reloc ? "%ld_pc_hi20" : "%got_pc_lo12"; ++ break; ++ ++ default: ++ gcc_unreachable (); ++ } ++ ++ fprintf (file, "%s(", reloc); ++ output_addr_const (file, loongarch_strip_unspec_address (op)); ++ fputc (')', file); ++} ++ + /* 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. ++ 'A' Print a _DB suffix if the memory model requires a release. ++ 'b' Print the address of a memory operand, without offset. ++ 'c' Print an integer. ++ 'C' Print the integer branch condition for comparison OP. + 'd' Print CONST_INT OP in decimal. ++ 'F' Print the FPU branch condition for comparison OP. ++ 'G' Print a DBAR insn if the memory model requires a release. ++ 'H' Print address 52-61bit relocation associated with OP. ++ 'h' Print the high-part relocation associated with OP. ++ 'i' Print i if the operand is not a register. ++ 'L' Print the low-part relocation associated with OP. + '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. ++ 'r' Print address 12-31bit relocation associated with OP. ++ 'R' Print address 32-51bit relocation associated with 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. */ ++ 'W' Print the inverse of the FPU branch condition for comparison OP. ++ 'X' Print CONST_INT OP in hexadecimal format. ++ 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. ++ 'Y' Print loongarch_fp_conditions[INTVAL (OP)] ++ 'y' Print exact log2 of CONST_INT OP in decimal. ++ 'Z' Print OP and a comma for 8CC, otherwise print nothing. ++ 'z' Print $0 if OP is zero, otherwise print OP normally. */ + + static void + loongarch_print_operand (FILE *file, rtx op, int letter) +@@ -4385,18 +5043,21 @@ loongarch_print_operand (FILE *file, rtx op, int letter) + + 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); ++ case 'A': ++ if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) ++ fputs ("_db", file); + break; + +- case 'x': ++ case 'c': + if (CONST_INT_P (op)) +- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); ++ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op)); + else +- output_operand_lossage ("invalid use of '%%%c'", letter); ++ output_operand_lossage ("unsupported operand for code '%c'", letter); ++ ++ break; ++ ++ case 'C': ++ loongarch_print_int_branch_condition (file, code, letter); + break; + + case 'd': +@@ -4406,6 +5067,37 @@ loongarch_print_operand (FILE *file, rtx op, int letter) + output_operand_lossage ("invalid use of '%%%c'", letter); + break; + ++ case 'F': ++ loongarch_print_float_branch_condition (file, code, letter); ++ break; ++ ++ case 'G': ++ if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) ++ fputs ("dbar\t0", file); ++ break; ++ ++ case 'h': ++ if (code == HIGH) ++ op = XEXP (op, 0); ++ loongarch_print_operand_reloc (file, op, false /* hi64_part */, ++ true /* hi_reloc */); ++ break; ++ ++ case 'H': ++ loongarch_print_operand_reloc (file, op, true /* hi64_part */, ++ true /* hi_reloc */); ++ break; ++ ++ case 'i': ++ if (code != REG) ++ fputs ("i", file); ++ break; ++ ++ case 'L': ++ loongarch_print_operand_reloc (file, op, false /* hi64_part*/, ++ false /* lo_reloc */); ++ break; ++ + case 'm': + if (CONST_INT_P (op)) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1); +@@ -4413,17 +5105,27 @@ loongarch_print_operand (FILE *file, rtx op, int letter) + 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); ++ case 'N': ++ loongarch_print_int_branch_condition (file, reverse_condition (code), ++ letter); ++ break; ++ ++ case 'r': ++ loongarch_print_operand_reloc (file, op, false /* hi64_part */, ++ true /* lo_reloc */); ++ break; ++ ++ case 'R': ++ loongarch_print_operand_reloc (file, op, true /* hi64_part */, ++ false /* lo_reloc */); ++ 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 'V': +@@ -4441,30 +5143,36 @@ loongarch_print_operand (FILE *file, rtx op, int letter) + 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); ++ case 'W': ++ loongarch_print_float_branch_condition (file, reverse_condition (code), ++ letter); + break; + +- case 'F': +- loongarch_print_float_branch_condition (file, code, letter); ++ 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 'W': +- loongarch_print_float_branch_condition (file, reverse_condition (code), +- 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 'T': +- case 't': +- { +- int truth = (code == NE) == (letter == 'T'); +- fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); +- } ++ 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 'Y': +@@ -4481,21 +5189,6 @@ loongarch_print_operand (FILE *file, rtx op, int letter) + fputc (',', file); + break; + +- case 'A': +- if (loongarch_memmodel_needs_rel_acq_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) + { +@@ -4555,6 +5248,12 @@ loongarch_print_operand_address (FILE *file, machine_mode /* mode */, rtx x) + reg_names[REGNO (addr.offset)]); + return; + ++ case ADDRESS_LO_SUM: ++ fprintf (file, "%s,", reg_names[REGNO (addr.reg)]); ++ loongarch_print_operand_reloc (file, addr.offset, false /* hi64_part */, ++ false /* hi_reloc */); ++ return; ++ + case ADDRESS_CONST_INT: + fprintf (file, "%s,", reg_names[GP_REG_FIRST]); + output_addr_const (file, x); +@@ -5522,16 +6221,61 @@ loongarch_option_override_internal (struct gcc_options *opts) + if (loongarch_branch_cost == 0) + loongarch_branch_cost = loongarch_cost->branch_cost; + ++ /* Set up parameters to be used in prefetching algorithm. */ ++ int simultaneous_prefetches ++ = loongarch_cpu_cache[LARCH_ACTUAL_TUNE].simultaneous_prefetches; ++ ++ SET_OPTION_IF_UNSET (opts, &global_options_set, ++ param_simultaneous_prefetches, ++ simultaneous_prefetches); ++ ++ SET_OPTION_IF_UNSET (opts, &global_options_set, ++ param_l1_cache_line_size, ++ loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l1d_line_size); ++ ++ SET_OPTION_IF_UNSET (opts, &global_options_set, ++ param_l1_cache_size, ++ loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l1d_size); ++ ++ SET_OPTION_IF_UNSET (opts, &global_options_set, ++ param_l2_cache_size, ++ loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l2d_size); ++ ++ ++ /* Enable sw prefetching at -O3 and higher. */ ++ if (opts->x_flag_prefetch_loop_arrays < 0 ++ && (opts->x_optimize >= 3 || opts->x_flag_profile_use) ++ && !opts->x_optimize_size) ++ opts->x_flag_prefetch_loop_arrays = 1; ++ ++ if (opts->x_flag_align_functions && !opts->x_str_align_functions) ++ opts->x_str_align_functions = loongarch_cpu_align[LARCH_ACTUAL_TUNE].function; ++ ++ if (opts->x_flag_align_labels && !opts->x_str_align_labels) ++ opts->x_str_align_labels = loongarch_cpu_align[LARCH_ACTUAL_TUNE].label; ++ ++ if (TARGET_DIRECT_EXTERN_ACCESS && flag_shlib) ++ error ("%qs cannot be used for compiling a shared library", ++ "-mdirect-extern-access"); + + switch (la_target.cmodel) + { +- case CMODEL_TINY_STATIC: + case CMODEL_EXTREME: ++ if (!TARGET_EXPLICIT_RELOCS) ++ error ("code model %qs needs %s", ++ "extreme", "-mexplicit-relocs"); ++ + if (opts->x_flag_plt) +- error ("code model %qs and %qs not support %s mode", +- "tiny-static", "extreme", "plt"); ++ { ++ if (global_options_set.x_flag_plt) ++ error ("code model %qs is not compatible with %s", ++ "extreme", "-fplt"); ++ opts->x_flag_plt = 0; ++ } + break; + ++ case CMODEL_TINY_STATIC: ++ case CMODEL_MEDIUM: + case CMODEL_NORMAL: + case CMODEL_TINY: + case CMODEL_LARGE: +@@ -5541,6 +6285,15 @@ loongarch_option_override_internal (struct gcc_options *opts) + gcc_unreachable (); + } + ++ /* Validate the guard size. */ ++ int guard_size = param_stack_clash_protection_guard_size; ++ ++ /* Enforce that interval is the same size as size so the mid-end does the ++ right thing. */ ++ SET_OPTION_IF_UNSET (opts, &global_options_set, ++ param_stack_clash_protection_probe_interval, ++ guard_size); ++ + loongarch_init_print_operand_punct (); + + /* Set up array to map GCC register number to debug register number. +@@ -5782,6 +6535,259 @@ loongarch_starting_frame_offset (void) + return crtl->outgoing_args_size; + } + ++static tree ++loongarch_handle_model_attribute (tree *node, tree name, tree arg, int, ++ bool *no_add_attrs) ++{ ++ tree decl = *node; ++ if (TREE_CODE (decl) == VAR_DECL) ++ { ++ if (DECL_THREAD_LOCAL_P (decl)) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "%qE attribute cannot be specified for thread-local " ++ "variables", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ if (DECL_CONTEXT (decl) ++ && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL ++ && !TREE_STATIC (decl)) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "%qE attribute cannot be specified for local " ++ "variables", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ if (DECL_REGISTER (decl)) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "%qE attribute cannot be specified for register " ++ "variables", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ if (!TARGET_EXPLICIT_RELOCS) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "%qE attribute requires %s", name, "-mexplicit-relocs"); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ ++ arg = TREE_VALUE (arg); ++ if (TREE_CODE (arg) != STRING_CST) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "invalid argument of %qE attribute", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ ++ const char *model = TREE_STRING_POINTER (arg); ++ if (strcmp (model, "normal") != 0 ++ && strcmp (model, "extreme") != 0) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "invalid argument of %qE attribute", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ ++ if (lookup_attribute ("model", DECL_ATTRIBUTES (decl))) ++ { ++ error_at (DECL_SOURCE_LOCATION (decl), ++ "multiple %qE attribute", name); ++ *no_add_attrs = true; ++ return NULL_TREE; ++ } ++ } ++ else ++ { ++ warning (OPT_Wattributes, "%qE attribute ignored", name); ++ *no_add_attrs = true; ++ } ++ return NULL_TREE; ++} ++ ++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 } */ ++ { "model", 1, 1, true, false, false, false, ++ loongarch_handle_model_attribute, NULL }, ++ /* The last attribute spec is set to be NULL. */ ++ {} ++}; ++ ++bool ++loongarch_use_anchors_for_symbol_p (const_rtx symbol) ++{ ++ tree decl = SYMBOL_REF_DECL (symbol); ++ ++ /* The section anchor optimization may break custom address model. */ ++ if (decl && lookup_attribute ("model", DECL_ATTRIBUTES (decl))) ++ return false; ++ ++ return default_use_anchors_for_symbol_p (symbol); ++} ++ ++/* Implement the TARGET_ASAN_SHADOW_OFFSET hook. */ ++ ++static unsigned HOST_WIDE_INT ++loongarch_asan_shadow_offset (void) ++{ ++ /* We only have libsanitizer support for LOONGARCH64 at present. ++ This value is taken from the file libsanitizer/asan/asan_mapping.h. */ ++ return TARGET_64BIT ? (HOST_WIDE_INT_1 << 46) : 0; ++} ++ ++static sbitmap ++loongarch_get_separate_components (void) ++{ ++ HOST_WIDE_INT offset; ++ sbitmap components = sbitmap_alloc (FIRST_PSEUDO_REGISTER); ++ bitmap_clear (components); ++ offset = cfun->machine->frame.gp_sp_offset; ++ ++ /* The stack should be aligned to 16-bytes boundary, so we can make the use ++ of ldptr instructions. */ ++ gcc_assert (offset % UNITS_PER_WORD == 0); ++ ++ for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) ++ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) ++ { ++ /* We can wrap general registers saved at [sp, sp + 32768) using the ++ ldptr/stptr instructions. For large offsets a pseudo register ++ might be needed which cannot be created during the shrink ++ wrapping pass. ++ ++ TODO: This may need a revise when we add LA32 as ldptr.w is not ++ guaranteed available by the manual. */ ++ if (offset < 32768) ++ bitmap_set_bit (components, regno); ++ ++ offset -= UNITS_PER_WORD; ++ } ++ ++ offset = cfun->machine->frame.fp_sp_offset; ++ for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) ++ if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) ++ { ++ /* We can only wrap FP registers with imm12 offsets. For large ++ offsets a pseudo register might be needed which cannot be ++ created during the shrink wrapping pass. */ ++ if (IMM12_OPERAND (offset)) ++ bitmap_set_bit (components, regno); ++ ++ offset -= UNITS_PER_FPREG; ++ } ++ ++ /* Don't mess with the hard frame pointer. */ ++ if (frame_pointer_needed) ++ bitmap_clear_bit (components, HARD_FRAME_POINTER_REGNUM); ++ ++ bitmap_clear_bit (components, RETURN_ADDR_REGNUM); ++ ++ return components; ++} ++ ++static sbitmap ++loongarch_components_for_bb (basic_block bb) ++{ ++ /* Registers are used in a bb if they are in the IN, GEN, or KILL sets. */ ++ auto_bitmap used; ++ bitmap_copy (used, DF_LIVE_IN (bb)); ++ bitmap_ior_into (used, &DF_LIVE_BB_INFO (bb)->gen); ++ bitmap_ior_into (used, &DF_LIVE_BB_INFO (bb)->kill); ++ ++ sbitmap components = sbitmap_alloc (FIRST_PSEUDO_REGISTER); ++ bitmap_clear (components); ++ ++ function_abi_aggregator callee_abis; ++ rtx_insn *insn; ++ FOR_BB_INSNS (bb, insn) ++ if (CALL_P (insn)) ++ callee_abis.note_callee_abi (insn_callee_abi (insn)); ++ ++ HARD_REG_SET extra_caller_saves = ++ callee_abis.caller_save_regs (*crtl->abi); ++ ++ for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) ++ if (!fixed_regs[regno] ++ && !crtl->abi->clobbers_full_reg_p (regno) ++ && (TEST_HARD_REG_BIT (extra_caller_saves, regno) || ++ bitmap_bit_p (used, regno))) ++ bitmap_set_bit (components, regno); ++ ++ for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) ++ if (!fixed_regs[regno] ++ && !crtl->abi->clobbers_full_reg_p (regno) ++ && (TEST_HARD_REG_BIT (extra_caller_saves, regno) || ++ bitmap_bit_p (used, regno))) ++ bitmap_set_bit (components, regno); ++ ++ return components; ++} ++ ++static void ++loongarch_disqualify_components (sbitmap, edge, sbitmap, bool) ++{ ++ /* Do nothing. */ ++} ++ ++static void ++loongarch_process_components (sbitmap components, loongarch_save_restore_fn fn) ++{ ++ HOST_WIDE_INT offset = cfun->machine->frame.gp_sp_offset; ++ ++ for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) ++ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) ++ { ++ if (bitmap_bit_p (components, regno)) ++ loongarch_save_restore_reg (word_mode, regno, offset, fn); ++ ++ offset -= UNITS_PER_WORD; ++ } ++ ++ offset = cfun->machine->frame.fp_sp_offset; ++ machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode; ++ ++ for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) ++ if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) ++ { ++ if (bitmap_bit_p (components, regno)) ++ loongarch_save_restore_reg (mode, regno, offset, fn); ++ ++ offset -= UNITS_PER_FPREG; ++ } ++} ++ ++static void ++loongarch_emit_prologue_components (sbitmap components) ++{ ++ loongarch_process_components (components, loongarch_save_reg); ++} ++ ++static void ++loongarch_emit_epilogue_components (sbitmap components) ++{ ++ loongarch_process_components (components, loongarch_restore_reg); ++} ++ ++static void ++loongarch_set_handled_components (sbitmap components) ++{ ++ for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) ++ if (bitmap_bit_p (components, regno)) ++ cfun->machine->reg_is_wrapped_separately[regno] = true; ++ ++ for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) ++ if (bitmap_bit_p (components, regno)) ++ cfun->machine->reg_is_wrapped_separately[regno] = true; ++} ++ + /* Initialize the GCC target structure. */ + #undef TARGET_ASM_ALIGNED_HI_OP + #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" +@@ -5929,6 +6935,12 @@ loongarch_starting_frame_offset (void) + #undef TARGET_TRAMPOLINE_INIT + #define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init + ++#undef TARGET_MIN_ANCHOR_OFFSET ++#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2) ++ ++#undef TARGET_MAX_ANCHOR_OFFSET ++#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1) ++ + #undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV + #define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv + +@@ -5964,6 +6976,38 @@ loongarch_starting_frame_offset (void) + #undef TARGET_HAVE_SPECULATION_SAFE_VALUE + #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed + ++#undef TARGET_ATTRIBUTE_TABLE ++#define TARGET_ATTRIBUTE_TABLE loongarch_attribute_table ++ ++#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P ++#define TARGET_USE_ANCHORS_FOR_SYMBOL_P loongarch_use_anchors_for_symbol_p ++ ++#undef TARGET_ASAN_SHADOW_OFFSET ++#define TARGET_ASAN_SHADOW_OFFSET loongarch_asan_shadow_offset ++ ++#undef TARGET_SHRINK_WRAP_GET_SEPARATE_COMPONENTS ++#define TARGET_SHRINK_WRAP_GET_SEPARATE_COMPONENTS \ ++ loongarch_get_separate_components ++ ++#undef TARGET_SHRINK_WRAP_COMPONENTS_FOR_BB ++#define TARGET_SHRINK_WRAP_COMPONENTS_FOR_BB loongarch_components_for_bb ++ ++#undef TARGET_SHRINK_WRAP_DISQUALIFY_COMPONENTS ++#define TARGET_SHRINK_WRAP_DISQUALIFY_COMPONENTS \ ++ loongarch_disqualify_components ++ ++#undef TARGET_SHRINK_WRAP_EMIT_PROLOGUE_COMPONENTS ++#define TARGET_SHRINK_WRAP_EMIT_PROLOGUE_COMPONENTS \ ++ loongarch_emit_prologue_components ++ ++#undef TARGET_SHRINK_WRAP_EMIT_EPILOGUE_COMPONENTS ++#define TARGET_SHRINK_WRAP_EMIT_EPILOGUE_COMPONENTS \ ++ loongarch_emit_epilogue_components ++ ++#undef TARGET_SHRINK_WRAP_SET_HANDLED_COMPONENTS ++#define TARGET_SHRINK_WRAP_SET_HANDLED_COMPONENTS \ ++ loongarch_set_handled_components ++ + struct gcc_target targetm = TARGET_INITIALIZER; + + #include "gt-loongarch.h" +diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h +index 015d70c46..bcecdc2ee 100644 +--- a/gcc/config/loongarch/loongarch.h ++++ b/gcc/config/loongarch/loongarch.h +@@ -612,12 +612,31 @@ enum reg_class + + #define CONST_LOW_PART(VALUE) ((VALUE) - CONST_HIGH_PART (VALUE)) + ++/* True if VALUE can be added onto a register with one addu16i.d ++ instruction. */ ++ ++#define ADDU16I_OPERAND(VALUE) \ ++ (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \ ++ && IMM16_OPERAND ((HOST_WIDE_INT) (VALUE) / 65536))) ++ ++/* True if VALUE can be added onto a register with two addi.{d/w} ++ instructions, but not one addi.{d/w} instruction. */ ++#define DUAL_IMM12_OPERAND(VALUE) \ ++ (IN_RANGE ((VALUE), -4096, 4094) && !IMM12_OPERAND (VALUE)) ++ ++/* True if VALUE can be added onto a register with two addu16i.d ++ instruction, but not one addu16i.d instruction. */ ++#define DUAL_ADDU16I_OPERAND(VALUE) \ ++ (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \ ++ && !ADDU16I_OPERAND (VALUE) \ ++ && IN_RANGE ((VALUE) / 65536, -0x10000, 0xfffe))) ++ + #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_12BIT_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) +@@ -668,11 +687,15 @@ enum reg_class + + #define STACK_BOUNDARY (TARGET_ABI_LP64 ? 128 : 64) + ++/* This value controls how many pages we manually unroll the loop for when ++ generating stack clash probes. */ ++#define STACK_CLASH_MAX_UNROLL_PAGES 4 ++ + /* 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 FP_RETURN ((TARGET_SOFT_FLOAT_ABI) ? GP_RETURN : (FP_REG_FIRST + 0)) + + #define MAX_ARGS_IN_REGISTERS 8 + +@@ -832,7 +855,6 @@ typedef struct { + 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 + + /* Return the asm template for a conditional branch instruction. + OPCODE is the opcode's mnemonic and OPERANDS is the asm template for +@@ -1040,21 +1062,21 @@ typedef struct { + + /* 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) ++#define LARCH_MAX_MOVE_OPS_PER_LOOP_ITER 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) ++#define LARCH_MAX_MOVE_OPS_STRAIGHT (LARCH_MAX_MOVE_OPS_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 ++#define LARCH_CALL_RATIO 6 + + /* Any loop-based implementation of cpymemsi will have at least +- LARCH_MAX_MOVE_BYTES_STRAIGHT / UNITS_PER_WORD memory-to-memory ++ LARCH_MAX_MOVE_OPS_PER_LOOP_ITER memory-to-memory + moves, so allow individual copies of fewer elements. + + When cpymemsi is not available, use a value approximating +@@ -1065,9 +1087,7 @@ typedef struct { + value of LARCH_CALL_RATIO to take that into account. */ + + #define MOVE_RATIO(speed) \ +- (HAVE_cpymemsi \ +- ? LARCH_MAX_MOVE_BYTES_PER_LOOP_ITER / UNITS_PER_WORD \ +- : CLEAR_RATIO (speed) / 2) ++ (HAVE_cpymemsi ? LARCH_MAX_MOVE_OPS_PER_LOOP_ITER : CLEAR_RATIO (speed) / 2) + + /* For CLEAR_RATIO, when optimizing for size, give a better estimate + of the length of a memset call, but use the default otherwise. */ +@@ -1127,6 +1147,8 @@ struct GTY (()) machine_function + /* The current frame information, calculated by loongarch_compute_frame_info. + */ + struct loongarch_frame_info frame; ++ ++ bool reg_is_wrapped_separately[FIRST_PSEUDO_REGISTER]; + }; + #endif + +@@ -1145,6 +1167,6 @@ struct GTY (()) machine_function + /* 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) ++ (TARGET_HARD_FLOAT_ABI ? (TARGET_DOUBLE_FLOAT_ABI ? 8 : 4) : 0) + + #define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN || (N) == FP_RETURN) +diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md +index 8f8412fba..c79951c1d 100644 +--- a/gcc/config/loongarch/loongarch.md ++++ b/gcc/config/loongarch/loongarch.md +@@ -35,13 +35,19 @@ + ;; Floating point unspecs. + UNSPEC_FRINT + UNSPEC_FCLASS ++ UNSPEC_FMAX ++ UNSPEC_FMIN ++ UNSPEC_FCOPYSIGN ++ UNSPEC_FTINT ++ UNSPEC_FTINTRM ++ UNSPEC_FTINTRP ++ UNSPEC_FSCALEB ++ UNSPEC_FLOGB + + ;; Override return address for exception handling. + UNSPEC_EH_RETURN + + ;; Bit operation +- UNSPEC_BYTEPICK_W +- UNSPEC_BYTEPICK_D + UNSPEC_BITREV_4B + UNSPEC_BITREV_8B + +@@ -57,6 +63,17 @@ + ;; CRC + UNSPEC_CRC + UNSPEC_CRCC ++ ++ UNSPEC_LOAD_FROM_GOT ++ UNSPEC_PCALAU12I ++ UNSPEC_ORI_L_LO12 ++ UNSPEC_LUI_L_HI20 ++ UNSPEC_LUI_H_LO20 ++ UNSPEC_LUI_H_HI12 ++ UNSPEC_TLS_LOW ++ ++ UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1 ++ UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1 + ]) + + (define_c_enum "unspecv" [ +@@ -110,6 +127,8 @@ + ;; + ;; .................... + ++(define_attr "enabled" "no,yes" (const_string "yes")) ++ + (define_attr "got" "unset,load" + (const_string "unset")) + +@@ -197,9 +216,12 @@ + ;; fdiv floating point divide + ;; frdiv floating point reciprocal divide + ;; fabs floating point absolute value ++;; flogb floating point exponent extract + ;; fneg floating point negation + ;; fcmp floating point compare ++;; fcopysign floating point copysign + ;; fcvt floating point convert ++;; fscaleb floating point scale + ;; fsqrt floating point square root + ;; frsqrt floating point reciprocal square root + ;; multi multiword sequence (or user asm statements) +@@ -211,8 +233,8 @@ + "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" ++ fmove,fadd,fmul,fmadd,fdiv,frdiv,fabs,flogb,fneg,fcmp,fcopysign,fcvt, ++ fscaleb,fsqrt,frsqrt,accext,accmod,multi,atomic,syncloop,nop,ghost" + (cond [(eq_attr "jirl" "!unset") (const_string "call") + (eq_attr "got" "load") (const_string "load") + +@@ -357,6 +379,11 @@ + (define_mode_iterator ANYF [(SF "TARGET_HARD_FLOAT") + (DF "TARGET_DOUBLE_FLOAT")]) + ++;; Iterator for fixed-point modes which can be hold by a hardware ++;; floating-point register. ++(define_mode_iterator ANYFI [(SI "TARGET_HARD_FLOAT") ++ (DI "TARGET_DOUBLE_FLOAT")]) ++ + ;; A mode for which moves involving FPRs may need to be split. + (define_mode_iterator SPLITF + [(DF "!TARGET_64BIT && TARGET_DOUBLE_FLOAT") +@@ -393,6 +420,10 @@ + ;; the controlling mode. + (define_mode_attr HALFMODE [(DF "SI") (DI "SI") (TF "DI")]) + ++;; This attribute gives the integer mode that has the same size of a ++;; floating-point mode. ++(define_mode_attr IMODE [(SF "SI") (DF "DI")]) ++ + ;; This code iterator allows signed and unsigned widening multiplications + ;; to use the same template. + (define_code_iterator any_extend [sign_extend zero_extend]) +@@ -498,6 +529,40 @@ + (define_code_attr sel [(eq "masknez") (ne "maskeqz")]) + (define_code_attr selinv [(eq "maskeqz") (ne "masknez")]) + ++;; Iterator and attributes for floating-point to fixed-point conversion ++;; instructions. ++(define_int_iterator LRINT [UNSPEC_FTINT UNSPEC_FTINTRM UNSPEC_FTINTRP]) ++(define_int_attr lrint_pattern [(UNSPEC_FTINT "lrint") ++ (UNSPEC_FTINTRM "lfloor") ++ (UNSPEC_FTINTRP "lceil")]) ++(define_int_attr lrint_submenmonic [(UNSPEC_FTINT "") ++ (UNSPEC_FTINTRM "rm") ++ (UNSPEC_FTINTRP "rp")]) ++(define_int_attr lrint_allow_inexact [(UNSPEC_FTINT "1") ++ (UNSPEC_FTINTRM "0") ++ (UNSPEC_FTINTRP "0")]) ++ ++;; Iterator and attributes for bytepick.d ++(define_int_iterator bytepick_w_ashift_amount [8 16 24]) ++(define_int_attr bytepick_w_lshiftrt_amount [(8 "24") ++ (16 "16") ++ (24 "8")]) ++(define_int_iterator bytepick_d_ashift_amount [8 16 24 32 40 48 56]) ++(define_int_attr bytepick_d_lshiftrt_amount [(8 "56") ++ (16 "48") ++ (24 "40") ++ (32 "32") ++ (40 "24") ++ (48 "16") ++ (56 "8")]) ++(define_int_attr bytepick_imm [(8 "1") ++ (16 "2") ++ (24 "3") ++ (32 "4") ++ (40 "5") ++ (48 "6") ++ (56 "7")]) ++ + ;; + ;; .................... + ;; +@@ -533,24 +598,64 @@ + [(set_attr "type" "fadd") + (set_attr "mode" "")]) + +-(define_insn "add3" +- [(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,I")))] ++(define_insn_and_split "add3" ++ [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r,r") ++ (plus:GPR (match_operand:GPR 1 "register_operand" "r,r,r,r,r,r,r") ++ (match_operand:GPR 2 "plus__operand" ++ "r,I,La,Lb,Lc,Ld,Le")))] + "" +- "add%i2.\t%0,%1,%2"; ++ "@ ++ add.\t%0,%1,%2 ++ addi.\t%0,%1,%2 ++ # ++ * operands[2] = GEN_INT (INTVAL (operands[2]) / 65536); \ ++ return \"addu16i.d\t%0,%1,%2\"; ++ # ++ # ++ #" ++ "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2]) \ ++ && !ADDU16I_OPERAND (INTVAL (operands[2]))" ++ [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3))) ++ (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))] ++ { ++ loongarch_split_plus_constant (&operands[2], mode); ++ } + [(set_attr "alu_type" "add") +- (set_attr "mode" "")]) +- +-(define_insn "*addsi3_extended" +- [(set (match_operand:DI 0 "register_operand" "=r,r") ++ (set_attr "mode" "") ++ (set_attr "insn_count" "1,1,2,1,2,2,2") ++ (set (attr "enabled") ++ (cond ++ [(match_test "mode != DImode && which_alternative == 4") ++ (const_string "no") ++ (match_test "mode != DImode && which_alternative == 5") ++ (const_string "no") ++ (match_test "mode != SImode && which_alternative == 6") ++ (const_string "no")] ++ (const_string "yes")))]) ++ ++(define_insn_and_split "*addsi3_extended" ++ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r") + (sign_extend:DI +- (plus:SI (match_operand:SI 1 "register_operand" "r,r") +- (match_operand:SI 2 "arith_operand" "r,I"))))] ++ (plus:SI (match_operand:SI 1 "register_operand" "r,r,r,r") ++ (match_operand:SI 2 "plus_si_extend_operand" ++ "r,I,La,Le"))))] + "TARGET_64BIT" +- "add%i2.w\t%0,%1,%2" ++ "@ ++ add.w\t%0,%1,%2 ++ addi.w\t%0,%1,%2 ++ # ++ #" ++ "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2])" ++ [(set (subreg:SI (match_dup 0) 0) (plus:SI (match_dup 1) (match_dup 3))) ++ (set (match_dup 0) ++ (sign_extend:DI (plus:SI (subreg:SI (match_dup 0) 0) ++ (match_dup 4))))] ++ { ++ loongarch_split_plus_constant (&operands[2], SImode); ++ } + [(set_attr "alu_type" "add") +- (set_attr "mode" "SI")]) ++ (set_attr "mode" "SI") ++ (set_attr "insn_count" "1,1,2,2")]) + + + ;; +@@ -750,6 +855,7 @@ + { + rtx reg1 = gen_reg_rtx (DImode); + rtx reg2 = gen_reg_rtx (DImode); ++ rtx rd = gen_reg_rtx (DImode); + + operands[1] = gen_rtx_SIGN_EXTEND (word_mode, operands[1]); + operands[2] = gen_rtx_SIGN_EXTEND (word_mode, operands[2]); +@@ -757,32 +863,45 @@ + emit_insn (gen_rtx_SET (reg1, operands[1])); + emit_insn (gen_rtx_SET (reg2, operands[2])); + +- emit_insn (gen_di3_fake (operands[0], reg1, reg2)); ++ emit_insn (gen_di3_fake (rd, reg1, reg2)); ++ emit_insn (gen_rtx_SET (operands[0], ++ simplify_gen_subreg (SImode, rd, DImode, 0))); + DONE; + } + }) + + (define_insn "*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")))] ++ [(set (match_operand:GPR 0 "register_operand" "=r,&r,&r") ++ (any_div:GPR (match_operand:GPR 1 "register_operand" "r,r,0") ++ (match_operand:GPR 2 "register_operand" "r,r,r")))] + "" + { + return loongarch_output_division (".\t%0,%1,%2", operands); + } + [(set_attr "type" "idiv") +- (set_attr "mode" "")]) ++ (set_attr "mode" "") ++ (set (attr "enabled") ++ (if_then_else ++ (match_test "!!which_alternative == loongarch_check_zero_div_p()") ++ (const_string "yes") ++ (const_string "no")))]) + + (define_insn "di3_fake" +- [(set (match_operand:SI 0 "register_operand" "=&r") +- (any_div:SI (match_operand:DI 1 "register_operand" "r") +- (match_operand:DI 2 "register_operand" "r")))] ++ [(set (match_operand:DI 0 "register_operand" "=r,&r,&r") ++ (sign_extend:DI ++ (any_div:SI (match_operand:DI 1 "register_operand" "r,r,0") ++ (match_operand:DI 2 "register_operand" "r,r,r"))))] + "" + { + return loongarch_output_division (".w\t%0,%1,%2", operands); + } + [(set_attr "type" "idiv") +- (set_attr "mode" "SI")]) ++ (set_attr "mode" "SI") ++ (set (attr "enabled") ++ (if_then_else ++ (match_test "!!which_alternative == loongarch_check_zero_div_p()") ++ (const_string "yes") ++ (const_string "no")))]) + + ;; Floating point multiply accumulate instructions. + +@@ -947,6 +1066,69 @@ + (set_attr "mode" "")]) + + ;; ++;; .................... ++;; ++;; FLOATING POINT COPYSIGN ++;; ++;; .................... ++ ++(define_insn "copysign3" ++ [(set (match_operand:ANYF 0 "register_operand" "=f") ++ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f") ++ (match_operand:ANYF 2 "register_operand" "f")] ++ UNSPEC_FCOPYSIGN))] ++ "TARGET_HARD_FLOAT" ++ "fcopysign.\t%0,%1,%2" ++ [(set_attr "type" "fcopysign") ++ (set_attr "mode" "")]) ++ ++;; ++;; .................... ++;; ++;; FLOATING POINT SCALE ++;; ++;; .................... ++ ++(define_insn "ldexp3" ++ [(set (match_operand:ANYF 0 "register_operand" "=f") ++ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f") ++ (match_operand: 2 "register_operand" "f")] ++ UNSPEC_FSCALEB))] ++ "TARGET_HARD_FLOAT" ++ "fscaleb.\t%0,%1,%2" ++ [(set_attr "type" "fscaleb") ++ (set_attr "mode" "")]) ++ ++;; ++;; .................... ++;; ++;; FLOATING POINT EXPONENT EXTRACT ++;; ++;; .................... ++ ++(define_insn "logb_non_negative2" ++ [(set (match_operand:ANYF 0 "register_operand" "=f") ++ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")] ++ UNSPEC_FLOGB))] ++ "TARGET_HARD_FLOAT" ++ "flogb.\t%0,%1" ++ [(set_attr "type" "flogb") ++ (set_attr "mode" "")]) ++ ++(define_expand "logb2" ++ [(set (match_operand:ANYF 0 "register_operand") ++ (unspec:ANYF [(abs:ANYF (match_operand:ANYF 1 "register_operand"))] ++ UNSPEC_FLOGB))] ++ "TARGET_HARD_FLOAT" ++{ ++ rtx tmp = gen_reg_rtx (mode); ++ ++ emit_insn (gen_abs2 (tmp, operands[1])); ++ emit_insn (gen_logb_non_negative2 (operands[0], tmp)); ++ DONE; ++}) ++ ++;; + ;; ................... + ;; + ;; Count leading zeroes. +@@ -1003,6 +1185,26 @@ + [(set_attr "type" "fmove") + (set_attr "mode" "")]) + ++(define_insn "fmax3" ++ [(set (match_operand:ANYF 0 "register_operand" "=f") ++ (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" "f")) ++ (use (match_operand:ANYF 2 "register_operand" "f"))] ++ UNSPEC_FMAX))] ++ "" ++ "fmax.\t%0,%1,%2" ++ [(set_attr "type" "fmove") ++ (set_attr "mode" "")]) ++ ++(define_insn "fmin3" ++ [(set (match_operand:ANYF 0 "register_operand" "=f") ++ (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" "f")) ++ (use (match_operand:ANYF 2 "register_operand" "f"))] ++ UNSPEC_FMIN))] ++ "" ++ "fmin.\t%0,%1,%2" ++ [(set_attr "type" "fmove") ++ (set_attr "mode" "")]) ++ + (define_insn "smaxa3" + [(set (match_operand:ANYF 0 "register_operand" "=f") + (if_then_else:ANYF +@@ -1575,23 +1777,41 @@ + DONE; + }) + +-(define_insn "*movdi_32bit" ++(define_insn_and_split "*movdi_32bit" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m") + (match_operand:DI 1 "move_operand" "r,i,w,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]); } ++ "CONST_INT_P (operands[1]) && REG_P (operands[0]) && GP_REG_P (REGNO ++ (operands[0]))" ++ [(const_int 0)] ++ " ++{ ++ loongarch_move_integer (operands[0], operands[0], INTVAL (operands[1])); ++ DONE; ++} ++ " + [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore") + (set_attr "mode" "DI")]) + +-(define_insn "*movdi_64bit" ++(define_insn_and_split "*movdi_64bit" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m") + (match_operand:DI 1 "move_operand" "r,Yd,w,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]); } ++ "CONST_INT_P (operands[1]) && REG_P (operands[0]) && GP_REG_P (REGNO ++ (operands[0]))" ++ [(const_int 0)] ++ " ++{ ++ loongarch_move_integer (operands[0], operands[0], INTVAL (operands[1])); ++ DONE; ++} ++ " + [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore") + (set_attr "mode" "DI")]) + +@@ -1606,12 +1826,21 @@ + DONE; + }) + +-(define_insn "*movsi_internal" ++(define_insn_and_split "*movsi_internal" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m,*r,*z") + (match_operand:SI 1 "move_operand" "r,Yd,w,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]); } ++ "CONST_INT_P (operands[1]) && REG_P (operands[0]) && GP_REG_P (REGNO ++ (operands[0]))" ++ [(const_int 0)] ++ " ++{ ++ loongarch_move_integer (operands[0], operands[0], INTVAL (operands[1])); ++ DONE; ++} ++ " + [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore,mftg,mgtf") + (set_attr "mode" "SI")]) + +@@ -1631,12 +1860,21 @@ + DONE; + }) + +-(define_insn "*movhi_internal" ++(define_insn_and_split "*movhi_internal" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,r,m,r,k") + (match_operand:HI 1 "move_operand" "r,Yd,I,m,rJ,k,rJ"))] + "(register_operand (operands[0], HImode) + || reg_or_0_operand (operands[1], HImode))" + { return loongarch_output_move (operands[0], operands[1]); } ++ "CONST_INT_P (operands[1]) && REG_P (operands[0]) && GP_REG_P (REGNO ++ (operands[0]))" ++ [(const_int 0)] ++ " ++{ ++ loongarch_move_integer (operands[0], operands[0], INTVAL (operands[1])); ++ DONE; ++} ++ " + [(set_attr "move_type" "move,const,const,load,store,load,store") + (set_attr "mode" "HI")]) + +@@ -1727,73 +1965,6 @@ + [(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])" +- [(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])" +- [(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; +@@ -1922,8 +2093,96 @@ + [(set_attr "type" "arith") + (set_attr "mode" "DI")]) + +-;; Convert floating-point numbers to integers +-(define_insn "frint_" ++;; Instructions for adding the low 12 bits of an address to a register. ++;; Operand 2 is the address: loongarch_print_operand works out which relocation ++;; should be applied. ++ ++(define_insn "*low" ++ [(set (match_operand:P 0 "register_operand" "=r") ++ (lo_sum:P (match_operand:P 1 "register_operand" " r") ++ (match_operand:P 2 "symbolic_operand" "")))] ++ "TARGET_EXPLICIT_RELOCS" ++ "addi.\t%0,%1,%L2" ++ [(set_attr "type" "arith") ++ (set_attr "mode" "")]) ++ ++(define_insn "@tls_low" ++ [(set (match_operand:P 0 "register_operand" "=r") ++ (unspec:P [(mem:P (lo_sum:P (match_operand:P 1 "register_operand" "r") ++ (match_operand:P 2 "symbolic_operand" "")))] ++ UNSPEC_TLS_LOW))] ++ "TARGET_EXPLICIT_RELOCS" ++ "addi.\t%0,%1,%L2" ++ [(set_attr "type" "arith") ++ (set_attr "mode" "")]) ++ ++;; Instructions for loading address from GOT entry. ++;; operands[1] is pc plus the high half of the address difference with the got ++;; entry; ++;; operands[2] is low 12 bits for low 12 bit of the address difference with the ++;; got entry. ++;; loongarch_print_operand works out which relocation should be applied. ++ ++(define_insn "@ld_from_got" ++ [(set (match_operand:P 0 "register_operand" "=r") ++ (unspec:P [(mem:P (lo_sum:P ++ (match_operand:P 1 "register_operand" "r") ++ (match_operand:P 2 "symbolic_operand")))] ++ UNSPEC_LOAD_FROM_GOT))] ++ "TARGET_EXPLICIT_RELOCS" ++ "ld.\t%0,%1,%L2" ++ [(set_attr "type" "move")] ++) ++ ++(define_insn "@lui_l_hi20" ++ [(set (match_operand:P 0 "register_operand" "=r") ++ (unspec:P [(match_operand:P 1 "symbolic_operand")] ++ UNSPEC_LUI_L_HI20))] ++ "" ++ "lu12i.w\t%0,%r1" ++ [(set_attr "type" "move")] ++) ++ ++(define_insn "@pcalau12i" ++ [(set (match_operand:P 0 "register_operand" "=j") ++ (unspec:P [(match_operand:P 1 "symbolic_operand" "")] ++ UNSPEC_PCALAU12I))] ++ "" ++ "pcalau12i\t%0,%%pc_hi20(%1)" ++ [(set_attr "type" "move")]) ++ ++(define_insn "@ori_l_lo12" ++ [(set (match_operand:P 0 "register_operand" "=r") ++ (unspec:P [(match_operand:P 1 "register_operand" "r") ++ (match_operand:P 2 "symbolic_operand")] ++ UNSPEC_ORI_L_LO12))] ++ "" ++ "ori\t%0,%1,%L2" ++ [(set_attr "type" "move")] ++) ++ ++(define_insn "lui_h_lo20" ++ [(set (match_operand:DI 0 "register_operand" "=r") ++ (unspec:DI [(match_operand:DI 1 "register_operand" "0") ++ (match_operand:DI 2 "symbolic_operand")] ++ UNSPEC_LUI_H_LO20))] ++ "TARGET_64BIT" ++ "lu32i.d\t%0,%R2" ++ [(set_attr "type" "move")] ++) ++ ++(define_insn "lui_h_hi12" ++ [(set (match_operand:DI 0 "register_operand" "=r") ++ (unspec:DI [(match_operand:DI 1 "register_operand" "r") ++ (match_operand:DI 2 "symbolic_operand")] ++ UNSPEC_LUI_H_HI12))] ++ "TARGET_64BIT" ++ "lu52i.d\t%0,%1,%H2" ++ [(set_attr "type" "move")] ++) ++ ++;; Round floating-point numbers to integers ++(define_insn "rint2" + [(set (match_operand:ANYF 0 "register_operand" "=f") + (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")] + UNSPEC_FRINT))] +@@ -1932,6 +2191,19 @@ + [(set_attr "type" "fcvt") + (set_attr "mode" "")]) + ++;; Convert floating-point numbers to integers ++(define_insn "2" ++ [(set (match_operand:ANYFI 0 "register_operand" "=f") ++ (unspec:ANYFI [(match_operand:ANYF 1 "register_operand" "f")] ++ LRINT))] ++ "TARGET_HARD_FLOAT && ++ ( ++ || flag_fp_int_builtin_inexact ++ || !flag_trapping_math)" ++ "ftint.. %0,%1" ++ [(set_attr "type" "fcvt") ++ (set_attr "mode" "")]) ++ + ;; Load the low word of operand 0 with operand 1. + (define_insn "load_low" + [(set (match_operand:SPLITF 0 "register_operand" "=f,f") +@@ -2216,7 +2488,8 @@ + "" + { + if (TARGET_DO_OPTIMIZE_BLOCK_MOVE_P +- && loongarch_expand_block_move (operands[0], operands[1], operands[2])) ++ && loongarch_expand_block_move (operands[0], operands[1], ++ operands[2], operands[3])) + DONE; + else + FAIL; +@@ -2622,6 +2895,10 @@ + } + [(set_attr "type" "branch")]) + ++;; Micro-architecture unconditionally treats a "jr $ra" as "return from subroutine", ++;; non-returning indirect jumps through $ra would interfere with both subroutine ++;; return prediction and the more general indirect branch prediction. ++ + (define_expand "indirect_jump" + [(set (pc) (match_operand 0 "register_operand"))] + "" +@@ -2632,7 +2909,7 @@ + }) + + (define_insn "@indirect_jump" +- [(set (pc) (match_operand:P 0 "register_operand" "r"))] ++ [(set (pc) (match_operand:P 0 "register_operand" "e"))] + "" + "jr\t%0" + [(set_attr "type" "jump") +@@ -2655,7 +2932,7 @@ + + (define_insn "@tablejump" + [(set (pc) +- (match_operand:P 0 "register_operand" "r")) ++ (match_operand:P 0 "register_operand" "e")) + (use (label_ref (match_operand 1 "" "")))] + "" + "jr\t%0" +@@ -2823,53 +3100,32 @@ + { + rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0)); + +- emit_call_insn (gen_sibcall_internal (target, operands[1])); ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_sibcall_internal_1 (Pmode, XEXP (target, 0), ++ XEXP (target, 1), ++ operands[1])); ++ else ++ 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")) ++ [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) ++ "@ ++ jr\t%0 ++ b\t%0 ++ b\t%%plt(%0)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@sibcall_internal_1" ++ [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j") ++ (match_operand:P 1 "symbolic_operand" ""))) ++ (match_operand 2 "" ""))] ++ "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM" ++ "jirl\t$r0,%0,%%pc_lo12(%1)" ++ [(set_attr "jirl" "indirect")]) + + (define_expand "sibcall_value" + [(parallel [(set (match_operand 0 "") +@@ -2886,7 +3142,14 @@ + 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, ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_sibcall_value_multiple_internal_1 (Pmode, arg1, ++ XEXP (target, 0), ++ XEXP (target, 1), ++ operands[2], ++ arg2)); ++ else ++ emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target, + operands[2], + arg2)); + } +@@ -2896,7 +3159,13 @@ + 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, ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_sibcall_value_internal_1 (Pmode, operands[0], ++ XEXP (target, 0), ++ XEXP (target, 1), ++ operands[2])); ++ else ++ emit_call_insn (gen_sibcall_value_internal (operands[0], target, + operands[2])); + } + DONE; +@@ -2904,96 +3173,52 @@ + + (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")) ++ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) ++ "@ ++ jr\t%1 ++ b\t%1 ++ b\t%%plt(%1)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@sibcall_value_internal_1" ++ [(set (match_operand 0 "register_operand" "") ++ (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j") ++ (match_operand:P 2 "symbolic_operand" ""))) ++ (match_operand 3 "" "")))] ++ "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM" ++ "jirl\t$r0,%1,%%pc_lo12(%2)" ++ [(set_attr "jirl" "indirect")]) + + (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")) ++ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) ++ "@ ++ jr\t%1 ++ b\t%1 ++ b\t%%plt(%1)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@sibcall_value_multiple_internal_1" ++ [(set (match_operand 0 "register_operand" "") ++ (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j") ++ (match_operand:P 2 "symbolic_operand" "")] ++ UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1)) ++ (match_operand 3 "" ""))) ++ (set (match_operand 4 "register_operand" "") ++ (call (mem:P (unspec:P [(match_dup 1) ++ (match_dup 2)] ++ UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1)) ++ (match_dup 3)))] ++ "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM" ++ "jirl\t$r0,%1,%%pc_lo12(%2)" ++ [(set_attr "jirl" "indirect")]) + + (define_expand "call" + [(parallel [(call (match_operand 0 "") +@@ -3004,55 +3229,33 @@ + { + rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0)); + +- emit_call_insn (gen_call_internal (target, operands[1])); ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_call_internal_1 (Pmode, XEXP (target, 0), ++ XEXP (target, 1), operands[1])); ++ else ++ 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")) ++ [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct") +- (set_attr "insn_count" "1,2,3,3,2")]) ++ "@ ++ jirl\t$r1,%0,0 ++ bl\t%0 ++ bl\t%%plt(%0)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@call_internal_1" ++ [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j") ++ (match_operand:P 1 "symbolic_operand" ""))) ++ (match_operand 2 "" "")) ++ (clobber (reg:SI RETURN_ADDR_REGNUM))] ++ "TARGET_CMODEL_MEDIUM" ++ "jirl\t$r1,%0,%%pc_lo12(%1)" ++ [(set_attr "jirl" "indirect")]) + + (define_expand "call_value" + [(parallel [(set (match_operand 0 "") +@@ -3068,7 +3271,13 @@ + 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, ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_call_value_multiple_internal_1 (Pmode, arg1, ++ XEXP (target, 0), ++ XEXP (target, 1), ++ operands[2], arg2)); ++ else ++ emit_call_insn (gen_call_value_multiple_internal (arg1, target, + operands[2], arg2)); + } + else +@@ -3077,7 +3286,13 @@ + 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, ++ if (GET_CODE (target) == LO_SUM) ++ emit_call_insn (gen_call_value_internal_1 (Pmode, operands[0], ++ XEXP (target, 0), ++ XEXP (target, 1), ++ operands[2])); ++ else ++ emit_call_insn (gen_call_value_internal (operands[0], target, + operands[2])); + } + DONE; +@@ -3085,100 +3300,56 @@ + + (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")) ++ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct") +- (set_attr "insn_count" "1,2,3,3,2")]) ++ "@ ++ jirl\t$r1,%1,0 ++ bl\t%1 ++ bl\t%%plt(%1)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@call_value_internal_1" ++ [(set (match_operand 0 "register_operand" "") ++ (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j") ++ (match_operand:P 2 "symbolic_operand" ""))) ++ (match_operand 3 "" ""))) ++ (clobber (reg:SI RETURN_ADDR_REGNUM))] ++ "TARGET_CMODEL_MEDIUM" ++ "jirl\t$r1,%1,%%pc_lo12(%2)" ++ [(set_attr "jirl" "indirect")]) + + (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")) ++ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) + (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 +- /* Cmodel extreme and tiny static not support plt. */ +- gcc_unreachable (); +- default: +- gcc_unreachable (); +- } +-} +- [(set_attr "jirl" "indirect,direct,direct,direct,direct") +- (set_attr "insn_count" "1,2,3,3,2")]) ++ "@ ++ jirl\t$r1,%1,0 ++ bl\t%1 ++ bl\t%%plt(%1)" ++ [(set_attr "jirl" "indirect,direct,direct")]) ++ ++(define_insn "@call_value_multiple_internal_1" ++ [(set (match_operand 0 "register_operand" "") ++ (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j") ++ (match_operand:P 2 "symbolic_operand" "")] ++ UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1)) ++ (match_operand 3 "" ""))) ++ (set (match_operand 4 "register_operand" "") ++ (call (mem:P (unspec:P [(match_dup 1) ++ (match_dup 2)] ++ UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1)) ++ (match_dup 3))) ++ (clobber (reg:SI RETURN_ADDR_REGNUM))] ++ "TARGET_CMODEL_MEDIUM" ++ "jirl\t$r1,%1,%%pc_lo12(%2)" ++ [(set_attr "jirl" "indirect")]) + + + ;; Call subroutine returning any type. +@@ -3211,6 +3382,20 @@ + ;; .................... + ;; + ++(define_insn "prefetch" ++ [(prefetch (match_operand 0 "address_operand" "ZD") ++ (match_operand 1 "const_int_operand" "n") ++ (match_operand 2 "const_int_operand" "n"))] ++ "" ++{ ++ switch (INTVAL (operands[1])) ++ { ++ case 0: return "preld\t0,%a0"; ++ case 1: return "preld\t8,%a0"; ++ default: gcc_unreachable (); ++ } ++}) ++ + (define_insn "nop" + [(const_int 0)] + "" +@@ -3243,24 +3428,35 @@ + [(set_attr "type" "unknown") + (set_attr "mode" "")]) + +-(define_insn "bytepick_w" ++(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))] ++ (ior:SI (lshiftrt (match_operand:SI 1 "register_operand" "r") ++ (const_int )) ++ (ashift (match_operand:SI 2 "register_operand" "r") ++ (const_int bytepick_w_ashift_amount))))] + "" +- "bytepick.w\t%0,%1,%2,%z3" ++ "bytepick.w\t%0,%1,%2," + [(set_attr "mode" "SI")]) + +-(define_insn "bytepick_d" ++(define_insn "bytepick_w__extend" + [(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" ++ (sign_extend:DI ++ (ior:SI (lshiftrt (match_operand:SI 1 "register_operand" "r") ++ (const_int )) ++ (ashift (match_operand:SI 2 "register_operand" "r") ++ (const_int bytepick_w_ashift_amount)))))] ++ "TARGET_64BIT" ++ "bytepick.w\t%0,%1,%2," ++ [(set_attr "mode" "SI")]) ++ ++(define_insn "bytepick_d_" ++ [(set (match_operand:DI 0 "register_operand" "=r") ++ (ior:DI (lshiftrt (match_operand:DI 1 "register_operand" "r") ++ (const_int )) ++ (ashift (match_operand:DI 2 "register_operand" "r") ++ (const_int bytepick_d_ashift_amount))))] ++ "TARGET_64BIT" ++ "bytepick.d\t%0,%1,%2," + [(set_attr "mode" "DI")]) + + (define_insn "bitrev_4b" +diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt +index 3ff0d8604..e917c789e 100644 +--- a/gcc/config/loongarch/loongarch.opt ++++ b/gcc/config/loongarch/loongarch.opt +@@ -161,6 +161,10 @@ 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. + ++mexplicit-relocs ++Target Var(TARGET_EXPLICIT_RELOCS) Init(1) ++Use %reloc() assembly operators. ++ + ; The code model option names for -mcmodel. + Enum + Name(cmodel) Type(int) +@@ -175,6 +179,9 @@ Enum(cmodel) String(tiny) Value(CMODEL_TINY) + EnumValue + Enum(cmodel) String(tiny-static) Value(CMODEL_TINY_STATIC) + ++EnumValue ++Enum(cmodel) String(medium) Value(CMODEL_MEDIUM) ++ + EnumValue + Enum(cmodel) String(large) Value(CMODEL_LARGE) + +@@ -184,3 +191,7 @@ Enum(cmodel) String(extreme) Value(CMODEL_EXTREME) + mcmodel= + Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(CMODEL_NORMAL) + Specify the code model. ++ ++mdirect-extern-access ++Target Var(TARGET_DIRECT_EXTERN_ACCESS) Init(0) ++Avoid using the GOT to access external symbols. +diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md +index edd74d478..4966d5569 100644 +--- a/gcc/config/loongarch/predicates.md ++++ b/gcc/config/loongarch/predicates.md +@@ -39,14 +39,50 @@ + (and (match_code "const_int") + (match_test "IMM12_OPERAND (INTVAL (op))"))) + ++(define_predicate "const_dual_imm12_operand" ++ (and (match_code "const_int") ++ (match_test "DUAL_IMM12_OPERAND (INTVAL (op))"))) ++ + (define_predicate "const_imm16_operand" + (and (match_code "const_int") + (match_test "IMM16_OPERAND (INTVAL (op))"))) + ++(define_predicate "const_addu16i_operand" ++ (and (match_code "const_int") ++ (match_test "ADDU16I_OPERAND (INTVAL (op))"))) ++ ++(define_predicate "const_addu16i_imm12_di_operand" ++ (and (match_code "const_int") ++ (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), DImode)"))) ++ ++(define_predicate "const_addu16i_imm12_si_operand" ++ (and (match_code "const_int") ++ (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), SImode)"))) ++ ++(define_predicate "const_dual_addu16i_operand" ++ (and (match_code "const_int") ++ (match_test "DUAL_ADDU16I_OPERAND (INTVAL (op))"))) ++ + (define_predicate "arith_operand" + (ior (match_operand 0 "const_arith_operand") + (match_operand 0 "register_operand"))) + ++(define_predicate "plus_di_operand" ++ (ior (match_operand 0 "arith_operand") ++ (match_operand 0 "const_dual_imm12_operand") ++ (match_operand 0 "const_addu16i_operand") ++ (match_operand 0 "const_addu16i_imm12_di_operand") ++ (match_operand 0 "const_dual_addu16i_operand"))) ++ ++(define_predicate "plus_si_extend_operand" ++ (ior (match_operand 0 "arith_operand") ++ (match_operand 0 "const_dual_imm12_operand") ++ (match_operand 0 "const_addu16i_imm12_si_operand"))) ++ ++(define_predicate "plus_si_operand" ++ (ior (match_operand 0 "plus_si_extend_operand") ++ (match_operand 0 "const_addu16i_operand"))) ++ + (define_predicate "const_immalsl_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 1, 4)"))) +@@ -91,14 +127,6 @@ + (ior (match_operand 0 "const_1_operand") + (match_operand 0 "register_operand"))) + +-(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 "lu52i_mask_operand" + (and (match_code "const_int") + (match_test "UINTVAL (op) == 0xfffffffffffff"))) +@@ -110,20 +138,43 @@ + (define_predicate "const_call_insn_operand" + (match_code "const,symbol_ref,label_ref") + { ++ /* Split symbol to high and low if return false. ++ If defined TARGET_CMODEL_EXTREME, all symbol would be splited, ++ else if offset is not zero, the symbol would be splited. */ ++ + enum loongarch_symbol_type symbol_type; ++ loongarch_symbolic_constant_p (op, &symbol_type); + +- if (!loongarch_symbolic_constant_p (op, &symbol_type)) ++ rtx offset, x = op; ++ split_const (x, &x, &offset); ++ ++ if (offset != const0_rtx) + return false; + ++ /* When compiling with '-mcmodel=medium -mexplicit-relocs' ++ symbols are splited in loongarch_legitimize_call_address. ++ ++ When compiling with '-mcmodel=medium -mno-explicit-relocs', ++ first obtain the symbolic address or the address of the ++ plt entry, and then perform an indirect jump, so return false. */ ++ + switch (symbol_type) + { ++ case SYMBOL_PCREL: ++ if (TARGET_CMODEL_EXTREME ++ || (TARGET_CMODEL_MEDIUM && !TARGET_EXPLICIT_RELOCS)) ++ return false; ++ else ++ return 1; ++ + 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; ++ if (TARGET_CMODEL_EXTREME ++ || !flag_plt ++ || (flag_plt && TARGET_CMODEL_MEDIUM ++ && !TARGET_EXPLICIT_RELOCS)) ++ return false; ++ else ++ return 1; + + default: + return false; +@@ -140,22 +191,11 @@ + (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" ++(define_predicate "is_const_call_no_local_symbol" + (and (match_operand 0 "const_call_insn_operand") +- (match_test "flag_plt == 0") +- (match_test "loongarch_global_symbol_noweak_p (op) != 0") ++ (ior (match_test "loongarch_global_symbol_p (op) != 0") ++ (match_test "loongarch_symbol_binds_local_p (op) == 0") ++ (match_test "loongarch_weak_symbol_p (op) != 0")) + (match_test "CONSTANT_P (op)"))) + + ;; A legitimate CONST_INT operand that takes more than one instruction +@@ -214,12 +254,24 @@ + switch (GET_CODE (op)) + { + case CONST_INT: +- return !splittable_const_int_operand (op, mode); ++ return true; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: +- return (loongarch_symbolic_constant_p (op, &symbol_type)); ++ return (loongarch_symbolic_constant_p (op, &symbol_type) ++ && (!TARGET_EXPLICIT_RELOCS ++ || !loongarch_split_symbol_type (symbol_type))); ++ ++ case HIGH: ++ /* '-mno-explicit-relocs' don't generate high/low pairs. */ ++ if (!TARGET_EXPLICIT_RELOCS) ++ return false; ++ ++ op = XEXP (op, 0); ++ return (loongarch_symbolic_constant_p (op, &symbol_type) ++ && loongarch_split_symbol_type (symbol_type)); ++ + default: + return true; + } +diff --git a/gcc/config/loongarch/t-linux b/gcc/config/loongarch/t-linux +index 5b9796aea..136b27d0a 100644 +--- a/gcc/config/loongarch/t-linux ++++ b/gcc/config/loongarch/t-linux +@@ -40,7 +40,7 @@ ifeq ($(filter LA_DISABLE_MULTILIB,$(tm_defines)),) + + MULTILIB_OSDIRNAMES = \ + mabi.lp64d=../lib64$\ +- $(call if_multiarch,:loongarch64-linux-gnuf64) ++ $(call if_multiarch,:loongarch64-linux-gnu) + + MULTILIB_OSDIRNAMES += \ + mabi.lp64f=../lib64/f32$\ +diff --git a/gcc/configure b/gcc/configure +index 81ae6b528..6cec3f9aa 100755 +--- a/gcc/configure ++++ b/gcc/configure +@@ -26303,6 +26303,40 @@ if test $gcc_cv_as_alpha_explicit_relocs = yes; then + + $as_echo "#define HAVE_AS_EXPLICIT_RELOCS 1" >>confdefs.h + ++fi ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for eh_frame pcrel encoding support" >&5 ++$as_echo_n "checking assembler for eh_frame pcrel encoding support... " >&6; } ++if ${gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support=no ++ if test x$gcc_cv_as != x; then ++ $as_echo '.cfi_startproc ++ .cfi_personality 0x9b,a ++ .cfi_lsda 0x1b,b ++ .cfi_endproc' > conftest.s ++ if { ac_try='$gcc_cv_as $gcc_cv_as_flags -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 ++ gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support=yes ++ 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_eh_frame_pcrel_encoding_support" >&5 ++$as_echo "$gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support" >&6; } ++if test $gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support = yes; then ++ ++$as_echo "#define HAVE_AS_EH_FRAME_PCREL_ENCODING_SUPPORT 1" >>confdefs.h ++ + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for jsrdirect relocation support" >&5 +diff --git a/gcc/configure.ac b/gcc/configure.ac +index 90bf68b69..233bc8449 100644 +--- a/gcc/configure.ac ++++ b/gcc/configure.ac +@@ -4453,6 +4453,14 @@ AS_HELP_STRING([--disable-fix-cortex-a53-843419], + jsr $26, ($27), a !lituse_jsrdirect!1],, + [AC_DEFINE(HAVE_AS_JSRDIRECT_RELOCS, 1, + [Define if your assembler supports the lituse_jsrdirect relocation.])]) ++ gcc_GAS_CHECK_FEATURE([eh_frame pcrel encoding support], ++ gcc_cv_as_loongarch_eh_frame_pcrel_encoding_support,, ++ [.cfi_startproc ++ .cfi_personality 0x9b,a ++ .cfi_lsda 0x1b,b ++ .cfi_endproc],, ++ [AC_DEFINE(HAVE_AS_EH_FRAME_PCREL_ENCODING_SUPPORT, 1, ++ [Define if your assembler supports eh_frame pcrel encoding.])]) + ;; + + avr-*-*) +diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi +index 84e6f6694..cd6a364b4 100644 +--- a/gcc/doc/extend.texi ++++ b/gcc/doc/extend.texi +@@ -7277,6 +7277,7 @@ attributes. + * Blackfin Variable Attributes:: + * H8/300 Variable Attributes:: + * IA-64 Variable Attributes:: ++* LoongArch Variable Attributes:: + * M32R/D Variable Attributes:: + * MeP Variable Attributes:: + * Microsoft Windows Variable Attributes:: +@@ -8061,6 +8062,22 @@ defined by shared libraries. + + @end table + ++@node LoongArch Variable Attributes ++@subsection LoongArch Variable Attributes ++ ++One attribute is currently defined for the LoongArch. ++ ++@table @code ++@item model("@var{name}") ++@cindex @code{model} variable attribute, LoongArch ++Use this attribute on the LoongArch to use a different code model for ++addressing this variable, than the code model specified by the global ++@option{-mcmodel} option. This attribute is mostly useful if a ++@code{section} attribute and/or a linker script will locate this object ++specially. Currently the only supported values of @var{name} are ++@code{normal} and @code{extreme}. ++@end table ++ + @node M32R/D Variable Attributes + @subsection M32R/D Variable Attributes + +@@ -10397,8 +10414,10 @@ ensures that modifying @var{a} does not affect the address referenced by + is undefined if @var{a} is modified before using @var{b}. + + @code{asm} supports operand modifiers on operands (for example @samp{%k2} +-instead of simply @samp{%2}). Typically these qualifiers are hardware +-dependent. The list of supported modifiers for x86 is found at ++instead of simply @samp{%2}). @ref{GenericOperandmodifiers, ++Generic Operand modifiers} lists the modifiers that are available ++on all targets. Other modifiers are hardware dependent. ++For example, the list of supported modifiers for x86 is found at + @ref{x86Operandmodifiers,x86 Operand modifiers}. + + If the C code that follows the @code{asm} makes no use of any of the output +@@ -10666,8 +10685,10 @@ optimizers may discard the @code{asm} statement as unneeded + (see @ref{Volatile}). + + @code{asm} supports operand modifiers on operands (for example @samp{%k2} +-instead of simply @samp{%2}). Typically these qualifiers are hardware +-dependent. The list of supported modifiers for x86 is found at ++instead of simply @samp{%2}). @ref{GenericOperandmodifiers, ++Generic Operand modifiers} lists the modifiers that are available ++on all targets. Other modifiers are hardware dependent. ++For example, the list of supported modifiers for x86 is found at + @ref{x86Operandmodifiers,x86 Operand modifiers}. + + In this example using the fictitious @code{combine} instruction, the +@@ -11019,6 +11040,30 @@ lab: + @} + @end example + ++@anchor{GenericOperandmodifiers} ++@subsubsection Generic Operand Modifiers ++@noindent ++The following table shows the modifiers supported by all targets and their effects: ++ ++@multitable {Modifier} {Description} {Example} ++@headitem Modifier @tab Description @tab Example ++@item @code{c} ++@tab Require a constant operand and print the constant expression with no punctuation. ++@tab @code{%c0} ++@item @code{n} ++@tab Like @samp{%c} except that the value of the constant is negated before printing. ++@tab @code{%n0} ++@item @code{a} ++@tab Substitute a memory reference, with the actual operand treated as the address. ++This may be useful when outputting a ``load address'' instruction, because ++often the assembler syntax for such an instruction requires you to write the ++operand as if it were a memory reference. ++@tab @code{%a0} ++@item @code{l} ++@tab Print the label name with no punctuation. ++@tab @code{%l0} ++@end multitable ++ + @anchor{x86Operandmodifiers} + @subsubsection x86 Operand Modifiers + +@@ -11369,6 +11414,21 @@ constant. Used to select the specified bit position. + @item @code{x} @tab Equivialent to @code{X}, but only for pointers. + @end multitable + ++@anchor{loongarchOperandmodifiers} ++@subsubsection LoongArch Operand Modifiers ++ ++The list below describes the supported modifiers and their effects for LoongArch. ++ ++@multitable @columnfractions .10 .90 ++@headitem Modifier @tab Description ++@item @code{d} @tab Same as @code{c}. ++@item @code{i} @tab Print the character ''@code{i}'' if the operand is not a register. ++@item @code{m} @tab Same as @code{c}, but the printed value is @code{operand - 1}. ++@item @code{X} @tab Print a constant integer operand in hexadecimal. ++@item @code{z} @tab Print the operand in its unmodified form, followed by a comma. ++@end multitable ++ ++ + @lowersections + @include md.texi + @raisesections +@@ -14618,6 +14678,7 @@ instructions, but allow the compiler to schedule those calls. + * Blackfin Built-in Functions:: + * BPF Built-in Functions:: + * FR-V Built-in Functions:: ++* LoongArch Base Built-in Functions:: + * MIPS DSP Built-in Functions:: + * MIPS Paired-Single Support:: + * MIPS Loongson Built-in Functions:: +@@ -16068,6 +16129,134 @@ Use the @code{nldub} instruction to load the contents of address @var{x} + into the data cache. The instruction is issued in slot I1@. + @end table + ++@node LoongArch Base Built-in Functions ++@subsection LoongArch Base Built-in Functions ++ ++These built-in functions are available for LoongArch. ++ ++Data Type Description: ++@itemize ++@item @code{imm0_31}, a compile-time constant in range 0 to 31; ++@item @code{imm0_16383}, a compile-time constant in range 0 to 16383; ++@item @code{imm0_32767}, a compile-time constant in range 0 to 32767; ++@item @code{imm_n2048_2047}, a compile-time constant in range -2048 to 2047; ++@end itemize ++ ++The intrinsics provided are listed below: ++@smallexample ++ unsigned int __builtin_loongarch_movfcsr2gr (imm0_31) ++ void __builtin_loongarch_movgr2fcsr (imm0_31, unsigned int) ++ void __builtin_loongarch_cacop_d (imm0_31, unsigned long int, imm_n2048_2047) ++ unsigned int __builtin_loongarch_cpucfg (unsigned int) ++ void __builtin_loongarch_asrtle_d (long int, long int) ++ void __builtin_loongarch_asrtgt_d (long int, long int) ++ long int __builtin_loongarch_lddir_d (long int, imm0_31) ++ void __builtin_loongarch_ldpte_d (long int, imm0_31) ++ ++ int __builtin_loongarch_crc_w_b_w (char, int) ++ int __builtin_loongarch_crc_w_h_w (short, int) ++ int __builtin_loongarch_crc_w_w_w (int, int) ++ int __builtin_loongarch_crc_w_d_w (long int, int) ++ int __builtin_loongarch_crcc_w_b_w (char, int) ++ int __builtin_loongarch_crcc_w_h_w (short, int) ++ int __builtin_loongarch_crcc_w_w_w (int, int) ++ int __builtin_loongarch_crcc_w_d_w (long int, int) ++ ++ unsigned int __builtin_loongarch_csrrd_w (imm0_16383) ++ unsigned int __builtin_loongarch_csrwr_w (unsigned int, imm0_16383) ++ unsigned int __builtin_loongarch_csrxchg_w (unsigned int, unsigned int, imm0_16383) ++ unsigned long int __builtin_loongarch_csrrd_d (imm0_16383) ++ unsigned long int __builtin_loongarch_csrwr_d (unsigned long int, imm0_16383) ++ unsigned long int __builtin_loongarch_csrxchg_d (unsigned long int, unsigned long int, imm0_16383) ++ ++ unsigned char __builtin_loongarch_iocsrrd_b (unsigned int) ++ unsigned short __builtin_loongarch_iocsrrd_h (unsigned int) ++ unsigned int __builtin_loongarch_iocsrrd_w (unsigned int) ++ unsigned long int __builtin_loongarch_iocsrrd_d (unsigned int) ++ void __builtin_loongarch_iocsrwr_b (unsigned char, unsigned int) ++ void __builtin_loongarch_iocsrwr_h (unsigned short, unsigned int) ++ void __builtin_loongarch_iocsrwr_w (unsigned int, unsigned int) ++ void __builtin_loongarch_iocsrwr_d (unsigned long int, unsigned int) ++ ++ void __builtin_loongarch_dbar (imm0_32767) ++ void __builtin_loongarch_ibar (imm0_32767) ++ ++ void __builtin_loongarch_syscall (imm0_32767) ++ void __builtin_loongarch_break (imm0_32767) ++@end smallexample ++ ++@emph{Note:}Since the control register is divided into 32-bit and 64-bit, ++but the access instruction is not distinguished. So GCC renames the control ++instructions when implementing intrinsics. ++ ++Take the csrrd instruction as an example, built-in functions are implemented as follows: ++@smallexample ++ __builtin_loongarch_csrrd_w // When reading the 32-bit control register use. ++ __builtin_loongarch_csrrd_d // When reading the 64-bit control register use. ++@end smallexample ++ ++For the convenience of use, the built-in functions are encapsulated, ++the encapsulated functions and @code{__drdtime_t, __rdtime_t} are ++defined in the @code{larchintrin.h}. So if you call the following ++function you need to include @code{larchintrin.h}. ++ ++@smallexample ++ typedef struct drdtime@{ ++ unsigned long dvalue; ++ unsigned long dtimeid; ++ @} __drdtime_t; ++ ++ typedef struct rdtime@{ ++ unsigned int value; ++ unsigned int timeid; ++ @} __rdtime_t; ++@end smallexample ++ ++@smallexample ++ __drdtime_t __rdtime_d (void) ++ __rdtime_t __rdtimel_w (void) ++ __rdtime_t __rdtimeh_w (void) ++ unsigned int __movfcsr2gr (imm0_31) ++ void __movgr2fcsr (imm0_31, unsigned int) ++ void __cacop_d (imm0_31, unsigned long, imm_n2048_2047) ++ unsigned int __cpucfg (unsigned int) ++ void __asrtle_d (long int, long int) ++ void __asrtgt_d (long int, long int) ++ long int __lddir_d (long int, imm0_31) ++ void __ldpte_d (long int, imm0_31) ++ ++ int __crc_w_b_w (char, int) ++ int __crc_w_h_w (short, int) ++ int __crc_w_w_w (int, int) ++ int __crc_w_d_w (long int, int) ++ int __crcc_w_b_w (char, int) ++ int __crcc_w_h_w (short, int) ++ int __crcc_w_w_w (int, int) ++ int __crcc_w_d_w (long int, int) ++ ++ unsigned int __csrrd_w (imm0_16383) ++ unsigned int __csrwr_w (unsigned int, imm0_16383) ++ unsigned int __csrxchg_w (unsigned int, unsigned int, imm0_16383) ++ unsigned long __csrrd_d (imm0_16383) ++ unsigned long __csrwr_d (unsigned long, imm0_16383) ++ unsigned long __csrxchg_d (unsigned long, unsigned long, imm0_16383) ++ ++ unsigned char __iocsrrd_b (unsigned int) ++ unsigned short __iocsrrd_h (unsigned int) ++ unsigned int __iocsrrd_w (unsigned int) ++ unsigned long __iocsrrd_d (unsigned int) ++ void __iocsrwr_b (unsigned char, unsigned int) ++ void __iocsrwr_h (unsigned short, unsigned int) ++ void __iocsrwr_w (unsigned int, unsigned int) ++ void __iocsrwr_d (unsigned long, unsigned int) ++ ++ void __dbar (imm0_32767) ++ void __ibar (imm0_32767) ++ ++ void __syscall (imm0_32767) ++ void __break (imm0_32767) ++@end smallexample ++ + @node MIPS DSP Built-in Functions + @subsection MIPS DSP Built-in Functions + +diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi +index 0c177c9c5..a34838836 100644 +--- a/gcc/doc/invoke.texi ++++ b/gcc/doc/invoke.texi +@@ -1004,6 +1004,8 @@ Objective-C and Objective-C++ Dialects}. + -mcond-move-float -mno-cond-move-float @gol + -memcpy -mno-memcpy -mstrict-align -mno-strict-align @gol + -mmax-inline-memcpy-size=@var{n} @gol ++-mexplicit-relocs -mno-explicit-relocs @gol ++-mdirect-extern-access -mno-direct-extern-access @gol + -mcmodel=@var{code-model}} + + @emph{M32R/D Options} +@@ -24573,52 +24575,51 @@ less than or equal to @var{n} bytes. The default value of @var{n} is 1024. + @item -mcmodel=@var{code-model} + 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 tiny-static (Not implemented yet) ++@item tiny (Not implemented yet) + + @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 ++The text segment must be within 128MB addressing space. The data segment must ++be within 2GB addressing space. + +-@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 medium ++The text segment and data segment must be within 2GB addressing space. + +-@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 ++@item large (Not implemented yet) ++ ++@item extreme ++This mode does not limit the size of the code segment and data segment. ++The @option{-mcmodel=extreme} option is incompatible with @option{-fplt} and ++@option{-mno-explicit-relocs}. + @end table + The default code model is @code{normal}. ++ ++@item -mexplicit-relocs ++@itemx -mno-explicit-relocs ++@opindex mexplicit-relocs ++@opindex mno-explicit-relocs ++Use or do not use assembler relocation operators when dealing with symbolic ++addresses. The alternative is to use assembler macros instead, which may ++limit optimization. The default value for the option is determined during ++GCC build-time by detecting corresponding assembler support: ++@code{-mexplicit-relocs} if said support is present, ++@code{-mno-explicit-relocs} otherwise. This option is mostly useful for ++debugging, or interoperation with assemblers different from the build-time ++one. ++ ++@item -mdirect-extern-access ++@itemx -mno-direct-extern-access ++@opindex mdirect-extern-access ++Do not use or use GOT to access external symbols. The default is ++@option{-mno-direct-extern-access}: GOT is used for external symbols with ++default visibility, but not used for other external symbols. ++ ++With @option{-mdirect-extern-access}, GOT is not used and all external ++symbols are PC-relatively addressed. It is @strong{only} suitable for ++environments where no dynamic link is performed, like firmwares, OS ++kernels, executables linked with @option{-static} or @option{-static-pie}. ++@option{-mdirect-extern-access} is not compatible with @option{-fPIC} or ++@option{-fpic}. + @end table + + @node M32C Options +diff --git a/gcc/testsuite/g++.target/loongarch/bytepick.C b/gcc/testsuite/g++.target/loongarch/bytepick.C +new file mode 100644 +index 000000000..a39e2fa65 +--- /dev/null ++++ b/gcc/testsuite/g++.target/loongarch/bytepick.C +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mabi=lp64d" } */ ++/* { dg-final { scan-assembler-times "bytepick.w\t\\\$r4,\\\$r5,\\\$r4" 3 } } */ ++/* { dg-final { scan-assembler-times "bytepick.d\t\\\$r4,\\\$r5,\\\$r4" 7 } } */ ++/* { dg-final { scan-assembler-not "slli.w" } } */ ++ ++template ++T ++merge (T a, T b) ++{ ++ return a << offs | b >> (8 * sizeof (T) - offs); ++} ++ ++using u32 = __UINT32_TYPE__; ++using u64 = __UINT64_TYPE__; ++using i64 = __INT64_TYPE__; ++ ++template u32 merge (u32, u32); ++template u32 merge (u32, u32); ++template u32 merge (u32, u32); ++ ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++template u64 merge (u64, u64); ++ ++/* we cannot use bytepick for the following cases */ ++template i64 merge (i64, i64); ++template u64 merge (u64, u64); +diff --git a/gcc/testsuite/g++.target/loongarch/pr106828.C b/gcc/testsuite/g++.target/loongarch/pr106828.C +new file mode 100644 +index 000000000..190c1db71 +--- /dev/null ++++ b/gcc/testsuite/g++.target/loongarch/pr106828.C +@@ -0,0 +1,4 @@ ++/* { dg-do-preprocess } */ ++/* { dg-options "-mabi=lp64d -fsanitize=address" } */ ++ ++/* Tests whether the compiler supports compile option '-fsanitize=address'. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/add-const.c b/gcc/testsuite/gcc.target/loongarch/add-const.c +new file mode 100644 +index 000000000..7b6a7cb92 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/add-const.c +@@ -0,0 +1,45 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O -mabi=lp64d" } */ ++ ++/* None of these functions should load the const operand into a temp ++ register. */ ++ ++/* { dg-final { scan-assembler-not "add\\.[dw]" } } */ ++ ++unsigned long f01 (unsigned long x) { return x + 1; } ++unsigned long f02 (unsigned long x) { return x - 1; } ++unsigned long f03 (unsigned long x) { return x + 2047; } ++unsigned long f04 (unsigned long x) { return x + 4094; } ++unsigned long f05 (unsigned long x) { return x - 2048; } ++unsigned long f06 (unsigned long x) { return x - 4096; } ++unsigned long f07 (unsigned long x) { return x + 0x7fff0000; } ++unsigned long f08 (unsigned long x) { return x - 0x80000000l; } ++unsigned long f09 (unsigned long x) { return x + 0x7fff0000l * 2; } ++unsigned long f10 (unsigned long x) { return x - 0x80000000l * 2; } ++unsigned long f11 (unsigned long x) { return x + 0x7fff0000 + 0x1; } ++unsigned long f12 (unsigned long x) { return x + 0x7fff0000 - 0x1; } ++unsigned long f13 (unsigned long x) { return x + 0x7fff0000 + 0x7ff; } ++unsigned long f14 (unsigned long x) { return x + 0x7fff0000 - 0x800; } ++unsigned long f15 (unsigned long x) { return x - 0x80000000l - 1; } ++unsigned long f16 (unsigned long x) { return x - 0x80000000l + 1; } ++unsigned long f17 (unsigned long x) { return x - 0x80000000l - 0x800; } ++unsigned long f18 (unsigned long x) { return x - 0x80000000l + 0x7ff; } ++ ++unsigned int g01 (unsigned int x) { return x + 1; } ++unsigned int g02 (unsigned int x) { return x - 1; } ++unsigned int g03 (unsigned int x) { return x + 2047; } ++unsigned int g04 (unsigned int x) { return x + 4094; } ++unsigned int g05 (unsigned int x) { return x - 2048; } ++unsigned int g06 (unsigned int x) { return x - 4096; } ++unsigned int g07 (unsigned int x) { return x + 0x7fff0000; } ++unsigned int g08 (unsigned int x) { return x - 0x80000000l; } ++unsigned int g09 (unsigned int x) { return x + 0x7fff0000l * 2; } ++unsigned int g10 (unsigned int x) { return x - 0x80000000l * 2; } ++unsigned int g11 (unsigned int x) { return x + 0x7fff0000 + 0x1; } ++unsigned int g12 (unsigned int x) { return x + 0x7fff0000 - 0x1; } ++unsigned int g13 (unsigned int x) { return x + 0x7fff0000 + 0x7ff; } ++unsigned int g14 (unsigned int x) { return x + 0x7fff0000 - 0x800; } ++unsigned int g15 (unsigned int x) { return x - 0x80000000l - 1; } ++unsigned int g16 (unsigned int x) { return x - 0x80000000l + 1; } ++unsigned int g17 (unsigned int x) { return x - 0x80000000l - 0x800; } ++unsigned int g18 (unsigned int x) { return x - 0x80000000l + 0x7ff; } +diff --git a/gcc/testsuite/gcc.target/loongarch/attr-model-1.c b/gcc/testsuite/gcc.target/loongarch/attr-model-1.c +new file mode 100644 +index 000000000..916d715b9 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/attr-model-1.c +@@ -0,0 +1,6 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mexplicit-relocs -mcmodel=normal -O2" } */ ++/* { dg-final { scan-assembler-times "%pc64_hi12" 2 } } */ ++ ++#define ATTR_MODEL_TEST ++#include "attr-model-test.c" +diff --git a/gcc/testsuite/gcc.target/loongarch/attr-model-2.c b/gcc/testsuite/gcc.target/loongarch/attr-model-2.c +new file mode 100644 +index 000000000..a74c795ac +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/attr-model-2.c +@@ -0,0 +1,6 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mexplicit-relocs -mcmodel=extreme -O2" } */ ++/* { dg-final { scan-assembler-times "%pc64_hi12" 3 } } */ ++ ++#define ATTR_MODEL_TEST ++#include "attr-model-test.c" +diff --git a/gcc/testsuite/gcc.target/loongarch/attr-model-diag.c b/gcc/testsuite/gcc.target/loongarch/attr-model-diag.c +new file mode 100644 +index 000000000..88beede74 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/attr-model-diag.c +@@ -0,0 +1,7 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mexplicit-relocs" } */ ++ ++__thread int x __attribute__((model("extreme"))); /* { dg-error "attribute cannot be specified for thread-local variables" } */ ++register int y __asm__("tp") __attribute__((model("extreme"))); /* { dg-error "attribute cannot be specified for register variables" } */ ++int z __attribute__((model(114))); /* { dg-error "invalid argument" } */ ++int t __attribute__((model("good"))); /* { dg-error "invalid argument" } */ +diff --git a/gcc/testsuite/gcc.target/loongarch/attr-model-test.c b/gcc/testsuite/gcc.target/loongarch/attr-model-test.c +new file mode 100644 +index 000000000..5b61a7af9 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/attr-model-test.c +@@ -0,0 +1,25 @@ ++#ifdef ATTR_MODEL_TEST ++int x __attribute__((model("extreme"))); ++int y __attribute__((model("normal"))); ++int z; ++ ++int ++test(void) ++{ ++ return x + y + z; ++} ++ ++/* The following will be used for kernel per-cpu storage implemention. */ ++ ++register char *per_cpu_base __asm__("r21"); ++static int counter __attribute__((section(".data..percpu"), model("extreme"))); ++ ++void ++inc_counter(void) ++{ ++ int *ptr = (int *)(per_cpu_base + (long)&counter); ++ (*ptr)++; ++} ++#endif ++ ++int dummy; +diff --git a/gcc/testsuite/gcc.target/loongarch/direct-extern-1.c b/gcc/testsuite/gcc.target/loongarch/direct-extern-1.c +new file mode 100644 +index 000000000..85c6c1e8a +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/direct-extern-1.c +@@ -0,0 +1,6 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mexplicit-relocs -mdirect-extern-access" } */ ++/* { dg-final { scan-assembler-not "got" } } */ ++ ++extern int x; ++int f() { return x; } +diff --git a/gcc/testsuite/gcc.target/loongarch/direct-extern-2.c b/gcc/testsuite/gcc.target/loongarch/direct-extern-2.c +new file mode 100644 +index 000000000..58d8bd68a +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/direct-extern-2.c +@@ -0,0 +1,6 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mno-explicit-relocs -mdirect-extern-access" } */ ++/* { dg-final { scan-assembler-not "la.global" } } */ ++ ++extern int x; ++int f() { return x; } +diff --git a/gcc/testsuite/gcc.target/loongarch/div-1.c b/gcc/testsuite/gcc.target/loongarch/div-1.c +new file mode 100644 +index 000000000..b1683f853 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/div-1.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mcheck-zero-division" } */ ++/* { dg-final { scan-assembler "div.\[wd\]\t\\\$r4,\\\$r4,\\\$r5" } } */ ++ ++long ++div(long a, long b) ++{ ++ return a / b; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/div-2.c b/gcc/testsuite/gcc.target/loongarch/div-2.c +new file mode 100644 +index 000000000..4c2beb5b9 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/div-2.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mno-check-zero-division" } */ ++/* { dg-final { scan-assembler "div.\[wd\]\t\\\$r4,\\\$r5,\\\$r4" } } */ ++ ++long ++div(long a, long b) ++{ ++ return b / a; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/div-3.c b/gcc/testsuite/gcc.target/loongarch/div-3.c +new file mode 100644 +index 000000000..d25969263 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/div-3.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mcheck-zero-division" } */ ++/* { dg-final { scan-assembler-not "div.\[wd\]\t\\\$r4,\\\$r5,\\\$r4" } } */ ++ ++long ++div(long a, long b) ++{ ++ return b / a; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/div-4.c b/gcc/testsuite/gcc.target/loongarch/div-4.c +new file mode 100644 +index 000000000..a52f87d6c +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/div-4.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2" } */ ++/* { dg-final { scan-assembler-not "slli" } } */ ++ ++int ++div(int a, int b) ++{ ++ return a / b; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/fcopysign.c b/gcc/testsuite/gcc.target/loongarch/fcopysign.c +new file mode 100644 +index 000000000..058ba2cf5 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/fcopysign.c +@@ -0,0 +1,16 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mdouble-float" } */ ++/* { dg-final { scan-assembler "fcopysign\\.s" } } */ ++/* { dg-final { scan-assembler "fcopysign\\.d" } } */ ++ ++double ++my_copysign (double a, double b) ++{ ++ return __builtin_copysign (a, b); ++} ++ ++float ++my_copysignf (float a, float b) ++{ ++ return __builtin_copysignf (a, b); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/flogb.c b/gcc/testsuite/gcc.target/loongarch/flogb.c +new file mode 100644 +index 000000000..1daefe54e +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/flogb.c +@@ -0,0 +1,18 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mdouble-float -fno-math-errno" } */ ++/* { dg-final { scan-assembler "fabs\\.s" } } */ ++/* { dg-final { scan-assembler "fabs\\.d" } } */ ++/* { dg-final { scan-assembler "flogb\\.s" } } */ ++/* { dg-final { scan-assembler "flogb\\.d" } } */ ++ ++double ++my_logb (double a) ++{ ++ return __builtin_logb (a); ++} ++ ++float ++my_logbf (float a) ++{ ++ return __builtin_logbf (a); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-1.c b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-1.c +new file mode 100644 +index 000000000..1c9490f6a +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-1.c +@@ -0,0 +1,14 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -mfpu=64 -march=loongarch64 -O2" } */ ++/* { dg-final { scan-assembler "frecip\\.d" } } */ ++/* { dg-final { scan-assembler-not "movgr2fr\\.d" } } */ ++/* { dg-final { scan-assembler-not "movfr2gr\\.d" } } */ ++ ++/* FPU is used for calculation and FPR is used for arguments and return ++ values. */ ++ ++double ++t (double x) ++{ ++ return 1.0 / x; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-2.c b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-2.c +new file mode 100644 +index 000000000..0580fd65d +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-2.c +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64s -mfpu=64 -march=loongarch64 -O2" } */ ++/* { dg-final { scan-assembler "frecip\\.d" } } */ ++/* { dg-final { scan-assembler "movgr2fr\\.d" } } */ ++/* { dg-final { scan-assembler "movfr2gr\\.d" } } */ ++ ++/* FPU is used for calculation but FPR cannot be used for arguments and ++ return values. */ ++ ++#include "flt-abi-isa-1.c" +diff --git a/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-3.c b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-3.c +new file mode 100644 +index 000000000..16a926f57 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-3.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64s -mfpu=none -march=loongarch64 -O2" } */ ++/* { dg-final { scan-assembler-not "frecip\\.d" } } */ ++/* { dg-final { scan-assembler-not "movgr2fr\\.d" } } */ ++/* { dg-final { scan-assembler-not "movfr2gr\\.d" } } */ ++ ++/* FPU cannot be used at all. */ ++ ++#include "flt-abi-isa-1.c" +diff --git a/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-4.c b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-4.c +new file mode 100644 +index 000000000..43b579c3f +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/flt-abi-isa-4.c +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-msoft-float -march=loongarch64 -O2" } */ ++/* { dg-final { scan-assembler-not "frecip\\.d" } } */ ++/* { dg-final { scan-assembler-not "movgr2fr\\.d" } } */ ++/* { dg-final { scan-assembler-not "movfr2gr\\.d" } } */ ++ ++/* -msoft-float implies both -mabi=lp64s and -mfpu=none. ++ FPU cannot be used at all. */ ++ ++#include "flt-abi-isa-1.c" +diff --git a/gcc/testsuite/gcc.target/loongarch/fmax-fmin.c b/gcc/testsuite/gcc.target/loongarch/fmax-fmin.c +new file mode 100644 +index 000000000..92cf8a150 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/fmax-fmin.c +@@ -0,0 +1,30 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mdouble-float -fno-finite-math-only" } */ ++/* { dg-final { scan-assembler "fmin\\.s" } } */ ++/* { dg-final { scan-assembler "fmin\\.d" } } */ ++/* { dg-final { scan-assembler "fmax\\.s" } } */ ++/* { dg-final { scan-assembler "fmax\\.d" } } */ ++ ++double ++_fmax(double a, double b) ++{ ++ return __builtin_fmax(a, b); ++} ++ ++float ++_fmaxf(float a, float b) ++{ ++ return __builtin_fmaxf(a, b); ++} ++ ++double ++_fmin(double a, double b) ++{ ++ return __builtin_fmin(a, b); ++} ++ ++float ++_fminf(float a, float b) ++{ ++ return __builtin_fminf(a, b); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/frint.c b/gcc/testsuite/gcc.target/loongarch/frint.c +new file mode 100644 +index 000000000..3ee6a8f97 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/frint.c +@@ -0,0 +1,16 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mdouble-float" } */ ++/* { dg-final { scan-assembler "frint\\.s" } } */ ++/* { dg-final { scan-assembler "frint\\.d" } } */ ++ ++double ++my_rint (double a) ++{ ++ return __builtin_rint (a); ++} ++ ++float ++my_rintf (float a) ++{ ++ return __builtin_rintf (a); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/fscaleb.c b/gcc/testsuite/gcc.target/loongarch/fscaleb.c +new file mode 100644 +index 000000000..f18470fbb +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/fscaleb.c +@@ -0,0 +1,48 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mabi=lp64d -mdouble-float -fno-math-errno" } */ ++/* { dg-final { scan-assembler-times "fscaleb\\.s" 3 } } */ ++/* { dg-final { scan-assembler-times "fscaleb\\.d" 4 } } */ ++/* { dg-final { scan-assembler-times "slli\\.w" 1 } } */ ++ ++double ++my_scalbln (double a, long b) ++{ ++ return __builtin_scalbln (a, b); ++} ++ ++double ++my_scalbn (double a, int b) ++{ ++ return __builtin_scalbn (a, b); ++} ++ ++double ++my_ldexp (double a, int b) ++{ ++ return __builtin_ldexp (a, b); ++} ++ ++float ++my_scalblnf (float a, long b) ++{ ++ return __builtin_scalblnf (a, b); ++} ++ ++float ++my_scalbnf (float a, int b) ++{ ++ return __builtin_scalbnf (a, b); ++} ++ ++float ++my_ldexpf (float a, int b) ++{ ++ return __builtin_ldexpf (a, b); ++} ++ ++/* b must be sign-extended */ ++double ++my_ldexp_long (double a, long b) ++{ ++ return __builtin_ldexp (a, b); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/ftint-no-inexact.c b/gcc/testsuite/gcc.target/loongarch/ftint-no-inexact.c +new file mode 100644 +index 000000000..88b83a9c0 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/ftint-no-inexact.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -mdouble-float -fno-math-errno -fno-fp-int-builtin-inexact" } */ ++/* { dg-final { scan-assembler "ftint\\.l\\.s" } } */ ++/* { dg-final { scan-assembler "ftint\\.l\\.d" } } */ ++/* { dg-final { scan-assembler-not "ftintrm\\.l\\.s" } } */ ++/* { dg-final { scan-assembler-not "ftintrm\\.l\\.d" } } */ ++/* { dg-final { scan-assembler-not "ftintrp\\.l\\.s" } } */ ++/* { dg-final { scan-assembler-not "ftintrp\\.l\\.d" } } */ ++ ++long ++my_lrint (double a) ++{ ++ return __builtin_lrint (a); ++} ++ ++long ++my_lrintf (float a) ++{ ++ return __builtin_lrintf (a); ++} ++ ++long ++my_lfloor (double a) ++{ ++ return __builtin_lfloor (a); ++} ++ ++long ++my_lfloorf (float a) ++{ ++ return __builtin_lfloorf (a); ++} ++ ++long ++my_lceil (double a) ++{ ++ return __builtin_lceil (a); ++} ++ ++long ++my_lceilf (float a) ++{ ++ return __builtin_lceilf (a); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/ftint.c b/gcc/testsuite/gcc.target/loongarch/ftint.c +new file mode 100644 +index 000000000..7a326a454 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/ftint.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -mdouble-float -fno-math-errno -ffp-int-builtin-inexact" } */ ++/* { dg-final { scan-assembler "ftint\\.l\\.s" } } */ ++/* { dg-final { scan-assembler "ftint\\.l\\.d" } } */ ++/* { dg-final { scan-assembler "ftintrm\\.l\\.s" } } */ ++/* { dg-final { scan-assembler "ftintrm\\.l\\.d" } } */ ++/* { dg-final { scan-assembler "ftintrp\\.l\\.s" } } */ ++/* { dg-final { scan-assembler "ftintrp\\.l\\.d" } } */ ++ ++long ++my_lrint (double a) ++{ ++ return __builtin_lrint (a); ++} ++ ++long ++my_lrintf (float a) ++{ ++ return __builtin_lrintf (a); ++} ++ ++long ++my_lfloor (double a) ++{ ++ return __builtin_lfloor (a); ++} ++ ++long ++my_lfloorf (float a) ++{ ++ return __builtin_lfloorf (a); ++} ++ ++long ++my_lceil (double a) ++{ ++ return __builtin_lceil (a); ++} ++ ++long ++my_lceilf (float a) ++{ ++ return __builtin_lceilf (a); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-1.c +new file mode 100644 +index 000000000..76bf11b0c +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-1.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-2.c +new file mode 100644 +index 000000000..4b468fef8 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-2.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-3.c +new file mode 100644 +index 000000000..dd3a4882d +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-3.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-4.c +new file mode 100644 +index 000000000..f8158ec34 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-4.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-5.c +new file mode 100644 +index 000000000..37994af43 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-5.c +@@ -0,0 +1,33 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-6.c +new file mode 100644 +index 000000000..8e366e376 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-6.c +@@ -0,0 +1,33 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-7.c +new file mode 100644 +index 000000000..4177c3d96 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-7.c +@@ -0,0 +1,34 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-8.c +new file mode 100644 +index 000000000..4254eaa16 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-8.c +@@ -0,0 +1,33 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ ++/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-extreme-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-extreme-1.c +new file mode 100644 +index 000000000..db1e0f853 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-extreme-1.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=extreme" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i.*%got_pc_hi20.*\n\taddi\.d.*%got_pc_lo12.*\n\tlu32i\.d.*%got64_pc_lo20.*\n\tlu52i\.d.*%got64_pc_hi12.*\n\tldx\.d" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20.*\n\taddi\.d.*%pc_lo12.*\n\tlu32i\.d.*%pc64_lo20.*\n\tlu52i\.d.*pc64_hi12.*\n\tadd\.d" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20.*\n\taddi\.d.*%pc_lo12.*\n\tlu32i\.d.*%pc64_lo20.*\n\tlu52i\.d.*pc64_hi12.*\n\tadd\.d" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-extreme-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-extreme-2.c +new file mode 100644 +index 000000000..21bf81ae8 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-extreme-2.c +@@ -0,0 +1,32 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=extreme" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i.*%got_pc_hi20.*\n\taddi\.d.*%got_pc_lo12.*\n\tlu32i\.d.*%got64_pc_lo20.*\n\tlu52i\.d.*%got64_pc_hi12.*\n\tldx\.d" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i.*%got_pc_hi20.*\n\taddi\.d.*%got_pc_lo12.*\n\tlu32i\.d.*%got64_pc_lo20.*\n\tlu52i\.d.*%got64_pc_hi12.*\n\tldx\.d" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20.*\n\taddi\.d.*%pc_lo12.*\n\tlu32i\.d.*%pc64_lo20.*\n\tlu52i\.d.*pc64_hi12.*\n\tadd\.d" } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c +new file mode 100644 +index 000000000..6339e832f +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c +@@ -0,0 +1,41 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" { target tls_native } } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c +new file mode 100644 +index 000000000..a53e75e0b +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c +@@ -0,0 +1,41 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" { target tls_native } } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c +new file mode 100644 +index 000000000..0da7bf98e +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c +@@ -0,0 +1,41 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" { target tls_native } } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c +new file mode 100644 +index 000000000..0219688ae +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c +@@ -0,0 +1,41 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" { target tls_native } } } */ ++ ++extern void g (void); ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c +new file mode 100644 +index 000000000..8a47b5afc +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */ ++/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c +new file mode 100644 +index 000000000..1e75e60e0 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */ ++/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c +new file mode 100644 +index 000000000..9e89085ca +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c +@@ -0,0 +1,43 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */ ++/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c +new file mode 100644 +index 000000000..fde9c6e0e +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c +@@ -0,0 +1,43 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=medium" } */ ++/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ ++/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */ ++/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */ ++/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++ ++extern void g (void); ++ ++void ++f (void) ++{} ++ ++static void ++l (void) ++{} ++ ++void ++test (void) ++{ ++ g (); ++} ++ ++void ++test1 (void) ++{ ++ f (); ++} ++ ++void ++test2 (void) ++{ ++ l (); ++} ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test3 (void) ++{ ++ a = 10; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/imm-load.c b/gcc/testsuite/gcc.target/loongarch/imm-load.c +new file mode 100644 +index 000000000..c04ca3399 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/imm-load.c +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O2 -fdump-rtl-split1" } */ ++ ++long int ++test (void) ++{ ++ return 0x1234567890abcdef; ++} ++/* { dg-final { scan-rtl-dump-times "scanning new insn with uid" 6 "split1" } } */ ++ +diff --git a/gcc/testsuite/gcc.target/loongarch/imm-load1.c b/gcc/testsuite/gcc.target/loongarch/imm-load1.c +new file mode 100644 +index 000000000..2ff029712 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/imm-load1.c +@@ -0,0 +1,26 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -O2" } */ ++/* { dg-final { scan-assembler "test:.*lu52i\.d.*\n\taddi\.w.*\n\.L2:" } } */ ++ ++ ++extern long long b[10]; ++static inline long long ++repeat_bytes (void) ++{ ++ long long r = 0x0101010101010101; ++ ++ return r; ++} ++ ++static inline long long ++highbit_mask (long long m) ++{ ++ return m & repeat_bytes (); ++} ++ ++void test(long long *a) ++{ ++ for (int i = 0; i < 10; i++) ++ b[i] = highbit_mask (a[i]); ++ ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/tst-asm-const.c b/gcc/testsuite/gcc.target/loongarch/pr107731.c +similarity index 78% +rename from gcc/testsuite/gcc.target/loongarch/tst-asm-const.c +rename to gcc/testsuite/gcc.target/loongarch/pr107731.c +index 2e04b99e3..80d84c48c 100644 +--- a/gcc/testsuite/gcc.target/loongarch/tst-asm-const.c ++++ b/gcc/testsuite/gcc.target/loongarch/pr107731.c +@@ -1,13 +1,13 @@ +-/* 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" ++ ".long %c0\n\t" ++ ".long %c1\n\t" + : + :"i"(0x3f3f3f3f), "i"(52) + : +diff --git a/gcc/testsuite/gcc.target/loongarch/pr109465-1.c b/gcc/testsuite/gcc.target/loongarch/pr109465-1.c +new file mode 100644 +index 000000000..4cd35d139 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/pr109465-1.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mabi=lp64d -mno-strict-align" } */ ++/* { dg-final { scan-assembler-times "st\\.d|stptr\\.d" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.w|stptr\\.w" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.h" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.b" 1 } } */ ++ ++extern char a[], b[]; ++void test() { __builtin_memcpy(a, b, 15); } +diff --git a/gcc/testsuite/gcc.target/loongarch/pr109465-2.c b/gcc/testsuite/gcc.target/loongarch/pr109465-2.c +new file mode 100644 +index 000000000..703eb951c +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/pr109465-2.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mabi=lp64d -mstrict-align" } */ ++/* { dg-final { scan-assembler-times "st\\.d|stptr\\.d" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.w|stptr\\.w" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.h" 1 } } */ ++/* { dg-final { scan-assembler-times "st\\.b" 1 } } */ ++ ++extern long a[], b[]; ++void test() { __builtin_memcpy(a, b, 15); } +diff --git a/gcc/testsuite/gcc.target/loongarch/pr109465-3.c b/gcc/testsuite/gcc.target/loongarch/pr109465-3.c +new file mode 100644 +index 000000000..d6a80659b +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/pr109465-3.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mabi=lp64d -mstrict-align" } */ ++ ++/* Three loop iterations each contains 4 st.b, and 3 st.b after the loop */ ++/* { dg-final { scan-assembler-times "st\\.b" 7 } } */ ++ ++/* { dg-final { scan-assembler-not "st\\.h" } } */ ++/* { dg-final { scan-assembler-not "st\\.w|stptr\\.w" } } */ ++/* { dg-final { scan-assembler-not "st\\.d|stptr\\.d" } } */ ++ ++extern char a[], b[]; ++void test() { __builtin_memcpy(a, b, 15); } +diff --git a/gcc/testsuite/gcc.target/loongarch/prolog-opt.c b/gcc/testsuite/gcc.target/loongarch/prolog-opt.c +index 0470a1f1e..e6a642633 100644 +--- a/gcc/testsuite/gcc.target/loongarch/prolog-opt.c ++++ b/gcc/testsuite/gcc.target/loongarch/prolog-opt.c +@@ -1,7 +1,7 @@ + /* Test that LoongArch backend stack drop operation optimized. */ + + /* { dg-do compile } */ +-/* { dg-options "-O2 -mabi=lp64d" } */ ++/* { dg-options "-O2 -mabi=lp64d -fno-stack-protector" } */ + /* { dg-final { scan-assembler "addi.d\t\\\$r3,\\\$r3,-16" } } */ + + extern int printf (char *, ...); +diff --git a/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c +new file mode 100644 +index 000000000..3ec8bd229 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c +@@ -0,0 +1,23 @@ ++/* { dg-do compile } */ ++/* { dg-options "-mabi=lp64d -mexplicit-relocs -fno-pic -O2 -mcmodel=normal" } */ ++/* { dg-final { scan-assembler "pcalau12i.*%pc_hi20\\(\.LANCHOR0\\)\n" } } */ ++/* { dg-final { scan-assembler "addi\.d.*%pc_lo12\\(\.LANCHOR0\\)\n" } } */ ++/* { dg-final { scan-assembler "ldptr.d\t\\\$r4,.*,0\n" } } */ ++/* { dg-final { scan-assembler "ld.d\t\\\$r5,.*,8\n" } } */ ++/* { dg-final { scan-assembler-not "\.LANCHOR0+8" } } */ ++ ++ ++struct S ++{ ++ char *a; ++ unsigned short int b; ++}; ++ ++struct S s1; ++ ++void test(struct S); ++void test1(void) ++{ ++ test(s1); ++} ++ +diff --git a/gcc/testsuite/gcc.target/loongarch/shrink-wrap.c b/gcc/testsuite/gcc.target/loongarch/shrink-wrap.c +new file mode 100644 +index 000000000..1431536c5 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/shrink-wrap.c +@@ -0,0 +1,19 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O -fshrink-wrap" } */ ++ ++/* We should not save anything before checking the value of x. */ ++/* { dg-final { scan-assembler-not "st(ptr)?\\\.\[dw\].*b(eq|ne)z" } } */ ++ ++int ++foo (int x) ++{ ++ __asm__ ("nop" :); ++ if (x) ++ { ++ __asm__ ("" ::: "s0", "s1"); ++ return x; ++ } ++ ++ __asm__ ("" ::: "s2", "s3"); ++ return 0; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c +new file mode 100644 +index 000000000..6ee589c4b +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c +@@ -0,0 +1,15 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE y ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */ ++/* { dg-final { scan-assembler-times {stx\.d\t\$r0,\$r3,\$r12} 1 } } */ ++ ++/* Dynamic alloca, expect loop, and 1 probes with top at sp. ++ 1st probe is inside the loop for the full guard-size allocations, second ++ probe is for the case where residual is zero. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c +new file mode 100644 +index 000000000..8deaa5873 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 0 ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-not {stp*t*r*\.d\t\$r0,\$r3,4088} } } */ ++ ++/* Alloca of 0 should emit no probes, boundary condition. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c +new file mode 100644 +index 000000000..e326ba9a0 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 100 ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-times {st\.d\t\$r0,\$r3,104} 1 } } */ ++ ++/* Alloca is less than guard-size, 1 probe at the top of the new allocation. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c +new file mode 100644 +index 000000000..b9f7572de +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 64 * 1024 ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */ ++ ++/* Alloca is exactly one guard-size, 1 probe expected at top. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c +new file mode 100644 +index 000000000..0ff6e493f +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 65 * 1024 ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,1016} 1 } } */ ++ ++/* Alloca is more than one guard-page. 2 probes expected. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c +new file mode 100644 +index 000000000..c5cf74fcb +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-require-effective-target alloca } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 127 * 64 * 1024 ++#include "stack-check-alloca.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */ ++ ++/* Large alloca of a constant amount which is a multiple of a guard-size. ++ Loop expected with top probe. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h +new file mode 100644 +index 000000000..8c75f6c0f +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h +@@ -0,0 +1,15 @@ ++ ++/* Avoid inclusion of alloca.h, unavailable on some systems. */ ++#define alloca __builtin_alloca ++ ++__attribute__((noinline, noipa)) ++void g (char* ptr, int y) ++{ ++ ptr[y] = '\0'; ++} ++ ++void f_caller (int y) ++{ ++ char* pStr = alloca(SIZE); ++ g (pStr, y); ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c +new file mode 100644 +index 000000000..cd72154f4 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables -fno-stack-protector" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 128*1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131072} 1 } } */ ++/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */ ++ ++/* Checks that the CFA notes are correct for every sp adjustment. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c +new file mode 100644 +index 000000000..3e5ca05b2 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables -fno-stack-protector" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 1280*1024 + 512 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311232} 1 } } */ ++/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */ ++ ++/* Checks that the CFA notes are correct for every sp adjustment. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c +new file mode 100644 +index 000000000..351bc1f61 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 128 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 0 } } */ ++ ++/* SIZE is smaller than guard-size so no probe expected. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c +new file mode 100644 +index 000000000..6bba659a3 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 63 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*.d\t\$r0,\$r3,0} 0 } } */ ++ ++/* SIZE is smaller than guard-size so no probe expected. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c +new file mode 100644 +index 000000000..164956c37 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 64 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */ ++ ++/* SIZE is equal to guard-size, 1 probe expected, boundary condition. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c +new file mode 100644 +index 000000000..f53da6b0d +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 65 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */ ++ ++/* SIZE is more than guard-size 1 probe expected. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c +new file mode 100644 +index 000000000..c092317ea +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 127 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */ ++ ++/* SIZE is more than 1x guard-size and remainder small than guard-size, ++ 1 probe expected, unrolled, no loop. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c +new file mode 100644 +index 000000000..70a2f53f6 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 128 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 2 } } */ ++ ++/* SIZE is more than 2x guard-size and no remainder, unrolled, no loop. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c +new file mode 100644 +index 000000000..e2df89acc +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */ ++/* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */ ++ ++#define SIZE 6 * 64 * 1024 ++#include "stack-check-prologue.h" ++ ++/* { dg-final { scan-assembler-times {stp*t*r*.d\t\$r0,\$r3,0} 1 } } */ ++ ++/* SIZE is more than 4x guard-size and no remainder, 1 probe expected in a loop ++ and no residual probe. */ +diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue.h b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue.h +new file mode 100644 +index 000000000..b7e06aedb +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue.h +@@ -0,0 +1,5 @@ ++int f_test (int x) ++{ ++ char arr[SIZE]; ++ return arr[x]; ++} +diff --git a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c +new file mode 100644 +index 000000000..9432c477e +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O0 -fno-plt -mcmodel=normal -mexplicit-relocs" } */ ++/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" { target tls_native } } } */ ++ ++__attribute__ ((tls_model ("global-dynamic"))) __thread int a; ++ ++void ++test (void) ++{ ++ a = 10; ++} ++ +diff --git a/gcc/testsuite/gcc.target/loongarch/va_arg.c b/gcc/testsuite/gcc.target/loongarch/va_arg.c +new file mode 100644 +index 000000000..980c96d0e +--- /dev/null ++++ b/gcc/testsuite/gcc.target/loongarch/va_arg.c +@@ -0,0 +1,24 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2" } */ ++ ++/* Technically we shouldn't save any register for this function: it should be ++ compiled as if it accepts 3 named arguments. But AFAIK no compilers can ++ achieve this "perfect" optimization now, so just ensure we are using the ++ knowledge provided by stdarg pass and we won't save GARs impossible to be ++ accessed with __builtin_va_arg () when the va_list does not escape. */ ++ ++/* { dg-final { scan-assembler-not "st.*r7" } } */ ++ ++int ++test (int a0, ...) ++{ ++ void *arg; ++ int a1, a2; ++ ++ __builtin_va_start (arg, a0); ++ a1 = __builtin_va_arg (arg, int); ++ a2 = __builtin_va_arg (arg, int); ++ __builtin_va_end (arg); ++ ++ return a0 + a1 + a2; ++} +diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp +index 3a4f65b9b..8171212f0 100644 +--- a/gcc/testsuite/lib/target-supports.exp ++++ b/gcc/testsuite/lib/target-supports.exp +@@ -11289,7 +11289,8 @@ proc check_effective_target_supports_stack_clash_protection { } { + + if { [istarget x86_64-*-*] || [istarget i?86-*-*] + || [istarget powerpc*-*-*] || [istarget rs6000*-*-*] +- || [istarget aarch64*-**] || [istarget s390*-*-*] } { ++ || [istarget aarch64*-**] || [istarget s390*-*-*] ++ || [istarget loongarch64*-**] } { + return 1 + } + return 0 +@@ -11340,6 +11341,10 @@ proc check_effective_target_caller_implicit_probes { } { + return 1; + } + ++ if { [istarget loongarch64*-*-*] } { ++ return 1; ++ } ++ + return 0 + } + +diff --git a/include/longlong.h b/include/longlong.h +index 64a7b10f9..c3a6f1e7e 100644 +--- a/include/longlong.h ++++ b/include/longlong.h +@@ -593,6 +593,18 @@ extern UDItype __umulsidi3 (USItype, USItype); + #define UMUL_TIME 14 + #endif + ++#ifdef __loongarch__ ++# if W_TYPE_SIZE == 32 ++# define count_leading_zeros(count, x) ((count) = __builtin_clz (x)) ++# define count_trailing_zeros(count, x) ((count) = __builtin_ctz (x)) ++# define COUNT_LEADING_ZEROS_0 32 ++# elif W_TYPE_SIZE == 64 ++# define count_leading_zeros(count, x) ((count) = __builtin_clzll (x)) ++# define count_trailing_zeros(count, x) ((count) = __builtin_ctzll (x)) ++# define COUNT_LEADING_ZEROS_0 64 ++# endif ++#endif ++ + #if defined (__M32R__) && W_TYPE_SIZE == 32 + #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + /* The cmp clears the condition bit. */ \ +diff --git a/include/vtv-change-permission.h b/include/vtv-change-permission.h +index 70bdad92b..e7b9294a0 100644 +--- a/include/vtv-change-permission.h ++++ b/include/vtv-change-permission.h +@@ -48,6 +48,10 @@ extern void __VLTChangePermission (int); + #else + #if defined(__sun__) && defined(__svr4__) && defined(__sparc__) + #define VTV_PAGE_SIZE 8192 ++#elif defined(__loongarch_lp64) ++/* The page size is configurable by the kernel to be 4, 16 or 64 KiB. ++ For now, only the default page size of 16KiB is supported. */ ++#define VTV_PAGE_SIZE 16384 + #else + #define VTV_PAGE_SIZE 4096 + #endif +diff --git a/libitm/config/loongarch/asm.h b/libitm/config/loongarch/asm.h +new file mode 100644 +index 000000000..a8e3304bb +--- /dev/null ++++ b/libitm/config/loongarch/asm.h +@@ -0,0 +1,54 @@ ++/* Copyright (C) 2022 Free Software Foundation, Inc. ++ Contributed by Loongson Co. Ltd. ++ ++ This file is part of the GNU Transactional Memory Library (libitm). ++ ++ Libitm 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. ++ ++ Libitm 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 ++ . */ ++ ++#ifndef _LA_ASM_H ++#define _LA_ASM_H ++ ++#if defined(__loongarch_lp64) ++# define GPR_L ld.d ++# define GPR_S st.d ++# define SZ_GPR 8 ++# define ADDSP(si) addi.d $sp, $sp, si ++#elif defined(__loongarch64_ilp32) ++# define GPR_L ld.w ++# define GPR_S st.w ++# define SZ_GPR 4 ++# define ADDSP(si) addi.w $sp, $sp, si ++#else ++# error Unsupported GPR size (must be 64-bit or 32-bit). ++#endif ++ ++#if defined(__loongarch_double_float) ++# define FPR_L fld.d ++# define FPR_S fst.d ++# define SZ_FPR 8 ++#elif defined(__loongarch_single_float) ++# define FPR_L fld.s ++# define FPR_S fst.s ++# define SZ_FPR 4 ++#else ++# define SZ_FPR 0 ++#endif ++ ++#endif /* _LA_ASM_H */ +diff --git a/libitm/config/loongarch/sjlj.S b/libitm/config/loongarch/sjlj.S +new file mode 100644 +index 000000000..f896e400e +--- /dev/null ++++ b/libitm/config/loongarch/sjlj.S +@@ -0,0 +1,130 @@ ++/* Copyright (C) 2022 Free Software Foundation, Inc. ++ Contributed by Loongson Co. Ltd. ++ ++ This file is part of the GNU Transactional Memory Library (libitm). ++ ++ Libitm 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. ++ ++ Libitm 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 ++ . */ ++ ++#include "asmcfi.h" ++#include "asm.h" ++ ++ .text ++ .align 2 ++ .global _ITM_beginTransaction ++ .type _ITM_beginTransaction, @function ++ ++_ITM_beginTransaction: ++ cfi_startproc ++ move $r5, $sp ++ ADDSP(-(12*SZ_GPR+8*SZ_FPR)) ++ cfi_adjust_cfa_offset(12*SZ_GPR+8*SZ_FPR) ++ ++ /* Frame Pointer */ ++ GPR_S $fp, $sp, 0*SZ_GPR ++ cfi_rel_offset(22, 0) ++ ++ /* Return Address */ ++ GPR_S $r1, $sp, 1*SZ_GPR ++ cfi_rel_offset(1, SZ_GPR) ++ ++ /* Caller's $sp */ ++ GPR_S $r5, $sp, 2*SZ_GPR ++ ++ /* Callee-saved scratch GPRs (r23-r31) */ ++ GPR_S $s0, $sp, 3*SZ_GPR ++ GPR_S $s1, $sp, 4*SZ_GPR ++ GPR_S $s2, $sp, 5*SZ_GPR ++ GPR_S $s3, $sp, 6*SZ_GPR ++ GPR_S $s4, $sp, 7*SZ_GPR ++ GPR_S $s5, $sp, 8*SZ_GPR ++ GPR_S $s6, $sp, 9*SZ_GPR ++ GPR_S $s7, $sp, 10*SZ_GPR ++ GPR_S $s8, $sp, 11*SZ_GPR ++ ++#if !defined(__loongarch_soft_float) ++ /* Callee-saved scratch FPRs (f24-f31) */ ++ FPR_S $f24, $sp, 12*SZ_GPR + 0*SZ_FPR ++ FPR_S $f25, $sp, 12*SZ_GPR + 1*SZ_FPR ++ FPR_S $f26, $sp, 12*SZ_GPR + 2*SZ_FPR ++ FPR_S $f27, $sp, 12*SZ_GPR + 3*SZ_FPR ++ FPR_S $f28, $sp, 12*SZ_GPR + 4*SZ_FPR ++ FPR_S $f29, $sp, 12*SZ_GPR + 5*SZ_FPR ++ FPR_S $f30, $sp, 12*SZ_GPR + 6*SZ_FPR ++ FPR_S $f31, $sp, 12*SZ_GPR + 7*SZ_FPR ++#endif ++ move $fp, $sp ++ ++ /* Invoke GTM_begin_transaction with the struct we've just built. */ ++ move $r5, $sp ++ bl %plt(GTM_begin_transaction) ++ ++ /* Return. (no call-saved scratch reg needs to be restored here) */ ++ GPR_L $fp, $sp, 0*SZ_GPR ++ cfi_restore(22) ++ GPR_L $r1, $sp, 1*SZ_GPR ++ cfi_restore(1) ++ ++ ADDSP(12*SZ_GPR+8*SZ_FPR) ++ cfi_adjust_cfa_offset(-(12*SZ_GPR+8*SZ_FPR)) ++ ++ jr $r1 ++ cfi_endproc ++ .size _ITM_beginTransaction, . - _ITM_beginTransaction ++ ++ .align 2 ++ .global GTM_longjmp ++ .hidden GTM_longjmp ++ .type GTM_longjmp, @function ++ ++GTM_longjmp: ++ cfi_startproc ++ GPR_L $s0, $r5, 3*SZ_GPR ++ GPR_L $s1, $r5, 4*SZ_GPR ++ GPR_L $s2, $r5, 5*SZ_GPR ++ GPR_L $s3, $r5, 6*SZ_GPR ++ GPR_L $s4, $r5, 7*SZ_GPR ++ GPR_L $s5, $r5, 8*SZ_GPR ++ GPR_L $s6, $r5, 9*SZ_GPR ++ GPR_L $s7, $r5, 10*SZ_GPR ++ GPR_L $s8, $r5, 11*SZ_GPR ++ ++#if !defined(__loongarch_soft_float) ++ /* Callee-saved scratch FPRs (f24-f31) */ ++ FPR_L $f24, $r5, 12*SZ_GPR + 0*SZ_FPR ++ FPR_L $f25, $r5, 12*SZ_GPR + 1*SZ_FPR ++ FPR_L $f26, $r5, 12*SZ_GPR + 2*SZ_FPR ++ FPR_L $f27, $r5, 12*SZ_GPR + 3*SZ_FPR ++ FPR_L $f28, $r5, 12*SZ_GPR + 4*SZ_FPR ++ FPR_L $f29, $r5, 12*SZ_GPR + 5*SZ_FPR ++ FPR_L $f30, $r5, 12*SZ_GPR + 6*SZ_FPR ++ FPR_L $f31, $r5, 12*SZ_GPR + 7*SZ_FPR ++#endif ++ ++ GPR_L $r7, $r5, 2*SZ_GPR ++ GPR_L $fp, $r5, 0*SZ_GPR ++ GPR_L $r1, $r5, 1*SZ_GPR ++ cfi_def_cfa(5, 0) ++ move $sp, $r7 ++ jr $r1 ++ cfi_endproc ++ .size GTM_longjmp, . - GTM_longjmp ++ ++#ifdef __linux__ ++.section .note.GNU-stack, "", @progbits ++#endif +diff --git a/libitm/config/loongarch/target.h b/libitm/config/loongarch/target.h +new file mode 100644 +index 000000000..0c5cf3ada +--- /dev/null ++++ b/libitm/config/loongarch/target.h +@@ -0,0 +1,50 @@ ++/* Copyright (C) 2022 Free Software Foundation, Inc. ++ Contributed by Loongson Co. Ltd. ++ ++ This file is part of the GNU Transactional Memory Library (libitm). ++ ++ Libitm 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. ++ ++ Libitm 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 ++ . */ ++ ++namespace GTM HIDDEN { ++ ++typedef struct gtm_jmpbuf ++ { ++ long int fp; /* Frame Pointer: r22 */ ++ long int pc; /* Return Address: r1 */ ++ void *cfa; /* CFA: r3 */ ++ long int gpr[9]; /* Callee-saved scratch GPRs: r23(s0)-r31(s8) */ ++ ++ /* Callee-saved scratch FPRs: f24-f31 */ ++#if defined(__loongarch_double_float) ++ double fpr[8]; ++#elif defined(__loongarch_single_float) ++ float fpr[8]; ++#endif ++ } gtm_jmpbuf; ++ ++#define HW_CACHELINE_SIZE 128 ++ ++static inline void ++cpu_relax (void) ++{ ++ __asm__ volatile ("" : : : "memory"); ++} ++ ++} // namespace GTM +diff --git a/libitm/configure.tgt b/libitm/configure.tgt +index 06e90973e..4c0e78cff 100644 +--- a/libitm/configure.tgt ++++ b/libitm/configure.tgt +@@ -80,6 +80,8 @@ EOF + ARCH=x86 + ;; + ++ loongarch*) ARCH=loongarch ;; ++ + sh*) ARCH=sh ;; + + sparc) +diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h +index 105c672cc..c5534d7f6 100644 +--- a/libsanitizer/asan/asan_interceptors.h ++++ b/libsanitizer/asan/asan_interceptors.h +@@ -119,7 +119,7 @@ void InitializePlatformInterceptors(); + + #if SANITIZER_LINUX && \ + (defined(__arm__) || defined(__aarch64__) || defined(__i386__) || \ +- defined(__x86_64__) || SANITIZER_RISCV64) ++ defined(__x86_64__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64) + # define ASAN_INTERCEPT_VFORK 1 + #else + # define ASAN_INTERCEPT_VFORK 0 +diff --git a/libsanitizer/asan/asan_interceptors_vfork.S b/libsanitizer/asan/asan_interceptors_vfork.S +index 3ae5503e8..ec29adc7b 100644 +--- a/libsanitizer/asan/asan_interceptors_vfork.S ++++ b/libsanitizer/asan/asan_interceptors_vfork.S +@@ -6,6 +6,7 @@ + #include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S" + #include "sanitizer_common/sanitizer_common_interceptors_vfork_arm.inc.S" + #include "sanitizer_common/sanitizer_common_interceptors_vfork_i386.inc.S" ++#include "sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S" + #include "sanitizer_common/sanitizer_common_interceptors_vfork_riscv64.inc.S" + #include "sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S" + #endif +diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h +index 4b0037fce..4fcb82776 100644 +--- a/libsanitizer/asan/asan_mapping.h ++++ b/libsanitizer/asan/asan_mapping.h +@@ -72,6 +72,12 @@ + // || `[0x2000000000, 0x23ffffffff]` || LowShadow || + // || `[0x0000000000, 0x1fffffffff]` || LowMem || + // ++// Default Linux/LoongArch64 (47-bit VMA) mapping: ++// || `[0x500000000000, 0x7fffffffffff]` || HighMem || ++// || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow || ++// || `[0x480000000000, 0x49ffffffffff]` || ShadowGap || ++// || `[0x400000000000, 0x47ffffffffff]` || LowShadow || ++// || `[0x000000000000, 0x3fffffffffff]` || LowMem || + // Default Linux/RISCV64 Sv39 mapping: + // || `[0x1555550000, 0x3fffffffff]` || HighMem || + // || `[0x0fffffa000, 0x1555555fff]` || HighShadow || +@@ -165,6 +171,7 @@ static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; + static const u64 kRiscv64_ShadowOffset64 = 0xd55550000; + static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; + static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; ++static const u64 kLoongArch64_ShadowOffset64 = 1ULL << 46; + static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; + static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52; + static const u64 kSPARC64_ShadowOffset64 = 1ULL << 43; // 0x80000000000 +@@ -215,6 +222,8 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 + # define SHADOW_OFFSET kDefaultShadowOffset64 + # elif defined(__mips64) + # define SHADOW_OFFSET kMIPS64_ShadowOffset64 ++# elif SANITIZER_LOONGARCH64 ++# define SHADOW_OFFSET kLoongArch64_ShadowOffset64 + #elif defined(__sparc__) + #define SHADOW_OFFSET kSPARC64_ShadowOffset64 + # elif SANITIZER_WINDOWS64 +diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt +index fb89df493..87d8a2c38 100644 +--- a/libsanitizer/configure.tgt ++++ b/libsanitizer/configure.tgt +@@ -72,6 +72,8 @@ case "${target}" in + ;; + riscv64-*-linux*) + ;; ++ loongarch64-*-linux*) ++ ;; + *) + UNSUPPORTED=1 + ;; +diff --git a/libsanitizer/lsan/lsan_common.cpp b/libsanitizer/lsan/lsan_common.cpp +index 308dbb3e4..6bd5b933e 100644 +--- a/libsanitizer/lsan/lsan_common.cpp ++++ b/libsanitizer/lsan/lsan_common.cpp +@@ -167,6 +167,9 @@ static inline bool CanBeAHeapPointer(uptr p) { + unsigned runtimeVMA = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + return ((p >> runtimeVMA) == 0); ++# elif defined(__loongarch_lp64) ++ // Allow 47-bit user-space VMA at current. ++ return ((p >> 47) == 0); + #else + return true; + #endif +diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h +index f9b55e4e8..db43a37c5 100644 +--- a/libsanitizer/lsan/lsan_common.h ++++ b/libsanitizer/lsan/lsan_common.h +@@ -42,6 +42,8 @@ + #define CAN_SANITIZE_LEAKS 1 + #elif defined(__arm__) && SANITIZER_LINUX + #define CAN_SANITIZE_LEAKS 1 ++#elif SANITIZER_LOONGARCH64 && SANITIZER_LINUX ++# define CAN_SANITIZE_LEAKS 1 + #elif SANITIZER_RISCV64 && SANITIZER_LINUX + #define CAN_SANITIZE_LEAKS 1 + #elif SANITIZER_NETBSD || SANITIZER_FUCHSIA +diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h +index 065154496..fe9995fcf 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_common.h ++++ b/libsanitizer/sanitizer_common/sanitizer_common.h +@@ -697,6 +697,7 @@ enum ModuleArch { + kModuleArchARMV7K, + kModuleArchARM64, + kModuleArchRISCV64, ++ kModuleArchLoongArch64, + kModuleArchHexagon + }; + +@@ -767,6 +768,8 @@ inline const char *ModuleArchToString(ModuleArch arch) { + return "arm64"; + case kModuleArchRISCV64: + return "riscv64"; ++ case kModuleArchLoongArch64: ++ return "loongarch64"; + case kModuleArchHexagon: + return "hexagon"; + } +diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S +new file mode 100644 +index 000000000..68782acb3 +--- /dev/null ++++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S +@@ -0,0 +1,57 @@ ++#if defined(__loongarch_lp64) && defined(__linux__) ++ ++#include "sanitizer_common/sanitizer_asm.h" ++ ++ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA) ++ASM_HIDDEN(_ZN14__interception10real_vforkE) ++ ++.text ++.globl ASM_WRAPPER_NAME(vfork) ++ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork)) ++ASM_WRAPPER_NAME(vfork): ++ // Save ra in the off-stack spill area. ++ // allocate space on stack ++ addi.d $sp, $sp, -16 ++ // store $ra value ++ st.d $ra, $sp, 8 ++ bl COMMON_INTERCEPTOR_SPILL_AREA ++ // restore previous values from stack ++ ld.d $ra, $sp, 8 ++ // adjust stack ++ addi.d $sp, $sp, 16 ++ // store $ra by $a0 ++ st.d $ra, $a0, 0 ++ ++ // Call real vfork. This may return twice. User code that runs between the first and the second return ++ // may clobber the stack frame of the interceptor; that's why it does not have a frame. ++ la.local $a0, _ZN14__interception10real_vforkE ++ ld.d $a0, $a0, 0 ++ jirl $ra, $a0, 0 ++ ++ // adjust stack ++ addi.d $sp, $sp, -16 ++ // store $a0 by adjusted stack ++ st.d $a0, $sp, 8 ++ // jump to exit label if $a0 is 0 ++ beqz $a0, .L_exit ++ ++ // $a0 != 0 => parent process. Clear stack shadow. ++ // put old $sp to $a0 ++ addi.d $a0, $sp, 16 ++ bl %plt(COMMON_INTERCEPTOR_HANDLE_VFORK) ++ ++.L_exit: ++ // Restore $ra ++ bl COMMON_INTERCEPTOR_SPILL_AREA ++ ld.d $ra, $a0, 0 ++ // load value by stack ++ ld.d $a0, $sp, 8 ++ // adjust stack ++ addi.d $sp, $sp, 16 ++ jr $ra ++ASM_SIZE(vfork) ++ ++.weak vfork ++.set vfork, ASM_WRAPPER_NAME(vfork) ++ ++#endif +diff --git a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc +index a38b13408..8c1ff7f65 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc ++++ b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc +@@ -2512,7 +2512,7 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { + # if !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \ +- SANITIZER_RISCV64) ++ defined(__loongarch__) || SANITIZER_RISCV64) + if (data) { + if (request == ptrace_setregs) { + PRE_READ((void *)data, struct_user_regs_struct_sz); +@@ -2534,7 +2534,7 @@ POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { + # if !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \ +- SANITIZER_RISCV64) ++ defined(__loongarch__) || SANITIZER_RISCV64) + if (res >= 0 && data) { + // Note that this is different from the interceptor in + // sanitizer_common_interceptors.inc. +diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cpp b/libsanitizer/sanitizer_common/sanitizer_linux.cpp +index aa59d9718..5f745fc26 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_linux.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_linux.cpp +@@ -78,6 +78,10 @@ + #include + #endif + ++#if SANITIZER_LINUX && defined(__loongarch__) ++# include ++#endif ++ + #if SANITIZER_FREEBSD + #include + #include +@@ -180,6 +184,8 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); } + # include "sanitizer_syscall_linux_arm.inc" + # elif SANITIZER_LINUX && defined(__hexagon__) + # include "sanitizer_syscall_linux_hexagon.inc" ++# elif SANITIZER_LINUX && SANITIZER_LOONGARCH64 ++# include "sanitizer_syscall_linux_loongarch64.inc" + # else + # include "sanitizer_syscall_generic.inc" + # endif +@@ -282,6 +288,28 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) { + } + #endif + ++#if SANITIZER_LINUX && defined(__loongarch__) ++static void statx_to_stat(struct statx *in, struct stat *out) { ++ internal_memset(out, 0, sizeof(*out)); ++ out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor); ++ out->st_ino = in->stx_ino; ++ out->st_mode = in->stx_mode; ++ out->st_nlink = in->stx_nlink; ++ out->st_uid = in->stx_uid; ++ out->st_gid = in->stx_gid; ++ out->st_rdev = makedev(in->stx_rdev_major, in->stx_rdev_minor); ++ out->st_size = in->stx_size; ++ out->st_blksize = in->stx_blksize; ++ out->st_blocks = in->stx_blocks; ++ out->st_atime = in->stx_atime.tv_sec; ++ out->st_atim.tv_nsec = in->stx_atime.tv_nsec; ++ out->st_mtime = in->stx_mtime.tv_sec; ++ out->st_mtim.tv_nsec = in->stx_mtime.tv_nsec; ++ out->st_ctime = in->stx_ctime.tv_sec; ++ out->st_ctim.tv_nsec = in->stx_ctime.tv_nsec; ++} ++#endif ++ + #if defined(__mips64) + // Undefine compatibility macros from + // so that they would not clash with the kernel_stat +@@ -336,8 +364,16 @@ uptr internal_stat(const char *path, void *buf) { + #if SANITIZER_FREEBSD + return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); + #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS ++# if defined(__loongarch__) ++ struct statx bufx; ++ int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, ++ AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx); ++ statx_to_stat(&bufx, (struct stat *)buf); ++ return res; ++# else + return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, + 0); ++# endif + #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS + # if defined(__mips64) + // For mips64, stat syscall fills buffer in the format of kernel_stat +@@ -361,8 +397,17 @@ uptr internal_lstat(const char *path, void *buf) { + return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, + AT_SYMLINK_NOFOLLOW); + #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS ++# if defined(__loongarch__) ++ struct statx bufx; ++ int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, ++ AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, ++ STATX_BASIC_STATS, (uptr)&bufx); ++ statx_to_stat(&bufx, (struct stat *)buf); ++ return res; ++# else + return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, + AT_SYMLINK_NOFOLLOW); ++# endif + #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS + # if SANITIZER_MIPS64 + // For mips64, lstat syscall fills buffer in the format of kernel_stat +@@ -389,6 +434,12 @@ uptr internal_fstat(fd_t fd, void *buf) { + int res = internal_syscall(SYSCALL(fstat), fd, &kbuf); + kernel_stat_to_stat(&kbuf, (struct stat *)buf); + return res; ++# elif SANITIZER_LINUX && defined(__loongarch__) ++ struct statx bufx; ++ int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH, ++ STATX_BASIC_STATS, (uptr)&bufx); ++ statx_to_stat(&bufx, (struct stat *)buf); ++ return res; + # else + return internal_syscall(SYSCALL(fstat), fd, (uptr)buf); + # endif +@@ -437,7 +488,7 @@ uptr internal_unlink(const char *path) { + } + + uptr internal_rename(const char *oldpath, const char *newpath) { +-#if defined(__riscv) && defined(__linux__) ++#if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__) + return internal_syscall(SYSCALL(renameat2), AT_FDCWD, (uptr)oldpath, AT_FDCWD, + (uptr)newpath, 0); + #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +@@ -482,7 +533,7 @@ bool FileExists(const char *filename) { + if (ShouldMockFailureToOpen(filename)) + return false; + struct stat st; +-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS ++#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS && !defined(__loongarch__) + if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0)) + #else + if (internal_stat(filename, &st)) +@@ -1032,7 +1083,7 @@ uptr GetMaxVirtualAddress() { + #if SANITIZER_NETBSD && defined(__x86_64__) + return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE) + #elif SANITIZER_WORDSIZE == 64 +-# if defined(__powerpc64__) || defined(__aarch64__) ++# if defined(__powerpc64__) || defined(__aarch64__) || defined(__loongarch__) + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure out which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. +@@ -1040,6 +1091,7 @@ uptr GetMaxVirtualAddress() { + // of the address space, so simply checking the stack address is not enough. + // This should (does) work for both PowerPC64 Endian modes. + // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. ++ // loongarch64 also has multiple address space layouts: default is 47-bit. + return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; + #elif SANITIZER_RISCV64 + return (1ULL << 38) - 1; +@@ -1336,6 +1388,49 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + : "memory", "$29" ); + return res; + } ++ ++#elif SANITIZER_LOONGARCH64 ++uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ++ int *parent_tidptr, void *newtls, int *child_tidptr) { ++ if (!fn || !child_stack) ++ return -EINVAL; ++ ++ CHECK_EQ(0, (uptr)child_stack % 16); ++ ++ register int res __asm__("$a0"); ++ register int __flags __asm__("$a0") = flags; ++ register void *__stack __asm__("$a1") = child_stack; ++ register int *__ptid __asm__("$a2") = parent_tidptr; ++ register int *__ctid __asm__("$a3") = child_tidptr; ++ register void *__tls __asm__("$a4") = newtls; ++ register int (*__fn)(void *) __asm__("$a5") = fn; ++ register void *__arg __asm__("$a6") = arg; ++ register int nr_clone __asm__("$a7") = __NR_clone; ++ ++ __asm__ __volatile__( ++ "syscall 0\n" ++ ++ // if ($a0 != 0) ++ // return $a0; ++ "bnez $a0, 1f\n" ++ ++ // In the child, now. Call "fn(arg)". ++ "move $a0, $a6\n" ++ "jirl $ra, $a5, 0\n" ++ ++ // Call _exit($a0). ++ "addi.d $a7, $zero, %9\n" ++ "syscall 0\n" ++ ++ "1:\n" ++ ++ : "=r"(res) ++ : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls), ++ "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit) ++ : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"); ++ return res; ++} ++ + #elif SANITIZER_RISCV64 + uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { +@@ -1874,6 +1969,13 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { + u64 esr; + if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN; + return esr & ESR_ELx_WNR ? WRITE : READ; ++#elif defined(__loongarch__) ++ u32 flags = ucontext->uc_mcontext.__flags; ++ if (flags & SC_ADDRERR_RD) ++ return SignalContext::READ; ++ if (flags & SC_ADDRERR_WR) ++ return SignalContext::WRITE; ++ return SignalContext::UNKNOWN; + #elif defined(__sparc__) + // Decode the instruction to determine the access type. + // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype). +@@ -2128,6 +2230,11 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.r30; + *sp = ucontext->uc_mcontext.r29; ++# elif defined(__loongarch__) ++ ucontext_t *ucontext = (ucontext_t *)context; ++ *pc = ucontext->uc_mcontext.__pc; ++ *bp = ucontext->uc_mcontext.__gregs[22]; ++ *sp = ucontext->uc_mcontext.__gregs[3]; + # else + # error "Unsupported arch" + # endif +diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h +index 6a235db0e..ee96a3c49 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_linux.h ++++ b/libsanitizer/sanitizer_common/sanitizer_linux.h +@@ -73,7 +73,7 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact); + void internal_sigdelset(__sanitizer_sigset_t *set, int signum); + #if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \ + defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \ +- defined(__arm__) || SANITIZER_RISCV64 ++ defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64 + uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); + #endif +diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp +index 4f22c78a1..d392bdebc 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp +@@ -203,7 +203,8 @@ void InitTlsSize() { + g_use_dlpi_tls_data = + GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25; + +-#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__) ++#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__) || \ ++ defined(__loongarch__) + void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); + size_t tls_align; + ((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align); +@@ -262,6 +263,8 @@ static uptr ThreadDescriptorSizeFallback() { + #elif defined(__mips__) + // TODO(sagarthakur): add more values as per different glibc versions. + val = FIRST_32_SECOND_64(1152, 1776); ++#elif SANITIZER_LOONGARCH64 ++ val = 1856; // from glibc 2.36 + #elif SANITIZER_RISCV64 + int major; + int minor; +@@ -301,12 +304,15 @@ uptr ThreadDescriptorSize() { + return val; + } + +-#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 ++#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \ ++ SANITIZER_LOONGARCH64 + // TlsPreTcbSize includes size of struct pthread_descr and size of tcb + // head structure. It lies before the static tls blocks. + static uptr TlsPreTcbSize() { + #if defined(__mips__) + const uptr kTcbHead = 16; // sizeof (tcbhead_t) ++#elif SANITIZER_LOONGARCH64 ++ const uptr kTcbHead = 16; // sizeof (tcbhead_t) + #elif defined(__powerpc64__) + const uptr kTcbHead = 88; // sizeof (tcbhead_t) + #elif SANITIZER_RISCV64 +@@ -475,6 +481,15 @@ static void GetTls(uptr *addr, uptr *size) { + *addr = reinterpret_cast(__builtin_thread_pointer()) - + ThreadDescriptorSize(); + *size = g_tls_size + ThreadDescriptorSize(); ++#elif SANITIZER_GLIBC && defined(__loongarch__) ++# ifdef __clang__ ++ *addr = reinterpret_cast(__builtin_thread_pointer()) - ++ ThreadDescriptorSize(); ++# else ++ asm("or %0,$tp,$zero" : "=r"(*addr)); ++ *addr -= ThreadDescriptorSize(); ++# endif ++ *size = g_tls_size + ThreadDescriptorSize(); + #elif SANITIZER_GLIBC && defined(__powerpc64__) + // Workaround for glibc<2.25(?). 2.27 is known to not need this. + uptr tp; +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h +index 3153de34e..daf8b4ab0 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform.h ++++ b/libsanitizer/sanitizer_common/sanitizer_platform.h +@@ -277,12 +277,18 @@ + #define SANITIZER_SIGN_EXTENDED_ADDRESSES 0 + #endif + ++#if defined(__loongarch_lp64) ++# define SANITIZER_LOONGARCH64 1 ++#else ++# define SANITIZER_LOONGARCH64 0 ++#endif ++ + // The AArch64 and RISC-V linux ports use the canonical syscall set as + // mandated by the upstream linux community for all new ports. Other ports + // may still use legacy syscalls. + #ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +-# if (defined(__aarch64__) || defined(__riscv) || defined(__hexagon__)) && \ +- SANITIZER_LINUX ++# if (defined(__aarch64__) || defined(__riscv) || defined(__hexagon__) || \ ++ defined(__loongarch__)) && SANITIZER_LINUX + # define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1 + # else + # define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0 +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +index 14610f2df..afbb47f74 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +@@ -271,7 +271,7 @@ + #if SI_LINUX_NOT_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ +- defined(__s390__) || SANITIZER_RISCV64) ++ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64) + #define SANITIZER_INTERCEPT_PTRACE 1 + #else + #define SANITIZER_INTERCEPT_PTRACE 0 +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp +index 2b1a2f793..9af142feb 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp +@@ -68,7 +68,8 @@ namespace __sanitizer { + + # if !defined(__powerpc64__) && !defined(__x86_64__) && \ + !defined(__aarch64__) && !defined(__mips__) && !defined(__s390__) && \ +- !defined(__sparc__) && !defined(__riscv) && !defined(__hexagon__) ++ !defined(__sparc__) && !defined(__riscv) && !defined(__hexagon__) && \ ++ !defined(__loongarch__) + COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat)); + #endif + +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp +index c335f33dd..a565d09ff 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp +@@ -94,7 +94,7 @@ + # include + # include + # if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \ +- defined(__hexagon__) || SANITIZER_RISCV64 ++ defined(__hexagon__) || defined(__loongarch__) || SANITIZER_RISCV64 + # include + # ifdef __arm__ + typedef struct user_fpregs elf_fpregset_t; +@@ -248,6 +248,10 @@ namespace __sanitizer { + defined(__powerpc__) || defined(__s390__) || defined(__sparc__) || \ + defined(__hexagon__) + # define SIZEOF_STRUCT_USTAT 20 ++# elif defined(__loongarch__) ++ // Not used. The minimum Glibc version available for LoongArch is 2.36 ++ // so ustat() wrapper is already gone. ++# define SIZEOF_STRUCT_USTAT 0 + # else + # error Unknown size of struct ustat + # endif +@@ -322,7 +326,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); + #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ +- defined(__s390__) || SANITIZER_RISCV64) ++ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64) + #if defined(__mips64) || defined(__powerpc64__) || defined(__arm__) + unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); + unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); +@@ -332,6 +336,9 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); + #elif defined(__aarch64__) + unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs); + unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state); ++#elif defined(__loongarch__) ++ unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs); ++ unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fp_state); + #elif defined(__s390__) + unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct); + unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct); +@@ -341,7 +348,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); + #endif // __mips64 || __powerpc64__ || __aarch64__ + #if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \ + defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \ +- SANITIZER_RISCV64 ++ defined(__loongarch__) || SANITIZER_RISCV64 + unsigned struct_user_fpxregs_struct_sz = 0; + #else + unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +index da53b5abe..532814c99 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +@@ -99,6 +99,9 @@ const unsigned struct_kernel_stat64_sz = 144; + const unsigned struct___old_kernel_stat_sz = 0; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 104; ++# elif defined(__loongarch__) ++const unsigned struct_kernel_stat_sz = 128; ++const unsigned struct_kernel_stat64_sz = 0; + #elif SANITIZER_RISCV64 + const unsigned struct_kernel_stat_sz = 128; + const unsigned struct_kernel_stat64_sz = 0; // RISCV64 does not use stat64 +@@ -125,7 +128,7 @@ const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long); + + #if SANITIZER_LINUX + +-#if defined(__powerpc64__) || defined(__s390__) ++#if defined(__powerpc64__) || defined(__s390__) || defined(__loongarch__) + const unsigned struct___old_kernel_stat_sz = 0; + #elif !defined(__sparc__) + const unsigned struct___old_kernel_stat_sz = 32; +@@ -820,7 +823,7 @@ typedef void __sanitizer_FILE; + #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ +- defined(__s390__) || SANITIZER_RISCV64) ++ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64) + extern unsigned struct_user_regs_struct_sz; + extern unsigned struct_user_fpregs_struct_sz; + extern unsigned struct_user_fpxregs_struct_sz; +diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp +index 5a12422fc..42182d88e 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp +@@ -127,7 +127,7 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, + #endif + #elif defined(__s390__) + uhwptr pc1 = frame[14]; +-#elif defined(__riscv) ++#elif defined(__riscv) || defined(__loongarch__) + // frame[-1] contains the return address + uhwptr pc1 = frame[-1]; + #else +@@ -142,7 +142,7 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, + trace_buffer[size++] = (uptr) pc1; + } + bottom = (uptr)frame; +-#if defined(__riscv) ++#if defined(__riscv) || defined(__loongarch__) + // frame[-2] contain fp of the previous frame + uptr new_bp = (uptr)frame[-2]; + #else +diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +index 403bda117..3efe8efb6 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +@@ -16,7 +16,7 @@ + #if SANITIZER_LINUX && \ + (defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \ + defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \ +- defined(__arm__) || SANITIZER_RISCV64) ++ defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64) + + #include "sanitizer_stoptheworld.h" + +@@ -31,7 +31,8 @@ + #include // for pid_t + #include // for iovec + #include // for NT_PRSTATUS +-#if (defined(__aarch64__) || SANITIZER_RISCV64) && !SANITIZER_ANDROID ++#if (defined(__aarch64__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64) && \ ++ !SANITIZER_ANDROID + // GLIBC 2.20+ sys/user does not include asm/ptrace.h + # include + #endif +@@ -500,6 +501,12 @@ static constexpr uptr kExtraRegs[] = {NT_X86_XSTATE, NT_FPREGSET}; + typedef pt_regs regs_struct; + #define REG_SP gpr[PT_R1] + ++#elif defined(__loongarch__) ++typedef struct user_pt_regs regs_struct; ++#define REG_SP regs[3] ++static constexpr uptr kExtraRegs[] = {0}; ++#define ARCH_IOVEC_FOR_GETREGSET ++ + #elif defined(__mips__) + typedef struct user regs_struct; + # if SANITIZER_ANDROID +diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +index 3fc994fd3..bc24430d8 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp ++++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +@@ -261,6 +261,8 @@ class LLVMSymbolizerProcess final : public SymbolizerProcess { + const char* const kSymbolizerArch = "--default-arch=i386"; + #elif SANITIZER_RISCV64 + const char *const kSymbolizerArch = "--default-arch=riscv64"; ++#elif SANITIZER_LOONGARCH64 ++ const char *const kSymbolizerArch = "--default-arch=loongarch64"; + #elif defined(__aarch64__) + const char* const kSymbolizerArch = "--default-arch=arm64"; + #elif defined(__arm__) +diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc +new file mode 100644 +index 000000000..80f5e6be8 +--- /dev/null ++++ b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc +@@ -0,0 +1,171 @@ ++//===-- sanitizer_syscall_linux_loongarch64.inc -----------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// Implementations of internal_syscall and internal_iserror for ++// Linux/loongarch64. ++// ++//===----------------------------------------------------------------------===// ++ ++// About local register variables: ++// https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables ++// ++// Kernel ABI: ++// https://lore.kernel.org/loongarch/1f353678-3398-e30b-1c87-6edb278f74db@xen0n.name/T/#m1613bc86c2d7bf5f6da92bd62984302bfd699a2f ++// syscall number is placed in a7 ++// parameters, if present, are placed in a0-a6 ++// upon return: ++// the return value is placed in a0 ++// t0-t8 should be considered clobbered ++// all other registers are preserved ++#define SYSCALL(name) __NR_##name ++ ++#define INTERNAL_SYSCALL_CLOBBERS \ ++ "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8" ++ ++static uptr __internal_syscall(u64 nr) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0"); ++ __asm__ volatile("syscall 0\n\t" ++ : "=r"(a0) ++ : "r"(a7) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall0(n) (__internal_syscall)(n) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall1(n, a1) (__internal_syscall)(n, (u64)(a1)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall2(n, a1, a2) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ register u64 a2 asm("$a2") = arg3; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1), "r"(a2) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall3(n, a1, a2, a3) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, ++ u64 arg4) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ register u64 a2 asm("$a2") = arg3; ++ register u64 a3 asm("$a3") = arg4; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1), "r"(a2), "r"(a3) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall4(n, a1, a2, a3, a4) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4, ++ long arg5) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ register u64 a2 asm("$a2") = arg3; ++ register u64 a3 asm("$a3") = arg4; ++ register u64 a4 asm("$a4") = arg5; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall5(n, a1, a2, a3, a4, a5) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \ ++ (u64)(a5)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4, ++ long arg5, long arg6) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ register u64 a2 asm("$a2") = arg3; ++ register u64 a3 asm("$a3") = arg4; ++ register u64 a4 asm("$a4") = arg5; ++ register u64 a5 asm("$a5") = arg6; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \ ++ (u64)(a5), (long)(a6)) ++ ++static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4, ++ long arg5, long arg6, long arg7) { ++ register u64 a7 asm("$a7") = nr; ++ register u64 a0 asm("$a0") = arg1; ++ register u64 a1 asm("$a1") = arg2; ++ register u64 a2 asm("$a2") = arg3; ++ register u64 a3 asm("$a3") = arg4; ++ register u64 a4 asm("$a4") = arg5; ++ register u64 a5 asm("$a5") = arg6; ++ register u64 a6 asm("$a6") = arg7; ++ __asm__ volatile("syscall 0\n\t" ++ : "+r"(a0) ++ : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), ++ "r"(a6) ++ : INTERNAL_SYSCALL_CLOBBERS); ++ return a0; ++} ++#define __internal_syscall7(n, a1, a2, a3, a4, a5, a6, a7) \ ++ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \ ++ (u64)(a5), (long)(a6), (long)(a7)) ++ ++#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n ++#define __SYSCALL_NARGS(...) \ ++ __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, ) ++#define __SYSCALL_CONCAT_X(a, b) a##b ++#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b) ++#define __SYSCALL_DISP(b, ...) \ ++ __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) ++ ++#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__) ++ ++// Helper function used to avoid clobbering of errno. ++bool internal_iserror(uptr retval, int *internal_errno) { ++ if (retval >= (uptr)-4095) { ++ if (internal_errno) ++ *internal_errno = -retval; ++ return true; ++ } ++ return false; ++} +diff --git a/libsanitizer/tsan/tsan_interceptors_posix.cpp b/libsanitizer/tsan/tsan_interceptors_posix.cpp +index 9a85ee00d..ada8fd031 100644 +--- a/libsanitizer/tsan/tsan_interceptors_posix.cpp ++++ b/libsanitizer/tsan/tsan_interceptors_posix.cpp +@@ -76,6 +76,8 @@ struct ucontext_t { + #define PTHREAD_ABI_BASE "GLIBC_2.3.2" + #elif defined(__aarch64__) || SANITIZER_PPC64V2 + #define PTHREAD_ABI_BASE "GLIBC_2.17" ++#elif SANITIZER_LOONGARCH64 ++#define PTHREAD_ABI_BASE "GLIBC_2.36" + #endif + + extern "C" int pthread_attr_init(void *attr); +diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h +index 7ff0acace..c3fbda98f 100644 +--- a/libsanitizer/tsan/tsan_platform.h ++++ b/libsanitizer/tsan/tsan_platform.h +@@ -192,6 +192,38 @@ struct MappingAarch64_39 { + static const uptr kVdsoBeg = 0x7f00000000ull; + }; + ++/* C/C++ on linux/loongarch64 (47-bit VMA) ++0000 0000 4000 - 0080 0000 0000: main binary ++0080 0000 0000 - 0100 0000 0000: - ++0100 0000 0000 - 1000 0000 0000: shadow memory ++1000 0000 0000 - 3000 0000 0000: - ++3000 0000 0000 - 3400 0000 0000: metainfo ++3400 0000 0000 - 5555 0000 0000: - ++5555 0000 0000 - 5556 0000 0000: main binary (PIE) ++5556 0000 0000 - 7ffe 0000 0000: - ++7ffe 0000 0000 - 7fff 0000 0000: heap ++7fff 0000 0000 - 7fff 8000 0000: - ++7fff 8000 0000 - 8000 0000 0000: modules and main thread stack ++*/ ++struct MappingLoongArch64_47 { ++ static const uptr kMetaShadowBeg = 0x300000000000ull; ++ static const uptr kMetaShadowEnd = 0x340000000000ull; ++ static const uptr kShadowBeg = 0x010000000000ull; ++ static const uptr kShadowEnd = 0x100000000000ull; ++ static const uptr kHeapMemBeg = 0x7ffe00000000ull; ++ static const uptr kHeapMemEnd = 0x7fff00000000ull; ++ static const uptr kLoAppMemBeg = 0x000000004000ull; ++ static const uptr kLoAppMemEnd = 0x008000000000ull; ++ static const uptr kMidAppMemBeg = 0x555500000000ull; ++ static const uptr kMidAppMemEnd = 0x555600000000ull; ++ static const uptr kHiAppMemBeg = 0x7fff80000000ull; ++ static const uptr kHiAppMemEnd = 0x800000000000ull; ++ static const uptr kShadowMsk = 0x780000000000ull; ++ static const uptr kShadowXor = 0x040000000000ull; ++ static const uptr kShadowAdd = 0x000000000000ull; ++ static const uptr kVdsoBeg = 0x7fffffffc000ull; ++}; ++ + /* + C/C++ on linux/aarch64 (42-bit VMA) + 00000 0010 00 - 01000 0000 00: main binary +@@ -670,6 +702,8 @@ ALWAYS_INLINE auto SelectMapping(Arg arg) { + case 47: + return Func::template Apply(arg); + } ++# elif SANITIZER_LOONGARCH64 ++ return Func::template Apply(arg); + # elif defined(__mips64) + return Func::template Apply(arg); + # elif defined(__s390x__) +@@ -689,6 +723,7 @@ void ForEachMapping() { + Func::template Apply(); + Func::template Apply(); + Func::template Apply(); ++ Func::template Apply(); + Func::template Apply(); + Func::template Apply(); + Func::template Apply(); +diff --git a/libsanitizer/tsan/tsan_platform_linux.cpp b/libsanitizer/tsan/tsan_platform_linux.cpp +index 73ec14892..3a51d3c36 100644 +--- a/libsanitizer/tsan/tsan_platform_linux.cpp ++++ b/libsanitizer/tsan/tsan_platform_linux.cpp +@@ -66,7 +66,8 @@ extern "C" void *__libc_stack_end; + void *__libc_stack_end = 0; + #endif + +-#if SANITIZER_LINUX && defined(__aarch64__) && !SANITIZER_GO ++#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) && \ ++ !SANITIZER_GO + # define INIT_LONGJMP_XOR_KEY 1 + #else + # define INIT_LONGJMP_XOR_KEY 0 +@@ -242,6 +243,14 @@ void InitializePlatformEarly() { + Die(); + } + #endif ++#elif SANITIZER_LOONGARCH64 ++# if !SANITIZER_GO ++ if (vmaSize != 47) { ++ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); ++ Printf("FATAL: Found %zd - Supported 47\n", vmaSize); ++ Die(); ++ } ++# endif + #elif defined(__powerpc64__) + # if !SANITIZER_GO + if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) { +@@ -302,7 +311,7 @@ void InitializePlatform() { + SetAddressSpaceUnlimited(); + reexec = true; + } +-#if SANITIZER_LINUX && defined(__aarch64__) ++#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) + // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in + // linux kernel, the random gap between stack and mapped area is increased + // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover +@@ -387,6 +396,8 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) { + # else + return mangled_sp; + # endif ++#elif defined(__loongarch_lp64) ++ return mangled_sp ^ longjmp_xor_key; + #elif defined(__powerpc64__) + // Reverse of: + // ld r4, -28696(r13) +@@ -418,6 +429,8 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) { + #elif SANITIZER_LINUX + # ifdef __aarch64__ + # define LONG_JMP_SP_ENV_SLOT 13 ++# elif defined(__loongarch__) ++# define LONG_JMP_SP_ENV_SLOT 1 + # elif defined(__mips64) + # define LONG_JMP_SP_ENV_SLOT 1 + # elif defined(__s390x__) +@@ -444,7 +457,11 @@ static void InitializeLongjmpXorKey() { + + // 2. Retrieve vanilla/mangled SP. + uptr sp; ++#ifdef __loongarch__ ++ asm("move %0, $sp" : "=r" (sp)); ++#else + asm("mov %0, sp" : "=r" (sp)); ++#endif + uptr mangled_sp = ((uptr *)&env)[LONG_JMP_SP_ENV_SLOT]; + + // 3. xor SPs to obtain key. +diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h +index eab837042..e39709a17 100644 +--- a/libsanitizer/tsan/tsan_rtl.h ++++ b/libsanitizer/tsan/tsan_rtl.h +@@ -55,7 +55,8 @@ namespace __tsan { + + #if !SANITIZER_GO + struct MapUnmapCallback; +-#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__) ++#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__) || \ ++ defined(__loongarch__) + + struct AP32 { + static const uptr kSpaceBeg = 0; +diff --git a/libsanitizer/tsan/tsan_rtl_loongarch64.S b/libsanitizer/tsan/tsan_rtl_loongarch64.S +new file mode 100644 +index 000000000..12856bd11 +--- /dev/null ++++ b/libsanitizer/tsan/tsan_rtl_loongarch64.S +@@ -0,0 +1,196 @@ ++#include "sanitizer_common/sanitizer_asm.h" ++ ++.section .text ++ ++ASM_HIDDEN(__tsan_setjmp) ++.comm _ZN14__interception11real_setjmpE,8,8 ++.globl ASM_SYMBOL_INTERCEPTOR(setjmp) ++ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(setjmp)) ++ASM_SYMBOL_INTERCEPTOR(setjmp): ++ CFI_STARTPROC ++ ++ // Save frame pointer and return address register ++ addi.d $sp, $sp, -32 ++ st.d $ra, $sp, 24 ++ st.d $fp, $sp, 16 ++ CFI_DEF_CFA_OFFSET (32) ++ CFI_OFFSET (1, -8) ++ CFI_OFFSET (22, -16) ++ ++ // Adjust the SP for previous frame ++ addi.d $fp, $sp, 32 ++ CFI_DEF_CFA_REGISTER (22) ++ ++ // Save env parameter ++ st.d $a0, $sp, 8 ++ CFI_OFFSET (4, -24) ++ ++ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)` ++ addi.d $a0, $fp, 0 ++ ++ // call tsan interceptor ++ bl ASM_SYMBOL(__tsan_setjmp) ++ ++ // Restore env parameter ++ ld.d $a0, $sp, 8 ++ CFI_RESTORE (4) ++ ++ // Restore frame/link register ++ ld.d $fp, $sp, 16 ++ ld.d $ra, $sp, 24 ++ addi.d $sp, $sp, 32 ++ CFI_RESTORE (22) ++ CFI_RESTORE (1) ++ CFI_DEF_CFA (3, 0) ++ ++ // tail jump to libc setjmp ++ la.local $a1, _ZN14__interception11real_setjmpE ++ ld.d $a1, $a1, 0 ++ jr $a1 ++ ++ CFI_ENDPROC ++ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(setjmp)) ++ ++.comm _ZN14__interception12real__setjmpE,8,8 ++.globl ASM_SYMBOL_INTERCEPTOR(_setjmp) ++ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(_setjmp)) ++ASM_SYMBOL_INTERCEPTOR(_setjmp): ++ CFI_STARTPROC ++ ++ // Save frame pointer and return address register ++ addi.d $sp, $sp, -32 ++ st.d $ra, $sp, 24 ++ st.d $fp, $sp, 16 ++ CFI_DEF_CFA_OFFSET (32) ++ CFI_OFFSET (1, -8) ++ CFI_OFFSET (22, -16) ++ ++ // Adjust the SP for previous frame ++ addi.d $fp, $sp, 32 ++ CFI_DEF_CFA_REGISTER (22) ++ ++ // Save env parameter ++ st.d $a0, $sp, 8 ++ CFI_OFFSET (4, -24) ++ ++ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)` ++ addi.d $a0, $fp, 0 ++ ++ // call tsan interceptor ++ bl ASM_SYMBOL(__tsan_setjmp) ++ ++ // Restore env parameter ++ ld.d $a0, $sp, 8 ++ CFI_RESTORE (4) ++ ++ // Restore frame/link register ++ ld.d $fp, $sp, 16 ++ ld.d $ra, $sp, 24 ++ addi.d $sp, $sp, 32 ++ CFI_RESTORE (22) ++ CFI_RESTORE (1) ++ CFI_DEF_CFA (3, 0) ++ ++ // tail jump to libc setjmp ++ la.local $a1, _ZN14__interception12real__setjmpE ++ ld.d $a1, $a1, 0 ++ jr $a1 ++ ++ CFI_ENDPROC ++ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(_setjmp)) ++ ++.comm _ZN14__interception14real_sigsetjmpE,8,8 ++.globl ASM_SYMBOL_INTERCEPTOR(sigsetjmp) ++ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(sigsetjmp)) ++ASM_SYMBOL_INTERCEPTOR(sigsetjmp): ++ CFI_STARTPROC ++ ++ // Save frame pointer and return address register ++ addi.d $sp, $sp, -32 ++ st.d $ra, $sp, 24 ++ st.d $fp, $sp, 16 ++ CFI_DEF_CFA_OFFSET (32) ++ CFI_OFFSET (1, -8) ++ CFI_OFFSET (22, -16) ++ ++ // Adjust the SP for previous frame ++ addi.d $fp, $sp, 32 ++ CFI_DEF_CFA_REGISTER (22) ++ ++ // Save env parameter ++ st.d $a0, $sp, 8 ++ CFI_OFFSET (4, -24) ++ ++ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)` ++ addi.d $a0, $fp, 0 ++ ++ // call tsan interceptor ++ bl ASM_SYMBOL(__tsan_setjmp) ++ ++ // Restore env parameter ++ ld.d $a0, $sp, 8 ++ CFI_RESTORE (4) ++ ++ // Restore frame/link register ++ ld.d $fp, $sp, 16 ++ ld.d $ra, $sp, 24 ++ addi.d $sp, $sp, 32 ++ CFI_RESTORE (22) ++ CFI_RESTORE (1) ++ CFI_DEF_CFA (3, 0) ++ ++ // tail jump to libc setjmp ++ la.local $a1, _ZN14__interception14real_sigsetjmpE ++ ld.d $a1, $a1, 0 ++ jr $a1 ++ ++ CFI_ENDPROC ++ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(sigsetjmp)) ++ ++.comm _ZN14__interception16real___sigsetjmpE,8,8 ++.globl ASM_SYMBOL_INTERCEPTOR(__sigsetjmp) ++ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp)) ++ASM_SYMBOL_INTERCEPTOR(__sigsetjmp): ++ CFI_STARTPROC ++ ++ // Save frame pointer and return address register ++ addi.d $sp, $sp, -32 ++ st.d $ra, $sp, 24 ++ st.d $fp, $sp, 16 ++ CFI_DEF_CFA_OFFSET (32) ++ CFI_OFFSET (1, -8) ++ CFI_OFFSET (22, -16) ++ ++ // Adjust the SP for previous frame ++ addi.d $fp, $sp, 32 ++ CFI_DEF_CFA_REGISTER (22) ++ ++ // Save env parameter ++ st.d $a0, $sp, 8 ++ CFI_OFFSET (4, -24) ++ ++ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)` ++ addi.d $a0, $fp, 0 ++ ++ // call tsan interceptor ++ bl ASM_SYMBOL(__tsan_setjmp) ++ ++ // Restore env parameter ++ ld.d $a0, $sp, 8 ++ CFI_RESTORE (4) ++ ++ // Restore frame/link register ++ ld.d $fp, $sp, 16 ++ ld.d $ra, $sp, 24 ++ addi.d $sp, $sp, 32 ++ CFI_RESTORE (22) ++ CFI_RESTORE (1) ++ CFI_DEF_CFA (3, 0) ++ ++ // tail jump to libc setjmp ++ la.local $a1, _ZN14__interception16real___sigsetjmpE ++ ld.d $a1, $a1, 0 ++ jr $a1 ++ ++ CFI_ENDPROC ++ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp)) +diff --git a/libvtv/configure.tgt b/libvtv/configure.tgt +index aa2a3f675..6cdd1e97a 100644 +--- a/libvtv/configure.tgt ++++ b/libvtv/configure.tgt +@@ -50,6 +50,9 @@ case "${target}" in + ;; + x86_64-*-darwin[1]* | i?86-*-darwin[1]*) + ;; ++ loongarch*-*-linux*) ++ VTV_SUPPORTED=yes ++ ;; + *) + ;; + esac +-- +2.33.0 + diff --git a/0002-libjccjit-do-not-link-objects-contained-same-element.patch b/0002-libjccjit-do-not-link-objects-contained-same-element.patch new file mode 100644 index 0000000..5cc09d0 --- /dev/null +++ b/0002-libjccjit-do-not-link-objects-contained-same-element.patch @@ -0,0 +1,43 @@ +From 28a1b49dfaca2956c4d0cb3a26613d6c586afe86 Mon Sep 17 00:00:00 2001 +From: Peng Fan +Date: Fri, 28 Jul 2023 09:58:05 +0800 +Subject: [PATCH] libjccjit: do not link objects contained same element + +Signed-off-by: Peng Fan +--- + gcc/jit/Make-lang.in | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in +index 6e10abfd0..7f53304d9 100644 +--- a/gcc/jit/Make-lang.in ++++ b/gcc/jit/Make-lang.in +@@ -157,18 +157,23 @@ LIBGCCJIT_EXTRA_OPTS = $(LIBGCCJIT_VERSION_SCRIPT_OPTION) \ + endif + endif + ++# Only link objects from $(EXTRA_GCC_OBJS) that's not already ++# included in libbackend.a ($(EXTRA_OBJS)). ++EXTRA_GCC_OBJS_EXCLUSIVE = $(foreach _obj1, $(EXTRA_GCC_OBJS), \ ++ $(if $(filter $(_obj1), $(EXTRA_OBJS)),, $(_obj1))) ++ + # We avoid using $(BACKEND) from Makefile.in in order to avoid pulling + # in main.o + $(LIBGCCJIT_FILENAME): $(jit_OBJS) \ + libbackend.a libcommon-target.a libcommon.a \ + $(CPPLIB) $(LIBDECNUMBER) \ + $(LIBDEPS) $(srcdir)/jit/libgccjit.map \ +- $(EXTRA_GCC_OBJS) $(jit.prev) ++ $(EXTRA_GCC_OBJS_EXCLUSIVE) $(jit.prev) + @$(call LINK_PROGRESS,$(INDEX.jit),start) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \ + $(jit_OBJS) libbackend.a libcommon-target.a libcommon.a \ + $(CPPLIB) $(LIBDECNUMBER) $(EXTRA_GCC_LIBS) $(LIBS) $(BACKENDLIBS) \ +- $(EXTRA_GCC_OBJS) \ ++ $(EXTRA_GCC_OBJS_EXCLUSIVE) \ + $(LIBGCCJIT_EXTRA_OPTS) + @$(call LINK_PROGRESS,$(INDEX.jit),end) + +-- +2.33.0 + diff --git a/gcc.spec b/gcc.spec index b6d5036..6886466 100644 --- a/gcc.spec +++ b/gcc.spec @@ -1,4 +1,4 @@ -%define anolis_release 2 +%define anolis_release 3 %global DATE 20221121 %global gitrev b3f5a0d53b84ed27cf00cfa2b9c3e2c78935c07d @@ -116,6 +116,11 @@ Patch101: gcc12-fortran-flogical-as-integer.patch Patch102: gcc12-fortran-fdec-override-kind.patch Patch103: gcc12-fortran-fdec-non-logical-if.patch +%ifarch loongarch64 +Patch104: 0001-LoongArch-backports-from-upstream.patch +Patch105: 0002-libjccjit-do-not-link-objects-contained-same-element.patch +%endif + %if %{build_go} %global __os_install_post \ chmod 644 %{buildroot}%{_prefix}/%{_lib}/libgo.so.21.* \ @@ -628,6 +633,10 @@ The %{name}-doc package contains documentation files for %{name}. %patch101 -p1 %patch102 -p1 %patch103 -p1 +%ifarch loongarch64 +%patch104 -p1 +%patch105 -p1 +%endif rm -f libphobos/testsuite/libphobos.gc/forkgc2.d @@ -2059,6 +2068,10 @@ end %changelog +* Tue Aug 8 2023 Peng Fan - 12.2.1-3 +- LoongArch: add new symbol type and backports. +- Last upstream commit: 5430c86e71927492399129f3df80824c6c334ddf + * Sat Apr 15 2023 Chunmei Xu - 12.2.1-2 - optmise spec file -- Gitee