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