/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/settings.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Shared settings module. |
2 | | //! |
3 | | //! This module defines data structures to access the settings defined in the meta language. |
4 | | //! |
5 | | //! Each settings group is translated to a `Flags` struct either in this module or in its |
6 | | //! ISA-specific `settings` module. The struct provides individual getter methods for all of the |
7 | | //! settings as well as computed predicate flags. |
8 | | //! |
9 | | //! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to |
10 | | //! create it. |
11 | | //! |
12 | | //! # Example |
13 | | //! ``` |
14 | | //! use cranelift_codegen::settings::{self, Configurable}; |
15 | | //! |
16 | | //! let mut b = settings::builder(); |
17 | | //! b.set("opt_level", "speed_and_size"); |
18 | | //! |
19 | | //! let f = settings::Flags::new(b); |
20 | | //! assert_eq!(f.opt_level(), settings::OptLevel::SpeedAndSize); |
21 | | //! ``` |
22 | | |
23 | | use crate::constant_hash::{probe, simple_hash}; |
24 | | use crate::isa::TargetIsa; |
25 | | use alloc::boxed::Box; |
26 | | use alloc::string::{String, ToString}; |
27 | | use core::fmt; |
28 | | use core::str; |
29 | | |
30 | | /// A string-based configurator for settings groups. |
31 | | /// |
32 | | /// The `Configurable` protocol allows settings to be modified by name before a finished `Flags` |
33 | | /// struct is created. |
34 | | pub trait Configurable { |
35 | | /// Set the string value of any setting by name. |
36 | | /// |
37 | | /// This can set any type of setting whether it is numeric, boolean, or enumerated. |
38 | | fn set(&mut self, name: &str, value: &str) -> SetResult<()>; |
39 | | |
40 | | /// Enable a boolean setting or apply a preset. |
41 | | /// |
42 | | /// If the identified setting isn't a boolean or a preset, a `BadType` error is returned. |
43 | | fn enable(&mut self, name: &str) -> SetResult<()>; |
44 | | } |
45 | | |
46 | | /// Represents the kind of setting. |
47 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
48 | | pub enum SettingKind { |
49 | | /// The setting is an enumeration. |
50 | | Enum, |
51 | | /// The setting is a number. |
52 | | Num, |
53 | | /// The setting is a boolean. |
54 | | Bool, |
55 | | /// The setting is a preset. |
56 | | Preset, |
57 | | } |
58 | | |
59 | | /// Represents an available builder setting. |
60 | | /// |
61 | | /// This is used for iterating settings in a builder. |
62 | | #[derive(Clone, Copy, Debug)] |
63 | | pub struct Setting { |
64 | | /// The name of the setting. |
65 | | pub name: &'static str, |
66 | | /// The description of the setting. |
67 | | pub description: &'static str, |
68 | | /// The kind of the setting. |
69 | | pub kind: SettingKind, |
70 | | /// The supported values of the setting (for enum values). |
71 | | pub values: Option<&'static [&'static str]>, |
72 | | } |
73 | | |
74 | | /// Represents a setting value. |
75 | | /// |
76 | | /// This is used for iterating values in `Flags`. |
77 | | pub struct Value { |
78 | | /// The name of the setting associated with this value. |
79 | | pub name: &'static str, |
80 | | pub(crate) detail: detail::Detail, |
81 | | pub(crate) values: Option<&'static [&'static str]>, |
82 | | pub(crate) value: u8, |
83 | | } |
84 | | |
85 | | impl Value { |
86 | | /// Gets the kind of setting. |
87 | 0 | pub fn kind(&self) -> SettingKind { |
88 | 0 | match &self.detail { |
89 | 0 | detail::Detail::Enum { .. } => SettingKind::Enum, |
90 | 0 | detail::Detail::Num => SettingKind::Num, |
91 | 0 | detail::Detail::Bool { .. } => SettingKind::Bool, |
92 | 0 | detail::Detail::Preset => unreachable!(), |
93 | | } |
94 | 0 | } |
95 | | |
96 | | /// Gets the enum value if the value is from an enum setting. |
97 | 0 | pub fn as_enum(&self) -> Option<&'static str> { |
98 | 0 | self.values.map(|v| v[self.value as usize]) |
99 | 0 | } |
100 | | |
101 | | /// Gets the numerical value if the value is from a num setting. |
102 | 0 | pub fn as_num(&self) -> Option<u8> { |
103 | 0 | match &self.detail { |
104 | 0 | detail::Detail::Num => Some(self.value), |
105 | 0 | _ => None, |
106 | | } |
107 | 0 | } |
108 | | |
109 | | /// Gets the boolean value if the value is from a boolean setting. |
110 | 0 | pub fn as_bool(&self) -> Option<bool> { |
111 | 0 | match &self.detail { |
112 | 0 | detail::Detail::Bool { bit } => Some(self.value & (1 << bit) != 0), |
113 | 0 | _ => None, |
114 | | } |
115 | 0 | } |
116 | | |
117 | | /// Builds a string from the current value |
118 | 0 | pub fn value_string(&self) -> String { |
119 | 0 | match self.kind() { |
120 | 0 | SettingKind::Enum => self.as_enum().map(|b| b.to_string()), |
121 | 0 | SettingKind::Num => self.as_num().map(|b| b.to_string()), |
122 | 0 | SettingKind::Bool => self.as_bool().map(|b| b.to_string()), |
123 | 0 | SettingKind::Preset => unreachable!(), |
124 | | } |
125 | 0 | .unwrap() |
126 | 0 | } |
127 | | } |
128 | | |
129 | | impl fmt::Display for Value { |
130 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
131 | 0 | if let Some(enum_variant) = self.as_enum() { |
132 | 0 | write!(f, "{}={}", self.name, enum_variant) |
133 | 0 | } else if let Some(num) = self.as_num() { |
134 | 0 | write!(f, "{}={}", self.name, num) |
135 | 0 | } else if let Some(b) = self.as_bool() { |
136 | 0 | if b { |
137 | 0 | write!(f, "{}=1", self.name) |
138 | | } else { |
139 | 0 | write!(f, "{}=0", self.name) |
140 | | } |
141 | | } else { |
142 | 0 | unreachable!() |
143 | | } |
144 | 0 | } |
145 | | } |
146 | | |
147 | | /// Collect settings values based on a template. |
148 | | #[derive(Clone, Hash)] |
149 | | pub struct Builder { |
150 | | template: &'static detail::Template, |
151 | | bytes: Box<[u8]>, |
152 | | } |
153 | | |
154 | | impl Builder { |
155 | | /// Create a new builder with defaults and names from the given template. |
156 | 20.0k | pub fn new(tmpl: &'static detail::Template) -> Self { |
157 | 20.0k | Self { |
158 | 20.0k | template: tmpl, |
159 | 20.0k | bytes: tmpl.defaults.into(), |
160 | 20.0k | } |
161 | 20.0k | } |
162 | | |
163 | | /// Extract contents of builder once everything is configured. |
164 | 20.0k | pub fn state_for(self, name: &str) -> Box<[u8]> { |
165 | 20.0k | assert_eq!(name, self.template.name); |
166 | 20.0k | self.bytes |
167 | 20.0k | } |
168 | | |
169 | | /// Iterates the available settings in the builder. |
170 | 0 | pub fn iter(&self) -> impl Iterator<Item = Setting> { |
171 | 0 | let template = self.template; |
172 | 0 |
|
173 | 0 | template.descriptors.iter().map(move |d| { |
174 | 0 | let (kind, values) = match d.detail { |
175 | 0 | detail::Detail::Enum { last, enumerators } => { |
176 | 0 | let values = template.enums(last, enumerators); |
177 | 0 | (SettingKind::Enum, Some(values)) |
178 | | } |
179 | 0 | detail::Detail::Num => (SettingKind::Num, None), |
180 | 0 | detail::Detail::Bool { .. } => (SettingKind::Bool, None), |
181 | 0 | detail::Detail::Preset => (SettingKind::Preset, None), |
182 | | }; |
183 | | |
184 | 0 | Setting { |
185 | 0 | name: d.name, |
186 | 0 | description: d.description, |
187 | 0 | kind, |
188 | 0 | values, |
189 | 0 | } |
190 | 0 | }) |
191 | 0 | } |
192 | | |
193 | | /// Set the value of a single bit. |
194 | 170k | fn set_bit(&mut self, offset: usize, bit: u8, value: bool) { |
195 | 170k | let byte = &mut self.bytes[offset]; |
196 | 170k | let mask = 1 << bit; |
197 | 170k | if value { |
198 | 170k | *byte |= mask; |
199 | 170k | } else { |
200 | 0 | *byte &= !mask; |
201 | 0 | } |
202 | 170k | } |
203 | | |
204 | | /// Apply a preset. The argument is a slice of (mask, value) bytes. |
205 | 0 | fn apply_preset(&mut self, values: &[(u8, u8)]) { |
206 | 0 | for (byte, &(mask, value)) in self.bytes.iter_mut().zip(values) { |
207 | 0 | *byte = (*byte & !mask) | value; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | /// Look up a descriptor by name. |
212 | 180k | fn lookup(&self, name: &str) -> SetResult<(usize, detail::Detail)> { |
213 | 180k | match probe(self.template, name, simple_hash(name)) { |
214 | 0 | Err(_) => Err(SetError::BadName(name.to_string())), |
215 | 180k | Ok(entry) => { |
216 | 180k | let d = &self.template.descriptors[self.template.hash_table[entry] as usize]; |
217 | 180k | Ok((d.offset as usize, d.detail)) |
218 | | } |
219 | | } |
220 | 180k | } |
221 | | } |
222 | | |
223 | 40.0k | fn parse_bool_value(value: &str) -> SetResult<bool> { |
224 | 40.0k | match value { |
225 | 40.0k | "true" | "on" | "yes" | "1" => Ok(true), |
226 | 0 | "false" | "off" | "no" | "0" => Ok(false), |
227 | 0 | _ => Err(SetError::BadValue("bool".to_string())), |
228 | | } |
229 | 40.0k | } |
230 | | |
231 | 10.0k | fn parse_enum_value(value: &str, choices: &[&str]) -> SetResult<u8> { |
232 | 20.0k | match choices.iter().position(|&tag| tag == value) { |
233 | 10.0k | Some(idx) => Ok(idx as u8), |
234 | | None => { |
235 | | // TODO: Use `join` instead of this code, once |
236 | | // https://github.com/rust-lang/rust/issues/27747 is resolved. |
237 | 0 | let mut all_choices = String::new(); |
238 | 0 | let mut first = true; |
239 | 0 | for choice in choices { |
240 | 0 | if first { |
241 | 0 | first = false |
242 | 0 | } else { |
243 | 0 | all_choices += ", "; |
244 | 0 | } |
245 | 0 | all_choices += choice; |
246 | | } |
247 | 0 | Err(SetError::BadValue(format!("any among {}", all_choices))) |
248 | | } |
249 | | } |
250 | 10.0k | } |
251 | | |
252 | | impl Configurable for Builder { |
253 | 130k | fn enable(&mut self, name: &str) -> SetResult<()> { |
254 | | use self::detail::Detail; |
255 | 130k | let (offset, detail) = self.lookup(name)?; |
256 | 130k | match detail { |
257 | 130k | Detail::Bool { bit } => { |
258 | 130k | self.set_bit(offset, bit, true); |
259 | 130k | Ok(()) |
260 | | } |
261 | | Detail::Preset => { |
262 | 0 | self.apply_preset(&self.template.presets[offset..]); |
263 | 0 | Ok(()) |
264 | | } |
265 | 0 | _ => Err(SetError::BadType), |
266 | | } |
267 | 130k | } |
268 | | |
269 | 50.0k | fn set(&mut self, name: &str, value: &str) -> SetResult<()> { |
270 | | use self::detail::Detail; |
271 | 50.0k | let (offset, detail) = self.lookup(name)?; |
272 | 50.0k | match detail { |
273 | 40.0k | Detail::Bool { bit } => { |
274 | 40.0k | self.set_bit(offset, bit, parse_bool_value(value)?); |
275 | | } |
276 | 0 | Detail::Num => { |
277 | 0 | self.bytes[offset] = value |
278 | 0 | .parse() |
279 | 0 | .map_err(|_| SetError::BadValue("number".to_string()))?; |
280 | | } |
281 | 10.0k | Detail::Enum { last, enumerators } => { |
282 | 10.0k | self.bytes[offset] = |
283 | 10.0k | parse_enum_value(value, self.template.enums(last, enumerators))?; |
284 | | } |
285 | 0 | Detail::Preset => return Err(SetError::BadName(name.to_string())), |
286 | | } |
287 | 50.0k | Ok(()) |
288 | 50.0k | } |
289 | | } |
290 | | |
291 | | /// An error produced when changing a setting. |
292 | | #[derive(Debug, PartialEq, Eq)] |
293 | | pub enum SetError { |
294 | | /// No setting by this name exists. |
295 | | BadName(String), |
296 | | |
297 | | /// Type mismatch for setting (e.g., setting an enum setting as a bool). |
298 | | BadType, |
299 | | |
300 | | /// This is not a valid value for this setting. |
301 | | BadValue(String), |
302 | | } |
303 | | |
304 | | impl std::error::Error for SetError {} |
305 | | |
306 | | impl fmt::Display for SetError { |
307 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
308 | 0 | match self { |
309 | 0 | SetError::BadName(name) => write!(f, "No existing setting named '{}'", name), |
310 | | SetError::BadType => { |
311 | 0 | write!(f, "Trying to set a setting with the wrong type") |
312 | | } |
313 | 0 | SetError::BadValue(value) => { |
314 | 0 | write!(f, "Unexpected value for a setting, expected {}", value) |
315 | | } |
316 | | } |
317 | 0 | } |
318 | | } |
319 | | |
320 | | /// A result returned when changing a setting. |
321 | | pub type SetResult<T> = Result<T, SetError>; |
322 | | |
323 | | /// A reference to just the boolean predicates of a settings object. |
324 | | /// |
325 | | /// The settings objects themselves are generated and appear in the `isa/*/settings.rs` modules. |
326 | | /// Each settings object provides a `predicate_view()` method that makes it possible to query |
327 | | /// ISA predicates by number. |
328 | | #[derive(Clone, Copy, Hash)] |
329 | | pub struct PredicateView<'a>(&'a [u8]); |
330 | | |
331 | | impl<'a> PredicateView<'a> { |
332 | | /// Create a new view of a precomputed predicate vector. |
333 | | /// |
334 | | /// See the `predicate_view()` method on the various `Flags` types defined for each ISA. |
335 | 0 | pub fn new(bits: &'a [u8]) -> Self { |
336 | 0 | PredicateView(bits) |
337 | 0 | } |
338 | | |
339 | | /// Check a numbered predicate. |
340 | 0 | pub fn test(self, p: usize) -> bool { |
341 | 0 | self.0[p / 8] & (1 << (p % 8)) != 0 |
342 | 0 | } |
343 | | } |
344 | | |
345 | | /// Implementation details for generated code. |
346 | | /// |
347 | | /// This module holds definitions that need to be public so the can be instantiated by generated |
348 | | /// code in other modules. |
349 | | pub mod detail { |
350 | | use crate::constant_hash; |
351 | | use core::fmt; |
352 | | use core::hash::Hash; |
353 | | |
354 | | /// An instruction group template. |
355 | | #[derive(Hash)] |
356 | | pub struct Template { |
357 | | /// Name of the instruction group. |
358 | | pub name: &'static str, |
359 | | /// List of setting descriptors. |
360 | | pub descriptors: &'static [Descriptor], |
361 | | /// Union of all enumerators. |
362 | | pub enumerators: &'static [&'static str], |
363 | | /// Hash table of settings. |
364 | | pub hash_table: &'static [u16], |
365 | | /// Default values. |
366 | | pub defaults: &'static [u8], |
367 | | /// Pairs of (mask, value) for presets. |
368 | | pub presets: &'static [(u8, u8)], |
369 | | } |
370 | | |
371 | | impl Template { |
372 | | /// Get enumerators corresponding to a `Details::Enum`. |
373 | 10.0k | pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] { |
374 | 10.0k | let from = enumerators as usize; |
375 | 10.0k | let len = usize::from(last) + 1; |
376 | 10.0k | &self.enumerators[from..from + len] |
377 | 10.0k | } |
378 | | |
379 | | /// Format a setting value as a TOML string. This is mostly for use by the generated |
380 | | /// `Display` implementation. |
381 | 0 | pub fn format_toml_value( |
382 | 0 | &self, |
383 | 0 | detail: Detail, |
384 | 0 | byte: u8, |
385 | 0 | f: &mut fmt::Formatter, |
386 | 0 | ) -> fmt::Result { |
387 | 0 | match detail { |
388 | 0 | Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0), |
389 | 0 | Detail::Num => write!(f, "{}", byte), |
390 | 0 | Detail::Enum { last, enumerators } => { |
391 | 0 | if byte <= last { |
392 | 0 | let tags = self.enums(last, enumerators); |
393 | 0 | write!(f, "\"{}\"", tags[usize::from(byte)]) |
394 | | } else { |
395 | 0 | write!(f, "{}", byte) |
396 | | } |
397 | | } |
398 | | // Presets aren't printed. They are reflected in the other settings. |
399 | 0 | Detail::Preset { .. } => Ok(()), |
400 | | } |
401 | 0 | } |
402 | | } |
403 | | |
404 | | /// The template contains a hash table for by-name lookup. |
405 | | impl<'a> constant_hash::Table<&'a str> for Template { |
406 | 180k | fn len(&self) -> usize { |
407 | 180k | self.hash_table.len() |
408 | 180k | } |
409 | | |
410 | 220k | fn key(&self, idx: usize) -> Option<&'a str> { |
411 | 220k | let e = self.hash_table[idx] as usize; |
412 | 220k | if e < self.descriptors.len() { |
413 | 220k | Some(self.descriptors[e].name) |
414 | | } else { |
415 | 0 | None |
416 | | } |
417 | 220k | } |
418 | | } |
419 | | |
420 | | /// A setting descriptor holds the information needed to generically set and print a setting. |
421 | | /// |
422 | | /// Each settings group will be represented as a constant DESCRIPTORS array. |
423 | | #[derive(Hash)] |
424 | | pub struct Descriptor { |
425 | | /// Lower snake-case name of setting as defined in meta. |
426 | | pub name: &'static str, |
427 | | |
428 | | /// The description of the setting. |
429 | | pub description: &'static str, |
430 | | |
431 | | /// Offset of byte containing this setting. |
432 | | pub offset: u32, |
433 | | |
434 | | /// Additional details, depending on the kind of setting. |
435 | | pub detail: Detail, |
436 | | } |
437 | | |
438 | | /// The different kind of settings along with descriptor bits that depend on the kind. |
439 | | #[derive(Clone, Copy, Hash)] |
440 | | pub enum Detail { |
441 | | /// A boolean setting only uses one bit, numbered from LSB. |
442 | | Bool { |
443 | | /// 0-7. |
444 | | bit: u8, |
445 | | }, |
446 | | |
447 | | /// A numerical setting uses the whole byte. |
448 | | Num, |
449 | | |
450 | | /// An Enum setting uses a range of enumerators. |
451 | | Enum { |
452 | | /// Numerical value of last enumerator, allowing for 1-256 enumerators. |
453 | | last: u8, |
454 | | |
455 | | /// First enumerator in the ENUMERATORS table. |
456 | | enumerators: u16, |
457 | | }, |
458 | | |
459 | | /// A preset is not an individual setting, it is a collection of settings applied at once. |
460 | | /// |
461 | | /// The `Descriptor::offset` field refers to the `PRESETS` table. |
462 | | Preset, |
463 | | } |
464 | | |
465 | | impl Detail { |
466 | | /// Check if a detail is a Detail::Preset. Useful because the Descriptor |
467 | | /// offset field has a different meaning when the detail is a preset. |
468 | 0 | pub fn is_preset(self) -> bool { |
469 | 0 | match self { |
470 | 0 | Self::Preset => true, |
471 | 0 | _ => false, |
472 | | } |
473 | 0 | } |
474 | | } |
475 | | } |
476 | | |
477 | | // Include code generated by `meta/gen_settings.rs`. This file contains a public `Flags` struct |
478 | | // with an implementation for all of the settings defined in |
479 | | // `cranelift-codegen/meta/src/shared/settings.rs`. |
480 | | include!(concat!(env!("OUT_DIR"), "/settings.rs")); |
481 | | |
482 | | /// Wrapper containing flags and optionally a `TargetIsa` trait object. |
483 | | /// |
484 | | /// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa` |
485 | | /// wrapper can be used to pass either, and extract the flags so they are always accessible. |
486 | | #[derive(Clone, Copy)] |
487 | | pub struct FlagsOrIsa<'a> { |
488 | | /// Flags are always present. |
489 | | pub flags: &'a Flags, |
490 | | |
491 | | /// The ISA may not be present. |
492 | | pub isa: Option<&'a dyn TargetIsa>, |
493 | | } |
494 | | |
495 | | impl<'a> From<&'a Flags> for FlagsOrIsa<'a> { |
496 | 0 | fn from(flags: &'a Flags) -> FlagsOrIsa { |
497 | 0 | FlagsOrIsa { flags, isa: None } |
498 | 0 | } |
499 | | } |
500 | | |
501 | | impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> { |
502 | 1.39M | fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa { |
503 | 1.39M | FlagsOrIsa { |
504 | 1.39M | flags: isa.flags(), |
505 | 1.39M | isa: Some(isa), |
506 | 1.39M | } |
507 | 1.39M | } |
508 | | } |
509 | | |
510 | | #[cfg(test)] |
511 | | mod tests { |
512 | | use super::Configurable; |
513 | | use super::SetError::*; |
514 | | use super::{builder, Flags}; |
515 | | use alloc::string::ToString; |
516 | | |
517 | | #[test] |
518 | | fn display_default() { |
519 | | let b = builder(); |
520 | | let f = Flags::new(b); |
521 | | assert_eq!( |
522 | | f.to_string(), |
523 | | r#"[shared] |
524 | | opt_level = "none" |
525 | | tls_model = "none" |
526 | | libcall_call_conv = "isa_default" |
527 | | probestack_size_log2 = 12 |
528 | | probestack_strategy = "outline" |
529 | | regalloc_checker = false |
530 | | regalloc_verbose_logs = false |
531 | | enable_alias_analysis = true |
532 | | use_egraphs = false |
533 | | enable_verifier = true |
534 | | is_pic = false |
535 | | use_colocated_libcalls = false |
536 | | avoid_div_traps = false |
537 | | enable_float = true |
538 | | enable_nan_canonicalization = false |
539 | | enable_pinned_reg = false |
540 | | use_pinned_reg_as_heap_base = false |
541 | | enable_simd = false |
542 | | enable_atomics = true |
543 | | enable_safepoints = false |
544 | | enable_llvm_abi_extensions = false |
545 | | unwind_info = true |
546 | | preserve_frame_pointers = false |
547 | | machine_code_cfg_info = false |
548 | | enable_probestack = false |
549 | | probestack_func_adjusts_sp = false |
550 | | enable_jump_tables = true |
551 | | enable_heap_access_spectre_mitigation = true |
552 | | enable_table_access_spectre_mitigation = true |
553 | | enable_incremental_compilation_cache_checks = false |
554 | | "# |
555 | | ); |
556 | | assert_eq!(f.opt_level(), super::OptLevel::None); |
557 | | assert_eq!(f.enable_simd(), false); |
558 | | } |
559 | | |
560 | | #[test] |
561 | | fn modify_bool() { |
562 | | let mut b = builder(); |
563 | | assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string()))); |
564 | | assert_eq!(b.enable("enable_simd"), Ok(())); |
565 | | assert_eq!(b.set("enable_simd", "false"), Ok(())); |
566 | | |
567 | | let f = Flags::new(b); |
568 | | assert_eq!(f.enable_simd(), false); |
569 | | } |
570 | | |
571 | | #[test] |
572 | | fn modify_string() { |
573 | | let mut b = builder(); |
574 | | assert_eq!( |
575 | | b.set("not_there", "true"), |
576 | | Err(BadName("not_there".to_string())) |
577 | | ); |
578 | | assert_eq!(b.set("enable_simd", ""), Err(BadValue("bool".to_string()))); |
579 | | assert_eq!( |
580 | | b.set("enable_simd", "best"), |
581 | | Err(BadValue("bool".to_string())) |
582 | | ); |
583 | | assert_eq!( |
584 | | b.set("opt_level", "true"), |
585 | | Err(BadValue( |
586 | | "any among none, speed, speed_and_size".to_string() |
587 | | )) |
588 | | ); |
589 | | assert_eq!(b.set("opt_level", "speed"), Ok(())); |
590 | | assert_eq!(b.set("enable_simd", "0"), Ok(())); |
591 | | |
592 | | let f = Flags::new(b); |
593 | | assert_eq!(f.enable_simd(), false); |
594 | | assert_eq!(f.opt_level(), super::OptLevel::Speed); |
595 | | } |
596 | | } |