Add a compilation pass timing facility.
Individual compilation passes call the corresponding timing::*() function and hold on to their timing token while they run. This causes nested per-pass timing information to be recorded in thread-local storage. The --time-passes command line option prints a pass timing report to stdout.
This commit is contained in:
@@ -10,7 +10,7 @@ extern crate num_cpus;
|
|||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
extern crate term;
|
extern crate term;
|
||||||
|
|
||||||
use cretonne::VERSION;
|
use cretonne::{VERSION, timing};
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::process;
|
use std::process;
|
||||||
@@ -27,16 +27,18 @@ const USAGE: &str = "
|
|||||||
Cretonne code generator utility
|
Cretonne code generator utility
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
cton-util test [-v] <file>...
|
cton-util test [-vT] <file>...
|
||||||
cton-util cat <file>...
|
cton-util cat <file>...
|
||||||
cton-util filecheck [-v] <file>
|
cton-util filecheck [-v] <file>
|
||||||
cton-util print-cfg <file>...
|
cton-util print-cfg <file>...
|
||||||
cton-util compile [-vp] [--set <set>]... [--isa <isa>] <file>...
|
cton-util compile [-vpT] [--set <set>]... [--isa <isa>] <file>...
|
||||||
cton-util wasm [-ctvp] [--set <set>]... [--isa <isa>] <file>...
|
cton-util wasm [-ctvpT] [--set <set>]... [--isa <isa>] <file>...
|
||||||
cton-util --help | --version
|
cton-util --help | --version
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-v, --verbose be more verbose
|
-v, --verbose be more verbose
|
||||||
|
-T, --time-passes
|
||||||
|
print pass timing report
|
||||||
-t, --just-decode
|
-t, --just-decode
|
||||||
just decode WebAssembly to Cretonne IL
|
just decode WebAssembly to Cretonne IL
|
||||||
-c, --check-translation
|
-c, --check-translation
|
||||||
@@ -64,6 +66,7 @@ struct Args {
|
|||||||
flag_verbose: bool,
|
flag_verbose: bool,
|
||||||
flag_set: Vec<String>,
|
flag_set: Vec<String>,
|
||||||
flag_isa: String,
|
flag_isa: String,
|
||||||
|
flag_time_passes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A command either succeeds or fails with an error message.
|
/// A command either succeeds or fails with an error message.
|
||||||
@@ -81,7 +84,7 @@ fn cton_util() -> CommandResult {
|
|||||||
.unwrap_or_else(|e| e.exit());
|
.unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
// Find the sub-command to execute.
|
// Find the sub-command to execute.
|
||||||
if args.cmd_test {
|
let result = if args.cmd_test {
|
||||||
filetest::run(args.flag_verbose, args.arg_file)
|
filetest::run(args.flag_verbose, args.arg_file)
|
||||||
} else if args.cmd_cat {
|
} else if args.cmd_cat {
|
||||||
cat::run(args.arg_file)
|
cat::run(args.arg_file)
|
||||||
@@ -104,7 +107,13 @@ fn cton_util() -> CommandResult {
|
|||||||
} else {
|
} else {
|
||||||
// Debugging / shouldn't happen with proper command line handling above.
|
// Debugging / shouldn't happen with proper command line handling above.
|
||||||
Err(format!("Unhandled args: {:?}", args))
|
Err(format!("Unhandled args: {:?}", args))
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.flag_time_passes {
|
||||||
|
print!("{}", timing::take_current());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//! This module provides the `ConcurrentRunner` struct which uses a pool of threads to run tests
|
//! This module provides the `ConcurrentRunner` struct which uses a pool of threads to run tests
|
||||||
//! concurrently.
|
//! concurrently.
|
||||||
|
|
||||||
|
use cretonne::timing;
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
@@ -33,7 +34,7 @@ pub struct ConcurrentRunner {
|
|||||||
// Workers have their own `Sender`.
|
// Workers have their own `Sender`.
|
||||||
reply_rx: Receiver<Reply>,
|
reply_rx: Receiver<Reply>,
|
||||||
|
|
||||||
handles: Vec<thread::JoinHandle<()>>,
|
handles: Vec<thread::JoinHandle<timing::PassTimes>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConcurrentRunner {
|
impl ConcurrentRunner {
|
||||||
@@ -64,11 +65,13 @@ impl ConcurrentRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Join all the worker threads.
|
/// Join all the worker threads.
|
||||||
|
/// Transfer pass timings from the worker threads to the current thread.
|
||||||
pub fn join(&mut self) {
|
pub fn join(&mut self) {
|
||||||
assert!(self.request_tx.is_none(), "must shutdown before join");
|
assert!(self.request_tx.is_none(), "must shutdown before join");
|
||||||
for h in self.handles.drain(..) {
|
for h in self.handles.drain(..) {
|
||||||
if let Err(e) = h.join() {
|
match h.join() {
|
||||||
println!("worker panicked: {:?}", e);
|
Ok(t) => timing::add_to_current(t),
|
||||||
|
Err(e) => println!("worker panicked: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +112,7 @@ fn worker_thread(
|
|||||||
thread_num: usize,
|
thread_num: usize,
|
||||||
requests: Arc<Mutex<Receiver<Request>>>,
|
requests: Arc<Mutex<Receiver<Request>>>,
|
||||||
replies: Sender<Reply>,
|
replies: Sender<Reply>,
|
||||||
) -> thread::JoinHandle<()> {
|
) -> thread::JoinHandle<timing::PassTimes> {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(format!("worker #{}", thread_num))
|
.name(format!("worker #{}", thread_num))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
@@ -142,6 +145,10 @@ fn worker_thread(
|
|||||||
|
|
||||||
replies.send(Reply::Done { jobid, result }).unwrap();
|
replies.send(Reply::Done { jobid, result }).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timing is accumulated independently per thread.
|
||||||
|
// Timings from this worker thread will be aggregated by `ConcurrentRunner::join()`.
|
||||||
|
timing::take_current()
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use std::time;
|
|||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
use cretonne::isa::TargetIsa;
|
use cretonne::isa::TargetIsa;
|
||||||
use cretonne::settings::Flags;
|
use cretonne::settings::Flags;
|
||||||
|
use cretonne::timing;
|
||||||
use cretonne::verify_function;
|
use cretonne::verify_function;
|
||||||
use cton_reader::parse_test;
|
use cton_reader::parse_test;
|
||||||
use cton_reader::IsaSpec;
|
use cton_reader::IsaSpec;
|
||||||
@@ -17,6 +18,7 @@ use filetest::subtest::{SubTest, Context, Result};
|
|||||||
///
|
///
|
||||||
/// If running this test causes a panic, it will propagate as normal.
|
/// If running this test causes a panic, it will propagate as normal.
|
||||||
pub fn run(path: &Path) -> TestResult {
|
pub fn run(path: &Path) -> TestResult {
|
||||||
|
let _tt = timing::process_file();
|
||||||
dbg!("---\nFile: {}", path.to_string_lossy());
|
dbg!("---\nFile: {}", path.to_string_lossy());
|
||||||
let started = time::Instant::now();
|
let started = time::Instant::now();
|
||||||
let buffer = read_to_string(path).map_err(|e| e.to_string())?;
|
let buffer = read_to_string(path).map_err(|e| e.to_string())?;
|
||||||
@@ -71,7 +73,6 @@ pub fn run(path: &Path) -> TestResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Actually run the tests.
|
|
||||||
Ok(started.elapsed())
|
Ok(started.elapsed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use unreachable_code::eliminate_unreachable_code;
|
|||||||
use verifier;
|
use verifier;
|
||||||
use simple_gvn::do_simple_gvn;
|
use simple_gvn::do_simple_gvn;
|
||||||
use licm::do_licm;
|
use licm::do_licm;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Persistent data structures and compilation pipeline.
|
/// Persistent data structures and compilation pipeline.
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
@@ -74,6 +75,7 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// Returns the size of the function's code.
|
/// Returns the size of the function's code.
|
||||||
pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
|
pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
|
||||||
|
let _tt = timing::compile();
|
||||||
self.verify_if(isa)?;
|
self.verify_if(isa)?;
|
||||||
|
|
||||||
self.compute_cfg();
|
self.compute_cfg();
|
||||||
@@ -100,6 +102,7 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// The machine code is not relocated. Instead, any relocations are emitted into `relocs`.
|
/// The machine code is not relocated. Instead, any relocations are emitted into `relocs`.
|
||||||
pub fn emit_to_memory(&self, mem: *mut u8, relocs: &mut RelocSink, isa: &TargetIsa) {
|
pub fn emit_to_memory(&self, mem: *mut u8, relocs: &mut RelocSink, isa: &TargetIsa) {
|
||||||
|
let _tt = timing::binemit();
|
||||||
isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs));
|
isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use flowgraph::{ControlFlowGraph, BasicBlock};
|
|||||||
use ir::{Ebb, Inst, Function, Layout, ProgramOrder, ExpandedProgramPoint};
|
use ir::{Ebb, Inst, Function, Layout, ProgramOrder, ExpandedProgramPoint};
|
||||||
use ir::instructions::BranchInfo;
|
use ir::instructions::BranchInfo;
|
||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
|
use timing;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@@ -227,6 +228,7 @@ impl DominatorTree {
|
|||||||
|
|
||||||
/// Reset and compute a CFG post-order and dominator tree.
|
/// Reset and compute a CFG post-order and dominator tree.
|
||||||
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
|
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
|
||||||
|
let _tt = timing::domtree();
|
||||||
debug_assert!(cfg.is_valid());
|
debug_assert!(cfg.is_valid());
|
||||||
self.compute_postorder(func);
|
self.compute_postorder(func);
|
||||||
self.compute_domtree(func, cfg);
|
self.compute_domtree(func, cfg);
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ use ir::{Function, Inst, Ebb};
|
|||||||
use ir::instructions::BranchInfo;
|
use ir::instructions::BranchInfo;
|
||||||
use entity::EntityMap;
|
use entity::EntityMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// A basic block denoted by its enclosing Ebb and last instruction.
|
/// A basic block denoted by its enclosing Ebb and last instruction.
|
||||||
pub type BasicBlock = (Ebb, Inst);
|
pub type BasicBlock = (Ebb, Inst);
|
||||||
|
|
||||||
/// A container for the successors and predecessors of some Ebb.
|
/// A container for the successors and predecessors of some Ebb.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct CFGNode {
|
struct CFGNode {
|
||||||
/// Instructions that can branch or jump to this EBB.
|
/// Instructions that can branch or jump to this EBB.
|
||||||
///
|
///
|
||||||
/// This maps branch instruction -> predecessor EBB which is redundant since the EBB containing
|
/// This maps branch instruction -> predecessor EBB which is redundant since the EBB containing
|
||||||
@@ -94,6 +95,7 @@ impl ControlFlowGraph {
|
|||||||
///
|
///
|
||||||
/// This will clear and overwrite any information already stored in this data structure.
|
/// This will clear and overwrite any information already stored in this data structure.
|
||||||
pub fn compute(&mut self, func: &Function) {
|
pub fn compute(&mut self, func: &Function) {
|
||||||
|
let _tt = timing::flowgraph();
|
||||||
self.clear();
|
self.clear();
|
||||||
self.data.resize(func.dfg.num_ebbs());
|
self.data.resize(func.dfg.num_ebbs());
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use isa::{TargetIsa, RegInfo, RegClass, EncInfo};
|
|||||||
use ir;
|
use ir;
|
||||||
use regalloc;
|
use regalloc;
|
||||||
use result;
|
use result;
|
||||||
|
use timing;
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -115,6 +116,7 @@ impl TargetIsa for Isa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
|
fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
|
||||||
|
let _tt = timing::prologue_epilogue();
|
||||||
abi::prologue_epilogue(func, self)
|
abi::prologue_epilogue(func, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ use settings;
|
|||||||
use ir;
|
use ir;
|
||||||
use regalloc;
|
use regalloc;
|
||||||
use result;
|
use result;
|
||||||
|
use timing;
|
||||||
use isa::enc_tables::Encodings;
|
use isa::enc_tables::Encodings;
|
||||||
|
|
||||||
#[cfg(build_riscv)]
|
#[cfg(build_riscv)]
|
||||||
@@ -236,6 +237,7 @@ pub trait TargetIsa {
|
|||||||
///
|
///
|
||||||
/// Return an error if the stack frame is too large.
|
/// Return an error if the stack frame is too large.
|
||||||
fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
|
fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
|
||||||
|
let _tt = timing::prologue_epilogue();
|
||||||
// This default implementation is unlikely to be good enough.
|
// This default implementation is unlikely to be good enough.
|
||||||
use stack_layout::layout_stack;
|
use stack_layout::layout_stack;
|
||||||
use ir::stackslot::{StackSize, StackOffset};
|
use ir::stackslot::{StackSize, StackOffset};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use flowgraph::ControlFlowGraph;
|
|||||||
use ir::{self, InstBuilder};
|
use ir::{self, InstBuilder};
|
||||||
use isa::TargetIsa;
|
use isa::TargetIsa;
|
||||||
use bitset::BitSet;
|
use bitset::BitSet;
|
||||||
|
use timing;
|
||||||
|
|
||||||
mod boundary;
|
mod boundary;
|
||||||
mod globalvar;
|
mod globalvar;
|
||||||
@@ -33,6 +34,7 @@ use self::heap::expand_heap_addr;
|
|||||||
/// - Fill out `func.encodings`.
|
/// - Fill out `func.encodings`.
|
||||||
///
|
///
|
||||||
pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &TargetIsa) {
|
pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &TargetIsa) {
|
||||||
|
let _tt = timing::legalize();
|
||||||
debug_assert!(cfg.is_valid());
|
debug_assert!(cfg.is_valid());
|
||||||
|
|
||||||
boundary::legalize_signatures(func, isa);
|
boundary::legalize_signatures(func, isa);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ pub mod packed_option;
|
|||||||
pub mod regalloc;
|
pub mod regalloc;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
pub mod timing;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
|
|
||||||
mod abi;
|
mod abi;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::collections::HashSet;
|
|||||||
use dominator_tree::DominatorTree;
|
use dominator_tree::DominatorTree;
|
||||||
use entity::{EntityList, ListPool};
|
use entity::{EntityList, ListPool};
|
||||||
use loop_analysis::{Loop, LoopAnalysis};
|
use loop_analysis::{Loop, LoopAnalysis};
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Performs the LICM pass by detecting loops within the CFG and moving
|
/// Performs the LICM pass by detecting loops within the CFG and moving
|
||||||
/// loop-invariant instructions out of them.
|
/// loop-invariant instructions out of them.
|
||||||
@@ -17,6 +18,7 @@ pub fn do_licm(
|
|||||||
domtree: &mut DominatorTree,
|
domtree: &mut DominatorTree,
|
||||||
loop_analysis: &mut LoopAnalysis,
|
loop_analysis: &mut LoopAnalysis,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::licm();
|
||||||
debug_assert!(cfg.is_valid());
|
debug_assert!(cfg.is_valid());
|
||||||
debug_assert!(domtree.is_valid());
|
debug_assert!(domtree.is_valid());
|
||||||
debug_assert!(loop_analysis.is_valid());
|
debug_assert!(loop_analysis.is_valid());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use entity::EntityMap;
|
|||||||
use flowgraph::ControlFlowGraph;
|
use flowgraph::ControlFlowGraph;
|
||||||
use ir::{Function, Ebb, Layout};
|
use ir::{Function, Ebb, Layout};
|
||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// A opaque reference to a code loop.
|
/// A opaque reference to a code loop.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -98,6 +99,7 @@ impl LoopAnalysis {
|
|||||||
impl LoopAnalysis {
|
impl LoopAnalysis {
|
||||||
/// Detects the loops in a function. Needs the control flow graph and the dominator tree.
|
/// Detects the loops in a function. Needs the control flow graph and the dominator tree.
|
||||||
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) {
|
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) {
|
||||||
|
let _tt = timing::loop_analysis();
|
||||||
self.loops.clear();
|
self.loops.clear();
|
||||||
self.ebb_loop_map.clear();
|
self.ebb_loop_map.clear();
|
||||||
self.ebb_loop_map.resize(func.dfg.num_ebbs());
|
self.ebb_loop_map.resize(func.dfg.num_ebbs());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use std::cmp::Ordering;
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use isa::{TargetIsa, EncInfo};
|
use isa::{TargetIsa, EncInfo};
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Dominator forest.
|
/// Dominator forest.
|
||||||
///
|
///
|
||||||
@@ -282,6 +283,7 @@ impl Coalescing {
|
|||||||
liveness: &mut Liveness,
|
liveness: &mut Liveness,
|
||||||
virtregs: &mut VirtRegs,
|
virtregs: &mut VirtRegs,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::ra_cssa();
|
||||||
dbg!("Coalescing for:\n{}", func.display(isa));
|
dbg!("Coalescing for:\n{}", func.display(isa));
|
||||||
let mut context = Context {
|
let mut context = Context {
|
||||||
isa,
|
isa,
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ use regalloc::liveness::Liveness;
|
|||||||
use regalloc::liverange::{LiveRange, LiveRangeContext};
|
use regalloc::liverange::{LiveRange, LiveRangeContext};
|
||||||
use regalloc::solver::{Solver, SolverError};
|
use regalloc::solver::{Solver, SolverError};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use timing;
|
||||||
|
|
||||||
|
|
||||||
/// Data structures for the coloring pass.
|
/// Data structures for the coloring pass.
|
||||||
@@ -123,6 +124,7 @@ impl Coloring {
|
|||||||
liveness: &mut Liveness,
|
liveness: &mut Liveness,
|
||||||
tracker: &mut LiveValueTracker,
|
tracker: &mut LiveValueTracker,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::ra_coloring();
|
||||||
dbg!("Coloring for:\n{}", func.display(isa));
|
dbg!("Coloring for:\n{}", func.display(isa));
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
usable_regs: isa.allocatable_registers(func),
|
usable_regs: isa.allocatable_registers(func),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use regalloc::reload::Reload;
|
|||||||
use regalloc::spilling::Spilling;
|
use regalloc::spilling::Spilling;
|
||||||
use regalloc::virtregs::VirtRegs;
|
use regalloc::virtregs::VirtRegs;
|
||||||
use result::CtonResult;
|
use result::CtonResult;
|
||||||
|
use timing;
|
||||||
use topo_order::TopoOrder;
|
use topo_order::TopoOrder;
|
||||||
use verifier::{verify_context, verify_liveness, verify_cssa, verify_locations};
|
use verifier::{verify_context, verify_liveness, verify_cssa, verify_locations};
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ impl Context {
|
|||||||
cfg: &ControlFlowGraph,
|
cfg: &ControlFlowGraph,
|
||||||
domtree: &mut DominatorTree,
|
domtree: &mut DominatorTree,
|
||||||
) -> CtonResult {
|
) -> CtonResult {
|
||||||
|
let _tt = timing::regalloc();
|
||||||
debug_assert!(domtree.is_valid());
|
debug_assert!(domtree.is_valid());
|
||||||
|
|
||||||
// `Liveness` and `Coloring` are self-clearing.
|
// `Liveness` and `Coloring` are self-clearing.
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ use regalloc::affinity::Affinity;
|
|||||||
use regalloc::liverange::{LiveRange, LiveRangeForest, LiveRangeContext};
|
use regalloc::liverange::{LiveRange, LiveRangeForest, LiveRangeContext};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// A set of live ranges, indexed by value number.
|
/// A set of live ranges, indexed by value number.
|
||||||
type LiveRangeSet = SparseMap<Value, LiveRange>;
|
type LiveRangeSet = SparseMap<Value, LiveRange>;
|
||||||
@@ -385,6 +386,7 @@ impl Liveness {
|
|||||||
/// Compute the live ranges of all SSA values used in `func`.
|
/// Compute the live ranges of all SSA values used in `func`.
|
||||||
/// This clears out any existing analysis stored in this data structure.
|
/// This clears out any existing analysis stored in this data structure.
|
||||||
pub fn compute(&mut self, isa: &TargetIsa, func: &mut Function, cfg: &ControlFlowGraph) {
|
pub fn compute(&mut self, isa: &TargetIsa, func: &mut Function, cfg: &ControlFlowGraph) {
|
||||||
|
let _tt = timing::ra_liveness();
|
||||||
self.ranges.clear();
|
self.ranges.clear();
|
||||||
|
|
||||||
// Get ISA data structures used for computing live range affinities.
|
// Get ISA data structures used for computing live range affinities.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use isa::{TargetIsa, Encoding, EncInfo, RecipeConstraints, ConstraintKind};
|
|||||||
use regalloc::affinity::Affinity;
|
use regalloc::affinity::Affinity;
|
||||||
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
|
use timing;
|
||||||
use topo_order::TopoOrder;
|
use topo_order::TopoOrder;
|
||||||
|
|
||||||
/// Reusable data structures for the reload pass.
|
/// Reusable data structures for the reload pass.
|
||||||
@@ -69,6 +70,7 @@ impl Reload {
|
|||||||
topo: &mut TopoOrder,
|
topo: &mut TopoOrder,
|
||||||
tracker: &mut LiveValueTracker,
|
tracker: &mut LiveValueTracker,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::ra_reload();
|
||||||
dbg!("Reload for:\n{}", func.display(isa));
|
dbg!("Reload for:\n{}", func.display(isa));
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
cur: EncCursor::new(func, isa),
|
cur: EncCursor::new(func, isa),
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use regalloc::liveness::Liveness;
|
|||||||
use regalloc::pressure::Pressure;
|
use regalloc::pressure::Pressure;
|
||||||
use regalloc::virtregs::VirtRegs;
|
use regalloc::virtregs::VirtRegs;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use timing;
|
||||||
use topo_order::TopoOrder;
|
use topo_order::TopoOrder;
|
||||||
|
|
||||||
/// Persistent data structures for the spilling pass.
|
/// Persistent data structures for the spilling pass.
|
||||||
@@ -87,6 +88,7 @@ impl Spilling {
|
|||||||
topo: &mut TopoOrder,
|
topo: &mut TopoOrder,
|
||||||
tracker: &mut LiveValueTracker,
|
tracker: &mut LiveValueTracker,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::ra_spilling();
|
||||||
dbg!("Spilling for:\n{}", func.display(isa));
|
dbg!("Spilling for:\n{}", func.display(isa));
|
||||||
let reginfo = isa.register_info();
|
let reginfo = isa.register_info();
|
||||||
let usable_regs = isa.allocatable_registers(func);
|
let usable_regs = isa.allocatable_registers(func);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use flowgraph::ControlFlowGraph;
|
|||||||
use dominator_tree::DominatorTree;
|
use dominator_tree::DominatorTree;
|
||||||
use ir::{InstructionData, Function, Inst, Opcode, Type};
|
use ir::{InstructionData, Function, Inst, Opcode, Type};
|
||||||
use scoped_hash_map::ScopedHashMap;
|
use scoped_hash_map::ScopedHashMap;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Test whether the given opcode is unsafe to even consider for GVN.
|
/// Test whether the given opcode is unsafe to even consider for GVN.
|
||||||
fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
||||||
@@ -16,6 +17,7 @@ fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
|||||||
/// Perform simple GVN on `func`.
|
/// Perform simple GVN on `func`.
|
||||||
///
|
///
|
||||||
pub fn do_simple_gvn(func: &mut Function, cfg: &mut ControlFlowGraph, domtree: &mut DominatorTree) {
|
pub fn do_simple_gvn(func: &mut Function, cfg: &mut ControlFlowGraph, domtree: &mut DominatorTree) {
|
||||||
|
let _tt = timing::gvn();
|
||||||
debug_assert!(cfg.is_valid());
|
debug_assert!(cfg.is_valid());
|
||||||
debug_assert!(domtree.is_valid());
|
debug_assert!(domtree.is_valid());
|
||||||
|
|
||||||
|
|||||||
225
lib/cretonne/src/timing.rs
Normal file
225
lib/cretonne/src/timing.rs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
//! Pass timing.
|
||||||
|
//!
|
||||||
|
//! This modules provides facilities for timing the execution of individual compilation passes.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub use self::details::{TimingToken, PassTimes, take_current, add_to_current};
|
||||||
|
|
||||||
|
// Each pass that can be timed is predefined with the `define_passes!` macro. Each pass has a
|
||||||
|
// snake_case name and a plain text description used when printing out the timing report.
|
||||||
|
//
|
||||||
|
// This macro defines:
|
||||||
|
//
|
||||||
|
// - A C-style enum containing all the pass names and a `NoPass` variant.
|
||||||
|
// - A usize constant with the number of defined passes.
|
||||||
|
// - A const array of pass descriptions.
|
||||||
|
// - A public function per pass used to start the timing of that pass.
|
||||||
|
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,)+ NoPass }
|
||||||
|
|
||||||
|
const $num_passes: usize = $enum::NoPass as usize;
|
||||||
|
|
||||||
|
const $descriptions: [&str; $num_passes] = [ $($desc),+ ];
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[doc=$desc]
|
||||||
|
pub fn $pass() -> TimingToken {
|
||||||
|
details::start_pass($enum::$pass)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass definitions.
|
||||||
|
define_passes!{
|
||||||
|
Pass, NUM_PASSES, DESCRIPTIONS;
|
||||||
|
|
||||||
|
process_file: "Processing test file",
|
||||||
|
parse_text: "Parsing textual Cretonne IL",
|
||||||
|
wasm_translate_module: "Translate WASM module",
|
||||||
|
wasm_translate_function: "Translate WASM function",
|
||||||
|
|
||||||
|
verifier: "Verify Cretonne IL",
|
||||||
|
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",
|
||||||
|
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",
|
||||||
|
binemit: "Binary machine code emission",
|
||||||
|
}
|
||||||
|
|
||||||
|
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>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Implementation details.
|
||||||
|
///
|
||||||
|
/// This whole module can be gated on a `cfg` feature to provide a dummy implementation for
|
||||||
|
/// performance-sensitive builds or restricted environments. The dummy implementation must provide
|
||||||
|
/// `TimingToken` and `PassTimings` types and a `take_current` function.
|
||||||
|
mod details {
|
||||||
|
use super::{Pass, NUM_PASSES, DESCRIPTIONS};
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::fmt;
|
||||||
|
use std::mem;
|
||||||
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
|
/// A timing token is responsible for timing the currently running pass. Timing starts when it
|
||||||
|
/// is created and ends when it is dropped.
|
||||||
|
///
|
||||||
|
/// Multiple passes can be active at the same time, but they must be started and stopped in a
|
||||||
|
/// LIFO fashion.
|
||||||
|
pub struct TimingToken {
|
||||||
|
/// Start time for this pass.
|
||||||
|
start: Instant,
|
||||||
|
|
||||||
|
// Pass being timed by this token.
|
||||||
|
pass: Pass,
|
||||||
|
|
||||||
|
// The previously active pass which will be restored when this token is dropped.
|
||||||
|
prev: Pass,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accumulated timing information for a single pass.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct PassTime {
|
||||||
|
/// Total time spent running this pas including children.
|
||||||
|
total: Duration,
|
||||||
|
|
||||||
|
/// Time spent running in child passes.
|
||||||
|
child: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accumulated timing for all passes.
|
||||||
|
#[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) {
|
||||||
|
// Omit passes that haven't run.
|
||||||
|
if time.total == Duration::default() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a duration as secs.milis, trailing space.
|
||||||
|
fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
// Round to nearest ms by adding 500us.
|
||||||
|
dur += Duration::new(0, 500_000);
|
||||||
|
let ms = dur.subsec_nanos() / 1_000_000;
|
||||||
|
write!(f, "{:3}.{:03} ", dur.as_secs(), ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmtdur(time.total, f)?;
|
||||||
|
if let Some(s) = time.total.checked_sub(time.child) {
|
||||||
|
fmtdur(s, f)?;
|
||||||
|
}
|
||||||
|
writeln!(f, " {}", desc)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "======= ======= ==================================")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about passes in a single thread.
|
||||||
|
thread_local!{
|
||||||
|
static CURRENT_PASS: Cell<Pass> = Cell::new(Pass::NoPass);
|
||||||
|
static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start timing `pass` as a child of the currently running pass, if any.
|
||||||
|
///
|
||||||
|
/// 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);
|
||||||
|
TimingToken {
|
||||||
|
start: Instant::now(),
|
||||||
|
pass,
|
||||||
|
prev,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dropping a timing token indicated the end of the pass.
|
||||||
|
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));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the current accumulated pass timings and reset the timings for the current thread.
|
||||||
|
pub fn take_current() -> PassTimes {
|
||||||
|
PASS_TIME.with(|rc| mem::replace(&mut *rc.borrow_mut(), Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add `timings` to the accumulated timings for the current thread.
|
||||||
|
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(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display() {
|
||||||
|
assert_eq!(Pass::NoPass.to_string(), "<no pass>");
|
||||||
|
assert_eq!(Pass::regalloc.to_string(), "Register allocation");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ use cursor::{Cursor, FuncCursor};
|
|||||||
use dominator_tree::DominatorTree;
|
use dominator_tree::DominatorTree;
|
||||||
use flowgraph::ControlFlowGraph;
|
use flowgraph::ControlFlowGraph;
|
||||||
use ir;
|
use ir;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Eliminate unreachable code.
|
/// Eliminate unreachable code.
|
||||||
///
|
///
|
||||||
@@ -16,6 +17,7 @@ pub fn eliminate_unreachable_code(
|
|||||||
cfg: &mut ControlFlowGraph,
|
cfg: &mut ControlFlowGraph,
|
||||||
domtree: &DominatorTree,
|
domtree: &DominatorTree,
|
||||||
) {
|
) {
|
||||||
|
let _tt = timing::unreachable_code();
|
||||||
let mut pos = FuncCursor::new(func);
|
let mut pos = FuncCursor::new(func);
|
||||||
while let Some(ebb) = pos.next_ebb() {
|
while let Some(ebb) = pos.next_ebb() {
|
||||||
if domtree.is_reachable(ebb) {
|
if domtree.is_reachable(ebb) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use ir::Function;
|
|||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
use regalloc::virtregs::VirtRegs;
|
use regalloc::virtregs::VirtRegs;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use timing;
|
||||||
use verifier::Result;
|
use verifier::Result;
|
||||||
|
|
||||||
/// Verify conventional SSA form for `func`.
|
/// Verify conventional SSA form for `func`.
|
||||||
@@ -29,6 +30,7 @@ pub fn verify_cssa(
|
|||||||
liveness: &Liveness,
|
liveness: &Liveness,
|
||||||
virtregs: &VirtRegs,
|
virtregs: &VirtRegs,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
let _tt = timing::verify_cssa();
|
||||||
let verifier = CssaVerifier {
|
let verifier = CssaVerifier {
|
||||||
func,
|
func,
|
||||||
cfg,
|
cfg,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use isa;
|
|||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
use std::result;
|
use std::result;
|
||||||
use verifier::{Result, Error};
|
use verifier::{Result, Error};
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Verify that CPU flags are used correctly.
|
/// Verify that CPU flags are used correctly.
|
||||||
///
|
///
|
||||||
@@ -26,6 +27,7 @@ pub fn verify_flags(
|
|||||||
cfg: &ControlFlowGraph,
|
cfg: &ControlFlowGraph,
|
||||||
isa: Option<&isa::TargetIsa>,
|
isa: Option<&isa::TargetIsa>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
let _tt = timing::verify_flags();
|
||||||
let mut verifier = FlagsVerifier {
|
let mut verifier = FlagsVerifier {
|
||||||
func,
|
func,
|
||||||
cfg,
|
cfg,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use regalloc::liveness::Liveness;
|
|||||||
use regalloc::liverange::LiveRange;
|
use regalloc::liverange::LiveRange;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use verifier::Result;
|
use verifier::Result;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Verify liveness information for `func`.
|
/// Verify liveness information for `func`.
|
||||||
///
|
///
|
||||||
@@ -27,6 +28,7 @@ pub fn verify_liveness(
|
|||||||
cfg: &ControlFlowGraph,
|
cfg: &ControlFlowGraph,
|
||||||
liveness: &Liveness,
|
liveness: &Liveness,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
let _tt = timing::verify_liveness();
|
||||||
let verifier = LivenessVerifier {
|
let verifier = LivenessVerifier {
|
||||||
isa,
|
isa,
|
||||||
func,
|
func,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use isa;
|
|||||||
use regalloc::RegDiversions;
|
use regalloc::RegDiversions;
|
||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
use verifier::Result;
|
use verifier::Result;
|
||||||
|
use timing;
|
||||||
|
|
||||||
/// Verify value locations for `func`.
|
/// Verify value locations for `func`.
|
||||||
///
|
///
|
||||||
@@ -22,6 +23,7 @@ pub fn verify_locations(
|
|||||||
func: &ir::Function,
|
func: &ir::Function,
|
||||||
liveness: Option<&Liveness>,
|
liveness: Option<&Liveness>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
let _tt = timing::verify_locations();
|
||||||
let verifier = LocationVerifier {
|
let verifier = LocationVerifier {
|
||||||
isa,
|
isa,
|
||||||
func,
|
func,
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ use std::collections::BTreeSet;
|
|||||||
use std::error as std_error;
|
use std::error as std_error;
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use timing;
|
||||||
|
|
||||||
pub use self::cssa::verify_cssa;
|
pub use self::cssa::verify_cssa;
|
||||||
pub use self::liveness::verify_liveness;
|
pub use self::liveness::verify_liveness;
|
||||||
@@ -126,6 +127,7 @@ pub type Result = result::Result<(), Error>;
|
|||||||
|
|
||||||
/// Verify `func`.
|
/// Verify `func`.
|
||||||
pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(func: &Function, fisa: FOI) -> Result {
|
pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(func: &Function, fisa: FOI) -> Result {
|
||||||
|
let _tt = timing::verifier();
|
||||||
Verifier::new(func, fisa.into()).run()
|
Verifier::new(func, fisa.into()).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +139,7 @@ pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
|
|||||||
domtree: &DominatorTree,
|
domtree: &DominatorTree,
|
||||||
fisa: FOI,
|
fisa: FOI,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
let _tt = timing::verifier();
|
||||||
let verifier = Verifier::new(func, fisa.into());
|
let verifier = Verifier::new(func, fisa.into());
|
||||||
if cfg.is_valid() {
|
if cfg.is_valid() {
|
||||||
verifier.cfg_integrity(cfg)?;
|
verifier.cfg_integrity(cfg)?;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use cretonne::ir::immediates::{Imm64, Uimm32, Offset32, Ieee32, Ieee64};
|
|||||||
use cretonne::ir::entities::AnyEntity;
|
use cretonne::ir::entities::AnyEntity;
|
||||||
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs};
|
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs};
|
||||||
use cretonne::isa::{self, TargetIsa, Encoding, RegUnit};
|
use cretonne::isa::{self, TargetIsa, Encoding, RegUnit};
|
||||||
use cretonne::settings;
|
use cretonne::{settings, timing};
|
||||||
use testfile::{TestFile, Details, Comment};
|
use testfile::{TestFile, Details, Comment};
|
||||||
use error::{Location, Error, Result};
|
use error::{Location, Error, Result};
|
||||||
use lexer::{self, Lexer, Token};
|
use lexer::{self, Lexer, Token};
|
||||||
@@ -26,6 +26,7 @@ use sourcemap::{SourceMap, MutableSourceMap};
|
|||||||
///
|
///
|
||||||
/// Any test commands or ISA declarations are ignored.
|
/// Any test commands or ISA declarations are ignored.
|
||||||
pub fn parse_functions(text: &str) -> Result<Vec<Function>> {
|
pub fn parse_functions(text: &str) -> Result<Vec<Function>> {
|
||||||
|
let _tt = timing::parse_text();
|
||||||
parse_test(text).map(|file| {
|
parse_test(text).map(|file| {
|
||||||
file.functions.into_iter().map(|(func, _)| func).collect()
|
file.functions.into_iter().map(|(func, _)| func).collect()
|
||||||
})
|
})
|
||||||
@@ -35,6 +36,7 @@ pub fn parse_functions(text: &str) -> Result<Vec<Function>> {
|
|||||||
///
|
///
|
||||||
/// The returned `TestFile` contains direct references to substrings of `text`.
|
/// The returned `TestFile` contains direct references to substrings of `text`.
|
||||||
pub fn parse_test<'a>(text: &'a str) -> Result<TestFile<'a>> {
|
pub fn parse_test<'a>(text: &'a str) -> Result<TestFile<'a>> {
|
||||||
|
let _tt = timing::parse_text();
|
||||||
let mut parser = Parser::new(text);
|
let mut parser = Parser::new(text);
|
||||||
// Gather the preamble comments as 'Function'.
|
// Gather the preamble comments as 'Function'.
|
||||||
parser.gather_comments(AnyEntity::Function);
|
parser.gather_comments(AnyEntity::Function);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use code_translator::translate_operator;
|
|||||||
use cretonne::entity::EntityRef;
|
use cretonne::entity::EntityRef;
|
||||||
use cretonne::ir::{self, InstBuilder, Ebb};
|
use cretonne::ir::{self, InstBuilder, Ebb};
|
||||||
use cretonne::result::{CtonResult, CtonError};
|
use cretonne::result::{CtonResult, CtonError};
|
||||||
|
use cretonne::timing;
|
||||||
use cton_frontend::{ILBuilder, FunctionBuilder};
|
use cton_frontend::{ILBuilder, FunctionBuilder};
|
||||||
use environ::FuncEnvironment;
|
use environ::FuncEnvironment;
|
||||||
use state::TranslationState;
|
use state::TranslationState;
|
||||||
@@ -66,6 +67,7 @@ impl FuncTranslator {
|
|||||||
func: &mut ir::Function,
|
func: &mut ir::Function,
|
||||||
environ: &mut FE,
|
environ: &mut FE,
|
||||||
) -> CtonResult {
|
) -> CtonResult {
|
||||||
|
let _tt = timing::wasm_translate_function();
|
||||||
dbg!(
|
dbg!(
|
||||||
"translate({} bytes, {}{})",
|
"translate({} bytes, {}{})",
|
||||||
reader.bytes_remaining(),
|
reader.bytes_remaining(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
|
//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
|
||||||
//! to deal with each part of it.
|
//! to deal with each part of it.
|
||||||
|
use cretonne::timing;
|
||||||
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
|
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
|
||||||
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
|
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
|
||||||
parse_function_section, parse_export_section, parse_start_section,
|
parse_function_section, parse_export_section, parse_start_section,
|
||||||
@@ -15,6 +16,7 @@ pub fn translate_module<'data>(
|
|||||||
data: &'data [u8],
|
data: &'data [u8],
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let _tt = timing::wasm_translate_module();
|
||||||
let mut parser = Parser::new(data);
|
let mut parser = Parser::new(data);
|
||||||
match *parser.read() {
|
match *parser.read() {
|
||||||
ParserState::BeginWasm { .. } => {}
|
ParserState::BeginWasm { .. } => {}
|
||||||
|
|||||||
Reference in New Issue
Block a user