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}