https://gcc.gnu.org/g:b9f37c0f6589eb27052fd17a70de891ac34226de
commit r16-5153-gb9f37c0f6589eb27052fd17a70de891ac34226de Author: Lulu Cheng <[email protected]> Date: Tue Oct 28 15:25:55 2025 +0800 LoongArch: Implement TARGET_GENERATE_VERSION_DISPATCHER_BODY. gcc/ChangeLog: * config/loongarch/genopts/gen-evolution.awk: * config/loongarch/loongarch-evol-attr.def: Regenerate. * config/loongarch/loongarch-protos.h (loongarch_parse_fmv_features): Function declaration. (get_feature_mask_for_version): Likewise. * 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. Diff: --- gcc/common/config/loongarch/cpu-features.h | 41 +++ gcc/config/loongarch/genopts/gen-evolution.awk | 6 +- gcc/config/loongarch/loongarch-evol-attr.def | 12 +- gcc/config/loongarch/loongarch-protos.h | 6 + gcc/config/loongarch/loongarch-target-attr.cc | 203 +++++++++++-- gcc/config/loongarch/loongarch.cc | 384 +++++++++++++++++++++++++ 6 files changed, 622 insertions(+), 30 deletions(-) diff --git a/gcc/common/config/loongarch/cpu-features.h b/gcc/common/config/loongarch/cpu-features.h new file mode 100644 index 000000000000..59ea8c05c6b9 --- /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/genopts/gen-evolution.awk b/gcc/config/loongarch/genopts/gen-evolution.awk index 3e27a3f92bd3..789be8054b2b 100644 --- a/gcc/config/loongarch/genopts/gen-evolution.awk +++ b/gcc/config/loongarch/genopts/gen-evolution.awk @@ -254,8 +254,10 @@ function gen_full_def() print "#endif" print "" for (i = 1; i <= NR; i++) - printf (" LARCH_ATTR_BOOL (\"%s\", OPT_m%s, OPTION_MASK_ISA_%s)\n", - orig_name[i], name[i], name_capitalized[i]) + printf (" LARCH_ATTR_BOOL (\"%s\", OPT_m%s, OPTION_MASK_ISA_%s, FEAT_%s," \ + " ARCH_LA64V%d_%d, LA_PRIO_%s)\n", + orig_name[i], name[i], name_capitalized[i], name_capitalized[i], + isa_version_major[i], isa_version_minor[i], name_capitalized[i]) } END { diff --git a/gcc/config/loongarch/loongarch-evol-attr.def b/gcc/config/loongarch/loongarch-evol-attr.def index a5137b2390ea..796d35592143 100644 --- a/gcc/config/loongarch/loongarch-evol-attr.def +++ b/gcc/config/loongarch/loongarch-evol-attr.def @@ -24,9 +24,9 @@ along with GCC; see the file COPYING3. If not see #define LARCH_ATTR_BOOL #endif - 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) + 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) diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h index e00dd898943a..db5eb0e69937 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 @@ -227,4 +229,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 f7e7d403c222..834179fc6db5 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,33 +96,36 @@ 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_ENUM(OPTS, OPTNUM, OPTMASK) \ - { \ - OPTS, OPTMASK, loongarch_attr_enum, OPTNUM, false \ - }, - -#define LARCH_ATTR_BOOL(OPTS, OPTNUM, OPTMASK) \ - { \ - OPTS, OPTMASK, loongarch_attr_bool, OPTNUM, true \ - }, +#define LARCH_ATTR_MASK(OPTS, OPTNUM, OPTMASK, FEATMASK, PRIO) \ +{ \ + OPTS, OPTMASK, loongarch_attr_mask, OPTNUM, true, 1ULL << FEATMASK, \ + N_ARCH_TYPES, PRIO \ +}, + +#define LARCH_ATTR_ENUM(OPTS, OPTNUM, PRIO) \ +{ \ + OPTS, 0, loongarch_attr_enum, OPTNUM, false, 0, N_ARCH_TYPES, PRIO \ +}, + +#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_MASK ("strict-align", OPT_mstrict_align, MASK_STRICT_ALIGN, + FEAT_UAL, LA_PRIO_STRICT_ALIGN) + LARCH_ATTR_ENUM ("cmodel", OPT_mcmodel_, LA_PRIO_NONE) + LARCH_ATTR_ENUM ("arch", OPT_march_, LA_PRIO_NONE) + LARCH_ATTR_ENUM ("tune", OPT_mtune_, 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) #include "loongarch-evol-attr.def" - { NULL, 0, loongarch_attr_bool, OPT____, false } + { NULL, 0, loongarch_attr_bool, OPT____, false, 0, N_ARCH_TYPES, LA_PRIO_NONE } }; #undef LARCH_ATTR_MASK #undef LARCH_ATTR_ENUM @@ -167,7 +199,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; @@ -476,3 +507,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 1e5ce54e83a0..edec88136554 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -11633,6 +11633,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" @@ -11932,6 +12312,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"
