[PATCH 2/3] RISC-V: Support -march=native on Linux hosts
Kito Cheng <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
Detect the ISA with the Linux hwprobe syscall and rewrite -march=native
into an ordinary -march= option, letting riscv_expand_arch canonicalise
the result.
gcc/ChangeLog:
* config.host: Build driver-riscv.o for riscv*-*-linux* hosts.
* config/riscv/driver-riscv.cc: New file.
* config/riscv/x-riscv: New file.
* config/riscv/riscv.h (host_detect_local_cpu): Declare.
(HAVE_LOCAL_CPU_DETECT): Define.
(RISCV_NATIVE_SPEC_FUNCTIONS): Define.
(RISCV_NATIVE_SPECS): Define.
(EXTRA_SPEC_FUNCTIONS): Add RISCV_NATIVE_SPEC_FUNCTIONS.
(DRIVER_SELF_SPECS): Add RISCV_NATIVE_SPECS.
* doc/invoke.texi (RISC-V Options): Document -march=native.
gcc/testsuite/ChangeLog:
* lib/target-supports.exp
(check_effective_target_riscv_native_cpu_detect): New.
* gcc.target/riscv/march-native-1.c: New test.
---
gcc/common/config/riscv/riscv-hwprobe.h | 3 +
gcc/config.host | 8 +
gcc/config/riscv/driver-riscv.cc | 162 ++++++++++++++++++
gcc/config/riscv/riscv.h | 19 +-
gcc/config/riscv/x-riscv | 5 +
gcc/doc/invoke.texi | 10 ++
.../gcc.target/riscv/march-native-1.c | 21 +++
gcc/testsuite/lib/target-supports.exp | 14 ++
8 files changed, 241 insertions(+), 1 deletion(-)
create mode 100644 gcc/config/riscv/driver-riscv.cc
create mode 100644 gcc/config/riscv/x-riscv
create mode 100644 gcc/testsuite/gcc.target/riscv/march-native-1.c
diff --git a/gcc/common/config/riscv/riscv-hwprobe.h b/gcc/common/config/riscv/riscv-hwprobe.h
index f1fe2aeac2c..ba989b99d2f 100644
--- a/gcc/common/config/riscv/riscv-hwprobe.h
+++ b/gcc/common/config/riscv/riscv-hwprobe.h
@@ -33,6 +33,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
enum {
#define RISCV_HWPROBE_KEY(NAME, VALUE) RISCV_HWPROBE_KEY_##NAME = VALUE,
#include "common/config/riscv/riscv-hwprobe.def"
+#define RISCV_HWPROBE_EXT(NAME, UPPERCASE_NAME, KEY, BIT, XLEN) \
+ RISCV_HWPROBE_EXT_##UPPERCASE_NAME = (1ULL << BIT),
+#include "common/config/riscv/riscv-hwprobe.def"
};
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1ULL << 0)
diff --git a/gcc/config.host b/gcc/config.host
index c9be68acb82..7e77e422d5b 100644
--- a/gcc/config.host
+++ b/gcc/config.host
@@ -142,6 +142,14 @@ case ${host} in
;;
esac
;;
+ riscv*-*-linux*)
+ case ${target} in
+ riscv*-*-*)
+ host_extra_gcc_objs="driver-riscv.o"
+ host_xmake_file="${host_xmake_file} riscv/x-riscv"
+ ;;
+ esac
+ ;;
rs6000-*-* \
| powerpc*-*-* )
case ${target} in
diff --git a/gcc/config/riscv/driver-riscv.cc b/gcc/config/riscv/driver-riscv.cc
new file mode 100644
index 00000000000..2997c5c2fe9
--- /dev/null
+++ b/gcc/config/riscv/driver-riscv.cc
@@ -0,0 +1,162 @@
+/* Native CPU detection for RISC-V.
+ Copyright (C) 2026 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 IN_TARGET_CODE 1
+
+#include "config.h"
+#define INCLUDE_STRING
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+
+#ifdef __linux__
+#include "common/config/riscv/riscv-hwprobe.h"
+
+/* One entry per extension the kernel can report. */
+
+struct riscv_hwprobe_ext
+{
+ const char *name;
+ int key;
+ int bit;
+ int xlen;
+};
+
+static const struct riscv_hwprobe_ext riscv_hwprobe_exts[] = {
+#define RISCV_HWPROBE_EXT(NAME, UPPERCASE_NAME, KEY, BIT, XLEN) \
+ { NAME, KEY, BIT, XLEN },
+#include "common/config/riscv/riscv-hwprobe.def"
+};
+
+/* Append the Zvl extension for this machine's vector register width,
+ which only the vlenb CSR reports. */
+
+static void
+riscv_add_vlen (std::string &isa)
+{
+ unsigned long vlenb;
+ unsigned long vlen;
+ char buf[32];
+
+ __asm__ volatile ("csrr %0, 0xc22" : "=r" (vlenb));
+
+ vlen = vlenb * 8;
+
+ gcc_assert (vlen >= 32 && vlen <= 65536 && (vlen & (vlen - 1)) == 0);
+
+ snprintf (buf, sizeof (buf), "_zvl%lub", vlen);
+ isa += buf;
+}
+
+/* Build the ISA string from what hwprobe reports, and set *VECTOR_P if the
+ machine can execute vector instructions. Return false if hwprobe cannot
+ describe the machine. */
+
+static bool
+riscv_arch_from_hwprobe (std::string &isa, bool *vector_p)
+{
+ struct riscv_hwprobe pairs[] = {
+ { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 },
+ { RISCV_HWPROBE_KEY_IMA_EXT_1, 0 }
+ };
+ size_t npairs = ARRAY_SIZE (pairs);
+ unsigned long long ima0;
+
+ if (riscv_hwprobe (pairs, npairs) != 0)
+ return false;
+
+ const int xlen = __riscv_xlen;
+
+ isa = xlen == 32 ? "rv32ima" : "rv64ima";
+
+ for (size_t i = 0; i < ARRAY_SIZE (riscv_hwprobe_exts); i++)
+ {
+ const struct riscv_hwprobe_ext *ext = &riscv_hwprobe_exts[i];
+ unsigned long long value;
+
+ if (ext->xlen != 0 && ext->xlen != xlen)
+ continue;
+
+ value = riscv_hwprobe_value (pairs, npairs, ext->key);
+ if (value & (1ULL << ext->bit))
+ {
+ isa += '_';
+ isa += ext->name;
+ }
+ }
+
+ ima0 = riscv_hwprobe_value (pairs, npairs, RISCV_HWPROBE_KEY_IMA_EXT_0);
+
+ if (ima0 & (RISCV_HWPROBE_IMA_V
+ | RISCV_HWPROBE_EXT_ZVE32X
+ | RISCV_HWPROBE_EXT_ZVE64X))
+ *vector_p = true;
+
+ return true;
+}
+
+/* Return the ISA string describing this machine, or NULL if it cannot be
+ determined. */
+
+static const char *
+riscv_native_arch (void)
+{
+ std::string isa;
+ bool vector_p = false;
+
+ if (!riscv_arch_from_hwprobe (isa, &vector_p))
+ return NULL;
+
+ if (vector_p)
+ riscv_add_vlen (isa);
+
+ return xstrdup (isa.c_str ());
+}
+
+#else /* !__linux__ */
+
+static const char *
+riscv_native_arch (void)
+{
+ return NULL;
+}
+
+#endif /* __linux__ */
+
+/* Implement the local_cpu_detect spec function. ARGV[0] selects what to
+ report: "arch" for an -march= option. */
+
+const char *
+host_detect_local_cpu (int argc, const char **argv)
+{
+ if (argc < 1 || argv[0] == NULL)
+ return NULL;
+
+ if (strcmp (argv[0], "arch") == 0)
+ {
+ const char *isa = riscv_native_arch ();
+
+ if (isa == NULL)
+ return NULL;
+
+ return concat ("-march=", isa, NULL);
+ }
+
+ return NULL;
+}
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 4ba6ad260e5..80356eacadb 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -52,12 +52,26 @@ extern const char *riscv_default_mtune (int argc, const char **argv);
extern const char *riscv_multi_lib_check (int argc, const char **argv);
extern const char *riscv_arch_help (int argc, const char **argv);
+#if defined (__riscv) && defined (__linux__)
+extern const char *host_detect_local_cpu (int argc, const char **argv);
+#define HAVE_LOCAL_CPU_DETECT
+# define RISCV_NATIVE_SPEC_FUNCTIONS \
+ { "local_cpu_detect", host_detect_local_cpu },
+
+# define RISCV_NATIVE_SPECS \
+ "%{march=native:%<march=native %:local_cpu_detect(arch)} "
+#else
+# define RISCV_NATIVE_SPEC_FUNCTIONS
+# define RISCV_NATIVE_SPECS ""
+#endif
+
# define EXTRA_SPEC_FUNCTIONS \
{ "riscv_expand_arch", riscv_expand_arch }, \
{ "riscv_expand_arch_from_cpu", riscv_expand_arch_from_cpu }, \
{ "riscv_default_mtune", riscv_default_mtune }, \
{ "riscv_multi_lib_check", riscv_multi_lib_check }, \
- { "riscv_arch_help", riscv_arch_help },
+ { "riscv_arch_help", riscv_arch_help }, \
+ RISCV_NATIVE_SPEC_FUNCTIONS
/* Support for a compile-time default CPU, et cetera. The rules are:
--with-cpu is ignored if -mcpu is specified.
@@ -119,8 +133,11 @@ ASM_MISA_SPEC
#define ARCH_UNSET_CLEANUP_SPECS \
"%{march=unset:%<march=*} " \
+/* RISCV_NATIVE_SPECS comes first so that the specs below, which expand and
+ validate -march=, see the option it produced. */
#undef DRIVER_SELF_SPECS
#define DRIVER_SELF_SPECS \
+RISCV_NATIVE_SPECS, \
ARCH_UNSET_CLEANUP_SPECS \
"%{march=help:%:riscv_arch_help()} " \
"%{print-supported-extensions:%:riscv_arch_help()} " \
diff --git a/gcc/config/riscv/x-riscv b/gcc/config/riscv/x-riscv
new file mode 100644
index 00000000000..55f32d7dd1c
--- /dev/null
+++ b/gcc/config/riscv/x-riscv
@@ -0,0 +1,5 @@
+driver-riscv.o: $(srcdir)/config/riscv/driver-riscv.cc \
+ $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(CORETYPES_H) \
+ $(srcdir)/common/config/riscv/riscv-hwprobe.h \
+ $(srcdir)/common/config/riscv/riscv-hwprobe.def
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2e53a8c8a7e..5cc8dcca3c8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -31325,6 +31325,16 @@ passed. This is useful for ensuring that the architecture is taken from the
@option{-mcpu} option, and an error results if no @option{-mcpu} option
is given when @option{-march=unset} is used.
+@option{-march=native} causes the compiler to work out the ISA of the
+machine it is running on. It is only available on a native GNU/Linux
+compiler; anywhere else the value is rejected like any other unknown ISA
+string. On a system whose cores are not all alike, such as one pairing
+fast and efficient cores, which of them the detected ISA describes is
+unspecified; write the ISA string out in full if it matters. If the
+running machine cannot be identified the option is ignored, and the
+architecture is taken from @option{-mcpu} or from the configured default
+as usual.
+
The syntax of the ISA string is defined as follows:
@itemize @bullet{}
diff --git a/gcc/testsuite/gcc.target/riscv/march-native-1.c b/gcc/testsuite/gcc.target/riscv/march-native-1.c
new file mode 100644
index 00000000000..6ffd686e515
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/march-native-1.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target riscv_native_cpu_detect } */
+/* { dg-options "-march=native" } */
+
+#ifndef __riscv_arch_test
+#error "-march=native did not produce a usable ISA string"
+#endif
+
+#ifndef __riscv_mul
+#error "-march=native lost the M extension"
+#endif
+
+#ifndef __riscv_atomic
+#error "-march=native lost the A extension"
+#endif
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index eb2444204a0..fc5309dad41 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -2064,6 +2064,20 @@ proc check_effective_target_rv_float_abi_soft { } {
}]
}
+# Return 1 if the compiler under test can work out the ISA of the machine
+# it is running on, 0 otherwise. This is only true of a native RISC-V
+# Linux compiler; a cross compiler rejects -march=native outright.
+# Cache the result.
+
+proc check_effective_target_riscv_native_cpu_detect { } {
+ if { ![istarget riscv*-*-linux*] } {
+ return 0
+ }
+ return [check_no_compiler_messages riscv_native_cpu_detect object {
+ int dummy;
+ } "-march=native"]
+}
+
# Return 1 if the target arch supports the atomic extension, 0 otherwise.
# Cache the result.
--
2.53.0