Scaffolding for defining settings.
Settings can be defined globally or per-ISA. They are available to code through a generated Settings struct with accessor methods per setting.
This commit is contained in:
@@ -25,6 +25,45 @@ The main driver for this source code generation process is the
|
||||
:file:`meta/build.py` script which is invoked as part of the build process if
|
||||
anything in the :file:`meta` directory has changed since the last build.
|
||||
|
||||
|
||||
Settings
|
||||
========
|
||||
|
||||
Settings are used by the environment embedding Cretonne to control the details
|
||||
of code generation. Each setting is defined in the meta language so a compact
|
||||
and consistent Rust representation can be generated. Shared settings are defined
|
||||
in the :mod:`cretonne.settings` module. Some settings are specific to a target
|
||||
ISA, and defined in a `settings` module under the appropriate :file:`meta/isa/*`
|
||||
directory.
|
||||
|
||||
Settings can take boolean on/off values, small numbers, or explicitly enumerated
|
||||
symbolic values. Each type is represented by a sub-class of :class:`Setting`:
|
||||
|
||||
.. inheritance-diagram:: Setting BoolSetting NumSetting EnumSetting
|
||||
:parts: 1
|
||||
|
||||
.. autoclass:: Setting
|
||||
.. autoclass:: BoolSetting
|
||||
.. autoclass:: NumSetting
|
||||
.. autoclass:: EnumSetting
|
||||
|
||||
All settings must belong to a *group*, represented by a :class:`SettingGroup`
|
||||
object.
|
||||
|
||||
.. autoclass:: SettingGroup
|
||||
|
||||
Normally, a setting group corresponds to all settings defined in a module. Such
|
||||
a module looks like this::
|
||||
|
||||
group = SettingGroup('example')
|
||||
|
||||
foo = BoolSetting('use the foo')
|
||||
bar = BoolSetting('enable bars', True)
|
||||
opt = EnumSetting('optimization level', 'Debug', 'Release')
|
||||
|
||||
group.close(globals())
|
||||
|
||||
|
||||
Instruction descriptions
|
||||
========================
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import argparse
|
||||
import isa
|
||||
import gen_instr
|
||||
import gen_settings
|
||||
import gen_build_deps
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate sources for Cretonne.')
|
||||
@@ -16,4 +17,5 @@ out_dir = args.out_dir
|
||||
isas = isa.all_isas()
|
||||
|
||||
gen_instr.generate(isas, out_dir)
|
||||
gen_settings.generate(isas, out_dir)
|
||||
gen_build_deps.generate()
|
||||
|
||||
@@ -18,6 +18,146 @@ def camel_case(s):
|
||||
return camel_re.sub(lambda m: m.group(2).upper(), s)
|
||||
|
||||
|
||||
class Setting(object):
|
||||
"""
|
||||
A named setting variable that can be configured externally to Cretonne.
|
||||
|
||||
Settings are normally not named when they are created. They get their name
|
||||
from the `extract_names` method.
|
||||
"""
|
||||
|
||||
def __init__(self, doc):
|
||||
self.name = None # Assigned later by `extract_names()`.
|
||||
self.__doc__ = doc
|
||||
# Offset of byte in settings vector containing this setting.
|
||||
self.byte_offset = None
|
||||
SettingGroup.append(self)
|
||||
|
||||
@staticmethod
|
||||
def extract_names(globs):
|
||||
"""
|
||||
Given a dict mapping name -> object as returned by `globals()`, find
|
||||
all the Setting objects and set their name from the dict key. This is
|
||||
used to name a bunch of global variables in a module.
|
||||
"""
|
||||
for name, obj in globs.iteritems():
|
||||
if isinstance(obj, Setting):
|
||||
assert obj.name is None
|
||||
obj.name = name
|
||||
|
||||
|
||||
class BoolSetting(Setting):
|
||||
"""
|
||||
A named setting with a boolean on/off value.
|
||||
|
||||
:param doc: Documentation string.
|
||||
:param default: The default value of this setting.
|
||||
"""
|
||||
|
||||
def __init__(self, doc, default=False):
|
||||
super(BoolSetting, self).__init__(doc)
|
||||
self.default = default
|
||||
|
||||
def default_byte(self):
|
||||
"""
|
||||
Get the default value of this setting, as a byte that can be bitwise
|
||||
or'ed with the other booleans sharing the same byte.
|
||||
"""
|
||||
if self.default:
|
||||
return 1 << self.bit_offset
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
class NumSetting(Setting):
|
||||
"""
|
||||
A named setting with an integral value in the range 0--255.
|
||||
|
||||
:param doc: Documentation string.
|
||||
:param default: The default value of this setting.
|
||||
"""
|
||||
|
||||
def __init__(self, doc, default=0):
|
||||
super(NumSetting, self).__init__(doc)
|
||||
assert default == int(default)
|
||||
assert default >= 0 and default <= 255
|
||||
self.default = default
|
||||
|
||||
def default_byte(self):
|
||||
return self.default
|
||||
|
||||
|
||||
class EnumSetting(Setting):
|
||||
"""
|
||||
A named setting with an enumerated set of possible values.
|
||||
|
||||
The default value is always the first enumerator.
|
||||
|
||||
:param doc: Documentation string.
|
||||
:param args: Tuple of unique strings representing the possible values.
|
||||
"""
|
||||
|
||||
def __init__(self, doc, *args):
|
||||
super(EnumSetting, self).__init__(doc)
|
||||
assert len(args) > 0, "EnumSetting must have at least one value"
|
||||
self.values = tuple(str(x) for x in args)
|
||||
self.default = self.values[0]
|
||||
|
||||
def default_byte(self):
|
||||
return 0
|
||||
|
||||
|
||||
class SettingGroup(object):
|
||||
"""
|
||||
A group of settings.
|
||||
|
||||
Whenever a :class:`Setting` object is created, it is added to the currently
|
||||
open group. A setting group must be closed explicitly before another can be
|
||||
opened.
|
||||
|
||||
:param name: Short mnemonic name for setting group.
|
||||
"""
|
||||
|
||||
# The currently open setting group.
|
||||
_current = None
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.settings = []
|
||||
self.open()
|
||||
|
||||
def open(self):
|
||||
"""
|
||||
Open this setting group such that future new settings are added to this
|
||||
group.
|
||||
"""
|
||||
assert SettingGroup._current is None, (
|
||||
"Can't open {} since {} is already open"
|
||||
.format(self, SettingGroup._current))
|
||||
SettingGroup._current = self
|
||||
|
||||
def close(self, globs=None):
|
||||
"""
|
||||
Close this setting group. This function must be called before opening
|
||||
another setting group.
|
||||
|
||||
:param globs: Pass in `globals()` to run `extract_names` on all
|
||||
settings defined in the module.
|
||||
"""
|
||||
assert SettingGroup._current is self, (
|
||||
"Can't close {}, the open setting group is {}"
|
||||
.format(self, SettingGroup._current))
|
||||
SettingGroup._current = None
|
||||
if globs:
|
||||
Setting.extract_names(globs)
|
||||
|
||||
@staticmethod
|
||||
def append(setting):
|
||||
assert SettingGroup._current, \
|
||||
"Open a setting group before defining settings."
|
||||
SettingGroup._current.settings.append(setting)
|
||||
|
||||
|
||||
# Kinds of operands.
|
||||
#
|
||||
# Each instruction has an opcode and a number of operands. The opcode
|
||||
@@ -711,7 +851,9 @@ class BoundInstruction(object):
|
||||
`(inst, typevars)` pair.
|
||||
"""
|
||||
if len(self.typevars) < 1 + len(self.inst.other_typevars):
|
||||
unb = ', '.join(str(tv) for tv in self.inst.other_typevars[len(self.typevars) - 1:])
|
||||
unb = ', '.join(
|
||||
str(tv) for tv in
|
||||
self.inst.other_typevars[len(self.typevars) - 1:])
|
||||
raise AssertionError("Unbound typevar {} in {}".format(unb, self))
|
||||
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
|
||||
return (self.inst, self.typevars)
|
||||
|
||||
13
meta/cretonne/settings.py
Normal file
13
meta/cretonne/settings.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Cretonne shared settings.
|
||||
|
||||
This module defines settings are are relevant for all code generators.
|
||||
"""
|
||||
|
||||
from . import SettingGroup, BoolSetting
|
||||
|
||||
group = SettingGroup('shared')
|
||||
|
||||
enable_simd = BoolSetting("Enable the use of SIMD instructions", default=True)
|
||||
|
||||
group.close(globals())
|
||||
104
meta/gen_settings.py
Normal file
104
meta/gen_settings.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Generate sources with settings.
|
||||
"""
|
||||
|
||||
import srcgen
|
||||
from cretonne import BoolSetting, NumSetting, settings
|
||||
|
||||
|
||||
def layout_group(sgrp):
|
||||
"""
|
||||
Layout the settings in sgrp, assigning byte and bit offsets.
|
||||
|
||||
Return the next unused byte offset.
|
||||
"""
|
||||
# Byte offset where booleans are allocated.
|
||||
bool_byte = -1
|
||||
# Next available bit number in bool_byte.
|
||||
bool_bit = 10
|
||||
# Next available whole byte.
|
||||
next_byte = 0
|
||||
|
||||
for setting in sgrp.settings:
|
||||
if isinstance(setting, BoolSetting):
|
||||
# Allocate a bit from bool_byte.
|
||||
if bool_bit > 7:
|
||||
bool_byte = next_byte
|
||||
next_byte += 1
|
||||
bool_bit = 0
|
||||
setting.byte_offset = bool_byte
|
||||
setting.bit_offset = bool_bit
|
||||
bool_bit += 1
|
||||
else:
|
||||
# This is a numerical or enumerated setting. Allocate a single
|
||||
# byte.
|
||||
setting.byte_offset = next_byte
|
||||
next_byte += 1
|
||||
|
||||
return next_byte
|
||||
|
||||
|
||||
def gen_getter(setting, fmt):
|
||||
"""
|
||||
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.bytes[{}] & (1 << {})) != 0'.format(
|
||||
setting.byte_offset,
|
||||
setting.bit_offset))
|
||||
elif isinstance(setting, NumSetting):
|
||||
proto = 'pub fn {}(&self) -> u8'.format(setting.name)
|
||||
with fmt.indented(proto + ' {', '}'):
|
||||
fmt.line('self.bytes[{}]'.format(setting.byte_offset))
|
||||
else:
|
||||
raise AssertionError("Unknown setting kind")
|
||||
|
||||
|
||||
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 {', '}'):
|
||||
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_group(sgrp, fmt):
|
||||
"""
|
||||
Generate a Settings struct representing `sgrp`.
|
||||
"""
|
||||
byte_size = layout_group(sgrp)
|
||||
|
||||
fmt.doc_comment('Settings group `{}`.'.format(sgrp.name))
|
||||
with fmt.indented('pub struct Settings {', '}'):
|
||||
fmt.line('bytes: [u8; {}],'.format(byte_size))
|
||||
|
||||
gen_getters(sgrp, fmt)
|
||||
gen_default(sgrp, byte_size, fmt)
|
||||
|
||||
|
||||
def generate(isas, out_dir):
|
||||
fmt = srcgen.Formatter()
|
||||
gen_group(settings.group, fmt)
|
||||
fmt.update_file('settings.rs', out_dir)
|
||||
@@ -13,5 +13,6 @@ pub mod write;
|
||||
pub mod cfg;
|
||||
pub mod dominator_tree;
|
||||
pub mod entity_map;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]pub mod test_utils;
|
||||
|
||||
7
src/libcretonne/settings.rs
Normal file
7
src/libcretonne/settings.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Shared settings module.
|
||||
//!
|
||||
//! This module defines data structures to access the settings defined in the meta language.
|
||||
|
||||
// Include code generated by `meta/gen_settings.py`. This file contains a public `Settings` struct
|
||||
// with an impl for all of the settings defined in `meta/cretonne/settings.py`.
|
||||
include!(concat!(env!("OUT_DIR"), "/settings.rs"));
|
||||
Reference in New Issue
Block a user