Skip to main content

wasmtime_environ/compile/
module_types.rs

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