mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 14:18:20 +00:00
250 lines
8.6 KiB
Rust
250 lines
8.6 KiB
Rust
//! JIT compilation.
|
|
|
|
use crate::error::InstantiationError;
|
|
use crate::resolver::Resolver;
|
|
use crate::tunables::Tunables;
|
|
use crate::CodeMemory;
|
|
use crate::{CompiledModule, DeserializeError, SerializeError};
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use wasm_common::entity::PrimaryMap;
|
|
use wasm_common::{FunctionType, LocalFunctionIndex, MemoryIndex, SignatureIndex, TableIndex};
|
|
use wasmer_compiler::{Compilation, CompileError, FunctionBody, Target};
|
|
#[cfg(feature = "compiler")]
|
|
use wasmer_compiler::{Compiler, CompilerConfig};
|
|
use wasmer_runtime::{
|
|
InstanceHandle, MemoryPlan, Module, SignatureRegistry, TablePlan, VMFunctionBody,
|
|
VMSharedSignatureIndex, VMTrampoline,
|
|
};
|
|
|
|
/// A WebAssembly `JIT` Engine.
|
|
#[derive(Clone)]
|
|
pub struct JITEngine {
|
|
inner: Arc<RefCell<JITEngineInner>>,
|
|
tunables: Arc<Box<dyn Tunables>>,
|
|
}
|
|
|
|
impl JITEngine {
|
|
const MAGIC_HEADER: &'static [u8] = b"\0wasmer-jit";
|
|
|
|
/// Create a new `JITEngine` with the given config
|
|
#[cfg(feature = "compiler")]
|
|
pub fn new<C: CompilerConfig>(config: &C, tunables: impl Tunables + 'static) -> Self
|
|
where
|
|
C: ?Sized,
|
|
{
|
|
let compiler = config.compiler();
|
|
Self {
|
|
inner: Arc::new(RefCell::new(JITEngineInner {
|
|
compiler: Some(compiler),
|
|
trampolines: HashMap::new(),
|
|
code_memory: CodeMemory::new(),
|
|
signatures: SignatureRegistry::new(),
|
|
})),
|
|
tunables: Arc::new(Box::new(tunables)),
|
|
}
|
|
}
|
|
|
|
/// Create a headless `JITEngine`
|
|
///
|
|
/// A headless engine is an engine without any compiler attached.
|
|
/// This is useful for assuring a minimal runtime for running
|
|
/// WebAssembly modules.
|
|
///
|
|
/// For example, for running in IoT devices where compilers are very
|
|
/// expensive, or also to optimize startup speed.
|
|
///
|
|
/// # Important
|
|
///
|
|
/// Headless engines can't compile or validate any modules,
|
|
/// they just take already processed Modules (via `Module::serialize`).
|
|
pub fn headless(tunables: impl Tunables + 'static) -> Self {
|
|
Self {
|
|
inner: Arc::new(RefCell::new(JITEngineInner {
|
|
#[cfg(feature = "compiler")]
|
|
compiler: None,
|
|
trampolines: HashMap::new(),
|
|
code_memory: CodeMemory::new(),
|
|
signatures: SignatureRegistry::new(),
|
|
})),
|
|
tunables: Arc::new(Box::new(tunables)),
|
|
}
|
|
}
|
|
|
|
/// Get the tunables
|
|
pub fn tunables(&self) -> &dyn Tunables {
|
|
&**self.tunables
|
|
}
|
|
|
|
pub(crate) fn compiler(&self) -> std::cell::Ref<'_, JITEngineInner> {
|
|
self.inner.borrow()
|
|
}
|
|
|
|
pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, JITEngineInner> {
|
|
self.inner.borrow_mut()
|
|
}
|
|
|
|
/// Check if the provided bytes look like a serialized
|
|
/// module by the `JITEngine` implementation.
|
|
pub fn is_deserializable(bytes: &[u8]) -> bool {
|
|
bytes.starts_with(Self::MAGIC_HEADER)
|
|
}
|
|
|
|
/// Register a signature
|
|
pub fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
|
|
let compiler = self.compiler();
|
|
compiler.signatures().register(func_type)
|
|
}
|
|
|
|
/// Lookup a signature
|
|
pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
|
|
let compiler = self.compiler();
|
|
compiler.signatures().lookup(sig)
|
|
}
|
|
|
|
/// Retrieves a trampoline given a signature
|
|
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
|
self.compiler().trampoline(sig)
|
|
}
|
|
|
|
/// Validates a WebAssembly module
|
|
pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
|
|
self.compiler().validate(binary)
|
|
}
|
|
|
|
/// Compile a WebAssembly binary
|
|
pub fn compile(&self, binary: &[u8]) -> Result<CompiledModule, CompileError> {
|
|
CompiledModule::new(&self, binary)
|
|
}
|
|
|
|
/// Instantiates a WebAssembly module
|
|
pub unsafe fn instantiate(
|
|
&self,
|
|
compiled_module: &CompiledModule,
|
|
resolver: &dyn Resolver,
|
|
) -> Result<InstanceHandle, InstantiationError> {
|
|
unsafe { compiled_module.instantiate(&self, resolver, Box::new(())) }
|
|
}
|
|
|
|
/// Serializes a WebAssembly module
|
|
pub fn serialize(&self, compiled_module: &CompiledModule) -> Result<Vec<u8>, SerializeError> {
|
|
// We append the header
|
|
let mut serialized = Self::MAGIC_HEADER.to_vec();
|
|
serialized.extend(compiled_module.serialize()?);
|
|
Ok(serialized)
|
|
}
|
|
|
|
/// Deserializes a WebAssembly module
|
|
pub fn deserialize(&self, bytes: &[u8]) -> Result<CompiledModule, DeserializeError> {
|
|
if !Self::is_deserializable(bytes) {
|
|
return Err(DeserializeError::Incompatible(
|
|
"The provided bytes are not wasmer-jit".to_string(),
|
|
));
|
|
}
|
|
Ok(CompiledModule::deserialize(
|
|
&self,
|
|
&bytes[Self::MAGIC_HEADER.len()..],
|
|
)?)
|
|
}
|
|
}
|
|
|
|
/// The inner contents of `JITEngine`
|
|
pub struct JITEngineInner {
|
|
/// The compiler
|
|
#[cfg(feature = "compiler")]
|
|
compiler: Option<Box<dyn Compiler>>,
|
|
/// Pointers to trampoline functions used to enter particular signatures
|
|
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
|
/// The code memory is responsible of publishing the compiled
|
|
/// functions to memory.
|
|
code_memory: CodeMemory,
|
|
/// The signature registry is used mainly to operate with trampolines
|
|
/// performantly.
|
|
signatures: SignatureRegistry,
|
|
}
|
|
|
|
impl JITEngineInner {
|
|
/// Gets the compiler associated to this JIT
|
|
#[cfg(feature = "compiler")]
|
|
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
|
|
if self.compiler.is_none() {
|
|
return Err(CompileError::Codegen("The JITEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
|
|
}
|
|
Ok(&**self.compiler.as_ref().unwrap())
|
|
}
|
|
|
|
/// Validate the module
|
|
#[cfg(feature = "compiler")]
|
|
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
|
self.compiler()?.validate_module(data)
|
|
}
|
|
|
|
/// Validate the module
|
|
#[cfg(not(feature = "compiler"))]
|
|
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
|
Err(CompileError::Validate(
|
|
"Validation is only enabled with the compiler feature".to_string(),
|
|
))
|
|
}
|
|
|
|
/// Compile the given function bodies.
|
|
pub(crate) fn allocate<'data>(
|
|
&mut self,
|
|
module: &Module,
|
|
functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
|
|
trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
|
|
) -> Result<PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]>, CompileError> {
|
|
// Allocate all of the compiled functions into executable memory,
|
|
// copying over their contents.
|
|
let allocated_functions =
|
|
self.code_memory
|
|
.allocate_functions(&functions)
|
|
.map_err(|message| {
|
|
CompileError::Resource(format!(
|
|
"failed to allocate memory for functions: {}",
|
|
message
|
|
))
|
|
})?;
|
|
|
|
for (sig_index, compiled_function) in trampolines.iter() {
|
|
let func_type = module.signatures.get(sig_index).unwrap();
|
|
let index = self.signatures.register(&func_type);
|
|
if self.trampolines.contains_key(&index) {
|
|
// We don't need to allocate the trampoline in case
|
|
// it's signature is already allocated.
|
|
continue;
|
|
}
|
|
let ptr = self
|
|
.code_memory
|
|
.allocate_for_function(&compiled_function)
|
|
.map_err(|message| {
|
|
CompileError::Resource(format!(
|
|
"failed to allocate memory for trampolines: {}",
|
|
message
|
|
))
|
|
})?
|
|
.as_ptr();
|
|
let trampoline =
|
|
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
|
|
self.trampolines.insert(index, trampoline);
|
|
}
|
|
Ok(allocated_functions)
|
|
}
|
|
|
|
/// Make memory containing compiled code executable.
|
|
pub(crate) fn publish_compiled_code(&mut self) {
|
|
self.code_memory.publish();
|
|
}
|
|
|
|
/// Shared signature registry.
|
|
pub fn signatures(&self) -> &SignatureRegistry {
|
|
&self.signatures
|
|
}
|
|
|
|
/// Gets the trampoline pre-registered for a particular signature
|
|
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
|
self.trampolines.get(&sig).cloned()
|
|
}
|
|
}
|