Add a --disasm option to clif-util wasm and compile (#713)

- Both the `wasm` and `compile` commands get this new subcommand, and it defaults to false.  This means that test runs with `wasm` can request disassembly (the main reason I am doing this) while test runs with `compile` now must request it, this changes current behavior.
- Switch to using context.compile_and_emit directly, and make the reloc and trap printers just accumulate output, not print it.  This allows us to factor the printing code into the disasm module.
This commit is contained in:
Lars T Hansen
2019-03-27 12:57:13 +01:00
committed by Benjamin Bouvier
parent 82c6867155
commit 141ccb9e9d
5 changed files with 283 additions and 186 deletions

View File

@@ -1,67 +1,19 @@
//! CLI tool to read Cranelift IR files and compile them into native code.
use crate::disasm::{print_all, PrintRelocs, PrintTraps};
use crate::utils::{parse_sets_and_triple, read_to_string};
use cfg_if::cfg_if;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::settings::FlagsOrIsa;
use cranelift_codegen::timing;
use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir};
use cranelift_reader::parse_test;
use std::path::Path;
use std::path::PathBuf;
struct PrintRelocs {
flag_print: bool,
}
impl binemit::RelocSink for PrintRelocs {
fn reloc_ebb(
&mut self,
where_: binemit::CodeOffset,
r: binemit::Reloc,
offset: binemit::CodeOffset,
) {
if self.flag_print {
println!("reloc_ebb: {} {} at {}", r, offset, where_);
}
}
fn reloc_external(
&mut self,
where_: binemit::CodeOffset,
r: binemit::Reloc,
name: &ir::ExternalName,
addend: binemit::Addend,
) {
if self.flag_print {
println!("reloc_external: {} {} {} at {}", r, name, addend, where_);
}
}
fn reloc_jt(&mut self, where_: binemit::CodeOffset, r: binemit::Reloc, jt: ir::JumpTable) {
if self.flag_print {
println!("reloc_jt: {} {} at {}", r, jt, where_);
}
}
}
struct PrintTraps {
flag_print: bool,
}
impl binemit::TrapSink for PrintTraps {
fn trap(&mut self, offset: binemit::CodeOffset, _srcloc: ir::SourceLoc, code: ir::TrapCode) {
if self.flag_print {
println!("trap: {} at {}", code, offset);
}
}
}
pub fn run(
files: Vec<String>,
flag_print: bool,
flag_disasm: bool,
flag_report_times: bool,
flag_set: &[String],
flag_isa: &str,
@@ -73,6 +25,7 @@ pub fn run(
let name = String::from(path.as_os_str().to_string_lossy());
handle_module(
flag_print,
flag_disasm,
flag_report_times,
&path.to_path_buf(),
&name,
@@ -84,6 +37,7 @@ pub fn run(
fn handle_module(
flag_print: bool,
flag_disasm: bool,
flag_report_times: bool,
path: &PathBuf,
name: &str,
@@ -106,39 +60,21 @@ fn handle_module(
let mut context = Context::new();
context.func = func;
// Compile and encode the result to machine code.
let total_size = context
.compile(isa)
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
let mut relocs = PrintRelocs::new(flag_print);
let mut traps = PrintTraps::new(flag_print);
let mut mem = vec![];
let mut mem = vec![0; total_size as usize];
let mut relocs = PrintRelocs { flag_print };
let mut traps = PrintTraps { flag_print };
let mut code_sink: binemit::MemoryCodeSink;
unsafe {
code_sink = binemit::MemoryCodeSink::new(mem.as_mut_ptr(), &mut relocs, &mut traps);
}
isa.emit_function_to_memory(&context.func, &mut code_sink);
// Compile and encode the result to machine code.
let (code_size, rodata_size) = context
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
if flag_print {
println!("{}", context.func.display(isa));
}
if flag_print {
print!(".byte ");
let mut first = true;
for byte in &mem {
if first {
first = false;
} else {
print!(", ");
}
print!("{}", byte);
}
println!();
print_disassembly(isa, &mem[0..code_sink.code_size as usize])?;
print_readonly_data(&mem[code_sink.code_size as usize..total_size as usize]);
if flag_disasm {
print_all(isa, &mem, code_size, rodata_size, &relocs, &traps)?;
}
}
@@ -148,99 +84,3 @@ fn handle_module(
Ok(())
}
fn print_readonly_data(mem: &[u8]) {
if mem.is_empty() {
return;
}
println!("\nFollowed by {} bytes of read-only data:", mem.len());
for (i, byte) in mem.iter().enumerate() {
if i % 16 == 0 {
if i != 0 {
println!();
}
print!("{:4}: ", i);
}
if i % 4 == 0 {
print!(" ");
}
print!("{:02x} ", byte);
}
println!();
}
cfg_if! {
if #[cfg(feature = "disas")] {
use capstone::prelude::*;
use target_lexicon::Architecture;
use std::fmt::Write;
fn get_disassembler(isa: &TargetIsa) -> Result<Capstone, String> {
let cs = match isa.triple().architecture {
Architecture::Riscv32 | Architecture::Riscv64 => {
return Err(String::from("No disassembler for RiscV"))
}
Architecture::I386 | Architecture::I586 | Architecture::I686 => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.build(),
Architecture::X86_64 => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.build(),
Architecture::Arm
| Architecture::Armv4t
| Architecture::Armv5te
| Architecture::Armv7
| Architecture::Armv7s => Capstone::new().arm().mode(arch::arm::ArchMode::Arm).build(),
Architecture::Thumbv6m | Architecture::Thumbv7em | Architecture::Thumbv7m => Capstone::new(
).arm()
.mode(arch::arm::ArchMode::Thumb)
.build(),
Architecture::Aarch64 => Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.build(),
_ => return Err(String::from("Unknown ISA")),
};
cs.map_err(|err| err.to_string())
}
fn print_disassembly(isa: &TargetIsa, mem: &[u8]) -> Result<(), String> {
let mut cs = get_disassembler(isa)?;
println!("\nDisassembly of {} bytes:", mem.len());
let insns = cs.disasm_all(&mem, 0x0).unwrap();
for i in insns.iter() {
let mut line = String::new();
write!(&mut line, "{:4x}:\t", i.address()).unwrap();
let mut bytes_str = String::new();
for b in i.bytes() {
write!(&mut bytes_str, "{:02x} ", b).unwrap();
}
write!(&mut line, "{:21}\t", bytes_str).unwrap();
if let Some(s) = i.mnemonic() {
write!(&mut line, "{}\t", s).unwrap();
}
if let Some(s) = i.op_str() {
write!(&mut line, "{}", s).unwrap();
}
println!("{}", line);
}
Ok(())
}
} else {
fn print_disassembly(_: &TargetIsa, _: &[u8]) -> Result<(), String> {
println!("\nNo disassembly available.");
Ok(())
}
}
}