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