//! 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;