//! Converting Cretonne IL to text. //! //! The `write` module provides the `write_function` function which converts an IL `Function` to an //! equivalent textual representation. This textual representation can be read back by the //! `cretonne-reader` crate. use std::io::{self, Write}; use repr::Function; use entities::{Inst, Ebb, Value}; use types::Type; pub type Result = io::Result<()>; /// Write `func` to `w` as equivalent text. pub fn write_function(w: &mut Write, func: &Function) -> Result { try!(write_spec(w, func)); try!(writeln!(w, " {{")); let mut any = try!(write_preamble(w, func)); for ebb in &func.layout { if any { try!(writeln!(w, "")); } try!(write_ebb(w, func, ebb)); any = true; } writeln!(w, "}}") } /// Convert `func` to a string. pub fn function_to_string(func: &Function) -> String { let mut buffer: Vec = Vec::new(); // Any errors here would be out-of-memory, which should not happen with normal functions. write_function(&mut buffer, func).unwrap(); // A UTF-8 conversion error is a real bug. String::from_utf8(buffer).unwrap() } // ====--------------------------------------------------------------------------------------====// // // Function spec. // // ====--------------------------------------------------------------------------------------====// // The function name may need quotes if it doesn't parse as an identifier. fn needs_quotes(name: &str) -> bool { let mut iter = name.chars(); if let Some(ch) = iter.next() { !ch.is_alphabetic() || !iter.all(char::is_alphanumeric) } else { // A blank function name needs quotes. true } } // Use Rust's escape_default which provides a few simple \t \r \n \' \" \\ escapes and uses // \u{xxxx} for anything else outside the ASCII printable range. fn escaped(name: &str) -> String { name.chars().flat_map(char::escape_default).collect() } fn write_spec(w: &mut Write, func: &Function) -> Result { let sig = func.own_signature(); if !needs_quotes(&func.name) { write!(w, "function {}{}", func.name, sig) } else { write!(w, "function \"{}\"{}", escaped(&func.name), sig) } } fn write_preamble(w: &mut Write, func: &Function) -> io::Result { let mut any = false; for ss in func.stack_slot_iter() { any = true; try!(writeln!(w, " {} = {}", ss, func[ss])); } Ok(any) } // ====--------------------------------------------------------------------------------------====// // // Basic blocks // // ====--------------------------------------------------------------------------------------====// pub fn write_arg(w: &mut Write, func: &Function, arg: Value) -> Result { write!(w, "{}: {}", arg, func.value_type(arg)) } pub fn write_ebb_header(w: &mut Write, func: &Function, ebb: Ebb) -> Result { // Write out the basic block header, outdented: // // ebb1: // ebb1(vx1: i32): // ebb10(vx4: f64, vx5: b1): // let mut args = func.ebb_args(ebb); match args.next() { None => return writeln!(w, "{}:", ebb), Some(arg) => { try!(write!(w, "{}(", ebb)); try!(write_arg(w, func, arg)); } } // Remaining args. for arg in args { try!(write!(w, ", ")); try!(write_arg(w, func, arg)); } writeln!(w, "):") } pub fn write_ebb(w: &mut Write, func: &Function, ebb: Ebb) -> Result { try!(write_ebb_header(w, func, ebb)); for inst in func.layout.ebb_insts(ebb) { try!(write_instruction(w, func, inst)); } Ok(()) } // ====--------------------------------------------------------------------------------------====// // // Instructions // // ====--------------------------------------------------------------------------------------====// // Should `inst` be printed with a type suffix? // // Polymorphic instructions may need a suffix indicating the value of the controlling type variable // if it can't be trivially inferred. // fn type_suffix(func: &Function, inst: Inst) -> Option { let constraints = func[inst].opcode().constraints(); if !constraints.is_polymorphic() { return None; } // If the controlling type variable can be inferred from the type of the designated value input // operand, we don't need the type suffix. // TODO: Should we include the suffix when the input value is defined in another block? The // parser needs to know the type of the value, so it must be defined in a block that lexically // comes before this one. if constraints.use_typevar_operand() { return None; } // This polymorphic instruction doesn't support basic type inference. // The controlling type variable is required to be the type of the first result. let rtype = func.value_type(func.first_result(inst)); assert!(!rtype.is_void(), "Polymorphic instruction must produce a result"); Some(rtype) } pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result { try!(write!(w, " ")); // First write out the result values, if any. let mut has_results = false; for r in func.inst_results(inst) { if !has_results { has_results = true; try!(write!(w, "{}", r)); } else { try!(write!(w, ", {}", r)); } } if has_results { try!(write!(w, " = ")); } // Then the opcode, possibly with a '.type' suffix. let opcode = func[inst].opcode(); match type_suffix(func, inst) { Some(suf) => try!(write!(w, "{}.{}", opcode, suf)), None => try!(write!(w, "{}", opcode)), } // Then the operands, depending on format. use instructions::InstructionData::*; match func[inst] { Nullary { .. } => writeln!(w, ""), Unary { arg, .. } => writeln!(w, " {}", arg), UnaryImm { imm, .. } => writeln!(w, " {}", imm), UnaryIeee32 { imm, .. } => writeln!(w, " {}", imm), UnaryIeee64 { imm, .. } => writeln!(w, " {}", imm), UnaryImmVector { .. } => writeln!(w, " [...]"), Binary { args, .. } => writeln!(w, " {}, {}", args[0], args[1]), BinaryImm { arg, imm, .. } => writeln!(w, " {}, {}", arg, imm), BinaryImmRev { imm, arg, .. } => writeln!(w, " {}, {}", imm, arg), BinaryOverflow { args, .. } => writeln!(w, " {}, {}", args[0], args[1]), Ternary { args, .. } => writeln!(w, " {}, {}, {}", args[0], args[1], args[2]), InsertLane { lane, args, .. } => writeln!(w, " {}, {}, {}", args[0], lane, args[1]), 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), BranchTable { arg, table, .. } => writeln!(w, " {}, {}", arg, table), Call { ref data, .. } => writeln!(w, " {}", data), Return { ref data, .. } => { if data.args.is_empty() { writeln!(w, "") } else { writeln!(w, " {}", data.args) } } } } #[cfg(test)] mod tests { use super::*; use super::{needs_quotes, escaped}; use repr::{Function, StackSlotData}; use types; #[test] fn quoting() { assert_eq!(needs_quotes(""), true); assert_eq!(needs_quotes("x"), false); assert_eq!(needs_quotes(" "), true); assert_eq!(needs_quotes("0"), true); assert_eq!(needs_quotes("x0"), false); } #[test] fn escaping() { assert_eq!(escaped(""), ""); assert_eq!(escaped("x"), "x"); assert_eq!(escaped(" "), " "); assert_eq!(escaped(" \n"), " \\n"); assert_eq!(escaped("a\u{1000}v"), "a\\u{1000}v"); } #[test] fn basic() { let mut f = Function::new(); assert_eq!(function_to_string(&f), "function \"\"() {\n}\n"); f.name.push_str("foo"); assert_eq!(function_to_string(&f), "function foo() {\n}\n"); f.make_stack_slot(StackSlotData::new(4)); assert_eq!(function_to_string(&f), "function foo() {\n ss0 = stack_slot 4\n}\n"); let ebb = f.make_ebb(); f.layout.append_ebb(ebb); assert_eq!(function_to_string(&f), "function foo() {\n ss0 = stack_slot 4\n\nebb0:\n}\n"); f.append_ebb_arg(ebb, types::I8); assert_eq!(function_to_string(&f), "function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8):\n}\n"); f.append_ebb_arg(ebb, types::F32.by(4).unwrap()); assert_eq!(function_to_string(&f), "function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8, vx1: f32x4):\n}\n"); } }