summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-codegen/src/timing.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cranelift-codegen/src/timing.rs
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/timing.rs')
-rw-r--r--third_party/rust/cranelift-codegen/src/timing.rs268
1 files changed, 268 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/timing.rs b/third_party/rust/cranelift-codegen/src/timing.rs
new file mode 100644
index 0000000000..127f954f6e
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/timing.rs
@@ -0,0 +1,268 @@
+//! Pass timing.
+//!
+//! This modules provides facilities for timing the execution of individual compilation passes.
+
+use core::fmt;
+
+pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
+
+// 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 `None` 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,)+ 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)
+ }
+ )+
+ }
+}
+
+// Pass definitions.
+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",
+ remove_constant_phis: "Remove constant phi-nodes",
+
+ vcode_lower: "VCode lowering",
+ vcode_post_ra: "VCode post-register allocation finalization",
+ vcode_emit: "VCode emission",
+ vcode_emit_finish: "VCode emission finalization",
+
+ 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>"),
+ }
+ }
+}
+
+/// 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 `PassTimes` types and `take_current`, `add_to_current`, and `start_pass` funcs
+#[cfg(feature = "std")]
+mod details {
+ use super::{Pass, DESCRIPTIONS, NUM_PASSES};
+ use log::debug;
+ use std::cell::{Cell, RefCell};
+ use std::fmt;
+ use std::mem;
+ use std::time::{Duration, Instant};
+
+ /// 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, Copy, Clone)]
+ struct PassTime {
+ /// Total time spent running this pass including children.
+ total: Duration,
+
+ /// Time spent running in child passes.
+ child: Duration,
+ }
+
+ /// Accumulated timing for all passes.
+ pub struct PassTimes {
+ pass: [PassTime; NUM_PASSES],
+ }
+
+ impl Default for PassTimes {
+ fn default() -> Self {
+ Self {
+ pass: [Default::default(); 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.millis, 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_millis();
+ write!(f, "{:4}.{: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::None);
+ 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));
+ debug!("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();
+ 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| {
+ 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(&times.pass[..]) {
+ a.total += b.total;
+ a.child += b.child;
+ }
+ })
+ }
+}
+
+/// Dummy `debug` implementation
+#[cfg(not(feature = "std"))]
+mod details {
+ use super::Pass;
+ /// Dummy `TimingToken`
+ pub struct TimingToken;
+ /// Dummy `PassTimes`
+ pub struct PassTimes;
+ /// Returns dummy `PassTimes`
+ pub fn take_current() -> PassTimes {
+ PassTimes
+ }
+ /// does nothing
+ pub fn add_to_current(_times: PassTimes) {}
+
+ /// does nothing
+ pub(super) fn start_pass(_pass: Pass) -> TimingToken {
+ TimingToken
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::string::ToString;
+
+ #[test]
+ fn display() {
+ assert_eq!(Pass::None.to_string(), "<no pass>");
+ assert_eq!(Pass::regalloc.to_string(), "Register allocation");
+ }
+}