diff --git a/cranelift/src/libcretonne/isa/riscv/settings.rs b/cranelift/src/libcretonne/isa/riscv/settings.rs index 0ee83b3015..335314a3c9 100644 --- a/cranelift/src/libcretonne/isa/riscv/settings.rs +++ b/cranelift/src/libcretonne/isa/riscv/settings.rs @@ -1,20 +1,21 @@ //! RISC-V Settings. -use settings::{Descriptor, Detail, Stringwise, Result, Error}; +use settings::{detail, Builder}; use std::fmt; -// Include code generated by `meta/gen_settings.py`. This file contains a public `Settings` struct +// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct // with an impl for all of the settings defined in `meta/cretonne/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings-riscv.rs")); #[cfg(test)] mod tests { - use super::Settings; + use super::{builder, Flags}; #[test] fn display_default() { - let s = Settings::default(); - assert_eq!(s.to_string(), + let b = builder(); + let f = Flags::new(b); + assert_eq!(f.to_string(), "[riscv]\n\ supports_m = false\n\ supports_a = false\n\ diff --git a/cranelift/src/libcretonne/settings.rs b/cranelift/src/libcretonne/settings.rs index af25dd458a..c65c5eeb68 100644 --- a/cranelift/src/libcretonne/settings.rs +++ b/cranelift/src/libcretonne/settings.rs @@ -2,49 +2,151 @@ //! //! This module defines data structures to access the settings defined in the meta language. //! -//! Each settings group is translated to a `Settings` struct either in this module or in its +//! Each settings group is translated to a `Flags` struct either in this module or in its //! ISA-specific `settings` module. The struct provides individual getter methods for all of the -//! settings. It also implements the `Stringwise` trait which allows settings to be manipulated by -//! name. +//! settings as well as computed predicate flags. +//! +//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to +//! create it. +//! +//! # Example +//! ``` +//! use cretonne::settings::{self, Configurable}; +//! +//! let mut b = settings::builder(); +//! b.set("opt_level", "fastest"); +//! +//! let f = settings::Flags::new(b); +//! assert_eq!(f.opt_level(), settings::OptLevel::Fastest); +//! ``` use std::fmt; use std::result; -/// A setting descriptor holds the information needed to generically set and print a setting. +use simple_hash::simple_hash; + +/// A string-based configurator for settings groups. /// -/// Each settings group will be represented as a constant DESCRIPTORS array. -pub struct Descriptor { - /// Lower snake-case name of setting as defined in meta. - pub name: &'static str, +/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags` +/// struct is created. +pub trait Configurable { + /// Set the string value of any setting by name. + /// + /// This can set any type of setting whether it is numeric, boolean, or enumerated. + fn set(&mut self, name: &str, value: &str) -> Result<()>; - /// Offset of byte containing this setting. - pub offset: u32, - - /// Additional details, depending on the kind of setting. - pub detail: Detail, + /// Set the value of a boolean setting by name. + /// + /// If the identified setting isn't a boolean, a `BadType` error is returned. + fn set_bool(&mut self, name: &str, value: bool) -> Result<()>; } -/// The different kind of settings along with descriptor bits that depend on the kind. -#[derive(Clone, Copy)] -pub enum Detail { - /// A boolean setting only uses one bit, numbered from LSB. - Bool { - bit: u8, - }, - - /// A numerical setting uses the whole byte. - Num, - - /// An Enum setting uses a range of enumerators. - Enum { - /// Numerical value of last enumerator, allowing for 1-256 enumerators. - last: u8, - - /// First enumerator in the ENUMERATORS table. - enumerators: u16, - }, +/// Collect settings values based on a template. +pub struct Builder { + template: &'static detail::Template, + bytes: Vec, } +impl Builder { + /// Create a new builder with defaults and names from the given template. + pub fn new(tmpl: &'static detail::Template) -> Builder { + Builder { + template: tmpl, + bytes: tmpl.defaults.into(), + } + } + + /// Extract contents of builder once everything is configured. + pub fn finish(self, name: &str) -> Vec { + assert_eq!(name, self.template.name); + self.bytes + } + + /// Set the value of a single bit. + fn set_bit(&mut self, offset: usize, bit: u8, value: bool) { + let byte = &mut self.bytes[offset]; + let mask = 1 << bit; + if value { + *byte |= mask; + } else { + *byte &= !mask; + } + } + + /// Look up a descriptor by name. + fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> { + let table = self.template.hash_table; + let descs = self.template.descriptors; + let mask = table.len() - 1; + assert!((mask + 1).is_power_of_two()); + + let mut idx = simple_hash(name) as usize; + let mut step: usize = 0; + + loop { + idx = idx & mask; + let entry = table[idx] as usize; + if entry >= descs.len() { + return Err(Error::BadName); + } + let desc = &descs[entry]; + if desc.name == name { + return Ok((desc.offset as usize, desc.detail)); + } + step += 1; + assert!(step <= mask); + idx += step; + } + } +} + +fn parse_bool_value(value: &str) -> Result { + match value { + "true" | "on" | "yes" | "1" => Ok(true), + "false" | "off" | "no" | "0" => Ok(false), + _ => Err(Error::BadValue), + } +} + +fn parse_enum_value(value: &str, choices: &[&str]) -> Result { + match choices.iter().position(|&tag| tag == value) { + Some(idx) => Ok(idx as u8), + None => Err(Error::BadValue), + } +} + +impl Configurable for Builder { + fn set_bool(&mut self, name: &str, value: bool) -> Result<()> { + use self::detail::Detail; + let (offset, detail) = try!(self.lookup(name)); + if let Detail::Bool { bit } = detail { + self.set_bit(offset, bit, value); + Ok(()) + } else { + Err(Error::BadType) + } + } + + fn set(&mut self, name: &str, value: &str) -> Result<()> { + use self::detail::Detail; + let (offset, detail) = try!(self.lookup(name)); + match detail { + Detail::Bool { bit } => { + self.set_bit(offset, bit, try!(parse_bool_value(value))); + } + Detail::Num => { + self.bytes[offset] = try!(value.parse().map_err(|_| Error::BadValue)); + } + Detail::Enum { last, enumerators } => { + self.bytes[offset] = try!(parse_enum_value(value, + self.template.enums(last, enumerators))); + } + } + Ok(()) + } +} + +/// An error produced when changing a setting. #[derive(Debug, PartialEq, Eq)] pub enum Error { /// No setting by this name exists. @@ -59,143 +161,133 @@ pub enum Error { pub type Result = result::Result; -fn parse_bool_value(value: &str) -> Result { - match value { - "true" | "on" | "yes" | "1" => Ok(true), - "false" | "off" | "no" | "0" => Ok(false), - _ => Err(Error::BadValue), +/// Implementation details for generated code. +/// +/// This module holds definitions that need to be public so the can be instantiated by generated +/// code in other modules. +pub mod detail { + use std::fmt; + + /// An instruction group template. + pub struct Template { + pub name: &'static str, + pub descriptors: &'static [Descriptor], + pub enumerators: &'static [&'static str], + pub hash_table: &'static [u16], + pub defaults: &'static [u8], } -} -/// Interface for working with a group of settings as strings. -pub trait Stringwise { - /// Look up a setting by name, return the byte offset and details of the setting. - fn lookup(&self, name: &str) -> Result<(usize, Detail)>; - - /// Get an enumerator string from the `Detail::enumerators` value and an offset. - fn enumerator(&self, enums: u16, value: u8) -> &'static str; - - /// Get the underlying byte array used to store settings. - fn raw_bytes_mut(&mut self) -> &mut [u8]; - - /// Format a setting value as a TOML string. This is mostly for use by the generated `Display` - /// implementation. - fn format_toml_value(&self, detail: Detail, byte: u8, f: &mut fmt::Formatter) -> fmt::Result { - match detail { - Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0), - Detail::Num => write!(f, "{}", byte), - Detail::Enum { last, enumerators } => { - if byte <= last { - write!(f, "\"{}\"", self.enumerator(enumerators, byte)) - } else { - write!(f, "{}", byte) - } - } + impl Template { + /// Get enumerators corresponding to a `Details::Enum`. + pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] { + let from = enumerators as usize; + let len = last as usize + 1; + &self.enumerators[from..from + len] } - } - /// Set a boolean setting by name. - fn set_bool(&mut self, name: &str, value: bool) -> Result<()> { - let (offset, detail) = try!(self.lookup(name)); - if let Detail::Bool { bit } = detail { - let mask = 1 << bit; - let byte = &mut self.raw_bytes_mut()[offset]; - if value { - *byte |= mask; - } else { - *byte &= !mask; - } - Ok(()) - } else { - Err(Error::BadType) - } - } - - /// Set the string value of a named setting. - /// - /// For boolean settings, any of the values accepted by `parse_bool_value` above are accepted - /// (true/false, on/off, yes/no, 1/0). - /// - /// For enumerated settings, the value must match one of the allowed values exactly. - fn set(&mut self, name: &str, value: &str) -> Result<()> { - let (offset, detail) = try!(self.lookup(name)); - match detail { - Detail::Bool { bit } => { - let mask = 1 << bit; - let byte = &mut self.raw_bytes_mut()[offset]; - if try!(parse_bool_value(value)) { - *byte |= mask; - } else { - *byte &= !mask; - } - } - Detail::Num => { - self.raw_bytes_mut()[offset] = try!(value.parse().map_err(|_| Error::BadValue)); - } - Detail::Enum { last, enumerators } => { - // Linear search.. - for i in 0.. { - if value == self.enumerator(enumerators, i) { - self.raw_bytes_mut()[offset] = i; - break; - } - if i == last { - return Err(Error::BadValue); + /// Format a setting value as a TOML string. This is mostly for use by the generated + /// `Display` implementation. + pub fn format_toml_value(&self, + detail: Detail, + byte: u8, + f: &mut fmt::Formatter) + -> fmt::Result { + match detail { + Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0), + Detail::Num => write!(f, "{}", byte), + Detail::Enum { last, enumerators } => { + if byte <= last { + let tags = self.enums(last, enumerators); + write!(f, "\"{}\"", tags[byte as usize]) + } else { + write!(f, "{}", byte) } } } } - Ok(()) + } + + /// A setting descriptor holds the information needed to generically set and print a setting. + /// + /// Each settings group will be represented as a constant DESCRIPTORS array. + pub struct Descriptor { + /// Lower snake-case name of setting as defined in meta. + pub name: &'static str, + + /// Offset of byte containing this setting. + pub offset: u32, + + /// Additional details, depending on the kind of setting. + pub detail: Detail, + } + + /// The different kind of settings along with descriptor bits that depend on the kind. + #[derive(Clone, Copy)] + pub enum Detail { + /// A boolean setting only uses one bit, numbered from LSB. + Bool { + bit: u8, + }, + + /// A numerical setting uses the whole byte. + Num, + + /// An Enum setting uses a range of enumerators. + Enum { + /// Numerical value of last enumerator, allowing for 1-256 enumerators. + last: u8, + + /// First enumerator in the ENUMERATORS table. + enumerators: u16, + }, } } -// Include code generated by `meta/gen_settings.py`. This file contains a public `Settings` struct +// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct // with an impl for all of the settings defined in `meta/cretonne/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings.rs")); #[cfg(test)] mod tests { - use super::Settings; + use super::{builder, Flags}; use super::Error::*; - use super::Stringwise; + use super::Configurable; #[test] fn display_default() { - let s = Settings::default(); - assert_eq!(s.to_string(), + let b = builder(); + let f = Flags::new(b); + assert_eq!(f.to_string(), "[shared]\n\ opt_level = \"default\"\n\ enable_simd = true\n"); + assert_eq!(f.opt_level(), super::OptLevel::Default); + assert_eq!(f.enable_simd(), true); } #[test] fn modify_bool() { - let mut s = Settings::default(); - assert_eq!(s.enable_simd(), true); - assert_eq!(s.set_bool("not_there", true), Err(BadName)); + let mut b = builder(); + assert_eq!(b.set_bool("not_there", true), Err(BadName)); + assert_eq!(b.set_bool("enable_simd", true), Ok(())); + assert_eq!(b.set_bool("enable_simd", false), Ok(())); - assert_eq!(s.set_bool("enable_simd", true), Ok(())); - assert_eq!(s.enable_simd(), true); - - assert_eq!(s.set_bool("enable_simd", false), Ok(())); - assert_eq!(s.enable_simd(), false); + let f = Flags::new(b); + assert_eq!(f.enable_simd(), false); } #[test] fn modify_string() { - let mut s = Settings::default(); - assert_eq!(s.enable_simd(), true); - assert_eq!(s.opt_level(), super::OptLevel::Default); + let mut b = builder(); + assert_eq!(b.set("not_there", "true"), Err(BadName)); + assert_eq!(b.set("enable_simd", ""), Err(BadValue)); + assert_eq!(b.set("enable_simd", "best"), Err(BadValue)); + assert_eq!(b.set("opt_level", "true"), Err(BadValue)); + assert_eq!(b.set("opt_level", "best"), Ok(())); + assert_eq!(b.set("enable_simd", "0"), Ok(())); - assert_eq!(s.set("not_there", "true"), Err(BadName)); - assert_eq!(s.set("enable_simd", ""), Err(BadValue)); - assert_eq!(s.set("enable_simd", "best"), Err(BadValue)); - assert_eq!(s.set("opt_level", "true"), Err(BadValue)); - - assert_eq!(s.set("enable_simd", "no"), Ok(())); - assert_eq!(s.enable_simd(), false); - - assert_eq!(s.set("opt_level", "best"), Ok(())); - assert_eq!(s.opt_level(), super::OptLevel::Best); + let f = Flags::new(b); + assert_eq!(f.enable_simd(), false); + assert_eq!(f.opt_level(), super::OptLevel::Best); } } diff --git a/meta/gen_settings.py b/meta/gen_settings.py index d8b0e4c919..a10dd5352a 100644 --- a/meta/gen_settings.py +++ b/meta/gen_settings.py @@ -75,10 +75,11 @@ def gen_getter(setting, fmt): proto = 'pub fn {}(&self) -> {}'.format(setting.name, ty) with fmt.indented(proto + ' {', '}'): with fmt.indented( - 'match self.bytes[{}] {{'.format(setting.byte_offset), '}'): + 'match self.bytes[{}] {{' + .format(setting.byte_offset), '}'): for i, v in enumerate(setting.values): - fmt.line( '{} => {}::{},'.format(i, ty, camel_case(v))) - fmt.line( '_ => panic!("Invalid enum value")') + fmt.line('{} => {}::{},'.format(i, ty, camel_case(v))) + fmt.line('_ => panic!("Invalid enum value")') else: raise AssertionError("Unknown setting kind") @@ -88,27 +89,11 @@ def gen_getters(sgrp, fmt): Emit getter functions for all the settings in fmt. """ fmt.doc_comment("User-defined settings.") - with fmt.indented('impl Settings {', '}'): + with fmt.indented('impl Flags {', '}'): for setting in sgrp.settings: gen_getter(setting, fmt) -def gen_default(sgrp, byte_size, fmt): - """ - Emit a Default impl for Settings. - """ - v = [0] * byte_size - for setting in sgrp.settings: - v[setting.byte_offset] |= setting.default_byte() - - with fmt.indented('impl Default for Settings {', '}'): - fmt.doc_comment('Return a `Settings` object with default values.') - with fmt.indented('fn default() -> Settings {', '}'): - with fmt.indented('Settings {', '}'): - vs = ', '.join('{:#04x}'.format(x) for x in v) - fmt.line('bytes: [ {} ],'.format(vs)) - - def gen_descriptors(sgrp, fmt): """ Generate the DESCRIPTORS and ENUMERATORS tables. @@ -117,30 +102,31 @@ def gen_descriptors(sgrp, fmt): enums = UniqueSeqTable() with fmt.indented( - 'const DESCRIPTORS: [Descriptor; {}] = [' + 'static DESCRIPTORS: [detail::Descriptor; {}] = [' .format(len(sgrp.settings)), '];'): for idx, setting in enumerate(sgrp.settings): setting.descriptor_index = idx - with fmt.indented('Descriptor {', '},'): + with fmt.indented('detail::Descriptor {', '},'): fmt.line('name: "{}",'.format(setting.name)) fmt.line('offset: {},'.format(setting.byte_offset)) if isinstance(setting, BoolSetting): fmt.line( - 'detail: Detail::Bool {{ bit: {} }},' + 'detail: detail::Detail::Bool {{ bit: {} }},' .format(setting.bit_offset)) elif isinstance(setting, NumSetting): - fmt.line('detail: Detail::Num,') + fmt.line('detail: detail::Detail::Num,') elif isinstance(setting, EnumSetting): offs = enums.add(setting.values) fmt.line( - 'detail: Detail::Enum {{ last: {}, enumerators: {} }},' + 'detail: detail::Detail::Enum ' + + '{{ last: {}, enumerators: {} }},' .format(len(setting.values)-1, offs)) else: raise AssertionError("Unknown setting kind") with fmt.indented( - 'const ENUMERATORS: [&\'static str; {}] = [' + 'static ENUMERATORS: [&\'static str; {}] = [' .format(len(enums.table)), '];'): for txt in enums.table: @@ -150,66 +136,46 @@ def gen_descriptors(sgrp, fmt): return constant_hash.simple_hash(s.name) hash_table = constant_hash.compute_quadratic(sgrp.settings, hash_setting) - if len(sgrp.settings) > 0xffff: - ty = 'u32' - elif len(sgrp.settings) > 0xff: - ty = 'u16' - else: - ty = 'u8' - with fmt.indented( - 'const HASH_TABLE: [{}; {}] = [' - .format(ty, len(hash_table)), + 'static HASH_TABLE: [u16; {}] = [' + .format(len(hash_table)), '];'): for h in hash_table: if h is None: - fmt.line('{},'.format(len(sgrp.settings))) + fmt.line('0xffff,') else: fmt.line('{},'.format(h.descriptor_index)) -def gen_stringwise(sgrp, fmt): +def gen_template(sgrp, byte_size, fmt): """ - Generate the Stringwise implementation and supporting tables. + Emit a Template constant. """ + v = [0] * byte_size + for setting in sgrp.settings: + v[setting.byte_offset] |= setting.default_byte() - with fmt.indented('impl Stringwise for Settings {', '}'): - with fmt.indented( - 'fn lookup(&self, name: &str)' + - '-> Result<(usize, Detail)> {', - '}'): - fmt.line('use simple_hash::simple_hash;') - fmt.line('let tlen = HASH_TABLE.len();') - fmt.line('assert!(tlen.is_power_of_two());') - fmt.line('let mut idx = simple_hash(name) as usize;') - fmt.line('let mut step: usize = 0;') - with fmt.indented('loop {', '}'): - fmt.line('idx = idx % tlen;') - fmt.line('let entry = HASH_TABLE[idx] as usize;') - with fmt.indented('if entry >= DESCRIPTORS.len() {', '}'): - fmt.line('return Err(Error::BadName)') - with fmt.indented('if DESCRIPTORS[entry].name == name {', '}'): - fmt.line( - 'return Ok((DESCRIPTORS[entry].offset as usize, ' + - 'DESCRIPTORS[entry].detail))') - fmt.line('step += 1;') - fmt.line('assert!(step < tlen);') - fmt.line('idx += step;') + with fmt.indented( + 'static TEMPLATE: detail::Template = detail::Template {', '};'): + fmt.line('name: "{}",'.format(sgrp.name)) + fmt.line('descriptors: &DESCRIPTORS,') + fmt.line('enumerators: &ENUMERATORS,') + fmt.line('hash_table: &HASH_TABLE,') + vs = ', '.join('{:#04x}'.format(x) for x in v) + fmt.line('defaults: &[ {} ],'.format(vs)) - with fmt.indented( - 'fn enumerator(&self, enums: u16, value: u8)' + - '-> &\'static str {', - '}'): - fmt.line('ENUMERATORS[enums as usize + value as usize]') + fmt.doc_comment( + 'Create a `settings::Builder` for the {} settings group.' + .format(sgrp.name)) + with fmt.indented('pub fn builder() -> Builder {', '}'): + fmt.line('Builder::new(&TEMPLATE)') - with fmt.indented('fn raw_bytes_mut(&mut self) -> &mut [u8] {', '}'): - fmt.line('&mut self.bytes') def gen_display(sgrp, fmt): """ - Generate the Display impl for Settings. + Generate the Display impl for Flags. """ - with fmt.indented('impl fmt::Display for Settings {', '}'): + with fmt.indented('impl fmt::Display for Flags {', '}'): with fmt.indented( 'fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {', '}'): @@ -217,27 +183,43 @@ def gen_display(sgrp, fmt): with fmt.indented('for d in &DESCRIPTORS {', '}'): fmt.line('try!(write!(f, "{} = ", d.name));') fmt.line( - 'try!(self.format_toml_value(d.detail,' + + 'try!(TEMPLATE.format_toml_value(d.detail,' + 'self.bytes[d.offset as usize], f));') fmt.line('try!(writeln!(f, ""));') fmt.line('Ok(())') +def gen_constructor(sgrp, byte_size, parent, fmt): + """ + Generate a Flags constructor. + """ + + with fmt.indented('impl Flags {', '}'): + with fmt.indented('pub fn new(builder: Builder) -> Flags {', '}'): + fmt.line('let bvec = builder.finish("{}");'.format(sgrp.name)) + fmt.line('let mut bytes = [0; {}];'.format(byte_size)) + fmt.line('assert_eq!(bytes.len(), bvec.len());') + with fmt.indented( + 'for (i, b) in bvec.into_iter().enumerate() {', '}'): + fmt.line('bytes[i] = b;') + fmt.line('Flags { bytes: bytes }') + + def gen_group(sgrp, fmt): """ - Generate a Settings struct representing `sgrp`. + Generate a Flags struct representing `sgrp`. """ byte_size = layout_group(sgrp) - fmt.doc_comment('Settings group `{}`.'.format(sgrp.name)) - with fmt.indented('pub struct Settings {', '}'): + fmt.doc_comment('Flags group `{}`.'.format(sgrp.name)) + with fmt.indented('pub struct Flags {', '}'): fmt.line('bytes: [u8; {}],'.format(byte_size)) + gen_constructor(sgrp, byte_size, None, fmt) gen_enum_types(sgrp, fmt) gen_getters(sgrp, fmt) - gen_default(sgrp, byte_size, fmt) gen_descriptors(sgrp, fmt) - gen_stringwise(sgrp, fmt) + gen_template(sgrp, byte_size, fmt) gen_display(sgrp, fmt)