[1/2] LoongArch: Implement target attribute.

Message ID 20250107113803.25633-2-chenglulu@loongson.cn
State New
Headers
Series Implement target attribute and pragma. |

Checks

Context Check Description
rivoscibot/toolchain-ci-rivos-lint warning Lint failed
rivoscibot/toolchain-ci-rivos-apply-patch success Patch applied
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc_zba_zbb_zbc_zbs-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-test success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 pending Patch applied
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed

Commit Message

Lulu Cheng Jan. 7, 2025, 11:38 a.m. UTC
  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
  

Patch

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" } } */