1 // Currently the `VMComponentContext` allocation by field looks like this:
2 //
3 // struct VMComponentContext {
4 //      magic: u32,
5 //      builtins: &'static VMComponentBuiltins,
6 //      limits: *const VMStoreContext,
7 //      flags: [VMGlobalDefinition; component.num_runtime_component_instances],
8 //      task_may_block: u32,
9 //      trampoline_func_refs: [VMFuncRef; component.num_trampolines],
10 //      unsafe_intrinsics: [VMFuncRef; component.num_unsafe_intrinsics],
11 //      lowerings: [VMLowering; component.num_lowerings],
12 //      memories: [*mut VMMemoryDefinition; component.num_runtime_memories],
13 //      tables: [VMTable; component.num_runtime_tables],
14 //      reallocs: [*mut VMFuncRef; component.num_runtime_reallocs],
15 //      post_returns: [*mut VMFuncRef; component.num_runtime_post_returns],
16 //      resource_destructors: [*mut VMFuncRef; component.num_resources],
17 // }
18 
19 use crate::PtrSize;
20 use crate::component::*;
21 
22 /// Equivalent of `VMCONTEXT_MAGIC` except for components.
23 ///
24 /// This is stored at the start of all `VMComponentContext` structures and
25 /// double-checked on `VMComponentContext::from_opaque`.
26 pub const VMCOMPONENT_MAGIC: u32 = u32::from_le_bytes(*b"comp");
27 
28 /// Flag for the `VMComponentContext::flags` field which corresponds to the
29 /// canonical ABI flag `may_leave`
30 pub const FLAG_MAY_LEAVE: i32 = 1 << 0;
31 
32 /// Runtime offsets within a `VMComponentContext` for a specific component.
33 #[derive(Debug, Clone, Copy)]
34 pub struct VMComponentOffsets<P> {
35     /// The host pointer size
36     pub ptr: P,
37 
38     /// The number of lowered functions this component will be creating.
39     pub num_lowerings: u32,
40     /// The number of memories which are recorded in this component for options.
41     pub num_runtime_memories: u32,
42     /// The number of tables which are recorded in this component for options.
43     pub num_runtime_tables: u32,
44     /// The number of reallocs which are recorded in this component for options.
45     pub num_runtime_reallocs: u32,
46     /// The number of callbacks which are recorded in this component for options.
47     pub num_runtime_callbacks: u32,
48     /// The number of post-returns which are recorded in this component for options.
49     pub num_runtime_post_returns: u32,
50     /// Number of component instances internally in the component (always at
51     /// least 1).
52     pub num_runtime_component_instances: u32,
53     /// Number of cranelift-compiled trampolines required for this component.
54     pub num_trampolines: u32,
55     /// Number of `VMFuncRef`s for unsafe intrinsics within this component's
56     /// context.
57     pub num_unsafe_intrinsics: u32,
58     /// Number of resources within a component which need destructors stored.
59     pub num_resources: u32,
60 
61     // precalculated offsets of various member fields
62     magic: u32,
63     builtins: u32,
64     vm_store_context: u32,
65     flags: u32,
66     task_may_block: u32,
67     trampoline_func_refs: u32,
68     intrinsic_func_refs: u32,
69     lowerings: u32,
70     memories: u32,
71     tables: u32,
72     reallocs: u32,
73     callbacks: u32,
74     post_returns: u32,
75     resource_destructors: u32,
76     size: u32,
77 }
78 
79 #[inline]
align(offset: u32, align: u32) -> u3280 fn align(offset: u32, align: u32) -> u32 {
81     assert!(align.is_power_of_two());
82     (offset + (align - 1)) & !(align - 1)
83 }
84 
85 impl<P: PtrSize> VMComponentOffsets<P> {
86     /// Creates a new set of offsets for the `component` specified configured
87     /// additionally for the `ptr` size specified.
new(ptr: P, component: &Component) -> Self88     pub fn new(ptr: P, component: &Component) -> Self {
89         let mut ret = Self {
90             ptr,
91             num_lowerings: component.num_lowerings,
92             num_runtime_memories: component.num_runtime_memories,
93             num_runtime_tables: component.num_runtime_tables,
94             num_runtime_reallocs: component.num_runtime_reallocs,
95             num_runtime_callbacks: component.num_runtime_callbacks,
96             num_runtime_post_returns: component.num_runtime_post_returns,
97             num_runtime_component_instances: component.num_runtime_component_instances,
98             num_trampolines: component.trampolines.len().try_into().unwrap(),
99             num_unsafe_intrinsics: if let Some(i) = component
100                 .unsafe_intrinsics
101                 .iter()
102                 .rposition(|x| x.is_some())
103             {
104                 // Note: We do not currently have an indirection between "the
105                 // `i`th unsafe intrinsic in the vmctx" and
106                 // `UnsafeIntrinsic::from_u32(i)`, so therefore if we are
107                 // compiling in *any* intrinsics, we need to include space for
108                 // all of them up to the max `i` that is used.
109                 //
110                 // We _could_ introduce such an indirection via a map in
111                 // `Component` like `PrimaryMap<UnsafeIntrinsicIndex,
112                 // UnsafeIntrinsic>`, and that would allow us to densely pack
113                 // intrinsics in the vmctx. However we do not do that today
114                 // because there are very few unsafe intrinsics, and we do not
115                 // see that changing anytime soon, so we aren't wasting much
116                 // space.
117                 u32::try_from(i + 1).unwrap()
118             } else {
119                 0
120             },
121             num_resources: component.num_resources,
122             magic: 0,
123             builtins: 0,
124             vm_store_context: 0,
125             flags: 0,
126             task_may_block: 0,
127             trampoline_func_refs: 0,
128             intrinsic_func_refs: 0,
129             lowerings: 0,
130             memories: 0,
131             tables: 0,
132             reallocs: 0,
133             callbacks: 0,
134             post_returns: 0,
135             resource_destructors: 0,
136             size: 0,
137         };
138 
139         // Convenience functions for checked addition and multiplication.
140         // As side effect this reduces binary size by using only a single
141         // `#[track_caller]` location for each function instead of one for
142         // each individual invocation.
143         #[inline]
144         fn cmul(count: u32, size: u8) -> u32 {
145             count.checked_mul(u32::from(size)).unwrap()
146         }
147 
148         let mut next_field_offset = 0;
149 
150         macro_rules! fields {
151             (size($field:ident) = $size:expr, $($rest:tt)*) => {
152                 ret.$field = next_field_offset;
153                 next_field_offset = next_field_offset.checked_add(u32::from($size)).unwrap();
154                 fields!($($rest)*);
155             };
156             (align($align:expr), $($rest:tt)*) => {
157                 next_field_offset = align(next_field_offset, $align);
158                 fields!($($rest)*);
159             };
160             () => {};
161         }
162 
163         fields! {
164             size(magic) = 4u32,
165             align(u32::from(ret.ptr.size())),
166             size(builtins) = ret.ptr.size(),
167             size(vm_store_context) = ret.ptr.size(),
168             align(16),
169             size(flags) = cmul(ret.num_runtime_component_instances, ret.ptr.size_of_vmglobal_definition()),
170             size(task_may_block) = ret.ptr.size_of_vmglobal_definition(),
171             align(u32::from(ret.ptr.size())),
172             size(trampoline_func_refs) = cmul(ret.num_trampolines, ret.ptr.size_of_vm_func_ref()),
173             size(intrinsic_func_refs) = cmul(ret.num_unsafe_intrinsics, ret.ptr.size_of_vm_func_ref()),
174             size(lowerings) = cmul(ret.num_lowerings, ret.ptr.size() * 2),
175             size(memories) = cmul(ret.num_runtime_memories, ret.ptr.size()),
176             size(tables) = cmul(ret.num_runtime_tables, ret.size_of_vmtable_import()),
177             size(reallocs) = cmul(ret.num_runtime_reallocs, ret.ptr.size()),
178             size(callbacks) = cmul(ret.num_runtime_callbacks, ret.ptr.size()),
179             size(post_returns) = cmul(ret.num_runtime_post_returns, ret.ptr.size()),
180             size(resource_destructors) = cmul(ret.num_resources, ret.ptr.size()),
181         }
182 
183         ret.size = next_field_offset;
184 
185         // This is required by the implementation of
186         // `VMComponentContext::from_opaque`. If this value changes then this
187         // location needs to be updated.
188         assert_eq!(ret.magic, 0);
189 
190         return ret;
191     }
192 
193     /// The size, in bytes, of the host pointer.
194     #[inline]
pointer_size(&self) -> u8195     pub fn pointer_size(&self) -> u8 {
196         self.ptr.size()
197     }
198 
199     /// The offset of the `magic` field.
200     #[inline]
magic(&self) -> u32201     pub fn magic(&self) -> u32 {
202         self.magic
203     }
204 
205     /// The offset of the `builtins` field.
206     #[inline]
builtins(&self) -> u32207     pub fn builtins(&self) -> u32 {
208         self.builtins
209     }
210 
211     /// The offset of the `flags` field.
212     #[inline]
instance_flags(&self, index: RuntimeComponentInstanceIndex) -> u32213     pub fn instance_flags(&self, index: RuntimeComponentInstanceIndex) -> u32 {
214         assert!(index.as_u32() < self.num_runtime_component_instances);
215         self.flags + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
216     }
217 
218     /// The offset of the `task_may_block` field.
task_may_block(&self) -> u32219     pub fn task_may_block(&self) -> u32 {
220         self.task_may_block
221     }
222 
223     /// The offset of the `vm_store_context` field.
224     #[inline]
vm_store_context(&self) -> u32225     pub fn vm_store_context(&self) -> u32 {
226         self.vm_store_context
227     }
228 
229     /// The offset of the `trampoline_func_refs` field.
230     #[inline]
trampoline_func_refs(&self) -> u32231     pub fn trampoline_func_refs(&self) -> u32 {
232         self.trampoline_func_refs
233     }
234 
235     /// The offset of `VMFuncRef` for the `index` specified.
236     #[inline]
trampoline_func_ref(&self, index: TrampolineIndex) -> u32237     pub fn trampoline_func_ref(&self, index: TrampolineIndex) -> u32 {
238         assert!(index.as_u32() < self.num_trampolines);
239         self.trampoline_func_refs() + index.as_u32() * u32::from(self.ptr.size_of_vm_func_ref())
240     }
241 
242     /// The offset of the `unsafe_intrinsic_func_refs` field.
243     #[inline]
unsafe_intrinsic_func_refs(&self) -> u32244     pub fn unsafe_intrinsic_func_refs(&self) -> u32 {
245         self.intrinsic_func_refs
246     }
247 
248     /// The offset of the `VMFuncRef` for the `intrinsic` specified.
249     #[inline]
unsafe_intrinsic_func_ref(&self, intrinsic: UnsafeIntrinsic) -> u32250     pub fn unsafe_intrinsic_func_ref(&self, intrinsic: UnsafeIntrinsic) -> u32 {
251         assert!(intrinsic.index() < self.num_unsafe_intrinsics);
252         self.unsafe_intrinsic_func_refs()
253             + intrinsic.index() * u32::from(self.ptr.size_of_vm_func_ref())
254     }
255 
256     /// The offset of the `lowerings` field.
257     #[inline]
lowerings(&self) -> u32258     pub fn lowerings(&self) -> u32 {
259         self.lowerings
260     }
261 
262     /// The offset of the `VMLowering` for the `index` specified.
263     #[inline]
lowering(&self, index: LoweredIndex) -> u32264     pub fn lowering(&self, index: LoweredIndex) -> u32 {
265         assert!(index.as_u32() < self.num_lowerings);
266         self.lowerings() + index.as_u32() * u32::from(2 * self.ptr.size())
267     }
268 
269     /// The offset of the `callee` for the `index` specified.
270     #[inline]
lowering_callee(&self, index: LoweredIndex) -> u32271     pub fn lowering_callee(&self, index: LoweredIndex) -> u32 {
272         self.lowering(index) + self.lowering_callee_offset()
273     }
274 
275     /// The offset of the `data` for the `index` specified.
276     #[inline]
lowering_data(&self, index: LoweredIndex) -> u32277     pub fn lowering_data(&self, index: LoweredIndex) -> u32 {
278         self.lowering(index) + self.lowering_data_offset()
279     }
280 
281     /// The size of the `VMLowering` type
282     #[inline]
lowering_size(&self) -> u8283     pub fn lowering_size(&self) -> u8 {
284         2 * self.ptr.size()
285     }
286 
287     /// The offset of the `callee` field within the `VMLowering` type.
288     #[inline]
lowering_callee_offset(&self) -> u32289     pub fn lowering_callee_offset(&self) -> u32 {
290         0
291     }
292 
293     /// The offset of the `data` field within the `VMLowering` type.
294     #[inline]
lowering_data_offset(&self) -> u32295     pub fn lowering_data_offset(&self) -> u32 {
296         u32::from(self.ptr.size())
297     }
298 
299     /// The offset of the base of the `runtime_memories` field
300     #[inline]
runtime_memories(&self) -> u32301     pub fn runtime_memories(&self) -> u32 {
302         self.memories
303     }
304 
305     /// The offset of the `*mut VMMemoryDefinition` for the runtime index
306     /// provided.
307     #[inline]
runtime_memory(&self, index: RuntimeMemoryIndex) -> u32308     pub fn runtime_memory(&self, index: RuntimeMemoryIndex) -> u32 {
309         assert!(index.as_u32() < self.num_runtime_memories);
310         self.runtime_memories() + index.as_u32() * u32::from(self.ptr.size())
311     }
312 
313     /// The offset of the base of the `runtime_tables` field
314     #[inline]
runtime_tables(&self) -> u32315     pub fn runtime_tables(&self) -> u32 {
316         self.tables
317     }
318 
319     /// The offset of the table for the runtime index provided.
320     #[inline]
runtime_table(&self, index: RuntimeTableIndex) -> u32321     pub fn runtime_table(&self, index: RuntimeTableIndex) -> u32 {
322         assert!(index.as_u32() < self.num_runtime_tables);
323         self.runtime_tables() + index.as_u32() * u32::from(self.size_of_vmtable_import())
324     }
325 
326     /// Return the size of `VMTableImport`, used here to hold the pointers to
327     /// the `VMTableDefinition` and `VMContext`.
328     #[inline]
size_of_vmtable_import(&self) -> u8329     pub fn size_of_vmtable_import(&self) -> u8 {
330         3 * self.pointer_size()
331     }
332 
333     /// The offset of the base of the `runtime_reallocs` field
334     #[inline]
runtime_reallocs(&self) -> u32335     pub fn runtime_reallocs(&self) -> u32 {
336         self.reallocs
337     }
338 
339     /// The offset of the `*mut VMFuncRef` for the runtime index
340     /// provided.
341     #[inline]
runtime_realloc(&self, index: RuntimeReallocIndex) -> u32342     pub fn runtime_realloc(&self, index: RuntimeReallocIndex) -> u32 {
343         assert!(index.as_u32() < self.num_runtime_reallocs);
344         self.runtime_reallocs() + index.as_u32() * u32::from(self.ptr.size())
345     }
346 
347     /// The offset of the base of the `runtime_callbacks` field
348     #[inline]
runtime_callbacks(&self) -> u32349     pub fn runtime_callbacks(&self) -> u32 {
350         self.callbacks
351     }
352 
353     /// The offset of the `*mut VMFuncRef` for the runtime index
354     /// provided.
355     #[inline]
runtime_callback(&self, index: RuntimeCallbackIndex) -> u32356     pub fn runtime_callback(&self, index: RuntimeCallbackIndex) -> u32 {
357         assert!(index.as_u32() < self.num_runtime_callbacks);
358         self.runtime_callbacks() + index.as_u32() * u32::from(self.ptr.size())
359     }
360 
361     /// The offset of the base of the `runtime_post_returns` field
362     #[inline]
runtime_post_returns(&self) -> u32363     pub fn runtime_post_returns(&self) -> u32 {
364         self.post_returns
365     }
366 
367     /// The offset of the `*mut VMFuncRef` for the runtime index
368     /// provided.
369     #[inline]
runtime_post_return(&self, index: RuntimePostReturnIndex) -> u32370     pub fn runtime_post_return(&self, index: RuntimePostReturnIndex) -> u32 {
371         assert!(index.as_u32() < self.num_runtime_post_returns);
372         self.runtime_post_returns() + index.as_u32() * u32::from(self.ptr.size())
373     }
374 
375     /// The offset of the base of the `resource_destructors` field
376     #[inline]
resource_destructors(&self) -> u32377     pub fn resource_destructors(&self) -> u32 {
378         self.resource_destructors
379     }
380 
381     /// The offset of the `*mut VMFuncRef` for the runtime index
382     /// provided.
383     #[inline]
resource_destructor(&self, index: ResourceIndex) -> u32384     pub fn resource_destructor(&self, index: ResourceIndex) -> u32 {
385         assert!(index.as_u32() < self.num_resources);
386         self.resource_destructors() + index.as_u32() * u32::from(self.ptr.size())
387     }
388 
389     /// Return the size of the `VMComponentContext` allocation.
390     #[inline]
size_of_vmctx(&self) -> u32391     pub fn size_of_vmctx(&self) -> u32 {
392         self.size
393     }
394 }
395