about summary refs log tree commit diff
path: root/library/std_detect/src/detect/os/riscv.rs
blob: 9b9e0cba09d1c430062f19886a260734875ceb4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! 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;