Browse Source

Move byte-vector layout into SettingGroup.layout().

Move all the byte-sized settings to the front of the byte-vector, and add a
mechanism for assigning numbers to predicates that have no name as well as
predicates from the parent settings group.

This way, all the boolean predicates that are used by a target ISA appear as a
contiguous bit-vector that is a suffix of the settings byte-vector. This
bit-vector can then be indexed linearly when resolving ISA predicates on
encodings.

Add a numbered_predicate() method to the generated Flags structs that can read
a predicate by number dynamically.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
84b0a92326
  1. 116
      meta/cretonne/__init__.py
  2. 109
      meta/gen_settings.py

116
meta/cretonne/__init__.py

@ -8,7 +8,7 @@ from __future__ import absolute_import
import re
import math
import importlib
from collections import namedtuple
from collections import namedtuple, OrderedDict
from .predicates import And
@ -135,7 +135,17 @@ class SettingGroup(object):
self.name = name
self.parent = parent
self.settings = []
self.predicates = []
# Named predicates computed from settings in this group or its
# parents.
self.named_predicates = []
# All boolean predicates that can be accessed by number. This includes:
# - All boolean settings in this group.
# - All named predicates.
# - Added anonymous predicates, see `number_predicate()`.
# - Added parent predicates that are replicated in this group.
# Maps predicate -> number.
self.predicate_number = OrderedDict()
self.open()
def open(self):
@ -169,7 +179,8 @@ class SettingGroup(object):
if isinstance(obj, Predicate):
assert obj.name is None
obj.name = name
self.predicates.append(obj)
self.named_predicates.append(obj)
self.layout()
@staticmethod
def append(setting):
@ -178,6 +189,90 @@ class SettingGroup(object):
g.settings.append(setting)
return g
def number_predicate(self, pred):
"""
Make sure that `pred` has an assigned number, and will be included in
this group's bit vector.
The numbered predicates include:
- `BoolSetting` settings that belong to this group.
- `Predicate` instances in `named_predicates`.
- `Predicate` instances without a name.
- Settings or computed predicates that belong to the parent group, but
need to be accessible by number in this group.
The numbered predicates are referenced by the encoding tables as ISA
predicates. See the `isap` field on `Encoding`.
:returns: The assigned predicate number in this group.
"""
if pred in self.predicate_number:
return self.predicate_number[pred]
else:
number = len(self.predicate_number)
self.predicate_number[pred] = number
return number
def layout(self):
"""
Compute the layout of the byte vector used to represent this settings
group.
The byte vector contains the following entries in order:
1. Byte-sized settings like `NumSetting` and `EnumSetting`.
2. `BoolSetting` settings.
3. Precomputed named predicates.
4. Other numbered predicates, including anonymous predicates and parent
predicates that need to be accessible by number.
Set `self.settings_size` to the length of the byte vector prefix that
contains the settings. All bytes after that are computed, not
configured.
Set `self.boolean_offset` to the beginning of the numbered predicates,
2. in the list above.
Assign `byte_offset` and `bit_offset` fields in all settings.
After calling this method, no more settings can be added, but
additional predicates can be made accessible with `number_predicate()`.
"""
assert len(self.predicate_number) == 0, "Too late for layout"
# Assign the non-boolean settings.
byte_offset = 0
for s in self.settings:
if not isinstance(s, BoolSetting):
s.byte_offset = byte_offset
byte_offset += 1
# Then the boolean settings.
self.boolean_offset = byte_offset
for s in self.settings:
if isinstance(s, BoolSetting):
number = self.number_predicate(s)
s.byte_offset = byte_offset + number // 8
s.bit_offset = number % 8
# This is the end of the settings. Round up to a whole number of bytes.
self.boolean_settings = len(self.predicate_number)
self.settings_size = self.byte_size()
# Now assign numbers to all our named predicates.
for p in self.named_predicates:
self.number_predicate(p)
def byte_size(self):
"""
Compute the number of bytes required to hold all settings and
precomputed predicates.
This is the size of the byte-sized settings plus all the numbered
predcate bits rounded up to a whole number of bytes.
"""
return self.boolean_offset + (len(self.predicate_number) + 7) // 8
# Kinds of operands.
#
@ -977,7 +1072,7 @@ class TargetISA(object):
:returns self:
"""
self._collect_encoding_recipes()
self._collect_instruction_predicates()
self._collect_predicates()
return self
def _collect_encoding_recipes(self):
@ -994,12 +1089,15 @@ class TargetISA(object):
rcps.add(recipe)
self.all_recipes.append(recipe)
def _collect_instruction_predicates(self):
def _collect_predicates(self):
"""
Collect and number all instruction predicates in use.
Collect and number all predicates in use.
Sets `instp.number` for all used instruction predicates and places them
in `self.all_instps` in numerical order.
Ensures that all ISA predicates have an assigned bit number in
`self.settings`.
"""
self.all_instps = list()
instps = set()
@ -1012,6 +1110,12 @@ class TargetISA(object):
instps.add(instp)
self.all_instps.append(instp)
# All referenced ISA predicates must have a number in
# `self.settings`. This may cause some parent predicates to be
# replicated here, which is OK.
if enc.isap:
self.settings.number_predicate(enc.isap)
class CPUMode(object):
"""

109
meta/gen_settings.py

