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            &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_config())
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
122fn 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
128pub(crate) struct MmapVecWrapper(pub MmapVec);
129
130/// Custom alignment requirements from the Engine for
131/// produced-at-runtime-in-memory code artifacts.
132pub(crate) struct CustomAlignment {
133    alignment: usize,
134}
135
136impl 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}