riscv_isa/
target.rs

1// Copyright James Wainwright
2//
3// SPDX-License-Identifier: MPL-2.0
4
5//! RISC-V target description.
6//!
7//! This module contains types for describing a RISC-V [`Target`], meaning
8//! an implementation of the RISC-V ISA supporting some number of modes and
9//! extensions.
10
11use std::fmt::{self, Display};
12use std::str::FromStr;
13
14/// Supported register widths.
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub enum Xlen {
17    /// 32-bit registers.
18    #[default]
19    Rv32,
20    /// 64-bit registers.
21    Rv64,
22}
23
24/// RISC-V target configuration.
25///
26/// # Example
27///
28/// ```rust
29/// # use std::str::FromStr;
30/// # use riscv_isa::{Target, Xlen};
31/// let target = Target::from_str("RV32IMACZifencei_Zicsr").unwrap();
32///
33/// assert_eq!(
34///     target,
35///     Target {
36///         xlen: Xlen::Rv32,
37///         m: true,
38///         a: true,
39///         c: true,
40///         zifencei: true,
41///         zicsr: true,
42///         ..Default::default()
43///     },
44/// );
45/// ```
46#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
47pub struct Target {
48    /// Register width.
49    pub xlen: Xlen,
50    /// Whether instructions from the privileged spec are supported.
51    pub privileged: bool,
52    /// Whether S-mode (supervisor) instructions are supported.
53    pub supervisor_mode: bool,
54    /// M standard extension (integer multiply and divide).
55    pub m: bool,
56    /// A standard extension (atomics).
57    pub a: bool,
58    /// F standard extension (32-bit floating point).
59    pub f: bool,
60    /// D standard extension (64-bit (double) floating point).
61    pub d: bool,
62    /// Q standard extension (128-bit (quad) floating point).
63    pub q: bool,
64    /// C standard extension (16-bit compressed instructions).
65    pub c: bool,
66    /// Zicsr standard extension.
67    pub zicsr: bool,
68    /// Zifencei standard extension.
69    pub zifencei: bool,
70    /// Zawrs standard extension.
71    pub zawrs: bool,
72    /// Zfh standard extension (16-bit (half) floating point).
73    pub zfh: bool,
74    /// Zba standard extension (bitmanip).
75    pub zba: bool,
76    /// Zbb standard extension (bitmanip).
77    pub zbb: bool,
78    /// Zbc standard extension (bitmanip).
79    pub zbc: bool,
80    /// Zbkb standard extension (bitmanip for cryptography).
81    pub zbkb: bool,
82    /// Zbs standard extension (bitmanip).
83    pub zbs: bool,
84}
85
86impl Target {
87    /// Check whether this target supports an extension.
88    ///
89    /// The given extension is case-insensitive and must not be multiple extensions.
90    /// Both supersets and subsets of extension groups can be used.
91    ///
92    /// Register widths can be given as either `X`, `rvX`, or `rvXi` (e.g. `32`,
93    /// `rv32`, `rv32i`).
94    ///
95    /// Examples:
96    ///
97    /// ```rust
98    /// # use std::str::FromStr;
99    /// # use riscv_isa::Target;
100    /// let target = Target::from_str("RV32IMACZba_Zbb_Zbs").unwrap();
101    ///
102    /// // Single-letter extensions:
103    /// assert_eq!(target.supports("M").unwrap(), true);
104    /// assert_eq!(target.supports("F").unwrap(), false);
105    ///
106    /// // Multi-letter extensions:
107    /// assert_eq!(target.supports("Zifencei").unwrap(), false);
108    ///
109    /// // Supersets and subsets of extensions:
110    /// assert_eq!(target.supports("B").unwrap(), true);
111    /// assert_eq!(target.supports("Zba").unwrap(), true);
112    ///
113    /// // Register widths:
114    /// assert_eq!(target.supports("RV32").unwrap(), true);
115    /// assert_eq!(target.supports("RV64").unwrap(), false);
116    /// ```
117    pub fn supports(&self, extension: &str) -> Result<bool, ParseTargetError> {
118        let supported = match extension.to_ascii_lowercase().as_str() {
119            "rv32i" | "rv32" | "32" => self.xlen == Xlen::Rv32,
120            "rv64i" | "rv64" | "64" => self.xlen == Xlen::Rv64,
121            "i" => true,
122            "m" => self.m,
123            "a" => self.a,
124            "f" => self.f,
125            "d" => self.d,
126            "q" => self.q,
127            "c" => self.c,
128            "b" => self.zba && self.zbb && self.zbs,
129            "zicsr" => self.zicsr,
130            "zifencei" => self.zifencei,
131            "zawrs" => self.zawrs,
132            "zfh" => self.zfh,
133            "zba" => self.zba,
134            "zbb" => self.zbb,
135            "zbc" => self.zbc,
136            "zbkb" => self.zbkb,
137            "zbs" => self.zbs,
138            _ => return Err(ParseTargetError::UnknownExt),
139        };
140
141        Ok(supported)
142    }
143
144    /// Check whether this target contains another, i.e. it is a superset of the other.
145    ///
146    /// All features of `other` must be supported by `self` for this to be true.
147    ///
148    /// Note that two targets with different `XLEN`s will always return `false` because
149    /// instruction encodings will have strict differences between the two.
150    pub fn contains(&self, other: &Target) -> bool {
151        (other.xlen == self.xlen)
152            && (!other.privileged || self.privileged)
153            && (!other.supervisor_mode || self.supervisor_mode)
154            && (!other.m || self.m)
155            && (!other.a || self.a)
156            && (!other.f || self.f)
157            && (!other.d || self.d)
158            && (!other.q || self.q)
159            && (!other.c || self.c)
160            && (!other.zicsr || self.zicsr)
161            && (!other.zifencei || self.zifencei)
162            && (!other.zawrs || self.zawrs)
163            && (!other.zfh || self.zfh)
164            && (!other.zba || self.zba)
165            && (!other.zbb || self.zbb)
166            && (!other.zbc || self.zbc)
167            && (!other.zbkb || self.zbkb)
168            && (!other.zbs || self.zbs)
169    }
170
171    /// Parse a target string, only accepting those which match the spec.
172    ///
173    /// The strict layout requires:
174    ///
175    /// * The string begins `RV32` or `RV64`.
176    /// * Single-letter extensions must be in the order given by the spec.
177    /// * Z-extensions must come at the end.
178    /// * Z-extensions are first ordered into groups by their second character,
179    ///   then alphabetically.
180    /// * Multi-character extensions must be separated by underscores.
181    pub fn from_str_strict(s: &str) -> Result<Self, ParseTargetError> {
182        let target = Target::from_str(s)?;
183
184        if s != target.to_string() {
185            return Err(ParseTargetError::Strict);
186        }
187
188        Ok(target)
189    }
190}
191
192/// Errors when parsing RISC-V target strings.
193#[derive(Clone, Copy, Debug, PartialEq, Eq)]
194pub enum ParseTargetError {
195    /// Unknown prefix (expecting `RV` or `RISCV`).
196    Prefix,
197    /// Unknown register width (expecting `32` or `64`).
198    Xlen,
199    /// Unknown extension string (expecting e.g. `M` or `Zicsr`).
200    UnknownExt,
201    /// String is parseable, but does not match the target string layout.
202    ///
203    /// The strict layout requires:
204    ///
205    /// * The string begins `RV32` or `RV64`.
206    /// * Single-letter extensions must be in the order given by the spec.
207    /// * Z-extensions must come at the end.
208    /// * Z-extensions are first ordered into groups by their second character,
209    ///   then alphabetically.
210    /// * Multi-character extensions must be separated by underscores.
211    Strict,
212}
213
214impl FromStr for Target {
215    type Err = ParseTargetError;
216
217    /// Parse a RISC-V target from a target string, e.g.
218    /// `RV64IMAFDZicsr_Zifencei`
219    ///
220    /// Target strings are case-insensitive. The non-standard `riscv` prefix is
221    /// accepted due to its use in target tuples/triples.
222    fn from_str(s: &str) -> Result<Self, Self::Err> {
223        let s = s.to_ascii_lowercase();
224        let rest = s
225            .strip_prefix("rv")
226            .or_else(|| s.strip_prefix("riscv"))
227            .ok_or(ParseTargetError::Prefix)?;
228
229        let xlen = match &rest.get(..2) {
230            Some("32") => Xlen::Rv32,
231            Some("64") => Xlen::Rv64,
232            _ => return Err(ParseTargetError::Xlen),
233        };
234        let rest = &rest[2..];
235
236        let mut target = Target { xlen, ..Default::default() };
237
238        let mut extensions = rest;
239        while !extensions.is_empty() {
240            let (ext, rest) = extension(extensions);
241            extensions = rest;
242
243            match ext {
244                "i" => (), // Currently always assumed.
245                "m" => target.m = true,
246                "a" => target.a = true,
247                "f" => target.f = true,
248                "d" => target.d = true,
249                "q" => target.q = true,
250                "c" => target.c = true,
251                "b" => {
252                    target.zba = true;
253                    target.zbb = true;
254                    target.zbs = true;
255                }
256                "zicsr" => target.zicsr = true,
257                "zifencei" => target.zifencei = true,
258                "zawrs" => target.zawrs = true,
259                "zfh" => target.zfh = true,
260                "zba" => target.zba = true,
261                "zbb" => target.zbb = true,
262                "zbc" => target.zbc = true,
263                "zbkb" => target.zbkb = true,
264                "zbs" => target.zbs = true,
265                _ => return Err(ParseTargetError::UnknownExt),
266            }
267        }
268
269        Ok(target)
270    }
271}
272
273impl Display for Target {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        match self.xlen {
276            Xlen::Rv32 => f.write_str("RV32I")?,
277            Xlen::Rv64 => f.write_str("RV64I")?,
278        }
279
280        let b = self.zba && self.zbb && self.zbs;
281        let extensions = [
282            (self.m, "M"),
283            (self.a, "A"),
284            (self.f, "F"),
285            (self.d, "D"),
286            (self.q, "Q"),
287            (self.c, "C"),
288            (b, "B"),
289            (self.zicsr, "Zicsr"),
290            (self.zifencei, "Zifencei"),
291            (self.zawrs, "Zawrs"),
292            (self.zfh, "Zfh"),
293            (self.zba && !b, "Zba"),
294            (self.zbb && !b, "Zbb"),
295            (self.zbc, "Zbc"),
296            (self.zbkb, "Zbkb"),
297            (self.zbs && !b, "Zbs"),
298        ];
299
300        let mut underscore = false;
301        for extension in extensions.into_iter().filter_map(|(en, s)| en.then_some(s)) {
302            // Enable underscores after the first multi-character extension has printed.
303            if underscore {
304                f.write_str("_")?;
305            }
306            if extension.len() > 1 {
307                underscore = true;
308            }
309
310            f.write_str(extension)?;
311        }
312
313        Ok(())
314    }
315}
316
317/// Parse a single extension from the start of the given string.
318///
319/// Returns the parsed extension and the rest of the string still to parse.
320fn extension(s: &str) -> (&str, &str) {
321    let len = if s.starts_with('z')
322        || s.starts_with('x')
323        || s.starts_with("sv")
324        || s.starts_with("ss")
325        || s.starts_with("sh")
326        || s.starts_with("sm")
327    {
328        s.find('_').unwrap_or(s.len())
329    } else {
330        1
331    };
332
333    let mut next_index = len;
334    if s[len..].starts_with('_') {
335        next_index += 1;
336    }
337    let rest = &s[next_index..];
338
339    (&s[..len], rest)
340}
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345
346    #[test]
347    fn extensions() {
348        assert_eq!(extension("a"), ("a", ""));
349        assert_eq!(extension("zifencei"), ("zifencei", ""));
350        assert_eq!(extension("a_"), ("a", ""));
351        assert_eq!(extension("zifencei_"), ("zifencei", ""));
352        assert_eq!(extension("am"), ("a", "m"));
353        assert_eq!(extension("a_m"), ("a", "m"));
354        assert_eq!(extension("a_zifencei"), ("a", "zifencei"));
355        assert_eq!(extension("zifencei_a"), ("zifencei", "a"));
356        assert_eq!(extension("zifencei_zicsr"), ("zifencei", "zicsr"));
357    }
358
359    #[test]
360    fn from_str() {
361        fn check(s: &str, expected: Target) {
362            let got = Target::from_str(s).unwrap();
363            assert_eq!(expected, got);
364        }
365        let def = Target::default();
366
367        // Prefix and xlen:
368        check("RV32I", Target { xlen: Xlen::Rv32, ..def });
369        check("RV64I", Target { xlen: Xlen::Rv64, ..def });
370        check("RISCV32I", Target { xlen: Xlen::Rv32, ..def });
371        check("RISCV64I", Target { xlen: Xlen::Rv64, ..def });
372
373        // Check all extensions individually:
374        check("RV32IM", Target { m: true, ..def });
375        check("RV32IA", Target { a: true, ..def });
376        check("RV32IC", Target { c: true, ..def });
377        check("RV32IF", Target { f: true, ..def });
378        check("RV32ID", Target { d: true, ..def });
379        check("RV32IQ", Target { q: true, ..def });
380        check("RV32IZba", Target { zba: true, ..def });
381        check("RV32IZbb", Target { zbb: true, ..def });
382        check("RV32IZbc", Target { zbc: true, ..def });
383        check("RV32IZbs", Target { zbs: true, ..def });
384        check("RV32IZbkb", Target { zbkb: true, ..def });
385        check("RV32IZifencei", Target { zifencei: true, ..def });
386        check("RV32IZicsr", Target { zicsr: true, ..def });
387
388        // Multiple extensions:
389        check("RV32IMA", Target { m: true, a: true, ..def });
390        check("RV32IZicsr", Target { zicsr: true, ..def });
391        check("RV32IMZicsr", Target { m: true, zicsr: true, ..def });
392        check("RV32IM_Zicsr", Target { m: true, zicsr: true, ..def });
393        check("RV32IZicsr_M", Target { m: true, zicsr: true, ..def });
394        check(
395            "RV32IZicsr_Zifencei",
396            Target { zicsr: true, zifencei: true, ..def },
397        );
398
399        // Group of extensions:
400        check("RV32IB", Target { zba: true, zbb: true, zbs: true, ..def });
401    }
402
403    #[test]
404    fn display() {
405        fn check_becomes(s: &str, expected: &str) {
406            let got = Target::from_str(s).unwrap().to_string();
407            assert_eq!(got, expected);
408        }
409        fn check(s: &str) {
410            check_becomes(s, s);
411        }
412
413        check("RV32I");
414        check("RV64IMAC");
415
416        // Multi-character extensions:
417        check("RV32IMACZicsr");
418        check("RV32IMACZicsr_Zba_Zbkb");
419
420        // Check that the three `B` sub-extensions collapse into one:
421        check_becomes("RV32IZba", "RV32IZba");
422        check_becomes("RV32IZba_Zbb", "RV32IZba_Zbb");
423        check_becomes("RV32IZba_Zbb_Zbs", "RV32IB");
424    }
425
426    #[test]
427    fn parse_strict() {
428        fn check(s: &str, accept: bool) {
429            let got = Target::from_str_strict(s);
430            let not_ = match accept {
431                true => "",
432                false => "not ",
433            };
434            assert!(got.is_ok() == accept, "expected '{s}' {not_}to be accepted");
435        }
436
437        check("RV32I", true);
438        check("RV64I", true);
439        check("RV32", false); // Missing I.
440        check("RV32M", false); // Missing I.
441        check("RV256I", false); // Unsupported XLEN.
442
443        check("RV64IMAC", true);
444        check("RV64IMCA", false); // Wrong order.
445
446        check("RV32IMZba", true);
447        check("RV32IMZba_Zbb", true);
448        check("RV32IMZicsr_Zba", true);
449        check("RV32IMZbb_Zba", false); // Wrong order.
450        check("RV32IMZba_Zicsr", false); // Wrong order.
451        check("RV32IMZbaZbb", false); // Missing underscore.
452    }
453
454    #[test]
455    fn contains() {
456        fn check(lhs: &str, rhs: &str, accept: bool) {
457            let lhs = Target::from_str(lhs).unwrap();
458            let rhs = Target::from_str(rhs).unwrap();
459            let not_ = match accept {
460                true => "",
461                false => "not ",
462            };
463            assert!(
464                lhs.contains(&rhs) == accept,
465                "expected {lhs}.contains({rhs} {not_}to be accepted)"
466            );
467        }
468
469        check("RV32I", "RV32I", true);
470        check("RV32IM", "RV32I", true);
471        check("RV32IMA", "RV32I", true);
472        check("RV32IA", "RV32I", true);
473        check("RV32IMAFDQCZicsr_Zifencei", "RV32I", true);
474
475        check("RV32IMAC", "RV32I", true);
476        check("RV32IMAC", "RV32IM", true);
477        check("RV32IMAC", "RV32IC", true);
478        check("RV32IMAC", "RV32IMC", true);
479        check("RV32IMAC", "RV32IMAC", true);
480
481        check("RV32I", "RV32IM", false);
482        check("RV32I", "RV32IMAC", false);
483        check("RV32IMAC", "RV32ID", false);
484        check("RV32IMAC", "RV32IMACD", false);
485
486        check("RV32I", "RV64I", false);
487        check("RV32IMAC", "RV64I", false);
488    }
489}