Implement value lists.

Values that are defined together are represented as a singly linked list. These
lists appear in:

- Instructions with multiple result values. The first result value is special, and the
  following results form a linked list of Def extended_value table entries.

- EBB arguments are represented as a linked list of Argument extended_value
  table entries. The EbbData struct has pointers to the first and last argument
  to allow fast insertion at both ends.

Add a Values iterator type whicih can enumerate both kinds of value lists.
This commit is contained in:
Jakob Stoklund Olesen
2016-05-02 15:25:43 -07:00
parent ab50f17412
commit 24970593ac

View File

@@ -3,6 +3,7 @@
use types::{Type, FunctionName, Signature}; use types::{Type, FunctionName, Signature};
use immediates::*; use immediates::*;
use std::default::Default;
use std::fmt::{self, Display, Formatter, Write}; use std::fmt::{self, Display, Formatter, Write};
use std::ops::Index; use std::ops::Index;
use std::u32; use std::u32;
@@ -81,12 +82,20 @@ pub struct StackSlotData {
} }
/// Contents of an extended basic block. /// 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)] #[derive(Debug)]
pub struct EbbData { pub struct EbbData {
/// Arguments for this extended basic block. These values dominate everything in the EBB. /// First argument to this EBB, or `NO_VALUE` if the block has no arguments.
/// All branches to this EBB must provide matching arguments, and the arguments to the entry ///
/// EBB must match the function arguments. /// The arguments are all ValueData::Argument entries that form a linked list from `first_arg`
pub arguments: Vec<Value>, /// to `last_arg`.
first_arg: Value,
/// Last argument to this EBB, or `NO_VALUE` if the block has no arguments.
last_arg: Value,
} }
/// Contents on an instruction. /// Contents on an instruction.
@@ -132,12 +141,11 @@ pub enum InstructionData {
/// Payload of a call instruction. /// Payload of a call instruction.
#[derive(Debug)] #[derive(Debug)]
pub struct CallData { pub struct CallData {
// Number of result values. /// Second result value for a call producing multiple return values.
results: u8, second_result: Value,
// Dynamically sized array containing `results-1` result values (not including the first value) // Dynamically sized array containing call argument values.
// followed by the argument values. arguments: Vec<Value>,
values: Vec<Value>,
} }
@@ -233,7 +241,10 @@ impl Display for Ebb {
impl EbbData { impl EbbData {
fn new() -> EbbData { fn new() -> EbbData {
EbbData { arguments: Vec::new() } EbbData {
first_arg: NO_VALUE,
last_arg: NO_VALUE,
}
} }
} }
@@ -284,6 +295,9 @@ enum ExpandedValue {
// This value is described in the extended value table. // This value is described in the extended value table.
Table(usize), Table(usize),
// This is NO_VALUE.
None,
} }
impl Value { impl Value {
@@ -302,6 +316,9 @@ impl Value {
// Expand the internal representation into something useful. // Expand the internal representation into something useful.
fn expand(&self) -> ExpandedValue { fn expand(&self) -> ExpandedValue {
use self::ExpandedValue::*; use self::ExpandedValue::*;
if *self == NO_VALUE {
return None;
}
let index = (self.0 / 2) as usize; let index = (self.0 / 2) as usize;
if self.0 % 2 == 0 { if self.0 % 2 == 0 {
Direct(Inst::new(index)) Direct(Inst::new(index))
@@ -311,13 +328,20 @@ impl Value {
} }
} }
impl Default for Value {
fn default() -> Value {
NO_VALUE
}
}
/// Display a `Value` reference as "v7" or "v2x". /// Display a `Value` reference as "v7" or "v2x".
impl Display for Value { impl Display for Value {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::ExpandedValue::*; use self::ExpandedValue::*;
match self.expand() { match self.expand() {
Direct(i) => write!(fmt, "v{}", i.0), Direct(i) => write!(fmt, "v{}", i.0),
Table(i) => write!(fmt, "v{}x", i), Table(i) => write!(fmt, "vx{}", i),
None => write!(fmt, "NO_VALUE"),
} }
} }
} }
@@ -326,25 +350,67 @@ impl Display for Value {
// Other values have an entry in the value table. // Other values have an entry in the value table.
#[derive(Debug)] #[derive(Debug)]
enum ValueData { enum ValueData {
// An unused entry in the value table. No instruction should be defining or using this value.
Unused,
// Value is defined by an instruction, but it is not the first result. // Value is defined by an instruction, but it is not the first result.
Def { Def {
ty: Type, ty: Type,
num: u8,
def: Inst, def: Inst,
next: Value, // Next result defined by `def`.
}, },
// Value is an EBB argument. // Value is an EBB argument.
Argument { Argument {
ty: Type, ty: Type,
num: u8,
ebb: Ebb, 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)
}
}
impl InstructionData { impl InstructionData {
/// Create data for a call instruction.
pub fn call(opc: Opcode, return_type: Type) -> InstructionData {
InstructionData::Call {
opcode: opc,
ty: return_type,
data: Box::new(CallData {
second_result: NO_VALUE,
arguments: Vec::new(),
}),
}
}
/// Get the opcode of this instruction. /// Get the opcode of this instruction.
pub fn opcode(&self) -> Opcode { pub fn opcode(&self) -> Opcode {
use self::InstructionData::*; use self::InstructionData::*;
@@ -370,6 +436,31 @@ impl InstructionData {
Call { ty, .. } => ty, Call { ty, .. } => ty,
} }
} }
/// Second result value, if any.
fn second_result(&self) -> Option<Value> {
use self::InstructionData::*;
match *self {
Nullary { .. } => None,
Unary { .. } => None,
UnaryImm { .. } => None,
Binary { .. } => None,
BinaryImm { .. } => None,
Call { ref data, .. } => Some(data.second_result),
}
}
fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value> {
use self::InstructionData::*;
match *self {
Nullary { .. } => None,
Unary { .. } => None,
UnaryImm { .. } => None,
Binary { .. } => None,
BinaryImm { .. } => None,
Call { ref mut data, .. } => Some(&mut data.second_result),
}
}
} }
impl Function { impl Function {
@@ -396,6 +487,8 @@ impl Function {
&self.signature &self.signature
} }
// Stack slots.
/// Allocate a new stack slot. /// Allocate a new stack slot.
pub fn make_stack_slot(&mut self, data: StackSlotData) -> StackSlot { pub fn make_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
let ss = StackSlot::new(self.stack_slots.len()); let ss = StackSlot::new(self.stack_slots.len());
@@ -411,14 +504,71 @@ impl Function {
} }
} }
// Instructions.
/// Create a new instruction. /// Create a new instruction.
pub fn make_inst(&mut self, data: InstructionData) -> Inst { ///
let iref = Inst::new(self.instructions.len()); /// The instruction is allowed to produce at most one result as indicated by `data.ty`. Use
/// `make_multi_inst()` to create instructions with multiple results.
pub fn make_inst(&mut self, data: InstructionData, extra_result_types: &[Type]) -> Inst {
let inst = Inst::new(self.instructions.len());
self.instructions.push(data); self.instructions.push(data);
// FIXME: Allocate extended value table entries if needed. inst
iref
} }
/// Make an instruction that may produce multiple results.
///
/// The type of the first result is `data.ty`. If the instruction generates more than one
/// result, additional result types are in `extra_result_types`.
///
/// Not all instruction formats can represent multiple result values. This function will panic
/// if the format of `data` is insufficient.
pub fn make_multi_inst(&mut self, data: InstructionData, extra_result_types: &[Type]) -> Inst {
let inst = self.make_inst(data);
if !extra_result_types.is_empty() {
// 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;
for ty in extra_result_types.into_iter().rev() {
head = self.make_value(ValueData::Def {
ty: *ty,
def: inst,
next: head,
});
}
// Update the second_result pointer in `inst`.
if let Some(second_result_ref) = self.instructions[inst.index()].second_result_mut() {
*second_result_ref = head;
} else {
panic!("Instruction format doesn't allow multiple results.");
}
}
inst
}
/// 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: Value::new_direct(inst),
}
}
// Basic blocks
/// Create a new basic block. /// Create a new basic block.
pub fn make_ebb(&mut self) -> Ebb { pub fn make_ebb(&mut self) -> Ebb {
let ebb = Ebb::new(self.extended_basic_blocks.len()); let ebb = Ebb::new(self.extended_basic_blocks.len());
@@ -426,6 +576,60 @@ impl Function {
ebb 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()]
}
/// 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. /// Get the type of a value.
pub fn value_type(&self, v: Value) -> Type { pub fn value_type(&self, v: Value) -> Type {
use self::ExpandedValue::*; use self::ExpandedValue::*;
@@ -434,11 +638,11 @@ impl Function {
Direct(i) => self[i].first_type(), Direct(i) => self[i].first_type(),
Table(i) => { Table(i) => {
match self.extended_values[i] { match self.extended_values[i] {
Unused => panic!("Can't get type of Unused value {}", v),
Def { ty, .. } => ty, Def { ty, .. } => ty,
Argument { ty, .. } => ty, Argument { ty, .. } => ty,
} }
} }
None => panic!("NO_VALUE has no type"),
} }
} }
} }
@@ -457,7 +661,7 @@ mod tests {
opcode: Opcode::Iconst, opcode: Opcode::Iconst,
ty: types::I32, ty: types::I32,
}; };
let inst = func.make_inst(idata); let inst = func.make_inst(idata, &[]);
assert_eq!(inst.to_string(), "inst0"); assert_eq!(inst.to_string(), "inst0");
// Immutable reference resolution. // Immutable reference resolution.
@@ -466,6 +670,21 @@ mod tests {
assert_eq!(ins.first_type(), types::I32); assert_eq!(ins.first_type(), types::I32);
} }
#[test]
fn multiple_results() {
use types::*;
let mut func = Function::new();
let idata = InstructionData::call(Opcode::Vconst, I64);
let inst = func.make_inst(idata, &[I8, F64]);
assert_eq!(inst.to_string(), "inst0");
let results: Vec<Value> = func.inst_results(inst).collect();
assert_eq!(results.len(), 3);
assert_eq!(func.value_type(results[0]), I64);
assert_eq!(func.value_type(results[1]), I8);
assert_eq!(func.value_type(results[2]), F64);
}
#[test] #[test]
fn stack_slot() { fn stack_slot() {
let mut func = Function::new(); let mut func = Function::new();
@@ -479,4 +698,28 @@ mod tests {
assert_eq!(func[ss1].size, 8); assert_eq!(func[ss1].size, 8);
} }
#[test]
fn ebb() {
let mut func = Function::new();
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);
}
}
} }