diff mbox series

[RFC] RISC-V: Implement riscv_minimal_hwprobe_feature_bits

Message ID 20241005181920.2223674-1-chenyangyu@isrc.iscas.ac.cn
State New
Headers show
Series [RFC] RISC-V: Implement riscv_minimal_hwprobe_feature_bits | expand

Commit Message

Yangyu Chen Oct. 5, 2024, 6:19 p.m. UTC
This patch implements the riscv_minimal_hwprobe_feature_bits feature
for the RISC-V target. The feature bits are defined in the previous
patch [1] to provide bitmasks of ISA extensions that defined in RISC-V
C-API. Thus, we need a function to generate the feature bits for IFUNC
resolver to dispatch between different functions based on the hardware
features. The final version of the target_clones support on RISC-V is
still under development, I am working on it.

The minimal feature bits means to use the earliest extension appeard in
the Linux hwprobe to cover the given ISA string. To allow older kernels
without some implied extensions probe to run the FMV dispatcher
correctly.

For example, V implies Zve32x, but Zve32x appears in the Linux kernel
since v6.11. If we use isa string directly to generate FMV dispatcher
with functions with "arch=+v" extension, since we have V implied the
Zve32x, FMV dispatcher will check if the Zve32x extension is supported
by the host. If the Linux kernel is older than v6.11, the FMV dispatcher
will fail to detect the Zve32x extension even it already implies by the
V extension, thus making the FMV dispatcher fail to dispatch the correct
function.

Thus, we need to generate the minimal feature bits to cover the given
ISA string to allow the FMV dispatcher to work correctly on older
kernels.

[1] https://patchwork.sourceware.org/project/gcc/patch/20241003182256.1765569-1-chenyangyu@isrc.iscas.ac.cn/

gcc/ChangeLog:

        * common/config/riscv/riscv-common.cc
        (struct riscv_ext_bitmask_table_t): New struct.
        (riscv_minimal_hwprobe_feature_bits): New function.
        * config/riscv/riscv-subset.h (GCC_RISCV_SUBSET_H):
        (riscv_minimal_hwprobe_feature_bits): Declare the function.
        * common/config/riscv/feature_bits.h: New file.
---
 gcc/common/config/riscv/feature_bits.h  |  33 ++++++
 gcc/common/config/riscv/riscv-common.cc | 144 ++++++++++++++++++++++++
 gcc/config/riscv/riscv-subset.h         |   4 +
 3 files changed, 181 insertions(+)
 create mode 100644 gcc/common/config/riscv/feature_bits.h


base-commit: a8e6360765336969e3f45ac16e4340e5e5468768
prerequisite-patch-id: 2a8fa0993d052b742835fa76402e4f763feb3475
diff mbox series

Patch

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 */