diff --git a/cranelift/src/tools/main.rs b/cranelift/src/tools/main.rs index 1ea40c201c..abad701af6 100644 --- a/cranelift/src/tools/main.rs +++ b/cranelift/src/tools/main.rs @@ -11,12 +11,14 @@ use std::process; mod cat; +mod print_cfg; const USAGE: &'static str = " Cretonne code generator utility Usage: cton-util cat ... + cton-util print-cfg ... cton-util --help | --version Options: @@ -28,6 +30,7 @@ Options: #[derive(RustcDecodable, Debug)] struct Args { cmd_cat: bool, + cmd_print_cfg: bool, arg_file: Vec, } @@ -48,6 +51,8 @@ fn cton_util() -> CommandResult { // Find the sub-command to execute. if args.cmd_cat { cat::run(args.arg_file) + } else if args.cmd_print_cfg { + print_cfg::run(args.arg_file) } else { // Debugging / shouldn't happen with proper command line handling above. Err(format!("Unhandled args: {:?}", args)) diff --git a/cranelift/src/tools/print_cfg.rs b/cranelift/src/tools/print_cfg.rs new file mode 100644 index 0000000000..e55033832a --- /dev/null +++ b/cranelift/src/tools/print_cfg.rs @@ -0,0 +1,169 @@ +//! The `print-cfg` sub-command. +//! +//! Read a series of Cretonne IL files and print their control flow graphs +//! in graphviz format. +use std::fs::File; +use std::io::{Read, Write, stdout}; + +use CommandResult; +use cretonne::repr::Function; +use cretonne::cfg::ControlFlowGraph; +use cretonne::instructions::InstructionData; +use cton_reader::parser::Parser; + +pub fn run(files: Vec) -> CommandResult { + for (i, f) in files.into_iter().enumerate() { + if i != 0 { + println!(""); + } + try!(print_cfg(f)) + } + Ok(()) +} + + +struct CFGPrinter { + level: usize, + writer: T, + buffer: String, +} + +impl CFGPrinter { + pub fn new(writer: T) -> CFGPrinter { + CFGPrinter{level: 0, writer: writer, buffer: String::new()} + } + + pub fn print(&mut self, func: &Function) -> Result<(), String> { + self.level = 0; + self.header(); + self.push_indent(); + self.ebb_subgraphs(func); + let cfg = ControlFlowGraph::new(&func); + self.cfg_connections(&cfg); + self.pop_indent(); + self.footer(); + self.write() + } + + fn write(&mut self) -> Result<(), String> { + match self.writer.write(self.buffer.as_bytes()) { + Err(_) => return Err("Write failed!".to_string()), + _ => (), + }; + match self.writer.flush() { + Err(_) => return Err("Flush failed!".to_string()), + _ => (), + }; + Ok(()) + } + + fn append(&mut self, s: &str) { + let mut indent = String::new(); + for _ in 0 .. self.level { + indent = indent + " "; + } + self.buffer.push_str(&(indent + s)); + } + + fn push_indent(&mut self) { + self.level += 1; + } + + fn pop_indent(&mut self) { + if self.level > 0 { + self.level -= 1; + } + } + + fn open_paren(&mut self) { + self.append("{"); + } + + fn close_paren(&mut self) { + self.append("}"); + } + + fn newline(&mut self) { + self.append("\n"); + } + + fn header(&mut self) { + self.append("digraph "); + self.open_paren(); + self.newline(); + self.push_indent(); + self.append("{rank=min; ebb0}"); + self.pop_indent(); + self.newline(); + } + + fn footer(&mut self) { + self.close_paren(); + self.newline(); + } + + fn ebb_subgraphs(&mut self, func: &Function) { + for ebb in func.ebbs_numerically() { + let inst_data = func.ebb_insts(ebb) + .filter(|inst| { + match func[*inst] { + InstructionData::Branch{ ty: _, opcode: _, data: _ } => true, + InstructionData::Jump{ ty: _, opcode: _, data: _ } => true, + _ => false + } + }) + .map(|inst| { + let op = match func[inst] { + InstructionData::Branch{ ty: _, opcode, ref data } => { + Some((opcode, data.destination)) + }, + InstructionData::Jump{ ty: _, opcode, ref data } => { + Some((opcode, data.destination)) + }, + _ => None + }; + (inst, op) + }) + .collect::>(); + + let mut insts = vec![format!("{}", ebb)]; + for (inst, data) in inst_data { + let (op, dest) = data.unwrap(); + insts.push(format!("<{}>{} {}", inst, op, dest)); + } + + self.append(&format!("{} [shape=record, label=\"{}{}{}\"]", + ebb, "{", insts.join(" | "), "}")); + self.newline(); + } + } + + fn cfg_connections(&mut self, cfg: &ControlFlowGraph) { + for (ref ebb, ref predecessors) in cfg.iter() { + for &(parent, inst) in *predecessors { + self.append(&format!("{}:{} -> {}", parent, inst, ebb)); + self.newline(); + } + } + } + +} + +fn print_cfg(filename: String) -> CommandResult { + let mut file = try!(File::open(&filename).map_err(|e| format!("{}: {}", filename, e))); + let mut buffer = String::new(); + try!(file.read_to_string(&mut buffer) + .map_err(|e| format!("Couldn't read {}: {}", filename, e))); + let items = try!(Parser::parse(&buffer).map_err(|e| format!("{}: {}", filename, e))); + + let mut cfg_printer = CFGPrinter::new(stdout()); + for (idx, func) in items.into_iter().enumerate() { + if idx != 0 { + println!(""); + } + + try!(cfg_printer.print(&func)); + } + + Ok(()) +}