//! Generate the ISA-specific settings. use std::collections::HashMap; use cranelift_codegen_shared::constant_hash::{generate_table, simple_hash}; use crate::cdsl::camel_case; use crate::cdsl::settings::{ BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting, }; use crate::error; use crate::srcgen::{Formatter, Match}; use crate::unique_table::UniqueSeqTable; pub(crate) enum ParentGroup { None, Shared, } /// Emits the constructor of the Flags structure. fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) { let args = match parent { ParentGroup::None => "builder: Builder", ParentGroup::Shared => "shared: &settings::Flags, builder: Builder", }; fmtln!(fmt, "impl Flags {"); fmt.indent(|fmt| { fmt.doc_comment(format!("Create flags {} settings group.", group.name)); fmtln!(fmt, "#[allow(unused_variables)]"); fmtln!(fmt, "pub fn new({}) -> Self {{", args); fmt.indent(|fmt| { fmtln!(fmt, "let bvec = builder.state_for(\"{}\");", group.name); fmtln!( fmt, "let mut {} = Self {{ bytes: [0; {}] }};", group.name, group.byte_size() ); fmtln!( fmt, "debug_assert_eq!(bvec.len(), {});", group.settings_size ); fmtln!( fmt, "{}.bytes[0..{}].copy_from_slice(&bvec);", group.name, group.settings_size ); // Now compute the predicates. for p in &group.predicates { fmt.comment(format!("Precompute #{}.", p.number)); fmtln!(fmt, "if {} {{", p.render(group)); fmt.indent(|fmt| { fmtln!( fmt, "{}.bytes[{}] |= 1 << {};", group.name, group.bool_start_byte_offset + p.number / 8, p.number % 8 ); }); fmtln!(fmt, "}"); } fmtln!(fmt, group.name); }); fmtln!(fmt, "}"); }); fmtln!(fmt, "}"); } /// Emit Display and FromStr implementations for enum settings. fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) { fmtln!(fmt, "impl fmt::Display for {} {{", name); fmt.indent(|fmt| { fmtln!( fmt, "fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {" ); fmt.indent(|fmt| { fmtln!(fmt, "f.write_str(match *self {"); fmt.indent(|fmt| { for v in values.iter() { fmtln!(fmt, "Self::{} => \"{}\",", camel_case(v), v); } }); fmtln!(fmt, "})"); }); fmtln!(fmt, "}"); }); fmtln!(fmt, "}"); fmtln!(fmt, "impl str::FromStr for {} {{", name); fmt.indent(|fmt| { fmtln!(fmt, "type Err = ();"); fmtln!(fmt, "fn from_str(s: &str) -> Result {"); fmt.indent(|fmt| { fmtln!(fmt, "match s {"); fmt.indent(|fmt| { for v in values.iter() { fmtln!(fmt, "\"{}\" => Ok(Self::{}),", v, camel_case(v)); } fmtln!(fmt, "_ => Err(()),"); }); fmtln!(fmt, "}"); }); fmtln!(fmt, "}"); }); fmtln!(fmt, "}"); } /// Emit real enum for the Enum settings. fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) { for setting in group.settings.iter() { let values = match setting.specific { SpecificSetting::Bool(_) | SpecificSetting::Num(_) => continue, SpecificSetting::Enum(ref values) => values, }; let name = camel_case(setting.name); fmt.doc_comment(format!("Values for `{}.{}`.", group.name, setting.name)); fmtln!(fmt, "#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]"); fmtln!(fmt, "pub enum {} {{", name); fmt.indent(|fmt| { for v in values.iter() { fmt.doc_comment(format!("`{}`.", v)); fmtln!(fmt, "{},", camel_case(v)); } }); fmtln!(fmt, "}"); gen_to_and_from_str(&name, values, fmt); } } /// Emit a getter function for `setting`. fn gen_getter(setting: &Setting, fmt: &mut Formatter) { fmt.doc_comment(setting.comment); match setting.specific { SpecificSetting::Bool(BoolSetting { predicate_number, .. }) => { fmtln!(fmt, "pub fn {}(&self) -> bool {{", setting.name); fmt.indent(|fmt| { fmtln!(fmt, "self.numbered_predicate({})", predicate_number); }); fmtln!(fmt, "}"); } SpecificSetting::Enum(ref values) => { let ty = camel_case(setting.name); fmtln!(fmt, "pub fn {}(&self) -> {} {{", setting.name, ty); fmt.indent(|fmt| { let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset)); for (i, v) in values.iter().enumerate() { m.arm_no_fields(format!("{}", i), format!("{}::{}", ty, camel_case(v))); } m.arm_no_fields("_", "panic!(\"Invalid enum value\")"); fmt.add_match(m); }); fmtln!(fmt, "}"); } SpecificSetting::Num(_) => { fmtln!(fmt, "pub fn {}(&self) -> u8 {{", setting.name); fmt.indent(|fmt| { fmtln!(fmt, "self.bytes[{}]", setting.byte_offset); }); fmtln!(fmt, "}"); } } } fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) { fmt.doc_comment(format!("Computed predicate `{}`.", predicate.render(group))); fmtln!(fmt, "pub fn {}(&self) -> bool {{", predicate.name); fmt.indent(|fmt| { fmtln!(fmt, "self.numbered_predicate({})", predicate.number); }); fmtln!(fmt, "}"); } /// Emits getters for each setting value. fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) { fmt.doc_comment("User-defined settings."); fmtln!(fmt, "#[allow(dead_code)]"); fmtln!(fmt, "impl Flags {"); fmt.indent(|fmt| { fmt.doc_comment("Get a view of the boolean predicates."); fmtln!( fmt, "pub fn predicate_view(&self) -> crate::settings::PredicateView {" ); fmt.indent(|fmt| { fmtln!( fmt, "crate::settings::PredicateView::new(&self.bytes[{}..])", group.bool_start_byte_offset ); }); fmtln!(fmt, "}"); if !group.settings.is_empty() { fmt.doc_comment("Dynamic numbered predicate getter."); fmtln!(fmt, "fn numbered_predicate(&self, p: usize) -> bool {"); fmt.indent(|fmt| { fmtln!( fmt, "self.bytes[{} + p / 8] & (1 << (p % 8)) != 0", group.bool_start_byte_offset ); }); fmtln!(fmt, "}"); } for setting in &group.settings { gen_getter(&setting, fmt); } for predicate in &group.predicates { gen_pred_getter(&predicate, &group, fmt); } }); fmtln!(fmt, "}"); } #[derive(Hash, PartialEq, Eq)] enum SettingOrPreset<'a> { Setting(&'a Setting), Preset(&'a Preset), } impl<'a> SettingOrPreset<'a> { fn name(&self) -> &str { match *self { SettingOrPreset::Setting(s) => s.name, SettingOrPreset::Preset(p) => p.name, } } } /// Emits DESCRIPTORS, ENUMERATORS, HASH_TABLE and PRESETS. fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) { let mut enum_table = UniqueSeqTable::new(); let mut descriptor_index_map: HashMap = HashMap::new(); // Generate descriptors. fmtln!( fmt, "static DESCRIPTORS: [detail::Descriptor; {}] = [", group.settings.len() + group.presets.len() ); fmt.indent(|fmt| { for (idx, setting) in group.settings.iter().enumerate() { fmtln!(fmt, "detail::Descriptor {"); fmt.indent(|fmt| { fmtln!(fmt, "name: \"{}\",", setting.name); fmtln!(fmt, "offset: {},", setting.byte_offset); match setting.specific { SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => { fmtln!( fmt, "detail: detail::Detail::Bool {{ bit: {} }},", bit_offset ); } SpecificSetting::Enum(ref values) => { let offset = enum_table.add(values); fmtln!( fmt, "detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},", values.len() - 1, offset ); } SpecificSetting::Num(_) => { fmtln!(fmt, "detail: detail::Detail::Num,"); } } descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx); }); fmtln!(fmt, "},"); } for (idx, preset) in group.presets.iter().enumerate() { fmtln!(fmt, "detail::Descriptor {"); fmt.indent(|fmt| { fmtln!(fmt, "name: \"{}\",", preset.name); fmtln!(fmt, "offset: {},", (idx as u8) * group.settings_size); fmtln!(fmt, "detail: detail::Detail::Preset,"); }); fmtln!(fmt, "},"); let whole_idx = idx + group.settings.len(); descriptor_index_map.insert(SettingOrPreset::Preset(preset), whole_idx); } }); fmtln!(fmt, "];"); // Generate enumerators. fmtln!(fmt, "static ENUMERATORS: [&str; {}] = [", enum_table.len()); fmt.indent(|fmt| { for enum_val in enum_table.iter() { fmtln!(fmt, "\"{}\",", enum_val); } }); fmtln!(fmt, "];"); // Generate hash table. let mut hash_entries: Vec = Vec::new(); hash_entries.extend(group.settings.iter().map(|x| SettingOrPreset::Setting(x))); hash_entries.extend(group.presets.iter().map(|x| SettingOrPreset::Preset(x))); let hash_table = generate_table(hash_entries.iter(), hash_entries.len(), |entry| { simple_hash(entry.name()) }); fmtln!(fmt, "static HASH_TABLE: [u16; {}] = [", hash_table.len()); fmt.indent(|fmt| { for h in &hash_table { match *h { Some(setting_or_preset) => fmtln!( fmt, "{},", &descriptor_index_map .get(setting_or_preset) .unwrap() .to_string() ), None => fmtln!(fmt, "0xffff,"), } } }); fmtln!(fmt, "];"); // Generate presets. fmtln!( fmt, "static PRESETS: [(u8, u8); {}] = [", group.presets.len() * (group.settings_size as usize) ); fmt.indent(|fmt| { for preset in &group.presets { fmt.comment(preset.name); for (mask, value) in preset.layout(&group) { fmtln!(fmt, "(0b{:08b}, 0b{:08b}),", mask, value); } } }); fmtln!(fmt, "];"); } fn gen_template(group: &SettingGroup, fmt: &mut Formatter) { let mut default_bytes: Vec = vec![0; group.settings_size as usize]; for setting in &group.settings { *default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte(); } let default_bytes: Vec = default_bytes .iter() .map(|x| format!("{:#04x}", x)) .collect(); let default_bytes_str = default_bytes.join(", "); fmtln!( fmt, "static TEMPLATE: detail::Template = detail::Template {" ); fmt.indent(|fmt| { fmtln!(fmt, "name: \"{}\",", group.name); fmtln!(fmt, "descriptors: &DESCRIPTORS,"); fmtln!(fmt, "enumerators: &ENUMERATORS,"); fmtln!(fmt, "hash_table: &HASH_TABLE,"); fmtln!(fmt, "defaults: &[{}],", default_bytes_str); fmtln!(fmt, "presets: &PRESETS,"); }); fmtln!(fmt, "};"); fmt.doc_comment(format!( "Create a `settings::Builder` for the {} settings group.", group.name )); fmtln!(fmt, "pub fn builder() -> Builder {"); fmt.indent(|fmt| { fmtln!(fmt, "Builder::new(&TEMPLATE)"); }); fmtln!(fmt, "}"); } fn gen_display(group: &SettingGroup, fmt: &mut Formatter) { fmtln!(fmt, "impl fmt::Display for Flags {"); fmt.indent(|fmt| { fmtln!( fmt, "fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {" ); fmt.indent(|fmt| { fmtln!(fmt, "writeln!(f, \"[{}]\")?;", group.name); fmtln!(fmt, "for d in &DESCRIPTORS {"); fmt.indent(|fmt| { fmtln!(fmt, "if !d.detail.is_preset() {"); fmt.indent(|fmt| { fmtln!(fmt, "write!(f, \"{} = \", d.name)?;"); fmtln!( fmt, "TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;", ); fmtln!(fmt, "writeln!(f)?;"); }); fmtln!(fmt, "}"); }); fmtln!(fmt, "}"); fmtln!(fmt, "Ok(())"); }); fmtln!(fmt, "}") }); fmtln!(fmt, "}"); } fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) { // Generate struct. fmtln!(fmt, "#[derive(Clone)]"); fmt.doc_comment(format!("Flags group `{}`.", group.name)); fmtln!(fmt, "pub struct Flags {"); fmt.indent(|fmt| { fmtln!(fmt, "bytes: [u8; {}],", group.byte_size()); }); fmtln!(fmt, "}"); gen_constructor(group, parent, fmt); gen_enum_types(group, fmt); gen_getters(group, fmt); gen_descriptors(group, fmt); gen_template(group, fmt); gen_display(group, fmt); } pub(crate) fn generate( settings: &SettingGroup, parent_group: ParentGroup, filename: &str, out_dir: &str, ) -> Result<(), error::Error> { let mut fmt = Formatter::new(); gen_group(&settings, parent_group, &mut fmt); fmt.update_file(filename, out_dir)?; Ok(()) }