diff --git a/Cargo.toml b/Cargo.toml index 482d5e9dca..1be8233633 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ name = "wasm2obj" path = "src/wasm2obj.rs" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-native = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-native = "0.36.0" wasmtime-debug = { path = "wasmtime-debug" } wasmtime-environ = { path = "wasmtime-environ" } wasmtime-runtime = { path = "wasmtime-runtime" } @@ -34,8 +34,7 @@ wasmtime-wasi = { path = "wasmtime-wasi" } wasmtime-wasi-c = { path = "wasmtime-wasi-c", optional = true } wasi-common = { git = "https://github.com/CraneStation/wasi-common", rev = "c3994bf57b5d2f1f973b0e4e37bc385695aa4ed2"} docopt = "1.0.1" -serde = "1.0.75" -serde_derive = "1.0.75" +serde = { "version" = "1.0.94", features = ["derive"] } faerie = "0.10.1" target-lexicon = { version = "0.4.0", default-features = false } pretty_env_logger = "0.3.0" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 1dc445f16c..48d1e4b0dc 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -11,9 +11,9 @@ cargo-fuzz = true [dependencies] wasmtime-environ = { path = "../wasmtime-environ" } wasmtime-jit = { path = "../wasmtime-jit" } -cranelift-codegen = "0.33.0" -cranelift-wasm = "0.33.0" -cranelift-native = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } +cranelift-native = "0.36.0" libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } wasmparser = { version = "0.32.1", default-features = false } binaryen = "0.5.0" diff --git a/src/wasm2obj.rs b/src/wasm2obj.rs index 4da5336666..754efdc174 100644 --- a/src/wasm2obj.rs +++ b/src/wasm2obj.rs @@ -29,14 +29,12 @@ ) )] -#[macro_use] -extern crate serde_derive; - use cranelift_codegen::isa; use cranelift_codegen::settings; use cranelift_native; use docopt::Docopt; use faerie::Artifact; +use serde::Deserialize; use std::error::Error; use std::fmt::format; use std::fs::File; @@ -49,6 +47,7 @@ use std::str; use std::str::FromStr; use target_lexicon::Triple; use wasmtime_debug::{emit_debugsections, read_debuginfo}; +use wasmtime_environ::cache_conf; use wasmtime_environ::{Compiler, Cranelift, ModuleEnvironment, Tunables}; use wasmtime_obj::emit_module; @@ -63,7 +62,7 @@ The translation is dependent on the environment chosen. The default is a dummy environment that produces placeholder values. Usage: - wasm2obj [--target TARGET] [-g] -o + wasm2obj [--target TARGET] [-cdg] -o wasm2obj --help | --version Options: @@ -71,6 +70,7 @@ Options: -h, --help print this help message --target build for the target triple; default is the host machine -g generate debug information + -c, --cache enable caching system --version print the Cranelift version -d, --debug enable debug output on stderr/stdout "; @@ -82,6 +82,7 @@ struct Args { arg_target: Option, flag_g: bool, flag_debug: bool, + flag_cache: bool, } fn read_wasm_file(path: PathBuf) -> Result, io::Error> { @@ -107,6 +108,8 @@ fn main() { utils::init_file_per_thread_logger(); } + cache_conf::init(args.flag_cache); + let path = Path::new(&args.arg_file); match handle_module( path.to_path_buf(), diff --git a/src/wasmtime.rs b/src/wasmtime.rs index b9c368e0cd..479efd2915 100644 --- a/src/wasmtime.rs +++ b/src/wasmtime.rs @@ -30,14 +30,12 @@ ) )] -#[macro_use] -extern crate serde_derive; - use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use cranelift_native; use docopt::Docopt; use pretty_env_logger; +use serde::Deserialize; use std::error::Error; use std::ffi::OsStr; use std::fs::File; @@ -48,6 +46,7 @@ use std::path::{Path, PathBuf}; use std::process::exit; use wabt; use wasi_common::preopen_dir; +use wasmtime_environ::cache_conf; use wasmtime_jit::{ActionOutcome, Context}; use wasmtime_wasi::instantiate_wasi; use wasmtime_wast::instantiate_spectest; @@ -67,13 +66,14 @@ including calling the start function if one is present. Additional functions given with --invoke are then called. Usage: - wasmtime [-odg] [--wasi-c] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] [...] - wasmtime [-odg] [--wasi-c] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] --invoke= [...] + wasmtime [-ocdg] [--wasi-c] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] [...] + wasmtime [-ocdg] [--wasi-c] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] --invoke= [...] wasmtime --help | --version Options: --invoke= name of function to run -o, --optimize runs optimization passes on the translated functions + -c, --cache enable caching system -g generate debug information -d, --debug enable debug output on stderr/stdout --wasi-c enable the wasi-c implementation of WASI @@ -91,6 +91,7 @@ struct Args { arg_file: String, arg_arg: Vec, flag_optimize: bool, + flag_cache: bool, flag_debug: bool, flag_g: bool, flag_invoke: Option, @@ -207,6 +208,8 @@ fn main() { utils::init_file_per_thread_logger(); } + cache_conf::init(args.flag_cache); + let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { panic!("host machine is not a supported target"); }); diff --git a/src/wast.rs b/src/wast.rs index 527668ae7d..a2af79b87e 100644 --- a/src/wast.rs +++ b/src/wast.rs @@ -25,16 +25,15 @@ ) )] -#[macro_use] -extern crate serde_derive; - use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use cranelift_native; use docopt::Docopt; use pretty_env_logger; +use serde::Deserialize; use std::path::Path; use std::process; +use wasmtime_environ::cache_conf; use wasmtime_jit::Compiler; use wasmtime_wast::WastContext; @@ -46,13 +45,14 @@ const USAGE: &str = " Wast test runner. Usage: - run_wast [-do] ... + run_wast [-cdo] ... run_wast --help | --version Options: -h, --help print this help message --version print the Cranelift version -o, --optimize runs optimization passes on the translated functions + -c, --cache enable caching system -d, --debug enable debug output on stderr/stdout "; @@ -62,6 +62,7 @@ struct Args { flag_debug: bool, flag_function: Option, flag_optimize: bool, + flag_cache: bool, } fn main() { @@ -80,6 +81,8 @@ fn main() { utils::init_file_per_thread_logger(); } + cache_conf::init(args.flag_cache); + let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { panic!("host machine is not a supported target"); }); diff --git a/wasmtime-debug/Cargo.toml b/wasmtime-debug/Cargo.toml index 4590d0ffee..3f1f3bcf49 100644 --- a/wasmtime-debug/Cargo.toml +++ b/wasmtime-debug/Cargo.toml @@ -14,9 +14,9 @@ edition = "2018" [dependencies] gimli = "0.19.0" wasmparser = { version = "0.34.0" } -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } faerie = "0.10.1" wasmtime-environ = { path = "../wasmtime-environ", default-features = false } target-lexicon = { version = "0.4.0", default-features = false } diff --git a/wasmtime-environ/Cargo.toml b/wasmtime-environ/Cargo.toml index 556c2700d9..fa62473083 100644 --- a/wasmtime-environ/Cargo.toml +++ b/wasmtime-environ/Cargo.toml @@ -12,14 +12,22 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } lightbeam = { path = "../lightbeam", optional = true } failure = { version = "0.1.3", default-features = false } failure_derive = { version = "0.1.3", default-features = false } indexmap = "1.0.2" rayon = "1.1" +directories = "2.0.1" +sha2 = "0.8.0" +base64 = "0.10.1" +serde = { version = "1.0.94", features = ["derive"] } +bincode = "1.1.4" +lazy_static = "1.3.0" +spin = "0.5.0" +log = { version = "0.4.6", default-features = false } [features] default = ["std"] diff --git a/wasmtime-environ/src/address_map.rs b/wasmtime-environ/src/address_map.rs index 7ce2c28a26..5295c9d0ad 100644 --- a/wasmtime-environ/src/address_map.rs +++ b/wasmtime-environ/src/address_map.rs @@ -4,10 +4,11 @@ use cranelift_codegen::ir; use cranelift_entity::PrimaryMap; use cranelift_wasm::DefinedFuncIndex; +use serde::{Deserialize, Serialize}; use std::vec::Vec; /// Single source location to generated address mapping. -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct InstructionAddressMap { /// Original source location. pub srcloc: ir::SourceLoc, @@ -20,7 +21,7 @@ pub struct InstructionAddressMap { } /// Function and its instructions addresses mappings. -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct FunctionAddressMap { /// Instructions maps. /// The array is sorted by the InstructionAddressMap::code_offset field. diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs new file mode 100644 index 0000000000..3c934794e2 --- /dev/null +++ b/wasmtime-environ/src/cache.rs @@ -0,0 +1,342 @@ +use crate::address_map::ModuleAddressMap; +use crate::compilation::{CodeAndJTOffsets, Compilation, Relocations}; +use crate::module::Module; +use cranelift_codegen::ir; +use cranelift_codegen::isa; +use directories::ProjectDirs; +use lazy_static::lazy_static; +use log::warn; +use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; +use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer}; +#[cfg(windows)] +use std::ffi::OsString; +use std::fmt; +use std::fs; +use std::io; +#[cfg(windows)] +use std::path::Path; +use std::path::PathBuf; + +/// Module for configuring the cache system. +pub mod conf { + use spin::Once; + + // Private static, so only internal function can access it. + static CACHE_ENABLED: Once = Once::new(); + + /// Returns true if and only if the cache is enabled. + pub fn cache_enabled() -> bool { + // Not everyone knows about the cache system, i.e. the tests, + // so the default is false. + *CACHE_ENABLED.call_once(|| false) + } + + /// Initializes the cache system. Should be called exactly once, + /// and before using the cache system. Otherwise it can panic. + pub fn init(enabled: bool) { + // init() should be called exactly once + assert!(CACHE_ENABLED.r#try().is_none()); + let val = *CACHE_ENABLED.call_once(|| enabled); + // But multiple threads can pass the first assertion, so let's guarantee consistency: + assert!(val == enabled); + } +} + +lazy_static! { + static ref CACHE_DIR: Option = + match ProjectDirs::from("", "CraneStation", "wasmtime") { + Some(proj_dirs) => { + let cache_dir = proj_dirs.cache_dir(); + // Temporary workaround for: https://github.com/rust-lang/rust/issues/32689 + #[cfg(windows)] + let mut long_path = OsString::from("\\\\?\\"); + #[cfg(windows)] + let cache_dir = { + if cache_dir.starts_with("\\\\?\\") { + cache_dir + } + else { + long_path.push(cache_dir.as_os_str()); + Path::new(&long_path) + } + }; + match fs::create_dir_all(cache_dir) { + Ok(()) => (), + Err(err) => warn!("Unable to create cache directory, failed with: {}", err), + }; + Some(cache_dir.to_path_buf()) + } + None => { + warn!("Unable to find cache directory"); + None + } + }; +} + +pub struct ModuleCacheEntry { + mod_cache_path: Option, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ModuleCacheData { + compilation: Compilation, + relocations: Relocations, + address_transforms: ModuleAddressMap, +} + +type ModuleCacheDataTupleType = (Compilation, Relocations, ModuleAddressMap); + +impl ModuleCacheEntry { + pub fn new(module: &Module, _isa: &dyn isa::TargetIsa, _generate_debug_info: bool) -> Self { + // TODO: cache directory hierarchy with isa name, compiler name & git revision, and files with flag if debug symbols are available + let mod_cache_path = if conf::cache_enabled() { + CACHE_DIR.clone().and_then(|p| { + module.hash.map(|hash| { + p.join(format!( + "mod-{}", + base64::encode_config(&hash, base64::URL_SAFE_NO_PAD) // standard encoding uses '/' which can't be used for filename + )) + }) + }) + } else { + None + }; + + ModuleCacheEntry { mod_cache_path } + } + + pub fn get_data(&self) -> Option { + if let Some(p) = &self.mod_cache_path { + match fs::read(p) { + Ok(cache_bytes) => match bincode::deserialize(&cache_bytes[..]) { + Ok(data) => Some(data), + Err(err) => { + warn!("Failed to deserialize cached code: {}", err); + None + } + }, + Err(_) => None, + } + } else { + None + } + } + + pub fn update_data(&self, data: &ModuleCacheData) { + if let Some(p) = &self.mod_cache_path { + let cache_buf = match bincode::serialize(&data) { + Ok(data) => data, + Err(err) => { + warn!("Failed to serialize cached code: {}", err); + return; + } + }; + match fs::write(p, &cache_buf) { + Ok(()) => (), + Err(err) => { + warn!( + "Failed to write cached code to disk, path: {}, message: {}", + p.display(), + err + ); + match fs::remove_file(p) { + Ok(()) => (), + Err(err) => { + if err.kind() != io::ErrorKind::NotFound { + warn!( + "Failed to cleanup invalid cache, path: {}, message: {}", + p.display(), + err + ); + } + } + } + } + } + } + } +} + +impl ModuleCacheData { + pub fn from_tuple(data: ModuleCacheDataTupleType) -> Self { + Self { + compilation: data.0, + relocations: data.1, + address_transforms: data.2, + } + } + + pub fn to_tuple(self) -> ModuleCacheDataTupleType { + (self.compilation, self.relocations, self.address_transforms) + } +} + +//-//////////////////////////////////////////////////////////////////// +// Serialization and deserialization of type containing SecondaryMap // +//-//////////////////////////////////////////////////////////////////// + +enum JtOffsetsWrapper<'a> { + Ref(&'a ir::JumpTableOffsets), // for serialization + Data(ir::JumpTableOffsets), // for deserialization +} + +impl Serialize for CodeAndJTOffsets { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut cajto = serializer.serialize_struct("CodeAndJTOffsets", 2)?; + cajto.serialize_field("body", &self.body)?; + cajto.serialize_field("jt_offsets", &JtOffsetsWrapper::Ref(&self.jt_offsets))?; + cajto.end() + } +} + +impl<'de> Deserialize<'de> for CodeAndJTOffsets { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(serde::Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Body, + JtOffsets, + }; + + struct CodeAndJTOffsetsVisitor; + + impl<'de> Visitor<'de> for CodeAndJTOffsetsVisitor { + type Value = CodeAndJTOffsets; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct CodeAndJTOffsets") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: SeqAccess<'de>, + { + let body = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let jt_offsets = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(1, &self))?; + match jt_offsets { + JtOffsetsWrapper::Data(jt_offsets) => Ok(CodeAndJTOffsets { body, jt_offsets }), + JtOffsetsWrapper::Ref(_) => Err(de::Error::custom( + "Received invalid variant of JtOffsetsWrapper", + )), + } + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut body = None; + let mut jt_offsets = None; + while let Some(key) = map.next_key()? { + match key { + Field::Body => { + if body.is_some() { + return Err(de::Error::duplicate_field("body")); + } + body = Some(map.next_value()?); + } + Field::JtOffsets => { + if jt_offsets.is_some() { + return Err(de::Error::duplicate_field("jt_offsets")); + } + jt_offsets = Some(map.next_value()?); + } + } + } + + let body = body.ok_or_else(|| de::Error::missing_field("body"))?; + let jt_offsets = + jt_offsets.ok_or_else(|| de::Error::missing_field("jt_offsets"))?; + match jt_offsets { + JtOffsetsWrapper::Data(jt_offsets) => Ok(CodeAndJTOffsets { body, jt_offsets }), + JtOffsetsWrapper::Ref(_) => Err(de::Error::custom( + "Received invalid variant of JtOffsetsWrapper", + )), + } + } + } + + const FIELDS: &'static [&'static str] = &["body", "jt_offsets"]; + deserializer.deserialize_struct("CodeAndJTOffsets", FIELDS, CodeAndJTOffsetsVisitor) + } +} + +impl Serialize for JtOffsetsWrapper<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + JtOffsetsWrapper::Ref(data) => { + // TODO: bincode encodes option as "byte for Some/None" and then optionally the content + // TODO: we can actually optimize it by encoding manually bitmask, then elements + let default_val = data.get_default(); + let mut seq = serializer.serialize_seq(Some(1 + data.len()))?; + seq.serialize_element(&Some(default_val))?; + for e in data.values() { + let some_e = Some(e); + seq.serialize_element(if e == default_val { &None } else { &some_e })?; + } + seq.end() + } + JtOffsetsWrapper::Data(_) => Err(ser::Error::custom( + "Received invalid variant of JtOffsetsWrapper", + )), + } + } +} + +impl<'de> Deserialize<'de> for JtOffsetsWrapper<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct JtOffsetsWrapperVisitor; + + impl<'de> Visitor<'de> for JtOffsetsWrapperVisitor { + type Value = JtOffsetsWrapper<'static>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct JtOffsetsWrapper") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + match seq.next_element()? { + Some(Some(default_val)) => { + let mut m = cranelift_entity::SecondaryMap::with_default(default_val); + let mut idx = 0; + while let Some(val) = seq.next_element()? { + let val: Option<_> = val; // compiler can't infer the type, and this line is needed + match ir::JumpTable::with_number(idx) { + Some(jt_idx) => m[jt_idx] = val.unwrap_or(default_val), + None => { + return Err(serde::de::Error::custom( + "Invalid JumpTable reference", + )) + } + }; + idx += 1; + } + Ok(JtOffsetsWrapper::Data(m)) + } + _ => Err(serde::de::Error::custom("Default value required")), + } + } + } + + deserializer.deserialize_seq(JtOffsetsWrapperVisitor {}) + } +} diff --git a/wasmtime-environ/src/compilation.rs b/wasmtime-environ/src/compilation.rs index c699cbc984..1ceb67c015 100644 --- a/wasmtime-environ/src/compilation.rs +++ b/wasmtime-environ/src/compilation.rs @@ -7,6 +7,7 @@ use crate::module_environ::FunctionBodyData; use cranelift_codegen::{binemit, ir, isa, CodegenError}; use cranelift_entity::PrimaryMap; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError}; +use serde::{Deserialize, Serialize}; use std::ops::Range; use std::vec::Vec; @@ -23,7 +24,7 @@ pub struct CodeAndJTOffsets { type Functions = PrimaryMap; /// The result of compiling a WebAssembly module's functions. -#[derive(Debug)] +#[derive(Deserialize, Serialize, Debug)] pub struct Compilation { /// Compiled machine code for the function bodies. functions: Functions, @@ -94,7 +95,7 @@ impl<'a> Iterator for Iter<'a> { } /// A record of a relocation to perform. -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Relocation { /// The relocation code. pub reloc: binemit::Reloc, @@ -107,7 +108,7 @@ pub struct Relocation { } /// Destination function. Can be either user function or some special one, like `memory.grow`. -#[derive(Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub enum RelocationTarget { /// The user function index. UserFunc(FuncIndex), diff --git a/wasmtime-environ/src/cranelift.rs b/wasmtime-environ/src/cranelift.rs index 9f12dbd0c2..508a50bad7 100644 --- a/wasmtime-environ/src/cranelift.rs +++ b/wasmtime-environ/src/cranelift.rs @@ -1,6 +1,7 @@ //! Support for compiling with Cranelift. use crate::address_map::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; +use crate::cache::{ModuleCacheData, ModuleCacheEntry}; use crate::compilation::{ CodeAndJTOffsets, Compilation, CompileError, Relocation, RelocationTarget, Relocations, }; @@ -123,73 +124,90 @@ impl crate::compilation::Compiler for Cranelift { isa: &dyn isa::TargetIsa, generate_debug_info: bool, ) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError> { - let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); + let cache_entry = ModuleCacheEntry::new(module, isa, generate_debug_info); - function_body_inputs - .into_iter() - .collect::)>>() - .par_iter() - .map(|(i, input)| { - let func_index = module.func_index(*i); - let mut context = Context::new(); - context.func.name = get_func_name(func_index); - context.func.signature = module.signatures[module.functions[func_index]].clone(); + let data = match cache_entry.get_data() { + Some(data) => data, + None => { + let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut trans = FuncTranslator::new(); - trans - .translate( - input.data, - input.module_offset, - &mut context.func, - &mut FuncEnvironment::new(isa.frontend_config(), module), - ) - .map_err(CompileError::Wasm)?; + function_body_inputs + .into_iter() + .collect::)>>() + .par_iter() + .map(|(i, input)| { + let func_index = module.func_index(*i); + let mut context = Context::new(); + context.func.name = get_func_name(func_index); + context.func.signature = + module.signatures[module.functions[func_index]].clone(); - let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink::new(func_index); - let mut trap_sink = binemit::NullTrapSink {}; - context - .compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink) - .map_err(CompileError::Codegen)?; + let mut trans = FuncTranslator::new(); + trans + .translate( + input.data, + input.module_offset, + &mut context.func, + &mut FuncEnvironment::new(isa.frontend_config(), module), + ) + .map_err(CompileError::Wasm)?; - let jt_offsets = context.func.jt_offsets.clone(); + let mut code_buf: Vec = Vec::new(); + let mut reloc_sink = RelocSink::new(func_index); + let mut trap_sink = binemit::NullTrapSink {}; + context + .compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink) + .map_err(CompileError::Codegen)?; - let address_transform = if generate_debug_info { - let body_len = code_buf.len(); - let at = get_address_transform(&context, isa); + let jt_offsets = context.func.jt_offsets.clone(); - Some(FunctionAddressMap { - instructions: at, - body_offset: 0, - body_len, + let address_transform = if generate_debug_info { + let body_len = code_buf.len(); + let at = get_address_transform(&context, isa); + + Some(FunctionAddressMap { + instructions: at, + body_offset: 0, + body_len, + }) + } else { + None + }; + + Ok(( + code_buf, + jt_offsets, + reloc_sink.func_relocs, + address_transform, + )) }) - } else { - None - }; + .collect::, CompileError>>()? + .into_iter() + .for_each(|(function, func_jt_offsets, relocs, address_transform)| { + functions.push(CodeAndJTOffsets { + body: function, + jt_offsets: func_jt_offsets, + }); + relocations.push(relocs); + if let Some(address_transform) = address_transform { + address_transforms.push(address_transform); + } + }); - Ok(( - code_buf, - jt_offsets, - reloc_sink.func_relocs, - address_transform, - )) - }) - .collect::, CompileError>>()? - .into_iter() - .for_each(|(function, func_jt_offsets, relocs, address_transform)| { - functions.push(CodeAndJTOffsets { - body: function, - jt_offsets: func_jt_offsets, - }); - relocations.push(relocs); - if let Some(address_transform) = address_transform { - address_transforms.push(address_transform); - } - }); + // TODO: Reorganize where we create the Vec for the resolved imports. - // TODO: Reorganize where we create the Vec for the resolved imports. - Ok((Compilation::new(functions), relocations, address_transforms)) + let data = ModuleCacheData::from_tuple(( + Compilation::new(functions), + relocations, + address_transforms, + )); + cache_entry.update_data(&data); + data + } + }; + + Ok(data.to_tuple()) } } diff --git a/wasmtime-environ/src/lib.rs b/wasmtime-environ/src/lib.rs index 1559416378..4dbeca9c62 100644 --- a/wasmtime-environ/src/lib.rs +++ b/wasmtime-environ/src/lib.rs @@ -45,11 +45,14 @@ mod module_environ; mod tunables; mod vmoffsets; +mod cache; + pub mod cranelift; #[cfg(feature = "lightbeam")] pub mod lightbeam; pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; +pub use crate::cache::conf as cache_conf; pub use crate::compilation::{ Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations, }; diff --git a/wasmtime-environ/src/module.rs b/wasmtime-environ/src/module.rs index 0a51fba9ba..3bcdea93ba 100644 --- a/wasmtime-environ/src/module.rs +++ b/wasmtime-environ/src/module.rs @@ -170,6 +170,10 @@ pub struct Module { /// WebAssembly table initializers. pub table_elements: Vec, + + /// Hash of the source wasm code if this module is not synthesized. + /// TODO: this is temporary workaround. Will be replaced with derive macro. + pub hash: Option<[u8; 32]>, } impl Module { @@ -188,6 +192,7 @@ impl Module { exports: IndexMap::new(), start_func: None, table_elements: Vec::new(), + hash: None, } } diff --git a/wasmtime-environ/src/module_environ.rs b/wasmtime-environ/src/module_environ.rs index 405034763d..8122864b68 100644 --- a/wasmtime-environ/src/module_environ.rs +++ b/wasmtime-environ/src/module_environ.rs @@ -10,6 +10,7 @@ use cranelift_wasm::{ self, translate_module, DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, WasmResult, }; +use sha2::{Digest, Sha256}; use std::boxed::Box; use std::string::String; use std::vec::Vec; @@ -79,6 +80,11 @@ impl<'data> ModuleEnvironment<'data> { pub fn translate(mut self, data: &'data [u8]) -> WasmResult> { translate_module(data, &mut self)?; + // TODO: this is temporary workaround and will be replaced with derive macro. + let mut hasher = Sha256::new(); + hasher.input(data); + self.result.module.hash = Some(hasher.result().into()); + Ok(self.result) } } diff --git a/wasmtime-jit/Cargo.toml b/wasmtime-jit/Cargo.toml index a2eeae90fd..3179b83c88 100644 --- a/wasmtime-jit/Cargo.toml +++ b/wasmtime-jit/Cargo.toml @@ -12,10 +12,10 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" -cranelift-frontend = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } +cranelift-frontend = "0.36.0" wasmtime-environ = { path = "../wasmtime-environ", default-features = false } wasmtime-runtime = { path = "../wasmtime-runtime", default-features = false } wasmtime-debug = { path = "../wasmtime-debug", default-features = false } diff --git a/wasmtime-obj/Cargo.toml b/wasmtime-obj/Cargo.toml index bf57631440..60c10a1d54 100644 --- a/wasmtime-obj/Cargo.toml +++ b/wasmtime-obj/Cargo.toml @@ -12,8 +12,8 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } wasmtime-environ = { path = "../wasmtime-environ" } faerie = "0.10.1" diff --git a/wasmtime-runtime/Cargo.toml b/wasmtime-runtime/Cargo.toml index 0595d7471d..1133fc7e4c 100644 --- a/wasmtime-runtime/Cargo.toml +++ b/wasmtime-runtime/Cargo.toml @@ -12,9 +12,9 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } wasmtime-environ = { path = "../wasmtime-environ", default-features = false } region = "2.0.0" lazy_static = "1.2.0" diff --git a/wasmtime-wasi-c/Cargo.toml b/wasmtime-wasi-c/Cargo.toml index dd35986d39..6f3575ffb9 100644 --- a/wasmtime-wasi-c/Cargo.toml +++ b/wasmtime-wasi-c/Cargo.toml @@ -13,9 +13,9 @@ readme = "README.md" wasmtime-runtime = { path = "../wasmtime-runtime" } wasmtime-environ = { path = "../wasmtime-environ" } wasmtime-jit = { path = "../wasmtime-jit" } -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } target-lexicon = "0.4.0" log = { version = "0.4.6", default-features = false } libc = "0.2.50" diff --git a/wasmtime-wasi/Cargo.toml b/wasmtime-wasi/Cargo.toml index 2dbba8d07e..d6f63ce21e 100644 --- a/wasmtime-wasi/Cargo.toml +++ b/wasmtime-wasi/Cargo.toml @@ -14,9 +14,9 @@ wasmtime-runtime = { path = "../wasmtime-runtime" } wasmtime-environ = { path = "../wasmtime-environ" } wasmtime-jit = { path = "../wasmtime-jit" } wasi-common = { git = "https://github.com/CraneStation/wasi-common", rev = "c3994bf57b5d2f1f973b0e4e37bc385695aa4ed2"} -cranelift-codegen = "0.33.0" -cranelift-entity = "0.33.0" -cranelift-wasm = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } target-lexicon = "0.4.0" log = { version = "0.4.6", default-features = false } diff --git a/wasmtime-wast/Cargo.toml b/wasmtime-wast/Cargo.toml index 638a993363..248462f298 100644 --- a/wasmtime-wast/Cargo.toml +++ b/wasmtime-wast/Cargo.toml @@ -12,10 +12,10 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = "0.33.0" -cranelift-native = "0.33.0" -cranelift-wasm = "0.33.0" -cranelift-entity = "0.33.0" +cranelift-codegen = { version = "0.36.0", features = ["enable-serde"] } +cranelift-native = "0.36.0" +cranelift-wasm = { version = "0.36.0", features = ["enable-serde"] } +cranelift-entity = { version = "0.36.0", features = ["enable-serde"] } wasmtime-jit = { path = "../wasmtime-jit" } wasmtime-runtime = { path = "../wasmtime-runtime" } wasmtime-environ = { path = "../wasmtime-environ" }