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     pub fn kind(&self) -> SettingKind {
88         match &self.detail {
89             detail::Detail::Enum { .. } => SettingKind::Enum,
90             detail::Detail::Num => SettingKind::Num,
91             detail::Detail::Bool { .. } => SettingKind::Bool,
92             detail::Detail::Preset => unreachable!(),
93         }
94     }
95 
96     /// Gets the enum value if the value is from an enum setting.
97     pub fn as_enum(&self) -> Option<&'static str> {
98         self.values.map(|v| v[self.value as usize])
99     }
100 
101     /// Gets the numerical value if the value is from a num setting.
102     pub fn as_num(&self) -> Option<u8> {
103         match &self.detail {
104             detail::Detail::Num => Some(self.value),
105             _ => None,
106         }
107     }
108 
109     /// Gets the boolean value if the value is from a boolean setting.
110     pub fn as_bool(&self) -> Option<bool> {
111         match &self.detail {
112             detail::Detail::Bool { bit } => Some(self.value & (1 << bit) != 0),
113             _ => None,
114         }
115     }
116 
117     /// Builds a string from the current value
118     pub fn value_string(&self) -> String {
119         match self.kind() {
120             SettingKind::Enum => self.as_enum().map(|b| b.to_string()),
121             SettingKind::Num => self.as_num().map(|b| b.to_string()),
122             SettingKind::Bool => self.as_bool().map(|b| b.to_string()),
123             SettingKind::Preset => unreachable!(),
124         }
125         .unwrap()
126     }
127 }
128 
129 impl fmt::Display for Value {
130     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131         if let Some(enum_variant) = self.as_enum() {
132             write!(f, "{}={}", self.name, enum_variant)
133         } else if let Some(num) = self.as_num() {
134             write!(f, "{}={}", self.name, num)
135         } else if let Some(b) = self.as_bool() {
136             if b {
137                 write!(f, "{}=1", self.name)
138             } else {
139                 write!(f, "{}=0", self.name)
140             }
141         } else {
142             unreachable!()
143         }
144     }
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     pub fn new(tmpl: &'static detail::Template) -> Self {
157         Self {
158             template: tmpl,
159             bytes: tmpl.defaults.into(),
160         }
161     }
162 
163     /// Extract contents of builder once everything is configured.
164     pub fn state_for(&self, name: &str) -> &[u8] {
165         assert_eq!(name, self.template.name);
166         &self.bytes
167     }
168 
169     /// Iterates the available settings in the builder.
170     pub fn iter(&self) -> impl Iterator<Item = Setting> {
171         let template = self.template;
172 
173         template.descriptors.iter().map(move |d| {
174             let (kind, values) = match d.detail {
175                 detail::Detail::Enum { last, enumerators } => {
176                     let values = template.enums(last, enumerators);
177                     (SettingKind::Enum, Some(values))
178                 }
179                 detail::Detail::Num => (SettingKind::Num, None),
180                 detail::Detail::Bool { .. } => (SettingKind::Bool, None),
181                 detail::Detail::Preset => (SettingKind::Preset, None),
182             };
183 
184             Setting {
185                 name: d.name,
186                 description: d.description,
187                 kind,
188                 values,
189             }
190         })
191     }
192 
193     /// Set the value of a single bit.
194     fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {
195         let byte = &mut self.bytes[offset];
196         let mask = 1 << bit;
197         if value {
198             *byte |= mask;
199         } else {
200             *byte &= !mask;
201         }
202     }
203 
204     /// Apply a preset. The argument is a slice of (mask, value) bytes.
205     fn apply_preset(&mut self, values: &[(u8, u8)]) {
206         for (byte, &(mask, value)) in self.bytes.iter_mut().zip(values) {
207             *byte = (*byte & !mask) | value;
208         }
209     }
210 
211     /// Look up a descriptor by name.
212     fn lookup(&self, name: &str) -> SetResult<(usize, detail::Detail)> {
213         match probe(self.template, name, simple_hash(name)) {
214             Err(_) => Err(SetError::BadName(name.to_string())),
215             Ok(entry) => {
216                 let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
217                 Ok((d.offset as usize, d.detail))
218             }
219         }
220     }
221 }
222 
223 fn parse_bool_value(value: &str) -> SetResult<bool> {
224     match value {
225         "true" | "on" | "yes" | "1" => Ok(true),
226         "false" | "off" | "no" | "0" => Ok(false),
227         _ => Err(SetError::BadValue("bool".to_string())),
228     }
229 }
230 
231 fn parse_enum_value(value: &str, choices: &[&str]) -> SetResult<u8> {
232     match choices.iter().position(|&tag| tag == value) {
233         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             let mut all_choices = String::new();
238             let mut first = true;
239             for choice in choices {
240                 if first {
241                     first = false
242                 } else {
243                     all_choices += ", ";
244                 }
245                 all_choices += choice;
246             }
247             Err(SetError::BadValue(format!("any among {}", all_choices)))
248         }
249     }
250 }
251 
252 impl Configurable for Builder {
253     fn enable(&mut self, name: &str) -> SetResult<()> {
254         use self::detail::Detail;
255         let (offset, detail) = self.lookup(name)?;
256         match detail {
257             Detail::Bool { bit } => {
258                 self.set_bit(offset, bit, true);
259                 Ok(())
260             }
261             Detail::Preset => {
262                 self.apply_preset(&self.template.presets[offset..]);
263                 Ok(())
264             }
265             _ => Err(SetError::BadType),
266         }
267     }
268 
269     fn set(&mut self, name: &str, value: &str) -> SetResult<()> {
270         use self::detail::Detail;
271         let (offset, detail) = self.lookup(name)?;
272         match detail {
273             Detail::Bool { bit } => {
274                 self.set_bit(offset, bit, parse_bool_value(value)?);
275             }
276             Detail::Num => {
277                 self.bytes[offset] = value
278                     .parse()
279                     .map_err(|_| SetError::BadValue("number".to_string()))?;
280             }
281             Detail::Enum { last, enumerators } => {
282                 self.bytes[offset] =
283                     parse_enum_value(value, self.template.enums(last, enumerators))?;
284             }
285             Detail::Preset => return Err(SetError::BadName(name.to_string())),
286         }
287         Ok(())
288     }
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     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308         match self {
309             SetError::BadName(name) => write!(f, "No existing setting named '{}'", name),
310             SetError::BadType => {
311                 write!(f, "Trying to set a setting with the wrong type")
312             }
313             SetError::BadValue(value) => {
314                 write!(f, "Unexpected value for a setting, expected {}", value)
315             }
316         }
317     }
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     pub fn new(bits: &'a [u8]) -> Self {
336         PredicateView(bits)
337     }
338 
339     /// Check a numbered predicate.
340     pub fn test(self, p: usize) -> bool {
341         self.0[p / 8] & (1 << (p % 8)) != 0
342     }
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         pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
374             let from = enumerators as usize;
375             let len = usize::from(last) + 1;
376             &self.enumerators[from..from + len]
377         }
378 
379         /// Format a setting value as a TOML string. This is mostly for use by the generated
380         /// `Display` implementation.
381         pub fn format_toml_value(
382             &self,
383             detail: Detail,
384             byte: u8,
385             f: &mut fmt::Formatter,
386         ) -> fmt::Result {
387             match detail {
388                 Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
389                 Detail::Num => write!(f, "{}", byte),
390                 Detail::Enum { last, enumerators } => {
391                     if byte <= last {
392                         let tags = self.enums(last, enumerators);
393                         write!(f, "\"{}\"", tags[usize::from(byte)])
394                     } else {
395                         write!(f, "{}", byte)
396                     }
397                 }
398                 // Presets aren't printed. They are reflected in the other settings.
399                 Detail::Preset { .. } => Ok(()),
400             }
401         }
402     }
403 
404     /// The template contains a hash table for by-name lookup.
405     impl<'a> constant_hash::Table<&'a str> for Template {
406         fn len(&self) -> usize {
407             self.hash_table.len()
408         }
409 
410         fn key(&self, idx: usize) -> Option<&'a str> {
411             let e = self.hash_table[idx] as usize;
412             if e < self.descriptors.len() {
413                 Some(self.descriptors[e].name)
414             } else {
415                 None
416             }
417         }
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         pub fn is_preset(self) -> bool {
469             match self {
470                 Self::Preset => true,
471                 _ => false,
472             }
473         }
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     fn from(flags: &'a Flags) -> FlagsOrIsa {
497         FlagsOrIsa { flags, isa: None }
498     }
499 }
500 
501 impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> {
502     fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa {
503         FlagsOrIsa {
504             flags: isa.flags(),
505             isa: Some(isa),
506         }
507     }
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         let actual = f.to_string();
522         let expected = r#"[shared]
523 opt_level = "none"
524 tls_model = "none"
525 libcall_call_conv = "isa_default"
526 probestack_size_log2 = 12
527 probestack_strategy = "outline"
528 bb_padding_log2_minus_one = 0
529 regalloc_checker = false
530 regalloc_verbose_logs = false
531 enable_alias_analysis = true
532 enable_verifier = true
533 is_pic = false
534 use_colocated_libcalls = false
535 enable_float = true
536 enable_nan_canonicalization = false
537 enable_pinned_reg = false
538 enable_atomics = true
539 enable_safepoints = false
540 enable_llvm_abi_extensions = false
541 unwind_info = true
542 preserve_frame_pointers = false
543 machine_code_cfg_info = false
544 enable_probestack = false
545 probestack_func_adjusts_sp = false
546 enable_jump_tables = true
547 enable_heap_access_spectre_mitigation = true
548 enable_table_access_spectre_mitigation = true
549 enable_incremental_compilation_cache_checks = false
550 "#;
551         if actual != expected {
552             panic!(
553                 "Default settings do not match expectations:\n\n{}",
554                 similar::TextDiff::from_lines(expected, &actual)
555                     .unified_diff()
556                     .header("expected", "actual")
557             );
558         }
559         assert_eq!(f.opt_level(), super::OptLevel::None);
560     }
561 
562     #[test]
563     fn modify_bool() {
564         let mut b = builder();
565         assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string())));
566         assert_eq!(b.enable("enable_atomics"), Ok(()));
567         assert_eq!(b.set("enable_atomics", "false"), Ok(()));
568 
569         let f = Flags::new(b);
570         assert_eq!(f.enable_atomics(), false);
571     }
572 
573     #[test]
574     fn modify_string() {
575         let mut b = builder();
576         assert_eq!(
577             b.set("not_there", "true"),
578             Err(BadName("not_there".to_string()))
579         );
580         assert_eq!(
581             b.set("enable_atomics", ""),
582             Err(BadValue("bool".to_string()))
583         );
584         assert_eq!(
585             b.set("enable_atomics", "best"),
586             Err(BadValue("bool".to_string()))
587         );
588         assert_eq!(
589             b.set("opt_level", "true"),
590             Err(BadValue(
591                 "any among none, speed, speed_and_size".to_string()
592             ))
593         );
594         assert_eq!(b.set("opt_level", "speed"), Ok(()));
595         assert_eq!(b.set("enable_atomics", "0"), Ok(()));
596 
597         let f = Flags::new(b);
598         assert_eq!(f.enable_atomics(), false);
599         assert_eq!(f.opt_level(), super::OptLevel::Speed);
600     }
601 }
602