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)
FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
Jump = InstructionFormat(ebb, VARIABLE_ARGS, boxed_storage=True)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS, boxed_storage=True)
Jump = InstructionFormat(ebb, VARIABLE_ARGS, value_list=True)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS, value_list=True)
BranchTable = InstructionFormat(VALUE, jump_table)
Call = InstructionFormat(
func_ref, VARIABLE_ARGS, multiple_results=True, value_list=True)
IndirectCall = InstructionFormat(
sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True, boxed_storage=True)
multiple_results=True, value_list=True)
Return = InstructionFormat(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.')
with fmt.indented(
'pub fn typevar_operand(&self) -> Option<Value> {', '}'):
'pub fn typevar_operand(&self, pool: &ValueListPool) -> '
'Option<Value> {', '}'):
with fmt.indented('match *self {', '}'):
for f in InstructionFormat.all_formats:
n = 'InstructionData::' + f.name
if f.typevar_operand is 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:
# We have a single value operand called 'arg'.
if f.boxed_storage:

View File

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

View File

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

View File

@@ -196,12 +196,14 @@ pub enum InstructionData {
Jump {
opcode: Opcode,
ty: Type,
data: Box<JumpData>,
destination: Ebb,
args: ValueList,
},
Branch {
opcode: Opcode,
ty: Type,
data: Box<BranchData>,
destination: Ebb,
args: ValueList,
},
BranchTable {
opcode: Opcode,
@@ -220,7 +222,8 @@ pub enum InstructionData {
opcode: Opcode,
ty: Type,
second_result: PackedOption<Value>,
data: Box<IndirectCallData>,
sig_ref: SigRef,
args: ValueList,
},
Return {
opcode: Opcode,
@@ -255,9 +258,10 @@ impl VariableArgs {
self.0.is_empty()
}
/// Convert this to a value list in `pool`.
pub fn into_value_list(self, pool: &mut ValueListPool) -> ValueList {
/// Convert this to a value list in `pool` with `fixed` prepended.
pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {
let mut vlist = ValueList::default();
vlist.extend(fixed.iter().cloned(), pool);
vlist.extend(self.0, pool);
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.
#[derive(Clone, Debug)]
pub struct ReturnData {
@@ -467,13 +392,13 @@ impl InstructionData {
///
/// Any instruction that can transfer control to another EBB reveals its possible destinations
/// here.
pub fn analyze_branch<'a>(&'a self) -> BranchInfo<'a> {
pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> {
match self {
&InstructionData::Jump { ref data, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs)
&InstructionData::Jump { destination, ref args, .. } => {
BranchInfo::SingleDest(destination, &args.as_slice(pool))
}
&InstructionData::Branch { ref data, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs)
&InstructionData::Branch { destination, ref args, .. } => {
BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..])
}
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
_ => BranchInfo::NotABranch,
@@ -488,8 +413,8 @@ impl InstructionData {
&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)
&InstructionData::IndirectCall { sig_ref, ref args, .. } => {
CallInfo::Indirect(sig_ref, &args.as_slice(pool))
}
_ => CallInfo::NotACall,
}
@@ -507,7 +432,7 @@ impl InstructionData {
} else if constraints.requires_typevar_operand() {
// Not all instruction formats have a designated operand, but in that case
// `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."))
} else {
// For locality of reference, we prefer to get the controlling type variable from

View File

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

View File

@@ -225,6 +225,7 @@ fn write_instruction(w: &mut Write,
}
// Then the operands, depending on format.
let pool = &func.dfg.value_lists;
use ir::instructions::InstructionData::*;
match func.dfg[inst] {
Nullary { .. } => writeln!(w, ""),
@@ -244,8 +245,28 @@ fn write_instruction(w: &mut Write,
ExtractLane { lane, arg, .. } => writeln!(w, " {}, {}", arg, lane),
IntCompare { 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),
Branch { ref data, .. } => writeln!(w, " {}", data),
Jump { destination, ref args, .. } => {
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),
Call { func_ref, ref args, .. } => {
writeln!(w,
@@ -253,8 +274,13 @@ fn write_instruction(w: &mut Write,
func_ref,
DisplayValues(args.as_slice(&func.dfg.value_lists)))
}
IndirectCall { ref data, .. } => {
writeln!(w, " {}, {}({})", data.sig_ref, data.arg, data.varargs)
IndirectCall { sig_ref, ref args, .. } => {
let args = args.as_slice(pool);
writeln!(w,
" {}, {}({})",
sig_ref,
args[0],
DisplayValues(&args[1..]))
}
Return { ref data, .. } => {
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::entities::AnyEntity;
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
TernaryOverflowData, JumpData, BranchData, IndirectCallData,
ReturnData, ReturnRegData};
TernaryOverflowData, ReturnData, ReturnRegData};
use cretonne::isa::{self, TargetIsa, Encoding};
use cretonne::settings;
use testfile::{TestFile, Details, Comment};
@@ -200,24 +199,22 @@ impl<'a> Context<'a> {
self.map.rewrite_values(&mut data.args, loc)?;
}
InstructionData::Jump { ref mut data, .. } => {
self.map.rewrite_ebb(&mut data.destination, loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?;
InstructionData::Jump { ref mut destination, ref mut args, .. } => {
self.map.rewrite_ebb(destination, loc)?;
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
}
InstructionData::Branch { ref mut data, .. } => {
self.map.rewrite_value(&mut data.arg, loc)?;
self.map.rewrite_ebb(&mut data.destination, loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?;
InstructionData::Branch { ref mut destination, ref mut args, .. } => {
self.map.rewrite_ebb(destination, loc)?;
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
}
InstructionData::Call { ref mut args, .. } => {
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
}
InstructionData::IndirectCall { ref mut data, .. } => {
self.map.rewrite_value(&mut data.arg, loc)?;
self.map.rewrite_values(&mut data.varargs, loc)?;
InstructionData::IndirectCall { ref mut args, .. } => {
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
}
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
// explicitly. It is unfortunate that the correctness of IL depends on the
// 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");
ctx.function.dfg.value_type(match ctx.map.get_value(ctrl_src_value) {
Some(v) => v,
@@ -1429,10 +1426,8 @@ impl<'a> Parser<'a> {
InstructionData::Jump {
opcode: opcode,
ty: VOID,
data: Box::new(JumpData {
destination: ebb_num,
varargs: args,
}),
destination: ebb_num,
args: args.into_value_list(&[], &mut ctx.function.dfg.value_lists),
}
}
InstructionFormat::Branch => {
@@ -1443,11 +1438,8 @@ impl<'a> Parser<'a> {
InstructionData::Branch {
opcode: opcode,
ty: VOID,
data: Box::new(BranchData {
arg: ctrl_arg,
destination: ebb_num,
varargs: args,
}),
destination: ebb_num,
args: args.into_value_list(&[ctrl_arg], &mut ctx.function.dfg.value_lists),
}
}
InstructionFormat::InsertLane => {
@@ -1511,7 +1503,7 @@ impl<'a> Parser<'a> {
ty: VOID,
second_result: None.into(),
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 => {
@@ -1526,11 +1518,8 @@ impl<'a> Parser<'a> {
opcode: opcode,
ty: VOID,
second_result: None.into(),
data: Box::new(IndirectCallData {
sig_ref: sig_ref,
arg: callee,
varargs: args,
}),
sig_ref: sig_ref,
args: args.into_value_list(&[callee], &mut ctx.function.dfg.value_lists),
}
}
InstructionFormat::Return => {

View File

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