wasmtime_environ/compile/
module_types.rs

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