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