wasmtime/compile/
runtime.rs

1#[cfg(feature = "component-model")]
2use crate::component::Component;
3use crate::prelude::*;
4use crate::runtime::vm::MmapVec;
5use crate::{CodeBuilder, CodeMemory, Engine, Module};
6use object::write::WritableBuffer;
7use std::sync::Arc;
8use wasmtime_environ::{FinishedObject, ObjectBuilder};
9
10impl<'a> CodeBuilder<'a> {
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).
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")]
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
154    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
165fn 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
171pub(crate) struct MmapVecWrapper(pub MmapVec);
172
173/// Custom alignment requirements from the Engine for
174/// produced-at-runtime-in-memory code artifacts.
175pub(crate) struct CustomAlignment {
176    alignment: usize,
177}
178
179impl FinishedObject for MmapVecWrapper {
180    type State = CustomAlignment;
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}