Generate code to precompute predicates.

Each ISA predicate is assigned a bit the the Flags struct, and a corresponding
method is generated.
This commit is contained in:
Jakob Stoklund Olesen
2016-08-11 17:38:56 -07:00
parent 13d33d5a7a
commit c998c79fe8
5 changed files with 135 additions and 15 deletions

View File

@@ -25,8 +25,8 @@ fn isa_constructor(shared_flags: shared_settings::Flags,
builder: shared_settings::Builder) builder: shared_settings::Builder)
-> Box<TargetIsa> { -> Box<TargetIsa> {
Box::new(Isa { Box::new(Isa {
isa_flags: settings::Flags::new(&shared_flags, builder),
shared_flags: shared_flags, shared_flags: shared_flags,
isa_flags: settings::Flags::new(builder),
}) })
} }

View File

@@ -1,6 +1,6 @@
//! RISC-V Settings. //! RISC-V Settings.
use settings::{detail, Builder}; use settings::{self, detail, Builder};
use std::fmt; use std::fmt;
// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct // 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)] #[cfg(test)]
mod tests { mod tests {
use super::{builder, Flags}; use super::{builder, Flags};
use settings::{self, Configurable};
#[test] #[test]
fn display_default() { fn display_default() {
let shared = settings::Flags::new(settings::builder());
let b = builder(); let b = builder();
let f = Flags::new(b); let f = Flags::new(&shared, b);
assert_eq!(f.to_string(), assert_eq!(f.to_string(),
"[riscv]\n\ "[riscv]\n\
supports_m = false\n\ supports_m = false\n\
supports_a = false\n\ supports_a = false\n\
supports_f = false\n\ supports_f = false\n\
supports_d = 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);
} }
} }

View File

@@ -63,6 +63,15 @@ class BoolSetting(Setting):
else: else:
return 0 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): class NumSetting(Setting):
""" """

View File

@@ -72,23 +72,54 @@ class And(Predicate):
Computed predicate that is true if all parts are true. Computed predicate that is true if all parts are true.
""" """
precedence = 2
def __init__(self, *args): def __init__(self, *args):
super(And, self).__init__(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): class Or(Predicate):
""" """
Computed predicate that is true if any parts are true. Computed predicate that is true if any parts are true.
""" """
precedence = 1
def __init__(self, *args): def __init__(self, *args):
super(Or, self).__init__(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): class Not(Predicate):
""" """
Computed predicate that is true if its single part is false. Computed predicate that is true if its single part is false.
""" """
precedence = 3
def __init__(self, part): def __init__(self, part):
super(Not, self).__init__((part,)) super(Not, self).__init__((part,))
def rust_predicate(self, prec):
return '!' + self.parts[0].rust_predicate(Not.precedence)

View File

@@ -12,7 +12,8 @@ def layout_group(sgrp):
""" """
Layout the settings in sgrp, assigning byte and bit offsets. 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. # Byte offset where booleans are allocated.
bool_byte = -1 bool_byte = -1
@@ -37,7 +38,20 @@ def layout_group(sgrp):
setting.byte_offset = next_byte setting.byte_offset = next_byte
next_byte += 1 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): def gen_enum_types(sgrp, fmt):
@@ -84,6 +98,18 @@ def gen_getter(setting, fmt):
raise AssertionError("Unknown setting kind") 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): def gen_getters(sgrp, fmt):
""" """
Emit getter functions for all the settings in fmt. Emit getter functions for all the settings in fmt.
@@ -92,6 +118,8 @@ def gen_getters(sgrp, fmt):
with fmt.indented('impl Flags {', '}'): with fmt.indented('impl Flags {', '}'):
for setting in sgrp.settings: for setting in sgrp.settings:
gen_getter(setting, fmt) gen_getter(setting, fmt)
for pred in sgrp.predicates:
gen_pred_getter(pred, fmt)
def gen_descriptors(sgrp, fmt): def gen_descriptors(sgrp, fmt):
@@ -147,11 +175,11 @@ def gen_descriptors(sgrp, fmt):
fmt.line('{},'.format(h.descriptor_index)) fmt.line('{},'.format(h.descriptor_index))
def gen_template(sgrp, byte_size, fmt): def gen_template(sgrp, settings_size, fmt):
""" """
Emit a Template constant. Emit a Template constant.
""" """
v = [0] * byte_size v = [0] * settings_size
for setting in sgrp.settings: for setting in sgrp.settings:
v[setting.byte_offset] |= setting.default_byte() v[setting.byte_offset] |= setting.default_byte()
@@ -189,50 +217,79 @@ def gen_display(sgrp, fmt):
fmt.line('Ok(())') 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. Generate a Flags constructor.
""" """
with fmt.indented('impl Flags {', '}'): 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 bvec = builder.finish("{}");'.format(sgrp.name))
fmt.line('let mut bytes = [0; {}];'.format(byte_size)) 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( with fmt.indented(
'for (i, b) in bvec.into_iter().enumerate() {', '}'): 'for (i, b) in bvec.into_iter().enumerate() {', '}'):
fmt.line('bytes[i] = b;') 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): def gen_group(sgrp, fmt):
""" """
Generate a Flags struct representing `sgrp`. Generate a Flags struct representing `sgrp`.
""" """
byte_size = layout_group(sgrp) settings_size, byte_size = layout_group(sgrp)
fmt.line('#[derive(Clone)]') fmt.line('#[derive(Clone)]')
fmt.doc_comment('Flags group `{}`.'.format(sgrp.name)) fmt.doc_comment('Flags group `{}`.'.format(sgrp.name))
with fmt.indented('pub struct Flags {', '}'): 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_enum_types(sgrp, fmt)
gen_getters(sgrp, fmt) gen_getters(sgrp, fmt)
gen_descriptors(sgrp, fmt) gen_descriptors(sgrp, fmt)
gen_template(sgrp, byte_size, fmt) gen_template(sgrp, settings_size, fmt)
gen_display(sgrp, fmt) gen_display(sgrp, fmt)
def generate(isas, out_dir): def generate(isas, out_dir):
# Generate shared settings. # Generate shared settings.
fmt = srcgen.Formatter() fmt = srcgen.Formatter()
settings.group.qual_mod = 'settings'
gen_group(settings.group, fmt) gen_group(settings.group, fmt)
fmt.update_file('settings.rs', out_dir) fmt.update_file('settings.rs', out_dir)
# Generate ISA-specific settings. # Generate ISA-specific settings.
for isa in isas: for isa in isas:
if isa.settings: if isa.settings:
isa.settings.qual_mod = 'isa::{}::settings'.format(
isa.settings.name)
fmt = srcgen.Formatter() fmt = srcgen.Formatter()
gen_group(isa.settings, fmt) gen_group(isa.settings, fmt)
fmt.update_file('settings-{}.rs'.format(isa.name), out_dir) fmt.update_file('settings-{}.rs'.format(isa.name), out_dir)