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:
@@ -3,6 +3,7 @@
|
||||
|
||||
use types::{Type, FunctionName, Signature};
|
||||
use immediates::*;
|
||||
use std::default::Default;
|
||||
use std::fmt::{self, Display, Formatter, Write};
|
||||
use std::ops::Index;
|
||||
use std::u32;
|
||||
@@ -81,12 +82,20 @@ pub struct StackSlotData {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Arguments for this extended basic block. These values 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.
|
||||
pub arguments: Vec<Value>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// Contents on an instruction.
|
||||
@@ -132,12 +141,11 @@ pub enum InstructionData {
|
||||
/// Payload of a call instruction.
|
||||
#[derive(Debug)]
|
||||
pub struct CallData {
|
||||
// Number of result values.
|
||||
results: u8,
|
||||
/// Second result value for a call producing multiple return values.
|
||||
second_result: Value,
|
||||
|
||||
// Dynamically sized array containing `results-1` result values (not including the first value)
|
||||
// followed by the argument values.
|
||||
values: Vec<Value>,
|
||||
// Dynamically sized array containing call argument values.
|
||||
arguments: Vec<Value>,
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +241,10 @@ impl Display for Ebb {
|
||||
|
||||
impl 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.
|
||||
Table(usize),
|
||||
|
||||
// This is NO_VALUE.
|
||||
None,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@@ -302,6 +316,9 @@ impl Value {
|
||||
// Expand the internal representation into something useful.
|
||||
fn expand(&self) -> ExpandedValue {
|
||||
use self::ExpandedValue::*;
|
||||
if *self == NO_VALUE {
|
||||
return None;
|
||||
}
|
||||
let index = (self.0 / 2) as usize;
|
||||
if self.0 % 2 == 0 {
|
||||
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".
|
||||
impl Display for Value {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
use self::ExpandedValue::*;
|
||||
match self.expand() {
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
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.
|
||||
Def {
|
||||
ty: Type,
|
||||
num: u8,
|
||||
def: Inst,
|
||||
next: Value, // Next result defined by `def`.
|
||||
},
|
||||
|
||||
// Value is an EBB argument.
|
||||
Argument {
|
||||
ty: Type,
|
||||
num: u8,
|
||||
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 {
|
||||
/// 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.
|
||||
pub fn opcode(&self) -> Opcode {
|
||||
use self::InstructionData::*;
|
||||
@@ -370,6 +436,31 @@ impl InstructionData {
|
||||
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 {
|
||||
@@ -396,6 +487,8 @@ impl Function {
|
||||
&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());
|
||||
@@ -411,14 +504,71 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
// Instructions.
|
||||
|
||||
/// 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);
|
||||
// FIXME: Allocate extended value table entries if needed.
|
||||
iref
|
||||
inst
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn make_ebb(&mut self) -> Ebb {
|
||||
let ebb = Ebb::new(self.extended_basic_blocks.len());
|
||||
@@ -426,6 +576,60 @@ impl Function {
|
||||
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.
|
||||
pub fn value_type(&self, v: Value) -> Type {
|
||||
use self::ExpandedValue::*;
|
||||
@@ -434,11 +638,11 @@ impl Function {
|
||||
Direct(i) => self[i].first_type(),
|
||||
Table(i) => {
|
||||
match self.extended_values[i] {
|
||||
Unused => panic!("Can't get type of Unused value {}", v),
|
||||
Def { ty, .. } => ty,
|
||||
Argument { ty, .. } => ty,
|
||||
}
|
||||
}
|
||||
None => panic!("NO_VALUE has no type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,7 +661,7 @@ mod tests {
|
||||
opcode: Opcode::Iconst,
|
||||
ty: types::I32,
|
||||
};
|
||||
let inst = func.make_inst(idata);
|
||||
let inst = func.make_inst(idata, &[]);
|
||||
assert_eq!(inst.to_string(), "inst0");
|
||||
|
||||
// Immutable reference resolution.
|
||||
@@ -466,6 +670,21 @@ mod tests {
|
||||
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]
|
||||
fn stack_slot() {
|
||||
let mut func = Function::new();
|
||||
@@ -479,4 +698,28 @@ mod tests {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user