diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index e9f859cd7b..7df5a6aa3c 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -92,3 +92,91 @@ impl<'c, 'fc, 'fd> InstBuilderBase<'fd> for InsertBuilder<'c, 'fc, 'fd> { (inst, self.dfg) } } + +/// Instruction builder that replaces an existing instruction. +/// +/// The inserted instruction will have the same `Inst` number as the old one. This is the only way +/// of rewriting the first result value of an instruction since this is a `ExpandedValue::Direct` +/// variant which encodes the instruction number directly. +/// +/// If the old instruction produced a value, the same value number will refer to the new +/// instruction's first result, so if that value has any uses the type should stay the same. +/// +/// If the old instruction still has secondary result values attached, it is assumed that the new +/// instruction produces the same number and types of results. The old secondary values are +/// preserved. If the replacemant instruction format does not support multiple results, the builder +/// panics. It is a bug to leave result values dangling. +/// +/// If the old instruction was capable of producing secondary results, but the values have been +/// detached, new result values are generated by calling `DataFlowGraph::make_inst_results()`. +pub struct ReplaceBuilder<'f> { + dfg: &'f mut DataFlowGraph, + inst: Inst, +} + +impl<'f> ReplaceBuilder<'f> { + /// Create a `ReplaceBuilder` that will overwrite `inst`. + pub fn new(dfg: &'f mut DataFlowGraph, inst: Inst) -> ReplaceBuilder { + ReplaceBuilder { + dfg: dfg, + inst: inst, + } + } +} + +impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> { + fn data_flow_graph(&self) -> &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. + let old_second_value = self.dfg[self.inst].second_result().unwrap_or_default(); + assert_eq!(old_second_value, + Value::default(), + "Secondary result values {:?} would be left dangling by replacing {} with {}", + self.dfg.inst_results(self.inst).collect::>(), + self.dfg[self.inst].opcode(), + data.opcode()); + + // Splat the new instruction on top of the old one. + self.dfg[self.inst] = data; + (self.inst, self.dfg) + } + + fn complex_instruction(self, + data: InstructionData, + ctrl_typevar: Type) + -> (Inst, &'f mut DataFlowGraph) { + // If the old instruction still has secondary results attached, we'll keep them. + let old_second_value = self.dfg[self.inst].second_result().unwrap_or_default(); + + // Splat the new instruction on top of the old one. + self.dfg[self.inst] = data; + + if old_second_value == Value::default() { + // The old secondary values were either detached or non-existent. + // Construct new ones and set the first result type too. + self.dfg.make_inst_results(self.inst, ctrl_typevar); + } else { + // Reattach the old secondary values. + if let Some(val_ref) = self.dfg[self.inst].second_result_mut() { + // Don't check types here. Leave that to the verifier. + *val_ref = old_second_value; + } else { + // Actually, this instruction format should have called `simple_instruction()`, but + // we don't have a rule against calling `complex_instruction()` even when it is + // overkill. + panic!("Secondary result values left dangling"); + } + + // Normally, make_inst_results() would also set the first result type, but we're not + // going to call that, so set it manually. + *self.dfg[self.inst].first_type_mut() = + self.dfg.compute_result_type(self.inst, 0, ctrl_typevar).unwrap_or_default(); + } + + (self.inst, self.dfg) + } +} diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 35b0af4dc6..7b81c8deed 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -5,7 +5,9 @@ use ir::entities::{NO_VALUE, ExpandedValue}; use ir::instructions::{InstructionData, CallInfo}; use ir::extfunc::ExtFuncData; use entity_map::{EntityMap, PrimaryEntityData}; +use ir::builder::ReplaceBuilder; +use std::mem; use std::ops::{Index, IndexMut}; use std::u16; @@ -269,6 +271,27 @@ impl DataFlowGraph { total_results } + /// Create a `ReplaceBuilder` that will replace `inst` with a new instruction in place. + pub fn replace(&mut self, inst: Inst) -> ReplaceBuilder { + ReplaceBuilder::new(self, inst) + } + + /// Detach secondary instruction results, and return them as an iterator. + /// + /// If `inst` produces two or more results, detach these secondary result values from `inst`, + /// and return an iterator that will enumerate them. The first result value cannot be detached. + /// + /// Use this method to detach secondary values before using `replace(inst)` to provide an + /// alternate instruction for computing the primary result value. + pub fn detach_secondary_results(&mut self, inst: Inst) -> Values { + let second_result = + self[inst].second_result_mut().map(|r| mem::replace(r, NO_VALUE)).unwrap_or_default(); + Values { + dfg: self, + cur: second_result, + } + } + /// Get the first result of an instruction. /// /// If `Inst` doesn't produce any results, this returns a `Value` with a `VOID` type. @@ -277,7 +300,7 @@ impl DataFlowGraph { } /// Iterate through all the results of an instruction. - pub fn inst_results<'a>(&'a self, inst: Inst) -> Values<'a> { + pub fn inst_results(&self, inst: Inst) -> Values { Values { dfg: self, cur: if self.insts[inst].first_type().is_void() { @@ -297,6 +320,34 @@ impl DataFlowGraph { CallInfo::Indirect(s, _) => Some(s), } } + + /// Compute the type of an instruction result from opcode constraints and call signatures. + /// + /// This computes the same sequence of result types that `make_inst_results()` above would + /// assign to the created result values, but it does not depend on `make_inst_results()` being + /// called first. + /// + /// Returns `None` if asked about a result index that is too large. + pub fn compute_result_type(&self, + inst: Inst, + result_idx: usize, + ctrl_typevar: Type) + -> Option { + let constraints = self.insts[inst].opcode().constraints(); + let fixed_results = constraints.fixed_results(); + + if result_idx < fixed_results { + return Some(constraints.result_type(result_idx, ctrl_typevar)); + } + + // Not a fixed result, try to extract a return type from the call signature. + self.call_signature(inst).and_then(|sigref| { + self.signatures[sigref] + .return_types + .get(result_idx - fixed_results) + .map(|&arg| arg.value_type) + }) + } } /// Allow immutable access to instructions via indexing. @@ -369,7 +420,7 @@ impl DataFlowGraph { } /// Iterate through the arguments to an EBB. - pub fn ebb_args<'a>(&'a self, ebb: Ebb) -> Values<'a> { + pub fn ebb_args(&self, ebb: Ebb) -> Values { Values { dfg: self, cur: self.ebbs[ebb].first_arg,