use std::fmt;
pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
macro_rules! define_passes {
{ $enum:ident, $num_passes:ident, $descriptions:ident;
$($pass:ident: $desc:expr,)+
} => {
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum $enum { $($pass,)+ None}
const $num_passes: usize = $enum::None as usize;
const $descriptions: [&str; $num_passes] = [ $($desc),+ ];
$(
#[doc=$desc]
pub fn $pass() -> TimingToken {
details::start_pass($enum::$pass)
}
)+
}
}
define_passes!{
Pass, NUM_PASSES, DESCRIPTIONS;
process_file: "Processing test file",
parse_text: "Parsing textual Cranelift IR",
wasm_translate_module: "Translate WASM module",
wasm_translate_function: "Translate WASM function",
verifier: "Verify Cranelift IR",
verify_cssa: "Verify CSSA",
verify_liveness: "Verify live ranges",
verify_locations: "Verify value locations",
verify_flags: "Verify CPU flags",
compile: "Compilation passes",
flowgraph: "Control flow graph",
domtree: "Dominator tree",
loop_analysis: "Loop analysis",
postopt: "Post-legalization rewriting",
preopt: "Pre-legalization rewriting",
dce: "Dead code elimination",
legalize: "Legalization",
gvn: "Global value numbering",
licm: "Loop invariant code motion",
unreachable_code: "Remove unreachable blocks",
regalloc: "Register allocation",
ra_liveness: "RA liveness analysis",
ra_cssa: "RA coalescing CSSA",
ra_spilling: "RA spilling",
ra_reload: "RA reloading",
ra_coloring: "RA coloring",
prologue_epilogue: "Prologue/epilogue insertion",
shrink_instructions: "Instruction encoding shrinking",
relax_branches: "Branch relaxation",
binemit: "Binary machine code emission",
layout_renumber: "Layout full renumbering",
canonicalize_nans: "Canonicalization of NaNs",
}
impl Pass {
pub fn idx(self) -> usize {
self as usize
}
}
impl fmt::Display for Pass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match DESCRIPTIONS.get(self.idx()) {
Some(s) => f.write_str(s),
None => f.write_str("<no pass>"),
}
}
}
#[cfg(feature = "std")]
mod details {
use super::{Pass, DESCRIPTIONS, NUM_PASSES};
use std::cell::{Cell, RefCell};
use std::fmt;
use std::mem;
use std::time::{Duration, Instant};
pub struct TimingToken {
start: Instant,
pass: Pass,
prev: Pass,
}
#[derive(Default)]
struct PassTime {
total: Duration,
child: Duration,
}
#[derive(Default)]
pub struct PassTimes {
pass: [PassTime; NUM_PASSES],
}
impl fmt::Display for PassTimes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "======== ======== ==================================")?;
writeln!(f, " Total Self Pass")?;
writeln!(f, "-------- -------- ----------------------------------")?;
for (time, desc) in self.pass.iter().zip(&DESCRIPTIONS) {
if time.total == Duration::default() {
continue;
}
fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
dur += Duration::new(0, 500_000);
write!(f, "{:4}.{:03} ", dur.as_secs(), dur.subsec_millis())
}
fmtdur(time.total, f)?;
if let Some(s) = time.total.checked_sub(time.child) {
fmtdur(s, f)?;
}
writeln!(f, " {}", desc)?;
}
writeln!(f, "======== ======== ==================================")
}
}
thread_local!{
static CURRENT_PASS: Cell<Pass> = Cell::new(Pass::None);
static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
}
pub(super) fn start_pass(pass: Pass) -> TimingToken {
let prev = CURRENT_PASS.with(|p| p.replace(pass));
dbg!("timing: Starting {}, (during {})", pass, prev);
TimingToken {
start: Instant::now(),
pass,
prev,
}
}
impl Drop for TimingToken {
fn drop(&mut self) {
let duration = self.start.elapsed();
dbg!("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| {
let mut table = rc.borrow_mut();
table.pass[self.pass.idx()].total += duration;
if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
parent.child += duration;
}
})
}
}
pub fn take_current() -> PassTimes {
PASS_TIME.with(|rc| mem::replace(&mut *rc.borrow_mut(), Default::default()))
}
pub fn add_to_current(times: &PassTimes) {
PASS_TIME.with(|rc| {
for (a, b) in rc.borrow_mut().pass.iter_mut().zip(×.pass) {
a.total += b.total;
a.child += b.child;
}
})
}
}
#[cfg(not(feature = "std"))]
mod details {
use super::Pass;
pub struct TimingToken;
pub struct PassTimes;
pub fn take_current() -> PassTimes {
PassTimes
}
pub fn add_to_current(_times: PassTimes) {}
pub(super) fn start_pass(_pass: Pass) -> TimingToken {
TimingToken
}
}
#[cfg(test)]
mod test {
use super::*;
use std::string::ToString;
#[test]
fn display() {
assert_eq!(Pass::None.to_string(), "<no pass>");
assert_eq!(Pass::regalloc.to_string(), "Register allocation");
}
}