From c998c79fe844fc9a2bc1e5187332f63bbbb11e3e Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Thu, 11 Aug 2016 17:38:56 -0700 Subject: [PATCH] Generate code to precompute predicates. Each ISA predicate is assigned a bit the the Flags struct, and a corresponding method is generated. --- cranelift/src/libcretonne/isa/riscv/mod.rs | 2 +- .../src/libcretonne/isa/riscv/settings.rs | 27 ++++++- meta/cretonne/__init__.py | 9 +++ meta/cretonne/predicates.py | 31 +++++++ meta/gen_settings.py | 81 ++++++++++++++++--- 5 files changed, 135 insertions(+), 15 deletions(-) diff --git a/cranelift/src/libcretonne/isa/riscv/mod.rs b/cranelift/src/libcretonne/isa/riscv/mod.rs index 641263b9f6..4938a7ad28 100644 --- a/cranelift/src/libcretonne/isa/riscv/mod.rs +++ b/cranelift/src/libcretonne/isa/riscv/mod.rs @@ -25,8 +25,8 @@ fn isa_constructor(shared_flags: shared_settings::Flags, builder: shared_settings::Builder) -> Box { Box::new(Isa { + isa_flags: settings::Flags::new(&shared_flags, builder), shared_flags: shared_flags, - isa_flags: settings::Flags::new(builder), }) } diff --git a/cranelift/src/libcretonne/isa/riscv/settings.rs b/cranelift/src/libcretonne/isa/riscv/settings.rs index 335314a3c9..1dd1adc405 100644 --- a/cranelift/src/libcretonne/isa/riscv/settings.rs +++ b/cranelift/src/libcretonne/isa/riscv/settings.rs @@ -1,6 +1,6 @@ //! RISC-V Settings. -use settings::{detail, Builder}; +use settings::{self, detail, Builder}; use std::fmt; // Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct @@ -10,16 +10,39 @@ include!(concat!(env!("OUT_DIR"), "/settings-riscv.rs")); #[cfg(test)] mod tests { use super::{builder, Flags}; + use settings::{self, Configurable}; #[test] fn display_default() { + let shared = settings::Flags::new(settings::builder()); let b = builder(); - let f = Flags::new(b); + let f = Flags::new(&shared, b); assert_eq!(f.to_string(), "[riscv]\n\ supports_m = false\n\ supports_a = false\n\ supports_f = false\n\ supports_d = false\n"); + // Predicates are not part of the Display output. + assert_eq!(f.full_float(), false); + } + + #[test] + fn predicates() { + let shared = settings::Flags::new(settings::builder()); + let mut b = builder(); + b.set_bool("supports_f", true).unwrap(); + b.set_bool("supports_d", true).unwrap(); + let f = Flags::new(&shared, b); + assert_eq!(f.full_float(), true); + + let mut sb = settings::builder(); + sb.set_bool("enable_simd", false).unwrap(); + let shared = settings::Flags::new(sb); + let mut b = builder(); + b.set_bool("supports_f", true).unwrap(); + b.set_bool("supports_d", true).unwrap(); + let f = Flags::new(&shared, b); + assert_eq!(f.full_float(), false); } } diff --git a/meta/cretonne/__init__.py b/meta/cretonne/__init__.py index f576380f65..cc31f47cf5 100644 --- a/meta/cretonne/__init__.py +++ b/meta/cretonne/__init__.py @@ -63,6 +63,15 @@ class BoolSetting(Setting): else: return 0 + def rust_predicate(self, prec): + """ + Return the Rust code to compute the value of this setting. + + The emitted code assumes that the setting group exists as a local + variable. + """ + return '{}.{}()'.format(self.group.name, self.name) + class NumSetting(Setting): """ diff --git a/meta/cretonne/predicates.py b/meta/cretonne/predicates.py index 2c34e489e5..dd50613a37 100644 --- a/meta/cretonne/predicates.py +++ b/meta/cretonne/predicates.py @@ -72,23 +72,54 @@ class And(Predicate): Computed predicate that is true if all parts are true. """ + precedence = 2 + def __init__(self, *args): super(And, self).__init__(args) + def rust_predicate(self, prec): + """ + Return a Rust expression computing the value of this predicate. + + The surrounding precedence determines whether parentheses are needed: + + 0. An `if` statement. + 1. An `||` expression. + 2. An `&&` expression. + 3. A `!` expression. + """ + s = ' && '.join(p.rust_predicate(And.precedence) for p in self.parts) + if prec > And.precedence: + s = '({})'.format(s) + return s + class Or(Predicate): """ Computed predicate that is true if any parts are true. """ + precedence = 1 + def __init__(self, *args): super(Or, self).__init__(args) + def rust_predicate(self, prec): + s = ' || '.join(p.rust_predicate(Or.precedence) for p in self.parts) + if prec > Or.precedence: + s = '({})'.format(s) + return s + class Not(Predicate): """ Computed predicate that is true if its single part is false. """ + precedence = 3 + def __init__(self, part): super(Not, self).__init__((part,)) + + def rust_predicate(self, prec): + return '!' + self.parts[0].rust_predicate(Not.precedence) diff --git a/meta/gen_settings.py b/meta/gen_settings.py index 486e71371e..85c154648d 100644 --- a/meta/gen_settings.py +++ b/meta/gen_settings.py @@ -12,7 +12,8 @@ def layout_group(sgrp): """ Layout the settings in sgrp, assigning byte and bit offsets. - Return the next unused byte offset. + 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 @@ -37,7 +38,20 @@ def layout_group(sgrp): setting.byte_offset = next_byte next_byte += 1 - return next_byte + 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): @@ -84,6 +98,18 @@ def gen_getter(setting, fmt): raise AssertionError("Unknown setting kind") +def gen_pred_getter(pred, 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)) + + def gen_getters(sgrp, fmt): """ Emit getter functions for all the settings in fmt. @@ -92,6 +118,8 @@ def gen_getters(sgrp, fmt): with fmt.indented('impl Flags {', '}'): for setting in sgrp.settings: gen_getter(setting, fmt) + for pred in sgrp.predicates: + gen_pred_getter(pred, fmt) def gen_descriptors(sgrp, fmt): @@ -147,11 +175,11 @@ def gen_descriptors(sgrp, fmt): fmt.line('{},'.format(h.descriptor_index)) -def gen_template(sgrp, byte_size, fmt): +def gen_template(sgrp, settings_size, fmt): """ Emit a Template constant. """ - v = [0] * byte_size + v = [0] * settings_size for setting in sgrp.settings: v[setting.byte_offset] |= setting.default_byte() @@ -189,50 +217,79 @@ def gen_display(sgrp, fmt): fmt.line('Ok(())') -def gen_constructor(sgrp, byte_size, parent, fmt): +def gen_constructor(sgrp, settings_size, byte_size, parent, fmt): """ Generate a Flags constructor. """ with fmt.indented('impl Flags {', '}'): - with fmt.indented('pub fn new(builder: Builder) -> Flags {', '}'): + args = 'builder: Builder' + if sgrp.parent: + p = sgrp.parent + args = '{}: &{}::Flags, {}'.format(p.name, p.qual_mod, args) + 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!(bytes.len(), bvec.len());') + fmt.line('assert_eq!(bytes.len(), {});'.format(settings_size)) with fmt.indented( 'for (i, b) in bvec.into_iter().enumerate() {', '}'): fmt.line('bytes[i] = b;') - fmt.line('Flags { bytes: bytes }') + + # Stop here without predicates. + if len(sgrp.predicates) == 0: + fmt.line('Flags { bytes: bytes }') + return + + # Now compute the predicates. + fmt.line( + 'let mut {} = Flags {{ bytes: bytes }};' + .format(sgrp.name)) + + for pred in sgrp.predicates: + fmt.comment('Precompute: {}.'.format(pred.name)) + with fmt.indented( + 'if {} {{'.format(pred.rust_predicate(0)), + '}'): + fmt.line( + '{}.bytes[{}] |= 1 << {};' + .format( + sgrp.name, pred.byte_offset, pred.bit_offset)) + + fmt.line(sgrp.name) def gen_group(sgrp, fmt): """ Generate a Flags struct representing `sgrp`. """ - byte_size = layout_group(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(settings_size)) - gen_constructor(sgrp, byte_size, None, fmt) + gen_constructor(sgrp, settings_size, byte_size, None, fmt) gen_enum_types(sgrp, fmt) gen_getters(sgrp, fmt) gen_descriptors(sgrp, fmt) - gen_template(sgrp, byte_size, fmt) + gen_template(sgrp, settings_size, fmt) gen_display(sgrp, fmt) def generate(isas, out_dir): # Generate shared settings. fmt = srcgen.Formatter() + settings.group.qual_mod = 'settings' gen_group(settings.group, fmt) fmt.update_file('settings.rs', out_dir) # Generate ISA-specific settings. for isa in isas: if isa.settings: + isa.settings.qual_mod = 'isa::{}::settings'.format( + isa.settings.name) fmt = srcgen.Formatter() gen_group(isa.settings, fmt) fmt.update_file('settings-{}.rs'.format(isa.name), out_dir)