Serialize and deserialize compilation artifacts. (#2020)

* Serialize and deserialize Module
* Use bincode to serialize
* Add wasm_module_serialize; docs
* Simple tests
This commit is contained in:
Yury Delendik
2020-07-21 15:05:50 -05:00
committed by GitHub
parent c420f65214
commit 399ee0a54c
17 changed files with 528 additions and 20 deletions

View File

@@ -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"] }

View File

@@ -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;
@@ -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)?);
}
@@ -110,6 +131,7 @@ impl CompilationArtifacts {
traps,
stack_maps,
address_transform,
debug_info,
})
}
}
@@ -136,6 +158,8 @@ pub struct CompiledModule {
traps: Traps,
stack_maps: StackMaps,
address_transform: ModuleAddressMap,
obj: Box<[u8]>,
unwind_info: Box<[ObjectUnwindInfo]>,
}
impl CompiledModule {
@@ -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,
@@ -155,12 +187,13 @@ 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
@@ -168,7 +201,7 @@ impl CompiledModule {
})?;
// 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));
@@ -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
@@ -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,
@@ -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,
@@ -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();

View File

@@ -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};

View File

@@ -3,6 +3,7 @@
use super::trampoline::build_trampoline;
use cranelift_frontend::FunctionBuilderContext;
use object::write::Object;
use serde::{Deserialize, Serialize};
use wasmtime_debug::DwarfSection;
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
@@ -13,7 +14,7 @@ use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
pub use wasmtime_obj::utils;
/// Unwind information for object files functions (including trampolines).
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ObjectUnwindInfo {
Func(FuncIndex, UnwindInfo),
Trampoline(SignatureIndex, UnwindInfo),