diff --git a/cranelift/src/libcretonne/repr.rs b/cranelift/src/libcretonne/repr.rs index c219ecb4b3..807be698ac 100644 --- a/cranelift/src/libcretonne/repr.rs +++ b/cranelift/src/libcretonne/repr.rs @@ -3,6 +3,7 @@ use types::{Type, FunctionName, Signature}; use immediates::*; +use std::default::Default; use std::fmt::{self, Display, Formatter, Write}; use std::ops::Index; use std::u32; @@ -81,12 +82,20 @@ pub struct StackSlotData { } /// Contents of an extended basic block. +/// +/// Arguments for an extended basic block are values that dominate everything in the EBB. All +/// branches to this EBB must provide matching arguments, and the arguments to the entry EBB must +/// match the function arguments. #[derive(Debug)] pub struct EbbData { - /// Arguments for this extended basic block. These values dominate everything in the EBB. - /// All branches to this EBB must provide matching arguments, and the arguments to the entry - /// EBB must match the function arguments. - pub arguments: Vec, + /// First argument to this EBB, or `NO_VALUE` if the block has no arguments. + /// + /// The arguments are all ValueData::Argument entries that form a linked list from `first_arg` + /// to `last_arg`. + first_arg: Value, + + /// Last argument to this EBB, or `NO_VALUE` if the block has no arguments. + last_arg: Value, } /// Contents on an instruction. @@ -132,12 +141,11 @@ pub enum InstructionData { /// Payload of a call instruction. #[derive(Debug)] pub struct CallData { - // Number of result values. - results: u8, + /// Second result value for a call producing multiple return values. + second_result: Value, - // Dynamically sized array containing `results-1` result values (not including the first value) - // followed by the argument values. - values: Vec, + // Dynamically sized array containing call argument values. + arguments: Vec, } @@ -233,7 +241,10 @@ impl Display for Ebb { impl EbbData { fn new() -> EbbData { - EbbData { arguments: Vec::new() } + EbbData { + first_arg: NO_VALUE, + last_arg: NO_VALUE, + } } } @@ -284,6 +295,9 @@ enum ExpandedValue { // This value is described in the extended value table. Table(usize), + + // This is NO_VALUE. + None, } impl Value { @@ -302,6 +316,9 @@ impl Value { // Expand the internal representation into something useful. fn expand(&self) -> ExpandedValue { use self::ExpandedValue::*; + if *self == NO_VALUE { + return None; + } let index = (self.0 / 2) as usize; if self.0 % 2 == 0 { Direct(Inst::new(index)) @@ -311,13 +328,20 @@ impl Value { } } +impl Default for Value { + fn default() -> Value { + NO_VALUE + } +} + /// Display a `Value` reference as "v7" or "v2x". impl Display for Value { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::ExpandedValue::*; match self.expand() { Direct(i) => write!(fmt, "v{}", i.0), - Table(i) => write!(fmt, "v{}x", i), + Table(i) => write!(fmt, "vx{}", i), + None => write!(fmt, "NO_VALUE"), } } } @@ -326,25 +350,67 @@ impl Display for Value { // Other values have an entry in the value table. #[derive(Debug)] enum ValueData { - // An unused entry in the value table. No instruction should be defining or using this value. - Unused, - // Value is defined by an instruction, but it is not the first result. Def { ty: Type, - num: u8, def: Inst, + next: Value, // Next result defined by `def`. }, // Value is an EBB argument. Argument { ty: Type, - num: u8, ebb: Ebb, + next: Value, // Next argument to `ebb`. }, } +/// Iterate through a list of related value references, such as: +/// +/// - All results defined by an instruction. +/// - All arguments to an EBB +/// +/// A value iterator borrows a Function reference. +pub struct Values<'a> { + func: &'a Function, + cur: Value, +} + +impl<'a> Iterator for Values<'a> { + type Item = Value; + + fn next(&mut self) -> Option { + let prev = self.cur; + + // Advance self.cur to the next value, or NO_VALUE. + self.cur = match prev.expand() { + ExpandedValue::Direct(inst) => self.func[inst].second_result().unwrap_or_default(), + ExpandedValue::Table(index) => { + match self.func.extended_values[index] { + ValueData::Def { next, .. } => next, + ValueData::Argument { next, .. } => next, + } + } + ExpandedValue::None => return None, + }; + + Some(prev) + } +} + impl InstructionData { + /// Create data for a call instruction. + pub fn call(opc: Opcode, return_type: Type) -> InstructionData { + InstructionData::Call { + opcode: opc, + ty: return_type, + data: Box::new(CallData { + second_result: NO_VALUE, + arguments: Vec::new(), + }), + } + } + /// Get the opcode of this instruction. pub fn opcode(&self) -> Opcode { use self::InstructionData::*; @@ -370,6 +436,31 @@ impl InstructionData { Call { ty, .. } => ty, } } + + /// Second result value, if any. + fn second_result(&self) -> Option { + use self::InstructionData::*; + match *self { + Nullary { .. } => None, + Unary { .. } => None, + UnaryImm { .. } => None, + Binary { .. } => None, + BinaryImm { .. } => None, + Call { ref data, .. } => Some(data.second_result), + } + } + + fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value> { + use self::InstructionData::*; + match *self { + Nullary { .. } => None, + Unary { .. } => None, + UnaryImm { .. } => None, + Binary { .. } => None, + BinaryImm { .. } => None, + Call { ref mut data, .. } => Some(&mut data.second_result), + } + } } impl Function { @@ -396,6 +487,8 @@ impl Function { &self.signature } + // Stack slots. + /// Allocate a new stack slot. pub fn make_stack_slot(&mut self, data: StackSlotData) -> StackSlot { let ss = StackSlot::new(self.stack_slots.len()); @@ -411,14 +504,71 @@ impl Function { } } + // Instructions. + /// Create a new instruction. - pub fn make_inst(&mut self, data: InstructionData) -> Inst { - let iref = Inst::new(self.instructions.len()); + /// + /// The instruction is allowed to produce at most one result as indicated by `data.ty`. Use + /// `make_multi_inst()` to create instructions with multiple results. + pub fn make_inst(&mut self, data: InstructionData, extra_result_types: &[Type]) -> Inst { + let inst = Inst::new(self.instructions.len()); self.instructions.push(data); - // FIXME: Allocate extended value table entries if needed. - iref + inst } + /// Make an instruction that may produce multiple results. + /// + /// The type of the first result is `data.ty`. If the instruction generates more than one + /// result, additional result types are in `extra_result_types`. + /// + /// Not all instruction formats can represent multiple result values. This function will panic + /// if the format of `data` is insufficient. + pub fn make_multi_inst(&mut self, data: InstructionData, extra_result_types: &[Type]) -> Inst { + let inst = self.make_inst(data); + + if !extra_result_types.is_empty() { + // Additional values form a linked list starting from the second result value. Generate + // the list backwards so we don't have to modify value table entries in place. (This + // causes additional result values to be numbered backwards which is not the aestetic + // choice, but since it is only visible in extremely rare instructions with 3+ results, + // we don't care). + let mut head = NO_VALUE; + for ty in extra_result_types.into_iter().rev() { + head = self.make_value(ValueData::Def { + ty: *ty, + def: inst, + next: head, + }); + } + + // Update the second_result pointer in `inst`. + if let Some(second_result_ref) = self.instructions[inst.index()].second_result_mut() { + *second_result_ref = head; + } else { + panic!("Instruction format doesn't allow multiple results."); + } + } + + inst + } + + /// Get the first result of an instruction. + /// + /// If `Inst` doesn't produce any results, this returns a `Value` with a `VOID` type. + pub fn first_result(&self, inst: Inst) -> Value { + Value::new_direct(inst) + } + + /// Iterate through all the results of an instruction. + pub fn inst_results<'a>(&'a self, inst: Inst) -> Values<'a> { + Values { + func: self, + cur: Value::new_direct(inst), + } + } + + // Basic blocks + /// Create a new basic block. pub fn make_ebb(&mut self) -> Ebb { let ebb = Ebb::new(self.extended_basic_blocks.len()); @@ -426,6 +576,60 @@ impl Function { ebb } + /// Reference the representation of an EBB. + fn ebb(&self, ebb: Ebb) -> &EbbData { + &self.extended_basic_blocks[ebb.index()] + } + + /// Mutably reference the representation of an EBB. + fn ebb_mut(&mut self, ebb: Ebb) -> &mut EbbData { + &mut self.extended_basic_blocks[ebb.index()] + } + + /// Append an argument with type `ty` to `ebb`. + pub fn append_ebb_arg(&mut self, ebb: Ebb, ty: Type) -> Value { + let val = self.make_value(ValueData::Argument { + ty: ty, + ebb: ebb, + next: NO_VALUE, + }); + + let last_arg = self.ebb(ebb).last_arg; + match last_arg.expand() { + // If last_arg = NO_VALUE, we're adding the first EBB argument. + ExpandedValue::None => self.ebb_mut(ebb).first_arg = val, + ExpandedValue::Table(index) => { + // Append to linked list of arguments. + if let ValueData::Argument { ref mut next, .. } = self.extended_values[index] { + *next = val; + } else { + panic!("wrong type of extended value referenced by Ebb::last_arg"); + } + } + ExpandedValue::Direct(_) => panic!("Direct value cannot appear as EBB argument"), + } + self.ebb_mut(ebb).last_arg = val; + + val + } + + /// Iterate through the arguments to an EBB. + pub fn ebb_args<'a>(&'a self, ebb: Ebb) -> Values<'a> { + Values { + func: self, + cur: self.ebb(ebb).first_arg, + } + } + + // Values. + + /// Allocate an extended value entry. + fn make_value(&mut self, data: ValueData) -> Value { + let vref = Value::new_table(self.extended_values.len()); + self.extended_values.push(data); + vref + } + /// Get the type of a value. pub fn value_type(&self, v: Value) -> Type { use self::ExpandedValue::*; @@ -434,11 +638,11 @@ impl Function { Direct(i) => self[i].first_type(), Table(i) => { match self.extended_values[i] { - Unused => panic!("Can't get type of Unused value {}", v), Def { ty, .. } => ty, Argument { ty, .. } => ty, } } + None => panic!("NO_VALUE has no type"), } } } @@ -457,7 +661,7 @@ mod tests { opcode: Opcode::Iconst, ty: types::I32, }; - let inst = func.make_inst(idata); + let inst = func.make_inst(idata, &[]); assert_eq!(inst.to_string(), "inst0"); // Immutable reference resolution. @@ -466,6 +670,21 @@ mod tests { assert_eq!(ins.first_type(), types::I32); } + #[test] + fn multiple_results() { + use types::*; + let mut func = Function::new(); + + let idata = InstructionData::call(Opcode::Vconst, I64); + let inst = func.make_inst(idata, &[I8, F64]); + assert_eq!(inst.to_string(), "inst0"); + let results: Vec = func.inst_results(inst).collect(); + assert_eq!(results.len(), 3); + assert_eq!(func.value_type(results[0]), I64); + assert_eq!(func.value_type(results[1]), I8); + assert_eq!(func.value_type(results[2]), F64); + } + #[test] fn stack_slot() { let mut func = Function::new(); @@ -479,4 +698,28 @@ mod tests { assert_eq!(func[ss1].size, 8); } + #[test] + fn ebb() { + let mut func = Function::new(); + + let ebb = func.make_ebb(); + assert_eq!(ebb.to_string(), "ebb0"); + assert_eq!(func.ebb_args(ebb).next(), None); + + let arg1 = func.append_ebb_arg(ebb, types::F32); + assert_eq!(arg1.to_string(), "vx0"); + { + let mut args1 = func.ebb_args(ebb); + assert_eq!(args1.next(), Some(arg1)); + assert_eq!(args1.next(), None); + } + let arg2 = func.append_ebb_arg(ebb, types::I16); + assert_eq!(arg2.to_string(), "vx1"); + { + let mut args2 = func.ebb_args(ebb); + assert_eq!(args2.next(), Some(arg1)); + assert_eq!(args2.next(), Some(arg2)); + assert_eq!(args2.next(), None); + } + } }