@@ -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},
@@ -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"
;;
@@ -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 */
new file mode 100644
@@ -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;
+}
@@ -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"
@@ -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 \
@@ -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
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } */
+{}
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } */
+}
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } */
+}
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */