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