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
|
: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.
|
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
|
Instruction descriptions
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import isa
|
import isa
|
||||||
import gen_instr
|
import gen_instr
|
||||||
|
import gen_settings
|
||||||
import gen_build_deps
|
import gen_build_deps
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Generate sources for Cretonne.')
|
parser = argparse.ArgumentParser(description='Generate sources for Cretonne.')
|
||||||
@@ -16,4 +17,5 @@ out_dir = args.out_dir
|
|||||||
isas = isa.all_isas()
|
isas = isa.all_isas()
|
||||||
|
|
||||||
gen_instr.generate(isas, out_dir)
|
gen_instr.generate(isas, out_dir)
|
||||||
|
gen_settings.generate(isas, out_dir)
|
||||||
gen_build_deps.generate()
|
gen_build_deps.generate()
|
||||||
|
|||||||
@@ -18,6 +18,146 @@ def camel_case(s):
|
|||||||
return camel_re.sub(lambda m: m.group(2).upper(), 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.
|
# Kinds of operands.
|
||||||
#
|
#
|
||||||
# Each instruction has an opcode and a number of operands. The opcode
|
# Each instruction has an opcode and a number of operands. The opcode
|
||||||
@@ -711,7 +851,9 @@ class BoundInstruction(object):
|
|||||||
`(inst, typevars)` pair.
|
`(inst, typevars)` pair.
|
||||||
"""
|
"""
|
||||||
if len(self.typevars) < 1 + len(self.inst.other_typevars):
|
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))
|
raise AssertionError("Unbound typevar {} in {}".format(unb, self))
|
||||||
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
|
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
|
||||||
return (self.inst, self.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 cfg;
|
||||||
pub mod dominator_tree;
|
pub mod dominator_tree;
|
||||||
pub mod entity_map;
|
pub mod entity_map;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
#[cfg(test)]pub mod test_utils;
|
#[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