Files
wasmtime/src/libcretonne/repr.rs
Jakob Stoklund Olesen 99464bc29d Implement EntityRef for most of the entities module.
The only exception is Value which has two dimensions.
2016-07-15 16:03:14 -07:00

645 lines
19 KiB
Rust

//! Representation of Cretonne IL functions.
use types::{Type, FunctionName, Signature, VOID};
use entity_map::EntityRef;
use entities::{Ebb, NO_EBB, Inst, NO_INST, Value, NO_VALUE, ExpandedValue, StackSlot};
use instructions::*;
use std::fmt::{self, Display, Formatter};
use std::ops::{Index, IndexMut};
/// 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>`.
///
#[derive(Debug)]
pub struct Function {
/// Name of this function. Mostly used by `.cton` files.
pub name: FunctionName,
/// Signature of this function.
signature: Signature,
/// The entry block.
pub entry_block: Ebb,
/// 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>,
// Linked list nodes for the layout order of instructions. Forms a double linked list per EBB,
// terminated in both ends by NO_INST.
inst_order: Vec<InstNode>,
}
impl Function {
/// Create a function with the given name and signature.
pub fn with_name_signature(name: FunctionName, sig: Signature) -> Function {
Function {
name: name,
signature: sig,
entry_block: NO_EBB,
stack_slots: Vec::new(),
instructions: Vec::new(),
extended_basic_blocks: Vec::new(),
extended_values: Vec::new(),
inst_order: Vec::new(),
}
}
/// Create a new empty, anomymous function.
pub fn new() -> Function {
Self::with_name_signature(FunctionName::new(), Signature::new())
}
/// Get the signature of this function.
pub fn own_signature(&self) -> &Signature {
&self.signature
}
// Stack slots.
/// Allocate a new stack slot.
pub fn make_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
let ss = StackSlot::new(self.stack_slots.len());
self.stack_slots.push(data);
ss
}
/// Iterate over all stack slots in function.
pub fn stack_slot_iter(&self) -> StackSlotIter {
StackSlotIter {
cur: 0,
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);
self.inst_order.push(InstNode {
prev: NO_INST,
next: NO_INST,
});
debug_assert_eq!(self.instructions.len(), self.inst_order.len());
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,
}
}
/// Append an instruction to a basic block.
pub fn append_inst(&mut self, ebb: Ebb, inst: Inst) {
let old_last = self[ebb].last_inst;
self.inst_order[inst.index()] = InstNode {
prev: old_last,
next: NO_INST,
};
if old_last == NO_INST {
assert!(self[ebb].first_inst == NO_INST);
self[ebb].first_inst = inst;
} else {
self.inst_order[old_last.index()].next = inst;
}
self[ebb].last_inst = inst;
}
/// Iterate through the instructions in `ebb`.
pub fn ebb_insts<'a>(&'a self, ebb: Ebb) -> EbbInsts<'a> {
EbbInsts {
func: self,
cur: self[ebb].first_inst,
}
}
// 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"),
}
}
}
// ====--------------------------------------------------------------------------------------====//
//
// Stack slot implementation.
//
// ====--------------------------------------------------------------------------------------====//
/// Contents of a stack slot.
#[derive(Debug)]
pub struct StackSlotData {
/// Size of stack slot in bytes.
pub size: u32,
}
impl StackSlotData {
/// Create a stack slot with the specified byte size.
pub fn new(size: u32) -> StackSlotData {
StackSlotData { size: size }
}
}
impl Display for StackSlotData {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "stack_slot {}", self.size)
}
}
/// Allow immutable access to stack slots via function indexing.
impl Index<StackSlot> for Function {
type Output = StackSlotData;
fn index<'a>(&'a self, ss: StackSlot) -> &'a StackSlotData {
&self.stack_slots[ss.index()]
}
}
/// Stack slot iterator visits all stack slots in a function, returning `StackSlot` references.
pub struct StackSlotIter {
cur: usize,
end: usize,
}
impl Iterator for StackSlotIter {
type Item = StackSlot;
fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.end {
let ss = StackSlot::new(self.cur);
self.cur += 1;
Some(ss)
} else {
None
}
}
}
// ====--------------------------------------------------------------------------------------====//
//
// 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,
/// First instruction in this block, or `NO_INST`.
first_inst: Inst,
/// Last instruction in this block, or `NO_INST`.
last_inst: Inst,
}
impl EbbData {
fn new() -> EbbData {
EbbData {
first_arg: NO_VALUE,
last_arg: NO_VALUE,
first_inst: NO_INST,
last_inst: NO_INST,
}
}
}
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()]
}
}
pub struct EbbInsts<'a> {
func: &'a Function,
cur: Inst,
}
impl<'a> Iterator for EbbInsts<'a> {
type Item = Inst;
fn next(&mut self) -> Option<Self::Item> {
let prev = self.cur;
if prev == NO_INST {
None
} else {
// Advance self.cur to the next inst.
self.cur = self.func.inst_order[prev.index()].next;
Some(prev)
}
}
}
/// 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()]
}
}
/// A node in a double linked list of instructions is a basic block.
#[derive(Debug)]
struct InstNode {
prev: Inst,
next: Inst,
}
// ====--------------------------------------------------------------------------------------====//
//
// 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() {
let mut func = Function::new();
let ss0 = func.make_stack_slot(StackSlotData::new(4));
let ss1 = func.make_stack_slot(StackSlotData::new(8));
assert_eq!(ss0.to_string(), "ss0");
assert_eq!(ss1.to_string(), "ss1");
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);
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.ebb_insts(ebb).next(), None);
let inst = func.make_inst(InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
});
func.append_inst(ebb, inst);
{
let mut ii = func.ebb_insts(ebb);
assert_eq!(ii.next(), Some(inst));
assert_eq!(ii.next(), None);
}
assert_eq!(func[ebb].first_inst, inst);
assert_eq!(func[ebb].last_inst, inst);
let inst2 = func.make_inst(InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
});
func.append_inst(ebb, inst2);
{
let mut ii = func.ebb_insts(ebb);
assert_eq!(ii.next(), Some(inst));
assert_eq!(ii.next(), Some(inst2));
assert_eq!(ii.next(), None);
}
assert_eq!(func[ebb].first_inst, inst);
assert_eq!(func[ebb].last_inst, inst2);
}
}