Files
wasmtime/lib/codegen/meta/gen_settings.py
Dan Gohman c5b15c2396 Refactor calling convention settings. (#304)
Add a calling-convention setting to the `Flags` used as part of the
`TargetIsa`. This allows Cretonne code that generates calls to use the
correct convention, such as when emitting libcalls during legalization
or when the wasm frontend is decoding functions. This setting can be
overridden per-function.

This also adds "fast", "cold", and "fastcall" conventions, with "fast"
as the new default. Note that "fast" and "cold" are not intended to be
ABI-compatible across Cretonne versions.

This will also ensure Windows users will get an `unimplemented!` rather
than silent calling-convention mismatches, which reflects the fact that
Windows calling conventions are not yet implemented.

This also renames SpiderWASM, which isn't camel-case, to Baldrdash,
which is, and which is also a more relevant name.
2018-04-22 21:35:18 -07:00

342 lines
13 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_to_and_from_str(ty, values, fmt):
# type: (str, Tuple[str, ...], srcgen.Formatter) -> None
"""
Emit Display and FromStr implementations for enum settings.
"""
with fmt.indented('impl fmt::Display for {} {{'.format(ty), '}'):
with fmt.indented(
'fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {',
'}'):
with fmt.indented('f.write_str(match *self {', '})'):
for v in values:
fmt.line('{}::{} => "{}",'
.format(ty, camel_case(v), v))
with fmt.indented('impl str::FromStr for {} {{'.format(ty), '}'):
fmt.line('type Err = ();')
with fmt.indented(
'fn from_str(s: &str) -> result::Result<Self, Self::Err> {',
'}'):
with fmt.indented('match s {', '}'):
for v in values:
fmt.line('"{}" => Ok({}::{}),'
.format(v, ty, camel_case(v)))
fmt.line('_ => Err(()),')
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, Copy, Clone, PartialEq, Eq, Hash)]')
with fmt.indented('pub enum {} {{'.format(ty), '}'):
for v in setting.values:
fmt.doc_comment('`{}`.'.format(v))
fmt.line(camel_case(v) + ',')
gen_to_and_from_str(ty, setting.values, fmt)
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 + ' {', '}'):
m = srcgen.Match('self.bytes[{}]'.format(setting.byte_offset))
for i, v in enumerate(setting.values):
m.arm(str(i), [], '{}::{}'.format(ty, camel_case(v)))
m.arm('_', [], 'panic!("Invalid enum value")')
fmt.match(m)
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({}) -> Self {{'.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('Self { bytes }')
return
# Now compute the predicates.
fmt.line(
'let mut {} = Self {{ 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)