wasmi/engine/func_types.rs
1use super::{EngineIdx, Guarded};
2use crate::{
3 collections::arena::{ArenaIndex, DedupArena, GuardedEntity},
4 FuncType,
5};
6
7/// A raw index to a function signature entity.
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct DedupFuncTypeIdx(u32);
10
11impl ArenaIndex for DedupFuncTypeIdx {
12 fn into_usize(self) -> usize {
13 self.0 as _
14 }
15
16 fn from_usize(value: usize) -> Self {
17 let value = value.try_into().unwrap_or_else(|error| {
18 panic!("index {value} is out of bounds as dedup func type index: {error}")
19 });
20 Self(value)
21 }
22}
23
24/// A deduplicated Wasm [`FuncType`].
25///
26/// # Note
27///
28/// Advantages over a non-deduplicated [`FuncType`] are:
29///
30/// - Comparison for equality is as fast as an integer value comparison.
31/// - With this we can speed up indirect calls in the engine.
32/// - Requires a lot less memory footprint to be stored somewhere compared
33/// to a full fledged [`FuncType`].
34///
35/// Disadvantages compared to non-deduplicated [`FuncType`] are:
36///
37/// - Requires another indirection to acquire information such as parameter
38/// or result types of the underlying [`FuncType`].
39#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
40#[repr(transparent)]
41pub struct DedupFuncType(GuardedEntity<EngineIdx, DedupFuncTypeIdx>);
42
43impl DedupFuncType {
44 /// Creates a new function signature reference.
45 pub(super) fn from_inner(stored: GuardedEntity<EngineIdx, DedupFuncTypeIdx>) -> Self {
46 Self(stored)
47 }
48
49 /// Returns the underlying stored representation.
50 pub(super) fn into_inner(self) -> GuardedEntity<EngineIdx, DedupFuncTypeIdx> {
51 self.0
52 }
53}
54
55/// A [`FuncType`] registry that efficiently deduplicate stored function types.
56///
57/// Can also be used to later resolve deduplicated function types into their
58/// original [`FuncType`] for inspecting their parameter and result types.
59///
60/// The big advantage of deduplicated [`FuncType`] entities is that we can use
61/// this for indirect calls to speed up the signature checks since comparing
62/// deduplicated [`FuncType`] instances is as fast as comparing integer values.
63/// Also with respect to Wasmi bytecode deduplicated [`FuncType`] entities
64/// require a lot less space to be stored.
65#[derive(Debug)]
66pub struct FuncTypeRegistry {
67 /// A unique identifier for the associated engine.
68 ///
69 /// # Note
70 ///
71 /// This is used to guard against invalid entity indices.
72 engine_idx: EngineIdx,
73 /// Deduplicated function types.
74 ///
75 /// # Note
76 ///
77 /// The engine deduplicates function types to make the equality
78 /// comparison very fast. This helps to speed up indirect calls.
79 func_types: DedupArena<DedupFuncTypeIdx, FuncType>,
80}
81
82impl FuncTypeRegistry {
83 /// Creates a new [`FuncTypeRegistry`] using the given [`EngineIdx`].
84 pub(crate) fn new(engine_idx: EngineIdx) -> Self {
85 Self {
86 engine_idx,
87 func_types: DedupArena::default(),
88 }
89 }
90
91 /// Unpacks the entity and checks if it is owned by the engine.
92 ///
93 /// # Panics
94 ///
95 /// If the guarded entity is not owned by the engine.
96 fn unwrap_index<Idx>(&self, func_type: Guarded<Idx>) -> Idx
97 where
98 Idx: ArenaIndex,
99 {
100 func_type.entity_index(self.engine_idx).unwrap_or_else(|| {
101 panic!(
102 "encountered foreign entity in func type registry: {}",
103 self.engine_idx.into_usize()
104 )
105 })
106 }
107
108 /// Allocates a new function type to the engine.
109 pub(crate) fn alloc_func_type(&mut self, func_type: FuncType) -> DedupFuncType {
110 DedupFuncType::from_inner(Guarded::new(
111 self.engine_idx,
112 self.func_types.alloc(func_type),
113 ))
114 }
115
116 /// Resolves a deduplicated function type into a [`FuncType`] entity.
117 ///
118 /// # Panics
119 ///
120 /// - If the deduplicated function type is not owned by the engine.
121 /// - If the deduplicated function type cannot be resolved to its entity.
122 pub(crate) fn resolve_func_type(&self, func_type: &DedupFuncType) -> &FuncType {
123 let entity_index = self.unwrap_index(func_type.into_inner());
124 self.func_types
125 .get(entity_index)
126 .unwrap_or_else(|| panic!("failed to resolve stored function type: {entity_index:?}"))
127 }
128}