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 /// Builds a string from the current value
value_string(&self) -> String118 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 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result130 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.
new(tmpl: &'static detail::Template) -> Self156 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.
state_for(&self, name: &str) -> &[u8]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.
iter(&self) -> impl Iterator<Item = Setting> + use<>170 pub fn iter(&self) -> impl Iterator<Item = Setting> + use<> {
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.
set_bit(&mut self, offset: usize, bit: u8, value: bool)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.
apply_preset(&mut self, values: &[(u8, u8)])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.
lookup(&self, name: &str) -> SetResult<(usize, detail::Detail)>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
parse_bool_value(value: &str) -> SetResult<bool>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
parse_enum_value(value: &str, choices: &[&str]) -> SetResult<u8>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 => Err(SetError::BadValue(format!(
235 "any among {}",
236 choices.join(", ")
237 ))),
238 }
239 }
240
241 impl Configurable for Builder {
enable(&mut self, name: &str) -> SetResult<()>242 fn enable(&mut self, name: &str) -> SetResult<()> {
243 use self::detail::Detail;
244 let (offset, detail) = self.lookup(name)?;
245 match detail {
246 Detail::Bool { bit } => {
247 self.set_bit(offset, bit, true);
248 Ok(())
249 }
250 Detail::Preset => {
251 self.apply_preset(&self.template.presets[offset..]);
252 Ok(())
253 }
254 _ => Err(SetError::BadType),
255 }
256 }
257
set(&mut self, name: &str, value: &str) -> SetResult<()>258 fn set(&mut self, name: &str, value: &str) -> SetResult<()> {
259 use self::detail::Detail;
260 let (offset, detail) = self.lookup(name)?;
261 match detail {
262 Detail::Bool { bit } => {
263 self.set_bit(offset, bit, parse_bool_value(value)?);
264 }
265 Detail::Num => {
266 self.bytes[offset] = value
267 .parse()
268 .map_err(|_| SetError::BadValue("number".to_string()))?;
269 }
270 Detail::Enum { last, enumerators } => {
271 self.bytes[offset] =
272 parse_enum_value(value, self.template.enums(last, enumerators))?;
273 }
274 Detail::Preset => return Err(SetError::BadName(name.to_string())),
275 }
276 Ok(())
277 }
278 }
279
280 /// An error produced when changing a setting.
281 #[derive(Debug, PartialEq, Eq)]
282 pub enum SetError {
283 /// No setting by this name exists.
284 BadName(String),
285
286 /// Type mismatch for setting (e.g., setting an enum setting as a bool).
287 BadType,
288
289 /// This is not a valid value for this setting.
290 BadValue(String),
291 }
292
293 impl core::error::Error for SetError {}
294
295 impl fmt::Display for SetError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result296 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297 match self {
298 SetError::BadName(name) => write!(f, "No existing setting named '{name}'"),
299 SetError::BadType => {
300 write!(f, "Trying to set a setting with the wrong type")
301 }
302 SetError::BadValue(value) => {
303 write!(f, "Unexpected value for a setting, expected {value}")
304 }
305 }
306 }
307 }
308
309 /// A result returned when changing a setting.
310 pub type SetResult<T> = Result<T, SetError>;
311
312 /// Implementation details for generated code.
313 ///
314 /// This module holds definitions that need to be public so the can be instantiated by generated
315 /// code in other modules.
316 pub mod detail {
317 use crate::constant_hash;
318 use core::fmt;
319 use core::hash::Hash;
320
321 /// An instruction group template.
322 #[derive(Hash)]
323 pub struct Template {
324 /// Name of the instruction group.
325 pub name: &'static str,
326 /// List of setting descriptors.
327 pub descriptors: &'static [Descriptor],
328 /// Union of all enumerators.
329 pub enumerators: &'static [&'static str],
330 /// Hash table of settings.
331 pub hash_table: &'static [u16],
332 /// Default values.
333 pub defaults: &'static [u8],
334 /// Pairs of (mask, value) for presets.
335 pub presets: &'static [(u8, u8)],
336 }
337
338 impl Template {
339 /// Get enumerators corresponding to a `Details::Enum`.
enums(&self, last: u8, enumerators: u16) -> &[&'static str]340 pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
341 let from = enumerators as usize;
342 let len = usize::from(last) + 1;
343 &self.enumerators[from..from + len]
344 }
345
346 /// Format a setting value as a TOML string. This is mostly for use by the generated
347 /// `Display` implementation.
format_toml_value( &self, detail: Detail, byte: u8, f: &mut fmt::Formatter, ) -> fmt::Result348 pub fn format_toml_value(
349 &self,
350 detail: Detail,
351 byte: u8,
352 f: &mut fmt::Formatter,
353 ) -> fmt::Result {
354 match detail {
355 Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
356 Detail::Num => write!(f, "{byte}"),
357 Detail::Enum { last, enumerators } => {
358 if byte <= last {
359 let tags = self.enums(last, enumerators);
360 write!(f, "\"{}\"", tags[usize::from(byte)])
361 } else {
362 write!(f, "{byte}")
363 }
364 }
365 // Presets aren't printed. They are reflected in the other settings.
366 Detail::Preset { .. } => Ok(()),
367 }
368 }
369 }
370
371 /// The template contains a hash table for by-name lookup.
372 impl<'a> constant_hash::Table<&'a str> for Template {
len(&self) -> usize373 fn len(&self) -> usize {
374 self.hash_table.len()
375 }
376
key(&self, idx: usize) -> Option<&'a str>377 fn key(&self, idx: usize) -> Option<&'a str> {
378 let e = self.hash_table[idx] as usize;
379 if e < self.descriptors.len() {
380 Some(self.descriptors[e].name)
381 } else {
382 None
383 }
384 }
385 }
386
387 /// A setting descriptor holds the information needed to generically set and print a setting.
388 ///
389 /// Each settings group will be represented as a constant DESCRIPTORS array.
390 #[derive(Hash)]
391 pub struct Descriptor {
392 /// Lower snake-case name of setting as defined in meta.
393 pub name: &'static str,
394
395 /// The description of the setting.
396 pub description: &'static str,
397
398 /// Offset of byte containing this setting.
399 pub offset: u32,
400
401 /// Additional details, depending on the kind of setting.
402 pub detail: Detail,
403 }
404
405 /// The different kind of settings along with descriptor bits that depend on the kind.
406 #[derive(Clone, Copy, Hash)]
407 pub enum Detail {
408 /// A boolean setting only uses one bit, numbered from LSB.
409 Bool {
410 /// 0-7.
411 bit: u8,
412 },
413
414 /// A numerical setting uses the whole byte.
415 Num,
416
417 /// An Enum setting uses a range of enumerators.
418 Enum {
419 /// Numerical value of last enumerator, allowing for 1-256 enumerators.
420 last: u8,
421
422 /// First enumerator in the ENUMERATORS table.
423 enumerators: u16,
424 },
425
426 /// A preset is not an individual setting, it is a collection of settings applied at once.
427 ///
428 /// The `Descriptor::offset` field refers to the `PRESETS` table.
429 Preset,
430 }
431
432 impl Detail {
433 /// Check if a detail is a Detail::Preset. Useful because the Descriptor
434 /// offset field has a different meaning when the detail is a preset.
is_preset(self) -> bool435 pub fn is_preset(self) -> bool {
436 match self {
437 Self::Preset => true,
438 _ => false,
439 }
440 }
441 }
442 }
443
444 // Include code generated by `meta/gen_settings.rs`. This file contains a public `Flags` struct
445 // with an implementation for all of the settings defined in
446 // `cranelift-codegen/meta/src/shared/settings.rs`.
447 include!(concat!(env!("OUT_DIR"), "/settings.rs"));
448
449 /// Wrapper containing flags and optionally a `TargetIsa` trait object.
450 ///
451 /// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa`
452 /// wrapper can be used to pass either, and extract the flags so they are always accessible.
453 #[derive(Clone, Copy)]
454 pub struct FlagsOrIsa<'a> {
455 /// Flags are always present.
456 pub flags: &'a Flags,
457
458 /// The ISA may not be present.
459 pub isa: Option<&'a dyn TargetIsa>,
460 }
461
462 impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {
from(flags: &'a Flags) -> FlagsOrIsa<'a>463 fn from(flags: &'a Flags) -> FlagsOrIsa<'a> {
464 FlagsOrIsa { flags, isa: None }
465 }
466 }
467
468 impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> {
from(isa: &'a dyn TargetIsa) -> FlagsOrIsa<'a>469 fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa<'a> {
470 FlagsOrIsa {
471 flags: isa.flags(),
472 isa: Some(isa),
473 }
474 }
475 }
476
477 #[cfg(test)]
478 mod tests {
479 use super::Configurable;
480 use super::SetError::*;
481 use super::{Flags, builder};
482 use alloc::string::ToString;
483
484 #[test]
display_default()485 fn display_default() {
486 let b = builder();
487 let f = Flags::new(b);
488 let actual = f.to_string();
489 let expected = r#"[shared]
490 regalloc_algorithm = "backtracking"
491 opt_level = "none"
492 tls_model = "none"
493 stack_switch_model = "none"
494 libcall_call_conv = "isa_default"
495 probestack_size_log2 = 12
496 probestack_strategy = "outline"
497 bb_padding_log2_minus_one = 0
498 log2_min_function_alignment = 0
499 regalloc_checker = false
500 regalloc_verbose_logs = false
501 enable_alias_analysis = true
502 enable_verifier = true
503 is_pic = false
504 use_colocated_libcalls = false
505 enable_nan_canonicalization = false
506 enable_pinned_reg = false
507 enable_llvm_abi_extensions = false
508 enable_multi_ret_implicit_sret = false
509 unwind_info = true
510 preserve_frame_pointers = false
511 machine_code_cfg_info = false
512 enable_probestack = false
513 enable_heap_access_spectre_mitigation = true
514 enable_table_access_spectre_mitigation = true
515 enable_incremental_compilation_cache_checks = false
516 "#;
517 if actual != expected {
518 panic!(
519 "Default settings do not match expectations:\n\n{}",
520 similar::TextDiff::from_lines(expected, &actual)
521 .unified_diff()
522 .header("expected", "actual")
523 );
524 }
525 assert_eq!(f.opt_level(), super::OptLevel::None);
526 }
527
528 #[test]
modify_bool()529 fn modify_bool() {
530 let mut b = builder();
531 assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string())));
532 assert_eq!(b.enable("enable_verifier"), Ok(()));
533 assert_eq!(b.set("enable_verifier", "false"), Ok(()));
534
535 let f = Flags::new(b);
536 assert_eq!(f.enable_verifier(), false);
537 }
538
539 #[test]
modify_string()540 fn modify_string() {
541 let mut b = builder();
542 assert_eq!(
543 b.set("not_there", "true"),
544 Err(BadName("not_there".to_string()))
545 );
546 assert_eq!(
547 b.set("enable_verifier", ""),
548 Err(BadValue("bool".to_string()))
549 );
550 assert_eq!(
551 b.set("enable_verifier", "best"),
552 Err(BadValue("bool".to_string()))
553 );
554 assert_eq!(
555 b.set("opt_level", "true"),
556 Err(BadValue(
557 "any among none, speed, speed_and_size".to_string()
558 ))
559 );
560 assert_eq!(b.set("opt_level", "speed"), Ok(()));
561 assert_eq!(b.set("enable_verifier", "0"), Ok(()));
562
563 let f = Flags::new(b);
564 assert_eq!(f.enable_verifier(), false);
565 assert_eq!(f.opt_level(), super::OptLevel::Speed);
566 }
567 }
568