mirror of
https://github.com/qemu/qemu.git
synced 2024-11-24 03:13:44 +08:00
f7230e09b1
Add support for probing RISC-V vector extension availability in the backend. This information will be used when deciding whether to use vector instructions in code generation. Cache lg2(vlenb) for the backend. The storing of lg2(vlenb) means we can convert all of the division into subtraction. While the compiler doesn't support RISCV_HWPROBE_EXT_ZVE64X, we use RISCV_HWPROBE_IMA_V instead. RISCV_HWPROBE_IMA_V is more strictly constrainted than RISCV_HWPROBE_EXT_ZVE64X. At least in current QEMU implemenation, the V vector extension depends on the zve64d extension. Signed-off-by: TANG Tiancheng <tangtiancheng.ttc@alibaba-inc.com> Reviewed-by: Liu Zhiwei <zhiwei_liu@linux.alibaba.com> Tested-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241007025700.47259-2-zhiwei_liu@linux.alibaba.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
152 lines
4.5 KiB
C
152 lines
4.5 KiB
C
/*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Host specific cpu identification for RISC-V.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/host-utils.h"
|
|
#include "host/cpuinfo.h"
|
|
|
|
#ifdef CONFIG_ASM_HWPROBE_H
|
|
#include <asm/hwprobe.h>
|
|
#include <sys/syscall.h>
|
|
#include <asm/unistd.h>
|
|
#endif
|
|
|
|
unsigned cpuinfo;
|
|
unsigned riscv_lg2_vlenb;
|
|
static volatile sig_atomic_t got_sigill;
|
|
|
|
static void sigill_handler(int signo, siginfo_t *si, void *data)
|
|
{
|
|
/* Skip the faulty instruction */
|
|
ucontext_t *uc = (ucontext_t *)data;
|
|
|
|
#ifdef __linux__
|
|
uc->uc_mcontext.__gregs[REG_PC] += 4;
|
|
#elif defined(__OpenBSD__)
|
|
uc->sc_sepc += 4;
|
|
#else
|
|
# error Unsupported OS
|
|
#endif
|
|
|
|
got_sigill = 1;
|
|
}
|
|
|
|
/* Called both as constructor and (possibly) via other constructors. */
|
|
unsigned __attribute__((constructor)) cpuinfo_init(void)
|
|
{
|
|
unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND | CPUINFO_ZVE64X;
|
|
unsigned info = cpuinfo;
|
|
|
|
if (info) {
|
|
return info;
|
|
}
|
|
|
|
/* Test for compile-time settings. */
|
|
#if defined(__riscv_arch_test) && defined(__riscv_zba)
|
|
info |= CPUINFO_ZBA;
|
|
#endif
|
|
#if defined(__riscv_arch_test) && defined(__riscv_zbb)
|
|
info |= CPUINFO_ZBB;
|
|
#endif
|
|
#if defined(__riscv_arch_test) && defined(__riscv_zicond)
|
|
info |= CPUINFO_ZICOND;
|
|
#endif
|
|
#if defined(__riscv_arch_test) && \
|
|
(defined(__riscv_vector) || defined(__riscv_zve64x))
|
|
info |= CPUINFO_ZVE64X;
|
|
#endif
|
|
left &= ~info;
|
|
|
|
#ifdef CONFIG_ASM_HWPROBE_H
|
|
if (left) {
|
|
/*
|
|
* TODO: glibc 2.40 will introduce <sys/hwprobe.h>, which
|
|
* provides __riscv_hwprobe and __riscv_hwprobe_one,
|
|
* which is a slightly cleaner interface.
|
|
*/
|
|
struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 };
|
|
if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0
|
|
&& pair.key >= 0) {
|
|
info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0;
|
|
info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0;
|
|
left &= ~(CPUINFO_ZBA | CPUINFO_ZBB);
|
|
#ifdef RISCV_HWPROBE_EXT_ZICOND
|
|
info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0;
|
|
left &= ~CPUINFO_ZICOND;
|
|
#endif
|
|
/* For rv64, V is Zve64d, a superset of Zve64x. */
|
|
info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0;
|
|
#ifdef RISCV_HWPROBE_EXT_ZVE64X
|
|
info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0;
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* CONFIG_ASM_HWPROBE_H */
|
|
|
|
/*
|
|
* We only detect support for vectors with hwprobe. All kernels with
|
|
* support for vectors in userspace also support the hwprobe syscall.
|
|
*/
|
|
left &= ~CPUINFO_ZVE64X;
|
|
|
|
if (left) {
|
|
struct sigaction sa_old, sa_new;
|
|
|
|
memset(&sa_new, 0, sizeof(sa_new));
|
|
sa_new.sa_flags = SA_SIGINFO;
|
|
sa_new.sa_sigaction = sigill_handler;
|
|
sigaction(SIGILL, &sa_new, &sa_old);
|
|
|
|
if (left & CPUINFO_ZBA) {
|
|
/* Probe for Zba: add.uw zero,zero,zero. */
|
|
got_sigill = 0;
|
|
asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero"
|
|
: : : "memory");
|
|
info |= got_sigill ? 0 : CPUINFO_ZBA;
|
|
left &= ~CPUINFO_ZBA;
|
|
}
|
|
|
|
if (left & CPUINFO_ZBB) {
|
|
/* Probe for Zbb: andn zero,zero,zero. */
|
|
got_sigill = 0;
|
|
asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero"
|
|
: : : "memory");
|
|
info |= got_sigill ? 0 : CPUINFO_ZBB;
|
|
left &= ~CPUINFO_ZBB;
|
|
}
|
|
|
|
if (left & CPUINFO_ZICOND) {
|
|
/* Probe for Zicond: czero.eqz zero,zero,zero. */
|
|
got_sigill = 0;
|
|
asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero"
|
|
: : : "memory");
|
|
info |= got_sigill ? 0 : CPUINFO_ZICOND;
|
|
left &= ~CPUINFO_ZICOND;
|
|
}
|
|
|
|
sigaction(SIGILL, &sa_old, NULL);
|
|
assert(left == 0);
|
|
}
|
|
|
|
if (info & CPUINFO_ZVE64X) {
|
|
/*
|
|
* We are guaranteed by RVV-1.0 that VLEN is a power of 2.
|
|
* We are guaranteed by Zve64x that VLEN >= 64, and that
|
|
* EEW of {8,16,32,64} are supported.
|
|
*/
|
|
unsigned long vlenb;
|
|
/* csrr %0, vlenb */
|
|
asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb));
|
|
assert(vlenb >= 8);
|
|
assert(is_power_of_2(vlenb));
|
|
/* Cache VLEN in a convenient form. */
|
|
riscv_lg2_vlenb = ctz32(vlenb);
|
|
}
|
|
|
|
info |= CPUINFO_ALWAYS;
|
|
cpuinfo = info;
|
|
return info;
|
|
}
|