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 9 use crate::error::{Location, ParseError}; 10 use crate::testcommand::TestOption; 11 use cranelift_codegen::isa::TargetIsa; 12 use cranelift_codegen::settings::{Configurable, Flags, SetError}; 13 14 /// The ISA specifications in a `.clif` file. 15 pub 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<Box<dyn TargetIsa>>), 23 } 24 25 impl 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`. 38 pub 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 61 impl 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 79 macro_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 pub fn parse_options<'a, I>( 91 iter: I, 92 config: &mut dyn Configurable, 93 loc: Location, 94 ) -> Result<(), ParseOptionError> 95 where 96 I: Iterator<Item = &'a str>, 97 { 98 for opt in iter.map(TestOption::new) { 99 match opt { 100 TestOption::Flag(name) => match config.enable(name) { 101 Ok(_) => {} 102 Err(SetError::BadName(name)) => { 103 return Err(ParseOptionError::UnknownFlag { loc, name }) 104 } 105 Err(_) => return option_err!(loc, "not a boolean flag: '{}'", opt), 106 }, 107 TestOption::Value(name, value) => match config.set(name, value) { 108 Ok(_) => {} 109 Err(SetError::BadName(name)) => { 110 return Err(ParseOptionError::UnknownValue { 111 loc, 112 name, 113 value: value.to_string(), 114 }) 115 } 116 Err(SetError::BadType) => { 117 return option_err!(loc, "invalid setting type: '{}'", opt) 118 } 119 Err(SetError::BadValue(expected)) => { 120 return option_err!( 121 loc, 122 "invalid setting value for '{}', expected {}", 123 opt, 124 expected 125 ); 126 } 127 }, 128 } 129 } 130 Ok(()) 131 } 132