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