Also use fmt::Write for the print-cfg command.
This prepares use for implementing a 'test print-cfg' sub-test.
This commit is contained in:
@@ -2,13 +2,13 @@
|
|||||||
//!
|
//!
|
||||||
//! Read a series of Cretonne IL files and print their control flow graphs
|
//! Read a series of Cretonne IL files and print their control flow graphs
|
||||||
//! in graphviz format.
|
//! in graphviz format.
|
||||||
use std::io::{Write, stdout};
|
use std::fmt::{Result, Write, Display, Formatter};
|
||||||
|
|
||||||
use CommandResult;
|
use CommandResult;
|
||||||
use utils::read_to_string;
|
use utils::read_to_string;
|
||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
use cretonne::cfg::ControlFlowGraph;
|
use cretonne::cfg::ControlFlowGraph;
|
||||||
use cretonne::ir::instructions::InstructionData;
|
use cretonne::ir::instructions::BranchInfo;
|
||||||
use cton_reader::parse_functions;
|
use cton_reader::parse_functions;
|
||||||
|
|
||||||
pub fn run(files: Vec<String>) -> CommandResult {
|
pub fn run(files: Vec<String>) -> CommandResult {
|
||||||
@@ -21,137 +21,69 @@ pub fn run(files: Vec<String>) -> CommandResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CFGPrinter<T: Write> {
|
struct CFGPrinter<'a> {
|
||||||
level: usize,
|
func: &'a Function,
|
||||||
writer: T,
|
cfg: ControlFlowGraph,
|
||||||
buffer: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Write> CFGPrinter<T> {
|
impl<'a> CFGPrinter<'a> {
|
||||||
pub fn new(writer: T) -> CFGPrinter<T> {
|
pub fn new(func: &'a Function) -> CFGPrinter<'a> {
|
||||||
CFGPrinter {
|
CFGPrinter {
|
||||||
level: 0,
|
func: func,
|
||||||
writer: writer,
|
cfg: ControlFlowGraph::new(func),
|
||||||
buffer: String::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(&mut self, func: &Function) -> Result<(), String> {
|
/// Write the CFG for this function to `w`.
|
||||||
self.level = 0;
|
pub fn write(&self, w: &mut Write) -> Result {
|
||||||
self.header(func);
|
try!(self.header(w));
|
||||||
self.push_indent();
|
try!(self.ebb_nodes(w));
|
||||||
self.ebb_subgraphs(func);
|
try!(self.cfg_connections(w));
|
||||||
let cfg = ControlFlowGraph::new(func);
|
writeln!(w, "}}")
|
||||||
self.cfg_connections(func, &cfg);
|
|
||||||
self.pop_indent();
|
|
||||||
self.footer();
|
|
||||||
self.write()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self) -> Result<(), String> {
|
fn header(&self, w: &mut Write) -> Result {
|
||||||
match self.writer.write(self.buffer.as_bytes()) {
|
try!(writeln!(w, "digraph {} {{", self.func.name));
|
||||||
Err(_) => return Err("Write failed!".to_string()),
|
if let Some(entry) = self.func.layout.entry_block() {
|
||||||
_ => (),
|
try!(writeln!(w, " {{rank=min; {}}}", entry));
|
||||||
};
|
}
|
||||||
match self.writer.flush() {
|
|
||||||
Err(_) => return Err("Flush failed!".to_string()),
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append(&mut self, s: &str) {
|
fn ebb_nodes(&self, w: &mut Write) -> Result {
|
||||||
let mut indent = String::new();
|
for ebb in &self.func.layout {
|
||||||
for _ in 0..self.level {
|
try!(write!(w, " {} [shape=record, label=\"{{{}", ebb, ebb));
|
||||||
indent = indent + " ";
|
// Add all outgoing branch instructions to the label.
|
||||||
|
for inst in self.func.layout.ebb_insts(ebb) {
|
||||||
|
let idata = &self.func.dfg[inst];
|
||||||
|
match idata.analyze_branch() {
|
||||||
|
BranchInfo::SingleDest(dest, _) => {
|
||||||
|
try!(write!(w, " | <{}>{} {}", inst, idata.opcode(), dest))
|
||||||
}
|
}
|
||||||
self.buffer.push_str(&(indent + s));
|
BranchInfo::Table(table) => {
|
||||||
|
try!(write!(w, " | <{}>{} {}", inst, idata.opcode(), table))
|
||||||
|
}
|
||||||
|
BranchInfo::NotABranch => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try!(writeln!(w, "}}\"]"))
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_indent(&mut self) {
|
fn cfg_connections(&self, w: &mut Write) -> Result {
|
||||||
self.level += 1;
|
for ebb in &self.func.layout {
|
||||||
|
for &(parent, inst) in self.cfg.get_predecessors(ebb) {
|
||||||
|
try!(writeln!(w, " {}:{} -> {}", parent, inst, ebb));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn pop_indent(&mut self) {
|
Ok(())
|
||||||
if self.level > 0 {
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_paren(&mut self) {
|
impl<'a> Display for CFGPrinter<'a> {
|
||||||
self.append("{");
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
}
|
self.write(f)
|
||||||
|
|
||||||
fn close_paren(&mut self) {
|
|
||||||
self.append("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn newline(&mut self) {
|
|
||||||
self.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn header(&mut self, func: &Function) {
|
|
||||||
self.append(&format!("digraph {} ", func.name));
|
|
||||||
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.layout {
|
|
||||||
let inst_data = func.layout
|
|
||||||
.ebb_insts(ebb)
|
|
||||||
.filter(|inst| {
|
|
||||||
match func.dfg[*inst] {
|
|
||||||
InstructionData::Branch { ty: _, opcode: _, data: _ } => true,
|
|
||||||
InstructionData::Jump { ty: _, opcode: _, data: _ } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|inst| {
|
|
||||||
let op = match func.dfg[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::<Vec<_>>();
|
|
||||||
|
|
||||||
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, func: &Function, cfg: &ControlFlowGraph) {
|
|
||||||
for ebb in &func.layout {
|
|
||||||
for &(parent, inst) in cfg.get_predecessors(ebb) {
|
|
||||||
self.append(&format!("{}:{} -> {}", parent, inst, ebb));
|
|
||||||
self.newline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,13 +91,11 @@ fn print_cfg(filename: String) -> CommandResult {
|
|||||||
let buffer = try!(read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e)));
|
let buffer = try!(read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e)));
|
||||||
let items = try!(parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e)));
|
let items = try!(parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e)));
|
||||||
|
|
||||||
let mut cfg_printer = CFGPrinter::new(stdout());
|
|
||||||
for (idx, func) in items.into_iter().enumerate() {
|
for (idx, func) in items.into_iter().enumerate() {
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
|
print!("{}", CFGPrinter::new(&func));
|
||||||
try!(cfg_printer.print(&func));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user