Skip to main content

wasmtime_environ/component/
vmcomponent_offsets.rs

1//! Offsets of the fields within a `VMComponentContext`.
2//!
3//! The layout itself is not defined here: it is defined once, alongside
4//! `VMContext`'s, in `for_each_vmctx_type!`. Everything in this module is either
5//! generated from that definition or is an offset that is not simply the offset
6//! of one of the layout's fields.
7
8use crate::GetPtrSize;
9use crate::PtrSize;
10use crate::component::*;
11
12/// Equivalent of `VMCONTEXT_MAGIC` except for components.
13///
14/// This is stored at the start of all `VMComponentContext` structures and
15/// double-checked on `VMComponentContext::from_opaque`.
16pub const VMCOMPONENT_MAGIC: u32 = u32::from_le_bytes(*b"comp");
17
18/// Runtime offsets within a `VMComponentContext` for a specific component.
19#[derive(Debug, Clone, Copy)]
20pub struct VMComponentOffsets<P> {
21    /// The host pointer size
22    pub ptr: P,
23
24    /// The number of lowered functions this component will be creating.
25    pub num_lowerings: u32,
26    /// The number of memories which are recorded in this component for options.
27    pub num_runtime_memories: u32,
28    /// The number of tables which are recorded in this component for options.
29    pub num_runtime_tables: u32,
30    /// The number of reallocs which are recorded in this component for options.
31    pub num_runtime_reallocs: u32,
32    /// The number of callbacks which are recorded in this component for options.
33    pub num_runtime_callbacks: u32,
34    /// The number of post-returns which are recorded in this component for options.
35    pub num_runtime_post_returns: u32,
36    /// Number of component instances internally in the component (always at
37    /// least 1).
38    pub num_runtime_component_instances: u32,
39    /// Number of cranelift-compiled trampolines required for this component.
40    pub num_trampolines: u32,
41    /// Number of `VMFuncRef`s for unsafe intrinsics within this component's
42    /// context.
43    pub num_unsafe_intrinsics: u32,
44    /// Number of resources within a component which need destructors stored.
45    pub num_resources: u32,
46
47    // Precalculated offsets of the dynamically-positioned fields, one per entry
48    // in `VMComponentContext`'s `dynamic` section in `for_each_vmctx_type!`,
49    // plus this `VMComponentContext`'s total size. These are all computed by the
50    // generated `compute_field_offsets` and read by the generated accessors of
51    // the same names.
52    may_leave: u32,
53    trampoline_func_refs: u32,
54    intrinsic_func_refs: u32,
55    lowerings: u32,
56    memories: u32,
57    tables: u32,
58    reallocs: u32,
59    callbacks: u32,
60    post_returns: u32,
61    resource_destructors: u32,
62    size: u32,
63}
64
65impl<P: PtrSize> GetPtrSize for VMComponentOffsets<P> {
66    type Ptr = P;
67
68    #[inline]
69    fn get_ptr_size(&self) -> &Self::Ptr {
70        &self.ptr
71    }
72}
73
74impl_vmctx_array_index! {
75    RuntimeComponentInstanceIndex,
76    TrampolineIndex,
77    LoweredIndex,
78    RuntimeMemoryIndex,
79    RuntimeTableIndex,
80    RuntimeReallocIndex,
81    RuntimeCallbackIndex,
82    RuntimePostReturnIndex,
83    ResourceIndex,
84}
85
86impl crate::VmctxArrayIndex for UnsafeIntrinsic {
87    #[inline]
88    fn vmctx_array_index(self) -> u32 {
89        self.index()
90    }
91}
92
93/// Generate the offsets of `VMComponentContext`'s dynamically-positioned fields,
94/// ignoring every other vmctx type.
95macro_rules! define_vmcomponent_offsets_dynamic_offsets {
96    (@one VMComponentContext $snake:ident { $($dyn:tt)* }) => {
97        /// Offsets of the dynamically-positioned fields of `VMComponentContext`.
98        impl<P: PtrSize> VMComponentOffsets<P> {
99            define_vmctx_dynamic_offsets!(@accessors (self) [ $($dyn)* ]);
100
101            define_vmctx_dynamic_offsets!(@compute_fn (self, next) $snake [ $($dyn)* ]);
102
103            /// Return the size of the `VMComponentContext` allocation.
104            #[inline]
105            pub fn size_of_vmctx(&self) -> u32 {
106                self.size
107            }
108        }
109    };
110    (@one $other:ident $snake:ident { $($dyn:tt)* }) => {};
111
112    ( $(
113        {
114            $Name:ident $snake:ident
115            static { $($stat:tt)* }
116            dynamic { $($dyn:tt)* }
117        }
118    )* ) => {
119        $( define_vmcomponent_offsets_dynamic_offsets!(@one $Name $snake { $($dyn)* }); )*
120    };
121}
122
123for_each_vmctx_type!(define_vmcomponent_offsets_dynamic_offsets);
124
125impl<P: PtrSize> VMComponentOffsets<P> {
126    /// Creates a new set of offsets for the `component` specified configured
127    /// additionally for the `ptr` size specified.
128    pub fn new(ptr: P, component: &Component) -> Self {
129        // This is required by the implementation of
130        // `VMComponentContext::from_opaque`. If this value changes then this
131        // location needs to be updated.
132        assert_eq!(ptr.vmcomponent().magic(), 0);
133
134        let mut ret = Self {
135            ptr,
136            num_lowerings: component.num_lowerings,
137            num_runtime_memories: component.num_runtime_memories,
138            num_runtime_tables: component.num_runtime_tables,
139            num_runtime_reallocs: component.num_runtime_reallocs,
140            num_runtime_callbacks: component.num_runtime_callbacks,
141            num_runtime_post_returns: component.num_runtime_post_returns,
142            num_runtime_component_instances: component.num_runtime_component_instances,
143            num_trampolines: component.trampolines.len().try_into().unwrap(),
144            num_unsafe_intrinsics: if let Some(i) = component
145                .unsafe_intrinsics
146                .iter()
147                .rposition(|x| x.is_some())
148            {
149                // Note: We do not currently have an indirection between "the
150                // `i`th unsafe intrinsic in the vmctx" and
151                // `UnsafeIntrinsic::from_u32(i)`, so therefore if we are
152                // compiling in *any* intrinsics, we need to include space for
153                // all of them up to the max `i` that is used.
154                //
155                // We _could_ introduce such an indirection via a map in
156                // `Component` like `PrimaryMap<UnsafeIntrinsicIndex,
157                // UnsafeIntrinsic>`, and that would allow us to densely pack
158                // intrinsics in the vmctx. However we do not do that today
159                // because there are very few unsafe intrinsics, and we do not
160                // see that changing anytime soon, so we aren't wasting much
161                // space.
162                u32::try_from(i + 1).unwrap()
163            } else {
164                0
165            },
166            num_resources: component.num_resources,
167            may_leave: 0,
168            trampoline_func_refs: 0,
169            intrinsic_func_refs: 0,
170            lowerings: 0,
171            memories: 0,
172            tables: 0,
173            reallocs: 0,
174            callbacks: 0,
175            post_returns: 0,
176            resource_destructors: 0,
177            size: 0,
178        };
179
180        ret.compute_field_offsets();
181
182        // The component-model flags must land where a compiler that only knows
183        // the pointer size can find them.
184        debug_assert!(
185            (0..ret.num_runtime_component_instances)
186                .map(RuntimeComponentInstanceIndex::from_u32)
187                .all(|i| ret.may_leave().at(i) == ret.ptr.vmcomponent().may_leave(i))
188        );
189
190        ret
191    }
192
193    /// The size, in bytes, of the host pointer.
194    #[inline]
195    pub fn pointer_size(&self) -> u8 {
196        self.ptr.size()
197    }
198
199    /// The offset of the `callee` for the `index` specified.
200    #[inline]
201    pub fn lowering_callee(&self, index: LoweredIndex) -> u32 {
202        self.lowerings().at(index) + self.lowering_callee_offset()
203    }
204
205    /// The offset of the `data` for the `index` specified.
206    #[inline]
207    pub fn lowering_data(&self, index: LoweredIndex) -> u32 {
208        self.lowerings().at(index) + self.lowering_data_offset()
209    }
210
211    /// The size of the `VMLowering` type
212    #[inline]
213    pub fn lowering_size(&self) -> u8 {
214        2 * self.ptr.size()
215    }
216
217    /// The offset of the `callee` field within the `VMLowering` type.
218    #[inline]
219    pub fn lowering_callee_offset(&self) -> u32 {
220        0
221    }
222
223    /// The offset of the `data` field within the `VMLowering` type.
224    #[inline]
225    pub fn lowering_data_offset(&self) -> u32 {
226        u32::from(self.ptr.size())
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    /// The pointer-size-only flag offsets must match the real layout for every
235    /// pointer width and every number of component instances, since core Wasm
236    /// compilation uses them to build alias regions that must agree with the
237    /// regions the component trampolines use.
238    #[test]
239    fn flag_offsets_match_layout() {
240        for ptr in [4u8, 8] {
241            for num_runtime_component_instances in 0..8 {
242                let component = Component {
243                    num_runtime_component_instances,
244                    ..Default::default()
245                };
246                let offsets = VMComponentOffsets::new(ptr, &component);
247
248                for i in 0..num_runtime_component_instances {
249                    let index = RuntimeComponentInstanceIndex::from_u32(i);
250                    assert_eq!(
251                        offsets.may_leave().at(index),
252                        ptr.vmcomponent().may_leave(index)
253                    );
254                }
255            }
256        }
257    }
258}