gcc/ChangeLog:
* config/loongarch/loongarch-protos.h
(loongarch_parse_fmv_features): Function declaration.
* config/loongarch/loongarch-target-attr.cc
(enum features_prio): Defining the priority of features.
(struct loongarch_attribute_info): Add members about
features.
(LARCH_ATTR_MASK): Likewise.
(LARCH_ATTR_ENUM): Likewise.
(LARCH_ATTR_BOOL): Likewise.
(loongarch_parse_fmv_features): Parse a function
multiversioning feature string STR.
* config/loongarch/loongarch.cc
(get_suffixed_assembler_name): Return an identifier for the
base assembler name of a versioned function.
(get_feature_mask_for_version): Get the mask and priority of
features.
(add_condition_to_bb): Insert judgment statements for different
features functions.
(dispatch_function_versions): Generates the dispatch function for
multi-versioned functions.
(make_resolver_func): Make the resolver function decl to dispatch
the versions of a multi-versioned function.
(loongarch_generate_version_dispatcher_body): Generate the
dispatcher logic to invoke the right function version at run-time
for a given set of function versions.
(TARGET_GENERATE_VERSION_DISPATCHER_BODY): Define.
* common/config/loongarch/cpu-features.h: New file.
---
gcc/common/config/loongarch/cpu-features.h | 41 ++
gcc/config/loongarch/loongarch-protos.h | 6 +
gcc/config/loongarch/loongarch-target-attr.cc | 211 ++++++++--
gcc/config/loongarch/loongarch.cc | 384 ++++++++++++++++++
4 files changed, 619 insertions(+), 23 deletions(-)
create mode 100644 gcc/common/config/loongarch/cpu-features.h
diff --git a/gcc/common/config/loongarch/cpu-features.h
b/gcc/common/config/loongarch/cpu-features.h
new file mode 100644
index 00000000000..59ea8c05c6b
--- /dev/null
+++ b/gcc/common/config/loongarch/cpu-features.h
@@ -0,0 +1,41 @@
+/* Definitions of target machine for GNU compiler. LoongArch version.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by Loongson Ltd.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef LOONGARCH_CPU_FEATURES_H
+#define LOONGARCH_CPU_FEATURES_H
+
+typedef unsigned long long loongarch_fmv_feature_mask;
+
+enum CPUFeatures {
+ FEAT_LA64,
+ FEAT_UAL,
+ FEAT_LSX,
+ FEAT_LASX,
+ FEAT_FRECIPE,
+ FEAT_DIV32,
+ FEAT_LAM_BH,
+ FEAT_LAMCAS,
+ FEAT_SCQ,
+ FEAT_LD_SEQ_SA,
+ FEAT_EXT = 62,
+ FEAT_INIT
+};
+
+#endif /* LOONGARCH_CPU_FEATURES_H */
diff --git a/gcc/config/loongarch/loongarch-protos.h
b/gcc/config/loongarch/loongarch-protos.h
index 6139af48d7a..f4aa8f86373 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_LOONGARCH_PROTOS_H
#define GCC_LOONGARCH_PROTOS_H
+#include "common/config/loongarch/cpu-features.h"
+
/* Classifies a SYMBOL_REF, LABEL_REF or UNSPEC address.
SYMBOL_GOT_DISP
@@ -223,4 +225,8 @@ extern void loongarch_register_pragmas (void);
extern bool loongarch_process_target_attr (tree args, tree fndecl);
extern rtx loongarch_gen_stepped_int_parallel (unsigned int nelts, int base,
int step);
+extern bool loongarch_parse_fmv_features (tree, string_slice,
loongarch_fmv_feature_mask *,
+ unsigned int *);
+extern void get_feature_mask_for_version (tree, loongarch_fmv_feature_mask *,
+ unsigned int *);
#endif /* ! GCC_LOONGARCH_PROTOS_H */
diff --git a/gcc/config/loongarch/loongarch-target-attr.cc
b/gcc/config/loongarch/loongarch-target-attr.cc
index f7a93782372..5a6b5846d84 100644
--- a/gcc/config/loongarch/loongarch-target-attr.cc
+++ b/gcc/config/loongarch/loongarch-target-attr.cc
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#define IN_TARGET_CODE 1
+#define INCLUDE_STRING
#include "config.h"
#include "system.h"
#include "coretypes.h"
@@ -40,6 +41,31 @@ enum loongarch_attr_opt_type
loongarch_attr_bool /* Attribute sets or unsets a boolean variable. */
};
+/* Describes the priority of each feature. The larger the value, the higher
+ the priority. The priority setting rule is vector priority.
+
+ The highest priority currently is "-mlasx".
+ The second highest is "-march=la64v1.1" (lsx and la64v1.1 enabled
+ instructions).
+ The third highest is "-mlsx".
+ */
+enum features_prio
+{
+ LA_PRIO_NONE = 0,
+ LA_PRIO_LOONGARCH64,
+ LA_PRIO_STRICT_ALIGN,
+ LA_PRIO_FRECIPE,
+ LA_PRIO_DIV32 = LA_PRIO_FRECIPE,
+ LA_PRIO_LAM_BH = LA_PRIO_FRECIPE,
+ LA_PRIO_LAMCAS = LA_PRIO_FRECIPE,
+ LA_PRIO_SCQ = LA_PRIO_FRECIPE,
+ LA_PRIO_LD_SEQ_SA = LA_PRIO_FRECIPE,
+ LA_PRIO_LSX,
+ LA_PRIO_LA64V1_0,
+ LA_PRIO_LA64V1_1,
+ LA_PRIO_LASX
+};
+
/* 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
@@ -56,6 +82,9 @@ struct loongarch_attribute_info
enum loongarch_attr_opt_type attr_type;
enum opt_code opt_num;
bool allow_neg;
+ const loongarch_fmv_feature_mask feat_mask;
+ const unsigned int arch_ver;
+ enum features_prio priority;
};
/* Construct a loongarch_attributes from the given arguments.
@@ -67,38 +96,47 @@ struct loongarch_attribute_info
OPTMASK is the mask corresponding to the mutation option. If the
compilation option does not have a corresponding mask, pass 0.
*/
-#define LARCH_ATTR_MASK(OPTS, OPTNUM, OPTMASK) \
- { \
- OPTS, OPTMASK, loongarch_attr_mask, OPTNUM, true \
+#define LARCH_ATTR_MASK(OPTS, OPTNUM, OPTMASK, FEATMASK, ARCH_V, PRIO)
\
+ {
\
+ OPTS, OPTMASK, loongarch_attr_mask, OPTNUM, true, 1ULL << FEATMASK,
ARCH_V, \
+ PRIO
\
}
-#define LARCH_ATTR_ENUM(OPTS, OPTNUM, OPTMASK) \
- { \
- OPTS, OPTMASK, loongarch_attr_enum, OPTNUM, false \
+#define LARCH_ATTR_ENUM(OPTS, OPTNUM, OPTMASK, ARCH_V, PRIO)
\
+ {
\
+ OPTS, OPTMASK, loongarch_attr_enum, OPTNUM, false, 0, ARCH_V, PRIO
\
}
-#define LARCH_ATTR_BOOL(OPTS, OPTNUM, OPTMASK) \
- { \
- OPTS, OPTMASK, loongarch_attr_bool, OPTNUM, true \
+#define LARCH_ATTR_BOOL(OPTS, OPTNUM, OPTMASK, FEATMASK, ARCH_V, PRIO)
\
+ {
\
+ OPTS, OPTMASK, loongarch_attr_bool, OPTNUM, true, 1ULL << FEATMASK,
ARCH_V, \
+ PRIO
\
}
/* The target attributes that we support. */
static const struct loongarch_attribute_info loongarch_attributes[] =
{
- LARCH_ATTR_MASK ("strict-align", OPT_mstrict_align, MASK_STRICT_ALIGN),
- LARCH_ATTR_ENUM ("cmodel", OPT_mcmodel_, 0),
- LARCH_ATTR_ENUM ("arch", OPT_march_, 0),
- LARCH_ATTR_ENUM ("tune", OPT_mtune_, 0),
- LARCH_ATTR_BOOL ("lsx", OPT_mlsx, 0),
- LARCH_ATTR_BOOL ("lasx", OPT_mlasx, 0),
- LARCH_ATTR_BOOL ("frecipe", OPT_mfrecipe, OPTION_MASK_ISA_FRECIPE),
- LARCH_ATTR_BOOL ("div32", OPT_mdiv32, OPTION_MASK_ISA_DIV32),
- LARCH_ATTR_BOOL ("lam-bh", OPT_mlam_bh, OPTION_MASK_ISA_LAM_BH),
- LARCH_ATTR_BOOL ("lamcas", OPT_mlamcas, OPTION_MASK_ISA_LAMCAS),
- LARCH_ATTR_BOOL ("scq", OPT_mscq, OPTION_MASK_ISA_SCQ),
- LARCH_ATTR_BOOL ("ld-seq-sa", OPT_mld_seq_sa, OPTION_MASK_ISA_LD_SEQ_SA),
- { NULL, 0, loongarch_attr_bool, OPT____, false }
+ LARCH_ATTR_MASK ("strict-align", OPT_mstrict_align, MASK_STRICT_ALIGN,
+ FEAT_UAL, 0, LA_PRIO_STRICT_ALIGN),
+ LARCH_ATTR_ENUM ("cmodel", OPT_mcmodel_, 0, 0, LA_PRIO_NONE),
+ LARCH_ATTR_ENUM ("arch", OPT_march_, 0, 0, LA_PRIO_NONE),
+ LARCH_ATTR_ENUM ("tune", OPT_mtune_, 0, 0, LA_PRIO_NONE),
+ LARCH_ATTR_BOOL ("lsx", OPT_mlsx, 0, FEAT_LSX, ARCH_LA64V1_0, LA_PRIO_LSX),
+ LARCH_ATTR_BOOL ("lasx", OPT_mlasx, 0, FEAT_LASX | FEAT_LSX, 0,
LA_PRIO_LASX),
+ LARCH_ATTR_BOOL ("frecipe", OPT_mfrecipe, OPTION_MASK_ISA_FRECIPE,
+ FEAT_FRECIPE, ARCH_LA64V1_1, LA_PRIO_FRECIPE),
+ LARCH_ATTR_BOOL ("div32", OPT_mdiv32, OPTION_MASK_ISA_DIV32, FEAT_DIV32,
+ ARCH_LA64V1_1, LA_PRIO_DIV32),
+ LARCH_ATTR_BOOL ("lam-bh", OPT_mlam_bh, OPTION_MASK_ISA_LAM_BH, FEAT_LAM_BH,
+ ARCH_LA64V1_1, LA_PRIO_LAM_BH),
+ LARCH_ATTR_BOOL ("lamcas", OPT_mlamcas, OPTION_MASK_ISA_LAMCAS, FEAT_LAMCAS,
+ ARCH_LA64V1_1, LA_PRIO_LAMCAS),
+ LARCH_ATTR_BOOL ("scq", OPT_mscq, OPTION_MASK_ISA_SCQ, FEAT_SCQ,
+ ARCH_LA64V1_1, LA_PRIO_SCQ),
+ LARCH_ATTR_BOOL ("ld-seq-sa", OPT_mld_seq_sa, OPTION_MASK_ISA_LD_SEQ_SA,
+ FEAT_LD_SEQ_SA, ARCH_LA64V1_1, LA_PRIO_LD_SEQ_SA),
+ { NULL, 0, loongarch_attr_bool, OPT____, false, 0, 0, LA_PRIO_NONE}
};
#undef LARCH_ATTR_MASK
#undef LARCH_ATTR_ENUM
@@ -200,7 +238,6 @@ loongarch_process_one_target_attr (char *arg_str,
location_t loc)
char *str_to_check = (char *) alloca (len + 1);
strcpy (str_to_check, arg_str);
-
if (len > 3 && startswith (str_to_check, "no-"))
{
invert = true;
@@ -484,3 +521,131 @@ loongarch_option_valid_attribute_p (tree fndecl, tree,
tree args, int)
return ret;
}
+/* Parse a function multiversioning feature string STR, as found in a
+ target_version or target_clones attribute.
+
+ If FEATURE_MASK is nonnull, then assign to it a bitmask representing
+ the set of features explicitly specified in the feature string.
+
+ If FEATURE_PRIORITY is nonnull, set an unsigned integer values
+ presenting the priority of the feature string. */
+
+bool
+loongarch_parse_fmv_features (tree decl, string_slice str,
+ loongarch_fmv_feature_mask *feature_mask,
+ unsigned int *feature_priority)
+{
+ location_t loc
+ = decl == NULL ? UNKNOWN_LOCATION : DECL_SOURCE_LOCATION (decl);
+
+ if (feature_mask)
+ *feature_mask = 0;
+
+ string_slice attr_str = string_slice::tokenize (&str, ";");
+ attr_str = attr_str.strip ();
+
+ if (str.is_valid ())
+ {
+ error_at (loc, "attribute %qs is invalid", attr_str.begin ());
+ return false;
+ }
+
+ if (attr_str == "default")
+ {
+ if (feature_priority)
+ *feature_priority = LA_PRIO_NONE;
+ return true;
+ }
+
+ if (attr_str.is_valid ())
+ {
+ int num_features = ARRAY_SIZE (loongarch_attributes);
+
+ /* Handle arch= if specified. For priority, set it to be 1 more than
+ the best instruction set the processor can handle. */
+ if (strstr (attr_str.begin (), "arch=") != NULL)
+ {
+ string_slice arch_name = attr_str;
+ string_slice::tokenize (&arch_name, "=");
+ if (!arch_name.is_valid ())
+ {
+ error_at (loc, "in attribute %qs you need to set a legal value "
+ "for \"arch\"", attr_str.begin ());
+ return false;
+ }
+
+ loongarch_fmv_feature_mask tmp_mask = 0ULL;
+ unsigned int tmp_prio = 0;
+
+ if (arch_name == "loongarch64")
+ {
+ tmp_mask = 1UL << FEAT_LA64;
+ tmp_prio = LA_PRIO_LOONGARCH64;
+ }
+ else if (arch_name == "la64v1.0")
+ {
+ tmp_mask = 1ULL << FEAT_LA64;
+ for (int i = 0; i < num_features; i++)
+ if (loongarch_attributes[i].arch_ver == ARCH_LA64V1_0)
+ tmp_mask |= loongarch_attributes[i].feat_mask;
+ tmp_prio = LA_PRIO_LA64V1_0;
+ }
+ else if (arch_name == "la64v1.1")
+ {
+ tmp_mask = 1ULL << FEAT_LA64;
+ for (int i = 0; i < num_features; i++)
+ if (loongarch_attributes[i].arch_ver == ARCH_LA64V1_0
+ || loongarch_attributes[i].arch_ver == ARCH_LA64V1_1)
+ tmp_mask |= loongarch_attributes[i].feat_mask;
+ tmp_prio = LA_PRIO_LA64V1_1;
+ }
+ else
+ {
+ error_at (loc, "in attribute %qs you need to set a legal value "
+ "for \"arch\"", attr_str.begin ());
+ return false;
+ }
+
+ if (feature_mask)
+ *feature_mask = tmp_mask;
+
+ if (feature_priority)
+ *feature_priority = tmp_prio;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < num_features - 1; i++)
+ {
+ if (loongarch_attributes[i].name == attr_str
+ || strstr (attr_str.begin (),
+ loongarch_attributes[i].name) != NULL)
+ {
+ if (loongarch_attributes[i].feat_mask == 0)
+ {
+ error_at (loc, "attribute %qs is not supported in "
+ "%<target_version%> or %<target_clones%>",
+ attr_str.begin ());
+ return false;
+ }
+
+ if (feature_mask)
+ *feature_mask = loongarch_attributes[i].feat_mask;
+
+ if (feature_priority)
+ *feature_priority = loongarch_attributes[i].priority;
+ break;
+ }
+ }
+
+ if (i == num_features - 1)
+ {
+ error_at (loc, "%qs is not supported in target attribute",
+ attr_str.begin ());
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/gcc/config/loongarch/loongarch.cc
b/gcc/config/loongarch/loongarch.cc
index a742431c73d..fe9f7a941cf 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -11461,6 +11461,386 @@ loongarch_mangle_decl_assembler_name (tree decl, tree
id)
return id;
}
+/* Return an identifier for the base assembler name of a versioned function.
+ This is computed by taking the default version's assembler name, and
+ stripping off the ".default" suffix if it's already been appended. */
+
+static tree
+get_suffixed_assembler_name (tree default_decl, const char *suffix)
+{
+ std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl));
+
+ auto size = name.size ();
+ if (size >= 8 && name.compare (size - 8, 8, ".default") == 0)
+ name.resize (size - 8);
+ name += suffix;
+ return get_identifier (name.c_str ());
+}
+
+/* Get the mask and priority of features. */
+void
+get_feature_mask_for_version (tree decl,
+ loongarch_fmv_feature_mask *feature_mask,
+ unsigned int *feature_priority)
+{
+ tree version_attr = lookup_attribute ("target_version",
+ DECL_ATTRIBUTES (decl));
+ if (version_attr == NULL)
+ return;
+
+ string_slice version_string
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr)));
+ loongarch_parse_fmv_features (decl, version_string, feature_mask,
+ feature_priority);
+
+ return;
+}
+
+/* This adds a condition to the basic_block NEW_BB in function FUNCTION_DECL
+ to return a pointer to VERSION_DECL if all feature bits specified in
+ FEATURE_MASK are not set in MASK_VAR. This function will be called during
+ version dispatch to decide which function version to execute. It returns
+ the basic block at the end, to which more conditions can be added. */
+static basic_block
+add_condition_to_bb (tree function_decl, tree version_decl,
+ loongarch_fmv_feature_mask feature_mask,
+ tree mask_var, basic_block new_bb)
+{
+ gimple *return_stmt;
+ tree convert_expr, result_var;
+ gimple *convert_stmt;
+ gimple *if_else_stmt;
+
+ basic_block bb1, bb2, bb3;
+ edge e12, e23;
+
+ gimple_seq gseq;
+
+ push_cfun (DECL_STRUCT_FUNCTION (function_decl));
+
+ gcc_assert (new_bb != NULL);
+ gseq = bb_seq (new_bb);
+
+ convert_expr = build1 (CONVERT_EXPR, ptr_type_node,
+ build_fold_addr_expr (version_decl));
+ result_var = create_tmp_var (ptr_type_node);
+ convert_stmt = gimple_build_assign (result_var, convert_expr);
+ return_stmt = gimple_build_return (result_var);
+
+ if (feature_mask == 0ULL)
+ {
+ /* Default version. */
+ gimple_seq_add_stmt (&gseq, convert_stmt);
+ gimple_seq_add_stmt (&gseq, return_stmt);
+ set_bb_seq (new_bb, gseq);
+ gimple_set_bb (convert_stmt, new_bb);
+ gimple_set_bb (return_stmt, new_bb);
+ pop_cfun ();
+ return new_bb;
+ }
+
+ tree and_expr_var = create_tmp_var (unsigned_type_node);
+ tree and_expr = build2 (BIT_AND_EXPR,
+ long_long_unsigned_type_node,
+ mask_var,
+ build_int_cst (unsigned_type_node,
+ feature_mask));
+ gimple *and_stmt = gimple_build_assign (and_expr_var, and_expr);
+ gimple_set_block (and_stmt, DECL_INITIAL (function_decl));
+ gimple_set_bb (and_stmt, new_bb);
+ gimple_seq_add_stmt (&gseq, and_stmt);
+
+ tree zero_llu = build_int_cst (unsigned_type_node, 0);
+ if_else_stmt = gimple_build_cond (EQ_EXPR, and_expr_var, zero_llu,
+ NULL_TREE, NULL_TREE);
+ gimple_set_block (if_else_stmt, DECL_INITIAL (function_decl));
+ gimple_set_bb (if_else_stmt, new_bb);
+ gimple_seq_add_stmt (&gseq, if_else_stmt);
+
+ gimple_seq_add_stmt (&gseq, convert_stmt);
+ gimple_seq_add_stmt (&gseq, return_stmt);
+ set_bb_seq (new_bb, gseq);
+
+ bb1 = new_bb;
+ e12 = split_block (bb1, if_else_stmt);
+ bb2 = e12->dest;
+ e12->flags &= ~EDGE_FALLTHRU;
+ e12->flags |= EDGE_TRUE_VALUE;
+
+ e23 = split_block (bb2, return_stmt);
+
+ gimple_set_bb (convert_stmt, bb2);
+ gimple_set_bb (return_stmt, bb2);
+
+ bb3 = e23->dest;
+ make_edge (bb1, bb3, EDGE_FALSE_VALUE);
+
+ remove_edge (e23);
+ make_edge (bb2, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+
+ pop_cfun ();
+
+ return bb3;
+}
+
+/* This function generates the dispatch function for
+ multi-versioned functions. DISPATCH_DECL is the function which will
+ contain the dispatch logic. FNDECLS are the function choices for
+ dispatch, and is a tree chain. EMPTY_BB is the basic block pointer
+ in DISPATCH_DECL in which the dispatch code is generated. */
+
+static int
+dispatch_function_versions (tree dispatch_decl,
+ void *fndecls_p,
+ basic_block *empty_bb)
+{
+ gimple *ifunc_cpu_init_stmt;
+ gimple_seq gseq;
+ vec<tree> *fndecls;
+
+ gcc_assert (dispatch_decl != NULL
+ && fndecls_p != NULL
+ && empty_bb != NULL);
+
+ push_cfun (DECL_STRUCT_FUNCTION (dispatch_decl));
+
+ gseq = bb_seq (*empty_bb);
+ /* Function version dispatch is via IFUNC. IFUNC resolvers fire before
+ constructors, so explicity call __init_loongarch_feature_bits here. */
+ tree init_fn_type = build_function_type_list (void_type_node,
+ void_type_node,
+ NULL);
+ tree init_fn_id = get_identifier ("__init_loongarch_features_resolver");
+ tree init_fn_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
+ init_fn_id, init_fn_type);
+ DECL_EXTERNAL (init_fn_decl) = 1;
+ TREE_PUBLIC (init_fn_decl) = 1;
+ DECL_VISIBILITY (init_fn_decl) = VISIBILITY_HIDDEN;
+ DECL_VISIBILITY_SPECIFIED (init_fn_decl) = 1;
+ ifunc_cpu_init_stmt = gimple_build_call (init_fn_decl, 0);
+ gimple_seq_add_stmt (&gseq, ifunc_cpu_init_stmt);
+ gimple_set_bb (ifunc_cpu_init_stmt, *empty_bb);
+
+ /* Build the struct type for __loongarch_feature_bits. */
+ tree global_type = lang_hooks.types.make_type (RECORD_TYPE);
+ tree field1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+ get_identifier ("features"),
+ unsigned_type_node);
+ DECL_FIELD_CONTEXT (field1) = global_type;
+ TYPE_FIELDS (global_type) = field1;
+ layout_type (global_type);
+
+ tree global_var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__loongarch_feature_bits"),
+ global_type);
+ DECL_EXTERNAL (global_var) = 1;
+ TREE_PUBLIC (global_var) = 1;
+ DECL_VISIBILITY (global_var) = VISIBILITY_HIDDEN;
+ DECL_VISIBILITY_SPECIFIED (global_var) = 1;
+ tree mask_var = create_tmp_var (unsigned_type_node);
+
+ tree component_expr = build3 (COMPONENT_REF, unsigned_type_node,
+ global_var, field1, NULL_TREE);
+ gimple *component_stmt = gimple_build_assign (mask_var, component_expr);
+ gimple_set_block (component_stmt, DECL_INITIAL (dispatch_decl));
+ gimple_set_bb (component_stmt, *empty_bb);
+ gimple_seq_add_stmt (&gseq, component_stmt);
+
+ tree not_expr = build1 (BIT_NOT_EXPR, unsigned_type_node, mask_var);
+ gimple *not_stmt = gimple_build_assign (mask_var, not_expr);
+ gimple_set_block (not_stmt, DECL_INITIAL (dispatch_decl));
+ gimple_set_bb (not_stmt, *empty_bb);
+ gimple_seq_add_stmt (&gseq, not_stmt);
+
+ set_bb_seq (*empty_bb, gseq);
+
+ pop_cfun ();
+
+ /* fndecls_p is actually a vector. */
+ fndecls = static_cast<vec<tree> *> (fndecls_p);
+
+ /* At least one more version other than the default. */
+ unsigned int num_versions = fndecls->length ();
+ gcc_assert (num_versions >= 2);
+
+ int i;
+ tree version_decl;
+ FOR_EACH_VEC_ELT_REVERSE (*fndecls, i, version_decl)
+ {
+ loongarch_fmv_feature_mask feature_mask = 0;
+ /* Get attribute string, parse it and find the right features. */
+ get_feature_mask_for_version (version_decl, &feature_mask,
+ NULL);
+ *empty_bb = add_condition_to_bb (dispatch_decl,
+ version_decl,
+ feature_mask,
+ mask_var,
+ *empty_bb);
+ }
+
+ return 0;
+}
+
+/* Make the resolver function decl to dispatch the versions of
+ a multi-versioned function, DEFAULT_DECL. IFUNC_ALIAS_DECL is
+ ifunc alias that will point to the created resolver. Create an
+ empty basic block in the resolver and store the pointer in
+ EMPTY_BB. Return the decl of the resolver function. */
+
+static tree
+make_resolver_func (const tree default_decl,
+ const tree ifunc_alias_decl,
+ basic_block *empty_bb)
+{
+ tree decl, type, t;
+
+ /* Create resolver function name based on default_decl. We need to remove an
+ existing ".default" suffix if this has already been appended. */
+ tree decl_name = get_suffixed_assembler_name (default_decl, ".resolver");
+ const char *resolver_name = IDENTIFIER_POINTER (decl_name);
+
+ /* The resolver function should return a (void *). */
+ type = build_function_type_list (ptr_type_node, NULL_TREE);
+
+ decl = build_fn_decl (resolver_name, type);
+ SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+
+ DECL_NAME (decl) = decl_name;
+ TREE_USED (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ TREE_PUBLIC (decl) = 0;
+ DECL_UNINLINABLE (decl) = 1;
+
+ /* Resolver is not external, body is generated. */
+ DECL_EXTERNAL (decl) = 0;
+ DECL_EXTERNAL (ifunc_alias_decl) = 0;
+
+ DECL_CONTEXT (decl) = NULL_TREE;
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ DECL_STATIC_CONSTRUCTOR (decl) = 0;
+
+ if (DECL_COMDAT_GROUP (default_decl)
+ || TREE_PUBLIC (default_decl))
+ {
+ /* In this case, each translation unit with a call to this
+ versioned function will put out a resolver. Ensure it
+ is comdat to keep just one copy. */
+ DECL_COMDAT (decl) = 1;
+ make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl));
+ }
+ else
+ TREE_PUBLIC (ifunc_alias_decl) = 0;
+
+ /* Build result decl and add to function_decl. */
+ t = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, ptr_type_node);
+ DECL_CONTEXT (t) = decl;
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+ DECL_RESULT (decl) = t;
+
+ gimplify_function_tree (decl);
+ push_cfun (DECL_STRUCT_FUNCTION (decl));
+ *empty_bb = init_lowered_empty_function (decl, false,
+ profile_count::uninitialized ());
+
+ cgraph_node::add_new_function (decl, true);
+ symtab->call_cgraph_insertion_hooks (cgraph_node::get_create (decl));
+
+ pop_cfun ();
+
+ gcc_assert (ifunc_alias_decl != NULL);
+ /* Mark ifunc_alias_decl as "ifunc" with resolver as resolver_name. */
+ DECL_ATTRIBUTES (ifunc_alias_decl)
+ = make_attribute ("ifunc", resolver_name,
+ DECL_ATTRIBUTES (ifunc_alias_decl));
+
+ /* Create the alias for dispatch to resolver here. */
+ cgraph_node::create_same_body_alias (ifunc_alias_decl, decl);
+ return decl;
+}
+
+/* Implement TARGET_GENERATE_VERSION_DISPATCHER_BODY. */
+
+tree
+loongarch_generate_version_dispatcher_body (void *node_p)
+{
+ tree resolver_decl;
+ basic_block empty_bb;
+ tree default_ver_decl;
+ struct cgraph_node *versn;
+ struct cgraph_node *node;
+
+ struct cgraph_function_version_info *node_version_info = NULL;
+ struct cgraph_function_version_info *versn_info = NULL;
+
+ node = (cgraph_node *)node_p;
+
+ node_version_info = node->function_version ();
+ gcc_assert (node->dispatcher_function
+ && node_version_info != NULL);
+
+ if (node_version_info->dispatcher_resolver)
+ return node_version_info->dispatcher_resolver;
+
+ /* The first version in the chain corresponds to the default version. */
+ default_ver_decl = node_version_info->next->this_node->decl;
+
+ /* node is going to be an alias, so remove the finalized bit. */
+ node->definition = false;
+
+ resolver_decl = make_resolver_func (default_ver_decl,
+ node->decl, &empty_bb);
+
+ node_version_info->dispatcher_resolver = resolver_decl;
+
+ push_cfun (DECL_STRUCT_FUNCTION (resolver_decl));
+
+ auto_vec<tree, 2> fn_ver_vec;
+
+ for (versn_info = node_version_info->next; versn_info;
+ versn_info = versn_info->next)
+ {
+ versn = versn_info->this_node;
+ /* Check for virtual functions here again, as by this time it should
+ have been determined if this function needs a vtable index or
+ not. This happens for methods in derived classes that override
+ virtual methods in base classes but are not explicitly marked as
+ virtual. */
+ if (DECL_VINDEX (versn->decl))
+ sorry ("virtual function multiversioning not supported");
+
+ fn_ver_vec.safe_push (versn->decl);
+ }
+
+ dispatch_function_versions (resolver_decl, &fn_ver_vec, &empty_bb);
+ cgraph_edge::rebuild_edges ();
+ pop_cfun ();
+
+ /* Fix up symbol names. First we need to obtain the base name, which may
+ have already been mangled. */
+ tree base_name = get_suffixed_assembler_name (default_ver_decl, "");
+
+ /* We need to redo the version mangling on the non-default versions for the
+ target_clones case. Redoing the mangling for the target_version case is
+ redundant but does no harm. We need to skip the default version, because
+ expand_clones will append ".default" later; fortunately that suffix is the
+ one we want anyway. */
+ for (versn_info = node_version_info->next->next; versn_info;
+ versn_info = versn_info->next)
+ {
+ tree version_decl = versn_info->this_node->decl;
+ tree name = loongarch_mangle_decl_assembler_name (version_decl,
+ base_name);
+ symtab->change_decl_assembler_name (version_decl, name);
+ }
+
+ /* We also need to use the base name for the ifunc declaration. */
+ symtab->change_decl_assembler_name (node->decl, base_name);
+
+ return resolver_decl;
+}
+
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
@@ -11756,6 +12136,10 @@ loongarch_mangle_decl_assembler_name (tree decl, tree
id)
#define TARGET_MANGLE_DECL_ASSEMBLER_NAME \
loongarch_mangle_decl_assembler_name
+#undef TARGET_GENERATE_VERSION_DISPATCHER_BODY
+#define TARGET_GENERATE_VERSION_DISPATCHER_BODY \
+ loongarch_generate_version_dispatcher_body
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-loongarch.h"
--
2.34.1