From 84b0a92326f82be295d6f52b60934476cd458cd6 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 31 Aug 2016 11:53:37 -0700 Subject: [PATCH] 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. --- meta/cretonne/__init__.py | 116 ++++++++++++++++++++++++++++++++++++-- meta/gen_settings.py | 109 +++++++++++++---------------------- 2 files changed, 148 insertions(+), 77 deletions(-) diff --git a/meta/cretonne/__init__.py b/meta/cretonne/__init__.py index cc3a079e5f..23ec938f61 100644 --- a/meta/cretonne/__init__.py +++ b/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): """ diff --git a/meta/gen_settings.py b/meta/gen_settings.py index c5c48ad4b7..588c2cf798 100644 --- a/meta/gen_settings.py +++ b/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)