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