public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Christoph Muellner <christoph.muellner@vrull.eu>
To: libc-alpha@sourceware.org, Palmer Dabbelt <palmer@dabbelt.com>,
	Darius Rad <darius@bluespec.com>,
	Andrew Waterman <andrew@sifive.com>, DJ Delorie <dj@redhat.com>,
	Vineet Gupta <vineetg@rivosinc.com>,
	Kito Cheng <kito.cheng@sifive.com>,
	Jeff Law <jeffreyalaw@gmail.com>,
	Philipp Tomsich <philipp.tomsich@vrull.eu>,
	Heiko Stuebner <heiko.stuebner@vrull.eu>
Cc: "Christoph Müllner" <christoph.muellner@vrull.eu>
Subject: [RFC PATCH 06/19] riscv: Adding ISA string parser for environment variables
Date: Tue,  7 Feb 2023 01:16:05 +0100	[thread overview]
Message-ID: <20230207001618.458947-7-christoph.muellner@vrull.eu> (raw)
In-Reply-To: <20230207001618.458947-1-christoph.muellner@vrull.eu>

From: Christoph Müllner <christoph.muellner@vrull.eu>

RISC-V does not have a reliable mechanism to detect hart features
like supported ISA extensions or cache block sizes at run-time
as of now.

Not knowing the hart features limits optimization strategies of glibc
(e.g. ifunc support requires run-time hard feature knowledge).

To circumvent this limitation this patch introduces a mechanism to
get the hart features via environment variables:
* RISCV_RT_MARCH represents a lower-case ISA string (-march string)
  E.g. RISCV_RT_MARCH=rv64gc_zicboz
* RISCV_RT_CBOM_BLOCKSIZE represents the cbom instruction block size
  E.g. RISCV_RT_CBOZ_BLOCKSIZE=64
* RISCV_RT_CBOZ_BLOCKSIZE represents the cboz instruction block size

These environment variables are parsed during startup and the found
ISA extensions are stored a struct (hart_features) for evaluation
by dynamic dispatching code.

As the parser code is executed very early, we cannot call functions
that have direct or indirect (via getenv()) dependencies to strlen()
and strncmp(), as these functions cannot be called before the ifunc
support is initialized. Therefore, this patch contains its own helper
functions for strlen(), strncmp(), and getenv().

Signed-off-by: Christoph Müllner <christoph.muellner@vrull.eu>
---
 sysdeps/unix/sysv/linux/riscv/hart-features.c | 294 ++++++++++++++++++
 .../unix/sysv/linux/riscv/macro-for-each.h    |  24 ++
 2 files changed, 318 insertions(+)
 create mode 100644 sysdeps/unix/sysv/linux/riscv/macro-for-each.h

diff --git a/sysdeps/unix/sysv/linux/riscv/hart-features.c b/sysdeps/unix/sysv/linux/riscv/hart-features.c
index 41111eff57..6de41a26cc 100644
--- a/sysdeps/unix/sysv/linux/riscv/hart-features.c
+++ b/sysdeps/unix/sysv/linux/riscv/hart-features.c
@@ -17,12 +17,17 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <hart-features.h>
+#include <macro-for-each.h>
+#include <string_private.h>
 
 /* The code in this file is executed very early, so we cannot call
    indirect functions because ifunc support is not initialized.
    Therefore this file adds a few simple helper functions to avoid
    dependencies to functions outside of this file.  */
 
+#define xstr(s) str(s)
+#define str(s) #s
+
 static inline void
 inhibit_loop_to_libcall
 simple_memset (void *s, int c, size_t n)
@@ -35,9 +40,298 @@ simple_memset (void *s, int c, size_t n)
     }
 }
 
