wasmtime_environ/compile/
module_types.rs

1use crate::{
2    EngineOrModuleTypeIndex, EntityRef, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex,
3    ModuleTypes, TypeConvert, TypeIndex, WasmArrayType, WasmCompositeInnerType, WasmCompositeType,
4    WasmFuncType, WasmHeapType, WasmResult, WasmStructType, WasmSubType, wasm_unsupported,
5};
6use std::{borrow::Cow, collections::HashMap, ops::Index};
7use wasmparser::{UnpackedIndex, Validator, ValidatorId};
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        validator_types: wasmparser::types::TypesRef<'_>,
86        rec_group_id: wasmparser::types::RecGroupId,
87    ) -> WasmResult<ModuleInternedRecGroupIndex> {
88        assert_eq!(validator_types.id(), self.validator_id);
89
90        if let Some(interned) = self.already_seen.get(&rec_group_id) {
91            return Ok(*interned);
92        }
93
94        self.define_new_rec_group(validator_types, rec_group_id)
95    }
96
97    /// Define a new recursion group that we haven't already interned.
98    fn define_new_rec_group(
99        &mut self,
100        validator_types: wasmparser::types::TypesRef<'_>,
101        rec_group_id: wasmparser::types::RecGroupId,
102    ) -> WasmResult<ModuleInternedRecGroupIndex> {
103        assert_eq!(validator_types.id(), self.validator_id);
104
105        self.start_rec_group(
106            validator_types,
107            validator_types.rec_group_elements(rec_group_id),
108        );
109
110        for id in validator_types.rec_group_elements(rec_group_id) {
111            let ty = &validator_types[id];
112            let wasm_ty = WasmparserTypeConverter::new(self, |_| {
113                unreachable!("no need to lookup indexes; we already have core type IDs")
114            })
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 sub_ty = &self.types[for_func_ty];
144        let trampoline = sub_ty.unwrap_func().trampoline_type();
145
146        if let Some(idx) = self.trampoline_types.get(&trampoline) {
147            // We've already interned this trampoline type; reuse it.
148            *idx
149        } else {
150            // We have not already interned this trampoline type.
151            match trampoline {
152                // The trampoline type is the same as the original function
153                // type. We can reuse the definition and its index, but still
154                // need to intern the type into our `trampoline_types` map so we
155                // can reuse it in the future.
156                Cow::Borrowed(f) => {
157                    self.trampoline_types.insert(f.clone(), for_func_ty);
158                    for_func_ty
159                }
160                // The trampoline type is different from the original function
161                // type. Define the trampoline type and then intern it in
162                // `trampoline_types` so we can reuse it in the future.
163                Cow::Owned(f) => {
164                    let idx = self.types.push(WasmSubType {
165                        is_final: true,
166                        supertype: None,
167                        composite_type: WasmCompositeType {
168                            inner: WasmCompositeInnerType::Func(f.clone()),
169                            shared: sub_ty.composite_type.shared,
170                        },
171                    });
172
173                    // The trampoline type is its own trampoline type.
174                    self.types.set_trampoline_type(idx, idx);
175
176                    let next = self.types.next_ty();
177                    self.types.push_rec_group(idx..next);
178                    self.trampoline_types.insert(f, idx);
179                    idx
180                }
181            }
182        }
183    }
184
185    /// Start defining a recursion group.
186    fn start_rec_group(
187        &mut self,
188        validator_types: wasmparser::types::TypesRef<'_>,
189        elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
190    ) {
191        log::trace!("Starting rec group of length {}", elems.len());
192
193        assert!(self.defining_rec_group.is_none());
194        assert_eq!(validator_types.id(), self.validator_id);
195
196        // Eagerly define the reverse map's entries for this rec group's types
197        // so that we can use them when converting `wasmparser` types to our
198        // types.
199        let len = elems.len();
200        for (i, wasmparser_id) in elems.enumerate() {
201            let interned = ModuleInternedTypeIndex::new(self.types.len_types() + i);
202            log::trace!(
203                "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
204                validator_types[wasmparser_id]
205            );
206
207            let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
208            debug_assert_eq!(
209                old_entry, None,
210                "should not have already inserted {wasmparser_id:?}"
211            );
212        }
213
214        self.defining_rec_group = Some(RecGroupStart {
215            rec_group_index: self.types.next_rec_group(),
216            start: self.types.next_ty(),
217            end: ModuleInternedTypeIndex::new(self.types.len_types() + len),
218        });
219    }
220
221    /// Finish defining a recursion group.
222    fn end_rec_group(
223        &mut self,
224        rec_group_id: wasmparser::types::RecGroupId,
225    ) -> ModuleInternedRecGroupIndex {
226        let RecGroupStart {
227            rec_group_index,
228            start,
229            end,
230        } = self
231            .defining_rec_group
232            .take()
233            .expect("should be defining a rec group");
234
235        log::trace!("Ending rec group {start:?}..{end:?}");
236
237        debug_assert!(start.index() < self.types.len_types());
238        debug_assert_eq!(
239            end,
240            self.types.next_ty(),
241            "should have defined the number of types declared in `start_rec_group`"
242        );
243
244        let idx = self.types.push_rec_group(start..end);
245        debug_assert_eq!(idx, rec_group_index);
246
247        self.already_seen.insert(rec_group_id, rec_group_index);
248        rec_group_index
249    }
250
251    /// Intern a type into this builder and get its Wasmtime index.
252    ///
253    /// This will intern not only the single given type, but the type's entire
254    /// rec group. This helper method is provided as a convenience so that
255    /// callers don't have to get the type's rec group, intern the rec group,
256    /// and then look up the Wasmtime index for the original type themselves.
257    pub fn intern_type(
258        &mut self,
259        validator_types: wasmparser::types::TypesRef<'_>,
260        id: wasmparser::types::CoreTypeId,
261    ) -> WasmResult<ModuleInternedTypeIndex> {
262        assert!(self.defining_rec_group.is_none());
263        assert_eq!(validator_types.id(), self.validator_id);
264
265        let rec_group_id = validator_types.rec_group_id_of(id);
266        debug_assert!(
267            validator_types
268                .rec_group_elements(rec_group_id)
269                .any(|e| e == id)
270        );
271
272        let interned_rec_group = self.intern_rec_group(validator_types, rec_group_id)?;
273
274        let interned_type = self.wasmparser_to_wasmtime[&id];
275        debug_assert!(
276            self.rec_group_elements(interned_rec_group)
277                .any(|e| e == interned_type)
278        );
279
280        Ok(interned_type)
281    }
282
283    /// Define a new Wasm type while we are defining a rec group.
284    fn wasm_sub_type_in_rec_group(
285        &mut self,
286        id: wasmparser::types::CoreTypeId,
287        ty: WasmSubType,
288    ) -> ModuleInternedTypeIndex {
289        assert!(
290            self.defining_rec_group.is_some(),
291            "must be defining a rec group to define new types"
292        );
293
294        let module_interned_index = self.types.push(ty);
295        debug_assert_eq!(
296            self.wasmparser_to_wasmtime.get(&id),
297            Some(&module_interned_index),
298            "should have reserved the right module-interned index for this wasmparser type already"
299        );
300
301        module_interned_index
302    }
303
304    /// Returns the result [`ModuleTypes`] of this builder.
305    pub fn finish(self) -> ModuleTypes {
306        self.types
307    }
308
309    /// Get the elements within an already-defined rec group.
310    pub fn rec_group_elements(
311        &self,
312        rec_group: ModuleInternedRecGroupIndex,
313    ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> + use<> {
314        self.types.rec_group_elements(rec_group)
315    }
316
317    /// Returns an iterator over all the unique wasm types defined thus far
318    /// within this builder.
319    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
320        self.types.wasm_types()
321    }
322
323    /// Get an iterator over all function types and their associated trampoline
324    /// type.
325    pub fn trampoline_types(
326        &self,
327    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
328        self.types.trampoline_types()
329    }
330
331    /// Get the associated trampoline type for the given function type.
332    pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
333        self.types.trampoline_type(ty)
334    }
335
336    /// Get and unwrap a [`WasmStructType`] for the given struct index.
337    ///
338    /// # Panics
339    ///
340    /// Panics if the unwrapped type is not a struct.
341    ///
342    /// # Errors
343    ///
344    /// For now, fails with an unsupported error if the type is shared.
345    pub fn unwrap_struct(&self, ty: ModuleInternedTypeIndex) -> WasmResult<&WasmStructType> {
346        let composite_type = &self.types[ty].composite_type;
347        if composite_type.shared {
348            return Err(wasm_unsupported!("shared structs are not yet implemented"));
349        }
350        match &composite_type.inner {
351            WasmCompositeInnerType::Struct(s) => Ok(s),
352            _ => unreachable!(),
353        }
354    }
355
356    /// Get and unwrap a [`WasmArrayType`] for the given array index.
357    ///
358    /// # Panics
359    ///
360    /// Panics if the unwrapped type is not an array.
361    ///
362    /// # Errors
363    ///
364    /// For now, fails with an unsupported error if the type is shared.
365    pub fn unwrap_array(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmArrayType> {
366        let composite_type = &self.types[interned_ty].composite_type;
367        if composite_type.shared {
368            return Err(wasm_unsupported!("shared arrays are not yet implemented"));
369        }
370        match &composite_type.inner {
371            WasmCompositeInnerType::Array(a) => Ok(a),
372            _ => unreachable!(),
373        }
374    }
375}
376
377// Forward the indexing impl to the internal `ModuleTypes`
378impl<T> Index<T> for ModuleTypesBuilder
379where
380    ModuleTypes: Index<T>,
381{
382    type Output = <ModuleTypes as Index<T>>::Output;
383
384    fn index(&self, sig: T) -> &Self::Output {
385        &self.types[sig]
386    }
387}
388
389/// A convert from `wasmparser` types to Wasmtime types.
390pub struct WasmparserTypeConverter<'a, F> {
391    types: &'a ModuleTypesBuilder,
392    lookup_type_idx: F,
393    rec_group_context: Option<(
394        wasmparser::types::TypesRef<'a>,
395        wasmparser::types::RecGroupId,
396    )>,
397}
398
399impl<'a, F> WasmparserTypeConverter<'a, F> {
400    /// Construct a new type converter from `wasmparser` types to Wasmtime types.
401    pub fn new(types: &'a ModuleTypesBuilder, lookup_type_idx: F) -> Self {
402        Self {
403            types,
404            lookup_type_idx,
405            rec_group_context: None,
406        }
407    }
408
409    /// Configure this converter to be within the context of defining the
410    /// current rec group.
411    pub fn with_rec_group(
412        &mut self,
413        wasmparser_types: wasmparser::types::TypesRef<'a>,
414        rec_group: wasmparser::types::RecGroupId,
415    ) -> &Self {
416        self.rec_group_context = Some((wasmparser_types, rec_group));
417        self
418    }
419}
420
421impl<F> TypeConvert for WasmparserTypeConverter<'_, F>
422where
423    F: Fn(TypeIndex) -> ModuleInternedTypeIndex,
424{
425    fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
426        match index {
427            UnpackedIndex::Id(id) => {
428                let interned = self.types.wasmparser_to_wasmtime[&id];
429                let index = EngineOrModuleTypeIndex::Module(interned);
430
431                // If this is a forward reference to a type in this type's rec
432                // group that we haven't converted yet, then we won't have an
433                // entry in `wasm_types` yet. In this case, fallback to a
434                // different means of determining whether this is a concrete
435                // array vs struct vs func reference. In this case, we can use
436                // the validator's type context.
437                if let Some(ty) = self.types.types.get(interned) {
438                    assert!(!ty.composite_type.shared);
439                    match &ty.composite_type.inner {
440                        WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
441                        WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
442                        WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
443                        WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
444                    }
445                } else if let Some((wasmparser_types, _)) = self.rec_group_context.as_ref() {
446                    let wasmparser_ty = &wasmparser_types[id].composite_type;
447                    assert!(!wasmparser_ty.shared);
448                    match &wasmparser_ty.inner {
449                        wasmparser::CompositeInnerType::Array(_) => {
450                            WasmHeapType::ConcreteArray(index)
451                        }
452                        wasmparser::CompositeInnerType::Func(_) => {
453                            WasmHeapType::ConcreteFunc(index)
454                        }
455                        wasmparser::CompositeInnerType::Struct(_) => {
456                            WasmHeapType::ConcreteStruct(index)
457                        }
458                        wasmparser::CompositeInnerType::Cont(_) => {
459                            WasmHeapType::ConcreteCont(index)
460                        }
461                    }
462                } else {
463                    panic!("forward reference to type outside of rec group?")
464                }
465            }
466
467            UnpackedIndex::Module(module_index) => {
468                let module_index = TypeIndex::from_u32(module_index);
469                let interned = (self.lookup_type_idx)(module_index);
470                let index = EngineOrModuleTypeIndex::Module(interned);
471
472                // See comment above about `wasm_types` maybe not having the
473                // converted sub type yet. However in this case we don't have a
474                // `wasmparser::types::CoreTypeId` on hand, so we have to
475                // indirectly get one by looking it up inside the current rec
476                // group.
477                if let Some(ty) = self.types.types.get(interned) {
478                    assert!(!ty.composite_type.shared);
479                    match &ty.composite_type.inner {
480                        WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
481                        WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
482                        WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
483                        WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
484                    }
485                } else if let Some((parser_types, rec_group)) = self.rec_group_context.as_ref() {
486                    let rec_group_index = interned.index() - self.types.types.len_types();
487                    let id = parser_types
488                        .rec_group_elements(*rec_group)
489                        .nth(rec_group_index)
490                        .unwrap();
491                    let wasmparser_ty = &parser_types[id].composite_type;
492                    assert!(!wasmparser_ty.shared);
493                    match &wasmparser_ty.inner {
494                        wasmparser::CompositeInnerType::Array(_) => {
495                            WasmHeapType::ConcreteArray(index)
496                        }
497                        wasmparser::CompositeInnerType::Func(_) => {
498                            WasmHeapType::ConcreteFunc(index)
499                        }
500                        wasmparser::CompositeInnerType::Struct(_) => {
501                            WasmHeapType::ConcreteStruct(index)
502                        }
503                        wasmparser::CompositeInnerType::Cont(_) => {
504                            WasmHeapType::ConcreteCont(index)
505                        }
506                    }
507                } else {
508                    panic!("forward reference to type outside of rec group?")
509                }
510            }
511
512            UnpackedIndex::RecGroup(_) => unreachable!(),
513        }
514    }
515
516    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
517        match index {
518            UnpackedIndex::Id(id) => {
519                let interned = self.types.wasmparser_to_wasmtime[&id];
520                EngineOrModuleTypeIndex::Module(interned)
521            }
522            UnpackedIndex::Module(module_index) => {
523                let module_index = TypeIndex::from_u32(module_index);
524                let interned = (self.lookup_type_idx)(module_index);
525                EngineOrModuleTypeIndex::Module(interned)
526            }
527            UnpackedIndex::RecGroup(_) => unreachable!(),
528        }
529    }
530}