Add function attributes support for LoongArch.

Currently, the following items are supported:

        __attribute__ ((target ("{no-}strict-align")))
        __attribute__ ((target ("cmodel=")))
        __attribute__ ((target ("arch=")))
        __attribute__ ((target ("tune=")))
        __attribute__ ((target ("{no-}lsx")))
        __attribute__ ((target ("{no-}lasx")))

This implementation is derived from AArch64.

gcc/ChangeLog:

        * attr-urls.def: Regenerate.
        * config.gcc: Add loongarch-target-attr.o to extra_objs.
        * config/loongarch/loongarch-protos.h
        (loongarch_option_valid_attribute_p): Function declaration.
        (loongarch_option_override_internal): Likewise.
        * config/loongarch/loongarch.cc
        (loongarch_option_override_internal): Delete the modifications
        to target_option_default_node and target_option_current_node.
        (loongarch_set_current_function): Add annotation information.
        (loongarch_option_override): add assignment operations to
        target_option_default_node and target_option_current_node.
        (TARGET_OPTION_VALID_ATTRIBUTE_P): Define.
        * config/loongarch/t-loongarch: Add compilation of target file
        loongarch-target-attr.o.
        * doc/extend.texi: Add description information of LoongArch
        Function Attributes.
        * config/loongarch/loongarch-target-attr.cc: New file.

gcc/testsuite/ChangeLog:

        * gcc.target/loongarch/arch-func-attr-1.c: New test.
        * gcc.target/loongarch/cmodel-func-attr-1.c: New test.
        * gcc.target/loongarch/lasx-func-attr-1.c: New test.
        * gcc.target/loongarch/lasx-func-attr-2.c: New test.
        * gcc.target/loongarch/lsx-func-attr-1.c: New test.
        * gcc.target/loongarch/lsx-func-attr-2.c: New test.
        * gcc.target/loongarch/strict_align-func-attr-1.c: New test.
        * gcc.target/loongarch/strict_align-func-attr-2.c: New test.
        * gcc.target/loongarch/vector-func-attr-1.c: New test.
        * gcc.target/loongarch/attr-check-error-message.c: New test.

---
 gcc/attr-urls.def                             |   6 +
 gcc/config.gcc                                |   2 +-
 gcc/config/loongarch/loongarch-protos.h       |   2 +
 gcc/config/loongarch/loongarch-target-attr.cc | 413 ++++++++++++++++++
 gcc/config/loongarch/loongarch.cc             |  26 +-
 gcc/config/loongarch/t-loongarch              |   6 +
 gcc/doc/extend.texi                           |  49 +++
 .../gcc.target/loongarch/arch-func-attr-1.c   |  16 +
 .../loongarch/attr-check-error-message.c      |  30 ++
 .../gcc.target/loongarch/cmodel-func-attr-1.c |  17 +
 .../gcc.target/loongarch/lasx-func-attr-1.c   |  15 +
 .../gcc.target/loongarch/lasx-func-attr-2.c   |  12 +
 .../gcc.target/loongarch/lsx-func-attr-1.c    |  15 +
 .../gcc.target/loongarch/lsx-func-attr-2.c    |  12 +
 .../loongarch/strict_align-func-attr-1.c      |  17 +
 .../loongarch/strict_align-func-attr-2.c      |  17 +
 .../gcc.target/loongarch/vector-func-attr-1.c |  15 +
 17 files changed, 665 insertions(+), 5 deletions(-)
 create mode 100644 gcc/config/loongarch/loongarch-target-attr.cc
 create mode 100644 gcc/testsuite/gcc.target/loongarch/arch-func-attr-1.c
 create mode 100644 
gcc/testsuite/gcc.target/loongarch/attr-check-error-message.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/cmodel-func-attr-1.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/lasx-func-attr-1.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/lasx-func-attr-2.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/lsx-func-attr-1.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/lsx-func-attr-2.c
 create mode 100644 
gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-1.c
 create mode 100644 
gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-2.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/vector-func-attr-1.c

