From 1135a89af9d5978e194f3f156eb0cbf0222a58e0 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Thu, 9 Mar 2017 14:53:31 -0800 Subject: [PATCH] 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. --- lib/cretonne/meta/base/formats.py | 6 +- lib/cretonne/meta/gen_instr.py | 9 +- lib/cretonne/src/cfg.rs | 12 +- lib/cretonne/src/dominator_tree.rs | 10 +- lib/cretonne/src/ir/instructions.rs | 109 +++--------------- .../src/regalloc/live_value_tracker.rs | 2 +- lib/cretonne/src/write.rs | 34 +++++- lib/reader/src/parser.rs | 45 +++----- src/print_cfg.rs | 2 +- 9 files changed, 88 insertions(+), 141 deletions(-) diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 9b29c54775..9f7a805940 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -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) diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index b8afeb3eb7..376bfc0d56 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -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 {', '}'): + 'pub fn typevar_operand(&self, pool: &ValueListPool) -> ' + 'Option {', '}'): 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: diff --git a/lib/cretonne/src/cfg.rs b/lib/cretonne/src/cfg.rs index 9b0f3f15e7..c3cf19efe4 100644 --- a/lib/cretonne/src/cfg.rs +++ b/lib/cretonne/src/cfg.rs @@ -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); } diff --git a/lib/cretonne/src/dominator_tree.rs b/lib/cretonne/src/dominator_tree.rs index fd2c6cfb50..8c90b46160 100644 --- a/lib/cretonne/src/dominator_tree.rs +++ b/lib/cretonne/src/dominator_tree.rs @@ -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); } diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 2425b8814a..d7393c9293 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -196,12 +196,14 @@ pub enum InstructionData { Jump { opcode: Opcode, ty: Type, - data: Box, + destination: Ebb, + args: ValueList, }, Branch { opcode: Opcode, ty: Type, - data: Box, + destination: Ebb, + args: ValueList, }, BranchTable { opcode: Opcode, @@ -220,7 +222,8 @@ pub enum InstructionData { opcode: Opcode, ty: Type, second_result: PackedOption, - data: Box, + 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 diff --git a/lib/cretonne/src/regalloc/live_value_tracker.rs b/lib/cretonne/src/regalloc/live_value_tracker.rs index bcbbb62fe1..c45da220e4 100644 --- a/lib/cretonne/src/regalloc/live_value_tracker.rs +++ b/lib/cretonne/src/regalloc/live_value_tracker.rs @@ -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), } diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 8cba9757f9..5e8968c48b 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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() { diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index c009270f54..26fbe234a0 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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 => { diff --git a/src/print_cfg.rs b/src/print_cfg.rs index d8261dad10..206f5430c2 100644 --- a/src/print_cfg.rs +++ b/src/print_cfg.rs @@ -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)? }