Add support for enumerated settings.
The EnumSetting objects can take one of 256 named values.
This commit is contained in:
@@ -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())
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user