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)]
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)]
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 
216     /// An instruction group template.
217     pub struct Template {
218         /// Name of the instruction group.
219         pub name: &'static str,
220         /// List of setting descriptors.
221         pub descriptors: &'static [Descriptor],
222         /// Union of all enumerators.
223         pub enumerators: &'static [&'static str],
224         /// Hash table of settings.
225         pub hash_table: &'static [u16],
226         /// Default values.
227         pub defaults: &'static [u8],
228         /// Pairs of (mask, value) for presets.
229         pub presets: &'static [(u8, u8)],
230     }
231 
232     impl Template {
233         /// Get enumerators corresponding to a `Details::Enum`.
234         pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
235             let from = enumerators as usize;
236             let len = usize::from(last) + 1;
237             &self.enumerators[from..from + len]
238         }
239 
240         /// Format a setting value as a TOML string. This is mostly for use by the generated
241         /// `Display` implementation.
242         pub fn format_toml_value(
243             &self,
244             detail: Detail,
245             byte: u8,
246             f: &mut fmt::Formatter,
247         ) -> fmt::Result {
248             match detail {
249                 Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
250                 Detail::Num => write!(f, "{}", byte),
251                 Detail::Enum { last, enumerators } => {
252                     if byte <= last {
253                         let tags = self.enums(last, enumerators);
254                         write!(f, "\"{}\"", tags[usize::from(byte)])
255                     } else {
256                         write!(f, "{}", byte)
257                     }
258                 }
259                 // Presets aren't printed. They are reflected in the other settings.
260                 Detail::Preset { .. } => Ok(()),
261             }
262         }
263     }
264 
265     /// The template contains a hash table for by-name lookup.
266     impl<'a> constant_hash::Table<&'a str> for Template {
267         fn len(&self) -> usize {
268             self.hash_table.len()
269         }
270 
271         fn key(&self, idx: usize) -> Option<&'a str> {
272             let e = self.hash_table[idx] as usize;
273             if e < self.descriptors.len() {
274                 Some(self.descriptors[e].name)
275             } else {
276                 None
277             }
278         }
279     }
280 
281     /// A setting descriptor holds the information needed to generically set and print a setting.
282     ///
283     /// Each settings group will be represented as a constant DESCRIPTORS array.
284     pub struct Descriptor {
285         /// Lower snake-case name of setting as defined in meta.
286         pub name: &'static str,
287 
288         /// Offset of byte containing this setting.
289         pub offset: u32,
290 
291         /// Additional details, depending on the kind of setting.
292         pub detail: Detail,
293     }
294 
295     /// The different kind of settings along with descriptor bits that depend on the kind.
296     #[derive(Clone, Copy)]
297     pub enum Detail {
298         /// A boolean setting only uses one bit, numbered from LSB.
299         Bool {
300             /// 0-7.
301             bit: u8,
302         },
303 
304         /// A numerical setting uses the whole byte.
305         Num,
306 
307         /// An Enum setting uses a range of enumerators.
308         Enum {
309             /// Numerical value of last enumerator, allowing for 1-256 enumerators.
310             last: u8,
311 
312             /// First enumerator in the ENUMERATORS table.
313             enumerators: u16,
314         },
315 
316         /// A preset is not an individual setting, it is a collection of settings applied at once.
317         ///
318         /// The `Descriptor::offset` field refers to the `PRESETS` table.
319         Preset,
320     }
321 
322     impl Detail {
323         /// Check if a detail is a Detail::Preset. Useful because the Descriptor
324         /// offset field has a different meaning when the detail is a preset.
325         pub fn is_preset(self) -> bool {
326             match self {
327                 Self::Preset => true,
328                 _ => false,
329             }
330         }
331     }
332 }
333 
334 // Include code generated by `meta/gen_settings.rs`. This file contains a public `Flags` struct
335 // with an implementation for all of the settings defined in
336 // `cranelift-codegen/meta/src/shared/settings.rs`.
337 include!(concat!(env!("OUT_DIR"), "/settings.rs"));
338 
339 /// Wrapper containing flags and optionally a `TargetIsa` trait object.
340 ///
341 /// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa`
342 /// wrapper can be used to pass either, and extract the flags so they are always accessible.
343 #[derive(Clone, Copy)]
344 pub struct FlagsOrIsa<'a> {
345     /// Flags are always present.
346     pub flags: &'a Flags,
347 
348     /// The ISA may not be present.
349     pub isa: Option<&'a dyn TargetIsa>,
350 }
351 
352 impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {
353     fn from(flags: &'a Flags) -> FlagsOrIsa {
354         FlagsOrIsa { flags, isa: None }
355     }
356 }
357 
358 impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> {
359     fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa {
360         FlagsOrIsa {
361             flags: isa.flags(),
362             isa: Some(isa),
363         }
364     }
365 }
366 
367 #[cfg(test)]
368 mod tests {
369     use super::Configurable;
370     use super::SetError::*;
371     use super::{builder, Flags};
372     use alloc::string::ToString;
373 
374     #[test]
375     fn display_default() {
376         let b = builder();
377         let f = Flags::new(b);
378         assert_eq!(
379             f.to_string(),
380             "[shared]\n\
381              opt_level = \"none\"\n\
382              tls_model = \"none\"\n\
383              libcall_call_conv = \"isa_default\"\n\
384              baldrdash_prologue_words = 0\n\
385              probestack_size_log2 = 12\n\
386              enable_verifier = true\n\
387              is_pic = false\n\
388              use_colocated_libcalls = false\n\
389              avoid_div_traps = false\n\
390              enable_float = true\n\
391              enable_nan_canonicalization = false\n\
392              enable_pinned_reg = false\n\
393              use_pinned_reg_as_heap_base = false\n\
394              enable_simd = false\n\
395              enable_atomics = true\n\
396              enable_safepoints = false\n\
397              emit_all_ones_funcaddrs = false\n\
398              enable_probestack = true\n\
399              probestack_func_adjusts_sp = false\n\
400              enable_jump_tables = true\n"
401         );
402         assert_eq!(f.opt_level(), super::OptLevel::None);
403         assert_eq!(f.enable_simd(), false);
404         assert_eq!(f.baldrdash_prologue_words(), 0);
405     }
406 
407     #[test]
408     fn modify_bool() {
409         let mut b = builder();
410         assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string())));
411         assert_eq!(b.enable("enable_simd"), Ok(()));
412         assert_eq!(b.set("enable_simd", "false"), Ok(()));
413 
414         let f = Flags::new(b);
415         assert_eq!(f.enable_simd(), false);
416     }
417 
418     #[test]
419     fn modify_string() {
420         let mut b = builder();
421         assert_eq!(
422             b.set("not_there", "true"),
423             Err(BadName("not_there".to_string()))
424         );
425         assert_eq!(b.set("enable_simd", ""), Err(BadValue("bool".to_string())));
426         assert_eq!(
427             b.set("enable_simd", "best"),
428             Err(BadValue("bool".to_string()))
429         );
430         assert_eq!(
431             b.set("opt_level", "true"),
432             Err(BadValue(
433                 "any among none, speed, speed_and_size".to_string()
434             ))
435         );
436         assert_eq!(b.set("opt_level", "speed"), Ok(()));
437         assert_eq!(b.set("enable_simd", "0"), Ok(()));
438 
439         let f = Flags::new(b);
440         assert_eq!(f.enable_simd(), false);
441         assert_eq!(f.opt_level(), super::OptLevel::Speed);
442     }
443 }
444