diff --git a/gcc/common/config/riscv/feature_bits.h b/gcc/common/config/riscv/feature_bits.h
new file mode 100644
index 00000000000..c6c6d983edb
--- /dev/null
+++ b/gcc/common/config/riscv/feature_bits.h
@@ -0,0 +1,33 @@
+/* Definition of RISC-V feature bits corresponding to
+   libgcc/config/riscv/feature_bits.c
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+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 RISCV_FEATURE_BITS_LENGTH 2
+struct riscv_feature_bits {
+  unsigned length;
+  unsigned long long features[RISCV_FEATURE_BITS_LENGTH];
+};
+
+#define RISCV_VENDOR_FEATURE_BITS_LENGTH 1
+
+struct riscv_vendor_feature_bits {
+  unsigned vendorID;
+  unsigned length;
+  unsigned long long features[RISCV_VENDOR_FEATURE_BITS_LENGTH];
+};
diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index bd42fd01532..9f343782ae6 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -19,6 +19,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include <sstream>
 #include <vector>
+#include <queue>
 
 #define INCLUDE_STRING
 #define INCLUDE_SET
@@ -1754,6 +1755,75 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {NULL, NULL, 0}
 };
 
+/* Types for recording extension to RISC-V C-API bitmask.  */
+struct riscv_ext_bitmask_table_t {
+  const char *ext;
+  int groupid;
+  int bit_position;
+};
+
+/* Mapping table between extension to RISC-V C-API extension bitmask.
+   This table should sort the extension by Linux hwprobe order to get the
+   minimal feature bits.  */
+static const riscv_ext_bitmask_table_t riscv_ext_bitmask_table[] =
+{
+  {"i",			0,  8},
+  {"m",			0, 12},
+  {"a",			0,  0},
+  {"f",			0,  5},
+  {"d",			0,  3},
+  {"c",			0,  2},
+  {"v",			0, 21},
+  {"zba",		0, 27},
+  {"zbb",		0, 28},
+  {"zbs",		0, 33},
+  {"zicboz",		0, 37},
+  {"zbc",		0, 29},
+  {"zbkb",		0, 30},
+  {"zbkc",		0, 31},
+  {"zbkx",		0, 32},
+  {"zknd",		0, 41},
+  {"zkne",		0, 42},
+  {"zknh",		0, 43},
+  {"zksed",		0, 44},
+  {"zksh",		0, 45},
+  {"zkt",		0, 46},
+  {"zvbb",		0, 48},
+  {"zvbc",		0, 49},
+  {"zvkb",		0, 52},
+  {"zvkg",		0, 53},
+  {"zvkned",		0, 54},
+  {"zvknha",		0, 55},
+  {"zvknhb",		0, 56},
+  {"zvksed",		0, 57},
+  {"zvksh",		0, 58},
+  {"zvkt",		0, 59},
+  {"zfh",		0, 35},
+  {"zfhmin",		0, 36},
+  {"zihintntl",		0, 39},
+  {"zvfh",		0, 50},
+  {"zvfhmin",		0, 51},
+  {"zfa",		0, 34},
+  {"ztso",		0, 47},
+  {"zacas",		0, 26},
+  {"zicond",		0, 38},
+  {"zihintpause",	0, 40},
+  {"zve32x",		0, 60},
+  {"zve32f",		0, 61},
+  {"zve64x",		0, 62},
+  {"zve64f",		0, 63},
+  {"zve64d",		1,  0},
+  {"zimop",		1,  1},
+  {"zca",		1,  2},
+  {"zcb",		1,  3},
+  {"zcd",		1,  4},
+  {"zcf",		1,  5},
+  {"zcmop",		1,  6},
+  {"zawrs",		1,  7},
+
+  {NULL,	       -1, -1}
+};
+
 /* Apply SUBSET_LIST to OPTS if OPTS is not null.  */
 
 void
@@ -1783,6 +1853,80 @@ riscv_set_arch_by_subset_list (riscv_subset_list *subset_list,
     }
 }
 
