1use crate::constant_hash::{probe, simple_hash};
24use crate::isa::TargetIsa;
25use alloc::boxed::Box;
26use alloc::string::{String, ToString};
27use core::fmt;
28use core::str;
29
30pub trait Configurable {
35 fn set(&mut self, name: &str, value: &str) -> SetResult<()>;
39
40 fn enable(&mut self, name: &str) -> SetResult<()>;
44}
45
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub enum SettingKind {
49 Enum,
51 Num,
53 Bool,
55 Preset,
57}
58
59#[derive(Clone, Copy, Debug)]
63pub struct Setting {
64 pub name: &'static str,
66 pub description: &'static str,
68 pub kind: SettingKind,
70 pub values: Option<&'static [&'static str]>,
72}
73
74pub struct Value {
78 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
85impl Value {
86 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 pub fn as_enum(&self) -> Option<&'static str> {
98 self.values.map(|v| v[self.value as usize])
99 }
100
101 pub fn as_num(&self) -> Option<u8> {
103 match &self.detail {
104 detail::Detail::Num => Some(self.value),
105 _ => None,
106 }
107 }
108
109 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 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
129impl fmt::Display for Value {
130 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#[derive(Clone, Hash)]
149pub struct Builder {
150 template: &'static detail::Template,
151 bytes: Box<[u8]>,
152}
153
154impl Builder {
155 pub fn new(tmpl: &'static detail::Template) -> Self {
157 Self {
158 template: tmpl,
159 bytes: tmpl.defaults.into(),
160 }
161 }
162
163 pub fn state_for(&self, name: &str) -> &[u8] {
165 assert_eq!(name, self.template.name);
166 &self.bytes
167 }
168
169 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 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 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 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
223fn 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
231fn 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
241impl Configurable for Builder {
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
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#[derive(Debug, PartialEq, Eq)]
282pub enum SetError {
283 BadName(String),
285
286 BadType,
288
289 BadValue(String),
291}
292
293impl std::error::Error for SetError {}
294
295impl fmt::Display for SetError {
296 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
309pub type SetResult<T> = Result<T, SetError>;
311
312#[derive(Clone, Copy, Hash)]
318pub struct PredicateView<'a>(&'a [u8]);
319
320impl<'a> PredicateView<'a> {
321 pub fn new(bits: &'a [u8]) -> Self {
325 PredicateView(bits)
326 }
327
328 pub fn test(self, p: usize) -> bool {
330 self.0[p / 8] & (1 << (p % 8)) != 0
331 }
332}
333
334pub mod detail {
339 use crate::constant_hash;
340 use core::fmt;
341 use core::hash::Hash;
342
343 #[derive(Hash)]
345 pub struct Template {
346 pub name: &'static str,
348 pub descriptors: &'static [Descriptor],
350 pub enumerators: &'static [&'static str],
352 pub hash_table: &'static [u16],
354 pub defaults: &'static [u8],
356 pub presets: &'static [(u8, u8)],
358 }
359
360 impl Template {
361 pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
363 let from = enumerators as usize;
364 let len = usize::from(last) + 1;
365 &self.enumerators[from..from + len]
366 }
367
368 pub fn format_toml_value(
371 &self,
372 detail: Detail,
373 byte: u8,
374 f: &mut fmt::Formatter,
375 ) -> fmt::Result {
376 match detail {
377 Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
378 Detail::Num => write!(f, "{byte}"),
379 Detail::Enum { last, enumerators } => {
380 if byte <= last {
381 let tags = self.enums(last, enumerators);
382 write!(f, "\"{}\"", tags[usize::from(byte)])
383 } else {
384 write!(f, "{byte}")
385 }
386 }
387 Detail::Preset { .. } => Ok(()),
389 }
390 }
391 }
392
393 impl<'a> constant_hash::Table<&'a str> for Template {
395 fn len(&self) -> usize {
396 self.hash_table.len()
397 }
398
399 fn key(&self, idx: usize) -> Option<&'a str> {
400 let e = self.hash_table[idx] as usize;
401 if e < self.descriptors.len() {
402 Some(self.descriptors[e].name)
403 } else {
404 None
405 }
406 }
407 }
408
409 #[derive(Hash)]
413 pub struct Descriptor {
414 pub name: &'static str,
416
417 pub description: &'static str,
419
420 pub offset: u32,
422
423 pub detail: Detail,
425 }
426
427 #[derive(Clone, Copy, Hash)]
429 pub enum Detail {
430 Bool {
432 bit: u8,
434 },
435
436 Num,
438
439 Enum {
441 last: u8,
443
444 enumerators: u16,
446 },
447
448 Preset,
452 }
453
454 impl Detail {
455 pub fn is_preset(self) -> bool {
458 match self {
459 Self::Preset => true,
460 _ => false,
461 }
462 }
463 }
464}
465
466include!(concat!(env!("OUT_DIR"), "/settings.rs"));
470
471#[derive(Clone, Copy)]
476pub struct FlagsOrIsa<'a> {
477 pub flags: &'a Flags,
479
480 pub isa: Option<&'a dyn TargetIsa>,
482}
483
484impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {
485 fn from(flags: &'a Flags) -> FlagsOrIsa<'a> {
486 FlagsOrIsa { flags, isa: None }
487 }
488}
489
490impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> {
491 fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa<'a> {
492 FlagsOrIsa {
493 flags: isa.flags(),
494 isa: Some(isa),
495 }
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use super::Configurable;
502 use super::SetError::*;
503 use super::{builder, Flags};
504 use alloc::string::ToString;
505
506 #[test]
507 fn display_default() {
508 let b = builder();
509 let f = Flags::new(b);
510 let actual = f.to_string();
511 let expected = r#"[shared]
512regalloc_algorithm = "backtracking"
513opt_level = "none"
514tls_model = "none"
515stack_switch_model = "none"
516libcall_call_conv = "isa_default"
517probestack_size_log2 = 12
518probestack_strategy = "outline"
519bb_padding_log2_minus_one = 0
520regalloc_checker = false
521regalloc_verbose_logs = false
522enable_alias_analysis = true
523enable_verifier = true
524enable_pcc = false
525is_pic = false
526use_colocated_libcalls = false
527enable_float = true
528enable_nan_canonicalization = false
529enable_pinned_reg = false
530enable_atomics = true
531enable_safepoints = false
532enable_llvm_abi_extensions = false
533enable_multi_ret_implicit_sret = false
534unwind_info = true
535preserve_frame_pointers = false
536machine_code_cfg_info = false
537enable_probestack = false
538enable_jump_tables = true
539enable_heap_access_spectre_mitigation = true
540enable_table_access_spectre_mitigation = true
541enable_incremental_compilation_cache_checks = false
542"#;
543 if actual != expected {
544 panic!(
545 "Default settings do not match expectations:\n\n{}",
546 similar::TextDiff::from_lines(expected, &actual)
547 .unified_diff()
548 .header("expected", "actual")
549 );
550 }
551 assert_eq!(f.opt_level(), super::OptLevel::None);
552 }
553
554 #[test]
555 fn modify_bool() {
556 let mut b = builder();
557 assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string())));
558 assert_eq!(b.enable("enable_atomics"), Ok(()));
559 assert_eq!(b.set("enable_atomics", "false"), Ok(()));
560
561 let f = Flags::new(b);
562 assert_eq!(f.enable_atomics(), false);
563 }
564
565 #[test]
566 fn modify_string() {
567 let mut b = builder();
568 assert_eq!(
569 b.set("not_there", "true"),
570 Err(BadName("not_there".to_string()))
571 );
572 assert_eq!(
573 b.set("enable_atomics", ""),
574 Err(BadValue("bool".to_string()))
575 );
576 assert_eq!(
577 b.set("enable_atomics", "best"),
578 Err(BadValue("bool".to_string()))
579 );
580 assert_eq!(
581 b.set("opt_level", "true"),
582 Err(BadValue(
583 "any among none, speed, speed_and_size".to_string()
584 ))
585 );
586 assert_eq!(b.set("opt_level", "speed"), Ok(()));
587 assert_eq!(b.set("enable_atomics", "0"), Ok(()));
588
589 let f = Flags::new(b);
590 assert_eq!(f.enable_atomics(), false);
591 assert_eq!(f.opt_level(), super::OptLevel::Speed);
592 }
593}