Skip to content

Commit

Permalink
Consolidate platform-specific definitions in Wasmtime (#7626)
Browse files Browse the repository at this point in the history
* Gate more functionality behind `debug-builtins`

This commit extends the gating behavior of the preexisting
`debug-builtins` Cargo feature to cover more GDB-related functionality
associated with debugging. This can additionally slim down the set of
exposed symbols from Wasmtime over the default with them included.

* Move timing in Cranelift behind a Cargo feature

This commit adds a `timing` feature to the `cranelift-codegen` crate
which is enabled by default. This feature gates the timing functionality
in Cranelift to enable turning it off if desired. The goal of this
commit is to remove a system dependency on `Instant` for possibly
esoteric environments.

* Consolidate platform-specific definitions in Wasmtime

Prior to this commit Wasmtime did not not have a style or system for
containing platform-specific logic in files. The goal of this commit is
to consolidate all platform-specific functionality into one location to
make it easier to port Wasmtime to new systems. This commit creates a
`sys` module within the `wasmtime-runtime` crate which conditionally
defines all of the platform support that Wasmtime requires, namely
things related to virtual memory management and trap handling. Many of
the previous `unix.rs` files interspersed throughout the tree are now
all located in a single `unix` directory. This additionally helps split
out `miri`-specific functionality by pretending `miri` is its own
platform.

This change additionally goes through `#[cfg]` directives throughout
`wasmtime-runtime`, `wasmtime-jit`, and `wasmtime` itself to place all
of this target-specific functionality within this `sys` directory. The
end state is that there are two new top-level modules in the
`wasmtime-runtime` crate:

* `arch` - this conditionally defines architecture-specific logic such
  as the state used by backtraces, libcalls, etc.
* `sys` - this conditionally defines OS or platform-specific logic such
  as virtual memory management.

One secondary goal of this commit is to enable the ability to easily
add new platforms to Wasmtime, even if it's in a fork of Wasmtime.
Previously patches might have to touch a good number of locations where
now they'd ideally only have to touch `sys/mod.rs` to declare a new
platform and `sys/$platform/*.rs` to define all the functionality.

* Fix build on Windows

prtest:full

* Fix some build warnings

* Fix miri build

* Include debug-builtins when testing

* Try to fix Windows tests

* Fix warnings on miri

* Fix miri build

* Fix debug-builtins feature

* More feature fixes

* Rename `fd` field

* Fix speeling

* Review comments
  • Loading branch information
alexcrichton authored Dec 4, 2023
1 parent af38ee0 commit 494e2b8
Show file tree
Hide file tree
Showing 59 changed files with 1,017 additions and 845 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ rustix = { workspace = true, features = ["mm", "param"] }

[dev-dependencies]
# depend again on wasmtime to activate its default features for tests
wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch'] }
wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch', 'debug-builtins'] }
env_logger = { workspace = true }
log = { workspace = true }
filecheck = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion cranelift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ harness = false

[dependencies]
cfg-if = { workspace = true }
cranelift-codegen = { workspace = true, features = ["disas", "trace-log"] }
cranelift-codegen = { workspace = true, features = ["disas", "trace-log", "timing"] }
cranelift-entity = { workspace = true }
cranelift-interpreter = { workspace = true }
cranelift-reader = { workspace = true }
Expand Down
7 changes: 6 additions & 1 deletion cranelift/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ cranelift-codegen-meta = { path = "meta", version = "0.103.0" }
cranelift-isle = { path = "../isle/isle", version = "=0.103.0" }

[features]
default = ["std", "unwind", "host-arch"]
default = ["std", "unwind", "host-arch", "timing"]

# The "std" feature enables use of libstd. The "core" feature enables use
# of some minimal std-like replacement libraries. At least one of these two
Expand Down Expand Up @@ -114,6 +114,11 @@ isle-errors = ["cranelift-isle/fancy-errors"]
# inspection, rather than inside of target/.
isle-in-source-tree = []

# Enable tracking how long passes take in Cranelift.
#
# Enabled by default.
timing = []

[[bench]]
name = "x64-evex-encoding"
harness = false
122 changes: 74 additions & 48 deletions cranelift/codegen/src/timing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use core::fmt;
use std::any::Any;
use std::boxed::Box;
use std::cell::{Cell, RefCell};
use std::cell::RefCell;
use std::mem;
use std::time::{Duration, Instant};
use std::time::Duration;

// 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.
Expand Down Expand Up @@ -130,22 +130,6 @@ fn start_pass(pass: Pass) -> Box<dyn Any> {
PROFILER.with(|profiler| profiler.borrow().start_pass(pass))
}

/// 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.
struct DefaultTimingToken {
/// 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 {
Expand Down Expand Up @@ -215,49 +199,91 @@ impl fmt::Display for PassTimes {

// Information about passes in a single thread.
thread_local! {
static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
}

/// The default profiler. You can get the results using [`take_current`].
pub struct DefaultProfiler;

impl Profiler for DefaultProfiler {
fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
let prev = CURRENT_PASS.with(|p| p.replace(pass));
log::debug!("timing: Starting {}, (during {})", pass, prev);
Box::new(DefaultTimingToken {
start: Instant::now(),
pass,
prev,
})
}
}

/// Dropping a timing token indicated the end of the pass.
impl Drop for DefaultTimingToken {
fn drop(&mut self) {
let duration = self.start.elapsed();
log::debug!("timing: Ending {}: {}ms", self.pass, duration.as_millis());
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.
///
/// Only applies when [`DefaultProfiler`] is used.
pub fn take_current() -> PassTimes {
PASS_TIME.with(|rc| mem::take(&mut *rc.borrow_mut()))
}

#[cfg(feature = "timing")]
mod enabled {
use super::{DefaultProfiler, Pass, Profiler, PASS_TIME};
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::time::Instant;

// Information about passes in a single thread.
thread_local! {
static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
}

impl Profiler for DefaultProfiler {
fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
let prev = CURRENT_PASS.with(|p| p.replace(pass));
log::debug!("timing: Starting {}, (during {})", pass, prev);
Box::new(DefaultTimingToken {
start: Instant::now(),
pass,
prev,
})
}
}

/// 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.
struct DefaultTimingToken {
/// 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,
}

/// Dropping a timing token indicated the end of the pass.
impl Drop for DefaultTimingToken {
fn drop(&mut self) {
let duration = self.start.elapsed();
log::debug!("timing: Ending {}: {}ms", self.pass, duration.as_millis());
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;
}
})
}
}
}

