Corresponding implementation in compiler-rt already merged in LLVM
side, so I plan to merge this into trunk tomorrow if no strong
objections.

NOTE: This has been tested with clang/llvm within our internal CI.

On Mon, Jul 22, 2024 at 10:16 PM Kito Cheng <kito.ch...@sifive.com> wrote:
>
> This provides a common abstraction layer to probe the available extensions at
> run-time. These functions can be used to implement function multi-versioning 
> or
> to detect available extensions.
>
> The advantages of providing this abstraction layer are:
> - Easy to port to other new platforms.
> - Easier to maintain in GCC for function multi-versioning.
>   - For example, maintaining platform-dependent code in C code/libgcc is much
>     easier than maintaining it in GCC by creating GIMPLEs...
>
> This API is intended to provide the capability to query minimal common 
> available extensions on the system.
>
> Proposal in riscv-c-api-doc: 
> https://github.com/riscv-non-isa/riscv-c-api-doc/pull/74
>
> Full function multi-versioning implementation will come later. We are posting
> this first because we intend to backport it to the GCC 14 branch to unblock
> LLVM 19 to use this with GCC 14.2, rather than waiting for GCC 15.
>
> Changes since v2:
> - Prevent it initialize more than once.
>
> Changes since v1:
> - Fix the format.
> - Prevented race conditions by introducing a local variable to avoid 
> load/store
>   operations during the computation of the feature bit.
>
> libgcc/ChangeLog:
>
>         * config/riscv/feature_bits.c: New.
>         * config/riscv/t-elf (LIB2ADD): Add feature_bits.c.
> ---
>  libgcc/config/riscv/feature_bits.c | 313 +++++++++++++++++++++++++++++
>  libgcc/config/riscv/t-elf          |   1 +
>  2 files changed, 314 insertions(+)
>  create mode 100644 libgcc/config/riscv/feature_bits.c
>
> diff --git a/libgcc/config/riscv/feature_bits.c 
> b/libgcc/config/riscv/feature_bits.c
> new file mode 100644
> index 00000000000..cce4fbfa6be
> --- /dev/null
> +++ b/libgcc/config/riscv/feature_bits.c
> @@ -0,0 +1,313 @@
> +/* Helper function for function multi-versioning for RISC-V.
> +
> +   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.
> +
> +Under Section 7 of GPL version 3, you are granted additional
> +permissions described in the GCC Runtime Library Exception, version
> +3.1, as published by the Free Software Foundation.
> +
> +You should have received a copy of the GNU General Public License and
> +a copy of the GCC Runtime Library Exception along with this program;
> +see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#define RISCV_FEATURE_BITS_LENGTH 1
> +struct {
> +  unsigned length;
> +  unsigned long long features[RISCV_FEATURE_BITS_LENGTH];
> +} __riscv_feature_bits __attribute__((visibility("hidden"), nocommon));
> +
> +#define RISCV_VENDOR_FEATURE_BITS_LENGTH 1
> +
> +struct {
> +  unsigned vendorID;
> +  unsigned length;
> +  unsigned long long features[RISCV_VENDOR_FEATURE_BITS_LENGTH];
> +} __riscv_vendor_feature_bits __attribute__((visibility("hidden"), 
> nocommon));
> +
> +#define A_GROUPID 0
> +#define A_BITMASK (1ULL << 0)
> +#define C_GROUPID 0
> +#define C_BITMASK (1ULL << 2)
> +#define D_GROUPID 0
> +#define D_BITMASK (1ULL << 3)
> +#define F_GROUPID 0
> +#define F_BITMASK (1ULL << 5)
> +#define I_GROUPID 0
> +#define I_BITMASK (1ULL << 8)
> +#define M_GROUPID 0
> +#define M_BITMASK (1ULL << 12)
> +#define V_GROUPID 0
> +#define V_BITMASK (1ULL << 21)
> +#define ZACAS_GROUPID 0
> +#define ZACAS_BITMASK (1ULL << 26)
> +#define ZBA_GROUPID 0
> +#define ZBA_BITMASK (1ULL << 27)
> +#define ZBB_GROUPID 0
> +#define ZBB_BITMASK (1ULL << 28)
> +#define ZBC_GROUPID 0
> +#define ZBC_BITMASK (1ULL << 29)
> +#define ZBKB_GROUPID 0
> +#define ZBKB_BITMASK (1ULL << 30)
> +#define ZBKC_GROUPID 0
> +#define ZBKC_BITMASK (1ULL << 31)
> +#define ZBKX_GROUPID 0
> +#define ZBKX_BITMASK (1ULL << 32)
> +#define ZBS_GROUPID 0
> +#define ZBS_BITMASK (1ULL << 33)
> +#define ZFA_GROUPID 0
> +#define ZFA_BITMASK (1ULL << 34)
> +#define ZFH_GROUPID 0
> +#define ZFH_BITMASK (1ULL << 35)
> +#define ZFHMIN_GROUPID 0
> +#define ZFHMIN_BITMASK (1ULL << 36)
> +#define ZICBOZ_GROUPID 0
> +#define ZICBOZ_BITMASK (1ULL << 37)
> +#define ZICOND_GROUPID 0
> +#define ZICOND_BITMASK (1ULL << 38)
> +#define ZIHINTNTL_GROUPID 0
> +#define ZIHINTNTL_BITMASK (1ULL << 39)
> +#define ZIHINTPAUSE_GROUPID 0
> +#define ZIHINTPAUSE_BITMASK (1ULL << 40)
> +#define ZKND_GROUPID 0
> +#define ZKND_BITMASK (1ULL << 41)
> +#define ZKNE_GROUPID 0
> +#define ZKNE_BITMASK (1ULL << 42)
> +#define ZKNH_GROUPID 0
> +#define ZKNH_BITMASK (1ULL << 43)
> +#define ZKSED_GROUPID 0
> +#define ZKSED_BITMASK (1ULL << 44)
> +#define ZKSH_GROUPID 0
> +#define ZKSH_BITMASK (1ULL << 45)
> +#define ZKT_GROUPID 0
> +#define ZKT_BITMASK (1ULL << 46)
> +#define ZTSO_GROUPID 0
> +#define ZTSO_BITMASK (1ULL << 47)
> +#define ZVBB_GROUPID 0
> +#define ZVBB_BITMASK (1ULL << 48)
> +#define ZVBC_GROUPID 0
> +#define ZVBC_BITMASK (1ULL << 49)
> +#define ZVFH_GROUPID 0
> +#define ZVFH_BITMASK (1ULL << 50)
> +#define ZVFHMIN_GROUPID 0
> +#define ZVFHMIN_BITMASK (1ULL << 51)
> +#define ZVKB_GROUPID 0
> +#define ZVKB_BITMASK (1ULL << 52)
> +#define ZVKG_GROUPID 0
> +#define ZVKG_BITMASK (1ULL << 53)
> +#define ZVKNED_GROUPID 0
> +#define ZVKNED_BITMASK (1ULL << 54)
> +#define ZVKNHA_GROUPID 0
> +#define ZVKNHA_BITMASK (1ULL << 55)
> +#define ZVKNHB_GROUPID 0
> +#define ZVKNHB_BITMASK (1ULL << 56)
> +#define ZVKSED_GROUPID 0
> +#define ZVKSED_BITMASK (1ULL << 57)
> +#define ZVKSH_GROUPID 0
> +#define ZVKSH_BITMASK (1ULL << 58)
> +#define ZVKT_GROUPID 0
> +#define ZVKT_BITMASK (1ULL << 59)
> +
> +#define SET_EXT(EXT) features[EXT##_GROUPID] |= EXT##_BITMASK
> +
> +#ifdef __linux
> +
> +#define __NR_riscv_hwprobe 258
> +#define RISCV_HWPROBE_KEY_MVENDORID 0
> +#define RISCV_HWPROBE_KEY_MARCHID 1
> +#define RISCV_HWPROBE_KEY_MIMPID 2
> +#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
> +#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1ULL << 0)
> +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
> +#define RISCV_HWPROBE_IMA_FD (1ULL << 0)
> +#define RISCV_HWPROBE_IMA_C (1ULL << 1)
> +#define RISCV_HWPROBE_IMA_V (1ULL << 2)
> +#define RISCV_HWPROBE_EXT_ZBA (1ULL << 3)
> +#define RISCV_HWPROBE_EXT_ZBB (1ULL << 4)
> +#define RISCV_HWPROBE_EXT_ZBS (1ULL << 5)
> +#define RISCV_HWPROBE_EXT_ZICBOZ (1ULL << 6)
> +#define RISCV_HWPROBE_EXT_ZBC (1ULL << 7)
> +#define RISCV_HWPROBE_EXT_ZBKB (1ULL << 8)
> +#define RISCV_HWPROBE_EXT_ZBKC (1ULL << 9)
> +#define RISCV_HWPROBE_EXT_ZBKX (1ULL << 10)
> +#define RISCV_HWPROBE_EXT_ZKND (1ULL << 11)
> +#define RISCV_HWPROBE_EXT_ZKNE (1ULL << 12)
> +#define RISCV_HWPROBE_EXT_ZKNH (1ULL << 13)
> +#define RISCV_HWPROBE_EXT_ZKSED (1ULL << 14)
> +#define RISCV_HWPROBE_EXT_ZKSH (1ULL << 15)
> +#define RISCV_HWPROBE_EXT_ZKT (1ULL << 16)
> +#define RISCV_HWPROBE_EXT_ZVBB (1ULL << 17)
> +#define RISCV_HWPROBE_EXT_ZVBC (1ULL << 18)
> +#define RISCV_HWPROBE_EXT_ZVKB (1ULL << 19)
> +#define RISCV_HWPROBE_EXT_ZVKG (1ULL << 20)
> +#define RISCV_HWPROBE_EXT_ZVKNED (1ULL << 21)
> +#define RISCV_HWPROBE_EXT_ZVKNHA (1ULL << 22)
> +#define RISCV_HWPROBE_EXT_ZVKNHB (1ULL << 23)
> +#define RISCV_HWPROBE_EXT_ZVKSED (1ULL << 24)
> +#define RISCV_HWPROBE_EXT_ZVKSH (1ULL << 25)
> +#define RISCV_HWPROBE_EXT_ZVKT (1ULL << 26)
> +#define RISCV_HWPROBE_EXT_ZFH (1ULL << 27)
> +#define RISCV_HWPROBE_EXT_ZFHMIN (1ULL << 28)
> +#define RISCV_HWPROBE_EXT_ZIHINTNTL (1ULL << 29)
> +#define RISCV_HWPROBE_EXT_ZVFH (1ULL << 30)
> +#define RISCV_HWPROBE_EXT_ZVFHMIN (1ULL << 31)
> +#define RISCV_HWPROBE_EXT_ZFA (1ULL << 32)
> +#define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33)
> +#define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34)
> +#define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35)
> +#define RISCV_HWPROBE_EXT_ZIHINTPAUSE (1ULL << 36)
> +#define RISCV_HWPROBE_KEY_CPUPERF_0 5
> +#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
> +#define RISCV_HWPROBE_MISALIGNED_EMULATED (1ULL << 0)
> +#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
> +#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
> +#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
> +#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
> +#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
> +
> +struct riscv_hwprobe {
> +  long long key;
> +  unsigned long long value;
> +};
> +
> +static long syscall_5_args(long number, long arg1, long arg2, long arg3,
> +                          long arg4, long arg5) {
> +  register long a7 __asm__ ("a7") = number;
> +  register long a0 __asm__ ("a0") = arg1;
> +  register long a1 __asm__ ("a1") = arg2;
> +  register long a2 __asm__ ("a2") = arg3;
> +  register long a3 __asm__ ("a3") = arg4;
> +  register long a4 __asm__ ("a4") = arg5;
> +  __asm__ __volatile__("ecall\n\t"
> +                      : "=r"(a0)
> +                      : "r"(a7), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
> +                      : "memory");
> +  return a0;
> +}
> +
> +#define SET_FROM_HWPROBE(HWPROBE_VAR, EXT) \
> +  if (HWPROBE_VAR.value & RISCV_HWPROBE_EXT_##EXT) \
> +    SET_EXT (EXT)
> +
> +#define SET_FROM_IMA_EXT(EXT) \
> +  SET_FROM_HWPROBE (hwprobe_ima_ext, EXT)
> +
> +static void __init_riscv_features_bits_linux ()
> +{
> +  struct riscv_hwprobe hwprobes[] = {
> +    {RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0},
> +    {RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
> +    {RISCV_HWPROBE_KEY_MVENDORID, 0},
> +  };
> +
> +  long rv = syscall_5_args (__NR_riscv_hwprobe, (long)&hwprobes,
> +                           sizeof (hwprobes) / sizeof (hwprobes[0]), 0,
> +                           0, 0);
> +
> +  if (rv)
> +    return;
> +
> +  const struct riscv_hwprobe hwprobe_base_behavior = hwprobes[0];
> +  unsigned long long features[RISCV_FEATURE_BITS_LENGTH];
> +  int i;
> +  for (i = 0; i < RISCV_FEATURE_BITS_LENGTH; ++i)
> +    features[i] = 0;
> +
> +  if (hwprobe_base_behavior.value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA)
> +    {
> +      SET_EXT (I);
> +      SET_EXT (M);
> +      SET_EXT (A);
> +    }
> +
> +  const struct riscv_hwprobe hwprobe_mvendorid = hwprobes[2];
> +
> +  __riscv_vendor_feature_bits.length = 0;
> +  __riscv_vendor_feature_bits.vendorID = hwprobe_mvendorid.value;
> +
> +  const struct riscv_hwprobe hwprobe_ima_ext = hwprobes[1];
> +
> +  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_FD)
> +    {
> +      SET_EXT (F);
> +      SET_EXT (D);
> +    }
> +
> +  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_C)
> +    SET_EXT (C);
> +  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_V)
> +    SET_EXT (V);
> +
> +  SET_FROM_IMA_EXT (ZBA);
> +  SET_FROM_IMA_EXT (ZBB);
> +  SET_FROM_IMA_EXT (ZBS);
> +  SET_FROM_IMA_EXT (ZICBOZ);
> +  SET_FROM_IMA_EXT (ZBC);
> +  SET_FROM_IMA_EXT (ZBKB);
> +  SET_FROM_IMA_EXT (ZBKC);
> +  SET_FROM_IMA_EXT (ZBKX);
> +  SET_FROM_IMA_EXT (ZKND);
> +  SET_FROM_IMA_EXT (ZKNE);
> +  SET_FROM_IMA_EXT (ZKNH);
> +  SET_FROM_IMA_EXT (ZKSED);
> +  SET_FROM_IMA_EXT (ZKSH);
> +  SET_FROM_IMA_EXT (ZKT);
> +  SET_FROM_IMA_EXT (ZVBB);
> +  SET_FROM_IMA_EXT (ZVBC);
> +  SET_FROM_IMA_EXT (ZVKB);
> +  SET_FROM_IMA_EXT (ZVKG);
> +  SET_FROM_IMA_EXT (ZVKNED);
> +  SET_FROM_IMA_EXT (ZVKNHA);
> +  SET_FROM_IMA_EXT (ZVKNHB);
> +  SET_FROM_IMA_EXT (ZVKSED);
> +  SET_FROM_IMA_EXT (ZVKSH);
> +  SET_FROM_IMA_EXT (ZVKT);
> +  SET_FROM_IMA_EXT (ZFH);
> +  SET_FROM_IMA_EXT (ZFHMIN);
> +  SET_FROM_IMA_EXT (ZIHINTNTL);
> +  SET_FROM_IMA_EXT (ZIHINTPAUSE);
> +  SET_FROM_IMA_EXT (ZVFH);
> +  SET_FROM_IMA_EXT (ZVFHMIN);
> +  SET_FROM_IMA_EXT (ZFA);
> +  SET_FROM_IMA_EXT (ZTSO);
> +  SET_FROM_IMA_EXT (ZACAS);
> +  SET_FROM_IMA_EXT (ZICOND);
> +
> +  for (i = 0; i < RISCV_FEATURE_BITS_LENGTH; ++i)
> +    __riscv_feature_bits.features[i] = features[i];
> +
> +  __riscv_feature_bits.length = RISCV_FEATURE_BITS_LENGTH;
> +}
> +#endif
> +
> +
> +static int __init = 0;
> +
> +void __init_riscv_feature_bits ()
> +{
> +  if (__init)
> +    return;
> +
> +#ifdef __linux
> +  __init_riscv_features_bits_linux ();
> +#else
> +  /* Unsupported, just initlizaed that into all zeros.  */
> +  __riscv_feature_bits.length = 0
> +  __riscv_vendor_feature_bits.length = 0;
> +  __riscv_vendor_feature_bits.vendorID = 0;
> +#endif
> +
> +  __init = 1;
> +}
> diff --git a/libgcc/config/riscv/t-elf b/libgcc/config/riscv/t-elf
> index 415e1fffbe7..acb5e280676 100644
> --- a/libgcc/config/riscv/t-elf
> +++ b/libgcc/config/riscv/t-elf
> @@ -3,6 +3,7 @@ LIB2ADD += $(srcdir)/config/riscv/save-restore.S \
>            $(srcdir)/config/riscv/multi3.c \
>            $(srcdir)/config/riscv/div.S \
>            $(srcdir)/config/riscv/atomic.c \
> +          $(srcdir)/config/riscv/feature_bits.c \
>
>  # Avoid the full unwinder being pulled along with the division libcalls.
>  LIB2_DIVMOD_EXCEPTION_FLAGS := -fasynchronous-unwind-tables
> --
> 2.34.1
>

Reply via email to