Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize and deserialize compilation artifacts. #2020

Merged
merged 9 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

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

7 changes: 5 additions & 2 deletions cranelift/codegen/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub trait Configurable {
}

/// Collect settings values based on a template.
#[derive(Clone)]
#[derive(Clone, Hash)]
pub struct Builder {
template: &'static detail::Template,
bytes: Box<[u8]>,
Expand Down Expand Up @@ -212,8 +212,10 @@ impl<'a> PredicateView<'a> {
pub mod detail {
use crate::constant_hash;
use core::fmt;
use core::hash::Hash;

/// An instruction group template.
#[derive(Hash)]
pub struct Template {
/// Name of the instruction group.
pub name: &'static str,
Expand Down Expand Up @@ -281,6 +283,7 @@ pub mod detail {
/// A setting descriptor holds the information needed to generically set and print a setting.
///
/// Each settings group will be represented as a constant DESCRIPTORS array.
#[derive(Hash)]
pub struct Descriptor {
/// Lower snake-case name of setting as defined in meta.
pub name: &'static str,
Expand All @@ -293,7 +296,7 @@ pub mod detail {
}

/// The different kind of settings along with descriptor bits that depend on the kind.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Hash)]
pub enum Detail {
/// A boolean setting only uses one bit, numbered from LSB.
Bool {
Expand Down
31 changes: 31 additions & 0 deletions crates/c-api/include/wasmtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,37 @@ WASM_API_EXTERN void wasmtime_externref_new_with_finalizer(
*/
WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap);

/**
* \brief This function serializes compiled module artifacts
* as blob data.
*
* \param module the module
* \param ret if the conversion is successful, this byte vector is filled in with
* the serialized compiled module.
*
* \return a non-null error if parsing fails, or returns `NULL`. If parsing
* fails then `ret` isn't touched.
*
* This function does not take ownership of `module`, and the caller is
* expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t.
*/
WASM_API_EXTERN own wasmtime_error_t* wasmtime_module_serialize(
wasm_module_t* module,
own wasm_byte_vec_t *ret
);

/**
* \brief Build a module from serialized data.
* *
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*/
WASM_API_EXTERN own wasmtime_error_t *wasmtime_module_deserialize(
wasm_engine_t *engine,
const wasm_byte_vec_t *serialized,
own wasm_module_t **ret
);

#undef own

#ifdef __cplusplus
Expand Down
60 changes: 60 additions & 0 deletions crates/c-api/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,63 @@ pub extern "C" fn wasm_module_obtain(
exports,
}))
}

#[no_mangle]
pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_byte_vec_t) {
drop(wasmtime_module_serialize(module, ret));
}

#[no_mangle]
pub extern "C" fn wasm_module_deserialize(
store: &wasm_store_t,
binary: &wasm_byte_vec_t,
) -> Option<Box<wasm_module_t>> {
let mut ret = ptr::null_mut();
let engine = wasm_engine_t {
engine: store.store.engine().clone(),
};
match wasmtime_module_deserialize(&engine, binary, &mut ret) {
Some(_err) => None,
None => {
assert!(!ret.is_null());
Some(unsafe { Box::from_raw(ret) })
}
}
}

#[no_mangle]
pub extern "C" fn wasmtime_module_serialize(
module: &wasm_module_t,
ret: &mut wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(module.module.serialize(), |buf| {
ret.set_buffer(buf);
})
}

#[no_mangle]
pub extern "C" fn wasmtime_module_deserialize(
engine: &wasm_engine_t,
binary: &wasm_byte_vec_t,
ret: &mut *mut wasm_module_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(
Module::deserialize(&engine.engine, binary.as_slice()),
|module| {
let imports = module
.imports()
.map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty()))
.collect::<Vec<_>>();
let exports = module
.exports()
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
.collect::<Vec<_>>();
let module = Box::new(wasm_module_t {
module: module,
imports,
exports,
});
*ret = Box::into_raw(module);
},
)
}
6 changes: 3 additions & 3 deletions crates/environ/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ impl TablePlan {

/// A translated WebAssembly module, excluding the function bodies and
/// memory initializers.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Module {
/// A unique identifier (within this process) for this module.
#[serde(skip_serializing, default = "Module::next_id")]
#[serde(skip_serializing, skip_deserializing, default = "Module::next_id")]
pub id: usize,

/// The name of this wasm module, often found in the wasm file.
Expand Down Expand Up @@ -181,7 +181,7 @@ pub struct Module {
/// This is stored within a `Module` and it implements `Hash`, unlike `Module`,
/// and is used as part of the cache key when we load compiled modules from the
/// global cache.
#[derive(Debug, Hash, Serialize, Deserialize)]
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct ModuleLocal {
/// Unprocessed signatures exactly as provided by `declare_signature()`.
pub signatures: PrimaryMap<SignatureIndex, (WasmFuncType, ir::Signature)>,
Expand Down
3 changes: 2 additions & 1 deletion crates/environ/src/module_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use cranelift_wasm::{
Memory, MemoryIndex, ModuleTranslationState, SignatureIndex, Table, TableIndex,
TargetEnvironment, WasmError, WasmFuncType, WasmResult,
};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::sync::Arc;

Expand Down Expand Up @@ -450,7 +451,7 @@ pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir

/// A memory index and offset within that memory where a data initialization
/// should is to be performed.
#[derive(Clone)]
#[derive(Clone, Serialize, Deserialize)]
pub struct DataInitializerLocation {
/// The index of the memory to initialize.
pub memory_index: MemoryIndex,
Expand Down
1 change: 1 addition & 0 deletions crates/jit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ cfg-if = "0.1.9"
log = "0.4"
gimli = { version = "0.21.0", default-features = false, features = ["write"] }
object = { version = "0.20", default-features = false, features = ["write"] }
serde = { version = "1.0.94", features = ["derive"] }

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
Expand Down
68 changes: 59 additions & 9 deletions crates/jit/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::link::link_module;
use crate::object::ObjectUnwindInfo;
use crate::resolver::Resolver;
use object::File as ObjectFile;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
Expand Down Expand Up @@ -51,27 +52,47 @@ pub enum SetupError {
DebugInfo(#[from] anyhow::Error),
}

// Contains all compilation artifacts.
struct CompilationArtifacts {
/// Contains all compilation artifacts.
#[derive(Serialize, Deserialize)]
pub struct CompilationArtifacts {
/// Module metadata.
module: Module,

/// ELF image with functions code.
obj: Box<[u8]>,

/// Unwind information for function code.
unwind_info: Box<[ObjectUnwindInfo]>,

/// Data initiailizers.
data_initializers: Box<[OwnedDataInitializer]>,

/// Traps descriptors.
traps: Traps,

/// Stack map descriptors.
stack_maps: StackMaps,

/// Wasm to function code address map.
address_transform: ModuleAddressMap,

/// Debug info presence flags.
debug_info: bool,
}

impl CompilationArtifacts {
fn new(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> {
/// Builds compilation artifacts.
pub fn build(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> {
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables());

let translation = environ
.translate(data)
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;

let debug_info = compiler.tunables().debug_info;

let mut debug_data = None;
if compiler.tunables().debug_info {
if debug_info {
// TODO Do we want to ignore invalid DWARF data?
debug_data = Some(read_debuginfo(&data)?);
}
Expand Down Expand Up @@ -110,6 +131,7 @@ impl CompilationArtifacts {
traps,
stack_maps,
address_transform,
debug_info,
})
}
}
Expand All @@ -136,6 +158,8 @@ pub struct CompiledModule {
traps: Traps,
stack_maps: StackMaps,
address_transform: ModuleAddressMap,
obj: Box<[u8]>,
unwind_info: Box<[ObjectUnwindInfo]>,
}

impl CompiledModule {
Expand All @@ -145,8 +169,16 @@ impl CompiledModule {
data: &'data [u8],
profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> {
let artifacts = CompilationArtifacts::new(compiler, data)?;
let artifacts = CompilationArtifacts::build(compiler, data)?;
Self::from_artifacts(artifacts, compiler.isa(), profiler)
}

/// Creates `CompiledModule` directly from `CompilationArtifacts`.
pub fn from_artifacts(
artifacts: CompilationArtifacts,
isa: &dyn TargetIsa,
profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> {
let CompilationArtifacts {
module,
obj,
Expand All @@ -155,20 +187,21 @@ impl CompiledModule {
traps,
stack_maps,
address_transform,
debug_info,
} = artifacts;

// Allocate all of the compiled functions into executable memory,
// copying over their contents.
let (code_memory, code_range, finished_functions, trampolines) =
build_code_memory(compiler.isa(), &obj, &module, unwind_info).map_err(|message| {
build_code_memory(isa, &obj, &module, &unwind_info).map_err(|message| {
SetupError::Instantiate(InstantiationError::Resource(format!(
"failed to build code memory for functions: {}",
message
)))
})?;

// Register GDB JIT images; initialize profiler and load the wasm module.
let dbg_jit_registration = if compiler.tunables().debug_info {
let dbg_jit_registration = if debug_info {
let bytes = create_dbg_image(obj.to_vec(), code_range, &module, &finished_functions)?;

profiler.module_load(&module, &finished_functions, Some(&bytes));
Expand All @@ -194,9 +227,25 @@ impl CompiledModule {
traps,
stack_maps,
address_transform,
obj,
unwind_info,
})
}

/// Extracts `CompilationArtifacts` from the compiled module.
pub fn to_compilation_artifacts(&self) -> CompilationArtifacts {
CompilationArtifacts {
module: (*self.module).clone(),
obj: self.obj.clone(),
unwind_info: self.unwind_info.clone(),
data_initializers: self.data_initializers.clone(),
traps: self.traps.clone(),
stack_maps: self.stack_maps.clone(),
address_transform: self.address_transform.clone(),
debug_info: self.code.dbg_jit_registration.is_some(),
}
}

/// Crate an `Instance` from this `CompiledModule`.
///
/// Note that if only one instance of this module is needed, it may be more
Expand Down Expand Up @@ -305,6 +354,7 @@ impl CompiledModule {

/// Similar to `DataInitializer`, but owns its own copy of the data rather
/// than holding a slice of the original module.
#[derive(Clone, Serialize, Deserialize)]
pub struct OwnedDataInitializer {
/// The location where the initialization is to be performed.
location: DataInitializerLocation,
Expand Down Expand Up @@ -340,7 +390,7 @@ fn build_code_memory(
isa: &dyn TargetIsa,
obj: &[u8],
module: &Module,
unwind_info: Box<[ObjectUnwindInfo]>,
unwind_info: &Box<[ObjectUnwindInfo]>,
) -> Result<
(
CodeMemory,
Expand All @@ -354,7 +404,7 @@ fn build_code_memory(

let mut code_memory = CodeMemory::new();

let allocation = code_memory.allocate_for_object(&obj, &unwind_info)?;
let allocation = code_memory.allocate_for_object(&obj, unwind_info)?;

// Second, create a PrimaryMap from result vector of pointers.
let mut finished_functions = PrimaryMap::new();
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 @@ -35,7 +35,7 @@ pub mod trampoline;

pub use crate::code_memory::CodeMemory;
pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
pub use crate::instantiate::{CompiledModule, SetupError};
pub use crate::instantiate::{CompilationArtifacts, CompiledModule, SetupError};
pub use crate::link::link_module;
pub use crate::resolver::{NullResolver, Resolver};

Expand Down
Loading