From d94e027c2ac8cbfc2907b8eb39692c6382b0edb0 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 21 Nov 2018 15:38:12 +0100 Subject: [PATCH] [build] Move settings generation from Python to Rust code; --- lib/codegen/build.rs | 24 +- lib/codegen/meta-python/cdsl/settings.py | 2 +- lib/codegen/meta-python/gen_settings.py | 6 +- lib/codegen/meta/src/base/mod.rs | 1 + lib/codegen/meta/src/base/settings.rs | 164 +++++++++ lib/codegen/meta/src/cdsl/isa.rs | 9 +- lib/codegen/meta/src/cdsl/mod.rs | 32 +- lib/codegen/meta/src/cdsl/settings.rs | 385 +++++++++++++++++++ lib/codegen/meta/src/constant_hash.rs | 50 +++ lib/codegen/meta/src/gen_registers.rs | 8 +- lib/codegen/meta/src/gen_settings.rs | 447 +++++++++++++++++++++++ lib/codegen/meta/src/isa/arm32/mod.rs | 10 +- lib/codegen/meta/src/isa/arm64/mod.rs | 10 +- lib/codegen/meta/src/isa/mod.rs | 11 +- lib/codegen/meta/src/isa/riscv/mod.rs | 57 ++- lib/codegen/meta/src/isa/x86/mod.rs | 77 +++- lib/codegen/meta/src/lib.rs | 7 +- lib/codegen/meta/src/srcgen.rs | 4 +- lib/codegen/meta/src/unique_table.rs | 68 ++++ lib/codegen/src/constant_hash.rs | 2 +- 20 files changed, 1334 insertions(+), 40 deletions(-) create mode 100644 lib/codegen/meta/src/base/settings.rs create mode 100644 lib/codegen/meta/src/cdsl/settings.rs create mode 100644 lib/codegen/meta/src/constant_hash.rs create mode 100644 lib/codegen/meta/src/gen_settings.rs create mode 100644 lib/codegen/meta/src/unique_table.rs diff --git a/lib/codegen/build.rs b/lib/codegen/build.rs index 67bbdfe175..122d441c45 100644 --- a/lib/codegen/build.rs +++ b/lib/codegen/build.rs @@ -82,20 +82,12 @@ fn main() { // Now that the Python build process is complete, generate files that are // emitted by the `meta` crate. // ------------------------------------------------------------------------ - let isas = meta::isa::define_all(); - if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) { + if let Err(err) = generate_meta(&out_dir) { eprintln!("Error: {}", err); process::exit(1); } - for isa in isas { - if let Err(err) = meta::gen_registers::generate(isa, "registers", &out_dir) { - eprintln!("Error: {}", err); - process::exit(1); - } - } - if let Ok(_) = env::var("CRANELIFT_VERBOSE") { println!( "cargo:warning=Build step took {:?}.", @@ -105,6 +97,20 @@ fn main() { } } +fn generate_meta(out_dir: &str) -> Result<(), meta::error::Error> { + let shared_settings = meta::gen_settings::generate_common("new_settings.rs", &out_dir)?; + let isas = meta::isa::define_all(&shared_settings); + + meta::gen_types::generate("types.rs", &out_dir)?; + + for isa in &isas { + meta::gen_registers::generate(&isa, "registers", &out_dir)?; + meta::gen_settings::generate(&isa, "new_settings", &out_dir)?; + } + + Ok(()) +} + fn identify_python() -> &'static str { for python in &["python", "python3", "python2.7"] { if process::Command::new(python) diff --git a/lib/codegen/meta-python/cdsl/settings.py b/lib/codegen/meta-python/cdsl/settings.py index cbcac2d98d..28ff0f0b3e 100644 --- a/lib/codegen/meta-python/cdsl/settings.py +++ b/lib/codegen/meta-python/cdsl/settings.py @@ -340,7 +340,7 @@ class SettingGroup(object): precomputed predicates. This is the size of the byte-sized settings plus all the numbered - predcate bits rounded up to a whole number of bytes. + predicate bits rounded up to a whole number of bytes. """ return self.boolean_offset + (len(self.predicate_number) + 7) // 8 diff --git a/lib/codegen/meta-python/gen_settings.py b/lib/codegen/meta-python/gen_settings.py index 287f1a2a39..88d2a9e0cd 100644 --- a/lib/codegen/meta-python/gen_settings.py +++ b/lib/codegen/meta-python/gen_settings.py @@ -255,8 +255,8 @@ def gen_display(sgrp, fmt): fmt.line('Ok(())') -def gen_constructor(sgrp, parent, fmt): - # type: (SettingGroup, PredContext, srcgen.Formatter) -> None +def gen_constructor(sgrp, fmt): + # type: (SettingGroup, srcgen.Formatter) -> None """ Generate a Flags constructor. """ @@ -310,7 +310,7 @@ def gen_group(sgrp, fmt): with fmt.indented('pub struct Flags {', '}'): fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size())) - gen_constructor(sgrp, None, fmt) + gen_constructor(sgrp, fmt) gen_enum_types(sgrp, fmt) gen_getters(sgrp, fmt) gen_descriptors(sgrp, fmt) diff --git a/lib/codegen/meta/src/base/mod.rs b/lib/codegen/meta/src/base/mod.rs index a75614d9c8..1d596a1306 100644 --- a/lib/codegen/meta/src/base/mod.rs +++ b/lib/codegen/meta/src/base/mod.rs @@ -1,3 +1,4 @@ //! Definitions for the base Cranelift language. +pub mod settings; pub mod types; diff --git a/lib/codegen/meta/src/base/settings.rs b/lib/codegen/meta/src/base/settings.rs new file mode 100644 index 0000000000..c89a4e20ff --- /dev/null +++ b/lib/codegen/meta/src/base/settings.rs @@ -0,0 +1,164 @@ +use cdsl::settings::{SettingGroup, SettingGroupBuilder}; + +pub fn generate() -> SettingGroup { + let mut settings = SettingGroupBuilder::new("shared"); + + settings.add_enum( + "opt_level", + r#" + Optimization level: + + - default: Very profitable optimizations enabled, none slow. + - best: Enable all optimizations + - fastest: Optimize for compile time by disabling most optimizations. + "#, + vec!["default", "best", "fastest"], + ); + + settings.add_bool( + "enable_verifier", + r#" + Run the Cranelift IR verifier at strategic times during compilation. + + This makes compilation slower but catches many bugs. The verifier is + disabled by default, except when reading Cranelift IR from a text file. + "#, + true, + ); + + // Note that Cranelift doesn't currently need an is_pie flag, because PIE is + // just PIC where symbols can't be pre-empted, which can be expressed with the + // `colocated` flag on external functions and global values. + settings.add_bool( + "is_pic", + "Enable Position-Independent Code generation", + false, + ); + + settings.add_bool( + "colocated_libcalls", + r#" + Use colocated libcalls. + + Generate code that assumes that libcalls can be declared "colocated", + meaning they will be defined along with the current function, such that + they can use more efficient addressing. + "#, + false, + ); + + settings.add_bool( + "avoid_div_traps", + r#" + Generate explicit checks around native division instructions to avoid + their trapping. + + This is primarily used by SpiderMonkey which doesn't install a signal + handler for SIGFPE, but expects a SIGILL trap for division by zero. + + On ISAs like ARM where the native division instructions don't trap, + this setting has no effect - explicit checks are always inserted. + "#, + false, + ); + + settings.add_bool( + "enable_float", + r#" + Enable the use of floating-point instructions + + Disabling use of floating-point instructions is not yet implemented. + "#, + true, + ); + + settings.add_bool( + "enable_nan_canonicalization", + r#" + Enable NaN canonicalization + + This replaces NaNs with a single canonical value, for users requiring + entirely deterministic WebAssembly computation. This is not required + by the WebAssembly spec, so it is not enabled by default. + "#, + false, + ); + + settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", true); + + settings.add_bool( + "enable_atomics", + "Enable the use of atomic instructions", + true, + ); + + // Settings specific to the `baldrdash` calling convention. + + settings.add_num( + "baldrdash_prologue_words", + r#" + Number of pointer-sized words pushed by the baldrdash prologue. + + Functions with the `baldrdash` calling convention don't generate their + own prologue and epilogue. They depend on externally generated code + that pushes a fixed number of words in the prologue and restores them + in the epilogue. + + This setting configures the number of pointer-sized words pushed on the + stack when the Cranelift-generated code is entered. This includes the + pushed return address on x86. + "#, + 0, + ); + + // BaldrMonkey requires that not-yet-relocated function addresses be encoded + // as all-ones bitpatterns. + settings.add_bool( + "allones_funcaddrs", + "Emit not-yet-relocated function addresses as all-ones bit patterns.", + false, + ); + + // Stack probing options. + + settings.add_bool( + "probestack_enabled", + r#" + Enable the use of stack probes, for calling conventions which support this + functionality. + "#, + true, + ); + + settings.add_bool( + "probestack_func_adjusts_sp", + r#" + Set this to true of the stack probe function modifies the stack pointer + itself. + "#, + false, + ); + + settings.add_num( + "probestack_size_log2", + r#" + The log2 of the size of the stack guard region. + + Stack frames larger than this size will have stack overflow checked + by calling the probestack function. + + The default is 12, which translates to a size of 4096. + "#, + 12, + ); + + // Jump table options. + + settings.add_bool( + "jump_tables_enabled", + "Enable the use of jump tables in generated machine code.", + true, + ); + + settings.finish() +} diff --git a/lib/codegen/meta/src/cdsl/isa.rs b/lib/codegen/meta/src/cdsl/isa.rs index 7168d32c85..50e4d842c8 100644 --- a/lib/codegen/meta/src/cdsl/isa.rs +++ b/lib/codegen/meta/src/cdsl/isa.rs @@ -3,19 +3,22 @@ use cranelift_entity::PrimaryMap; use super::regs::{ RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto, }; +use super::settings::SettingGroup; pub struct TargetIsa { pub name: &'static str, pub reg_banks: PrimaryMap, pub reg_classes: PrimaryMap, + pub settings: SettingGroup, } impl TargetIsa { - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str, settings: SettingGroup) -> Self { Self { name, reg_banks: PrimaryMap::new(), reg_classes: PrimaryMap::new(), + settings, } } } @@ -25,9 +28,9 @@ pub struct TargetIsaBuilder { } impl TargetIsaBuilder { - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str, settings: SettingGroup) -> Self { Self { - isa: TargetIsa::new(name), + isa: TargetIsa::new(name, settings), } } diff --git a/lib/codegen/meta/src/cdsl/mod.rs b/lib/codegen/meta/src/cdsl/mod.rs index 4d29421e0b..87c615d70e 100644 --- a/lib/codegen/meta/src/cdsl/mod.rs +++ b/lib/codegen/meta/src/cdsl/mod.rs @@ -5,10 +5,38 @@ pub mod isa; pub mod regs; +pub mod settings; pub mod types; +/// A macro that converts boolean settings into predicates to look more natural. +#[macro_export] +macro_rules! predicate { + ($a:ident && $($b:tt)*) => { + PredicateNode::And(Box::new($a.into()), Box::new(predicate!($($b)*))) + }; + ($a:ident) => { + $a.into() + }; +} + +#[macro_export] +macro_rules! preset { + () => { + vec![] + }; + ($($x:ident)&&*) => { + { + let mut v = Vec::new(); + $( + v.push($x.into()); + )* + v + } + }; +} + /// Convert the string `s` to CamelCase. -fn _camel_case(s: &str) -> String { +pub fn camel_case(s: &str) -> String { let mut output_chars = String::with_capacity(s.len()); let mut capitalize = true; @@ -30,7 +58,7 @@ fn _camel_case(s: &str) -> String { #[cfg(test)] mod tests { - use super::_camel_case as camel_case; + use super::camel_case; #[test] fn camel_case_works() { diff --git a/lib/codegen/meta/src/cdsl/settings.rs b/lib/codegen/meta/src/cdsl/settings.rs new file mode 100644 index 0000000000..f5e4c9b368 --- /dev/null +++ b/lib/codegen/meta/src/cdsl/settings.rs @@ -0,0 +1,385 @@ +use std::iter; + +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +pub struct BoolSettingIndex(usize); + +#[derive(Hash, PartialEq, Eq)] +pub struct BoolSetting { + pub default: bool, + pub bit_offset: u8, + pub predicate_number: u8, +} + +#[derive(Hash, PartialEq, Eq)] +pub enum SpecificSetting { + Bool(BoolSetting), + Enum(Vec<&'static str>), + Num(u8), +} + +#[derive(Hash, PartialEq, Eq)] +pub struct Setting { + pub name: &'static str, + pub comment: &'static str, + pub specific: SpecificSetting, + pub byte_offset: u8, +} + +impl Setting { + pub fn default_byte(&self) -> u8 { + match self.specific { + SpecificSetting::Bool(BoolSetting { + default, + bit_offset, + .. + }) => { + if default { + 1 << bit_offset + } else { + 0 + } + } + SpecificSetting::Enum(_) => 0, + SpecificSetting::Num(default) => default, + } + } + + fn byte_for_value(&self, v: bool) -> u8 { + match self.specific { + SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => { + if v { + 1 << bit_offset + } else { + 0 + } + } + _ => panic!("byte_for_value shouldn't be used for non-boolean settings."), + } + } + + fn byte_mask(&self) -> u8 { + match self.specific { + SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset, + _ => panic!("byte_for_value shouldn't be used for non-boolean settings."), + } + } +} + +#[derive(Hash, PartialEq, Eq)] +pub struct PresetIndex(usize); + +#[derive(Hash, PartialEq, Eq)] +pub enum PresetType { + BoolSetting(BoolSettingIndex), + OtherPreset(PresetIndex), +} + +impl Into for BoolSettingIndex { + fn into(self) -> PresetType { + PresetType::BoolSetting(self) + } +} +impl Into for PresetIndex { + fn into(self) -> PresetType { + PresetType::OtherPreset(self) + } +} + +#[derive(Hash, PartialEq, Eq)] +pub struct Preset { + pub name: &'static str, + values: Vec, +} + +impl Preset { + pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> { + let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0)) + .take(group.settings_size as usize) + .collect(); + for bool_index in &self.values { + let setting = &group.settings[bool_index.0]; + let mask = setting.byte_mask(); + let val = setting.byte_for_value(true); + assert!((val & !mask) == 0); + let (l_mask, l_val) = layout.get_mut(setting.byte_offset as usize).unwrap(); + *l_mask |= mask; + *l_val = (*l_val & !mask) | val; + } + layout + } +} + +pub struct SettingGroup { + pub name: &'static str, + pub settings: Vec, + pub bool_start_byte_offset: u8, + pub settings_size: u8, + pub presets: Vec, + pub predicates: Vec, +} + +impl SettingGroup { + fn num_bool_settings(&self) -> u8 { + self.settings + .iter() + .filter(|s| { + if let SpecificSetting::Bool(_) = s.specific { + true + } else { + false + } + }).count() as u8 + } + + pub fn byte_size(&self) -> u8 { + let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8); + self.bool_start_byte_offset + (num_predicates + 7) / 8 + } + + pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) { + for (i, s) in self.settings.iter().enumerate() { + if let SpecificSetting::Bool(_) = s.specific { + if s.name == name { + return (BoolSettingIndex(i), self); + } + } + } + panic!("Should have found bool setting by name."); + } +} + +/// This is the basic information needed to track the specific parts of a setting when building +/// them. +pub enum ProtoSpecificSetting { + Bool(bool, u8), + Enum(Vec<&'static str>), + Num(u8), +} + +/// This is the information provided during building for a setting. +struct ProtoSetting { + name: &'static str, + comment: &'static str, + specific: ProtoSpecificSetting, +} + +#[derive(Hash, PartialEq, Eq)] +pub enum PredicateNode { + OwnedBool(BoolSettingIndex), + SharedBool(&'static str, &'static str), + And(Box, Box), +} + +impl Into for BoolSettingIndex { + fn into(self) -> PredicateNode { + PredicateNode::OwnedBool(self) + } +} +impl<'a> Into for (BoolSettingIndex, &'a SettingGroup) { + fn into(self) -> PredicateNode { + let (index, group) = (self.0, self.1); + let setting = &group.settings[index.0]; + PredicateNode::SharedBool(group.name, setting.name) + } +} + +impl PredicateNode { + fn render(&self, group: &SettingGroup) -> String { + match self { + PredicateNode::OwnedBool(bool_setting_index) => format!( + "{}.{}()", + group.name, group.settings[bool_setting_index.0].name + ), + PredicateNode::SharedBool(group_name, bool_name) => { + format!("{}.{}()", group_name, bool_name) + } + PredicateNode::And(lhs, rhs) => { + format!("{} && {}", lhs.render(group), rhs.render(group)) + } + } + } +} + +pub struct Predicate { + pub name: &'static str, + node: PredicateNode, + pub number: u8, +} + +impl Predicate { + pub fn render(&self, group: &SettingGroup) -> String { + self.node.render(group) + } +} + +pub struct SettingGroupBuilder { + name: &'static str, + settings: Vec, + presets: Vec, + predicates: Vec, + predicate_number: u8, +} + +impl SettingGroupBuilder { + pub fn new(name: &'static str) -> Self { + Self { + name, + settings: Vec::new(), + presets: Vec::new(), + predicates: Vec::new(), + predicate_number: 0, + } + } + + fn add_setting( + &mut self, + name: &'static str, + comment: &'static str, + specific: ProtoSpecificSetting, + ) { + self.settings.push(ProtoSetting { + name, + comment, + specific, + }) + } + + pub fn add_bool( + &mut self, + name: &'static str, + comment: &'static str, + default: bool, + ) -> BoolSettingIndex { + assert!( + self.predicates.len() == 0, + "predicates must be added after the boolean settings" + ); + let predicate_number = self.predicate_number; + self.predicate_number += 1; + self.add_setting( + name, + comment, + ProtoSpecificSetting::Bool(default, predicate_number), + ); + BoolSettingIndex(self.settings.len() - 1) + } + + pub fn add_enum( + &mut self, + name: &'static str, + comment: &'static str, + values: Vec<&'static str>, + ) { + self.add_setting(name, comment, ProtoSpecificSetting::Enum(values)); + } + + pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) { + self.add_setting(name, comment, ProtoSpecificSetting::Num(default)); + } + + pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) { + let number = self.predicate_number; + self.predicate_number += 1; + self.predicates.push(Predicate { name, node, number }); + } + + pub fn add_preset(&mut self, name: &'static str, args: Vec) -> PresetIndex { + let mut values = Vec::new(); + for arg in args { + match arg { + PresetType::OtherPreset(index) => { + values.extend(self.presets[index.0].values.iter()); + } + PresetType::BoolSetting(index) => values.push(index), + } + } + self.presets.push(Preset { name, values }); + PresetIndex(self.presets.len() - 1) + } + + /// Compute the layout of the byte vector used to represent this settings + /// group. + /// + /// The byte vector contains the following entries in order: + /// + /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`. + /// 2. `BoolSetting` settings. + /// 3. Precomputed named predicates. + /// 4. Other numbered predicates, including anonymous predicates and parent + /// predicates that need to be accessible by number. + /// + /// Set `self.settings_size` to the length of the byte vector prefix that + /// contains the settings. All bytes after that are computed, not + /// configured. + /// + /// Set `self.boolean_offset` to the beginning of the numbered predicates, + /// 2. in the list above. + /// + /// Assign `byte_offset` and `bit_offset` fields in all settings. + /// + /// After calling this method, no more settings can be added, but + /// additional predicates can be made accessible with `number_predicate()`. + pub fn finish(self) -> SettingGroup { + let mut group = SettingGroup { + name: self.name, + settings: Vec::new(), + bool_start_byte_offset: 0, + settings_size: 0, + presets: Vec::new(), + predicates: Vec::new(), + }; + + let mut byte_offset = 0; + + // Assign the non-boolean settings first. + for s in &self.settings { + let specific = match s.specific { + ProtoSpecificSetting::Bool(..) => continue, + ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()), + ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default), + }; + + group.settings.push(Setting { + name: s.name, + comment: s.comment, + byte_offset, + specific, + }); + + byte_offset += 1; + } + + group.bool_start_byte_offset = byte_offset; + + // Then the boolean settings. + for s in &self.settings { + let (default, predicate_number) = match s.specific { + ProtoSpecificSetting::Bool(default, predicate_number) => { + (default, predicate_number) + } + ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue, + }; + group.settings.push(Setting { + name: s.name, + comment: s.comment, + byte_offset: byte_offset + predicate_number / 8, + specific: SpecificSetting::Bool(BoolSetting { + default, + bit_offset: predicate_number % 8, + predicate_number, + }), + }); + } + + assert!( + group.predicates.len() == 0, + "settings_size is the byte size before adding predicates" + ); + group.settings_size = group.byte_size(); + + group.predicates.extend(self.predicates); + group.presets.extend(self.presets); + + group + } +} diff --git a/lib/codegen/meta/src/constant_hash.rs b/lib/codegen/meta/src/constant_hash.rs new file mode 100644 index 0000000000..84b5513d5a --- /dev/null +++ b/lib/codegen/meta/src/constant_hash.rs @@ -0,0 +1,50 @@ +pub fn simple_hash(s: &str) -> usize { + let mut h: u32 = 5381; + for c in s.chars() { + h = (h ^ c as u32).wrapping_add(h.rotate_right(6)); + } + h as usize +} + +/// Compute an open addressed, quadratically probed hash table containing +/// `items`. The returned table is a list containing the elements of the +/// iterable `items` and `None` in unused slots. +pub fn generate_table usize>(items: &Vec, hash_function: H) -> Vec> { + let size = (1.20 * items.len() as f64) as usize; + // TODO do we really need the multiply by two here? + let size = if size.is_power_of_two() { + size * 2 + } else { + size.next_power_of_two() + }; + + let mut table: Vec> = Vec::new(); + table.resize(size, None); + + for i in items { + let mut h = hash_function(i) % size; + let mut s = 0; + while table[h].is_some() { + s += 1; + h = (h + s) % size; + } + table[h] = Some(i); + } + + table +} + +#[test] +fn test_generate_table() { + let v = vec!["Hello".to_string(), "world".to_string()]; + let table = generate_table(&v, |s| simple_hash(&s)); + assert_eq!( + table, + vec![ + None, + Some(&"Hello".to_string()), + Some(&"world".to_string()), + None + ] + ); +} diff --git a/lib/codegen/meta/src/gen_registers.rs b/lib/codegen/meta/src/gen_registers.rs index 637a3ea3fa..9234dc5c67 100644 --- a/lib/codegen/meta/src/gen_registers.rs +++ b/lib/codegen/meta/src/gen_registers.rs @@ -78,7 +78,7 @@ fn gen_regbank_units(reg_bank: &RegBank, fmt: &mut Formatter) { } } -fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) -> Result<(), error::Error> { +fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) { // Emit RegInfo. fmt.line("pub static INFO: RegInfo = RegInfo {"); @@ -128,13 +128,11 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) -> Result<(), error::Error> { fmt.line("}") }); fmt.line("}"); - - Ok(()) } -pub fn generate(isa: TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> { +pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> { let mut fmt = Formatter::new(); - gen_isa(&isa, &mut fmt)?; + gen_isa(&isa, &mut fmt); fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?; Ok(()) } diff --git a/lib/codegen/meta/src/gen_settings.rs b/lib/codegen/meta/src/gen_settings.rs new file mode 100644 index 0000000000..cd50a57f88 --- /dev/null +++ b/lib/codegen/meta/src/gen_settings.rs @@ -0,0 +1,447 @@ +use base; +use cdsl::camel_case; +use cdsl::isa::TargetIsa; +use cdsl::settings::{BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting}; +use constant_hash::{generate_table, simple_hash}; +use error; +use srcgen::{Formatter, Match}; +use std::collections::HashMap; +use unique_table::UniqueTable; + +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", + }; + fmt.line("impl Flags {"); + fmt.indent(|fmt| { + fmt.doc_comment(&format!("Create flags {} settings group.", group.name)); + fmt.line("#[allow(unused_variables)]"); + fmt.line(&format!("pub fn new({}) -> Self {{", args)); + fmt.indent(|fmt| { + fmt.line(&format!( + "let bvec = builder.state_for(\"{}\");", + group.name + )); + fmt.line(&format!( + "let mut {} = Self {{ bytes: [0; {}] }};", + group.name, + group.byte_size() + )); + fmt.line(&format!( + "debug_assert_eq!(bvec.len(), {});", + group.settings_size + )); + fmt.line(&format!( + "{}.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)); + fmt.line(&format!("if {} {{", p.render(group))); + fmt.indent(|fmt| { + fmt.line(&format!( + "{}.bytes[{}] |= 1 << {};", + group.name, + group.bool_start_byte_offset + p.number / 8, + p.number % 8 + )); + }); + fmt.line("}"); + } + + fmt.line(group.name); + }); + fmt.line("}"); + }); + fmt.line("}"); +} + +/// Emit Display and FromStr implementations for enum settings. +fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) { + fmt.line(&format!("impl fmt::Display for {} {{", name)); + fmt.indent(|fmt| { + fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {"); + fmt.indent(|fmt| { + fmt.line("f.write_str(match *self {"); + fmt.indent(|fmt| { + for v in values.iter() { + fmt.line(&format!("{}::{} => \"{}\",", name, camel_case(v), v)); + } + }); + fmt.line("})"); + }); + fmt.line("}"); + }); + fmt.line("}"); + + fmt.line(&format!("impl str::FromStr for {} {{", name)); + fmt.indent(|fmt| { + fmt.line("type Err = ();"); + fmt.line("fn from_str(s: &str) -> Result {"); + fmt.indent(|fmt| { + fmt.line("match s {"); + fmt.indent(|fmt| { + for v in values.iter() { + fmt.line(&format!("\"{}\" => Ok({}::{}),", v, name, camel_case(v))); + } + fmt.line("_ => Err(()),"); + }); + fmt.line("}"); + }); + fmt.line("}"); + }); + fmt.line("}"); +} + +/// 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)); + fmt.line("#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]"); + fmt.line(&format!("pub enum {} {{", name)); + fmt.indent(|fmt| { + for v in values.iter() { + fmt.doc_comment(&format!("`{}`.", v)); + fmt.line(&format!("{},", camel_case(v))); + } + }); + fmt.line("}"); + + 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, .. + }) => { + fmt.line(&format!("pub fn {}(&self) -> bool {{", setting.name)); + fmt.indent(|fmt| { + fmt.line(&format!("self.numbered_predicate({})", predicate_number)); + }); + fmt.line("}"); + } + SpecificSetting::Enum(ref values) => { + let ty = camel_case(setting.name); + fmt.line(&format!("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( + format!("{}", i), + vec![], + format!("{}::{}", ty, camel_case(v)), + ); + } + m.arm("_", vec![], "panic!(\"Invalid enum value\")"); + fmt.add_match(m); + }); + fmt.line("}"); + } + SpecificSetting::Num(_) => { + fmt.line(&format!("pub fn {}(&self) -> u8 {{", setting.name)); + fmt.indent(|fmt| { + fmt.line(&format!("self.bytes[{}]", setting.byte_offset)); + }); + fmt.line("}"); + } + } +} + +fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) { + fmt.doc_comment(&format!( + "Computed predicate `{}`.", + predicate.render(group) + )); + fmt.line(&format!("pub fn {}(&self) -> bool {{", predicate.name)); + fmt.indent(|fmt| { + fmt.line(&format!("self.numbered_predicate({})", predicate.number)); + }); + fmt.line("}"); +} + +/// Emits getters for each setting value. +fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) { + fmt.doc_comment("User-defined settings."); + fmt.line("#[allow(dead_code)]"); + fmt.line("impl Flags {"); + fmt.indent(|fmt| { + fmt.doc_comment("Get a view of the boolean predicates."); + fmt.line("pub fn predicate_view(&self) -> ::settings::PredicateView {"); + fmt.indent(|fmt| { + fmt.line(&format!( + "::settings::PredicateView::new(&self.bytes[{}..])", + group.bool_start_byte_offset + )); + }); + fmt.line("}"); + + if group.settings.len() > 0 { + fmt.doc_comment("Dynamic numbered predicate getter."); + fmt.line("fn numbered_predicate(&self, p: usize) -> bool {"); + fmt.indent(|fmt| { + fmt.line(&format!( + "self.bytes[{} + p / 8] & (1 << (p % 8)) != 0", + group.bool_start_byte_offset + )); + }); + fmt.line("}"); + } + + for setting in &group.settings { + gen_getter(&setting, fmt); + } + for predicate in &group.predicates { + gen_pred_getter(&predicate, &group, fmt); + } + }); + fmt.line("}"); +} + +#[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: UniqueTable<&'static str> = UniqueTable::new(); + + let mut descriptor_index_map: HashMap = HashMap::new(); + + // Generate descriptors. + fmt.line(&format!( + "static DESCRIPTORS: [detail::Descriptor; {}] = [", + group.settings.len() + group.presets.len() + )); + fmt.indent(|fmt| { + for (idx, setting) in group.settings.iter().enumerate() { + fmt.line("detail::Descriptor {"); + fmt.indent(|fmt| { + fmt.line(&format!("name: \"{}\",", setting.name)); + fmt.line(&format!("offset: {},", setting.byte_offset)); + match &setting.specific { + SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => { + fmt.line(&format!( + "detail: detail::Detail::Bool {{ bit: {} }},", + bit_offset + )); + } + SpecificSetting::Enum(values) => { + let offset = enum_table.add(values); + fmt.line(&format!( + "detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},", + values.len() - 1, + offset + )); + } + SpecificSetting::Num(_) => { + fmt.line("detail: detail::Detail::Num,"); + } + } + + descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx); + }); + fmt.line("},"); + } + + for (idx, preset) in group.presets.iter().enumerate() { + fmt.line("detail::Descriptor {"); + fmt.indent(|fmt| { + fmt.line(&format!("name: \"{}\",", preset.name)); + fmt.line(&format!("offset: {},", (idx as u8) * group.settings_size)); + fmt.line("detail: detail::Detail::Preset,"); + }); + fmt.line("},"); + + descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx); + } + }); + fmt.line("];"); + + // Generate enumerators. + fmt.line(&format!( + "static ENUMERATORS: [&str; {}] = [", + enum_table.len() + )); + fmt.indent(|fmt| { + for enum_val in enum_table.iter() { + fmt.line(&format!("\"{}\",", enum_val)); + } + }); + fmt.line("];"); + + // Generate hash table. + let mut hash_entries: Vec = Vec::new(); + hash_entries.extend( + group + .settings + .iter() + .map(|x| SettingOrPreset::Setting(x)) + .collect::>(), + ); + hash_entries.extend( + group + .presets + .iter() + .map(|x| SettingOrPreset::Preset(x)) + .collect::>(), + ); + let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name())); + fmt.line(&format!( + "static HASH_TABLE: [u16; {}] = [", + hash_table.len() + )); + fmt.indent(|fmt| { + for h in &hash_table { + match h { + Some(setting_or_preset) => fmt.line(&format!( + "{},", + &descriptor_index_map + .get(setting_or_preset) + .unwrap() + .to_string() + )), + None => fmt.line("0xffff,"), + } + } + }); + fmt.line("];"); + + // Generate presets. + fmt.line(&format!( + "static PRESETS: [(u8, u8); {}] = [", + group.presets.len() + )); + fmt.indent(|fmt| { + for preset in &group.presets { + fmt.comment(preset.name); + for (mask, value) in preset.layout(&group) { + fmt.line(&format!("(0b{:08b}, 0b{:08b}),", mask, value)); + } + } + }); + fmt.line("];"); +} + +fn gen_template(group: &SettingGroup, fmt: &mut Formatter) { + let mut default_bytes: Vec = Vec::new(); + default_bytes.resize(group.settings_size as usize, 0); + 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(", "); + + fmt.line("static TEMPLATE: detail::Template = detail::Template {"); + fmt.indent(|fmt| { + fmt.line(&format!("name: \"{}\",", group.name)); + fmt.line("descriptors: &DESCRIPTORS,"); + fmt.line("enumerators: &ENUMERATORS,"); + fmt.line("hash_table: &HASH_TABLE,"); + fmt.line(&format!("defaults: &[{}],", default_bytes_str)); + fmt.line("presets: &PRESETS,"); + }); + fmt.line("};"); + + fmt.doc_comment(&format!( + "Create a `settings::Builder` for the {} settings group.", + group.name + )); + fmt.line("pub fn builder() -> Builder {"); + fmt.indent(|fmt| { + fmt.line("Builder::new(&TEMPLATE)"); + }); + fmt.line("}"); +} + +fn gen_display(group: &SettingGroup, fmt: &mut Formatter) { + fmt.line("impl fmt::Display for Flags {"); + fmt.indent(|fmt| { + fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {"); + fmt.indent(|fmt| { + fmt.line(&format!("writeln!(f, \"[{}]\")?;", group.name)); + fmt.line("for d in &DESCRIPTORS {"); + fmt.indent(|fmt| { + fmt.line("if !d.detail.is_preset() {"); + fmt.indent(|fmt| { + fmt.line("write!(f, \"{} = \", d.name)?;"); + fmt.line( + "TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;", + ); + fmt.line("writeln!(f)?;"); + }); + fmt.line("}"); + }); + fmt.line("}"); + fmt.line("Ok(())"); + }); + fmt.line("}") + }); + fmt.line("}"); +} + +fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) { + // Generate struct. + fmt.line("#[derive(Clone)]"); + fmt.doc_comment(&format!("Flags group `{}`.", group.name)); + fmt.line("pub struct Flags {"); + fmt.indent(|fmt| { + fmt.line(&format!("bytes: [u8; {}],", group.byte_size())); + }); + fmt.line("}"); + + 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 fn generate_common(filename: &str, out_dir: &str) -> Result { + let settings = base::settings::generate(); + let mut fmt = Formatter::new(); + gen_group(&settings, ParentGroup::None, &mut fmt); + fmt.update_file(filename, out_dir)?; + Ok(settings) +} + +pub fn generate(isa: &TargetIsa, prefix: &str, out_dir: &str) -> Result<(), error::Error> { + let mut fmt = Formatter::new(); + gen_group(&isa.settings, ParentGroup::Shared, &mut fmt); + fmt.update_file(&format!("{}-{}.rs", prefix, isa.name), out_dir)?; + Ok(()) +} diff --git a/lib/codegen/meta/src/isa/arm32/mod.rs b/lib/codegen/meta/src/isa/arm32/mod.rs index e0e47c2e2e..1a4fe471d7 100644 --- a/lib/codegen/meta/src/isa/arm32/mod.rs +++ b/lib/codegen/meta/src/isa/arm32/mod.rs @@ -1,8 +1,14 @@ use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use cdsl::settings::{SettingGroup, SettingGroupBuilder}; -pub fn define() -> TargetIsa { - let mut isa = TargetIsaBuilder::new("arm32"); +fn define_settings(_shared: &SettingGroup) -> SettingGroup { + let setting = SettingGroupBuilder::new("arm32"); + setting.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let mut isa = TargetIsaBuilder::new("arm32", define_settings(shared_settings)); let builder = RegBankBuilder::new("FloatRegs", "s") .units(64) diff --git a/lib/codegen/meta/src/isa/arm64/mod.rs b/lib/codegen/meta/src/isa/arm64/mod.rs index 2435f3676e..63f5fa661e 100644 --- a/lib/codegen/meta/src/isa/arm64/mod.rs +++ b/lib/codegen/meta/src/isa/arm64/mod.rs @@ -1,8 +1,14 @@ use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use cdsl::settings::{SettingGroup, SettingGroupBuilder}; -pub fn define() -> TargetIsa { - let mut isa = TargetIsaBuilder::new("arm64"); +fn define_settings(_shared: &SettingGroup) -> SettingGroup { + let setting = SettingGroupBuilder::new("arm64"); + setting.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let mut isa = TargetIsaBuilder::new("arm64", define_settings(shared_settings)); // The `x31` regunit serves as the stack pointer / zero register depending on context. We // reserve it and don't model the difference. diff --git a/lib/codegen/meta/src/isa/mod.rs b/lib/codegen/meta/src/isa/mod.rs index 18f75c42e5..f6d012fe78 100644 --- a/lib/codegen/meta/src/isa/mod.rs +++ b/lib/codegen/meta/src/isa/mod.rs @@ -1,4 +1,5 @@ use cdsl::isa::TargetIsa; +use cdsl::settings::SettingGroup; use std::fmt; mod arm32; @@ -61,11 +62,11 @@ impl fmt::Display for Isa { } } -pub fn define_all() -> Vec { +pub fn define_all(shared_settings: &SettingGroup) -> Vec { vec![ - riscv::define(), - arm32::define(), - arm64::define(), - x86::define(), + riscv::define(shared_settings), + arm32::define(shared_settings), + arm64::define(shared_settings), + x86::define(shared_settings), ] } diff --git a/lib/codegen/meta/src/isa/riscv/mod.rs b/lib/codegen/meta/src/isa/riscv/mod.rs index 67f504c0f8..9d82997e94 100644 --- a/lib/codegen/meta/src/isa/riscv/mod.rs +++ b/lib/codegen/meta/src/isa/riscv/mod.rs @@ -1,8 +1,61 @@ use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; -pub fn define() -> TargetIsa { - let mut isa = TargetIsaBuilder::new("riscv"); +fn define_settings(shared: &SettingGroup) -> SettingGroup { + let mut setting = SettingGroupBuilder::new("riscv"); + + let supports_m = setting.add_bool( + "supports_m", + "CPU supports the 'M' extension (mul/div)", + false, + ); + let supports_a = setting.add_bool( + "supports_a", + "CPU supports the 'A' extension (atomics)", + false, + ); + let supports_f = setting.add_bool( + "supports_f", + "CPU supports the 'F' extension (float)", + false, + ); + let supports_d = setting.add_bool( + "supports_d", + "CPU supports the 'D' extension (double)", + false, + ); + + let enable_m = setting.add_bool( + "enable_m", + "Enable the use of 'M' instructions if available", + true, + ); + + setting.add_bool( + "enable_e", + "Enable the 'RV32E' instruction set with only 16 registers", + true, + ); + + let shared_enable_atomics = shared.get_bool("enable_atomics"); + let shared_enable_float = shared.get_bool("enable_float"); + let shared_enable_simd = shared.get_bool("enable_simd"); + + setting.add_predicate("use_m", predicate!(supports_m && enable_m)); + setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics)); + setting.add_predicate("use_f", predicate!(supports_f && shared_enable_float)); + setting.add_predicate("use_d", predicate!(supports_d && shared_enable_float)); + setting.add_predicate( + "full_float", + predicate!(shared_enable_simd && supports_f && supports_d), + ); + + setting.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let mut isa = TargetIsaBuilder::new("riscv", define_settings(shared_settings)); let builder = RegBankBuilder::new("IntRegs", "x") .units(32) diff --git a/lib/codegen/meta/src/isa/x86/mod.rs b/lib/codegen/meta/src/isa/x86/mod.rs index 7970ea06db..962446c9be 100644 --- a/lib/codegen/meta/src/isa/x86/mod.rs +++ b/lib/codegen/meta/src/isa/x86/mod.rs @@ -1,9 +1,74 @@ use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; -pub fn define() -> TargetIsa { - let mut isa = TargetIsaBuilder::new("x86"); +pub fn define_settings(_shared: &SettingGroup) -> SettingGroup { + let mut settings = SettingGroupBuilder::new("x86"); + // CPUID.01H:ECX + let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false); + let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false); + let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false); + let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false); + let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false); + settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false); + + // CPUID.(EAX=07H, ECX=0H):EBX + let has_bmi1 = settings.add_bool( + "has_bmi1", + "BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]", + false, + ); + let has_bmi2 = settings.add_bool( + "has_bmi2", + "BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]", + false, + ); + + // CPUID.EAX=80000001H:ECX + let has_lzcnt = settings.add_bool( + "has_lzcnt", + "LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]", + false, + ); + + settings.add_predicate("use_sse41", predicate!(has_sse41)); + settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42)); + settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42)); + settings.add_predicate("use_bmi1", predicate!(has_bmi1)); + settings.add_predicate("use_lznct", predicate!(has_lzcnt)); + + settings.add_preset("baseline", preset!()); + let nehalem = settings.add_preset( + "nehalem", + preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt), + ); + let haswell = settings.add_preset( + "haswell", + preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt), + ); + let broadwell = settings.add_preset("broadwell", preset!(haswell)); + let skylake = settings.add_preset("skylake", preset!(broadwell)); + let cannonlake = settings.add_preset("cannonlake", preset!(skylake)); + settings.add_preset("icelake", preset!(cannonlake)); + settings.add_preset( + "znver1", + preset!( + has_sse3 + && has_ssse3 + && has_sse41 + && has_sse42 + && has_popcnt + && has_bmi1 + && has_bmi2 + && has_lzcnt + ), + ); + + settings.finish() +} + +fn define_registers(isa: &mut TargetIsaBuilder) { let builder = RegBankBuilder::new("IntRegs", "r") .units(16) .names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"]) @@ -38,6 +103,14 @@ pub fn define() -> TargetIsa { let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8); isa.add_reg_class(builder); +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let settings = define_settings(shared_settings); + + let mut isa = TargetIsaBuilder::new("x86", settings); + + define_registers(&mut isa); isa.finish() } diff --git a/lib/codegen/meta/src/lib.rs b/lib/codegen/meta/src/lib.rs index 233138e35d..e393079f45 100644 --- a/lib/codegen/meta/src/lib.rs +++ b/lib/codegen/meta/src/lib.rs @@ -1,11 +1,16 @@ #[macro_use] extern crate cranelift_entity; +#[macro_use] +mod cdsl; + pub mod error; pub mod gen_registers; +pub mod gen_settings; pub mod gen_types; pub mod isa; mod base; -mod cdsl; +mod constant_hash; mod srcgen; +mod unique_table; diff --git a/lib/codegen/meta/src/srcgen.rs b/lib/codegen/meta/src/srcgen.rs index a7941e9bf8..db3eac85e8 100644 --- a/lib/codegen/meta/src/srcgen.rs +++ b/lib/codegen/meta/src/srcgen.rs @@ -99,7 +99,7 @@ impl Formatter { } /// Add a comment line. - pub fn _comment(&mut self, s: &str) { + pub fn comment(&mut self, s: &str) { let commented_line = format!("// {}", s); self.line(&commented_line); } @@ -302,7 +302,7 @@ match x { let mut fmt = Formatter::new(); fmt.line("Hello line 1"); fmt.indent_push(); - fmt._comment("Nested comment"); + fmt.comment("Nested comment"); fmt.indent_pop(); fmt.line("Back home again"); let expected_lines = vec![ diff --git a/lib/codegen/meta/src/unique_table.rs b/lib/codegen/meta/src/unique_table.rs new file mode 100644 index 0000000000..8e325cdeed --- /dev/null +++ b/lib/codegen/meta/src/unique_table.rs @@ -0,0 +1,68 @@ +use std::slice; + +/// A table of sequences which tries to avoid common subsequences. +pub struct UniqueTable { + table: Vec, +} + +impl UniqueTable { + pub fn new() -> Self { + Self { table: Vec::new() } + } + pub fn add(&mut self, values: &Vec) -> usize { + if let Some(offset) = find_subsequence(values, &self.table) { + offset + } else { + let offset = self.table.len(); + self.table.extend((*values).clone()); + offset + } + } + pub fn len(&self) -> usize { + self.table.len() + } + pub fn iter(&self) -> slice::Iter { + self.table.iter() + } +} + +/// Try to find the subsequence `sub` in the `whole` sequence. Returns None if +/// it's not been found, or Some(index) if it has been. Naive implementation +/// until proven we need something better. +fn find_subsequence(sub: &Vec, whole: &Vec) -> Option { + assert!(sub.len() > 0); + // We want i + sub.len() <= whole.len(), i.e. i < whole.len() + 1 - sub.len(). + if whole.len() < sub.len() { + return None; + } + let max = whole.len() + 1 - sub.len(); + for i in 0..max { + let mut found: Option = Some(i); + for j in 0..sub.len() { + if sub[j] != whole[i + j] { + found = None; + break; + } + } + if found.is_some() { + return found; + } + } + return None; +} + +#[test] +fn test_find_subsequence() { + assert_eq!(find_subsequence(&vec![1], &vec![4]), None); + assert_eq!(find_subsequence(&vec![1], &vec![1]), Some(0)); + assert_eq!(find_subsequence(&vec![1, 2], &vec![1]), None); + assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 2]), Some(0)); + assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 3]), None); + assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 2]), Some(1)); + assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1]), None); + assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1, 2]), Some(3)); + assert_eq!( + find_subsequence(&vec![1, 1, 3], &vec![1, 1, 1, 3, 3]), + Some(1) + ); +} diff --git a/lib/codegen/src/constant_hash.rs b/lib/codegen/src/constant_hash.rs index 0aaa3c51c2..2c3bca1a20 100644 --- a/lib/codegen/src/constant_hash.rs +++ b/lib/codegen/src/constant_hash.rs @@ -56,7 +56,7 @@ pub fn probe + ?Sized>( } /// A primitive hash function for matching opcodes. -/// Must match `lib/codegen/meta-python/constant_hash.py`. +/// Must match `lib/codegen/meta-python/constant_hash.py` and `lib/codegen/meta/constant_hash.rs`. pub fn simple_hash(s: &str) -> usize { let mut h: u32 = 5381; for c in s.chars() {