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)
-> Box<TargetIsa> {
Box::new(Isa {
isa_flags: settings::Flags::new(&shared_flags, builder),
shared_flags: shared_flags,
isa_flags: settings::Flags::new(builder),
})
}

View File

@@ -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);
}
}

View File

@@ -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):
"""

View File

@@ -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)

View File

@@ -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;')
# 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)