1 use crate::compile::HashedEngineCompileEnv; 2 #[cfg(feature = "component-model")] 3 use crate::component::Component; 4 use crate::prelude::*; 5 use crate::runtime::vm::MmapVec; 6 use crate::{CodeBuilder, CodeMemory, Engine, Module}; 7 use object::write::WritableBuffer; 8 use std::sync::Arc; 9 use wasmtime_environ::{FinishedObject, ObjectBuilder, ObjectKind}; 10 11 impl<'a> CodeBuilder<'a> { 12 fn compile_cached<T>( 13 &self, 14 build_artifacts: fn(&Engine, &[u8], Option<&[u8]>) -> Result<(MmapVecWrapper, Option<T>)>, 15 ) -> Result<(Arc<CodeMemory>, Option<T>)> { 16 let wasm = self.wasm_binary()?; 17 let dwarf_package = self.dwarf_package_binary(); 18 19 self.engine 20 .check_compatible_with_native_host() 21 .context("compilation settings are not compatible with the native host")?; 22 23 #[cfg(feature = "cache")] 24 { 25 let state = ( 26 HashedEngineCompileEnv(self.engine), 27 &wasm, 28 &dwarf_package, 29 // Don't hash this as it's just its own "pure" function pointer. 30 NotHashed(build_artifacts), 31 ); 32 let (code, info_and_types) = 33 wasmtime_cache::ModuleCacheEntry::new("wasmtime", self.engine.cache_config()) 34 .get_data_raw( 35 &state, 36 // Cache miss, compute the actual artifacts 37 |(engine, wasm, dwarf_package, build_artifacts)| -> Result<_> { 38 let (mmap, info) = 39 (build_artifacts.0)(engine.0, wasm, dwarf_package.as_deref())?; 40 let code = publish_mmap(mmap.0)?; 41 Ok((code, info)) 42 }, 43 // Implementation of how to serialize artifacts 44 |(_engine, _wasm, _, _), (code, _info_and_types)| { 45 Some(code.mmap().to_vec()) 46 }, 47 // Cache hit, deserialize the provided artifacts 48 |(engine, wasm, _, _), serialized_bytes| { 49 let kind = if wasmparser::Parser::is_component(&wasm) { 50 ObjectKind::Component 51 } else { 52 ObjectKind::Module 53 }; 54 let code = engine.0.load_code_bytes(&serialized_bytes, kind).ok()?; 55 Some((code, None)) 56 }, 57 )?; 58 return Ok((code, info_and_types)); 59 } 60 61 #[cfg(not(feature = "cache"))] 62 { 63 let (mmap, info_and_types) = 64 build_artifacts(self.engine, &wasm, dwarf_package.as_deref())?; 65 let code = publish_mmap(mmap.0)?; 66 return Ok((code, info_and_types)); 67 } 68 69 struct NotHashed<T>(T); 70 71 impl<T> std::hash::Hash for NotHashed<T> { 72 fn hash<H: std::hash::Hasher>(&self, _hasher: &mut H) {} 73 } 74 } 75 76 /// Same as [`CodeBuilder::compile_module_serialized`] except that a 77 /// [`Module`](crate::Module) is produced instead. 78 /// 79 /// Note that this method will cache compilations if the `cache` feature is 80 /// enabled and turned on in [`Config`](crate::Config). 81 pub fn compile_module(&self) -> Result<Module> { 82 let (code, info_and_types) = self.compile_cached(super::build_artifacts)?; 83 Module::from_parts(self.engine, code, info_and_types) 84 } 85 86 /// Same as [`CodeBuilder::compile_module`] except that it compiles a 87 /// [`Component`] instead of a module. 88 #[cfg(feature = "component-model")] 89 pub fn compile_component(&self) -> Result<Component> { 90 let (code, artifacts) = self.compile_cached(super::build_component_artifacts)?; 91 Component::from_parts(self.engine, code, artifacts) 92 } 93 } 94 95 fn publish_mmap(mmap: MmapVec) -> Result<Arc<CodeMemory>> { 96 let mut code = CodeMemory::new(mmap)?; 97 code.publish()?; 98 Ok(Arc::new(code)) 99 } 100 101 pub(crate) struct MmapVecWrapper(pub MmapVec); 102 103 impl FinishedObject for MmapVecWrapper { 104 fn finish_object(obj: ObjectBuilder<'_>) -> Result<Self> { 105 let mut result = ObjectMmap::default(); 106 return match obj.finish(&mut result) { 107 Ok(()) => { 108 assert!(result.mmap.is_some(), "no reserve"); 109 let mmap = result.mmap.expect("reserve not called"); 110 assert_eq!(mmap.len(), result.len); 111 Ok(MmapVecWrapper(mmap)) 112 } 113 Err(e) => match result.err.take() { 114 Some(original) => Err(original.context(e)), 115 None => Err(e.into()), 116 }, 117 }; 118 119 /// Helper struct to implement the `WritableBuffer` trait from the `object` 120 /// crate. 121 /// 122 /// This enables writing an object directly into an mmap'd memory so it's 123 /// immediately usable for execution after compilation. This implementation 124 /// relies on a call to `reserve` happening once up front with all the needed 125 /// data, and the mmap internally does not attempt to grow afterwards. 126 #[derive(Default)] 127 struct ObjectMmap { 128 mmap: Option<MmapVec>, 129 len: usize, 130 err: Option<Error>, 131 } 132 133 impl WritableBuffer for ObjectMmap { 134 fn len(&self) -> usize { 135 self.len 136 } 137 138 fn reserve(&mut self, additional: usize) -> Result<(), ()> { 139 assert!(self.mmap.is_none(), "cannot reserve twice"); 140 self.mmap = match MmapVec::with_capacity(additional) { 141 Ok(mmap) => Some(mmap), 142 Err(e) => { 143 self.err = Some(e); 144 return Err(()); 145 } 146 }; 147 Ok(()) 148 } 149 150 fn resize(&mut self, new_len: usize) { 151 // Resizing always appends 0 bytes and since new mmaps start out as 0 152 // bytes we don't actually need to do anything as part of this other 153 // than update our own length. 154 if new_len <= self.len { 155 return; 156 } 157 self.len = new_len; 158 } 159 160 fn write_bytes(&mut self, val: &[u8]) { 161 let mmap = self.mmap.as_mut().expect("write before reserve"); 162 mmap[self.len..][..val.len()].copy_from_slice(val); 163 self.len += val.len(); 164 } 165 } 166 } 167 } 168