Convert the Branch and Jump instruction formats to value_list.

The Branch format also stores its fixed argument in the value list. This
requires the value pool to be passed to a few more functions.

Note that this actually makes the Branch and Jump variants of
InstructionData identical. The instruction format hashing does not yet
understand that all value operands are stored in the value list. We'll
fix that in a later patch.

Also convert IndirectCall, noting that Call and IndirectCall remain
separate instruction formats because they have different immediate
fields.
This commit is contained in:
Jakob Stoklund Olesen
2017-03-09 14:53:31 -08:00
parent 364b8e5f0a
commit 1135a89af9
9 changed files with 88 additions and 141 deletions

View File

@@ -42,15 +42,15 @@ ExtractLane = InstructionFormat(VALUE, ('lane', uimm8))
IntCompare = InstructionFormat(intcc, VALUE, VALUE) IntCompare = InstructionFormat(intcc, VALUE, VALUE)
FloatCompare = InstructionFormat(floatcc, VALUE, VALUE) FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
Jump = InstructionFormat(ebb, VARIABLE_ARGS, boxed_storage=True) Jump = InstructionFormat(ebb, VARIABLE_ARGS, value_list=True)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS, boxed_storage=True) Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS, value_list=True)
BranchTable = InstructionFormat(VALUE, jump_table) BranchTable = InstructionFormat(VALUE, jump_table)
Call = InstructionFormat( Call = InstructionFormat(
func_ref, VARIABLE_ARGS, multiple_results=True, value_list=True) func_ref, VARIABLE_ARGS, multiple_results=True, value_list=True)
IndirectCall = InstructionFormat( IndirectCall = InstructionFormat(
sig_ref, VALUE, VARIABLE_ARGS, sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True, boxed_storage=True) multiple_results=True, value_list=True)
Return = InstructionFormat(VARIABLE_ARGS, boxed_storage=True) Return = InstructionFormat(VARIABLE_ARGS, boxed_storage=True)
ReturnReg = InstructionFormat(VALUE, VARIABLE_ARGS, boxed_storage=True) ReturnReg = InstructionFormat(VALUE, VARIABLE_ARGS, boxed_storage=True)

View File

@@ -206,12 +206,19 @@ def gen_instruction_data_impl(fmt):
fmt.doc_comment('Get the controlling type variable operand.') fmt.doc_comment('Get the controlling type variable operand.')
with fmt.indented( with fmt.indented(
'pub fn typevar_operand(&self) -> Option<Value> {', '}'): 'pub fn typevar_operand(&self, pool: &ValueListPool) -> '
'Option<Value> {', '}'):
with fmt.indented('match *self {', '}'): with fmt.indented('match *self {', '}'):
for f in InstructionFormat.all_formats: for f in InstructionFormat.all_formats:
n = 'InstructionData::' + f.name n = 'InstructionData::' + f.name
if f.typevar_operand is None: if f.typevar_operand is None:
fmt.line(n + ' { .. } => None,') fmt.line(n + ' { .. } => None,')
elif f.has_value_list:
# We keep all arguments in a value list.
i = f.value_operands.index(f.typevar_operand)
fmt.line(
'{} {{ ref args, .. }} => '
'args.get({}, pool),'.format(n, i))
elif len(f.value_operands) == 1: elif len(f.value_operands) == 1:
# We have a single value operand called 'arg'. # We have a single value operand called 'arg'.
if f.boxed_storage: if f.boxed_storage:

View File

