From 8fadc5f5d98fa3afcfba8ae2ba8180b248a75879 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Fri, 19 Jul 2019 16:25:14 -0700 Subject: [PATCH 1/9] Simple module compilation cache --- Cargo.toml | 7 +- fuzz/Cargo.toml | 6 +- src/wasm2obj.rs | 4 +- src/wasmtime.rs | 4 +- src/wast.rs | 4 +- wasmtime-debug/Cargo.toml | 6 +- wasmtime-environ/Cargo.toml | 13 +- wasmtime-environ/src/address_map.rs | 5 +- wasmtime-environ/src/cache.rs | 274 +++++++++++++++++++++++++ wasmtime-environ/src/compilation.rs | 7 +- wasmtime-environ/src/cranelift.rs | 150 ++++++++------ wasmtime-environ/src/lib.rs | 2 + wasmtime-environ/src/module.rs | 5 + wasmtime-environ/src/module_environ.rs | 6 + wasmtime-jit/Cargo.toml | 8 +- wasmtime-obj/Cargo.toml | 6 +- wasmtime-runtime/Cargo.toml | 6 +- wasmtime-wasi-c/Cargo.toml | 6 +- wasmtime-wasi/Cargo.toml | 6 +- wasmtime-wast/Cargo.toml | 8 +- 20 files changed, 420 insertions(+), 113 deletions(-) create mode 100644 wasmtime-environ/src/cache.rs diff --git a/Cargo.toml b/Cargo.toml index 9161e567d90d..d0f63914b036 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" } 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 1dc445f16c3a..48d1e4b0dc43 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 4edb94982e17..ae101dcc0af1 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; diff --git a/src/wasmtime.rs b/src/wasmtime.rs index d949cba1adb0..136e3c77cb96 100644 --- a/src/wasmtime.rs +++ b/src/wasmtime.rs @@ -30,15 +30,13 @@ ) )] -#[macro_use] -extern crate serde_derive; - use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use cranelift_native; use docopt::Docopt; use file_per_thread_logger; use pretty_env_logger; +use serde::Deserialize; use std::error::Error; use std::ffi::OsStr; use std::fs::File; diff --git a/src/wast.rs b/src/wast.rs index 7e87bc189fad..9c1d1f42bf80 100644 --- a/src/wast.rs +++ b/src/wast.rs @@ -25,15 +25,13 @@ ) )] -#[macro_use] -extern crate serde_derive; - use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use cranelift_native; use docopt::Docopt; use file_per_thread_logger; use pretty_env_logger; +use serde::Deserialize; use std::path::Path; use std::process; use wasmtime_jit::Compiler; diff --git a/wasmtime-debug/Cargo.toml b/wasmtime-debug/Cargo.toml index a5521c8e94c3..a7f82a867a2c 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.32.1" } -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 ba7347aaaf05..d91a12e81f6f 100644 --- a/wasmtime-environ/Cargo.toml +++ b/wasmtime-environ/Cargo.toml @@ -12,14 +12,21 @@ 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.0" +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" +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 7ce2c28a2665..5295c9d0ad28 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 000000000000..e0ec9085cd8e --- /dev/null +++ b/wasmtime-environ/src/cache.rs @@ -0,0 +1,274 @@ +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::{debug, warn}; +use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; +use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer}; +use std::fmt; +use std::fs; +use std::path::PathBuf; + +lazy_static! { + static ref CACHE_DIR: Option = + match ProjectDirs::from("org", "CraneStation", "wasmtime") { + Some(proj_dirs) => { + let cache_dir = proj_dirs.cache_dir(); + 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 & build's uuid, and flag if debug symbols are available + let option_hash = module.hash; + + let mod_cache_path = CACHE_DIR + .clone() + .and_then(|p| option_hash.map(|hash| p.join(format!("mod-{}", base64::encode(&hash))))); + + 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) => { + debug!("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) => { + debug!("Failed to serialize cached code: {}", err); + return; + } + }; + match fs::write(p, &cache_buf) { + Ok(()) => (), + Err(err) => warn!("Failed to write cached code to disk: {}", 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 c699cbc98445..1ceb67c015a9 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 9f12dbd0c255..508a50bad7e3 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()); - - 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 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 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 jt_offsets = context.func.jt_offsets.clone(); - - 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, + let cache_entry = ModuleCacheEntry::new(module, isa, generate_debug_info); + + 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()); + + 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 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 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 jt_offsets = context.func.jt_offsets.clone(); + + 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 - }; - - 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); - } - }); + .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. + + let data = ModuleCacheData::from_tuple(( + Compilation::new(functions), + relocations, + address_transforms, + )); + cache_entry.update_data(&data); + data + } + }; - // TODO: Reorganize where we create the Vec for the resolved imports. - Ok((Compilation::new(functions), relocations, address_transforms)) + Ok(data.to_tuple()) } } diff --git a/wasmtime-environ/src/lib.rs b/wasmtime-environ/src/lib.rs index 1559416378ae..253e678de6f5 100644 --- a/wasmtime-environ/src/lib.rs +++ b/wasmtime-environ/src/lib.rs @@ -45,6 +45,8 @@ mod module_environ; mod tunables; mod vmoffsets; +mod cache; + pub mod cranelift; #[cfg(feature = "lightbeam")] pub mod lightbeam; diff --git a/wasmtime-environ/src/module.rs b/wasmtime-environ/src/module.rs index 0a51fba9baa8..3bcdea93ba05 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 405034763d04..8122864b688f 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 60b9fbec3693..b662d67866b7 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 bf5763144088..60c10a1d543e 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 688819c8a63f..af730b5f40b8 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 cfe14d89b534..9e5604007ebd 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 80d52a45a2dd..de9a26b237cc 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" } -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 638a99336306..248462f298e3 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" } From 7a96fa2c5a3f80ec4c8332a0b8ec652b8d8f09ee Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Mon, 22 Jul 2019 10:34:05 -0700 Subject: [PATCH 2/9] Fix base64 encoding bug --- wasmtime-environ/src/cache.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index e0ec9085cd8e..2d06720e2b0d 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -48,9 +48,14 @@ impl ModuleCacheEntry { // TODO: cache directory hierarchy with isa name, compiler name & build's uuid, and flag if debug symbols are available let option_hash = module.hash; - let mod_cache_path = CACHE_DIR - .clone() - .and_then(|p| option_hash.map(|hash| p.join(format!("mod-{}", base64::encode(&hash))))); + let mod_cache_path = CACHE_DIR.clone().and_then(|p| { + option_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 + )) + }) + }); ModuleCacheEntry { mod_cache_path } } @@ -83,7 +88,11 @@ impl ModuleCacheEntry { }; match fs::write(p, &cache_buf) { Ok(()) => (), - Err(err) => warn!("Failed to write cached code to disk: {}", err), + Err(err) => warn!( + "Failed to write cached code to disk, path: {}, message: {}", + p.display(), + err + ), } } } From 0ea737bf637dfc6a95d3d279a0cf7fdda818b329 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Mon, 22 Jul 2019 14:12:28 -0700 Subject: [PATCH 3/9] Use warn! everywhere in cache system --- wasmtime-environ/src/cache.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 2d06720e2b0d..159d3eb3cbd6 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -45,7 +45,7 @@ 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 & build's uuid, and flag if debug symbols are available + // TODO: cache directory hierarchy with isa name, compiler name & git revision, and files with flag if debug symbols are available let option_hash = module.hash; let mod_cache_path = CACHE_DIR.clone().and_then(|p| { @@ -66,7 +66,7 @@ impl ModuleCacheEntry { Ok(cache_bytes) => match bincode::deserialize(&cache_bytes[..]) { Ok(data) => Some(data), Err(err) => { - debug!("Failed to deserialize cached code: {}", err); + warn!("Failed to deserialize cached code: {}", err); None } }, @@ -82,7 +82,7 @@ impl ModuleCacheEntry { let cache_buf = match bincode::serialize(&data) { Ok(data) => data, Err(err) => { - debug!("Failed to serialize cached code: {}", err); + warn!("Failed to serialize cached code: {}", err); return; } }; From 0a6ff884958b35a4aaad867ee12da3b887fb74e8 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Mon, 22 Jul 2019 14:16:07 -0700 Subject: [PATCH 4/9] Remove unused import --- wasmtime-environ/src/cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 159d3eb3cbd6..d232bdad7457 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -5,7 +5,7 @@ use cranelift_codegen::ir; use cranelift_codegen::isa; use directories::ProjectDirs; use lazy_static::lazy_static; -use log::{debug, warn}; +use log::warn; use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer}; use std::fmt; From 66520d2b3f3bc8a5f54cb9976b72a5d09b8bba09 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Mon, 22 Jul 2019 14:55:15 -0700 Subject: [PATCH 5/9] Temporary workaround for long path on Windows --- wasmtime-environ/src/cache.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index d232bdad7457..136d143c54da 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -8,15 +8,30 @@ 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::path::PathBuf; +use std::path::{Path, PathBuf}; lazy_static! { static ref CACHE_DIR: Option = match ProjectDirs::from("org", "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), From b35a71534ab22fb75e6347887afee6daece7426b Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Tue, 23 Jul 2019 11:40:37 -0700 Subject: [PATCH 6/9] Remove unused import for non-windows builds --- wasmtime-environ/src/cache.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 136d143c54da..30a917b60129 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -12,7 +12,9 @@ use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer}; use std::ffi::OsString; use std::fmt; use std::fs; -use std::path::{Path, PathBuf}; +#[cfg(windows)] +use std::path::Path; +use std::path::PathBuf; lazy_static! { static ref CACHE_DIR: Option = From c7800da8dd031f9f01a54956c77d054597ac33d5 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Tue, 23 Jul 2019 15:09:28 -0700 Subject: [PATCH 7/9] Cache directory hierarchy --- wasmtime-environ/build.rs | 9 +++ wasmtime-environ/src/cache.rs | 118 ++++++++++++++++++++++-------- wasmtime-environ/src/cranelift.rs | 2 +- 3 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 wasmtime-environ/build.rs diff --git a/wasmtime-environ/build.rs b/wasmtime-environ/build.rs new file mode 100644 index 000000000000..21082e31bc40 --- /dev/null +++ b/wasmtime-environ/build.rs @@ -0,0 +1,9 @@ +use std::process::Command; + +fn main() { + let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() { + Ok(output) => String::from_utf8(output.stdout).unwrap(), + Err(_) => String::from("git-not-found"), + }; + println!("cargo:rustc-env=GIT_REV={}", git_rev); +} diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 30a917b60129..8d137dc09039 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -5,16 +5,16 @@ use cranelift_codegen::ir; use cranelift_codegen::isa; use directories::ProjectDirs; use lazy_static::lazy_static; -use log::warn; +use log::{debug, 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; -#[cfg(windows)] -use std::path::Path; use std::path::PathBuf; +#[cfg(debug)] +use std::string::String; lazy_static! { static ref CACHE_DIR: Option = @@ -22,31 +22,52 @@ lazy_static! { 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) + #[cfg(windows)] { + let mut long_path = OsString::new(); + if !cache_dir.starts_with("\\\\?\\") { + long_path.push("\\\\?\\"); } - }; - match fs::create_dir_all(cache_dir) { - Ok(()) => (), - Err(err) => warn!("Unable to create cache directory, failed with: {}", err), - }; + long_path.push(cache_dir.as_os_str()); + Some(PathBuf::from(long_path)) + } + #[cfg(not(windows))] Some(cache_dir.to_path_buf()) } None => { - warn!("Unable to find cache directory"); + warn!("Failed to find proper cache directory location."); None } }; } +#[cfg(debug)] +lazy_static! { + static ref SELF_MTIME: String = { + match std::env::current_exe() { + Ok(path) => match fs::metadata(&path) { + Ok(metadata) => match metadata.modified() { + Ok(mtime) => match mtime.duration_since(std::time::UNIX_EPOCH) { + Ok(duration) => format!("{}", duration.as_millis()), + Err(err) => format!("m{}", err.duration().as_millis()), + }, + Err(_) => { + warn!("Failed to get modification time of current executable"); + "no-mtime".to_string() + } + }, + Err(_) => { + warn!("Failed to get metadata of current executable"); + "no-mtime".to_string() + } + }, + Err(_) => { + warn!("Failed to get path of current executable"); + "no-mtime".to_string() + } + } + }; +} + pub struct ModuleCacheEntry { mod_cache_path: Option, } @@ -61,15 +82,33 @@ pub struct ModuleCacheData { 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 + pub fn new( + module: &Module, + isa: &dyn isa::TargetIsa, + compiler_name: &str, + generate_debug_info: bool, + ) -> Self { let option_hash = module.hash; let mod_cache_path = CACHE_DIR.clone().and_then(|p| { option_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 + #[cfg(debug)] + let compiler_dir = format!( + "{comp_name}-{comp_ver}-{comp_mtime}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + comp_mtime = SELF_MTIME, + ); + #[cfg(not(debug))] + let compiler_dir = format!( + "{comp_name}-{comp_ver}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + ); + p.join(isa.name()).join(compiler_dir).join(format!( + "mod-{mod_hash}{mod_dbg}", + mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename + mod_dbg = if generate_debug_info { ".d" } else { "" }, )) }) }); @@ -103,13 +142,34 @@ impl ModuleCacheEntry { return; } }; + // Optimize syscalls: first, try writing to disk. It should succeed in most cases. + // Otherwise, try creating the cache directory and retry writing to the file. match fs::write(p, &cache_buf) { Ok(()) => (), - Err(err) => warn!( - "Failed to write cached code to disk, path: {}, message: {}", - p.display(), - err - ), + Err(err) => { + debug!( + "Attempting to create the cache directory, because \ + failed to write cached code to disk, path: {}, message: {}", + p.display(), + err, + ); + let cache_dir = p.parent().unwrap(); + match fs::create_dir_all(cache_dir) { + Ok(()) => match fs::write(p, &cache_buf) { + Ok(()) => (), + Err(err) => warn!( + "Failed to write cached code to disk, path: {}, message: {}", + p.display(), + err, + ), + }, + Err(err) => warn!( + "Failed to create cache directory, path: {}, message: {}", + cache_dir.display(), + err + ), + } + } } } } diff --git a/wasmtime-environ/src/cranelift.rs b/wasmtime-environ/src/cranelift.rs index 508a50bad7e3..83e1734f85d6 100644 --- a/wasmtime-environ/src/cranelift.rs +++ b/wasmtime-environ/src/cranelift.rs @@ -124,7 +124,7 @@ impl crate::compilation::Compiler for Cranelift { isa: &dyn isa::TargetIsa, generate_debug_info: bool, ) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError> { - let cache_entry = ModuleCacheEntry::new(module, isa, generate_debug_info); + let cache_entry = ModuleCacheEntry::new(module, isa, "cranelift", generate_debug_info); let data = match cache_entry.get_data() { Some(data) => data, From a74b56e9c209ee269d26d4ebb3c995f5bad7643e Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Tue, 23 Jul 2019 15:52:23 -0700 Subject: [PATCH 8/9] Fix conditional compilation for debug mode --- wasmtime-environ/src/cache.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 8d137dc09039..ac6262c7242c 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -13,8 +13,8 @@ use std::ffi::OsString; use std::fmt; use std::fs; use std::path::PathBuf; -#[cfg(debug)] -use std::string::String; +#[cfg(debug_assertions)] +use std::string::{String, ToString}; lazy_static! { static ref CACHE_DIR: Option = @@ -40,7 +40,7 @@ lazy_static! { }; } -#[cfg(debug)] +#[cfg(debug_assertions)] lazy_static! { static ref SELF_MTIME: String = { match std::env::current_exe() { @@ -92,14 +92,14 @@ impl ModuleCacheEntry { let mod_cache_path = CACHE_DIR.clone().and_then(|p| { option_hash.map(|hash| { - #[cfg(debug)] + #[cfg(debug_assertions)] let compiler_dir = format!( "{comp_name}-{comp_ver}-{comp_mtime}", comp_name = compiler_name, comp_ver = env!("GIT_REV"), - comp_mtime = SELF_MTIME, + comp_mtime = *SELF_MTIME, ); - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] let compiler_dir = format!( "{comp_name}-{comp_ver}", comp_name = compiler_name, From 4741d71dd0a2554f1263a67aa7b890e124d8d139 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Tue, 23 Jul 2019 16:05:42 -0700 Subject: [PATCH 9/9] Minor enhancements --- wasmtime-environ/src/cache.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index ac6262c7242c..328e4e213a74 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -22,7 +22,7 @@ lazy_static! { Some(proj_dirs) => { let cache_dir = proj_dirs.cache_dir(); // Temporary workaround for: https://github.com/rust-lang/rust/issues/32689 - #[cfg(windows)] { + if cfg!(windows) { let mut long_path = OsString::new(); if !cache_dir.starts_with("\\\\?\\") { long_path.push("\\\\?\\"); @@ -30,8 +30,9 @@ lazy_static! { long_path.push(cache_dir.as_os_str()); Some(PathBuf::from(long_path)) } - #[cfg(not(windows))] - Some(cache_dir.to_path_buf()) + else { + Some(cache_dir.to_path_buf()) + } } None => { warn!("Failed to find proper cache directory location."); @@ -92,19 +93,20 @@ impl ModuleCacheEntry { let mod_cache_path = CACHE_DIR.clone().and_then(|p| { option_hash.map(|hash| { - #[cfg(debug_assertions)] - let compiler_dir = format!( - "{comp_name}-{comp_ver}-{comp_mtime}", - comp_name = compiler_name, - comp_ver = env!("GIT_REV"), - comp_mtime = *SELF_MTIME, - ); - #[cfg(not(debug_assertions))] - let compiler_dir = format!( - "{comp_name}-{comp_ver}", - comp_name = compiler_name, - comp_ver = env!("GIT_REV"), - ); + let compiler_dir = if cfg!(debug_assertions) { + format!( + "{comp_name}-{comp_ver}-{comp_mtime}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + comp_mtime = *SELF_MTIME, + ) + } else { + format!( + "{comp_name}-{comp_ver}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + ) + }; p.join(isa.name()).join(compiler_dir).join(format!( "mod-{mod_hash}{mod_dbg}", mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename