Skip to content

Commit

Permalink
RISC-V: Add function multiversioning support
Browse files Browse the repository at this point in the history
This patch adds support for function multi-versioning to the RISC-V
using the target_clones and target_versions attributes, which follow
the RISC-V C-API Docs [1] and the existing proposal about priority
syntax [2].

This patch copies many codes from commit 0cfde68 ("[aarch64]
Add function multiversioning support") and modifies them to fit the
RISC-V port. Some key differences are introduced in previously
submitted patches [3] and [4], commit [5] and [6].

To test this patch with the GLIBC dynamic loader, you should apply
patch [7] for GLIBC to ensure the dynamic loader will initialize
the gp register correctly.

[1] https://github.com/riscv-non-isa/riscv-c-api-doc/blob/c6c5d6d9cf96b342293315a5dff3d25e96ef8191/src/c-api.adoc#__attribute__targetattr-string
[2] riscv-non-isa/riscv-c-api-doc#85
[3] https://patchwork.sourceware.org/project/gcc/patch/[email protected]/
[4] https://patchwork.sourceware.org/project/gcc/patch/[email protected]/
[5] 6183523
[6] 30e0118
[7] https://patchwork.sourceware.org/project/glibc/patch/[email protected]/

Co-Developed-by: Hank Chang <[email protected]>

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-protos.h
        (riscv_option_valid_version_attribute_p): New function.
        (riscv_process_target_attr): New function.
        * config/riscv/riscv-subset.h
        (riscv_minimal_hwprobe_feature_bits): New function.
        * config/riscv/riscv-target-attr.cc
        (riscv_target_attr_parser::handle_priority): New function.
        (riscv_target_attr_parser::update_settings): Update priority
        attribute and never free the arch string.
        (riscv_process_one_target_attr): Add const qualifier to arg_str
        and split arg_str with ';'.
        (riscv_process_target_attr): New implementation which consumes
        the const char *args instead of tree.
        (riscv_option_valid_attribute_p): Reapply any target_version
        attribute after target attribute.
        (riscv_process_target_version_attr): New function.
        * config/riscv/riscv.cc (riscv_can_inline_p): Refuse to inline
        when callee is versioned but caller is not.
        (parse_features_for_version): New function.
        (compare_fmv_features): New function.
        (riscv_compare_version_priority): New function.
        (riscv_common_function_versions): New function.
        (add_condition_to_bb): New function.
        (dispatch_function_versions): New function.
        (get_suffixed_assembler_name): New function.
        (make_resolver_func): New function.
        (riscv_mangle_decl_assembler_name): New function.
        (riscv_generate_version_dispatcher_body): New function.
        (riscv_get_function_versions_dispatcher): New function.
        (TARGET_OPTION_VALID_VERSION_ATTRIBUTE_P): Implement it.
        (TARGET_OPTION_FUNCTION_VERSIONS): Implement it.
        (TARGET_COMPARE_VERSION_PRIORITY): Implement it.
        (TARGET_GENERATE_VERSION_DISPATCHER_BODY): Implement it.
        (TARGET_GET_FUNCTION_VERSIONS_DISPATCHER): Implement it.
        (TARGET_MANGLE_DECL_ASSEMBLER_NAME): Implement it.
        * config/riscv/riscv.opt: Add TargetVariable riscv_fmv_priority.
        * defaults.h (TARGET_CLONES_ATTR_SEPARATOR): Define new macro.
        * multiple_target.cc (get_attr_str): Use
          TARGET_CLONES_ATTR_SEPARATOR to separate attributes.
        (separate_attrs): Likewise.
        * config/riscv/riscv.h
        (TARGET_CLONES_ATTR_SEPARATOR): Implement it.
        (TARGET_HAS_FMV_TARGET_ATTRIBUTE): Implement it.
        * config/riscv/feature_bits.h: New file.
  • Loading branch information
cyyself committed Oct 20, 2024
1 parent 01f50eb commit 7e5a787
Show file tree
Hide file tree
Showing 10 changed files with 1,157 additions and 41 deletions.
145 changes: 145 additions & 0 deletions gcc/common/config/riscv/riscv-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1760,6 +1761,75 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
{NULL, 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
Expand Down Expand Up @@ -1826,6 +1896,81 @@ riscv_x_target_flags_isa_mask (void)
return mask;
}

/* 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,
location_t loc)
{
riscv_subset_list *subset_list;
subset_list = riscv_subset_list::parse (isa, loc);
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 visited 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. Avoiding 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. */

Expand Down
44 changes: 44 additions & 0 deletions gcc/config/riscv/feature_bits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* 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/>. */

#ifndef GCC_RISCV_FEATURE_BITS_H
#define GCC_RISCV_FEATURE_BITS_H

#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 length;
unsigned long long features[RISCV_VENDOR_FEATURE_BITS_LENGTH];
};

struct riscv_cpu_model {
unsigned mvendorid;
unsigned long long marchid;
unsigned long long mimpid;
};

#endif /* GCC_RISCV_FEATURE_BITS_H */
4 changes: 4 additions & 0 deletions gcc/config/riscv/riscv-protos.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,10 @@ extern bool riscv_use_divmod_expander (void);
void riscv_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, int);
extern bool
riscv_option_valid_attribute_p (tree, tree, tree, int);
extern bool
riscv_option_valid_version_attribute_p (tree, tree, tree, int);
extern bool
riscv_process_target_attr (const char *, location_t);
extern void
riscv_override_options_internal (struct gcc_options *);
extern void riscv_option_override (void);
Expand Down
5 changes: 5 additions & 0 deletions gcc/config/riscv/riscv-subset.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 "feature_bits.h"

#define RISCV_DONT_CARE_VERSION -1

/* Subset info. */
Expand Down Expand Up @@ -120,6 +122,9 @@ class riscv_subset_list
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 *,
location_t);
extern bool
riscv_ext_is_subset (struct cl_target_option *, struct cl_target_option *);
extern int riscv_x_target_flags_isa_mask (void);
Expand Down
Loading

0 comments on commit 7e5a787

Please sign in to comment.