diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 8977cc6751..e86b87de32 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -261,7 +261,7 @@ pub enum ValueDef { } // Internal table storage for extended values. -#[derive(Clone)] +#[derive(Clone, Debug)] enum ValueData { // Value is defined by an instruction, but it is not the first result. Inst { @@ -663,6 +663,67 @@ impl DataFlowGraph { } } + /// Replace an EBB argument with a new value of type `ty`. + /// + /// The `old_value` must be an attached EBB argument. It is removed from its place in the list + /// of arguments and replaced by a new value of type `new_type`. The new value gets the same + /// position in the list, and other arguments are not disturbed. + /// + /// The old value is left detached, so it should probably be changed into something else. + /// + /// Returns the new value. + pub fn replace_ebb_arg(&mut self, old_arg: Value, new_type: Type) -> Value { + let old_data = if let ExpandedValue::Table(index) = old_arg.expand() { + self.extended_values[index].clone() + } else { + panic!("old_arg: {} must be an EBB argument", old_arg); + }; + + // Create new value identical to the old one except for the type. + let (ebb, num, new_arg) = if let ValueData::Arg { num, ebb, next, .. } = old_data { + (ebb, + num, + self.make_value(ValueData::Arg { + ty: new_type, + num: num, + ebb: ebb, + next: next, + })) + } else { + panic!("old_arg: {} must be an EBB argument: {:?}", + old_arg, + old_data); + }; + + // Now fix up the linked lists. + if self.ebbs[ebb].last_arg.expand() == Some(old_arg) { + self.ebbs[ebb].last_arg = new_arg.into(); + } + + if self.ebbs[ebb].first_arg.expand() == Some(old_arg) { + assert_eq!(num, 0); + self.ebbs[ebb].first_arg = new_arg.into(); + } else { + // We need to find the num-1 argument value and change its next link. + let mut arg = self.ebbs[ebb].first_arg.expect("EBB has no arguments"); + for _ in 1..num { + arg = self.next_ebb_arg(arg).expect("Too few EBB arguments"); + } + if let ExpandedValue::Table(index) = arg.expand() { + if let ValueData::Arg { ref mut next, .. } = self.extended_values[index] { + assert_eq!(next.expand(), Some(old_arg)); + *next = new_arg.into(); + } else { + panic!("{} is not an EBB argument", arg); + } + } else { + panic!("{} is not an EBB argument", arg); + } + } + + new_arg + } + /// Given a value that is known to be an EBB argument, return the next EBB argument. pub fn next_ebb_arg(&self, arg: Value) -> Option { if let ExpandedValue::Table(index) = arg.expand() { @@ -886,6 +947,36 @@ mod tests { assert_eq!(dfg.ebb_args(ebb).collect::>(), [take2, arg3, take1]); } + #[test] + fn replace_ebb_arguments() { + let mut dfg = DataFlowGraph::new(); + + let ebb = dfg.make_ebb(); + let arg1 = dfg.append_ebb_arg(ebb, types::F32); + + let new1 = dfg.replace_ebb_arg(arg1, types::I64); + assert_eq!(dfg.value_type(arg1), types::F32); + assert_eq!(dfg.value_type(new1), types::I64); + assert_eq!(dfg.ebb_args(ebb).collect::>(), [new1]); + + dfg.attach_ebb_arg(ebb, arg1); + assert_eq!(dfg.ebb_args(ebb).collect::>(), [new1, arg1]); + + let new2 = dfg.replace_ebb_arg(arg1, types::I8); + assert_eq!(dfg.value_type(arg1), types::F32); + assert_eq!(dfg.value_type(new2), types::I8); + assert_eq!(dfg.ebb_args(ebb).collect::>(), [new1, new2]); + + dfg.attach_ebb_arg(ebb, arg1); + assert_eq!(dfg.ebb_args(ebb).collect::>(), [new1, new2, arg1]); + + let new3 = dfg.replace_ebb_arg(new2, types::I16); + assert_eq!(dfg.value_type(new1), types::I64); + assert_eq!(dfg.value_type(new2), types::I8); + assert_eq!(dfg.value_type(new3), types::I16); + assert_eq!(dfg.ebb_args(ebb).collect::>(), [new1, new3, arg1]); + } + #[test] fn aliases() { use ir::InstBuilder;