diff --git a/gcc/attr-urls.def b/gcc/attr-urls.def
index e8417cff43c..0d27400d218 100644
--- a/gcc/attr-urls.def
+++ b/gcc/attr-urls.def
@@ -18,6 +18,7 @@ const attr_url_entry function_attrs[] = {
  { "amdgpu_hsa_kernel", 
"gcc/AMD-GCN-Function-Attributes.html#index-amdgpu_005fhsa_005fkernel-function-attribute_002c-AMD-GCN",
 "AMD GCN", 17},
  { "arch=", 
"gcc/AArch64-Function-Attributes.html#index-arch_003d-function-attribute_002c-AArch64",
 "AArch64", 5},
  { "arch=", 
"gcc/ARM-Function-Attributes.html#index-arch_003d-function-attribute_002c-ARM", 
"ARM", 5},
+ { "arch=", 
"gcc/LoongArch-Function-Attributes.html#index-arch_003d-function-attribute_002c-LoongArch",
 "LoongArch", 5},
  { "arch=", 
"gcc/RISC-V-Function-Attributes.html#index-arch_003d-function-attribute_002c-RISC-V",
 "RISC-V", 5},
  { "artificial", 
"gcc/Common-Function-Attributes.html#index-artificial-function-attribute", "", 
10},
  { "assume_aligned", 
"gcc/Common-Function-Attributes.html#index-assume_005faligned-function-attribute",
 "", 14},
@@ -29,6 +30,7 @@ const attr_url_entry function_attrs[] = {
  { "cdecl", 
"gcc/x86-Function-Attributes.html#index-cdecl-function-attribute_002c-x86-32", 
"x86-32", 5},
  { "cf_check", 
"gcc/x86-Function-Attributes.html#index-cf_005fcheck-function-attribute_002c-x86",
 "x86", 8},
  { "cmodel=", 
"gcc/AArch64-Function-Attributes.html#index-cmodel_003d-function-attribute_002c-AArch64",
 "AArch64", 7},
+ { "cmodel=", 
"gcc/LoongArch-Function-Attributes.html#index-cmodel_003d-function-attribute_002c-LoongArch",
 "LoongArch", 7},
  { "code_readable", 
"gcc/MIPS-Function-Attributes.html#index-code_005freadable-function-attribute_002c-MIPS",
 "MIPS", 13},
  { "cold", 
"gcc/Common-Function-Attributes.html#index-cold-function-attribute", "", 4},
  { "const", 
"gcc/Common-Function-Attributes.html#index-const-function-attribute", "", 5},
@@ -113,6 +115,7 @@ const attr_url_entry function_attrs[] = {
  { "kspisusp", 
"gcc/Blackfin-Function-Attributes.html#index-kspisusp-function-attribute_002c-Blackfin",
 "Blackfin", 8},
  { "l1_text", 
"gcc/Blackfin-Function-Attributes.html#index-l1_005ftext-function-attribute_002c-Blackfin",
 "Blackfin", 7},
  { "l2", 
"gcc/Blackfin-Function-Attributes.html#index-l2-function-attribute_002c-Blackfin",
 "Blackfin", 2},
+ { "lasx", 
"gcc/LoongArch-Function-Attributes.html#index-lasx-function-attribute_002c-LoongArch",
 "LoongArch", 4},
  { "leaf", 
"gcc/Common-Function-Attributes.html#index-leaf-function-attribute", "", 4},
  { "long_call", 
"gcc/ARC-Function-Attributes.html#index-long_005fcall-function-attribute_002c-ARC",
 "ARC", 9},
  { "long_call", 
"gcc/ARM-Function-Attributes.html#index-long_005fcall-function-attribute_002c-ARM",
 "ARM", 9},
@@ -121,6 +124,7 @@ const attr_url_entry function_attrs[] = {
  { "longcall", 
"gcc/Blackfin-Function-Attributes.html#index-longcall-function-attribute_002c-Blackfin",
 "Blackfin", 8},
  { "longcall", 
"gcc/PowerPC-Function-Attributes.html#index-longcall-function-attribute_002c-PowerPC",
 "PowerPC", 8},
  { "lower", 
"gcc/MSP430-Function-Attributes.html#index-lower-function-attribute_002c-MSP430",
 "MSP430", 5},
+ { "lsx", 
"gcc/LoongArch-Function-Attributes.html#index-lsx-function-attribute_002c-LoongArch",
 "LoongArch", 3},
  { "malloc", 
"gcc/Common-Function-Attributes.html#index-malloc-function-attribute", "", 6},
  { "medium_call", 
"gcc/ARC-Function-Attributes.html#index-medium_005fcall-function-attribute_002c-ARC",
 "ARC", 11},
  { "micromips", 
"gcc/MIPS-Function-Attributes.html#index-micromips-function-attribute", "", 9},
@@ -217,6 +221,7 @@ const attr_url_entry function_attrs[] = {
  { "stack_protect", 
"gcc/Common-Function-Attributes.html#index-stack_005fprotect-function-attribute",
 "", 13},
  { "stdcall", 
"gcc/x86-Function-Attributes.html#index-stdcall-function-attribute_002c-x86-32",
 "x86-32", 7},
  { "strict-align", 
"gcc/AArch64-Function-Attributes.html#index-strict-align-function-attribute_002c-AArch64",
 "AArch64", 12},
+ { "strict-align", 
"gcc/LoongArch-Function-Attributes.html#index-strict-align-function-attribute_002c-LoongArch",
 "LoongArch", 12},
  { "symver", 
"gcc/Common-Function-Attributes.html#index-symver-function-attribute", "", 6},
  { "syscall_linkage", 
"gcc/IA-64-Function-Attributes.html#index-syscall_005flinkage-function-attribute_002c-IA-64",
 "IA-64", 15},
  { "sysv_abi", 
"gcc/x86-Function-Attributes.html#index-sysv_005fabi-function-attribute_002c-x86",
 "x86", 8},
@@ -232,6 +237,7 @@ const attr_url_entry function_attrs[] = {
  { "trap_exit", 
"gcc/SH-Function-Attributes.html#index-trap_005fexit-function-attribute_002c-SH",
 "SH", 9},
  { "trapa_handler", 
"gcc/SH-Function-Attributes.html#index-trapa_005fhandler-function-attribute_002c-SH",
 "SH", 13},
  { "tune=", 
"gcc/AArch64-Function-Attributes.html#index-tune_003d-function-attribute_002c-AArch64",
 "AArch64", 5},
+ { "tune=", 
"gcc/LoongArch-Function-Attributes.html#index-tune_003d-function-attribute_002c-LoongArch",
 "LoongArch", 5},
  { "tune=", 
"gcc/RISC-V-Function-Attributes.html#index-tune_003d-function-attribute_002c-RISC-V",
 "RISC-V", 5},
  { "unavailable", 
"gcc/Common-Function-Attributes.html#index-unavailable-function-attribute", "", 
11},
  { "unused", 
"gcc/Common-Function-Attributes.html#index-unused-function-attribute", "", 6},
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 55e37146ee0..89740e08bbc 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -494,7 +494,7 @@ loongarch*-*-*)
        cpu_type=loongarch
        d_target_objs="loongarch-d.o"
        extra_headers="larchintrin.h lsxintrin.h lasxintrin.h"
-       extra_objs="loongarch-c.o loongarch-builtins.o loongarch-cpu.o 
loongarch-opts.o loongarch-def.o loongarch-evolution.o"
+       extra_objs="loongarch-c.o loongarch-builtins.o loongarch-cpu.o 
loongarch-opts.o loongarch-def.o loongarch-evolution.o loongarch-target-attr.o"
        extra_gcc_objs="loongarch-driver.o loongarch-cpu.o loongarch-opts.o 
loongarch-def.o"
        extra_options="${extra_options} g.opt fused-madd.opt"
        ;;
diff --git a/gcc/config/loongarch/loongarch-protos.h 
b/gcc/config/loongarch/loongarch-protos.h
index fb544ad75ca..531b0bcb636 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -212,4 +212,6 @@ extern void loongarch_emit_swrsqrtsf (rtx, rtx, 
machine_mode, bool);
 extern void loongarch_emit_swdivsf (rtx, rtx, rtx, machine_mode);
 extern bool loongarch_explicit_relocs_p (enum loongarch_symbol_type);
 extern bool loongarch_symbol_extreme_p (enum loongarch_symbol_type);
+extern bool loongarch_option_valid_attribute_p (tree, tree, tree, int);
+extern void loongarch_option_override_internal (struct loongarch_target *, 
struct gcc_options *, struct gcc_options *);
 #endif /* ! GCC_LOONGARCH_PROTOS_H */
diff --git a/gcc/config/loongarch/loongarch-target-attr.cc 
b/gcc/config/loongarch/loongarch-target-attr.cc
new file mode 100644
index 00000000000..1fafd4c4466
--- /dev/null
+++ b/gcc/config/loongarch/loongarch-target-attr.cc
@@ -0,0 +1,413 @@
+/* Subroutines used for LoongArch code generation.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+   Based on AArch64 target for GNU compiler.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "tm_p.h"
+#include "diagnostic.h"
+#include "opts.h"
+
+/* Enum describing the various ways we can handle attributes.
+   In many cases we can reuse the generic option handling machinery.  */
+
+enum loongarch_attr_opt_type
+{
+  loongarch_attr_mask, /* Attribute should set a bit in target_flags.  */
+  loongarch_attr_enum, /* Attribute sets an enum variable.  */
+  loongarch_attr_bool  /* Attribute sets or unsets a boolean variable.  */
+};
+
+/* All the information needed to handle a target attribute.
+   NAME is the name of the attribute.
+   ATTR_TYPE specifies the type of behavior of the attribute as described
+   in the definition of enum loongarch_attr_opt_type.
+   ALLOW_NEG is true if the attribute supports a "no-" form.
+   OPT_NUM is the enum specifying the option that the attribute modifies.
+   This is needed for attributes that mirror the behavior of a command-line
+   option, that is it has ATTR_TYPE loongarch_attr_mask.  */
+
+struct loongarch_attribute_info
+{
+  const char *name;
+  enum loongarch_attr_opt_type attr_type;
+  bool allow_neg;
+  enum opt_code opt_num;
+};
+/* The target attributes that we support.  */
+
+static const struct loongarch_attribute_info loongarch_attributes[] =
+{
+  { "strict-align", loongarch_attr_mask, true, OPT_mstrict_align },
+  { "cmodel", loongarch_attr_enum, false, OPT_mcmodel_ },
+  { "arch", loongarch_attr_enum, false, OPT_march_ },
+  { "tune", loongarch_attr_enum, false, OPT_mtune_ },
+  { "lsx", loongarch_attr_bool, true, OPT_mlsx },
+  { "lasx", loongarch_attr_bool, true, OPT_mlasx },
+  { NULL, loongarch_attr_bool, false, OPT____ }
+};
+
+bool
+loongarch_handle_option (struct gcc_options *opts,
+                        struct gcc_options *opts_set ATTRIBUTE_UNUSED,
+                        const struct cl_decoded_option *decoded,
+                        location_t loc ATTRIBUTE_UNUSED)
+{
+  size_t code = decoded->opt_index;
+  int val = decoded->value;
+
+  switch (code)
+    {
+    case OPT_mstrict_align:
+      if (val)
+       opts->x_target_flags |= MASK_STRICT_ALIGN;
+      else
+       opts->x_target_flags &= ~MASK_STRICT_ALIGN;
+      return true;
+
+    case OPT_mcmodel_:
+      opts->x_la_opt_cmodel = val;
+      return true;
+
+    case OPT_march_:
+      opts->x_la_opt_cpu_arch = val;
+
+      /* Set these variables to the initial values so that they can be reset
+        in the loongarch_config_target function according to the ARCH
+        settings.  */
+      opts->x_la_opt_simd = M_OPT_UNSET;
+      opts->x_la_opt_fpu = M_OPT_UNSET;
+      opts->x_la_isa_evolution = 0;
+      return true;
+
+    case OPT_mtune_:
+      opts->x_la_opt_cpu_tune = val;
+
+      /* Set these variables to the initial values so that they can be reset
+        in the loongarch_target_option_override function according to the TUNE
+        settings.  */
+      opts->x_str_align_functions = NULL;
+      opts->x_str_align_loops = NULL;
+      opts->x_str_align_jumps = NULL;
+      return true;
+
+    case OPT_mlsx:
+      opts->x_la_opt_simd = val ? (la_opt_simd == ISA_EXT_SIMD_LASX
+       ? ISA_EXT_SIMD_LASX : ISA_EXT_SIMD_LSX) : ISA_EXT_NONE;
+      return true;
+
+    case OPT_mlasx:
+      opts->x_la_opt_simd = val ? ISA_EXT_SIMD_LASX
+       : (la_opt_simd == ISA_EXT_SIMD_LSX || la_opt_simd == ISA_EXT_SIMD_LSX
+          ? ISA_EXT_SIMD_LSX : ISA_EXT_NONE);
+      return true;
+
+    default:
+      return true;
+    }
+}
+
+/* Parse ARG_STR which contains the definition of one target attribute.
+   Show appropriate errors if any or return true if the attribute is valid.  */
+
+static bool
+loongarch_process_one_target_attr (char *arg_str, location_t loc)
+{
+  bool invert = false;
+
+  size_t len = strlen (arg_str);
+
+  if (len == 0)
+    {
+      error_at (loc, "malformed %<target()%> pragma or attribute");
+      return false;
+    }
+
+  char *str_to_check = (char *) alloca (len + 1);
+  strcpy (str_to_check, arg_str);
+
+  if (len > 3 && startswith (str_to_check, "no-"))
+    {
+      invert = true;
+      str_to_check += 3;
+    }
+  char *arg = strchr (str_to_check, '=');
+
+  /* If we found opt=foo then terminate STR_TO_CHECK at the '='
+     and point ARG to "foo".  */
+  if (arg)
+    {
+      *arg = '\0';
+      arg++;
+    }
+  const struct loongarch_attribute_info *p_attr;
+  bool found = false;
+  for (p_attr = loongarch_attributes; p_attr->name; p_attr++)
+    {
+      /* If the names don't match up, or the user has given an argument
+        to an attribute that doesn't accept one, or didn't give an argument
+        to an attribute that expects one, fail to match.  */
+      if (strcmp (str_to_check, p_attr->name) != 0)
+       continue;
+
+      found = true;
+
+      /* If the name matches but the attribute does not allow "no-" versions
+        then we can't match.  */
+      if (invert && !p_attr->allow_neg)
+       {
+         error_at (loc, "pragma or attribute %<target(\"%s\")%> does not "
+                   "allow a negated form", str_to_check);
+         return false;
+       }
+
+      switch (p_attr->attr_type)
+       {
+         /* Either set or unset a boolean option.  */
+       case loongarch_attr_mask:
+           {
+             struct cl_decoded_option decoded;
+
+             /* We only need to specify the option number.
+                loongarch_handle_option will know which mask to apply.  */
+             decoded.opt_index = p_attr->opt_num;
+             decoded.value = !invert;
+
+             loongarch_handle_option (&global_options, &global_options_set,
+                                      &decoded, input_location);
+             break;
+           }
+
+         /* Use the option setting machinery to set an option to an enum.  */
+         case loongarch_attr_enum:
+           {
+             gcc_assert (arg);
+             bool valid;
+             int value;
+             struct cl_decoded_option decoded;
+             valid = opt_enum_arg_to_value (p_attr->opt_num, arg,
+                                             &value, CL_TARGET);
+
+             decoded.opt_index = p_attr->opt_num;
+             decoded.value = value;
+
+             if (valid)
+               loongarch_handle_option (&global_options,
+                                        &global_options_set,
+                                        &decoded, input_location);
+             else
+               error_at (loc, "pragma or attribute %<target(\"%s=%s\")%> is "
+                         "not valid", str_to_check, arg);
+             break;
+           }
+
+         /* Either set or unset a boolean option.  */
+         case loongarch_attr_bool:
+           {
+             struct cl_decoded_option decoded;
+
+             generate_option (p_attr->opt_num, NULL, !invert,
+                              CL_TARGET, &decoded);
+             loongarch_handle_option (&global_options, &global_options_set,
+                                      &decoded, input_location);
+             break;
+           }
+       default:
+         gcc_unreachable ();
+       }
+    }
+
+  /* If we reached here we either have found an attribute and validated
+     it or didn't match any.  If we matched an attribute but its arguments
+     were malformed we will have returned false already.  */
+  if (!found)
+    error_at (loc, "attribute %<target%> argument %qs is unknown",
+             str_to_check);
+
+  return found;
+}
+
+/* Count how many times the character C appears in
+   NULL-terminated string STR.  */
+
+static unsigned int
+num_occurences_in_str (char c, char *str)
+{
+  unsigned int res = 0;
+  while (*str != '\0')
+    {
+      if (*str == c)
+       res++;
+
+      str++;
+    }
+
+  return res;
+}
+
+/* Parse the tree in ARGS that contains the target attribute information
+   and update the global target options space.  */
+
+bool
+loongarch_process_target_attr (tree args, tree fndecl)
+{
+  location_t loc
+    = fndecl == NULL ? UNKNOWN_LOCATION : DECL_SOURCE_LOCATION (fndecl);
+
+  if (TREE_CODE (args) == TREE_LIST)
+    {
+      do
+       {
+         tree head = TREE_VALUE (args);
+         if (head)
+           {
+             if (!loongarch_process_target_attr (head, fndecl))
+               return false;
+           }
+         args = TREE_CHAIN (args);
+       } while (args);
+
+      return true;
+    }
+
+  if (TREE_CODE (args) != STRING_CST)
+    {
+      error_at (loc, "attribute %<target%> argument not a string");
+      return false;
+    }
+
+  size_t len = strlen (TREE_STRING_POINTER (args));
+  auto_vec<char, 32> buffer;
+  buffer.safe_grow (len + 1);
+  char *str_to_check = buffer.address ();
+  memcpy (str_to_check, TREE_STRING_POINTER (args), len + 1);
+
+  if (len == 0)
+    {
+      error_at (loc, "malformed %<target()%> pragma or attribute");
+      return false;
+    }
+
+  /* Used to catch empty spaces between commas i.e.
+     attribute ((target ("attr1,,attr2"))).  */
+  unsigned int num_commas = num_occurences_in_str (',', str_to_check);
+
+  /* Handle multiple target attributes separated by ','.  */
+  char *token = strtok_r (str_to_check, ",", &str_to_check);
+
+  unsigned int num_attrs = 0;
+  while (token)
+    {
+      num_attrs++;
+      if (!loongarch_process_one_target_attr (token, loc))
+       return false;
+
+      token = strtok_r (NULL, ",", &str_to_check);
+    }
+
+  if (num_attrs != num_commas + 1)
+    {
+      error_at (loc, "malformed %<target(\"%s\")%> pragma or attribute",
+               TREE_STRING_POINTER (args));
+      return false;
+    }
+
+  return true;
+}
+
+/* Implement TARGET_OPTION_VALID_ATTRIBUTE_P.  This is used to
+   process attribute ((target ("..."))).  */
+
+bool
+loongarch_option_valid_attribute_p (tree fndecl, tree, tree args, int)
+{
+  struct cl_target_option cur_target;
+  bool ret;
+  tree old_optimize;
+  tree new_target, new_optimize;
+  tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
+
+  tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+  old_optimize
+    = build_optimization_node (&global_options, &global_options_set);
+
+  /* If the function changed the optimization levels as well as setting
+     target options, start with the optimizations specified.  */
+  if (func_optimize && func_optimize != old_optimize)
+    cl_optimization_restore (&global_options, &global_options_set,
+                            TREE_OPTIMIZATION (func_optimize));
+
+  /* Save the current target options to restore at the end.  */
+  cl_target_option_save (&cur_target, &global_options, &global_options_set);
+
+  /* If fndecl already has some target attributes applied to it, unpack
+     them so that we add this attribute on top of them, rather than
+     overwriting them.  */
+  if (existing_target)
+    {
+      struct cl_target_option *existing_options
+       = TREE_TARGET_OPTION (existing_target);
+
+      if (existing_options)
+       cl_target_option_restore (&global_options, &global_options_set,
+                                 existing_options);
+    }
+  else
+    cl_target_option_restore (&global_options, &global_options_set,
+                             TREE_TARGET_OPTION (target_option_current_node));
+
+  ret = loongarch_process_target_attr (args, fndecl);
+
+  /* Set up any additional state.  */
+  if (ret)
+    {
+      loongarch_option_override_internal (&la_target,
+                                         &global_options,
+                                         &global_options_set);
+      new_target = build_target_option_node (&global_options,
+                                            &global_options_set);
+    }
+  else
+    new_target = NULL;
+
+  new_optimize = build_optimization_node (&global_options,
+                                         &global_options_set);
+
+  if (fndecl && ret)
+    {
+      DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
+
+      if (old_optimize != new_optimize)
+       DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
+    }
+
+  cl_target_option_restore (&global_options, &global_options_set, &cur_target);
+
+  if (old_optimize != new_optimize)
+    cl_optimization_restore (&global_options, &global_options_set,
+                            TREE_OPTIMIZATION (old_optimize));
+  return ret;
+}
diff --git a/gcc/config/loongarch/loongarch.cc 
b/gcc/config/loongarch/loongarch.cc
index d5e90bfd1e1..f3514073cea 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -7644,7 +7644,7 @@ loongarch_reg_init (void)
        = loongarch_hard_regno_mode_ok_uncached (regno, (machine_mode) mode);
 }
 
-static void
+void
 loongarch_option_override_internal (struct loongarch_target *target,
                                    struct gcc_options *opts,
                                    struct gcc_options *opts_set)
@@ -7670,9 +7670,6 @@ loongarch_option_override_internal (struct 
loongarch_target *target,
   /* Override some options according to the resolved target.  */
   loongarch_target_option_override (target, opts, opts_set);
 
-  target_option_default_node = target_option_current_node
-    = build_target_option_node (opts, opts_set);
-
   loongarch_reg_init ();
 }
 
@@ -7711,10 +7708,15 @@ loongarch_set_current_function (tree fndecl)
   else
     old_tree = target_option_default_node;
 
+  /* When the function is optimized, the pop_cfun will be called, and
+     the fndecl will be NULL.  */
   if (fndecl == NULL_TREE)
     {
       if (old_tree != target_option_current_node)
        {
+         /* When this function is set with special options, we need to
+            restore the original global optimization options at the end
+            of function optimization.  */
          loongarch_previous_fndecl = NULL_TREE;
          cl_target_option_restore (&global_options, &global_options_set,
                                    TREE_TARGET_OPTION
@@ -7724,6 +7726,9 @@ loongarch_set_current_function (tree fndecl)
     }
 
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
+
+  /* When no separate compilation parameters are set for the function,
+    new_tree is NULL.  */
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
 
@@ -7732,9 +7737,14 @@ loongarch_set_current_function (tree fndecl)
   if (new_tree == old_tree)
     return;
 
+  /* According to the settings of the functions attribute and pragma,
+     the options is corrected.  */
   cl_target_option_restore (&global_options, &global_options_set,
                            TREE_TARGET_OPTION (new_tree));
 
+  /* After correcting the value of options, we need to update the
+     rules for using the hardware registers to ensure that the
+     rules correspond to the options.  */
   loongarch_reg_init ();
 
   loongarch_save_restore_target_globals (new_tree);
@@ -7755,6 +7765,11 @@ loongarch_option_override (void)
                                      &global_options,
                                      &global_options_set);
 
+  /* Save the initial options so that we can restore the initial option
+     settings later when processing attributes and pragmas.  */
+  target_option_default_node = target_option_current_node
+    = build_target_option_node (&global_options, &global_options_set);
+
 }
 
 /* Implement TARGET_OPTION_SAVE.  */
@@ -11324,6 +11339,9 @@ loongarch_asm_code_end (void)
 #undef TARGET_C_MODE_FOR_FLOATING_TYPE
 #define TARGET_C_MODE_FOR_FLOATING_TYPE loongarch_c_mode_for_floating_type
 
+#undef TARGET_OPTION_VALID_ATTRIBUTE_P
+#define TARGET_OPTION_VALID_ATTRIBUTE_P loongarch_option_valid_attribute_p
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-loongarch.h"
diff --git a/gcc/config/loongarch/t-loongarch b/gcc/config/loongarch/t-loongarch
index 8f2f57f27ba..b7dbb4befc5 100644
--- a/gcc/config/loongarch/t-loongarch
+++ b/gcc/config/loongarch/t-loongarch
@@ -47,6 +47,12 @@ loongarch-c.o: $(srcdir)/config/loongarch/loongarch-c.cc 
$(CONFIG_H) $(SYSTEM_H)
        $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
        $(srcdir)/config/loongarch/loongarch-c.cc
 
+loongarch-target-attr.o: $(srcdir)/config/loongarch/loongarch-target-attr.cc \
+       $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TARGET_H) $(TREE_H) $(TM_H) \
+       $(DIAGNOSTIC_CORE_H)
+       $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+       $(srcdir)/config/loongarch/loongarch-target-attr.cc
+
 loongarch-builtins.o: $(srcdir)/config/loongarch/loongarch-builtins.cc 
$(CONFIG_H) \
        $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) $(RECOG_H) 
langhooks.h \
        $(DIAGNOSTIC_CORE_H) $(OPTABS_H) 
$(srcdir)/config/loongarch/loongarch-ftypes.def \
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 1e1b4cc837d..d896677fd3a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2642,6 +2642,7 @@ GCC plugins may provide their own attributes.
 * Epiphany Function Attributes::
 * H8/300 Function Attributes::
 * IA-64 Function Attributes::
+* LoongArch Function Attributes::
 * M32C Function Attributes::
 * M32R/D Function Attributes::
 * m68k Function Attributes::
@@ -5642,6 +5643,54 @@ extern int foo () __attribute__((version_id 
("20040821")));
 Calls to @code{foo} are mapped to calls to @code{foo@{20040821@}}.
 @end table
 
+@node LoongArch Function Attributes
+@subsection LoongArch Function Attributes
+
+These function attributes are supported by the LoongArch end:
+
+@table @code
+@cindex @code{strict-align} function attribute, LoongArch
+@item strict-align
+@itemx no-strict-align
+@code{strict-align} indicates that the compiler should not assume that 
unaligned
+memory references are handled by the system.  To allow the compiler to assume
+that aligned memory references are handled by the system, the inverse attribute
+@code{no-strict-align} can be specified.  The behavior is same as for the
+command-line option @option{-mstrict-align} and @option{-mno-strict-align}.
+
+@cindex @code{cmodel=} function attribute, LoongArch
+@item cmodel=
+Indicates that code should be generated for a particular code model for
+this function.  The behavior and permissible arguments are the same as
+for the command-line option @option{-mcmodel=}.
+
+@cindex @code{arch=} function attribute, LoongArch
+@item arch=
+Specifies the architecture version and architectural extensions to use
+for this function.  The behavior and permissible arguments are the same as
+for the @option{-march=} command-line option.
+
+@cindex @code{tune=} function attribute, LoongArch
+@item tune=
+Specifies the core for which to tune the performance of this function.
+The behavior and permissible arguments are the same as for the @option{-mtune=}
+command-line option.
+
+@cindex @code{lsx} function attribute, LoongArch
+@item lsx
+@itemx no-lsx
+@code{lsx} indicates that vector instruction generation is allowed (not 
allowed)
+when compiling the function.  The behavior is same as for the command-line 
option
+@option{-mlsx} and @option{-mno-lsx}.
+
+@cindex @code{lasx} function attribute, LoongArch
+@item lasx
+@itemx no-lasx
+@code{lasx} indicates that lasx instruction generation is allowed (not allowed)
+when compiling the function.  The behavior is same as for the command-line 
option
+@option{-mlasx} and @option{-mno-lasx}.
+@end table
+
 @node M32C Function Attributes
 @subsection M32C Function Attributes
 
diff --git a/gcc/testsuite/gcc.target/loongarch/arch-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/arch-func-attr-1.c
new file mode 100644
index 00000000000..98cc7e577e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/arch-func-attr-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=loongarch64 -mno-lsx" } */
+
+extern char a[64];
+extern char b[64];
+
+__attribute__ ((target ("arch=la64v1.1")))
+void
+test (void)
+{
+  for (int i = 0; i < 64; i++)
+    a[i] = b[i];
+}
+
+
+/* { dg-final { scan-assembler "vld" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/attr-check-error-message.c 
b/gcc/testsuite/gcc.target/loongarch/attr-check-error-message.c
new file mode 100644
index 00000000000..82dcd172555
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/attr-check-error-message.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wno-attributes" } */
+
+__attribute__ ((target ("mno-lsx"))) void
+test1 (void)   /* { dg-error "attribute \\\'target\\\' argument 
\\\'mno-lsx\\\' is unknown" } */
+{}
+
+__attribute__ ((target (""))) void
+test2 (void)   /* { dg-error "malformed \\\'target\\\(\\\)\\\' pragma or 
attribute" } */
+{}
+
+__attribute__ ((target ("no-cmodel="))) void
+test3 (void)   /* { dg-error "pragma or attribute 
\\\'target\\\(\\\"cmodel\\\"\\\)\\\' does not allow a negated form" } */
+{}
+
+__attribute__ ((target ("cmodel=test"))) void
+test4 (void)   /* { dg-error "pragma or attribute 
\\\'target\\\(\\\"cmodel=test\\\"\\\)\\\' is not valid" } */
+{}
+
+__attribute__ ((target ("test"))) void
+test5 (void)   /* { dg-error "attribute \\\'target\\\' argument \\\'test\\\' 
is unknown" } */
+{}
+
+__attribute__ ((target (lsx))) void    /* { dg-error "\\\'lsx\\\' undeclared 
here" } */
+test6 (void)   /* { dg-error "attribute \\\'target\\\' argument not a string" 
} */
+{}
+
+__attribute__ ((target ("lsx,"))) void
+test7 (void)   /* { dg-error "malformed \\\'target\\\(\\\"lsx,\\\"\\\)\\\' 
pragma or attribute" } */
+{}
diff --git a/gcc/testsuite/gcc.target/loongarch/cmodel-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/cmodel-func-attr-1.c
new file mode 100644
index 00000000000..119cd0e1646
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/cmodel-func-attr-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcmodel=normal -mexplicit-relocs=none" } */
+
+extern char a[8];
+extern char b[8];
+
+__attribute__ ((target ("cmodel=extreme")))
+void
+test (void)
+{
+  a[0] = b[1]; 
+  a[1] = b[2]; 
+  a[2] = b[3]; 
+  a[3] = b[4]; 
+}
+
+/* { dg-final { scan-assembler "la.global\t\\\$r\[0-9\]+,\\\$r\[0-9\]+,a" } } 
*/
diff --git a/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-1.c
new file mode 100644
index 00000000000..5dad9821f03
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-lsx" } */
+
+typedef int v8i32 __attribute__ ((vector_size(32), aligned(32)));
+extern v8i32 a, b, c;
+
+__attribute__ ((target ("lasx")))
+void
+test (void)
+{
+  a = b + c;
+}
+
+
+/* { dg-final { scan-assembler "xvadd.w" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-2.c 
b/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-2.c
new file mode 100644
index 00000000000..33cc924d0e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/lasx-func-attr-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mlasx" } */
+
+typedef int v8i32 __attribute__ ((vector_size(32), aligned(32)));
+extern v8i32 a, b, c;
+
+__attribute__ ((target ("no-lasx")))
+void
+test (void)
+{
+  a = __builtin_lasx_xvadd_w (b, c); /* { dg-error "built-in function 
'__builtin_lasx_xvadd_w' is not enabled" } */
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-1.c
new file mode 100644
index 00000000000..3e2c1dc3359
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-lsx" } */
+
+typedef int v4i32 __attribute__ ((vector_size(16), aligned(16)));
+extern v4i32 a, b, c;
+
+__attribute__ ((target ("lsx")))
+void
+test (void)
+{
+  a = b + c;
+}
+
+
+/* { dg-final { scan-assembler "vadd.w" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-2.c 
b/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-2.c
new file mode 100644
index 00000000000..97475fff579
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/lsx-func-attr-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mlsx" } */
+
+typedef int v4i32 __attribute__ ((vector_size(16), aligned(16)));
+extern v4i32 a, b, c;
+
+__attribute__ ((target ("no-lsx")))
+void
+test (void)
+{
+  a = __builtin_lsx_vadd_w (b, c); /* { dg-error "built-in function 
'__builtin_lsx_vadd_w' is not enabled" } */
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-1.c
new file mode 100644
index 00000000000..04893746de8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mstrict-align" } */
+extern char a[8];
+extern char b[8];
+
+__attribute__ ((target ("no-strict-align")))
+void
+test (void)
+{
+  a[0] = b[1]; 
+  a[1] = b[2]; 
+  a[2] = b[3]; 
+  a[3] = b[4]; 
+}
+
+
+/* { dg-final { scan-assembler-not "ld.bu" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-2.c 
b/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-2.c
new file mode 100644
index 00000000000..0e81486cd53
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/strict_align-func-attr-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-strict-align" } */
+extern char a[8];
+extern char b[8];
+
+__attribute__ ((target ("strict-align")))
+void
+test (void)
+{
+  a[0] = b[1]; 
+  a[1] = b[2]; 
+  a[2] = b[3]; 
+  a[3] = b[4]; 
+}
+
+
+/* { dg-final { scan-assembler-not "ld.w" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/vector-func-attr-1.c 
b/gcc/testsuite/gcc.target/loongarch/vector-func-attr-1.c
new file mode 100644
index 00000000000..655ca234be0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/vector-func-attr-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mlsx" } */
+
+typedef int v4i32 __attribute__ ((vector_size(16), aligned(16)));
+extern v4i32 a, b, c;
+
+__attribute__ ((target ("no-lasx")))
+void
+test (void)
+{
+  a = b + c;
+}
+
+
+/* { dg-final { scan-assembler "vadd.w" } } */
-- 
2.34.1


Reply via email to