@@ -75,7 +75,7 @@ impl ControlFlowGraph {
for ebb in &func.layout { for ebb in &func.layout {
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
match func.dfg[inst].analyze_branch() { match func.dfg[inst].analyze_branch(&func.dfg.value_lists) {
BranchInfo::SingleDest(dest, _) => { BranchInfo::SingleDest(dest, _) => {
self.add_edge((ebb, inst), dest); self.add_edge((ebb, inst), dest);
} }
@@ -148,7 +148,7 @@ impl ControlFlowGraph {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ir::{Function, InstBuilder, Cursor, VariableArgs, types}; use ir::{Function, InstBuilder, Cursor, types};
#[test] #[test]
fn empty() { fn empty() {
@@ -197,12 +197,12 @@ mod tests {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.insert_ebb(ebb0); cur.insert_ebb(ebb0);
br_ebb0_ebb2 = dfg.ins(cur).brnz(cond, ebb2, VariableArgs::new()); br_ebb0_ebb2 = dfg.ins(cur).brnz(cond, ebb2, &[]);
jmp_ebb0_ebb1 = dfg.ins(cur).jump(ebb1, VariableArgs::new()); jmp_ebb0_ebb1 = dfg.ins(cur).jump(ebb1, &[]);
cur.insert_ebb(ebb1); cur.insert_ebb(ebb1);
br_ebb1_ebb1 = dfg.ins(cur).brnz(cond, ebb1, VariableArgs::new()); br_ebb1_ebb1 = dfg.ins(cur).brnz(cond, ebb1, &[]);
jmp_ebb1_ebb2 = dfg.ins(cur).jump(ebb2, VariableArgs::new()); jmp_ebb1_ebb2 = dfg.ins(cur).jump(ebb2, &[]);
cur.insert_ebb(ebb2); cur.insert_ebb(ebb2);
} }

View File

@@ -209,7 +209,7 @@ impl DominatorTree {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use ir::{Function, InstBuilder, Cursor, VariableArgs, types}; use ir::{Function, InstBuilder, Cursor, types};
use cfg::ControlFlowGraph; use cfg::ControlFlowGraph;
#[test] #[test]
@@ -238,14 +238,14 @@ mod test {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.insert_ebb(ebb3); cur.insert_ebb(ebb3);
jmp_ebb3_ebb1 = dfg.ins(cur).jump(ebb1, VariableArgs::new()); jmp_ebb3_ebb1 = dfg.ins(cur).jump(ebb1, &[]);
cur.insert_ebb(ebb1); cur.insert_ebb(ebb1);
br_ebb1_ebb0 = dfg.ins(cur).brnz(cond, ebb0, VariableArgs::new()); br_ebb1_ebb0 = dfg.ins(cur).brnz(cond, ebb0, &[]);
jmp_ebb1_ebb2 = dfg.ins(cur).jump(ebb2, VariableArgs::new()); jmp_ebb1_ebb2 = dfg.ins(cur).jump(ebb2, &[]);
cur.insert_ebb(ebb2); cur.insert_ebb(ebb2);
dfg.ins(cur).jump(ebb0, VariableArgs::new()); dfg.ins(cur).jump(ebb0, &[]);
cur.insert_ebb(ebb0); cur.insert_ebb(ebb0);
} }

View File

@@ -196,12 +196,14 @@ pub enum InstructionData {
Jump { Jump {
opcode: Opcode, opcode: Opcode,
ty: Type, ty: Type,
data: Box<JumpData>, destination: Ebb,
args: ValueList,
}, },
Branch { Branch {
opcode: Opcode, opcode: Opcode,
ty: Type, ty: Type,
data: Box<BranchData>, destination: Ebb,
args: ValueList,
}, },
BranchTable { BranchTable {
opcode: Opcode, opcode: Opcode,
@@ -220,7 +222,8 @@ pub enum InstructionData {
opcode: Opcode, opcode: Opcode,
ty: Type, ty: Type,
second_result: PackedOption<Value>, second_result: PackedOption<Value>,
data: Box<IndirectCallData>, sig_ref: SigRef,
args: ValueList,
}, },
Return { Return {
opcode: Opcode, opcode: Opcode,
@@ -255,9 +258,10 @@ impl VariableArgs {
self.0.is_empty() self.0.is_empty()
} }
/// Convert this to a value list in `pool`. /// Convert this to a value list in `pool` with `fixed` prepended.
pub fn into_value_list(self, pool: &mut ValueListPool) -> ValueList { pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {
let mut vlist = ValueList::default(); let mut vlist = ValueList::default();
vlist.extend(fixed.iter().cloned(), pool);
vlist.extend(self.0, pool); vlist.extend(self.0, pool);
vlist vlist
} }
@@ -327,85 +331,6 @@ impl Display for TernaryOverflowData {
} }
} }
/// Payload data for jump instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed `InstructionData` size.
#[derive(Clone, Debug)]
pub struct JumpData {
/// Jump destination EBB.
pub destination: Ebb,
/// Arguments passed to destination EBB.
pub varargs: VariableArgs,
}
impl Display for JumpData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.varargs.is_empty() {
write!(f, "{}", self.destination)
} else {
write!(f, "{}({})", self.destination, self.varargs)
}
}
}
/// Payload data for branch instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed `InstructionData` size.
#[derive(Clone, Debug)]
pub struct BranchData {
/// Value argument controlling the branch.
pub arg: Value,
/// Branch destination EBB.
pub destination: Ebb,
/// Arguments passed to destination EBB.
pub varargs: VariableArgs,
}
impl BranchData {
/// Get references to the arguments.
pub fn arguments(&self) -> [&[Value]; 2] {
[ref_slice(&self.arg), &self.varargs]
}
/// Get mutable references to the arguments.
pub fn arguments_mut(&mut self) -> [&mut [Value]; 2] {
[ref_slice_mut(&mut self.arg), &mut self.varargs]
}
}
impl Display for BranchData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}, {}", self.arg, self.destination)?;
if !self.varargs.is_empty() {
write!(f, "({})", self.varargs)?;
}
Ok(())
}
}
/// Payload of an indirect call instruction.
#[derive(Clone, Debug)]
pub struct IndirectCallData {
/// Callee function.
pub arg: Value,
/// Signature of the callee function.
pub sig_ref: SigRef,
/// Dynamically sized array containing call argument values.
pub varargs: VariableArgs,
}
impl IndirectCallData {
/// Get references to the arguments.
pub fn arguments(&self) -> [&[Value]; 2] {
[ref_slice(&self.arg), &self.varargs]
}
/// Get mutable references to the arguments.
pub fn arguments_mut(&mut self) -> [&mut [Value]; 2] {
[ref_slice_mut(&mut self.arg), &mut self.varargs]
}
}
/// Payload of a return instruction. /// Payload of a return instruction.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ReturnData { pub struct ReturnData {
@@ -467,13 +392,13 @@ impl InstructionData {
/// ///
/// Any instruction that can transfer control to another EBB reveals its possible destinations /// Any instruction that can transfer control to another EBB reveals its possible destinations
/// here. /// here.
pub fn analyze_branch<'a>(&'a self) -> BranchInfo<'a> { pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> {
match self { match self {
&InstructionData::Jump { ref data, .. } => { &InstructionData::Jump { destination, ref args, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs) BranchInfo::SingleDest(destination, &args.as_slice(pool))
} }
&InstructionData::Branch { ref data, .. } => { &InstructionData::Branch { destination, ref args, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs) BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..])
} }
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table), &InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
_ => BranchInfo::NotABranch, _ => BranchInfo::NotABranch,
@@ -488,8 +413,8 @@ impl InstructionData {
&InstructionData::Call { func_ref, ref args, .. } => { &InstructionData::Call { func_ref, ref args, .. } => {
CallInfo::Direct(func_ref, &args.as_slice(pool)) CallInfo::Direct(func_ref, &args.as_slice(pool))
} }
&InstructionData::IndirectCall { ref data, .. } => { &InstructionData::IndirectCall { sig_ref, ref args, .. } => {
CallInfo::Indirect(data.sig_ref, &data.varargs) CallInfo::Indirect(sig_ref, &args.as_slice(pool))
} }
_ => CallInfo::NotACall, _ => CallInfo::NotACall,
} }
@@ -507,7 +432,7 @@ impl InstructionData {
} else if constraints.requires_typevar_operand() { } else if constraints.requires_typevar_operand() {
// Not all instruction formats have a designated operand, but in that case // Not all instruction formats have a designated operand, but in that case
// `requires_typevar_operand()` should never be true. // `requires_typevar_operand()` should never be true.
dfg.value_type(self.typevar_operand() dfg.value_type(self.typevar_operand(&dfg.value_lists)
.expect("Instruction format doesn't have a designated operand, bad opcode.")) .expect("Instruction format doesn't have a designated operand, bad opcode."))
} else { } else {
// For locality of reference, we prefer to get the controlling type variable from // For locality of reference, we prefer to get the controlling type variable from

View File

@@ -217,7 +217,7 @@ impl LiveValueTracker {
-> (&[LiveValue], &[LiveValue]) { -> (&[LiveValue], &[LiveValue]) {
// Save a copy of the live values before any branches or jumps that could be somebody's // Save a copy of the live values before any branches or jumps that could be somebody's
// immediate dominator. // immediate dominator.
match dfg[inst].analyze_branch() { match dfg[inst].analyze_branch(&dfg.value_lists) {
BranchInfo::NotABranch => {} BranchInfo::NotABranch => {}
_ => self.save_idom_live_set(inst), _ => self.save_idom_live_set(inst),
} }

View File

@@ -225,6 +225,7 @@ fn write_instruction(w: &mut Write,
} }
// Then the operands, depending on format. // Then the operands, depending on format.
let pool = &func.dfg.value_lists;
use ir::instructions::InstructionData::*; use ir::instructions::InstructionData::*;
match func.dfg[inst] { match func.dfg[inst] {
Nullary { .. } => writeln!(w, ""), Nullary { .. } => writeln!(w, ""),
@@ -244,8 +245,28 @@ fn write_instruction(w: &mut Write,
ExtractLane { lane, arg, .. } => writeln!(w, " {}, {}", arg, lane), ExtractLane { lane, arg, .. } => writeln!(w, " {}, {}", arg, lane),
IntCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]), IntCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]),
FloatCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]), FloatCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]),
Jump { ref data, .. } => writeln!(w, " {}", data), Jump { destination, ref args, .. } => {
Branch { ref data, .. } => writeln!(w, " {}", data), if args.is_empty() {
writeln!(w, " {}", destination)
} else {
writeln!(w,
" {}({})",
destination,
DisplayValues(args.as_slice(pool)))
}
}
Branch { destination, ref args, .. } => {
let args = args.as_slice(pool);
if args.len() == 1 {
writeln!(w, " {}, {}", args[0], destination)
} else {
writeln!(w,
" {}, {}({})",
args[0],
destination,
DisplayValues(&args[1..]))
}
}
BranchTable { arg, table, .. } => writeln!(w, " {}, {}", arg, table), BranchTable { arg, table, .. } => writeln!(w, " {}, {}", arg, table),
Call { func_ref, ref args, .. } => { Call { func_ref, ref args, .. } => {
writeln!(w, writeln!(w,
@@ -253,8 +274,13 @@ fn write_instruction(w: &mut Write,
func_ref, func_ref,
DisplayValues(args.as_slice(&func.dfg.value_lists))) DisplayValues(args.as_slice(&func.dfg.value_lists)))
} }
IndirectCall { ref data, .. } => { IndirectCall { sig_ref, ref args, .. } => {
writeln!(w, " {}, {}({})", data.sig_ref, data.arg, data.varargs) let args = args.as_slice(pool);
writeln!(w,
" {}, {}({})",
sig_ref,
args[0],
DisplayValues(&args[1..]))
} }
Return { ref data, .. } => { Return { ref data, .. } => {
if data.varargs.is_empty() { if data.varargs.is_empty() {

View File

@@ -15,8 +15,7 @@ use cretonne::ir::types::VOID;
use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64}; use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs, use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
TernaryOverflowData, JumpData, BranchData, IndirectCallData, TernaryOverflowData, ReturnData, ReturnRegData};
ReturnData, ReturnRegData};
use cretonne::isa::{self, TargetIsa, Encoding}; use cretonne::isa::{self, TargetIsa, Encoding};
use cretonne::settings; use cretonne::settings;
use testfile::{TestFile, Details, Comment}; use testfile::{TestFile, Details, Comment};
@@ -200,24 +199,22 @@ impl<'a> Context<'a> {
self.map.rewrite_values(&mut data.args, loc)?; self.map.rewrite_values(&mut data.args, loc)?;
} }
InstructionData::Jump { ref mut data, .. } => { InstructionData::Jump { ref mut destination, ref mut args, .. } => {
self.map.rewrite_ebb(&mut data.destination, loc)?; self.map.rewrite_ebb(destination, loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?; self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
} }
InstructionData::Branch { ref mut data, .. } => { InstructionData::Branch { ref mut destination, ref mut args, .. } => {
self.map.rewrite_value(&mut data.arg, loc)?; self.map.rewrite_ebb(destination, loc)?;
self.map.rewrite_ebb(&mut data.destination, loc)?; self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?;
} }
InstructionData::Call { ref mut args, .. } => { InstructionData::Call { ref mut args, .. } => {
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?; self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
} }
InstructionData::IndirectCall { ref mut data, .. } => { InstructionData::IndirectCall { ref mut args, .. } => {
self.map.rewrite_value(&mut data.arg, loc)?; self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?;
} }
InstructionData::Return { ref mut data, .. } => { InstructionData::Return { ref mut data, .. } => {
@@ -1209,7 +1206,7 @@ impl<'a> Parser<'a> {
// TBD: If it is defined in another block, the type should have been specified // TBD: If it is defined in another block, the type should have been specified
// explicitly. It is unfortunate that the correctness of IL depends on the // explicitly. It is unfortunate that the correctness of IL depends on the
// layout of the blocks. // layout of the blocks.
let ctrl_src_value = inst_data.typevar_operand() let ctrl_src_value = inst_data.typevar_operand(&ctx.function.dfg.value_lists)
.expect("Constraints <-> Format inconsistency"); .expect("Constraints <-> Format inconsistency");
ctx.function.dfg.value_type(match ctx.map.get_value(ctrl_src_value) { ctx.function.dfg.value_type(match ctx.map.get_value(ctrl_src_value) {
Some(v) => v, Some(v) => v,
@@ -1429,10 +1426,8 @@ impl<'a> Parser<'a> {
InstructionData::Jump { InstructionData::Jump {
opcode: opcode, opcode: opcode,
ty: VOID, ty: VOID,
data: Box::new(JumpData { destination: ebb_num,
destination: ebb_num, args: args.into_value_list(&[], &mut ctx.function.dfg.value_lists),
varargs: args,
}),
} }
} }
InstructionFormat::Branch => { InstructionFormat::Branch => {
@@ -1443,11 +1438,8 @@ impl<'a> Parser<'a> {
InstructionData::Branch { InstructionData::Branch {
opcode: opcode, opcode: opcode,
ty: VOID, ty: VOID,
data: Box::new(BranchData { destination: ebb_num,
arg: ctrl_arg, args: args.into_value_list(&[ctrl_arg], &mut ctx.function.dfg.value_lists),
destination: ebb_num,
varargs: args,
}),
} }
} }
InstructionFormat::InsertLane => { InstructionFormat::InsertLane => {
@@ -1511,7 +1503,7 @@ impl<'a> Parser<'a> {
ty: VOID, ty: VOID,
second_result: None.into(), second_result: None.into(),
func_ref: func_ref, func_ref: func_ref,
args: args.into_value_list(&mut ctx.function.dfg.value_lists), args: args.into_value_list(&[], &mut ctx.function.dfg.value_lists),
} }
} }
InstructionFormat::IndirectCall => { InstructionFormat::IndirectCall => {
@@ -1526,11 +1518,8 @@ impl<'a> Parser<'a> {
opcode: opcode, opcode: opcode,
ty: VOID, ty: VOID,
second_result: None.into(), second_result: None.into(),
data: Box::new(IndirectCallData { sig_ref: sig_ref,
sig_ref: sig_ref, args: args.into_value_list(&[callee], &mut ctx.function.dfg.value_lists),
arg: callee,
varargs: args,
}),
} }
} }
InstructionFormat::Return => { InstructionFormat::Return => {

View File

@@ -59,7 +59,7 @@ impl<'a> CFGPrinter<'a> {
// Add all outgoing branch instructions to the label. // Add all outgoing branch instructions to the label.
for inst in self.func.layout.ebb_insts(ebb) { for inst in self.func.layout.ebb_insts(ebb) {
let idata = &self.func.dfg[inst]; let idata = &self.func.dfg[inst];
match idata.analyze_branch() { match idata.analyze_branch(&self.func.dfg.value_lists) {
BranchInfo::SingleDest(dest, _) => { BranchInfo::SingleDest(dest, _) => {
write!(w, " | <{}>{} {}", inst, idata.opcode(), dest)? write!(w, " | <{}>{} {}", inst, idata.opcode(), dest)?
} }