Files
wasmtime/cranelift/src/libcretonne/write.rs
Jakob Stoklund Olesen 652ebbdc27 Use EBB layout order almost everywhere.
The ebbs_numerically() function was a workaround for the unimplemented EBB layout
order.
2016-07-18 18:52:35 -07:00

267 lines
8.8 KiB
Rust

//! 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<u8> = 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<bool> {
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<Type> {
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");
}
}