+static inline size_t
+inhibit_loop_to_libcall
+simple_strlen (const char *s)
+{
+  size_t n = 0;
+  char c = *s;
+  while (c != 0)
+    {
+      s++;
+      n++;
+      c = *s;
+    }
+  return n;
+}
+
+static inline int
+inhibit_loop_to_libcall
+simple_strncmp (const char *s1, const char *s2, size_t n)
+{
+  while (n != 0)
+    {
+      if (*s1 == 0 || *s1 != *s2)
+	return *((const unsigned char *)s1) - *((const unsigned char *)s2);
+      n--;
+      s1++;
+      s2++;
+    }
+  return 0;
+}
+
+extern char **__environ;
+static inline char*
+simple_getenv (const char *name)
+{
+  char **ep;
+  uint16_t name_start;
+
+  if (__environ == NULL || name[0] == 0 || name[1] == 0)
+    return NULL;
+
+  size_t len = simple_strlen (name);
+#if _STRING_ARCH_unaligned
+  name_start = *(const uint16_t *) name;
+#else
+  name_start = (((const unsigned char *) name)[0]
+		| (((const unsigned char *) name)[1] << 8));
+#endif
+  len -= 2;
+  name += 2;
+
+  for (ep = __environ; *ep != NULL; ++ep)
+    {
+#if _STRING_ARCH_unaligned
+      uint16_t ep_start = *(uint16_t *) *ep;
+#else
+      uint16_t ep_start = (((unsigned char *) *ep)[0]
+			   | (((unsigned char *) *ep)[1] << 8));
+#endif
+      if (name_start == ep_start && !simple_strncmp (*ep + 2, name, len)
+	  && (*ep)[len + 2] == '=')
+	return &(*ep)[len + 3];
+    }
+  return NULL;
+}
+
+/* Check if the given number is a power of 2.
+   Return true if so, or false otherwise.  */
+static inline int
+is_power_of_two (unsigned long v)
+{
+  return (v & (v - 1)) == 0;
+}
+
+/* Check if the given string str starts with
+   the prefix pre.  Return true if so, or false
+   otherwise.  */
+static inline int
+starts_with (const char *str, const char *pre)
+{
+  return simple_strncmp (pre, str, simple_strlen (pre)) == 0;
+}
+
+/* Lower all characters of a string up to the
+   first NUL-character in the string.  */
+static inline void
+strtolower (char *s)
+{
+  char c = *s;
+  while (c != '\0')
+    {
+      if (c >= 'A' && c <= 'Z')
+	*s = c + 'a' - 'A';
+      s++;
+      c = *s;
+    }
+}
+
+/* Count the number of detected extensions.  */
+static inline unsigned long
+count_extensions (struct hart_features *hart_features)
+{
+  unsigned long n = 0;
+#define ISA_EXT(e)							\
+  if (hart_features->have_##e == 1)					\
+    n++;
+#define ISA_EXT_GROUP(g, ...)						\
+  if (hart_features->have_##g == 1)					\
+    n++;
+#include "isa-extensions.def"
+  return n;
+}
+
+/* Check if the given charater is not '0'-'9'.  */
+static inline int
+notanumber (const char c)
+{
+  return (c < '0' || c > '9');
+}
+
+/* Parse RISCV_RT_MARCH and store found extensions.  */
+static inline void
+parse_rt_march (struct hart_features *hart_features)
+{
+  const char* s = simple_getenv ("RISCV_RT_MARCH");
+  if (s == NULL)
+    goto end;
+
+  hart_features->rt_march = s;
+
+  /* "RISC-V ISA strings begin with either RV32I, RV32E, RV64I, or RV128I
+      indicating the supported address space size in bits for the base
+      integer ISA."  */
+  if (starts_with (s, "rv32") && notanumber (*(s+4)))
+    {
+      hart_features->xlen = 32;
+      s += 4;
+    }
+  else if (starts_with (s, "rv64") && notanumber (*(s+4)))
+    {
+      hart_features->xlen = 64;
+      s += 4;
+    }
+  else if (starts_with (s, "rv128") && notanumber (*(s+5)))
+    {
+      hart_features->xlen = 128;
+      s += 5;
+    }
+  else
+    {
+      goto fail;
+    }
+
+  /* Parse the extensions.  */
+  const char *s_old = s;
+  while (*s != '\0')
+    {
+#define ISA_EXT(e)							\
+      else if (starts_with (s, xstr (e)))				\
+	{								\
+	  hart_features->have_##e = 1;					\
+	  s += simple_strlen (xstr (e));				\
+	}
+#define ISA_EXT_GROUP(g, ...)						\
+      ISA_EXT (g)
+      if (0);
+#include "isa-extensions.def"
+
+      /* Consume optional version information.  */
+      while (*s >= '0' && *s <= '9')
+	s++;
+      while (*s == 'p')
+	s++;
+      while (*s >= '0' && *s <= '9')
+	s++;
+
+      /* Consume optional '_'.  */
+      if (*s == '_')
+	s++;
+
+      /* If we got stuck, bail out.  */
+      if (s == s_old)
+	goto fail;
+    }
+
+  /* Propagate subsets (until we reach a fixpoint).  */
+  unsigned long n = count_extensions (hart_features);
+  while (1)
+    {
+      /* Forward-propagation.  E.g.:
+      if (hart_features->have_g == 1)
+	{
+	  hart_features->have_i = 1;
+	  ...
+	  hart_features->have_zifencei = 1;
+	}  */
+#define ISA_EXT_GROUP_HEAD(y)						\
+      if (hart_features->have_##y)					\
+	{
+#define ISA_EXT_GROUP_SUBSET(s)						\
+	  hart_features->have_##s = 1;
+#define ISA_EXT_GROUP_TAIL(z)						\
+	}
+#define ISA_EXT_GROUP(x, ...)						\
+	ISA_EXT_GROUP_HEAD (x)						\
+	FOR_EACH (ISA_EXT_GROUP_SUBSET, __VA_ARGS__)			\
+	ISA_EXT_GROUP_TAIL (x)
+#include "isa-extensions.def"
+#undef ISA_EXT_GROUP_HEAD
+#undef ISA_EXT_GROUP_SUBSET
+#undef ISA_EXT_GROUP_TAIL
+
+      /* Backward-propagation.  E.g.:
+      if (1
+	  && hart_features->have_i == 1
+	  ...
+	  && hart_features->have_zifencei == 1
+	  )
+	hart_features->have_g = 1;  */
+#define ISA_EXT_GROUP_HEAD(y)						\
+      if (1
+#define ISA_EXT_GROUP_SUBSET(s)						\
+	  && hart_features->have_##s == 1
+#define ISA_EXT_GROUP_TAIL(z)						\
+	  )								\
+	hart_features->have_##z = 1;
+#define ISA_EXT_GROUP(x, ...)						\
+	ISA_EXT_GROUP_HEAD (x)						\
+	FOR_EACH (ISA_EXT_GROUP_SUBSET, __VA_ARGS__)			\
+	ISA_EXT_GROUP_TAIL (x)
+#include "isa-extensions.def"
+#undef ISA_EXT_GROUP_HEAD
+#undef ISA_EXT_GROUP_SUBSET
+#undef ISA_EXT_GROUP_TAIL
+
+      unsigned long n2 = count_extensions (hart_features);
+      /* Stop if fix-point reached.  */
+      if (n == n2)
+	break;
+      n = n2;
+    }
+
+end:
+  return;
+
+fail:
+  hart_features->rt_march = NULL;
+}
+
+/* Parse RISCV_RT_CBOM_BLOCKSIZE and store value.  */
+static inline void
+parse_rt_cbom_blocksize (struct hart_features *hart_features)
+{
+  hart_features->rt_cbom_blocksize = NULL;
+  hart_features->cbom_blocksize = 0;
+
+  const char *s = simple_getenv ("RISCV_RT_CBOM_BLOCKSIZE");
+  if (s == NULL)
+    return;
+
+  uint64_t v = _dl_strtoul (s, NULL);
+  if (!is_power_of_two (v))
+    return;
+
+  hart_features->rt_cbom_blocksize = s;
+  hart_features->cbom_blocksize = v;
+}
+
+/* Parse RISCV_RT_CBOZ_BLOCKSIZE and store value.  */
+static inline void
+parse_rt_cboz_blocksize (struct hart_features *hart_features)
+{
+  hart_features->rt_cboz_blocksize = NULL;
+  hart_features->cboz_blocksize = 0;
+
+  const char *s = simple_getenv ("RISCV_RT_CBOZ_BLOCKSIZE");
+  if (s == NULL)
+    return;
+
+  uint64_t v = _dl_strtoul (s, NULL);
+  if (!is_power_of_two (v))
+    return;
+
+  hart_features->rt_cboz_blocksize = s;
+  hart_features->cboz_blocksize = v;
+}
+
 /* Discover hart features and store them.  */
 static inline void
 init_hart_features (struct hart_features *hart_features)
 {
   simple_memset (hart_features, 0, sizeof (*hart_features));
+  parse_rt_march (hart_features);
+  parse_rt_cbom_blocksize (hart_features);
+  parse_rt_cboz_blocksize (hart_features);
 }
diff --git a/sysdeps/unix/sysv/linux/riscv/macro-for-each.h b/sysdeps/unix/sysv/linux/riscv/macro-for-each.h
new file mode 100644
index 0000000000..524bef3c0a
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/riscv/macro-for-each.h
@@ -0,0 +1,24 @@
+/* Recursive macros implementation by David Mazières
+   https://www.scs.stanford.edu/~dm/blog/va-opt.html  */
+
+#ifndef _MACRO_FOR_EACH_H
+#define _MACRO_FOR_EACH_H
+
+#define EXPAND1(...) __VA_ARGS__
+#define EXPAND2(...) EXPAND1 (EXPAND1 (EXPAND1 (EXPAND1 (__VA_ARGS__))))
+#define EXPAND3(...) EXPAND2 (EXPAND2 (EXPAND2 (EXPAND2 (__VA_ARGS__))))
+#define EXPAND4(...) EXPAND3 (EXPAND3 (EXPAND3 (EXPAND3 (__VA_ARGS__))))
+#define EXPAND(...)  EXPAND4 (EXPAND4 (EXPAND4 (EXPAND4 (__VA_ARGS__))))
+
+#define FOR_EACH(macro, ...)						\
+  __VA_OPT__ (EXPAND (FOR_EACH_HELPER (macro, __VA_ARGS__)))
+
+#define PARENS ()
+
+#define FOR_EACH_HELPER(macro, a1, ...)					\
+  macro (a1)								\
+  __VA_OPT__ (FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
+
+#define FOR_EACH_AGAIN() FOR_EACH_HELPER
+
+#endif /* _MACRO_FOR_EACH_H  */
-- 
2.39.1


  parent reply	other threads:[~2023-02-07  0:16 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-07  0:15 [RFC PATCH 00/19] riscv: ifunc support with optimized mem*/str*/cpu_relax routines Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 01/19] Inhibit early libcalls before ifunc support is ready Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 02/19] riscv: LEAF: Use C_LABEL() to construct the asm name for a C symbol Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 03/19] riscv: Add ENTRY_ALIGN() macro Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 04/19] riscv: Add hart feature run-time detection framework Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 05/19] riscv: Introduction of ISA extensions Christoph Muellner
