Use value lists for call arguments.

Add a new kind of instruction format that keeps all of its value
arguments in a value list. These value lists are all allocated out of
the dfg.value_lists memory pool.

Instruction formats with the value_list property set store *all* of
their value arguments in a single value list. There is no distinction
between fixed arguments and variable arguments.

Change the Call instruction format to use the value list representation
for its arguments.

This change is only the beginning. The intent is to eliminate the
boxed_storage instruction formats completely. Value lists use less
memory, and when the transition is complete, InstructionData will have a
trivial Drop implementation.
This commit is contained in:
Jakob Stoklund Olesen
2017-03-09 13:10:27 -08:00
parent e21bf444dc
commit a6c2cc71df
12 changed files with 142 additions and 44 deletions

View File

@@ -54,6 +54,11 @@ use entity_map::EntityRef;
///
/// All of the list methods that take a pool reference must be given the same pool reference every
/// time they are called. Otherwise data structures will be corrupted.
///
/// Entity lists can be cloned, but that operation should only be used as part of cloning the whole
/// function they belong to. *Cloning an entity list does not allocate new memory for the clone*.
/// It creates an alias of the same memory.
#[derive(Clone, Debug)]
pub struct EntityList<T: EntityRef> {
index: u32,
unused: PhantomData<T>,
@@ -70,6 +75,7 @@ impl<T: EntityRef> Default for EntityList<T> {
}
/// A memory pool for storing lists of `T`.
#[derive(Clone)]
pub struct ListPool<T: EntityRef> {
// The main array containing the lists.
data: Vec<T>,

View File

@@ -5,7 +5,7 @@
use ir::{types, instructions};
use ir::{InstructionData, DataFlowGraph, Cursor};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, VariableArgs, SigRef, FuncRef};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, VariableArgs, SigRef, FuncRef, ValueList};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector};
use ir::condcodes::{IntCC, FloatCC};
@@ -21,6 +21,7 @@ pub trait InstBuilderBase<'f>: Sized {
/// Get an immutable reference to the data flow graph that will hold the constructed
/// instructions.
fn data_flow_graph(&self) -> &DataFlowGraph;
fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph;
/// Insert a simple instruction and return a reference to it.
///
@@ -76,6 +77,10 @@ impl<'c, 'fc, 'fd> InstBuilderBase<'fd> for InsertBuilder<'c, 'fc, 'fd> {
self.dfg
}
fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
self.dfg
}
fn simple_instruction(self, data: InstructionData) -> (Inst, &'fd mut DataFlowGraph) {
let inst = self.dfg.make_inst(data);
self.pos.insert_inst(inst);
@@ -129,6 +134,10 @@ impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> {
self.dfg
}
fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
self.dfg
}
fn simple_instruction(self, data: InstructionData) -> (Inst, &'f mut DataFlowGraph) {
// The replacement instruction cannot generate multiple results, so verify that the old
// instruction's secondary results have been detached.

View File

@@ -1,6 +1,6 @@
//! Data flow graph tracking Instructions, Values, and EBBs.
use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef};
use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef, ValueListPool};
use ir::entities::ExpandedValue;
use ir::instructions::{Opcode, InstructionData, CallInfo};
use ir::extfunc::ExtFuncData;
@@ -24,7 +24,10 @@ pub struct DataFlowGraph {
/// Data about all of the instructions in the function, including opcodes and operands.
/// The instructions in this map are not in program order. That is tracked by `Layout`, along
/// with the EBB containing each instruction.
insts: EntityMap<Inst, InstructionData>,
pub insts: EntityMap<Inst, InstructionData>,
/// Memory pool of value lists referenced by instructions in `insts`.
pub value_lists: ValueListPool,
/// Extended basic blocks in the function and their arguments.
/// This map is not in program order. That is handled by `Layout`, and so is the sequence of
@@ -56,6 +59,7 @@ impl DataFlowGraph {
pub fn new() -> DataFlowGraph {
DataFlowGraph {
insts: EntityMap::new(),
value_lists: ValueListPool::new(),
ebbs: EntityMap::new(),
extended_values: Vec::new(),
signatures: EntityMap::new(),
@@ -429,7 +433,7 @@ impl DataFlowGraph {
/// Get the call signature of a direct or indirect call instruction.
/// Returns `None` if `inst` is not a call instruction.
pub fn call_signature(&self, inst: Inst) -> Option<SigRef> {
match self.insts[inst].analyze_call() {
match self.insts[inst].analyze_call(&self.value_lists) {
CallInfo::NotACall => None,
CallInfo::Direct(f, _) => Some(self.ext_funcs[f].signature),
CallInfo::Indirect(s, _) => Some(s),

View File

@@ -16,8 +16,17 @@ use ir::condcodes::*;
use ir::types;
use ir::DataFlowGraph;
use ref_slice::*;
use entity_list;
use packed_option::PackedOption;
use ref_slice::{ref_slice, ref_slice_mut};
/// Some instructions use an external list of argument values because there is not enough space in
/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in
/// `dfg.value_lists`.
pub type ValueList = entity_list::EntityList<Value>;
/// Memory pool for holding value lists. See `ValueList`.
pub type ValueListPool = entity_list::ListPool<Value>;
// Include code generated by `lib/cretonne/meta/gen_instr.py`. This file contains:
//
@@ -204,7 +213,8 @@ pub enum InstructionData {
opcode: Opcode,
ty: Type,
second_result: PackedOption<Value>,
data: Box<CallData>,
func_ref: FuncRef,
args: ValueList,
},
IndirectCall {
opcode: Opcode,
@@ -244,6 +254,13 @@ impl VariableArgs {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Convert this to a value list in `pool`.
pub fn into_value_list(self, pool: &mut ValueListPool) -> ValueList {
let mut vlist = ValueList::default();
vlist.extend(self.0, pool);
vlist
}
}
// Coerce `VariableArgs` into a `&[Value]` slice.
@@ -364,16 +381,6 @@ impl Display for BranchData {
}
}
/// Payload of a call instruction.
#[derive(Clone, Debug)]
pub struct CallData {
/// Callee function.
pub func_ref: FuncRef,
/// Dynamically sized array containing call argument values.
pub varargs: VariableArgs,
}
/// Payload of an indirect call instruction.
#[derive(Clone, Debug)]
pub struct IndirectCallData {
@@ -434,10 +441,10 @@ impl ReturnRegData {
impl InstructionData {
/// Execute a closure once for each argument to this instruction.
/// See also the `arguments()` method.
pub fn each_arg<F>(&self, mut func: F)
pub fn each_arg<F>(&self, pool: &ValueListPool, mut func: F)
where F: FnMut(Value)
{
for part in &self.arguments() {
for part in &self.arguments(pool) {
for &arg in part.iter() {
func(arg);
}
@@ -446,10 +453,10 @@ impl InstructionData {
/// Execute a closure with a mutable reference to each argument to this instruction.
/// See also the `arguments_mut()` method.
pub fn each_arg_mut<F>(&mut self, mut func: F)
pub fn each_arg_mut<F>(&mut self, pool: &mut ValueListPool, mut func: F)
where F: FnMut(&mut Value)
{
for part in &mut self.arguments_mut() {
for part in &mut self.arguments_mut(pool) {
for arg in part.iter_mut() {
func(arg);
}
@@ -476,10 +483,10 @@ impl InstructionData {
/// Return information about a call instruction.
///
/// Any instruction that can call another function reveals its call signature here.
pub fn analyze_call<'a>(&'a self) -> CallInfo<'a> {
pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> {
match self {
&InstructionData::Call { ref data, .. } => {
CallInfo::Direct(data.func_ref, &data.varargs)
&InstructionData::Call { func_ref, ref args, .. } => {
CallInfo::Direct(func_ref, &args.as_slice(pool))
}
&InstructionData::IndirectCall { ref data, .. } => {
CallInfo::Indirect(data.sig_ref, &data.varargs)

View File

@@ -20,7 +20,7 @@ pub use ir::funcname::FunctionName;
pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ExtFuncData};
pub use ir::types::Type;
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::instructions::{Opcode, InstructionData, VariableArgs};
pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool};
pub use ir::stackslot::StackSlotData;
pub use ir::jumptable::JumpTableData;
pub use ir::valueloc::{ValueLoc, ArgumentLoc};

View File

@@ -299,7 +299,8 @@ impl<'a> Context<'a> {
}
ConstraintKind::Tied(arg_index) => {
// This def must use the same register as a fixed instruction argument.
let loc = locations[dfg[inst].arguments()[0][arg_index as usize]];
let arg = dfg[inst].arguments(&dfg.value_lists)[0][arg_index as usize];
let loc = locations[arg];
*locations.ensure(lv.value) = loc;
// Mark the reused register. It's not really clear if we support tied
// stack operands. We could do that for some Intel read-modify-write

View File

@@ -318,7 +318,7 @@ impl Liveness {
let mut operand_constraints =
recipe_constraints.get(recipe).map(|c| c.ins).unwrap_or(&[]).iter();
func.dfg[inst].each_arg(|arg| {
func.dfg[inst].each_arg(&func.dfg.value_lists, |arg| {
// Get the live range, create it as a dead range if necessary.
let lr = get_or_create(&mut self.ranges, arg, func, recipe_constraints);

View File

@@ -6,7 +6,7 @@
use ir::{Function, Ebb, Inst, Value, Type};
use isa::{TargetIsa, RegInfo};
use std::fmt::{Result, Error, Write};
use std::fmt::{self, Result, Error, Write};
use std::result;
/// Write `func` to `w` as equivalent text.
@@ -157,7 +157,7 @@ fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
// Write out any value aliases appearing in `inst`.
fn write_value_aliases(w: &mut Write, func: &Function, inst: Inst, indent: usize) -> Result {
for &arg in func.dfg[inst].arguments().iter().flat_map(|x| x.iter()) {
for &arg in func.dfg[inst].arguments(&func.dfg.value_lists).iter().flat_map(|x| x.iter()) {
let resolved = func.dfg.resolve_aliases(arg);
if resolved != arg {
writeln!(w, "{1:0$}{2} -> {3}", indent, "", arg, resolved)?;
@@ -247,7 +247,12 @@ fn write_instruction(w: &mut Write,
Jump { ref data, .. } => writeln!(w, " {}", data),
Branch { ref data, .. } => writeln!(w, " {}", data),
BranchTable { arg, table, .. } => writeln!(w, " {}, {}", arg, table),
Call { ref data, .. } => writeln!(w, " {}({})", data.func_ref, data.varargs),
Call { func_ref, ref args, .. } => {
writeln!(w,
" {}({})",
func_ref,
DisplayValues(args.as_slice(&func.dfg.value_lists)))
}
IndirectCall { ref data, .. } => {
writeln!(w, " {}, {}({})", data.sig_ref, data.arg, data.varargs)
}
@@ -268,6 +273,22 @@ fn write_instruction(w: &mut Write,
}
}
/// Displayable slice of values.
struct DisplayValues<'a>(&'a [Value]);
impl<'a> fmt::Display for DisplayValues<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result {
for (i, val) in self.0.iter().enumerate() {
if i == 0 {
write!(f, "{}", val)?;
} else {
write!(f, ", {}", val)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use ir::{Function, FunctionName, StackSlotData};