use crate::isa::{Arch, Isa, IsaSet};
mod by_scalar;
mod reduce;
#[cfg(tract_rvv)]
mod rvv;
mod unicast;
#[cfg(tract_rvv)]
pub use rvv::*;
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
const NR_RISCV_HWPROBE: libc::c_long = 258;
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
const HWPROBE_KEY_IMA_EXT_0: i64 = 4;
const HWPROBE_IMA_V: u64 = 1 << 2;
const HWPROBE_EXT_ZVFH: u64 = 1 << 30;
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
#[repr(C)]
struct HwprobePair {
key: i64,
value: u64,
}
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
fn probe_ima_ext_0() -> u64 {
let mut pair = HwprobePair { key: HWPROBE_KEY_IMA_EXT_0, value: 0 };
let rc = unsafe {
libc::syscall(
NR_RISCV_HWPROBE,
&mut pair as *mut HwprobePair,
1 as libc::c_ulong,
0 as libc::c_ulong,
std::ptr::null_mut::<libc::c_ulong>(),
0 as libc::c_uint,
)
};
if rc == 0 && pair.key == HWPROBE_KEY_IMA_EXT_0 { pair.value } else { 0 }
}
#[cfg(not(all(target_arch = "riscv64", target_os = "linux")))]
fn probe_ima_ext_0() -> u64 {
0
}
#[cfg(target_arch = "riscv64")]
fn read_vlenb() -> usize {
let vlenb: usize;
unsafe {
std::arch::asm!("csrr {out}, 0xC22", out = out(reg) vlenb, options(nomem, nostack, preserves_flags));
}
vlenb
}
#[cfg(not(target_arch = "riscv64"))]
fn read_vlenb() -> usize {
0
}
lazy_static::lazy_static! {
static ref IMA_EXT_0: u64 = probe_ima_ext_0();
static ref HAS_RVV: bool = *IMA_EXT_0 & HWPROBE_IMA_V != 0;
static ref HAS_ZVFH: bool = *HAS_RVV && *IMA_EXT_0 & HWPROBE_EXT_ZVFH != 0;
static ref VLENB: usize = if *HAS_RVV { read_vlenb() } else { 0 };
}
pub fn has_rvv() -> bool {
*HAS_RVV
}
pub fn has_zvfh() -> bool {
*HAS_ZVFH
}
pub fn vlenb() -> usize {
*VLENB
}
pub fn vlmax_f32(lmul: usize) -> usize {
vlenb() * lmul / std::mem::size_of::<f32>()
}
pub fn vlmax_f16(lmul: usize) -> usize {
vlenb() * lmul / std::mem::size_of::<crate::f16>()
}
pub fn isa_set() -> IsaSet {
let mut set = IsaSet::of_arch(Arch::RiscV64);
if has_rvv() {
set = set.with(Isa::RiscV64V);
if vlenb() >= 32 {
set = set.with(Isa::RiscV64Vlen256);
}
if has_zvfh() {
set = set.with(Isa::RiscV64Zvfh);
}
log::info!("RVV 1.0 available, VLEN = {} bits, zvfh = {}", vlenb() * 8, has_zvfh());
}
set
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn detection_is_coherent() {
eprintln!(
"rvv={} VLEN={} zvfh={} vlmax_f32(lmul=1,2,4)={:?}",
has_rvv(),
vlenb() * 8,
has_zvfh(),
[vlmax_f32(1), vlmax_f32(2), vlmax_f32(4)],
);
if has_rvv() {
let vlenb = vlenb();
assert!(vlenb >= 16, "RVV 1.0 mandates VLEN >= 128, got VLEN={}", vlenb * 8);
assert!(vlenb.is_power_of_two(), "VLEN must be a power of two, got {}", vlenb * 8);
assert_eq!(vlmax_f32(1), vlenb / 4);
assert_eq!(vlmax_f32(4), vlenb);
assert_eq!(vlmax_f16(1), 2 * vlmax_f32(1));
} else {
assert_eq!(vlenb(), 0);
assert!(!has_zvfh(), "Zvfh is a vector extension and cannot be present without V");
}
}
#[test]
fn vlen256_is_what_the_wide_tiles_need() {
let set = isa_set();
assert_eq!(set.has(Isa::RiscV64Vlen256), has_rvv() && vlmax_f32(2) >= 16);
assert_eq!(set.has(Isa::RiscV64Vlen256), has_rvv() && vlmax_f32(8) >= 64);
assert_eq!(set.has(Isa::RiscV64Vlen256), has_rvv() && vlmax_f16(2) >= 32);
assert_eq!(set.has(Isa::RiscV64Vlen256), has_rvv() && vlmax_f16(8) >= 128);
}
}