blob: 9b9e0cba09d1c430062f19886a260734875ceb4b [file] [log] [blame]
//! Run-time feature detection utility for RISC-V.
//!
//! On RISC-V, full feature detection needs a help of one or more
//! feature detection mechanisms (usually provided by the operating system).
//!
//! RISC-V architecture defines many extensions and some have dependency to others.
//! More importantly, some of them cannot be enabled without resolving such
//! dependencies due to limited set of features that such mechanisms provide.
//!
//! This module provides an OS-independent utility to process such relations
//! between RISC-V extensions.
use crate::detect::{Feature, cache};
/// Imply features by the given set of enabled features.
///
/// Note that it does not perform any consistency checks including existence of
/// conflicting extensions and/or complicated requirements. Eliminating such
/// inconsistencies is the responsibility of the feature detection logic and
/// its provider(s).
pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initializer {
loop {
// Check convergence of the feature flags later.
let prev = value;
// Expect that the optimizer turns repeated operations into
// a fewer number of bit-manipulation operations.
macro_rules! imply {
// Regular implication:
// A1 => (B1[, B2...]), A2 => (B1[, B2...]) and so on.
($($from: ident)|+ => $($to: ident)&+) => {
if [$(Feature::$from as u32),+].iter().any(|&x| value.test(x)) {
$(
value.set(Feature::$to as u32);
)+
}
};
// Implication with multiple requirements:
// A1 && A2 ... => (B1[, B2...]).
($($from: ident)&+ => $($to: ident)&+) => {
if [$(Feature::$from as u32),+].iter().all(|&x| value.test(x)) {
$(
value.set(Feature::$to as u32);
)+
}
};
}
macro_rules! group {
($group: ident == $($member: ident)&+) => {
// Forward implication as defined in the specifications.
imply!($group => $($member)&+);
// Reverse implication to "group extension" from its members.
// This is not a part of specifications but convenient for
// feature detection and implemented in e.g. LLVM.
imply!($($member)&+ => $group);
};
}
/*
If a dependency/implication is not explicitly stated in the
specification, it is denoted as a comment as follows:
"defined as subset":
The latter extension is described as a subset of the former
(but the evidence is weak).
"functional":
The former extension is functionally a superset of the latter
(no direct references though).
*/
imply!(zvbb => zvkb);
// Certain set of vector cryptography extensions form a group.
group!(zvkn == zvkned & zvknhb & zvkb & zvkt);
group!(zvknc == zvkn & zvbc);
group!(zvkng == zvkn & zvkg);
group!(zvks == zvksed & zvksh & zvkb & zvkt);
group!(zvksc == zvks & zvbc);
group!(zvksg == zvks & zvkg);
imply!(zvknhb => zvknha); // functional
// For vector cryptography, Zvknhb and Zvbc require integer arithmetic
// with EEW=64 (Zve64x) while others not depending on them
// require EEW=32 (Zve32x).
imply!(zvknhb | zvbc => zve64x);
imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x);
imply!(zbc => zbkc); // defined as subset
group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh);
group!(zks == zbkb & zbkc & zbkx & zksed & zksh);
group!(zk == zkn & zkr & zkt);
imply!(zabha | zacas => zaamo);
group!(a == zalrsc & zaamo);
group!(b == zba & zbb & zbs);
imply!(zcf => zca & f);
imply!(zcd => zca & d);
imply!(zcmop | zcb => zca);
imply!(zhinx => zhinxmin);
imply!(zdinx | zhinxmin => zfinx);
imply!(zvfh => zvfhmin); // functional
imply!(zvfh => zve32f & zfhmin);
imply!(zvfhmin => zve32f);
imply!(zvfbfwma => zvfbfmin & zfbfmin);
imply!(zvfbfmin => zve32f);
imply!(v => zve64d);
imply!(zve64d => zve64f & d);
imply!(zve64f => zve64x & zve32f);
imply!(zve64x => zve32x);
imply!(zve32f => zve32x & f);
imply!(zfh => zfhmin);
imply!(q => d);
imply!(d | zfhmin | zfa => f);
imply!(zfbfmin => f); // and some of (not all) "Zfh" instructions.
// Relatively complex implication rules around the "C" extension.
// (from "C" and some others)
imply!(c => zca);
imply!(c & d => zcd);
#[cfg(target_arch = "riscv32")]
imply!(c & f => zcf);
// (to "C"; defined as superset)
cfg_select! {
target_arch = "riscv32" => {
if value.test(Feature::d as u32) {
imply!(zcf & zcd => c);
} else if value.test(Feature::f as u32) {
imply!(zcf => c);
} else {
imply!(zca => c);
}
}
_ => {
if value.test(Feature::d as u32) {
imply!(zcd => c);
} else {
imply!(zca => c);
}
}
}
imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr);
// Loop until the feature flags converge.
if prev == value {
return value;
}
}
}
#[cfg(test)]
#[path = "riscv/tests.rs"]
mod tests;