Files
wasmtime/lib/cretonne/meta/gen_settings.py
Dan Gohman 30f8daa9d6 Replace assert! with debug_assert! in production code paths.
This allows the assertions to be disabled in release builds, so that
the code is faster and smaller, at the expense of not performing the
checks. Assertions can be re-enabled in release builds with the
debug-assertions flag in Cargo.toml, as the top-level Cargo.toml
file does.
2018-03-12 12:38:30 -07:00

314 lines
12 KiB
Python

"""
Generate sources with settings.
"""
from __future__ import absolute_import
import srcgen
from unique_table import UniqueSeqTable
import constant_hash
from cdsl import camel_case
from cdsl.settings import BoolSetting, NumSetting, EnumSetting
from base import settings
try:
from typing import Sequence, Set, Tuple, List, Union, TYPE_CHECKING # noqa
if TYPE_CHECKING:
from cdsl.isa import TargetISA # noqa
from cdsl.settings import Setting, Preset, SettingGroup # noqa
from cdsl.predicates import Predicate, PredContext # noqa
except ImportError:
pass
def gen_enum_types(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Emit enum types for any enum settings.
"""
for setting in sgrp.settings:
if not isinstance(setting, EnumSetting):
continue
ty = camel_case(setting.name)
fmt.doc_comment('Values for {}.'.format(setting))
fmt.line('#[derive(Debug, PartialEq, Eq)]')
with fmt.indented('pub enum {} {{'.format(ty), '}'):
for v in setting.values:
fmt.doc_comment('`{}`.'.format(v))
fmt.line(camel_case(v) + ',')
def gen_getter(setting, sgrp, fmt):
# type: (Setting, SettingGroup, srcgen.Formatter) -> None
"""
Emit a getter function for `setting`.
"""
fmt.doc_comment(setting.__doc__)
if isinstance(setting, BoolSetting):
proto = 'pub fn {}(&self) -> bool'.format(setting.name)
with fmt.indented(proto + ' {', '}'):
fmt.line(
'self.numbered_predicate({})'
.format(sgrp.predicate_number[setting]))
elif isinstance(setting, NumSetting):
proto = 'pub fn {}(&self) -> u8'.format(setting.name)
with fmt.indented(proto + ' {', '}'):
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:
raise AssertionError("Unknown setting kind")
def gen_pred_getter(name, pred, sgrp, fmt):
# type: (str, Predicate, SettingGroup, srcgen.Formatter) -> None
"""
Emit a getter for a named pre-computed predicate.
"""
fmt.doc_comment('Computed predicate `{}`.'.format(pred.rust_predicate(0)))
proto = 'pub fn {}(&self) -> bool'.format(name)
with fmt.indented(proto + ' {', '}'):
fmt.line(
'self.numbered_predicate({})'
.format(sgrp.predicate_number[pred]))
def gen_getters(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Emit getter functions for all the settings in fmt.
"""
fmt.doc_comment("User-defined settings.")
fmt.line('#[allow(dead_code)]')
with fmt.indented('impl Flags {', '}'):
fmt.doc_comment('Get a view of the boolean predicates.')
with fmt.indented(
'pub fn predicate_view(&self) -> ::settings::PredicateView {',
'}'):
fmt.format(
'::settings::PredicateView::new(&self.bytes[{}..])',
sgrp.boolean_offset)
if sgrp.settings:
fmt.doc_comment('Dynamic numbered predicate getter.')
with fmt.indented(
'fn numbered_predicate(&self, p: usize) -> bool {', '}'):
fmt.line(
'self.bytes[{} + p / 8] & (1 << (p % 8)) != 0'
.format(sgrp.boolean_offset))
for setting in sgrp.settings:
gen_getter(setting, sgrp, fmt)
for name, pred in sgrp.named_predicates.items():
gen_pred_getter(name, pred, sgrp, fmt)
def gen_descriptors(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Generate the DESCRIPTORS, ENUMERATORS, and PRESETS tables.
"""
enums = UniqueSeqTable()
with fmt.indented(
'static DESCRIPTORS: [detail::Descriptor; {}] = ['
.format(len(sgrp.settings) + len(sgrp.presets)),
'];'):
for idx, setting in enumerate(sgrp.settings):
setting.descriptor_index = idx
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::Detail::Bool {{ bit: {} }},'
.format(setting.bit_offset))
elif isinstance(setting, NumSetting):
fmt.line('detail: detail::Detail::Num,')
elif isinstance(setting, EnumSetting):
offs = enums.add(setting.values)
fmt.line(
'detail: detail::Detail::Enum ' +
'{{ last: {}, enumerators: {} }},'
.format(len(setting.values)-1, offs))
else:
raise AssertionError("Unknown setting kind")
for idx, preset in enumerate(sgrp.presets):
preset.descriptor_index = len(sgrp.settings) + idx
with fmt.indented('detail::Descriptor {', '},'):
fmt.line('name: "{}",'.format(preset.name))
fmt.line('offset: {},'.format(idx * sgrp.settings_size))
fmt.line('detail: detail::Detail::Preset,')
with fmt.indented(
'static ENUMERATORS: [&str; {}] = ['
.format(len(enums.table)),
'];'):
for txt in enums.table:
fmt.line('"{}",'.format(txt))
def hash_setting(s):
# type: (Union[Setting, Preset]) -> int
return constant_hash.simple_hash(s.name)
hash_elems = [] # type: List[Union[Setting, Preset]]
hash_elems.extend(sgrp.settings)
hash_elems.extend(sgrp.presets)
hash_table = constant_hash.compute_quadratic(hash_elems, hash_setting)
with fmt.indented(
'static HASH_TABLE: [u16; {}] = ['
.format(len(hash_table)),
'];'):
for h in hash_table:
if h is None:
fmt.line('0xffff,')
else:
fmt.line('{},'.format(h.descriptor_index))
with fmt.indented(
'static PRESETS: [(u8, u8); {}] = ['
.format(len(sgrp.presets) * sgrp.settings_size),
'];'):
for preset in sgrp.presets:
fmt.comment(preset.name)
for mask, value in preset.layout():
fmt.format('(0b{:08b}, 0b{:08b}),', mask, value)
def gen_template(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Emit a Template constant.
"""
v = [0] * sgrp.settings_size
for setting in sgrp.settings:
v[setting.byte_offset] |= setting.default_byte()
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))
fmt.line('presets: &PRESETS,')
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)')
def gen_display(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Generate the Display impl for Flags.
"""
with fmt.indented('impl fmt::Display for Flags {', '}'):
with fmt.indented(
'fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {',
'}'):
fmt.line('writeln!(f, "[{}]")?;'.format(sgrp.name))
with fmt.indented('for d in &DESCRIPTORS {', '}'):
with fmt.indented('if !d.detail.is_preset() {', '}'):
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('Ok(())')
def gen_constructor(sgrp, parent, fmt):
# type: (SettingGroup, PredContext, srcgen.Formatter) -> None
"""
Generate a Flags constructor.
"""
with fmt.indented('impl Flags {', '}'):
args = 'builder: &Builder'
if sgrp.parent:
p = sgrp.parent
args = '{}: &{}::Flags, {}'.format(p.name, p.qual_mod, args)
fmt.doc_comment('Create flags {} settings group.'.format(sgrp.name))
fmt.line('#[allow(unused_variables)]')
with fmt.indented(
'pub fn new({}) -> Flags {{'.format(args), '}'):
fmt.line('let bvec = builder.state_for("{}");'.format(sgrp.name))
fmt.line('let mut bytes = [0; {}];'.format(sgrp.byte_size()))
fmt.line('debug_assert_eq!(bvec.len(), {});'.format(sgrp.settings_size))
with fmt.indented(
'for (i, b) in bvec.iter().enumerate() {', '}'):
fmt.line('bytes[i] = *b;')
# Stop here without predicates.
if len(sgrp.predicate_number) == sgrp.boolean_settings:
fmt.line('Flags { bytes: bytes }')
return
# Now compute the predicates.
fmt.line(
'let mut {} = Flags {{ bytes: bytes }};'
.format(sgrp.name))
for pred, number in sgrp.predicate_number.items():
# Don't compute our own settings.
if number < sgrp.boolean_settings:
continue
fmt.comment('Precompute #{}.'.format(number))
with fmt.indented(
'if {} {{'.format(pred.rust_predicate(0)),
'}'):
fmt.line(
'{}.bytes[{}] |= 1 << {};'
.format(
sgrp.name,
sgrp.boolean_offset + number // 8,
number % 8))
fmt.line(sgrp.name)
def gen_group(sgrp, fmt):
# type: (SettingGroup, srcgen.Formatter) -> None
"""
Generate a Flags struct representing `sgrp`.
"""
fmt.line('#[derive(Clone)]')
fmt.doc_comment('Flags group `{}`.'.format(sgrp.name))
with fmt.indented('pub struct Flags {', '}'):
fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size()))
gen_constructor(sgrp, None, fmt)
gen_enum_types(sgrp, fmt)
gen_getters(sgrp, fmt)
gen_descriptors(sgrp, fmt)
gen_template(sgrp, fmt)
gen_display(sgrp, fmt)
def generate(isas, out_dir):
# type: (Sequence[TargetISA], str) -> None
# Generate shared settings.
fmt = srcgen.Formatter()
settings.group.qual_mod = 'settings'
gen_group(settings.group, fmt)
fmt.update_file('settings.rs', out_dir)
# Generate ISA-specific settings.
for isa in isas:
isa.settings.qual_mod = 'isa::{}::settings'.format(
isa.settings.name)
fmt = srcgen.Formatter()
gen_group(isa.settings, fmt)
fmt.update_file('settings-{}.rs'.format(isa.name), out_dir)