Add support for enumerated settings.

The EnumSetting objects can take one of 256 named values.
This commit is contained in:
Jakob Stoklund Olesen
2016-08-09 14:12:36 -07:00
parent 07e851a222
commit 1ef72dd5ec
4 changed files with 127 additions and 16 deletions

View File

@@ -4,10 +4,22 @@ Cretonne shared settings.
This module defines settings are are relevant for all code generators. This module defines settings are are relevant for all code generators.
""" """
from . import SettingGroup, BoolSetting from . import SettingGroup, BoolSetting, EnumSetting
group = SettingGroup('shared') group = SettingGroup('shared')
enable_simd = BoolSetting("Enable the use of SIMD instructions", default=True) opt_level = EnumSetting(
"""
Optimization level:
- default: Very profitable optimizations enabled, none slow.
- best: Enable all optimizations
- fastest: Optimize for compile time by disabling most optimizations.
""",
'default', 'best', 'fastest')
enable_simd = BoolSetting(
"""Enable the use of SIMD instructions.""",
default=True)
group.close(globals()) group.close(globals())

View File

@@ -5,7 +5,7 @@ Generate sources with settings.
import srcgen import srcgen
from unique_table import UniqueSeqTable from unique_table import UniqueSeqTable
import constant_hash import constant_hash
from cretonne import BoolSetting, NumSetting, settings from cretonne import camel_case, BoolSetting, NumSetting, EnumSetting, settings
def layout_group(sgrp): def layout_group(sgrp):
@@ -40,11 +40,25 @@ def layout_group(sgrp):
return next_byte return next_byte
def gen_enum_types(sgrp, fmt):
"""
Emit enum types for any enum settings.
"""
for setting in sgrp.settings:
if not isinstance(setting, EnumSetting):
continue
ty = camel_case(setting.name)
fmt.line('#[derive(Debug, PartialEq, Eq)]')
fmt.line(
'pub enum {} {{ {} }}'
.format(ty, ", ".join(camel_case(v) for v in setting.values)))
def gen_getter(setting, fmt): def gen_getter(setting, fmt):
""" """
Emit a getter function for `setting`. Emit a getter function for `setting`.
""" """
fmt.doc_comment(setting.__doc__ + '.') fmt.doc_comment(setting.__doc__)
if isinstance(setting, BoolSetting): if isinstance(setting, BoolSetting):
proto = 'pub fn {}(&self) -> bool'.format(setting.name) proto = 'pub fn {}(&self) -> bool'.format(setting.name)
@@ -56,6 +70,15 @@ def gen_getter(setting, fmt):
proto = 'pub fn {}(&self) -> u8'.format(setting.name) proto = 'pub fn {}(&self) -> u8'.format(setting.name)
with fmt.indented(proto + ' {', '}'): with fmt.indented(proto + ' {', '}'):
fmt.line('self.bytes[{}]'.format(setting.byte_offset)) fmt.line('self.bytes[{}]'.format(setting.byte_offset))
elif isinstance(setting, EnumSetting):
ty = camel_case(setting.name)
proto = 'pub fn {}(&self) -> {}'.format(setting.name, ty)
with fmt.indented(proto + ' {', '}'):
with fmt.indented(
'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")')
else: else:
raise AssertionError("Unknown setting kind") raise AssertionError("Unknown setting kind")
@@ -108,6 +131,11 @@ def gen_descriptors(sgrp, fmt):
.format(setting.bit_offset)) .format(setting.bit_offset))
elif isinstance(setting, NumSetting): elif isinstance(setting, NumSetting):
fmt.line('detail: Detail::Num,') fmt.line('detail: Detail::Num,')
elif isinstance(setting, EnumSetting):
offs = enums.add(setting.values)
fmt.line(
'detail: Detail::Enum {{ last: {}, enumerators: {} }},'
.format(len(setting.values)-1, offs))
else: else:
raise AssertionError("Unknown setting kind") raise AssertionError("Unknown setting kind")
@@ -147,8 +175,8 @@ def gen_stringwise(sgrp, fmt):
with fmt.indented('impl Stringwise for Settings {', '}'): with fmt.indented('impl Stringwise for Settings {', '}'):
with fmt.indented( with fmt.indented(
'fn lookup_mut(&mut self, name: &str)' + 'fn lookup(&self, name: &str)' +
'-> Result<(Detail, &mut u8)> {', '-> Result<(usize, Detail)> {',
'}'): '}'):
fmt.line('use simple_hash::simple_hash;') fmt.line('use simple_hash::simple_hash;')
fmt.line('let tlen = HASH_TABLE.len();') fmt.line('let tlen = HASH_TABLE.len();')
@@ -162,9 +190,8 @@ def gen_stringwise(sgrp, fmt):
fmt.line('return Err(Error::BadName)') fmt.line('return Err(Error::BadName)')
with fmt.indented('if DESCRIPTORS[entry].name == name {', '}'): with fmt.indented('if DESCRIPTORS[entry].name == name {', '}'):
fmt.line( fmt.line(
'return Ok((DESCRIPTORS[entry].detail, ' + 'return Ok((DESCRIPTORS[entry].offset as usize, ' +
'&mut self.bytes[DESCRIPTORS[entry].offset ' + 'DESCRIPTORS[entry].detail))')
'as usize]))')
fmt.line('step += 1;') fmt.line('step += 1;')
fmt.line('assert!(step < tlen);') fmt.line('assert!(step < tlen);')
fmt.line('idx += step;') fmt.line('idx += step;')
@@ -175,6 +202,8 @@ def gen_stringwise(sgrp, fmt):
'}'): '}'):
fmt.line('ENUMERATORS[enums as usize + value as usize]') fmt.line('ENUMERATORS[enums as usize + value as usize]')
with fmt.indented('fn raw_bytes_mut(&mut self) -> &mut [u8] {', '}'):
fmt.line('&mut self.bytes')
def gen_display(sgrp, fmt): def gen_display(sgrp, fmt):
""" """
@@ -204,6 +233,7 @@ def gen_group(sgrp, fmt):
with fmt.indented('pub struct Settings {', '}'): with fmt.indented('pub struct Settings {', '}'):
fmt.line('bytes: [u8; {}],'.format(byte_size)) fmt.line('bytes: [u8; {}],'.format(byte_size))
gen_enum_types(sgrp, fmt)
gen_getters(sgrp, fmt) gen_getters(sgrp, fmt)
gen_default(sgrp, byte_size, fmt) gen_default(sgrp, byte_size, fmt)
gen_descriptors(sgrp, fmt) gen_descriptors(sgrp, fmt)

