Browse Source

Use DataFlowGraph in Function.

Replace the three tables instructions, extended_basic_blocks, and
extended_values with a single 'dfg' public member.

Clients using Function are changed to refer to func.layout and func.dfg
respectively.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
c1806d0ab0
  1. 14
      src/libcretonne/cfg.rs
  2. 444
      src/libcretonne/repr.rs
  3. 4
      src/libcretonne/test_utils/make_inst.rs
  4. 20
      src/libcretonne/write.rs
  5. 20
      src/libreader/parser.rs
  6. 4
      src/tools/print_cfg.rs

14
src/libcretonne/cfg.rs

@ -56,7 +56,7 @@ impl ControlFlowGraph {
// Flips to true when a terminating instruction is seen. So that if additional
// instructions occur an error may be returned.
for inst in func.layout.ebb_insts(ebb) {
match func[inst] {
match func.dfg[inst] {
InstructionData::Branch { ty: _, opcode: _, ref data } => {
cfg.add_predecessor(data.destination, (ebb, inst));
}
@ -109,9 +109,9 @@ mod tests {
#[test]
fn no_predecessors() {
let mut func = Function::new();
let ebb0 = func.make_ebb();
let ebb1 = func.make_ebb();
let ebb2 = func.make_ebb();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0);
func.layout.append_ebb(ebb1);
func.layout.append_ebb(ebb2);
@ -130,9 +130,9 @@ mod tests {
#[test]
fn branches_and_jumps() {
let mut func = Function::new();
let ebb0 = func.make_ebb();
let ebb1 = func.make_ebb();
let ebb2 = func.make_ebb();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0);
func.layout.append_ebb(ebb1);
func.layout.append_ebb(ebb2);

444
src/libcretonne/repr.rs

@ -1,18 +1,14 @@
//! Representation of Cretonne IL functions.
use types::{Type, FunctionName, Signature, VOID};
use types::{FunctionName, Signature};
use entity_map::EntityRef;
use entities::{Ebb, NO_EBB, Inst, Value, NO_VALUE, ExpandedValue, StackSlot};
use instructions::*;
use entities::{Ebb, NO_EBB, StackSlot};
use dfg::DataFlowGraph;
use layout::Layout;
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Index, IndexMut};
use std::ops::Index;
/// A function.
///
/// The `Function` struct owns all of its instructions and extended basic blocks, and it works as a
/// container for those objects by implementing both `Index<Inst>` and `Index<Ebb>`.
///
pub struct Function {
/// Name of this function. Mostly used by `.cton` files.
pub name: FunctionName,
@ -26,17 +22,8 @@ pub struct Function {
/// Stack slots allocated in this function.
stack_slots: Vec<StackSlotData>,
/// Data about all of the instructions in the function. The instructions in this vector is not
/// necessarily in program order. The `Inst` reference indexes into this vector.
instructions: Vec<InstructionData>,
/// Extended basic blocks in the function, not necessarily in program order. The `Ebb`
/// reference indexes into this vector.
extended_basic_blocks: Vec<EbbData>,
/// Extended value table. Most `Value` references refer directly to their defining instruction.
/// Others index into this table.
extended_values: Vec<ValueData>,
/// Data flow graph containing the primary definition of all instructions, EBBs and values.
pub dfg: DataFlowGraph,
/// Layout of EBBs and instructions in the function body.
pub layout: Layout,
@ -50,9 +37,7 @@ impl Function {
signature: sig,
entry_block: NO_EBB,
stack_slots: Vec::new(),
instructions: Vec::new(),
extended_basic_blocks: Vec::new(),
extended_values: Vec::new(),
dfg: DataFlowGraph::new(),
layout: Layout::new(),
}
}
@ -83,175 +68,6 @@ impl Function {
end: self.stack_slots.len(),
}
}
// Instructions.
/// Create a new instruction.
///
/// The type of the first result is indicated by `data.ty`. If the instruction produces
/// multiple results, also call `make_inst_results` to allocate value table entries.
pub fn make_inst(&mut self, data: InstructionData) -> Inst {
let inst = Inst::new(self.instructions.len());
self.instructions.push(data);
inst
}
/// Create result values for an instruction that produces multiple results.
///
/// Instructions that produce 0 or 1 result values only need to be created with `make_inst`. If
/// the instruction may produce more than 1 result, call `make_inst_results` to allocate
/// `Value` table entries for the additional results.
///
/// The result value types are determined from the instruction's value type constraints and the
/// provided `ctrl_typevar` type for polymorphic instructions. For non-polymorphic
/// instructions, `ctrl_typevar` is ignored, and `VOID` can be used.
///
/// The type of the first result value is also set, even if it was already set in the
/// `InstructionData` passed to `make_inst`. If this function is called with a single-result
/// instruction, that is the only effect.
///
/// Returns the number of results produced by the instruction.
pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize {
let constraints = self[inst].opcode().constraints();
let fixed_results = constraints.fixed_results();
// Additional values form a linked list starting from the second result value. Generate
// the list backwards so we don't have to modify value table entries in place. (This
// causes additional result values to be numbered backwards which is not the aestetic
// choice, but since it is only visible in extremely rare instructions with 3+ results,
// we don't care).
let mut head = NO_VALUE;
let mut first_type = Type::default();
// TBD: Function call return values for direct and indirect function calls.
if fixed_results > 0 {
for res_idx in (1..fixed_results).rev() {
head = self.make_value(ValueData::Def {
ty: constraints.result_type(res_idx, ctrl_typevar),
def: inst,
next: head,
});
}
first_type = constraints.result_type(0, ctrl_typevar);
}
// Update the second_result pointer in `inst`.
if head != NO_VALUE {
*self[inst]
.second_result_mut()
.expect("instruction format doesn't allow multiple results") = head;
}
*self[inst].first_type_mut() = first_type;
fixed_results
}
/// Get the first result of an instruction.
///
/// If `Inst` doesn't produce any results, this returns a `Value` with a `VOID` type.
pub fn first_result(&self, inst: Inst) -> Value {
Value::new_direct(inst)
}
/// Iterate through all the results of an instruction.
pub fn inst_results<'a>(&'a self, inst: Inst) -> Values<'a> {
Values {
func: self,
cur: if self[inst].first_type() == VOID {
NO_VALUE
} else {
Value::new_direct(inst)
},
}
}
// Basic blocks
/// Create a new basic block.
pub fn make_ebb(&mut self) -> Ebb {
let ebb = Ebb::new(self.extended_basic_blocks.len());
self.extended_basic_blocks.push(EbbData::new());
ebb
}
/// Reference the representation of an EBB.
fn ebb(&self, ebb: Ebb) -> &EbbData {
&self.extended_basic_blocks[ebb.index()]
}
/// Mutably reference the representation of an EBB.
fn ebb_mut(&mut self, ebb: Ebb) -> &mut EbbData {
&mut self.extended_basic_blocks[ebb.index()]
}
/// Iterate over all the EBBs in order of creation.
pub fn ebbs_numerically(&self) -> NumericalEbbs {
NumericalEbbs {
cur: 0,
limit: self.extended_basic_blocks.len(),
}
}
/// Append an argument with type `ty` to `ebb`.
pub fn append_ebb_arg(&mut self, ebb: Ebb, ty: Type) -> Value {
let val = self.make_value(ValueData::Argument {
ty: ty,
ebb: ebb,
next: NO_VALUE,
});
let last_arg = self.ebb(ebb).last_arg;
match last_arg.expand() {
// If last_arg = NO_VALUE, we're adding the first EBB argument.
ExpandedValue::None => self.ebb_mut(ebb).first_arg = val,
ExpandedValue::Table(index) => {
// Append to linked list of arguments.
if let ValueData::Argument { ref mut next, .. } = self.extended_values[index] {
*next = val;
} else {
panic!("wrong type of extended value referenced by Ebb::last_arg");
}
}
ExpandedValue::Direct(_) => panic!("Direct value cannot appear as EBB argument"),
}
self.ebb_mut(ebb).last_arg = val;
val
}
/// Iterate through the arguments to an EBB.
pub fn ebb_args<'a>(&'a self, ebb: Ebb) -> Values<'a> {
Values {
func: self,
cur: self.ebb(ebb).first_arg,
}
}
// Values.
/// Allocate an extended value entry.
fn make_value(&mut self, data: ValueData) -> Value {
let vref = Value::new_table(self.extended_values.len());
self.extended_values.push(data);
vref
}
/// Get the type of a value.
pub fn value_type(&self, v: Value) -> Type {
use entities::ExpandedValue::*;
use self::ValueData::*;
match v.expand() {
Direct(i) => self[i].first_type(),
Table(i) => {
match self.extended_values[i] {
Def { ty, .. } => ty,
Argument { ty, .. } => ty,
}
}
None => panic!("NO_VALUE has no type"),
}
}
}
impl Debug for Function {
@ -316,197 +132,9 @@ impl Iterator for StackSlotIter {
}
}
// ====--------------------------------------------------------------------------------------====//
//
// Extended basic block implementation.
//
// ====--------------------------------------------------------------------------------------====//
/// Contents of an extended basic block.
///
/// Arguments for an extended basic block are values that dominate everything in the EBB. All
/// branches to this EBB must provide matching arguments, and the arguments to the entry EBB must
/// match the function arguments.
#[derive(Debug)]
pub struct EbbData {
/// First argument to this EBB, or `NO_VALUE` if the block has no arguments.
///
/// The arguments are all ValueData::Argument entries that form a linked list from `first_arg`
/// to `last_arg`.
first_arg: Value,
/// Last argument to this EBB, or `NO_VALUE` if the block has no arguments.
last_arg: Value,
}
impl EbbData {
fn new() -> EbbData {
EbbData {
first_arg: NO_VALUE,
last_arg: NO_VALUE,
}
}
}
impl Index<Ebb> for Function {
type Output = EbbData;
fn index<'a>(&'a self, ebb: Ebb) -> &'a EbbData {
&self.extended_basic_blocks[ebb.index()]
}
}
impl IndexMut<Ebb> for Function {
fn index_mut<'a>(&'a mut self, ebb: Ebb) -> &'a mut EbbData {
&mut self.extended_basic_blocks[ebb.index()]
}
}
/// Iterate through all EBBs in a function in numerical order.
/// This order is stable, but has little significance to the semantics of the function.
pub struct NumericalEbbs {
cur: usize,
limit: usize,
}
impl Iterator for NumericalEbbs {
type Item = Ebb;
fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.limit {
let prev = Ebb::new(self.cur);
self.cur += 1;
Some(prev)
} else {
None
}
}
}
// ====--------------------------------------------------------------------------------------====//
//
// Instruction implementation.
//
// The InstructionData layout is defined in the `instructions` module.
//
// ====--------------------------------------------------------------------------------------====//
/// Allow immutable access to instructions via function indexing.
impl Index<Inst> for Function {
type Output = InstructionData;
fn index<'a>(&'a self, inst: Inst) -> &'a InstructionData {
&self.instructions[inst.index()]
}
}
/// Allow mutable access to instructions via function indexing.
impl IndexMut<Inst> for Function {
fn index_mut<'a>(&'a mut self, inst: Inst) -> &'a mut InstructionData {
&mut self.instructions[inst.index()]
}
}
// ====--------------------------------------------------------------------------------------====//
//
// Value implementation.
//
// ====--------------------------------------------------------------------------------------====//
// Most values are simply the first value produced by an instruction.
// Other values have an entry in the value table.
#[derive(Debug)]
enum ValueData {
// Value is defined by an instruction, but it is not the first result.
Def {
ty: Type,
def: Inst,
next: Value, // Next result defined by `def`.
},
// Value is an EBB argument.
Argument {
ty: Type,
ebb: Ebb,
next: Value, // Next argument to `ebb`.
},
}
/// Iterate through a list of related value references, such as:
///
/// - All results defined by an instruction.
/// - All arguments to an EBB
///
/// A value iterator borrows a Function reference.
pub struct Values<'a> {
func: &'a Function,
cur: Value,
}
impl<'a> Iterator for Values<'a> {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
let prev = self.cur;
// Advance self.cur to the next value, or NO_VALUE.
self.cur = match prev.expand() {
ExpandedValue::Direct(inst) => self.func[inst].second_result().unwrap_or_default(),
ExpandedValue::Table(index) => {
match self.func.extended_values[index] {
ValueData::Def { next, .. } => next,
ValueData::Argument { next, .. } => next,
}
}
ExpandedValue::None => return None,
};
Some(prev)
}
}
#[cfg(test)]
mod tests {
use super::*;
use types;
use instructions::*;
#[test]
fn make_inst() {
let mut func = Function::new();
let idata = InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
};
let inst = func.make_inst(idata);
assert_eq!(inst.to_string(), "inst0");
// Immutable reference resolution.
let ins = &func[inst];
assert_eq!(ins.opcode(), Opcode::Iconst);
assert_eq!(ins.first_type(), types::I32);
// Result iterator.
let mut res = func.inst_results(inst);
assert!(res.next().is_some());
assert!(res.next().is_none());
}
#[test]
fn no_results() {
let mut func = Function::new();
let idata = InstructionData::Nullary {
opcode: Opcode::Trap,
ty: types::VOID,
};
let inst = func.make_inst(idata);
// Result iterator should be empty.
let mut res = func.inst_results(inst);
assert_eq!(res.next(), None);
}
#[test]
fn stack_slot() {
@ -520,62 +148,4 @@ mod tests {
assert_eq!(func[ss0].size, 4);
assert_eq!(func[ss1].size, 8);
}
#[test]
fn ebb() {
let mut func = Function::new();
assert_eq!(func.ebbs_numerically().next(), None);
let ebb = func.make_ebb();
assert_eq!(ebb.to_string(), "ebb0");
assert_eq!(func.ebb_args(ebb).next(), None);
func.layout.append_ebb(ebb);
let arg1 = func.append_ebb_arg(ebb, types::F32);
assert_eq!(arg1.to_string(), "vx0");
{
let mut args1 = func.ebb_args(ebb);
assert_eq!(args1.next(), Some(arg1));
assert_eq!(args1.next(), None);
}
let arg2 = func.append_ebb_arg(ebb, types::I16);
assert_eq!(arg2.to_string(), "vx1");
{
let mut args2 = func.ebb_args(ebb);
assert_eq!(args2.next(), Some(arg1));
assert_eq!(args2.next(), Some(arg2));
assert_eq!(args2.next(), None);
}
// The numerical ebb iterator doesn't capture the function.
let mut ebbs = func.ebbs_numerically();
assert_eq!(ebbs.next(), Some(ebb));
assert_eq!(ebbs.next(), None);
assert_eq!(func.layout.ebb_insts(ebb).next(), None);
let inst = func.make_inst(InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
});
func.layout.append_inst(inst, ebb);
{
let mut ii = func.layout.ebb_insts(ebb);
assert_eq!(ii.next(), Some(inst));
assert_eq!(ii.next(), None);
}
let inst2 = func.make_inst(InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
});
func.layout.append_inst(inst2, ebb);
{
let mut ii = func.layout.ebb_insts(ebb);
assert_eq!(ii.next(), Some(inst));
assert_eq!(ii.next(), Some(inst2));
assert_eq!(ii.next(), None);
}
}
}

