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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user