View File

@@ -8,6 +8,7 @@ source code.
import sys import sys
import os import os
import re
class Formatter(object): class Formatter(object):
@@ -108,8 +109,9 @@ class Formatter(object):
self.line('// ' + s) self.line('// ' + s)
def doc_comment(self, s): def doc_comment(self, s):
"""Add a documentation comment line.""" """Add a (multi-line) documentation comment."""
self.line('/// ' + s) s = re.sub('^', self.indent + '/// ', s, flags=re.M) + '\n'
self.lines.append(s)
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@@ -59,16 +59,26 @@ pub enum Error {
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
fn parse_bool_value(value: &str) -> Result<bool> {
match value {
"true" | "on" | "yes" | "1" => Ok(true),
"false" | "off" | "no" | "0" => Ok(false),
_ => Err(Error::BadValue),
}
}
/// Interface for working with a group of settings as strings. /// Interface for working with a group of settings as strings.
pub trait Stringwise { pub trait Stringwise {
/// Look up a setting by name, return the details of the setting along with a reference to the /// Look up a setting by name, return the byte offset and details of the setting.
/// byte holding the value of the setting. fn lookup(&self, name: &str) -> Result<(usize, Detail)>;
fn lookup_mut(&mut self, name: &str) -> Result<(Detail, &mut u8)>;
/// Get an enumerator string from the `Detail::enumerators` value and an offset. /// Get an enumerator string from the `Detail::enumerators` value and an offset.
fn enumerator(&self, enums: u16, value: u8) -> &'static str; fn enumerator(&self, enums: u16, value: u8) -> &'static str;
/// Format a setting value as a TOML string. This is mostly for use by the generateed `Display` /// 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. /// implementation.
fn format_toml_value(&self, detail: Detail, byte: u8, f: &mut fmt::Formatter) -> fmt::Result { fn format_toml_value(&self, detail: Detail, byte: u8, f: &mut fmt::Formatter) -> fmt::Result {
match detail { match detail {
@@ -86,9 +96,10 @@ pub trait Stringwise {
/// Set a boolean setting by name. /// Set a boolean setting by name.
fn set_bool(&mut self, name: &str, value: bool) -> Result<()> { fn set_bool(&mut self, name: &str, value: bool) -> Result<()> {
let (detail, byte) = try!(self.lookup_mut(name)); let (offset, detail) = try!(self.lookup(name));
if let Detail::Bool { bit } = detail { if let Detail::Bool { bit } = detail {
let mask = 1 << bit; let mask = 1 << bit;
let byte = &mut self.raw_bytes_mut()[offset];
if value { if value {
*byte |= mask; *byte |= mask;
} else { } else {
@@ -99,6 +110,43 @@ pub trait Stringwise {
Err(Error::BadType) 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);
}
}
}
}
Ok(())
}
} }
// 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 `Settings` struct
@@ -116,6 +164,7 @@ mod tests {
let s = Settings::default(); let s = Settings::default();
assert_eq!(s.to_string(), assert_eq!(s.to_string(),
"[shared]\n\ "[shared]\n\
opt_level = \"default\"\n\
enable_simd = true\n"); enable_simd = true\n");
} }
@@ -131,4 +180,22 @@ mod tests {
assert_eq!(s.set_bool("enable_simd", false), Ok(())); assert_eq!(s.set_bool("enable_simd", false), Ok(()));
assert_eq!(s.enable_simd(), false); assert_eq!(s.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);
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);
}
} }