diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index b674fbf7f9..b3c7672c0c 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -34,6 +34,8 @@ term = "0.5.1" capstone = { version = "0.4", optional = true } wabt = { version = "0.4", optional = true } target-lexicon = "0.0.3" +pretty_env_logger = "0.2.4" +file-per-thread-logger = "0.1.1" [features] default = ["disas", "wasm"] diff --git a/cranelift/README.md b/cranelift/README.md index b304a97891..b325058da1 100644 --- a/cranelift/README.md +++ b/cranelift/README.md @@ -8,7 +8,7 @@ into executable machine code. [![Documentation Status](https://readthedocs.org/projects/cranelift/badge/?version=latest)](https://cranelift.readthedocs.io/en/latest/?badge=latest) [![Build Status](https://travis-ci.org/CraneStation/cranelift.svg?branch=master)](https://travis-ci.org/CraneStation/cranelift) -[![Gitter chat](https://badges.gitter.im/CraneStation/CraneStation.svg)](https://gitter.im/CraneStation/Lobby/~chat) +[![Gitter chat](https://badges.gitter.im/CraneStation/CraneStation.svg)](https://gitter.im/CraneStation/Lobby) For more information, see [the documentation](https://cranelift.readthedocs.io/en/latest/?badge=latest). @@ -127,7 +127,7 @@ Building the documentation -------------------------- To build the Cranelift documentation, you need the [Sphinx documentation -generator](https://www.sphinx-doc.org/): +generator](http://www.sphinx-doc.org/) as well as Python 3:: $ pip install sphinx sphinx-autobuild sphinx_rtd_theme $ cd cranelift/docs diff --git a/cranelift/src/clif-util.rs b/cranelift/src/clif-util.rs index 0f7edd4ce3..890bf95be7 100644 --- a/cranelift/src/clif-util.rs +++ b/cranelift/src/clif-util.rs @@ -1,5 +1,5 @@ -#![deny(trivial_numeric_casts)] -#![warn(unused_import_braces, unstable_features, unused_extern_crates)] +#![deny(trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr( feature = "cargo-clippy", warn( @@ -14,11 +14,13 @@ extern crate cranelift_codegen; extern crate cranelift_filetests; extern crate cranelift_reader; extern crate docopt; +extern crate file_per_thread_logger; extern crate filecheck; #[macro_use] extern crate serde_derive; #[cfg(feature = "disas")] extern crate capstone; +extern crate pretty_env_logger; extern crate term; cfg_if! { @@ -31,6 +33,7 @@ cfg_if! { } extern crate target_lexicon; +use cranelift_codegen::dbg::LOG_FILENAME_PREFIX; use cranelift_codegen::{timing, VERSION}; use docopt::Docopt; use std::io::{self, Write}; @@ -46,16 +49,17 @@ const USAGE: &str = " Cranelift code generator utility Usage: - clif-util test [-vT] ... - clif-util cat ... - clif-util filecheck [-v] - clif-util print-cfg ... - clif-util compile [-vpT] [--set ]... [--target ] ... - clif-util wasm [-ctvpTs] [--set ]... [--target ] ... + clif-util test [-vTd] ... + clif-util cat [-d] ... + clif-util filecheck [-vd] + clif-util print-cfg [-d] ... + clif-util compile [-vpTd] [--set ]... [--target ] ... + clif-util wasm [-ctvpTsd] [--set ]... [--target ] ... clif-util --help | --version Options: -v, --verbose be more verbose + -d, --debug enable debug output on stderr/stdout -T, --time-passes print pass timing report -t, --just-decode @@ -90,6 +94,7 @@ struct Args { flag_target: String, flag_time_passes: bool, flag_print_size: bool, + flag_debug: bool, } /// A command either succeeds or fails with an error message. @@ -106,6 +111,12 @@ fn clif_util() -> CommandResult { }) .unwrap_or_else(|e| e.exit()); + if args.flag_debug { + pretty_env_logger::init(); + } else { + file_per_thread_logger::initialize(LOG_FILENAME_PREFIX); + } + // Find the sub-command to execute. let result = if args.cmd_test { cranelift_filetests::run(args.flag_verbose, &args.arg_file).map(|_time| ()) diff --git a/lib/codegen/Cargo.toml b/lib/codegen/Cargo.toml index 5bddad6418..4d8f48f012 100644 --- a/lib/codegen/Cargo.toml +++ b/lib/codegen/Cargo.toml @@ -17,6 +17,7 @@ failure = { version = "0.1.1", default-features = false, features = ["derive"] } failure_derive = { version = "0.1.1", default-features = false } hashmap_core = { version = "0.1.9", optional = true } target-lexicon = { version = "0.0.3", default-features = false } +log = { version = "0.4.3", default-features = false, features = ["release_max_level_warn"] } # It is a goal of the cranelift-codegen crate to have minimal external dependencies. # Please don't add any unless they are essential to the task of creating binary # machine code. Integration tests that need external dependencies can be diff --git a/lib/codegen/src/binemit/relaxation.rs b/lib/codegen/src/binemit/relaxation.rs index f23d3218f8..ad13dc21c1 100644 --- a/lib/codegen/src/binemit/relaxation.rs +++ b/lib/codegen/src/binemit/relaxation.rs @@ -140,7 +140,7 @@ fn relax_branch( isa: &TargetIsa, ) -> CodeOffset { let inst = cur.current_inst().unwrap(); - dbg!( + debug!( "Relaxing [{}] {} for {:#x}-{:#x} range", encinfo.display(cur.func.encodings[inst]), cur.func.dfg.display_inst(inst, isa), @@ -156,7 +156,7 @@ fn relax_branch( .find(|&enc| { let range = encinfo.branch_range(enc).expect("Branch with no range"); if !range.contains(offset, dest_offset) { - dbg!(" trying [{}]: out of range", encinfo.display(enc)); + debug!(" trying [{}]: out of range", encinfo.display(enc)); false } else if encinfo.operand_constraints(enc) != encinfo.operand_constraints(cur.func.encodings[inst]) @@ -166,10 +166,10 @@ fn relax_branch( // which the existing operands don't satisfy. We can't check for // validity directly because we don't have a RegDiversions active so // we don't know which registers are actually in use. - dbg!(" trying [{}]: constraints differ", encinfo.display(enc)); + debug!(" trying [{}]: constraints differ", encinfo.display(enc)); false } else { - dbg!(" trying [{}]: OK", encinfo.display(enc)); + debug!(" trying [{}]: OK", encinfo.display(enc)); true } }) { diff --git a/lib/codegen/src/binemit/shrink.rs b/lib/codegen/src/binemit/shrink.rs index 63b0329278..5027aeeeb7 100644 --- a/lib/codegen/src/binemit/shrink.rs +++ b/lib/codegen/src/binemit/shrink.rs @@ -54,7 +54,7 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) { if best_enc != enc { func.encodings[inst] = best_enc; - dbg!( + debug!( "Shrunk [{}] to [{}] in {}, reducing the size from {} to {}", encinfo.display(enc), encinfo.display(best_enc), diff --git a/lib/codegen/src/dbg.rs b/lib/codegen/src/dbg.rs index 0b32bf2bf5..f19c222caf 100644 --- a/lib/codegen/src/dbg.rs +++ b/lib/codegen/src/dbg.rs @@ -1,133 +1,8 @@ -//! Debug tracing macros. -//! -//! This module defines the `dbg!` macro which works like `println!` except it writes to the -//! Cranelift tracing output file if enabled. -//! -//! Tracing can be enabled by setting the `CRANELIFT_DBG` environment variable to something -/// other than `0`. -/// -/// The output will appear in files named `cranelift.dbg.*`, where the suffix is named after the -/// thread doing the logging. -#[cfg(feature = "std")] -use std::cell::RefCell; -#[cfg(feature = "std")] -use std::env; -#[cfg(feature = "std")] -use std::ffi::OsStr; +//! Debug tracing helpers. use std::fmt; -#[cfg(feature = "std")] -use std::fs::File; -#[cfg(feature = "std")] -use std::io::{self, Write}; -#[cfg(feature = "std")] -use std::sync::atomic; -#[cfg(feature = "std")] -use std::thread; -#[cfg(feature = "std")] -static STATE: atomic::AtomicIsize = atomic::ATOMIC_ISIZE_INIT; - -/// Is debug tracing enabled? -/// -/// Debug tracing can be enabled by setting the `CRANELIFT_DBG` environment variable to something -/// other than `0`. -/// -/// This inline function turns into a constant `false` when debug assertions are disabled. -#[cfg(feature = "std")] -#[inline] -pub fn enabled() -> bool { - if cfg!(debug_assertions) { - match STATE.load(atomic::Ordering::Relaxed) { - 0 => initialize(), - s => s > 0, - } - } else { - false - } -} - -/// Does nothing -#[cfg(not(feature = "std"))] -#[inline] -pub fn enabled() -> bool { - false -} - -/// Initialize `STATE` from the environment variable. -#[cfg(feature = "std")] -fn initialize() -> bool { - let enable = match env::var_os("CRANELIFT_DBG") { - Some(s) => s != OsStr::new("0"), - None => false, - }; - - if enable { - STATE.store(1, atomic::Ordering::Relaxed); - } else { - STATE.store(-1, atomic::Ordering::Relaxed); - } - - enable -} - -#[cfg(feature = "std")] -thread_local! { - static WRITER : RefCell> = RefCell::new(open_file()); -} - -/// Write a line with the given format arguments. -/// -/// This is for use by the `dbg!` macro. -#[cfg(feature = "std")] -pub fn writeln_with_format_args(args: fmt::Arguments) -> io::Result<()> { - WRITER.with(|rc| { - let mut w = rc.borrow_mut(); - writeln!(*w, "{}", args)?; - w.flush() - }) -} - -/// Open the tracing file for the current thread. -#[cfg(feature = "std")] -fn open_file() -> io::BufWriter { - let curthread = thread::current(); - let tmpstr; - let mut path = "cranelift.dbg.".to_owned(); - path.extend( - match curthread.name() { - Some(name) => name.chars(), - // The thread is unnamed, so use the thread ID instead. - None => { - tmpstr = format!("{:?}", curthread.id()); - tmpstr.chars() - } - }.filter(|ch| ch.is_alphanumeric() || *ch == '-' || *ch == '_'), - ); - let file = File::create(path).expect("Can't open tracing file"); - io::BufWriter::new(file) -} - -/// Write a line to the debug trace file if tracing is enabled. -/// -/// Arguments are the same as for `printf!`. -#[cfg(feature = "std")] -#[macro_export] -macro_rules! dbg { - ($($arg:tt)+) => { - if $crate::dbg::enabled() { - // Drop the error result so we don't get compiler errors for ignoring it. - // What are you going to do, log the error? - $crate::dbg::writeln_with_format_args(format_args!($($arg)+)).ok(); - } - } -} - -/// `dbg!` isn't supported in `no_std` mode, so expand it into nothing. -#[cfg(not(feature = "std"))] -#[macro_export] -macro_rules! dbg { - ($($arg:tt)+) => {}; -} +/// Prefix added to the log file names, just before the thread name or id. +pub static LOG_FILENAME_PREFIX: &str = "cranelift.dbg."; /// Helper for printing lists. pub struct DisplayList<'a, T>(pub &'a [T]) diff --git a/lib/codegen/src/ir/layout.rs b/lib/codegen/src/ir/layout.rs index 9f03969061..a9dc08d786 100644 --- a/lib/codegen/src/ir/layout.rs +++ b/lib/codegen/src/ir/layout.rs @@ -316,7 +316,7 @@ impl Layout { next_inst = self.insts[inst].next.expand(); } } - dbg!("Renumbered {} program points", seq / MAJOR_STRIDE); + debug!("Renumbered {} program points", seq / MAJOR_STRIDE); } } diff --git a/lib/codegen/src/isa/x86/abi.rs b/lib/codegen/src/isa/x86/abi.rs index d0305d918b..21796e2a10 100644 --- a/lib/codegen/src/isa/x86/abi.rs +++ b/lib/codegen/src/isa/x86/abi.rs @@ -217,6 +217,7 @@ fn callee_saved_gprs(isa: &TargetIsa) -> &'static [RU] { } } +/// Get the set of callee-saved registers that are used. fn callee_saved_gprs_used(isa: &TargetIsa, func: &ir::Function) -> RegisterSet { let mut all_callee_saved = RegisterSet::empty(); for reg in callee_saved_gprs(isa) { diff --git a/lib/codegen/src/legalizer/boundary.rs b/lib/codegen/src/legalizer/boundary.rs index 640c3a82b0..6f3143028b 100644 --- a/lib/codegen/src/legalizer/boundary.rs +++ b/lib/codegen/src/legalizer/boundary.rs @@ -270,7 +270,7 @@ where // Reconstruct how `ty` was legalized into the `arg_type` argument. let conversion = legalize_abi_value(ty, &arg_type); - dbg!("convert_from_abi({}): {:?}", ty, conversion); + debug!("convert_from_abi({}): {:?}", ty, conversion); // The conversion describes value to ABI argument. We implement the reverse conversion here. match conversion { @@ -279,7 +279,7 @@ where let abi_ty = ty.half_width().expect("Invalid type for conversion"); let lo = convert_from_abi(pos, abi_ty, None, get_arg); let hi = convert_from_abi(pos, abi_ty, None, get_arg); - dbg!( + debug!( "intsplit {}: {}, {}: {}", lo, pos.func.dfg.value_type(lo), @@ -586,7 +586,7 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph // the legalized signature. These values should simply be propagated from the entry block // arguments. if special_args > 0 { - dbg!( + debug!( "Adding {} special-purpose arguments to {}", special_args, pos.func.dfg.display_inst(inst, None) diff --git a/lib/codegen/src/legalizer/call.rs b/lib/codegen/src/legalizer/call.rs index 365ba3988d..21753d19db 100644 --- a/lib/codegen/src/legalizer/call.rs +++ b/lib/codegen/src/legalizer/call.rs @@ -8,7 +8,8 @@ use flowgraph::ControlFlowGraph; use ir::{self, InstBuilder}; use isa::TargetIsa; -/// Expand a `call` instruction. +/// Expand a `call` instruction. This lowers it to a `call_indirect`, which +/// is only done if the ABI doesn't support direct calls. pub fn expand_call( inst: ir::Inst, func: &mut ir::Function, diff --git a/lib/codegen/src/lib.rs b/lib/codegen/src/lib.rs index 23959e67ac..10e8a7a461 100644 --- a/lib/codegen/src/lib.rs +++ b/lib/codegen/src/lib.rs @@ -2,7 +2,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] -#![cfg_attr(feature = "std", warn(unstable_features))] +#![cfg_attr(feature = "std", deny(unstable_features))] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature="cargo-clippy", allow( // This requires Rust 1.27 or later. @@ -49,6 +49,9 @@ extern crate failure_derive; #[cfg_attr(test, macro_use)] extern crate target_lexicon; +#[macro_use] +extern crate log; + pub use context::Context; pub use legalizer::legalize_function; pub use verifier::verify_function; @@ -59,15 +62,12 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); #[macro_use] pub extern crate cranelift_entity as entity; - pub extern crate cranelift_bforest as bforest; -#[macro_use] -pub mod dbg; - pub mod binemit; pub mod cfg_printer; pub mod cursor; +pub mod dbg; pub mod dominator_tree; pub mod flowgraph; pub mod ir; diff --git a/lib/codegen/src/regalloc/coalescing.rs b/lib/codegen/src/regalloc/coalescing.rs index b9415524a0..c7db253767 100644 --- a/lib/codegen/src/regalloc/coalescing.rs +++ b/lib/codegen/src/regalloc/coalescing.rs @@ -6,7 +6,6 @@ //! parameter will belong to the same virtual register as the EBB parameter value itself. use cursor::{Cursor, EncCursor}; -#[cfg(feature = "std")] use dbg::DisplayList; use dominator_tree::{DominatorTree, DominatorTreePreorder}; use flowgraph::{BasicBlock, ControlFlowGraph}; @@ -116,7 +115,7 @@ impl Coalescing { virtregs: &mut VirtRegs, ) { let _tt = timing::ra_cssa(); - dbg!("Coalescing for:\n{}", func.display(isa)); + debug!("Coalescing for:\n{}", func.display(isa)); self.preorder.compute(domtree, &func.layout); let mut context = Context { isa, @@ -185,7 +184,7 @@ impl<'a> Context<'a> { continue; } - dbg!( + debug!( " - checking {} params at back-edge {}: {}", num_params, pred_ebb, @@ -229,7 +228,7 @@ impl<'a> Context<'a> { if Some(def_ebb) == self.func.layout.entry_block() && self.func.signature.params[def_num].location.is_stack() { - dbg!("-> isolating function stack parameter {}", arg); + debug!("-> isolating function stack parameter {}", arg); let new_arg = self.isolate_arg(pred_ebb, pred_inst, argnum, arg); self.virtregs.union(param, new_arg); continue; @@ -296,7 +295,7 @@ impl<'a> Context<'a> { let inst = pos.built_inst(); self.liveness.move_def_locally(param, inst); - dbg!( + debug!( "-> inserted {}, following {}({}: {})", pos.display_inst(inst), ebb, @@ -365,7 +364,7 @@ impl<'a> Context<'a> { pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy; - dbg!( + debug!( "-> inserted {}, before {}: {}", pos.display_inst(inst), pred_ebb, @@ -381,7 +380,7 @@ impl<'a> Context<'a> { /// closure of the relation formed by EBB parameter-argument pairs found by `union_find_ebb()`. fn finish_union_find(&mut self) { self.virtregs.finish_union_find(None); - dbg!("After union-find phase:{}", self.virtregs); + debug!("After union-find phase:{}", self.virtregs); } } @@ -412,7 +411,7 @@ impl<'a> Context<'a> { fn check_vreg(&mut self, vreg: VirtReg) -> bool { // Order the values according to the dominator pre-order of their definition. let values = self.virtregs.sort_values(vreg, self.func, self.preorder); - dbg!("Checking {} = {}", vreg, DisplayList(values)); + debug!("Checking {} = {}", vreg, DisplayList(values)); // Now push the values in order to the dominator forest. // This gives us the closest dominating value def for each of the values. @@ -434,7 +433,7 @@ impl<'a> Context<'a> { let ctx = self.liveness.context(&self.func.layout); if self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx) { // The two values are interfering, so they can't be in the same virtual register. - dbg!("-> interference: {} overlaps def of {}", parent, value); + debug!("-> interference: {} overlaps def of {}", parent, value); return false; } } @@ -458,7 +457,7 @@ impl<'a> Context<'a> { self.cfg, self.preorder, ); - dbg!( + debug!( "Synthesizing {} from {} branches and params {}", vreg, self.vcopies.branches.len(), @@ -546,7 +545,7 @@ impl<'a> Context<'a> { } let _vreg = self.virtregs.unify(self.values); - dbg!("-> merged into {} = {}", _vreg, DisplayList(self.values)); + debug!("-> merged into {} = {}", _vreg, DisplayList(self.values)); true } @@ -568,7 +567,7 @@ impl<'a> Context<'a> { // registers and the filtered virtual copies. let v0 = self.virtregs.congruence_class(¶m); let v1 = self.virtregs.congruence_class(&arg); - dbg!( + debug!( " - set 0: {}\n - set 1: {}", DisplayList(v0), DisplayList(v1) @@ -621,7 +620,7 @@ impl<'a> Context<'a> { if node.set_id != parent.set_id && self.liveness[parent.value].reaches_use(inst, node.ebb, ctx) { - dbg!( + debug!( " - interference: {} overlaps vcopy at {}:{}", parent, node.ebb, @@ -646,7 +645,7 @@ impl<'a> Context<'a> { && self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx) { // The two values are interfering. - dbg!(" - interference: {} overlaps def of {}", parent, node.value); + debug!(" - interference: {} overlaps def of {}", parent, node.value); return false; } } diff --git a/lib/codegen/src/regalloc/coloring.rs b/lib/codegen/src/regalloc/coloring.rs index b824246d5b..56691eecf3 100644 --- a/lib/codegen/src/regalloc/coloring.rs +++ b/lib/codegen/src/regalloc/coloring.rs @@ -124,7 +124,7 @@ impl Coloring { tracker: &mut LiveValueTracker, ) { let _tt = timing::ra_coloring(); - dbg!("Coloring for:\n{}", func.display(isa)); + debug!("Coloring for:\n{}", func.display(isa)); let mut ctx = Context { usable_regs: isa.allocatable_registers(func), cur: EncCursor::new(func, isa), @@ -156,7 +156,7 @@ impl<'a> Context<'a> { /// Visit `ebb`, assuming that the immediate dominator has already been visited. fn visit_ebb(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) { - dbg!("Coloring {}:", ebb); + debug!("Coloring {}:", ebb); let mut regs = self.visit_ebb_header(ebb, tracker); tracker.drop_dead_params(); self.divert.clear(); @@ -216,7 +216,7 @@ impl<'a> Context<'a> { let mut regs = AvailableRegs::new(&self.usable_regs); for lv in live.iter().filter(|lv| !lv.is_dead) { - dbg!( + debug!( "Live-in: {}:{} in {}", lv.value, lv.affinity.display(&self.reginfo), @@ -294,7 +294,7 @@ impl<'a> Context<'a> { tracker: &mut LiveValueTracker, regs: &mut AvailableRegs, ) -> bool { - dbg!( + debug!( "Coloring {}\n from {}", self.cur.display_inst(inst), regs.input.display(&self.reginfo), @@ -362,7 +362,7 @@ impl<'a> Context<'a> { if let Affinity::Reg(rci) = lv.affinity { let rc = self.reginfo.rc(rci); let reg = self.divert.reg(lv.value, &self.cur.func.locations); - dbg!( + debug!( " kill {} in {} ({} {})", lv.value, self.reginfo.display_regunit(reg), @@ -380,7 +380,7 @@ impl<'a> Context<'a> { } // This aligns with the " from" line at the top of the function. - dbg!(" glob {}", regs.global.display(&self.reginfo)); + debug!(" glob {}", regs.global.display(&self.reginfo)); // This flag is set when the solver failed to find a solution for the global defines that // doesn't interfere with `regs.global`. We need to rewrite all of `inst`s global defines @@ -419,7 +419,7 @@ impl<'a> Context<'a> { // Finally, we've fully programmed the constraint solver. // We expect a quick solution in most cases. let output_regs = self.solver.quick_solve(®s.global).unwrap_or_else(|_| { - dbg!("quick_solve failed for {}", self.solver); + debug!("quick_solve failed for {}", self.solver); self.iterate_solution(throughs, ®s.global, &mut replace_global_defines) }); @@ -454,7 +454,7 @@ impl<'a> Context<'a> { regs.input = output_regs; for lv in defs { let loc = self.cur.func.locations[lv.value]; - dbg!( + debug!( " color {} -> {}{}", lv.value, loc.display(&self.reginfo), @@ -700,7 +700,7 @@ impl<'a> Context<'a> { ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => { self.add_fixed_output(lv.value, op.regclass, reg, throughs); if !lv.is_local && !global_regs.is_avail(op.regclass, reg) { - dbg!( + debug!( "Fixed output {} in {}:{} is not available in global regs", lv.value, op.regclass, @@ -736,7 +736,7 @@ impl<'a> Context<'a> { let rc = self.reginfo.rc(rci); self.add_fixed_output(lv.value, rc, reg, throughs); if !lv.is_local && !global_regs.is_avail(rc, reg) { - dbg!( + debug!( "ABI output {} in {}:{} is not available in global regs", lv.value, rc, @@ -812,7 +812,7 @@ impl<'a> Context<'a> { // We need to make sure that fixed output register is compatible with the // global register set. if !lv.is_local && !global_regs.is_avail(op.regclass, reg) { - dbg!( + debug!( "Tied output {} in {}:{} is not available in global regs", lv.value, op.regclass, @@ -848,7 +848,7 @@ impl<'a> Context<'a> { debug_assert!(added, "Ran out of registers in {}", rc); } Err(SolverError::Global(_value)) => { - dbg!( + debug!( "Not enough global registers for {}, trying as local", _value ); @@ -863,7 +863,7 @@ impl<'a> Context<'a> { /// Try to add an `rc` variable to the solver from the `throughs` set. fn try_add_var(&mut self, rc: RegClass, throughs: &[LiveValue]) -> bool { - dbg!("Trying to add a {} reg from {} values", rc, throughs.len()); + debug!("Trying to add a {} reg from {} values", rc, throughs.len()); for lv in throughs { if let Affinity::Reg(rci) = lv.affinity { @@ -995,7 +995,7 @@ impl<'a> Context<'a> { /// the constraints on the instruction operands. /// fn replace_global_defines(&mut self, inst: Inst, tracker: &mut LiveValueTracker) { - dbg!("Replacing global defs on {}", self.cur.display_inst(inst)); + debug!("Replacing global defs on {}", self.cur.display_inst(inst)); // We'll insert copies *after `inst`. Our caller will move the cursor back. self.cur.next_inst(); @@ -1042,14 +1042,14 @@ impl<'a> Context<'a> { lv.endpoint = copy; lv.is_local = true; - dbg!( + debug!( " + {} with {} in {}", self.cur.display_inst(copy), local, loc.display(&self.reginfo) ); } - dbg!("Done: {}", self.cur.display_inst(inst)); + debug!("Done: {}", self.cur.display_inst(inst)); } /// Process kills on a ghost instruction. diff --git a/lib/codegen/src/regalloc/reload.rs b/lib/codegen/src/regalloc/reload.rs index a712616af8..f07f35cea0 100644 --- a/lib/codegen/src/regalloc/reload.rs +++ b/lib/codegen/src/regalloc/reload.rs @@ -72,7 +72,7 @@ impl Reload { tracker: &mut LiveValueTracker, ) { let _tt = timing::ra_reload(); - dbg!("Reload for:\n{}", func.display(isa)); + debug!("Reload for:\n{}", func.display(isa)); let mut ctx = Context { cur: EncCursor::new(func, isa), encinfo: isa.encoding_info(), @@ -119,7 +119,7 @@ impl<'a> Context<'a> { } fn visit_ebb(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) { - dbg!("Reloading {}:", ebb); + debug!("Reloading {}:", ebb); self.visit_ebb_header(ebb, tracker); tracker.drop_dead_params(); diff --git a/lib/codegen/src/regalloc/solver.rs b/lib/codegen/src/regalloc/solver.rs index 1140aa7536..283a461378 100644 --- a/lib/codegen/src/regalloc/solver.rs +++ b/lib/codegen/src/regalloc/solver.rs @@ -532,7 +532,7 @@ impl Solver { /// In either case, `to` will not be available for variables on the input side of the /// instruction. pub fn reassign_in(&mut self, value: Value, rc: RegClass, from: RegUnit, to: RegUnit) { - dbg!( + debug!( "reassign_in({}:{}, {} -> {})", value, rc, @@ -545,7 +545,7 @@ impl Solver { // added as a variable previously. A fixed constraint beats a variable, so convert it. if let Some(idx) = self.vars.iter().position(|v| v.value == value) { let v = self.vars.remove(idx); - dbg!("-> converting variable {} to a fixed constraint", v); + debug!("-> converting variable {} to a fixed constraint", v); // The spiller is responsible for ensuring that all constraints on the uses of a // value are compatible. debug_assert!( @@ -578,7 +578,7 @@ impl Solver { /// This function can only be used before calling `inputs_done()`. Afterwards, more input-side /// variables can be added by calling `add_killed_var()` and `add_through_var()` pub fn add_var(&mut self, value: Value, constraint: RegClass, from: RegUnit) { - dbg!( + debug!( "add_var({}:{}, from={})", value, constraint, @@ -593,7 +593,7 @@ impl Solver { /// /// This function should be called after `inputs_done()` only. Use `add_var()` before. pub fn add_killed_var(&mut self, value: Value, constraint: RegClass, from: RegUnit) { - dbg!( + debug!( "add_killed_var({}:{}, from={})", value, constraint, @@ -608,7 +608,7 @@ impl Solver { /// /// This function should be called after `inputs_done()` only. Use `add_var()` before. pub fn add_through_var(&mut self, value: Value, constraint: RegClass, from: RegUnit) { - dbg!( + debug!( "add_through_var({}:{}, from={})", value, constraint, @@ -635,7 +635,7 @@ impl Solver { if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) { // We have an existing variable entry for `value`. Combine the constraints. if let Some(rc) = v.constraint.intersect(constraint) { - dbg!("-> combining constraint with {} yields {}", v, rc); + debug!("-> combining constraint with {} yields {}", v, rc); v.constraint = rc; return; } else { @@ -647,7 +647,7 @@ impl Solver { // No variable, then it must be a fixed reassignment. if let Some(a) = self.assignments.get(value) { - dbg!("-> already fixed assignment {}", a); + debug!("-> already fixed assignment {}", a); debug_assert!( constraint.contains(a.to), "Incompatible constraints for {}", @@ -656,12 +656,12 @@ impl Solver { return; } - dbg!("{}", self); + debug!("{}", self); panic!("Wrong from register for {}", value); } let new_var = Variable::new_live(value, constraint, from, live_through); - dbg!("-> new var: {}", new_var); + debug!("-> new var: {}", new_var); self.regs_in.free(constraint, from); if self.inputs_done && live_through { @@ -777,7 +777,7 @@ impl Solver { if is_global { let mut new_var = Variable::new_live(value, rc, reg, true); new_var.is_global = true; - dbg!("add_tied_input: new tied-global value: {}", new_var); + debug!("add_tied_input: new tied-global value: {}", new_var); self.vars.push(new_var); self.regs_in.free(rc, reg); } else { @@ -899,7 +899,7 @@ impl Solver { ) }); - dbg!("real_solve for {}", self); + debug!("real_solve for {}", self); self.find_solution(global_regs) } @@ -982,7 +982,7 @@ impl Solver { .extend(self.assignments.values().filter_map(Move::with_assignment)); if !(self.moves.is_empty()) { - dbg!("collect_moves: {}", DisplayList(&self.moves)); + debug!("collect_moves: {}", DisplayList(&self.moves)); } } @@ -1024,7 +1024,7 @@ impl Solver { if let Some((rc, reg)) = m.from_reg() { avail.free(rc, reg); } - dbg!("move #{}: {}", i, m); + debug!("move #{}: {}", i, m); i += 1; continue; } @@ -1058,7 +1058,7 @@ impl Solver { let m = self.moves[i].clone(); let toprc = m.rc().toprc(); if let Some(reg) = avail.iter(toprc).next() { - dbg!( + debug!( "breaking cycle at {} with available {} register {}", m, toprc, @@ -1089,7 +1089,7 @@ impl Solver { // a last resort. let slot = num_spill_slots; num_spill_slots += 1; - dbg!("breaking cycle at {} with slot {}", m, slot); + debug!("breaking cycle at {} with slot {}", m, slot); let old_to_reg = self.moves[i].change_to_spill(slot); self.fills.push(Move::Fill { value: m.value(), diff --git a/lib/codegen/src/regalloc/spilling.rs b/lib/codegen/src/regalloc/spilling.rs index 457b07c663..d1244c0774 100644 --- a/lib/codegen/src/regalloc/spilling.rs +++ b/lib/codegen/src/regalloc/spilling.rs @@ -90,7 +90,7 @@ impl Spilling { tracker: &mut LiveValueTracker, ) { let _tt = timing::ra_spilling(); - dbg!("Spilling for:\n{}", func.display(isa)); + debug!("Spilling for:\n{}", func.display(isa)); let reginfo = isa.register_info(); let usable_regs = isa.allocatable_registers(func); let mut ctx = Context { @@ -118,7 +118,7 @@ impl<'a> Context<'a> { } fn visit_ebb(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) { - dbg!("Spilling {}:", ebb); + debug!("Spilling {}:", ebb); self.cur.goto_top(ebb); self.visit_ebb_header(ebb, tracker); tracker.drop_dead_params(); @@ -198,14 +198,12 @@ impl<'a> Context<'a> { if let Affinity::Reg(rci) = lv.affinity { let rc = self.reginfo.rc(rci); 'try_take: while let Err(mask) = self.pressure.take_transient(rc) { - dbg!("Need {} reg for EBB param {}", rc, lv.value); + debug!("Need {} reg for EBB param {}", rc, lv.value); match self.spill_candidate(mask, liveins) { Some(cand) => { - dbg!( + debug!( "Spilling live-in {} to make room for {} EBB param {}", - cand, - rc, - lv.value + cand, rc, lv.value ); self.spill_reg(cand); } @@ -213,7 +211,7 @@ impl<'a> Context<'a> { // We can't spill any of the live-in registers, so we have to spill an // EBB argument. Since the current spill metric would consider all the // EBB arguments equal, just spill the present register. - dbg!("Spilling {} EBB argument {}", rc, lv.value); + debug!("Spilling {} EBB argument {}", rc, lv.value); // Since `spill_reg` will free a register, add the current one here. self.pressure.take(rc); @@ -237,7 +235,7 @@ impl<'a> Context<'a> { constraints: &RecipeConstraints, tracker: &mut LiveValueTracker, ) { - dbg!("Inst {}, {}", self.cur.display_inst(inst), self.pressure); + debug!("Inst {}, {}", self.cur.display_inst(inst), self.pressure); debug_assert_eq!(self.cur.current_inst(), Some(inst)); debug_assert_eq!(self.cur.current_ebb(), Some(ebb)); @@ -279,7 +277,7 @@ impl<'a> Context<'a> { if op.kind != ConstraintKind::Stack { // Add register def to pressure, spill if needed. while let Err(mask) = self.pressure.take_transient(op.regclass) { - dbg!("Need {} reg from {} throughs", op.regclass, throughs.len()); + debug!("Need {} reg from {} throughs", op.regclass, throughs.len()); match self.spill_candidate(mask, throughs) { Some(cand) => self.spill_reg(cand), None => panic!( @@ -333,7 +331,7 @@ impl<'a> Context<'a> { // Only collect the interesting register uses. if reguse.fixed || reguse.tied || reguse.spilled { - dbg!(" reguse: {}", reguse); + debug!(" reguse: {}", reguse); self.reg_uses.push(reguse); } } @@ -406,7 +404,7 @@ impl<'a> Context<'a> { if need_copy || ru.spilled { let rc = self.reginfo.rc(ru.rci); while let Err(mask) = self.pressure.take_transient(rc) { - dbg!("Copy of {} reg causes spill", rc); + debug!("Copy of {} reg causes spill", rc); // Spill a live register that is *not* used by the current instruction. // Spilling a use wouldn't help. // @@ -489,7 +487,7 @@ impl<'a> Context<'a> { let rc = self.reginfo.rc(rci); self.pressure.free(rc); self.spills.push(value); - dbg!("Spilled {}:{} -> {}", value, rc, self.pressure); + debug!("Spilled {}:{} -> {}", value, rc, self.pressure); } else { panic!("Cannot spill {} that was already on the stack", value); } diff --git a/lib/codegen/src/timing.rs b/lib/codegen/src/timing.rs index 707efbf3f8..b340796578 100644 --- a/lib/codegen/src/timing.rs +++ b/lib/codegen/src/timing.rs @@ -179,7 +179,7 @@ mod details { /// This function is called by the publicly exposed pass functions. pub(super) fn start_pass(pass: Pass) -> TimingToken { let prev = CURRENT_PASS.with(|p| p.replace(pass)); - dbg!("timing: Starting {}, (during {})", pass, prev); + debug!("timing: Starting {}, (during {})", pass, prev); TimingToken { start: Instant::now(), pass, @@ -191,7 +191,7 @@ mod details { impl Drop for TimingToken { fn drop(&mut self) { let duration = self.start.elapsed(); - dbg!("timing: Ending {}", self.pass); + debug!("timing: Ending {}", self.pass); let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev)); debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order"); PASS_TIME.with(|rc| { diff --git a/lib/codegen/src/unreachable_code.rs b/lib/codegen/src/unreachable_code.rs index 36ec7e673c..74cc6f4ea4 100644 --- a/lib/codegen/src/unreachable_code.rs +++ b/lib/codegen/src/unreachable_code.rs @@ -24,14 +24,14 @@ pub fn eliminate_unreachable_code( continue; } - dbg!("Eliminating unreachable {}", ebb); + debug!("Eliminating unreachable {}", ebb); // Move the cursor out of the way and make sure the next lop iteration goes to the right // EBB. pos.prev_ebb(); // Remove all instructions from `ebb`. while let Some(inst) = pos.func.layout.first_inst(ebb) { - dbg!(" - {}", pos.func.dfg.display_inst(inst, None)); + debug!(" - {}", pos.func.dfg.display_inst(inst, None)); pos.func.layout.remove_inst(inst); } diff --git a/lib/entity/src/lib.rs b/lib/entity/src/lib.rs index cb1c248db1..dd666c6ac7 100644 --- a/lib/entity/src/lib.rs +++ b/lib/entity/src/lib.rs @@ -31,7 +31,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] -#![cfg_attr(feature = "std", warn(unstable_features))] +#![cfg_attr(feature = "std", deny(unstable_features))] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/faerie/src/lib.rs b/lib/faerie/src/lib.rs index 14397afbd0..078488a953 100644 --- a/lib/faerie/src/lib.rs +++ b/lib/faerie/src/lib.rs @@ -2,8 +2,8 @@ //! //! Users of this module should not have to depend on faerie directly. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/filetests/Cargo.toml b/lib/filetests/Cargo.toml index ac66ca660c..487675971f 100644 --- a/lib/filetests/Cargo.toml +++ b/lib/filetests/Cargo.toml @@ -11,5 +11,7 @@ publish = false [dependencies] cranelift-codegen = { path = "../codegen", version = "0.18.1" } cranelift-reader = { path = "../reader", version = "0.18.1" } +file-per-thread-logger = "0.1.1" filecheck = "0.3.0" num_cpus = "1.8.0" +log = "0.4.3" diff --git a/lib/filetests/src/concurrent.rs b/lib/filetests/src/concurrent.rs index f874e952f2..104967d757 100644 --- a/lib/filetests/src/concurrent.rs +++ b/lib/filetests/src/concurrent.rs @@ -3,7 +3,9 @@ //! This module provides the `ConcurrentRunner` struct which uses a pool of threads to run tests //! concurrently. +use cranelift_codegen::dbg::LOG_FILENAME_PREFIX; use cranelift_codegen::timing; +use file_per_thread_logger; use num_cpus; use std::panic::catch_unwind; use std::path::{Path, PathBuf}; @@ -100,6 +102,7 @@ fn heartbeat_thread(replies: Sender) -> thread::JoinHandle<()> { thread::Builder::new() .name("heartbeat".to_string()) .spawn(move || { + file_per_thread_logger::initialize(LOG_FILENAME_PREFIX); while replies.send(Reply::Tick).is_ok() { thread::sleep(Duration::from_secs(1)); } @@ -116,6 +119,7 @@ fn worker_thread( thread::Builder::new() .name(format!("worker #{}", thread_num)) .spawn(move || { + file_per_thread_logger::initialize(LOG_FILENAME_PREFIX); loop { // Lock the mutex only long enough to extract a request. let Request(jobid, path) = match requests.lock().unwrap().recv() { @@ -140,7 +144,7 @@ fn worker_thread( }); if let Err(ref msg) = result { - dbg!("FAIL: {}", msg); + error!("FAIL: {}", msg); } replies.send(Reply::Done { jobid, result }).unwrap(); diff --git a/lib/filetests/src/lib.rs b/lib/filetests/src/lib.rs index cdeac02df9..90565b5cb4 100644 --- a/lib/filetests/src/lib.rs +++ b/lib/filetests/src/lib.rs @@ -3,8 +3,8 @@ //! This crate contains the main test driver as well as implementations of the //! available filetest commands. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "cargo-clippy", allow( type_complexity, @@ -18,11 +18,13 @@ ) )] -#[macro_use(dbg)] extern crate cranelift_codegen; extern crate cranelift_reader; +extern crate file_per_thread_logger; extern crate filecheck; extern crate num_cpus; +#[macro_use] +extern crate log; use cranelift_reader::TestCommand; use runner::TestRunner; diff --git a/lib/filetests/src/runone.rs b/lib/filetests/src/runone.rs index 8a28755003..b98f5f8cfb 100644 --- a/lib/filetests/src/runone.rs +++ b/lib/filetests/src/runone.rs @@ -29,7 +29,7 @@ fn read_to_string>(path: P) -> io::Result { /// If running this test causes a panic, it will propagate as normal. pub fn run(path: &Path) -> TestResult { let _tt = timing::process_file(); - dbg!("---\nFile: {}", path.to_string_lossy()); + info!("---\nFile: {}", path.to_string_lossy()); let started = time::Instant::now(); let buffer = read_to_string(path).map_err(|e| e.to_string())?; let testfile = parse_test(&buffer).map_err(|e| e.to_string())?; @@ -122,7 +122,7 @@ fn run_one_test<'a>( ) -> SubtestResult<()> { let (test, flags, isa) = tuple; let name = format!("{}({})", test.name(), func.name); - dbg!("Test: {} {}", name, isa.map_or("-", TargetIsa::name)); + info!("Test: {} {}", name, isa.map_or("-", TargetIsa::name)); context.flags = flags; context.isa = isa; diff --git a/lib/filetests/src/test_compile.rs b/lib/filetests/src/test_compile.rs index 0e79bab429..1fb6d0e4c0 100644 --- a/lib/filetests/src/test_compile.rs +++ b/lib/filetests/src/test_compile.rs @@ -41,7 +41,7 @@ impl SubTest for TestCompile { .compile(isa) .map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; - dbg!( + info!( "Generated {} bytes of code:\n{}", code_size, comp_ctx.func.display(isa) diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs index be19ebc2be..7fca5ae122 100644 --- a/lib/frontend/src/lib.rs +++ b/lib/frontend/src/lib.rs @@ -129,7 +129,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] -#![cfg_attr(feature = "std", warn(unstable_features))] +#![cfg_attr(feature = "std", deny(unstable_features))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default))] #![cfg_attr( feature = "cargo-clippy", diff --git a/lib/module/Cargo.toml b/lib/module/Cargo.toml index d8a2bf9ccb..a137bcebd0 100644 --- a/lib/module/Cargo.toml +++ b/lib/module/Cargo.toml @@ -13,6 +13,7 @@ cranelift-codegen = { path = "../codegen", version = "0.18.1", default-features cranelift-entity = { path = "../entity", version = "0.18.1", default-features = false } hashmap_core = { version = "0.1.9", optional = true } failure = "0.1.1" +log = { version = "0.4.3", default-features = false, features = ["release_max_level_warn"] } [features] default = ["std"] diff --git a/lib/module/src/lib.rs b/lib/module/src/lib.rs index 32b774824a..45e3bd683e 100644 --- a/lib/module/src/lib.rs +++ b/lib/module/src/lib.rs @@ -2,7 +2,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] -#![cfg_attr(feature = "std", warn(unstable_features))] +#![cfg_attr(feature = "std", deny(unstable_features))] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( @@ -20,12 +20,13 @@ #[cfg_attr(test, macro_use)] extern crate alloc; -#[macro_use] extern crate cranelift_codegen; #[macro_use] extern crate cranelift_entity; #[macro_use] extern crate failure; +#[macro_use] +extern crate log; mod backend; mod data_context; diff --git a/lib/module/src/module.rs b/lib/module/src/module.rs index d5c616db66..133ad5af9f 100644 --- a/lib/module/src/module.rs +++ b/lib/module/src/module.rs @@ -467,7 +467,7 @@ where pub fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> ModuleResult<()> { let compiled = { let code_size = ctx.compile(self.backend.isa()).map_err(|e| { - dbg!( + info!( "defining function {}: {}", func, ctx.func.display(self.backend.isa()) diff --git a/lib/native/src/lib.rs b/lib/native/src/lib.rs index 5439d743cc..3d4e4bac2c 100644 --- a/lib/native/src/lib.rs +++ b/lib/native/src/lib.rs @@ -1,8 +1,8 @@ //! Performs autodetection of the host for the purposes of running //! Cranelift to generate code to run on the same machine. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/reader/src/lib.rs b/lib/reader/src/lib.rs index edac07622f..2873a644ac 100644 --- a/lib/reader/src/lib.rs +++ b/lib/reader/src/lib.rs @@ -3,8 +3,8 @@ //! The `cranelift_reader` library supports reading .clif files. This functionality is needed for //! testing Cranelift, but is not essential for a JIT compiler. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/serde/src/clif-json.rs b/lib/serde/src/clif-json.rs index 659bc818a8..5fc8a1d469 100644 --- a/lib/serde/src/clif-json.rs +++ b/lib/serde/src/clif-json.rs @@ -1,7 +1,9 @@ //! Utility for `cranelift_serde`. -#![deny(trivial_numeric_casts)] -#![warn(unused_import_braces, unstable_features, unused_extern_crates)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] +#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] +#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( feature = "cargo-clippy", warn( diff --git a/lib/simplejit/src/backend.rs b/lib/simplejit/src/backend.rs index 0ae65a8a99..c867c0d825 100644 --- a/lib/simplejit/src/backend.rs +++ b/lib/simplejit/src/backend.rs @@ -10,6 +10,7 @@ use cranelift_module::{ use cranelift_native; use libc; use memory::Memory; +use std::collections::HashMap; use std::ffi::CString; use std::ptr; use target_lexicon::PointerWidth; @@ -19,6 +20,7 @@ use winapi; /// A builder for `SimpleJITBackend`. pub struct SimpleJITBuilder { isa: Box, + symbols: HashMap, } impl SimpleJITBuilder { @@ -40,7 +42,44 @@ impl SimpleJITBuilder { /// instead. pub fn with_isa(isa: Box) -> Self { debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code"); - Self { isa } + let symbols = HashMap::new(); + Self { isa, symbols } + } + + /// Define a symbol in the internal symbol table. + /// + /// The JIT will use the symbol table to resolve names that are declared, + /// but not defined, in the module being compiled. A common example is + /// external functions. With this method, functions and data can be exposed + /// to the code being compiled which are defined by the host. + /// + /// If a symbol is defined more than once, the most recent definition will + /// be retained. + /// + /// If the JIT fails to find a symbol in its internal table, it will fall + /// back to a platform-specific search (this typically involves searching + /// the current process for public symbols, followed by searching the + /// platform's C runtime). + pub fn symbol<'a, K>(&'a mut self, name: K, ptr: *const u8) -> &'a mut Self + where + K: Into, + { + self.symbols.insert(name.into(), ptr); + self + } + + /// Define multiple symbols in the internal symbol table. + /// + /// Using this is equivalent to calling `symbol` on each element. + pub fn symbols<'a, It, K>(&'a mut self, symbols: It) -> &'a mut Self + where + It: IntoIterator, + K: Into, + { + for (name, ptr) in symbols { + self.symbols.insert(name.into(), ptr); + } + self } } @@ -48,6 +87,7 @@ impl SimpleJITBuilder { /// directly called and accessed. pub struct SimpleJITBackend { isa: Box, + symbols: HashMap, code_memory: Memory, readonly_memory: Memory, writable_memory: Memory, @@ -73,6 +113,15 @@ pub struct SimpleJITCompiledData { relocs: Vec, } +impl SimpleJITBackend { + fn lookup_symbol(&self, name: &str) -> *const u8 { + match self.symbols.get(name) { + Some(&ptr) => ptr, + None => lookup_with_dlsym(name), + } + } +} + impl<'simple_jit_backend> Backend for SimpleJITBackend { type Builder = SimpleJITBuilder; @@ -96,6 +145,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend { fn new(builder: SimpleJITBuilder) -> Self { Self { isa: builder.isa, + symbols: builder.symbols, code_memory: Memory::new(), readonly_memory: Memory::new(), writable_memory: Memory::new(), @@ -249,13 +299,13 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend { let (def, name_str, _signature) = namespace.get_function_definition(&name); match def { Some(compiled) => compiled.code, - None => lookup_with_dlsym(name_str), + None => self.lookup_symbol(name_str), } } else { let (def, name_str, _writable) = namespace.get_data_definition(&name); match def { Some(compiled) => compiled.storage, - None => lookup_with_dlsym(name_str), + None => self.lookup_symbol(name_str), } }; // TODO: Handle overflow. @@ -314,13 +364,13 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend { let (def, name_str, _signature) = namespace.get_function_definition(&name); match def { Some(compiled) => compiled.code, - None => lookup_with_dlsym(name_str), + None => self.lookup_symbol(name_str), } } else { let (def, name_str, _writable) = namespace.get_data_definition(&name); match def { Some(compiled) => compiled.storage, - None => lookup_with_dlsym(name_str), + None => self.lookup_symbol(name_str), } }; // TODO: Handle overflow. diff --git a/lib/simplejit/src/lib.rs b/lib/simplejit/src/lib.rs index 01dd0938f0..99297f33b0 100644 --- a/lib/simplejit/src/lib.rs +++ b/lib/simplejit/src/lib.rs @@ -1,7 +1,7 @@ //! Top-level lib.rs for `cranelift_simplejit`. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/umbrella/src/lib.rs b/lib/umbrella/src/lib.rs index ad241483e7..4faeabdd8a 100644 --- a/lib/umbrella/src/lib.rs +++ b/lib/umbrella/src/lib.rs @@ -1,7 +1,7 @@ //! Cranelift umbrella crate, providing a convenient one-line dependency. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces, unstable_features)] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)] +#![warn(unused_import_braces)] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index abc33510a1..4e40b5022d 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -17,6 +17,7 @@ hashmap_core = { version = "0.1.9", optional = true } failure = { version = "0.1.1", default-features = false, features = ["derive"] } failure_derive = { version = "0.1.1", default-features = false } target-lexicon = { version = "0.0.3", default-features = false } +log = { version = "0.4.3", default-features = false, features = ["release_max_level_warn"] } [dev-dependencies] wabt = "0.4" diff --git a/lib/wasm/src/environ/dummy.rs b/lib/wasm/src/environ/dummy.rs index 21ee1d08a3..5077612529 100644 --- a/lib/wasm/src/environ/dummy.rs +++ b/lib/wasm/src/environ/dummy.rs @@ -1,4 +1,5 @@ -//! "Dummy" environment for testing wasm translation. +//! "Dummy" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing +//! wasm translation. use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::immediates::Imm64; diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index 8f764c102a..f9ab937ad4 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -67,7 +67,7 @@ impl FuncTranslator { environ: &mut FE, ) -> WasmResult<()> { let _tt = timing::wasm_translate_function(); - dbg!( + info!( "translate({} bytes, {}{})", reader.bytes_remaining(), func.name, @@ -265,7 +265,7 @@ mod tests { trans .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); - dbg!("{}", ctx.func.display(None)); + debug!("{}", ctx.func.display(None)); ctx.verify(runtime.func_env().flags()).unwrap(); } @@ -296,7 +296,7 @@ mod tests { trans .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); - dbg!("{}", ctx.func.display(None)); + debug!("{}", ctx.func.display(None)); ctx.verify(runtime.func_env().flags()).unwrap(); } @@ -335,7 +335,7 @@ mod tests { trans .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); - dbg!("{}", ctx.func.display(None)); + debug!("{}", ctx.func.display(None)); ctx.verify(runtime.func_env().flags()).unwrap(); } } diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index 7606ebc0b4..a559ae941d 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -11,7 +11,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] -#![cfg_attr(feature = "std", warn(unstable_features))] +#![cfg_attr(feature = "std", deny(unstable_features))] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))] #![cfg_attr( @@ -24,7 +24,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] -#[macro_use(dbg)] extern crate cranelift_codegen; #[macro_use] extern crate cranelift_entity; @@ -36,6 +35,9 @@ extern crate failure; #[macro_use] extern crate failure_derive; +#[macro_use] +extern crate log; + mod code_translator; mod environ; mod func_translator;