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