wasmtime_environ/compile/
module_types.rs

1use crate::{EntityRef, Module, ModuleTypes, TypeConvert};
2use std::{borrow::Cow, collections::HashMap, ops::Index};
3use wasmparser::{UnpackedIndex, Validator, ValidatorId};
4use wasmtime_types::{
5    EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, TypeIndex,
6    WasmCompositeType, WasmFuncType, WasmHeapType, WasmResult, WasmSubType,
7};
8
9/// A type marking the start of a recursion group's definition.
10///
11/// This is initialized by `ModuleTypesBuilder::start_rec_group` and then
12/// finished in `ModuleTypes::end_rec_group` after all of the types in the rec
13/// group have been defined.
14struct RecGroupStart {
15    rec_group_index: ModuleInternedRecGroupIndex,
16    start: ModuleInternedTypeIndex,
17    end: ModuleInternedTypeIndex,
18}
19
20/// A builder for [`ModuleTypes`].
21pub struct ModuleTypesBuilder {
22    /// The ID of the validator that this builder is configured for. Using a
23    /// different validator, or multiple validators, with this builder would
24    /// result in silliness because our `wasmparser::types::*Id`s are only
25    /// unique within the context of a particular validator. Getting this wrong
26    /// could result in generating calls to functions of the wrong type, for
27    /// example. So therefore we always assert that a builder instances is only
28    /// ever paired with a particular validator context.
29    validator_id: ValidatorId,
30
31    /// The canonicalized and deduplicated set of types we are building.
32    types: ModuleTypes,
33
34    /// The set of trampoline-compatible function types we have already added to
35    /// `self.types`. We do this additional level of deduping, on top of what
36    /// `wasmparser` already does, so we can quickly and easily get the
37    /// trampoline type for a given function type if we've already interned one.
38    trampoline_types: HashMap<WasmFuncType, ModuleInternedTypeIndex>,
39
40    /// A map from already-interned `wasmparser` types to their corresponding
41    /// Wasmtime type.
42    wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
43
44    /// The set of recursion groups we have already seen and interned.
45    already_seen: HashMap<wasmparser::types::RecGroupId, ModuleInternedRecGroupIndex>,
46
47    /// If we are in the middle of defining a recursion group, this is the
48    /// metadata about the recursion group we started defining.
49    defining_rec_group: Option<RecGroupStart>,
50}
51
52impl ModuleTypesBuilder {
53    /// Construct a new `ModuleTypesBuilder` using the given validator.
54    pub fn new(validator: &Validator) -> Self {
55        Self {
56            validator_id: validator.id(),
57            types: ModuleTypes::default(),
58            trampoline_types: HashMap::default(),
59            wasmparser_to_wasmtime: HashMap::default(),
60            already_seen: HashMap::default(),
61            defining_rec_group: None,
62        }
63    }
64
65    /// Reserves space for `amt` more type signatures.
66    pub fn reserve_wasm_signatures(&mut self, amt: usize) {
67        self.types.reserve(amt);
68        self.wasmparser_to_wasmtime.reserve(amt);
69        self.already_seen.reserve(amt);
70    }
71
72    /// Get the id of the validator that this builder is configured for.
73    pub fn validator_id(&self) -> ValidatorId {
74        self.validator_id
75    }
76
77    /// Intern a recursion group and all of its types into this builder.
78    ///
79    /// If the recursion group has already been interned, then it is reused.
80    ///
81    /// Panics if given types from a different validator than the one that this
82    /// builder is associated with.
83    pub fn intern_rec_group(
84        &mut self,
85        module: &Module,
86        validator_types: wasmparser::types::TypesRef<'_>,
87        rec_group_id: wasmparser::types::RecGroupId,
88    ) -> WasmResult<ModuleInternedRecGroupIndex> {
89        assert_eq!(validator_types.id(), self.validator_id);
90
91        if let Some(interned) = self.already_seen.get(&rec_group_id) {
92            return Ok(*interned);
93        }
94
95        self.define_new_rec_group(module, validator_types, rec_group_id)
96    }
97
98    /// Define a new recursion group that we haven't already interned.
99    fn define_new_rec_group(
100        &mut self,
101        module: &Module,
102        validator_types: wasmparser::types::TypesRef<'_>,
103        rec_group_id: wasmparser::types::RecGroupId,
104    ) -> WasmResult<ModuleInternedRecGroupIndex> {
105        assert_eq!(validator_types.id(), self.validator_id);
106
107        self.start_rec_group(
108            validator_types,
109            validator_types.rec_group_elements(rec_group_id),
110        );
111
112        for id in validator_types.rec_group_elements(rec_group_id) {
113            let ty = &validator_types[id];
114            let wasm_ty = WasmparserTypeConverter::new(self, module)
115                .with_rec_group(validator_types, rec_group_id)
116                .convert_sub_type(ty);
117            self.wasm_sub_type_in_rec_group(id, wasm_ty);
118        }
119
120        let rec_group_index = self.end_rec_group(rec_group_id);
121
122        // Iterate over all the types we just defined and make sure that every
123        // function type has an associated trampoline type. This needs to happen
124        // *after* we finish defining the rec group because we may need to
125        // intern new function types, which would conflict with the contiguous
126        // range of type indices we pre-reserved for the rec group elements.
127        for ty in self.rec_group_elements(rec_group_index) {
128            if self.types[ty].is_func() {
129                let trampoline = self.intern_trampoline_type(ty);
130                self.types.set_trampoline_type(ty, trampoline);
131            }
132        }
133
134        Ok(rec_group_index)
135    }
136
137    /// Get or create the trampoline function type for the given function
138    /// type. Returns the interned type index of the trampoline function type.
139    fn intern_trampoline_type(
140        &mut self,
141        for_func_ty: ModuleInternedTypeIndex,
142    ) -> ModuleInternedTypeIndex {
143        let trampoline = self.types[for_func_ty].unwrap_func().trampoline_type();
144
145        if let Some(idx) = self.trampoline_types.get(&trampoline) {
146            // We've already interned this trampoline type; reuse it.
147            *idx
148        } else {
149            // We have not already interned this trampoline type.
150            match trampoline {
151                // The trampoline type is the same as the original function
152                // type. We can reuse the definition and its index, but still
153                // need to intern the type into our `trampoline_types` map so we
154                // can reuse it in the future.
155                Cow::Borrowed(f) => {
156                    self.trampoline_types.insert(f.clone(), for_func_ty);
157                    for_func_ty
158                }
159                // The trampoline type is different from the original function
160                // type. Define the trampoline type and then intern it in
161                // `trampoline_types` so we can reuse it in the future.
162                Cow::Owned(f) => {
163                    let idx = self.types.push(WasmSubType {
164                        is_final: true,
165                        supertype: None,
166                        composite_type: WasmCompositeType::Func(f.clone()),
167                    });
168
169                    // The trampoline type is its own trampoline type.
170                    self.types.set_trampoline_type(idx, idx);
171
172                    let next = self.types.next_ty();
173                    self.types.push_rec_group(idx..next);
174                    self.trampoline_types.insert(f, idx);
175                    idx
176                }
177            }
178        }
179    }
180
181    /// Start defining a recursion group.
182    fn start_rec_group(
183        &mut self,
184        validator_types: wasmparser::types::TypesRef<'_>,
185        elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
186    ) {
187        log::trace!("Starting rec group of length {}", elems.len());
188
189        assert!(self.defining_rec_group.is_none());
190        assert_eq!(validator_types.id(), self.validator_id);
191
192        // Eagerly define the reverse map's entries for this rec group's types
193        // so that we can use them when converting `wasmparser` types to our
194        // types.
195        let len = elems.len();
196        for (i, wasmparser_id) in elems.enumerate() {
197            let interned = ModuleInternedTypeIndex::new(self.types.len_types() + i);
198            log::trace!(
199                "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
200                validator_types[wasmparser_id]
201            );
202
203            let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
204            debug_assert_eq!(
205                old_entry, None,
206                "should not have already inserted {wasmparser_id:?}"
207            );
208        }
209
210        self.defining_rec_group = Some(RecGroupStart {
211            rec_group_index: self.types.next_rec_group(),
212            start: self.types.next_ty(),
213            end: ModuleInternedTypeIndex::new(self.types.len_types() + len),
214        });
215    }
216
217    /// Finish defining a recursion group.
218    fn end_rec_group(
219        &mut self,
220        rec_group_id: wasmparser::types::RecGroupId,
221    ) -> ModuleInternedRecGroupIndex {
222        let RecGroupStart {
223            rec_group_index,
224            start,
225            end,
226        } = self
227            .defining_rec_group
228            .take()
229            .expect("should be defining a rec group");
230
231        log::trace!("Ending rec group {start:?}..{end:?}");
232
233        debug_assert!(start.index() < self.types.len_types());
234        debug_assert_eq!(
235            end,
236            self.types.next_ty(),
237            "should have defined the number of types declared in `start_rec_group`"
238        );
239
240        let idx = self.types.push_rec_group(start..end);
241        debug_assert_eq!(idx, rec_group_index);
242
243        self.already_seen.insert(rec_group_id, rec_group_index);
244        rec_group_index
245    }
246
247    /// Intern a type into this builder and get its Wasmtime index.
248    ///
249    /// This will intern not only the single given type, but the type's entire
250    /// rec group. This helper method is provided as a convenience so that
251    /// callers don't have to get the type's rec group, intern the rec group,
252    /// and then look up the Wasmtime index for the original type themselves.
253    pub fn intern_type(
254        &mut self,
255        module: &Module,
256        validator_types: wasmparser::types::TypesRef<'_>,
257        id: wasmparser::types::CoreTypeId,
258    ) -> WasmResult<ModuleInternedTypeIndex> {
259        assert!(self.defining_rec_group.is_none());
260        assert_eq!(validator_types.id(), self.validator_id);
261
262        let rec_group_id = validator_types.rec_group_id_of(id);
263        debug_assert!(validator_types
264            .rec_group_elements(rec_group_id)
265            .any(|e| e == id));
266
267        let interned_rec_group = self.intern_rec_group(module, validator_types, rec_group_id)?;
268
269        let interned_type = self.wasmparser_to_wasmtime[&id];
270        debug_assert!(self
271            .rec_group_elements(interned_rec_group)
272            .any(|e| e == interned_type));
273
274        Ok(interned_type)
275    }
276
277    /// Define a new Wasm type while we are defining a rec group.
278    fn wasm_sub_type_in_rec_group(
279        &mut self,
280        id: wasmparser::types::CoreTypeId,
281        ty: WasmSubType,
282    ) -> ModuleInternedTypeIndex {
283        assert!(
284            self.defining_rec_group.is_some(),
285            "must be defining a rec group to define new types"
286        );
287
288        let module_interned_index = self.types.push(ty);
289        debug_assert_eq!(
290            self.wasmparser_to_wasmtime.get(&id),
291            Some(&module_interned_index),
292            "should have reserved the right module-interned index for this wasmparser type already"
293        );
294
295        module_interned_index
296    }
297
298    /// Returns the result [`ModuleTypes`] of this builder.
299    pub fn finish(self) -> ModuleTypes {
300        self.types
301    }
302
303    /// Get the elements within an already-defined rec group.
304    pub fn rec_group_elements(
305        &self,
306        rec_group: ModuleInternedRecGroupIndex,
307    ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> {
308        self.types.rec_group_elements(rec_group)
309    }
310
311    /// Returns an iterator over all the unique wasm types defined thus far
312    /// within this builder.
313    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
314        self.types.wasm_types()
315    }
316
317    /// Get an iterator over all function types and their associated trampoline
318    /// type.
319    pub fn trampoline_types(
320        &self,
321    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
322        self.types.trampoline_types()
323    }
324
325    /// Get the associated trampoline type for the given function type.
326    pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
327        self.types.trampoline_type(ty)
328    }
329}
330
331// Forward the indexing impl to the internal `ModuleTypes`
332impl<T> Index<T> for ModuleTypesBuilder
333where
334    ModuleTypes: Index<T>,
335{
336    type Output = <ModuleTypes as Index<T>>::Output;
337
338    fn index(&self, sig: T) -> &Self::Output {
339        &self.types[sig]
340    }
341}
342
343/// A convert from `wasmparser` types to Wasmtime types.
344pub struct WasmparserTypeConverter<'a> {
345    types: &'a ModuleTypesBuilder,
346    module: &'a Module,
347    rec_group_context: Option<(
348        wasmparser::types::TypesRef<'a>,
349        wasmparser::types::RecGroupId,
350    )>,
351}
352
353impl<'a> WasmparserTypeConverter<'a> {
354    /// Construct a new type converter from `wasmparser` types to Wasmtime types.
355    pub fn new(types: &'a ModuleTypesBuilder, module: &'a Module) -> Self {
356        Self {
357            types,
358            module,
359            rec_group_context: None,
360        }
361    }
362
363    /// Configure this converter to be within the context of defining the
364    /// current rec group.
365    pub fn with_rec_group(
366        &mut self,
367        wasmparser_types: wasmparser::types::TypesRef<'a>,
368        rec_group: wasmparser::types::RecGroupId,
369    ) -> &Self {
370        self.rec_group_context = Some((wasmparser_types, rec_group));
371        self
372    }
373}
374
375impl TypeConvert for WasmparserTypeConverter<'_> {
376    fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
377        match index {
378            UnpackedIndex::Id(id) => {
379                let interned = self.types.wasmparser_to_wasmtime[&id];
380                let index = EngineOrModuleTypeIndex::Module(interned);
381
382                // If this is a forward reference to a type in this type's rec
383                // group that we haven't converted yet, then we won't have an
384                // entry in `wasm_types` yet. In this case, fallback to a
385                // different means of determining whether this is a concrete
386                // array vs struct vs func reference. In this case, we can use
387                // the validator's type context.
388                if let Some(ty) = self.types.types.get(interned) {
389                    match &ty.composite_type {
390                        WasmCompositeType::Array(_) => WasmHeapType::ConcreteArray(index),
391                        WasmCompositeType::Func(_) => WasmHeapType::ConcreteFunc(index),
392                        WasmCompositeType::Struct(_) => WasmHeapType::ConcreteStruct(index),
393                    }
394                } else if let Some((wasmparser_types, _)) = self.rec_group_context.as_ref() {
395                    let wasmparser_ty = &wasmparser_types[id].composite_type;
396                    assert!(!wasmparser_ty.shared);
397                    match &wasmparser_ty.inner {
398                        wasmparser::CompositeInnerType::Array(_) => {
399                            WasmHeapType::ConcreteArray(index)
400                        }
401                        wasmparser::CompositeInnerType::Func(_) => {
402                            WasmHeapType::ConcreteFunc(index)
403                        }
404                        wasmparser::CompositeInnerType::Struct(_) => {
405                            WasmHeapType::ConcreteStruct(index)
406                        }
407                    }
408                } else {
409                    panic!("forward reference to type outside of rec group?")
410                }
411            }
412
413            UnpackedIndex::Module(module_index) => {
414                let module_index = TypeIndex::from_u32(module_index);
415                let interned = self.module.types[module_index];
416                let index = EngineOrModuleTypeIndex::Module(interned);
417
418                // See comment above about `wasm_types` maybe not having the
419                // converted sub type yet. However in this case we don't have a
420                // `wasmparser::types::CoreTypeId` on hand, so we have to
421                // indirectly get one by looking it up inside the current rec
422                // group.
423                if let Some(ty) = self.types.types.get(interned) {
424                    match &ty.composite_type {
425                        WasmCompositeType::Array(_) => WasmHeapType::ConcreteArray(index),
426                        WasmCompositeType::Func(_) => WasmHeapType::ConcreteFunc(index),
427                        WasmCompositeType::Struct(_) => WasmHeapType::ConcreteStruct(index),
428                    }
429                } else if let Some((parser_types, rec_group)) = self.rec_group_context.as_ref() {
430                    let rec_group_index = interned.index() - self.types.types.len_types();
431                    let id = parser_types
432                        .rec_group_elements(*rec_group)
433                        .nth(rec_group_index)
434                        .unwrap();
435                    let wasmparser_ty = &parser_types[id].composite_type;
436                    assert!(!wasmparser_ty.shared);
437                    match &wasmparser_ty.inner {
438                        wasmparser::CompositeInnerType::Array(_) => {
439                            WasmHeapType::ConcreteArray(index)
440                        }
441                        wasmparser::CompositeInnerType::Func(_) => {
442                            WasmHeapType::ConcreteFunc(index)
443                        }
444                        wasmparser::CompositeInnerType::Struct(_) => {
445                            WasmHeapType::ConcreteStruct(index)
446                        }
447                    }
448                } else {
449                    panic!("forward reference to type outside of rec group?")
450                }
451            }
452
453            UnpackedIndex::RecGroup(_) => unreachable!(),
454        }
455    }
456
457    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
458        match index {
459            UnpackedIndex::Id(id) => {
460                let interned = self.types.wasmparser_to_wasmtime[&id];
461                EngineOrModuleTypeIndex::Module(interned)
462            }
463            UnpackedIndex::Module(module_index) => {
464                let module_index = TypeIndex::from_u32(module_index);
465                let interned = self.module.types[module_index];
466                EngineOrModuleTypeIndex::Module(interned)
467            }
468            UnpackedIndex::RecGroup(_) => unreachable!(),
469        }
470    }
471}