Add support for setting presets.
Fixes #11. Presets are groups of settings and values applied at once. This is used as a shorthand in test files, so for example "isa intel nehalem" enables all of the CPUID bits that the Nehalem micro-architecture provides.
This commit is contained in:
@@ -4,7 +4,8 @@ from collections import OrderedDict
|
||||
from .predicates import Predicate
|
||||
|
||||
try:
|
||||
from typing import Set, List, Dict, Any, TYPE_CHECKING # noqa
|
||||
from typing import Tuple, Set, List, Dict, Any, Union, TYPE_CHECKING # noqa
|
||||
BoolOrPresetOrDict = Union['BoolSetting', 'Preset', Dict['Setting', Any]]
|
||||
if TYPE_CHECKING:
|
||||
from .predicates import PredLeaf, PredNode # noqa
|
||||
except ImportError:
|
||||
@@ -47,6 +48,17 @@ class Setting(object):
|
||||
# type: () -> int
|
||||
raise NotImplementedError("default_byte is an abstract method")
|
||||
|
||||
def byte_for_value(self, value):
|
||||
# type: (Any) -> int
|
||||
"""Get the setting byte value that corresponds to `value`"""
|
||||
raise NotImplementedError("byte_for_value is an abstract method")
|
||||
|
||||
def byte_mask(self):
|
||||
# type: () -> int
|
||||
"""Get a mask of bits in our byte that are relevant to this setting."""
|
||||
# Only BoolSetting has a different mask.
|
||||
return 0xff
|
||||
|
||||
|
||||
class BoolSetting(Setting):
|
||||
"""
|
||||
@@ -73,6 +85,17 @@ class BoolSetting(Setting):
|
||||
else:
|
||||
return 0
|
||||
|
||||
def byte_for_value(self, value):
|
||||
# type: (Any) -> int
|
||||
if value:
|
||||
return 1 << self.bit_offset
|
||||
else:
|
||||
return 0
|
||||
|
||||
def byte_mask(self):
|
||||
# type: () -> int
|
||||
return 1 << self.bit_offset
|
||||
|
||||
def predicate_leafs(self, leafs):
|
||||
# type: (Set[PredLeaf]) -> None
|
||||
leafs.add(self)
|
||||
@@ -107,6 +130,12 @@ class NumSetting(Setting):
|
||||
# type: () -> int
|
||||
return self.default
|
||||
|
||||
def byte_for_value(self, value):
|
||||
# type: (Any) -> int
|
||||
assert isinstance(value, int), "NumSetting must be set to an int"
|
||||
assert value >= 0 and value <= 255
|
||||
return value
|
||||
|
||||
|
||||
class EnumSetting(Setting):
|
||||
"""
|
||||
@@ -129,6 +158,10 @@ class EnumSetting(Setting):
|
||||
# type: () -> int
|
||||
return 0
|
||||
|
||||
def byte_for_value(self, value):
|
||||
# type: (Any) -> int
|
||||
return self.values.index(value)
|
||||
|
||||
|
||||
class SettingGroup(object):
|
||||
"""
|
||||
@@ -160,6 +193,7 @@ class SettingGroup(object):
|
||||
# - Added parent predicates that are replicated in this group.
|
||||
# Maps predicate -> number.
|
||||
self.predicate_number = OrderedDict() # type: OrderedDict[PredNode, int] # noqa
|
||||
self.presets = [] # type: List[Preset]
|
||||
|
||||
# Fully qualified Rust module name. See gen_settings.py.
|
||||
self.qual_mod = None # type: str
|
||||
@@ -199,6 +233,10 @@ class SettingGroup(object):
|
||||
assert obj.name is None
|
||||
obj.name = name
|
||||
self.named_predicates.append(obj)
|
||||
if isinstance(obj, Preset):
|
||||
assert obj.name is None, obj.name
|
||||
obj.name = name
|
||||
|
||||
self.layout()
|
||||
|
||||
@staticmethod
|
||||
@@ -209,6 +247,14 @@ class SettingGroup(object):
|
||||
g.settings.append(setting)
|
||||
return g
|
||||
|
||||
@staticmethod
|
||||
def append_preset(preset):
|
||||
# type: (Preset) -> SettingGroup
|
||||
g = SettingGroup._current
|
||||
assert g, "Open a setting group before defining presets."
|
||||
g.presets.append(preset)
|
||||
return g
|
||||
|
||||
def number_predicate(self, pred):
|
||||
# type: (PredNode) -> int
|
||||
"""
|
||||
@@ -295,3 +341,65 @@ class SettingGroup(object):
|
||||
predcate bits rounded up to a whole number of bytes.
|
||||
"""
|
||||
return self.boolean_offset + (len(self.predicate_number) + 7) // 8
|
||||
|
||||
|
||||
class Preset(object):
|
||||
"""
|
||||
A collection of setting values that are applied at once.
|
||||
|
||||
A `Preset` represents a shorthand notation for applying a number of
|
||||
settings at once. Example:
|
||||
|
||||
nehalem = Preset(has_sse41, has_cmov, has_avx=0)
|
||||
|
||||
Enabling the `nehalem` setting is equivalent to enabling `has_sse41` and
|
||||
`has_cmov` while disabling the `has_avx` setting.
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
# type: (*BoolOrPresetOrDict) -> None
|
||||
self.name = None # type: str # Assigned later by `SettingGroup`.
|
||||
# Each tuple provides the value for a setting.
|
||||
self.values = list() # type: List[Tuple[Setting, Any]]
|
||||
|
||||
for arg in args:
|
||||
if isinstance(arg, Preset):
|
||||
# Any presets in args are immediately expanded.
|
||||
self.values.extend(arg.values)
|
||||
elif isinstance(arg, dict):
|
||||
# A dictionary of key: value pairs.
|
||||
self.values.extend(arg.items())
|
||||
else:
|
||||
# A BoolSetting to enable.
|
||||
assert isinstance(arg, BoolSetting)
|
||||
self.values.append((arg, True))
|
||||
|
||||
self.group = SettingGroup.append_preset(self)
|
||||
# Index into the generated DESCRIPTORS table.
|
||||
self.descriptor_index = None # type: int
|
||||
|
||||
def layout(self):
|
||||
# type: () -> List[Tuple[int, int]]
|
||||
"""
|
||||
Compute a list of (mask, byte) pairs that incorporate all values in
|
||||
this preset.
|
||||
|
||||
The list will have an entry for each setting byte in the settings
|
||||
group.
|
||||
"""
|
||||
l = [(0, 0)] * self.group.settings_size
|
||||
|
||||
# Apply setting values in order.
|
||||
for s, v in self.values:
|
||||
ofs = s.byte_offset
|
||||
s_mask = s.byte_mask()
|
||||
s_val = s.byte_for_value(v)
|
||||
assert (s_val & ~s_mask) == 0
|
||||
l_mask, l_val = l[ofs]
|
||||
# Accumulated mask of modified bits.
|
||||
l_mask |= s_mask
|
||||
# Overwrite the relevant bits with the new value.
|
||||
l_val = (l_val & ~s_mask) | s_val
|
||||
l[ofs] = (l_mask, l_val)
|
||||
|
||||
return l
|
||||
|
||||
@@ -10,10 +10,10 @@ from cdsl.settings import BoolSetting, NumSetting, EnumSetting
|
||||
from base import settings
|
||||
|
||||
try:
|
||||
from typing import Sequence, Set, Tuple, List, TYPE_CHECKING # noqa
|
||||
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, SettingGroup # noqa
|
||||
from cdsl.settings import Setting, Preset, SettingGroup # noqa
|
||||
from cdsl.predicates import Predicate, PredContext # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -106,14 +106,14 @@ def gen_getters(sgrp, fmt):
|
||||
def gen_descriptors(sgrp, fmt):
|
||||
# type: (SettingGroup, srcgen.Formatter) -> None
|
||||
"""
|
||||
Generate the DESCRIPTORS and ENUMERATORS tables.
|
||||
Generate the DESCRIPTORS, ENUMERATORS, and PRESETS tables.
|
||||
"""
|
||||
|
||||
enums = UniqueSeqTable()
|
||||
|
||||
with fmt.indented(
|
||||
'static DESCRIPTORS: [detail::Descriptor; {}] = ['
|
||||
.format(len(sgrp.settings)),
|
||||
.format(len(sgrp.settings) + len(sgrp.presets)),
|
||||
'];'):
|
||||
for idx, setting in enumerate(sgrp.settings):
|
||||
setting.descriptor_index = idx
|
||||
@@ -135,6 +135,13 @@ def gen_descriptors(sgrp, fmt):
|
||||
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)),
|
||||
@@ -143,10 +150,13 @@ def gen_descriptors(sgrp, fmt):
|
||||
fmt.line('"{}",'.format(txt))
|
||||
|
||||
def hash_setting(s):
|
||||
# type: (Setting) -> int
|
||||
# type: (Union[Setting, Preset]) -> int
|
||||
return constant_hash.simple_hash(s.name)
|
||||
|
||||
hash_table = constant_hash.compute_quadratic(sgrp.settings, hash_setting)
|
||||
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)),
|
||||
@@ -157,6 +167,15 @@ def gen_descriptors(sgrp, fmt):
|
||||
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
|
||||
@@ -175,6 +194,7 @@ def gen_template(sgrp, fmt):
|
||||
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.'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Intel settings.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.settings import SettingGroup, BoolSetting
|
||||
from cdsl.settings import SettingGroup, BoolSetting, Preset
|
||||
from cdsl.predicates import And
|
||||
import base.settings as shared
|
||||
from .defs import ISA
|
||||
@@ -38,4 +38,10 @@ use_popcnt = And(has_popcnt, has_sse42)
|
||||
use_bmi1 = And(has_bmi1)
|
||||
use_lzcnt = And(has_lzcnt)
|
||||
|
||||
# Presets corresponding to Intel CPUs.
|
||||
|
||||
nehalem = Preset(
|
||||
has_sse2, has_sse3, has_ssse3, has_sse41, has_sse42, has_popcnt)
|
||||
haswell = Preset(nehalem, has_bmi1, has_lzcnt)
|
||||
|
||||
ISA.settings.close(globals())
|
||||
|
||||
Reference in New Issue
Block a user