@ -8,52 +8,6 @@ import constant_hash
from cretonne import camel_case, BoolSetting, NumSetting, EnumSetting, settings
def layout_group(sgrp):
"""
Layout the settings in sgrp, assigning byte and bit offsets.
Return the number of bytes needed for settings and the total number of
bytes needed when including predicates.
"""
# 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
settings_size = next_byte
# Allocate bits for all the precomputed predicates.
for pred in sgrp.predicates:
# Allocate a bit from bool_byte.
if bool_bit > 7:
bool_byte = next_byte
next_byte += 1
bool_bit = 0
pred.byte_offset = bool_byte
pred.bit_offset = bool_bit
bool_bit += 1
return (settings_size, next_byte)
def gen_enum_types(sgrp, fmt):
"""
Emit enum types for any enum settings.
@ -68,7 +22,7 @@ def gen_enum_types(sgrp, fmt):
.format(ty, ", ".join(camel_case(v) for v in setting.values)))
def gen_getter(setting, fmt):
def gen_getter(setting, sgrp, fmt):
"""
Emit a getter function for `setting`.
"""
@ -77,9 +31,9 @@ def gen_getter(setting, fmt):
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))
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 + ' {', '}'):
@ -98,16 +52,16 @@ def gen_getter(setting, fmt):
raise AssertionError("Unknown setting kind")
def gen_pred_getter(pred, fmt):
def gen_pred_getter(pred, sgrp, fmt):
"""
Emit a getter for a pre-computed predicate.
"""
fmt.doc_comment('Computed predicate `{}`.'.format(pred.rust_predicate(0)))
proto = 'pub fn {}(&self) -> bool'.format(pred.name)
with fmt.indented(proto + ' {', '}'):
fmt.line('(self.bytes[{}] & (1 << {})) != 0'.format(
pred.byte_offset,
pred.bit_offset))
fmt.line(
'self.numbered_predicate({})'
.format(sgrp.predicate_number[pred]))
def gen_getters(sgrp, fmt):
@ -116,10 +70,16 @@ def gen_getters(sgrp, fmt):
"""
fmt.doc_comment("User-defined settings.")
with fmt.indented('impl Flags {', '}'):
# Dynamic numbered predicate getter.
with fmt.indented(
'pub 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, fmt)
for pred in sgrp.predicates:
gen_pred_getter(pred, fmt)
gen_getter(setting, sgrp, fmt)
for pred in sgrp.named_predicates:
gen_pred_getter(pred, sgrp, fmt)
def gen_descriptors(sgrp, fmt):
@ -175,11 +135,11 @@ def gen_descriptors(sgrp, fmt):
fmt.line('{},'.format(h.descriptor_index))
def gen_template(sgrp, settings_size, fmt):
def gen_template(sgrp, fmt):
"""
Emit a Template constant.
"""
v = [0] * settings_size
v = [0] * sgrp.settings_size
for setting in sgrp.settings:
v[setting.byte_offset] |= setting.default_byte()
@ -217,7 +177,7 @@ def gen_display(sgrp, fmt):
fmt.line('Ok(())')
def gen_constructor(sgrp, settings_size, byte_size, parent, fmt):
def gen_constructor(sgrp, parent, fmt):
"""
Generate a Flags constructor.
"""
@ -230,14 +190,14 @@ def gen_constructor(sgrp, settings_size, byte_size, parent, fmt):
with fmt.indented(
'pub fn new({}) -> Flags {{'.format(args), '}'):
fmt.line('let bvec = builder.finish("{}");'.format(sgrp.name))
fmt.line('let mut bytes = [0; {}];'.format(byte_size))
fmt.line('assert_eq!(bvec.len(), {});'.format(settings_size))
fmt.line('let mut bytes = [0; {}];'.format(sgrp.byte_size()))
fmt.line('assert_eq!(bvec.len(), {});'.format(sgrp.settings_size))
with fmt.indented(
'for (i, b) in bvec.into_iter().enumerate() {', '}'):
fmt.line('bytes[i] = b;')
# Stop here without predicates.
if len(sgrp.predicates) == 0:
if len(sgrp.predicate_number) == sgrp.boolean_settings:
fmt.line('Flags { bytes: bytes }')
return
@ -246,15 +206,24 @@ def gen_constructor(sgrp, settings_size, byte_size, parent, fmt):
'let mut {} = Flags {{ bytes: bytes }};'
.format(sgrp.name))
for pred in sgrp.predicates:
fmt.comment('Precompute: {}.'.format(pred.name))
for pred, number in sgrp.predicate_number.items():
# Don't compute our own settings.
if number < sgrp.boolean_settings:
continue
if pred.name:
fmt.comment(
'Precompute #{} ({}).'.format(number, pred.name))
else:
fmt.comment('Precompute #{}.'.format(number))
with fmt.indented(
'if {} {{'.format(pred.rust_predicate(0)),
'}'):
fmt.line(
'{}.bytes[{}] |= 1 << {};'
.format(
sgrp.name, pred.byte_offset, pred.bit_offset))
sgrp.name,
sgrp.boolean_offset + number // 8,
number % 8))
fmt.line(sgrp.name)
@ -263,18 +232,16 @@ def gen_group(sgrp, fmt):
"""
Generate a Flags struct representing `sgrp`.
"""
settings_size, byte_size = layout_group(sgrp)
fmt.line('#[derive(Clone)]')
fmt.doc_comment('Flags group `{}`.'.format(sgrp.name))
with fmt.indented('pub struct Flags {', '}'):
fmt.line('bytes: [u8; {}],'.format(byte_size))
fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size()))
gen_constructor(sgrp, settings_size, byte_size, None, fmt)
gen_constructor(sgrp, None, fmt)
gen_enum_types(sgrp, fmt)
gen_getters(sgrp, fmt)
gen_descriptors(sgrp, fmt)
gen_template(sgrp, settings_size, fmt)
gen_template(sgrp, fmt)
gen_display(sgrp, fmt)

Loading…
Cancel
Save