cranelift_reader/
isaspec.rs

1//! Parsed representation of `set` and `isa` commands.
2//!
3//! A test case file can contain `set` commands that set ISA-independent settings, and it can
4//! contain `isa` commands that select an ISA and applies ISA-specific settings.
5//!
6//! If a test case file contains `isa` commands, the tests will only be run against the specified
7//! ISAs. If the file contains no `isa` commands, the tests will be run against all supported ISAs.
8
9use crate::error::{Location, ParseError};
10use crate::testcommand::TestOption;
11use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
12use cranelift_codegen::settings::{Configurable, Flags, SetError};
13
14/// The ISA specifications in a `.clif` file.
15pub enum IsaSpec {
16    /// The parsed file does not contain any `isa` commands, but it may contain `set` commands
17    /// which are reflected in the finished `Flags` object.
18    None(Flags),
19
20    /// The parsed file does contain `isa` commands.
21    /// Each `isa` command is used to configure a `TargetIsa` trait object.
22    Some(Vec<OwnedTargetIsa>),
23}
24
25impl IsaSpec {
26    /// If the `IsaSpec` contains exactly 1 `TargetIsa` we return a reference to it
27    pub fn unique_isa(&self) -> Option<&dyn TargetIsa> {
28        if let Self::Some(ref isa_vec) = *self {
29            if isa_vec.len() == 1 {
30                return Some(&*isa_vec[0]);
31            }
32        }
33        None
34    }
35}
36
37/// An error type returned by `parse_options`.
38pub enum ParseOptionError {
39    /// A generic ParseError.
40    Generic(ParseError),
41
42    /// An unknown flag was used, with the given name at the given location.
43    UnknownFlag {
44        /// Location where the flag was given.
45        loc: Location,
46        /// Name of the unknown flag.
47        name: String,
48    },
49
50    /// An unknown value was used, with the given name at the given location.
51    UnknownValue {
52        /// Location where the flag was given.
53        loc: Location,
54        /// Name of the unknown value.
55        name: String,
56        /// Value of the unknown value.
57        value: String,
58    },
59}
60
61impl From<ParseOptionError> for ParseError {
62    fn from(err: ParseOptionError) -> Self {
63        match err {
64            ParseOptionError::Generic(err) => err,
65            ParseOptionError::UnknownFlag { loc, name } => Self {
66                location: loc,
67                message: format!("unknown setting '{name}'"),
68                is_warning: false,
69            },
70            ParseOptionError::UnknownValue { loc, name, value } => Self {
71                location: loc,
72                message: format!("unknown setting '{name}={value}'"),
73                is_warning: false,
74            },
75        }
76    }
77}
78
79macro_rules! option_err {
80    ( $loc:expr, $fmt:expr, $( $arg:expr ),+ ) => {
81        Err($crate::ParseOptionError::Generic($crate::ParseError {
82            location: $loc.clone(),
83            message: format!( $fmt, $( $arg ),+ ),
84            is_warning: false,
85        }))
86    };
87}
88
89/// Parse an iterator of command line options and apply them to `config`.
90///
91/// Note that parsing terminates after the first error is encountered.
92pub fn parse_options<'a, I>(
93    iter: I,
94    config: &mut dyn Configurable,
95    loc: Location,
96) -> Result<(), ParseOptionError>
97where
98    I: Iterator<Item = &'a str>,
99{
100    for opt in iter {
101        parse_option(opt, config, loc)?;
102    }
103    Ok(())
104}
105
106/// Parse an single command line options and apply it to `config`.
107pub fn parse_option(
108    opt: &str,
109    config: &mut dyn Configurable,
110    loc: Location,
111) -> Result<(), ParseOptionError> {
112    match TestOption::new(opt) {
113        TestOption::Flag(name) => match config.enable(name) {
114            Ok(_) => Ok(()),
115            Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownFlag { loc, name }),
116            Err(_) => option_err!(loc, "not a boolean flag: '{}'", opt),
117        },
118        TestOption::Value(name, value) => match config.set(name, value) {
119            Ok(_) => Ok(()),
120            Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownValue {
121                loc,
122                name,
123                value: value.to_string(),
124            }),
125            Err(SetError::BadType) => option_err!(loc, "invalid setting type: '{}'", opt),
126            Err(SetError::BadValue(expected)) => {
127                option_err!(
128                    loc,
129                    "invalid setting value for '{}', expected {}",
130                    opt,
131                    expected
132                )
133            }
134        },
135    }
136}