#[cfg(not(feature = "timing"))]
mod disabled {
use super::{DefaultProfiler, Pass, Profiler};
use std::any::Any;
use std::boxed::Box;

impl Profiler for DefaultProfiler {
fn start_pass(&self, _pass: Pass) -> Box<dyn Any> {
Box::new(())
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/cranelift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ anyhow = { workspace = true }
log = { workspace = true }
wasmtime-environ = { workspace = true }
cranelift-wasm = { workspace = true }
cranelift-codegen = { workspace = true, features = ["default"] }
cranelift-codegen = { workspace = true, features = ["host-arch"] }
cranelift-frontend = { workspace = true }
cranelift-entity = { workspace = true }
cranelift-native = { workspace = true }
Expand Down
7 changes: 3 additions & 4 deletions crates/jit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ workspace = true

[dependencies]
wasmtime-environ = { workspace = true }
wasmtime-jit-debug = { workspace = true, features = [
"perf_jitdump",
], optional = true }
wasmtime-jit-debug = { workspace = true, features = ["perf_jitdump"], optional = true }
wasmtime-runtime = { workspace = true }
target-lexicon = { workspace = true }
anyhow = { workspace = true }
Expand All @@ -44,5 +42,6 @@ features = ["Win32_System_Diagnostics_Debug"]
ittapi = { version = "0.4.0", optional = true }

[features]
profiling = ['dep:wasmtime-jit-debug', 'dep:ittapi']
profiling = ['dep:ittapi', 'dep:wasmtime-jit-debug']
demangle = ['dep:rustc-demangle', 'dep:cpp_demangle']
debug-builtins = ['wasmtime-runtime/debug-builtins']
4 changes: 1 addition & 3 deletions crates/jit/src/code_memory.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
//! Memory management for executable code.
use crate::subslice_range;
use crate::unwind::UnwindRegistration;
use anyhow::{anyhow, bail, Context, Result};
use object::read::{File, Object, ObjectSection};
use object::ObjectSymbol;
use std::mem::ManuallyDrop;
use std::ops::Range;
use wasmtime_environ::obj;
use wasmtime_jit_icache_coherence as icache_coherence;
use wasmtime_runtime::libcalls;
use wasmtime_runtime::MmapVec;
use wasmtime_runtime::{libcalls, MmapVec, UnwindRegistration};

/// Management of executable memory within a `MmapVec`
///
Expand Down
24 changes: 15 additions & 9 deletions crates/jit/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
//! steps.
use crate::code_memory::CodeMemory;
use crate::debug::create_gdbjit_image;
use crate::profiling::ProfilingAgent;
use anyhow::{bail, Context, Error, Result};
use anyhow::{bail, Error, Result};
use object::write::{Object, SectionId, StandardSegment, WritableBuffer};
use object::SectionKind;
use serde_derive::{Deserialize, Serialize};
Expand All @@ -19,9 +18,7 @@ use wasmtime_environ::{
DefinedFuncIndex, FuncIndex, FunctionLoc, MemoryInitialization, Module, ModuleTranslation,
PrimaryMap, SignatureIndex, StackMapInformation, Tunables, WasmFunctionInfo,
};
use wasmtime_runtime::{
CompiledModuleId, CompiledModuleIdAllocator, GdbJitImageRegistration, MmapVec,
};
use wasmtime_runtime::{CompiledModuleId, CompiledModuleIdAllocator, MmapVec};

/// Secondary in-memory results of function compilation.
#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -426,7 +423,8 @@ pub struct CompiledModule {
wasm_to_native_trampolines: Vec<(SignatureIndex, FunctionLoc)>,
meta: Metadata,
code_memory: Arc<CodeMemory>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
#[cfg(feature = "debug-builtins")]
dbg_jit_registration: Option<wasmtime_runtime::GdbJitImageRegistration>,
/// A unique ID used to register this module with the engine.
unique_id: CompiledModuleId,
func_names: Vec<FunctionName>,
Expand Down Expand Up @@ -459,6 +457,7 @@ impl CompiledModule {
module: Arc::new(info.module),
funcs: info.funcs,
wasm_to_native_trampolines: info.wasm_to_native_trampolines,
#[cfg(feature = "debug-builtins")]
dbg_jit_registration: None,
code_memory,
meta: info.meta,
Expand All @@ -471,11 +470,17 @@ impl CompiledModule {
}

fn register_debug_and_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
#[cfg(feature = "debug-builtins")]
if self.meta.native_debug_info_present {
use anyhow::Context;

let text = self.text();
let bytes = create_gdbjit_image(self.mmap().to_vec(), (text.as_ptr(), text.len()))
.context("failed to create jit image for gdb")?;
let reg = GdbJitImageRegistration::register(bytes);
let bytes = crate::debug::create_gdbjit_image(
self.mmap().to_vec(),
(text.as_ptr(), text.len()),
)
.context("failed to create jit image for gdb")?;
let reg = wasmtime_runtime::GdbJitImageRegistration::register(bytes);
self.dbg_jit_registration = Some(reg);
}
profiler.register_module(&self.code_memory, &|addr| {
Expand Down Expand Up @@ -669,6 +674,7 @@ impl CompiledModule {
/// what filename and line number a wasm pc comes from.
#[cfg(feature = "addr2line")]
pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> {
use anyhow::Context;
use gimli::EndianSlice;
if !self.meta.has_wasm_debuginfo {
return Ok(None);
Expand Down
2 changes: 1 addition & 1 deletion crates/jit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#![deny(missing_docs)]

mod code_memory;
#[cfg(feature = "debug-builtins")]
mod debug;
mod demangling;
mod instantiate;
pub mod profiling;
mod unwind;

pub use crate::code_memory::CodeMemory;
#[cfg(feature = "addr2line")]
Expand Down
14 changes: 0 additions & 14 deletions crates/jit/src/unwind.rs

This file was deleted.

Loading

0 comments on commit 494e2b8

Please sign in to comment.