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.

Change-Id: Ifc3a1a59dcb302cc77d1edeb0c393fa47b0d5c91
---
 gcc/common/config/loongarch/cpu-features.h    |  41 ++
 .../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 | 201 ++++++++-
 gcc/config/loongarch/loongarch.cc             | 384 ++++++++++++++++++
 6 files changed, 621 insertions(+), 29 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/genopts/gen-evolution.awk 
b/gcc/config/loongarch/genopts/gen-evolution.awk
index 3e27a3f92bd..789be8054b2 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 a5137b2390e..796d3559214 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 6ecbe27218c..9179cbd5e0c 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
@@ -224,4 +226,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 f7e7d403c22..5a4b0345bcb 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,31 +96,34 @@ 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 }
 };
@@ -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 5b8bda59c4f..9bf16e8459d 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -11505,6 +11505,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"
@@ -11804,6 +12184,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

Reply via email to