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