4
src/libcretonne/test_utils/make_inst.rs

@ -6,7 +6,7 @@ use instructions::{InstructionData, Opcode, VariableArgs, JumpData, BranchData};
use types;
pub fn jump(func: &mut Function, dest: Ebb) -> Inst {
func.make_inst(InstructionData::Jump {
func.dfg.make_inst(InstructionData::Jump {
opcode: Opcode::Jump,
ty: types::VOID,
data: Box::new(JumpData {
@ -17,7 +17,7 @@ pub fn jump(func: &mut Function, dest: Ebb) -> Inst {
}
pub fn branch(func: &mut Function, dest: Ebb) -> Inst {
func.make_inst(InstructionData::Branch {
func.dfg.make_inst(InstructionData::Branch {
opcode: Opcode::Brz,
ty: types::VOID,
data: Box::new(BranchData {

20
src/libcretonne/write.rs

@ -85,7 +85,7 @@ fn write_preamble(w: &mut Write, func: &Function) -> io::Result<bool> {
// ====--------------------------------------------------------------------------------------====//
pub fn write_arg(w: &mut Write, func: &Function, arg: Value) -> Result {
write!(w, "{}: {}", arg, func.value_type(arg))
write!(w, "{}: {}", arg, func.dfg.value_type(arg))
}
pub fn write_ebb_header(w: &mut Write, func: &Function, ebb: Ebb) -> Result {
@ -96,7 +96,7 @@ pub fn write_ebb_header(w: &mut Write, func: &Function, ebb: Ebb) -> Result {
// ebb10(vx4: f64, vx5: b1):
//
let mut args = func.ebb_args(ebb);
let mut args = func.dfg.ebb_args(ebb);
match args.next() {
None => return writeln!(w, "{}:", ebb),
Some(arg) => {
@ -133,7 +133,7 @@ pub fn write_ebb(w: &mut Write, func: &Function, ebb: Ebb) -> Result {
// if it can't be trivially inferred.
//
fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
let constraints = func[inst].opcode().constraints();
let constraints = func.dfg[inst].opcode().constraints();
if !constraints.is_polymorphic() {
return None;
@ -150,7 +150,7 @@ fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
// This polymorphic instruction doesn't support basic type inference.
// The controlling type variable is required to be the type of the first result.
let rtype = func.value_type(func.first_result(inst));
let rtype = func.dfg.value_type(func.dfg.first_result(inst));
assert!(!rtype.is_void(),
"Polymorphic instruction must produce a result");
Some(rtype)
@ -161,7 +161,7 @@ pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result {
// First write out the result values, if any.
let mut has_results = false;
for r in func.inst_results(inst) {
for r in func.dfg.inst_results(inst) {
if !has_results {
has_results = true;
try!(write!(w, "{}", r));
@ -174,7 +174,7 @@ pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result {
}
// Then the opcode, possibly with a '.type' suffix.
let opcode = func[inst].opcode();
let opcode = func.dfg[inst].opcode();
match type_suffix(func, inst) {
Some(suf) => try!(write!(w, "{}.{}", opcode, suf)),
@ -183,7 +183,7 @@ pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result {
// Then the operands, depending on format.
use instructions::InstructionData::*;
match func[inst] {
match func.dfg[inst] {
Nullary { .. } => writeln!(w, ""),
Unary { arg, .. } => writeln!(w, " {}", arg),
UnaryImm { imm, .. } => writeln!(w, " {}", imm),
@ -250,16 +250,16 @@ mod tests {
assert_eq!(function_to_string(&f),
"function foo() {\n ss0 = stack_slot 4\n}\n");
let ebb = f.make_ebb();
let ebb = f.dfg.make_ebb();
f.layout.append_ebb(ebb);
assert_eq!(function_to_string(&f),
"function foo() {\n ss0 = stack_slot 4\n\nebb0:\n}\n");
f.append_ebb_arg(ebb, types::I8);
f.dfg.append_ebb_arg(ebb, types::I8);
assert_eq!(function_to_string(&f),
"function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8):\n}\n");
f.append_ebb_arg(ebb, types::F32.by(4).unwrap());
f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap());
assert_eq!(function_to_string(&f),
"function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8, vx1: f32x4):\n}\n");
}

20
src/libreader/parser.rs

@ -100,7 +100,7 @@ impl Context {
// Allocate a new EBB and add a mapping src_ebb -> Ebb.
fn add_ebb(&mut self, src_ebb: Ebb, loc: &Location) -> Result<Ebb> {
let ebb = self.function.make_ebb();
let ebb = self.function.dfg.make_ebb();
self.function.layout.append_ebb(ebb);
if self.ebbs.insert(src_ebb, ebb).is_some() {
err!(loc, "duplicate EBB: {}", src_ebb)
@ -163,7 +163,7 @@ impl Context {
// Rewrite all EBB and value references in the function.
fn rewrite_references(&mut self) -> Result<()> {
for &(inst, loc) in &self.inst_locs {
match self.function[inst] {
match self.function.dfg[inst] {
InstructionData::Nullary { .. } |
InstructionData::UnaryImm { .. } |
InstructionData::UnaryIeee32 { .. } |
@ -646,7 +646,7 @@ impl<'a> Parser<'a> {
// ebb-arg ::= Value(vx) ":" * Type(t)
let t = try!(self.match_type("expected EBB argument type"));
// Allocate the EBB argument and add the mapping.
let value = ctx.function.append_ebb_arg(ebb, t);
let value = ctx.function.dfg.append_ebb_arg(ebb, t);
ctx.add_value(vx, value, &vx_location)
}
@ -703,8 +703,8 @@ impl<'a> Parser<'a> {
// or function call signature. We also need to create values with the right type for all
// the instruction results.
let ctrl_typevar = try!(self.infer_typevar(ctx, opcode, explicit_ctrl_type, &inst_data));
let inst = ctx.function.make_inst(inst_data);
let num_results = ctx.function.make_inst_results(inst, ctrl_typevar);
let inst = ctx.function.dfg.make_inst(inst_data);
let num_results = ctx.function.dfg.make_inst_results(inst, ctrl_typevar);
ctx.function.layout.append_inst(inst, ebb);
ctx.add_inst_loc(inst, &opcode_loc);
@ -720,7 +720,7 @@ impl<'a> Parser<'a> {
// holds a reference to `ctx.function`.
self.add_values(&mut ctx.values,
results.into_iter(),
ctx.function.inst_results(inst))
ctx.function.dfg.inst_results(inst))
}
// Type inference for polymorphic instructions.
@ -750,7 +750,7 @@ impl<'a> Parser<'a> {
// layout of the blocks.
let ctrl_src_value = inst_data.typevar_operand()
.expect("Constraints <-> Format inconsistency");
ctx.function.value_type(match ctx.values.get(&ctrl_src_value) {
ctx.function.dfg.value_type(match ctx.values.get(&ctrl_src_value) {
Some(&v) => v,
None => {
return err!(self.loc,
@ -1119,12 +1119,12 @@ mod tests {
let mut ebbs = func.layout.ebbs();
let ebb0 = ebbs.next().unwrap();
assert_eq!(func.ebb_args(ebb0).next(), None);
assert_eq!(func.dfg.ebb_args(ebb0).next(), None);
let ebb4 = ebbs.next().unwrap();
let mut ebb4_args = func.ebb_args(ebb4);
let mut ebb4_args = func.dfg.ebb_args(ebb4);
let arg0 = ebb4_args.next().unwrap();
assert_eq!(func.value_type(arg0), types::I32);
assert_eq!(func.dfg.value_type(arg0), types::I32);
assert_eq!(ebb4_args.next(), None);
}
}

4
src/tools/print_cfg.rs

@ -110,14 +110,14 @@ impl<T: Write> CFGPrinter<T> {
let inst_data = func.layout
.ebb_insts(ebb)
.filter(|inst| {
match func[*inst] {
match func.dfg[*inst] {
InstructionData::Branch { ty: _, opcode: _, data: _ } => true,
InstructionData::Jump { ty: _, opcode: _, data: _ } => true,
_ => false,
}
})
.map(|inst| {
let op = match func[inst] {
let op = match func.dfg[inst] {
InstructionData::Branch { ty: _, opcode, ref data } => {
Some((opcode, data.destination))
}

Loading…
Cancel
Save