+/* Get the minimal feature bits in Linux hwprobe of the given ISA string.
+
+   Used for generating Function Multi-Versioning (FMV) dispatcher for RISC-V.
+
+   The minimal feature bits refer to using the earliest extension that appeared
+   in the Linux hwprobe to support the specified ISA string.  This ensures that
+   older kernels, which may lack certain implied extensions, can still run the
+   FMV dispatcher correctly.  */
+
+bool
+riscv_minimal_hwprobe_feature_bits (const char *isa,
+				    struct riscv_feature_bits *res)
+{
+  riscv_subset_list *subset_list;
+  subset_list = riscv_subset_list::parse (isa, UNKNOWN_LOCATION);
+  if (!subset_list)
+    return false;
+
+  /* Initialize the result feature bits to zero.  */
+  res->length = RISCV_FEATURE_BITS_LENGTH;
+  for (int i = 0; i < RISCV_FEATURE_BITS_LENGTH; ++i)
+    res->features[i] = 0;
+
+  /* Use a std::set to record all visted implied extensions.  */
+  std::set <std::string> implied_exts;
+
+  /* Iterate through the extension bitmask table in Linux hwprobe order to get
+     the minimal covered feature bits.  Avoding some sub-extensions which will
+     be implied by the super-extensions like V implied Zve32x.  */
+  const riscv_ext_bitmask_table_t *ext_bitmask_tab;
+  for (ext_bitmask_tab = &riscv_ext_bitmask_table[0];
+       ext_bitmask_tab->ext;
+       ++ext_bitmask_tab)
+    {
+      /* Skip the extension if it is not in the subset list or already implied
+	 by previous extension.  */
+      if (subset_list->lookup (ext_bitmask_tab->ext) == NULL
+	  || implied_exts.count (ext_bitmask_tab->ext))
+	continue;
+
+      res->features[ext_bitmask_tab->groupid]
+	|= 1ULL << ext_bitmask_tab->bit_position;
+
+      /* Find the sub-extension using BFS and set the corresponding bit.  */
+      std::queue <const char *> search_q;
+      search_q.push (ext_bitmask_tab->ext);
+
+      while (!search_q.empty ())
+	{
+	  const char * search_ext = search_q.front ();
+	  search_q.pop ();
+
+	  /* Iterate through the implied extension table.  */
+	  const riscv_implied_info_t *implied_info;
+	  for (implied_info = &riscv_implied_info[0];
+	      implied_info->ext;
+	      ++implied_info)
+	    {
+	      /* When the search extension matches the implied extension and
+		 the implied extension has not been visited, mark the implied
+		 extension in the implied_exts set and push it into the
+		 queue.  */
+	      if (implied_info->match (subset_list, search_ext)
+		  && implied_exts.count (implied_info->implied_ext) == 0)
+		{
+		  implied_exts.insert (implied_info->implied_ext);
+		  search_q.push (implied_info->implied_ext);
+		}
+	    }
+	}
+    }
+  return true;
+}
+
 /* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch
    dependent mask bits, in case more than one -march string is passed.  */
 
diff --git a/gcc/config/riscv/riscv-subset.h b/gcc/config/riscv/riscv-subset.h
index dace4de6575..5a1489c00ea 100644
--- a/gcc/config/riscv/riscv-subset.h
+++ b/gcc/config/riscv/riscv-subset.h
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_RISCV_SUBSET_H
 #define GCC_RISCV_SUBSET_H
 
+#include "common/config/riscv/feature_bits.h"
+
 #define RISCV_DONT_CARE_VERSION -1
 
 /* Subset info.  */
@@ -120,5 +122,7 @@ public:
 extern const riscv_subset_list *riscv_cmdline_subset_list (void);
 extern void
 riscv_set_arch_by_subset_list (riscv_subset_list *, struct gcc_options *);
+extern bool riscv_minimal_hwprobe_feature_bits (const char *,
+						struct riscv_feature_bits *);
 
 #endif /* ! GCC_RISCV_SUBSET_H */
