wasmtime/compile/
runtime.rs

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