From patchwork Tue Feb 7 00:16:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Christoph_M=C3=BCllner?= X-Patchwork-Id: 64393 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 794073894C2A for ; Tue, 7 Feb 2023 00:19:39 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by sourceware.org (Postfix) with ESMTPS id 0ABCC3858C62 for ; Tue, 7 Feb 2023 00:16:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0ABCC3858C62 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=vrull.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=vrull.eu Received: by mail-wm1-x335.google.com with SMTP id z13so2433241wmp.2 for ; Mon, 06 Feb 2023 16:16:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vrull.eu; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=y0pe2Jlsqh24PBbKuqrb5puaOeETVYzfcrURNZiRsmY=; b=MqABSmo8hzx5QnT9kaVenCrKDTUIhXr94oyyIg8EW+Cej+XmNvZTl/lWHkipnPFioG pIyay/jQjtTTTFLqtPiyxuzglhpG6MfV2zcKY7joBFQxTCJ4B3jo+v03iWRTo10GPN2U HnsKhWus1IvwUVnAPWvcoAiFv4Puf7UM3XgwfZz9yEgVujEvmuGJiCtRqe1AP0Z3uvWo e0vKGscV74siopnVWOuRqRKk6/MjdziLnXb2jnXTA03AElAGUSIzLmIXCkzGSg1KTPGh ln+Zp6ttxH0JVrMjRzisyNn0VB2rT/D2yWixRnmMmyt3oGvtOMk7w22Qp9a4kRGw5jGb mwdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=y0pe2Jlsqh24PBbKuqrb5puaOeETVYzfcrURNZiRsmY=; b=dSWSPxHLVAPWfIMDSV2GRBybZeiUTQ18DT6GVzj6KDK0nqcfMY9jd8dFmvtWAJcWu1 pL3wCUTVW9aKgiO4vdZtnXhQnNLYxEiLEDaBsX+hYu4BsZG02RaV8MYPaHmlvvKWhIiH B/MmeNKdWTz2ttZUbHr9avH781f+5fqRnz85ohBIG7TtCndjyKOeP5WRKvaZGDYZHrwH 2+w/GIRS9quSE8oCd3Na9JBVtYLmC2fD2CYpTjMN5l4R7JV3NeLf6X7hoKJ85EPUSRY+ GrQQHD6W4OfISxI0CuQMZdvV0a+TqSh13djbvOIQkka63j3YC4qE2IPEo4Ti8kPx/jP+ Wrfg== X-Gm-Message-State: AO0yUKXLIE2m6YkBsr9+OideyT8UwM4V4qWJSRVgkJ/2SRBusEXKSabB dJfXHqAfSBTOZQvvAQVR3k+Tq3VKOrRkD7of X-Google-Smtp-Source: AK7set9YkBkSA8kdqe27yYKdK0nPdCacI6z3Hpa3bOf+Oml0xcMZxuGgtx4zLdlolNAbokQuoC9acg== X-Received: by 2002:a05:600c:502b:b0:3dc:c05:9db6 with SMTP id n43-20020a05600c502b00b003dc0c059db6mr1252975wmr.33.1675728999235; Mon, 06 Feb 2023 16:16:39 -0800 (PST) Received: from beast.fritz.box (62-178-148-172.cable.dynamic.surfer.at. [62.178.148.172]) by smtp.gmail.com with ESMTPSA id f1-20020a1cc901000000b003df14531724sm16862050wmb.21.2023.02.06.16.16.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Feb 2023 16:16:38 -0800 (PST) From: Christoph Muellner To: libc-alpha@sourceware.org, Palmer Dabbelt , Darius Rad , Andrew Waterman , DJ Delorie , Vineet Gupta , Kito Cheng , Jeff Law , Philipp Tomsich , Heiko Stuebner Cc: =?utf-8?q?Christoph_M=C3=BCllner?= Subject: [RFC PATCH 06/19] riscv: Adding ISA string parser for environment variables Date: Tue, 7 Feb 2023 01:16:05 +0100 Message-Id: <20230207001618.458947-7-christoph.muellner@vrull.eu> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230207001618.458947-1-christoph.muellner@vrull.eu> References: <20230207001618.458947-1-christoph.muellner@vrull.eu> MIME-Version: 1.0 X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_MANYTO, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" From: Christoph Müllner 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 --- 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 @@ . */ #include +#include +#include /* 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 */