2023-02-07  0:16 ` Christoph Muellner [this message]
2023-02-07  6:20   ` [RFC PATCH 06/19] riscv: Adding ISA string parser for environment variables David Abdurachmanov
2023-02-07  0:16 ` [RFC PATCH 07/19] riscv: hart-features: Add fast_unaligned property Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 08/19] riscv: Add (empty) ifunc framework Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 09/19] riscv: Add ifunc support for memset Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 10/19] riscv: Add accelerated memset routines for RV64 Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 11/19] riscv: Add ifunc support for memcpy/memmove Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 12/19] riscv: Add accelerated memcpy/memmove routines for RV64 Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 13/19] riscv: Add ifunc support for strlen Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 14/19] riscv: Add accelerated strlen routine Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 15/19] riscv: Add ifunc support for strcmp Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 16/19] riscv: Add accelerated strcmp routines Christoph Muellner
2023-02-07 11:57   ` Xi Ruoyao
2023-02-07 14:15     ` Christoph Müllner
2023-03-31  5:06       ` Jeff Law
2023-03-31 12:31         ` Adhemerval Zanella Netto
2023-03-31 14:30           ` Jeff Law
2023-03-31 14:48             ` Adhemerval Zanella Netto
2023-03-31 17:19               ` Palmer Dabbelt
2023-03-31 14:32       ` Jeff Law
2023-02-07  0:16 ` [RFC PATCH 17/19] riscv: Add ifunc support for strncmp Christoph Muellner
2023-02-07  0:16 ` [RFC PATCH 18/19] riscv: Add an optimized strncmp routine Christoph Muellner
2023-02-07  1:19   ` Noah Goldstein
2023-02-08 15:13     ` Philipp Tomsich
2023-02-08 17:55       ` Palmer Dabbelt
2023-02-08 19:48         ` Adhemerval Zanella Netto
2023-02-08 18:04       ` Noah Goldstein
2023-02-07  0:16 ` [RFC PATCH 19/19] riscv: Add __riscv_cpu_relax() to allow yielding in busy loops Christoph Muellner
2023-02-07  0:23   ` Andrew Waterman
2023-02-07  0:29     ` Christoph Müllner
2023-02-07  2:59 ` [RFC PATCH 00/19] riscv: ifunc support with optimized mem*/str*/cpu_relax routines Kito Cheng
2023-02-07 16:40 ` Adhemerval Zanella Netto
2023-02-07 17:16   ` DJ Delorie
2023-02-07 19:32     ` Philipp Tomsich
2023-02-07 21:14       ` DJ Delorie
2023-02-08 11:26         ` Christoph Müllner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230207001618.458947-7-christoph.muellner@vrull.eu \
    --to=christoph.muellner@vrull.eu \
    --cc=andrew@sifive.com \
    --cc=darius@bluespec.com \
    --cc=dj@redhat.com \
    --cc=heiko.stuebner@vrull.eu \
    --cc=jeffreyalaw@gmail.com \
    --cc=kito.cheng@sifive.com \
    --cc=libc-alpha@sourceware.org \
    --cc=palmer@dabbelt.com \
    --cc=philipp.tomsich@vrull.eu \
    --cc=vineetg@rivosinc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).