wasmtime_environ/
vmoffsets.rs

1//! Offsets and sizes of various structs in `wasmtime::runtime::vm::*` that are
2//! accessed directly by compiled Wasm code.
3
4// Currently the `VMContext` allocation by field looks like this:
5//
6// struct VMContext {
7//      // Fixed-width data comes first so the calculation of the offset of
8//      // these fields is a compile-time constant when using `HostPtr`.
9//      magic: u32,
10//      _padding: u32, // (On 64-bit systems)
11//      vm_store_context: *const VMStoreContext,
12//      builtin_functions: *mut VMBuiltinFunctionsArray,
13//      epoch_ptr: *mut AtomicU64,
14//      gc_heap_data: *mut T, // Collector-specific pointer
15//      type_ids: *const VMSharedTypeIndex,
16//
17//      // Variable-width fields come after the fixed-width fields above. Place
18//      // memory-related items first as they're some of the most frequently
19//      // accessed items and minimizing their offset in this structure can
20//      // shrink the size of load/store instruction offset immediates on
21//      // platforms like x64 and Pulley (e.g. fit in an 8-bit offset instead
22//      // of needing a 32-bit offset)
23//      imported_memories: [VMMemoryImport; module.num_imported_memories],
24//      memories: [*mut VMMemoryDefinition; module.num_defined_memories],
25//      owned_memories: [VMMemoryDefinition; module.num_owned_memories],
26//      imported_functions: [VMFunctionImport; module.num_imported_functions],
27//      imported_tables: [VMTableImport; module.num_imported_tables],
28//      imported_globals: [VMGlobalImport; module.num_imported_globals],
29//      imported_tags: [VMTagImport; module.num_imported_tags],
30//      tables: [VMTableDefinition; module.num_defined_tables],
31//      globals: [VMGlobalDefinition; module.num_defined_globals],
32//      tags: [VMTagDefinition; module.num_defined_tags],
33//      func_refs: [VMFuncRef; module.num_escaped_funcs],
34// }
35
36use crate::{
37    DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, FuncIndex,
38    FuncRefIndex, GlobalIndex, MemoryIndex, Module, OwnedMemoryIndex, TableIndex, TagIndex,
39};
40use cranelift_entity::packed_option::ReservedValue;
41
42#[cfg(target_pointer_width = "32")]
43fn cast_to_u32(sz: usize) -> u32 {
44    u32::try_from(sz).unwrap()
45}
46#[cfg(target_pointer_width = "64")]
47fn cast_to_u32(sz: usize) -> u32 {
48    u32::try_from(sz).expect("overflow in cast from usize to u32")
49}
50
51/// Align an offset used in this module to a specific byte-width by rounding up
52#[inline]
53fn align(offset: u32, width: u32) -> u32 {
54    (offset + (width - 1)) / width * width
55}
56
57/// This class computes offsets to fields within `VMContext` and other
58/// related structs that JIT code accesses directly.
59#[derive(Debug, Clone, Copy)]
60pub struct VMOffsets<P> {
61    /// The size in bytes of a pointer on the target.
62    pub ptr: P,
63    /// The number of imported functions in the module.
64    pub num_imported_functions: u32,
65    /// The number of imported tables in the module.
66    pub num_imported_tables: u32,
67    /// The number of imported memories in the module.
68    pub num_imported_memories: u32,
69    /// The number of imported globals in the module.
70    pub num_imported_globals: u32,
71    /// The number of imported tags in the module.
72    pub num_imported_tags: u32,
73    /// The number of defined tables in the module.
74    pub num_defined_tables: u32,
75    /// The number of defined memories in the module.
76    pub num_defined_memories: u32,
77    /// The number of memories owned by the module instance.
78    pub num_owned_memories: u32,
79    /// The number of defined globals in the module.
80    pub num_defined_globals: u32,
81    /// The number of defined tags in the module.
82    pub num_defined_tags: u32,
83    /// The number of escaped functions in the module, the size of the func_refs
84    /// array.
85    pub num_escaped_funcs: u32,
86
87    // precalculated offsets of various member fields
88    imported_functions: u32,
89    imported_tables: u32,
90    imported_memories: u32,
91    imported_globals: u32,
92    imported_tags: u32,
93    defined_tables: u32,
94    defined_memories: u32,
95    owned_memories: u32,
96    defined_globals: u32,
97    defined_tags: u32,
98    defined_func_refs: u32,
99    size: u32,
100}
101
102/// Trait used for the `ptr` representation of the field of `VMOffsets`
103pub trait PtrSize {
104    /// Returns the pointer size, in bytes, for the target.
105    fn size(&self) -> u8;
106
107    /// The offset of the `VMContext::store_context` field
108    fn vmcontext_store_context(&self) -> u8 {
109        u8::try_from(align(
110            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
111            u32::from(self.size()),
112        ))
113        .unwrap()
114    }
115
116    /// The offset of the `VMContext::builtin_functions` field
117    fn vmcontext_builtin_functions(&self) -> u8 {
118        self.vmcontext_store_context() + self.size()
119    }
120
121    /// The offset of the `array_call` field.
122    #[inline]
123    fn vm_func_ref_array_call(&self) -> u8 {
124        0 * self.size()
125    }
126
127    /// The offset of the `wasm_call` field.
128    #[inline]
129    fn vm_func_ref_wasm_call(&self) -> u8 {
130        1 * self.size()
131    }
132
133    /// The offset of the `type_index` field.
134    #[inline]
135    fn vm_func_ref_type_index(&self) -> u8 {
136        2 * self.size()
137    }
138
139    /// The offset of the `vmctx` field.
140    #[inline]
141    fn vm_func_ref_vmctx(&self) -> u8 {
142        3 * self.size()
143    }
144
145    /// Return the size of `VMFuncRef`.
146    #[inline]
147    fn size_of_vm_func_ref(&self) -> u8 {
148        4 * self.size()
149    }
150
151    /// Return the size of `VMGlobalDefinition`; this is the size of the largest value type (i.e. a
152    /// V128).
153    #[inline]
154    fn size_of_vmglobal_definition(&self) -> u8 {
155        16
156    }
157
158    /// Return the size of `VMTagDefinition`.
159    #[inline]
160    fn size_of_vmtag_definition(&self) -> u8 {
161        4
162    }
163
164    /// This is the size of the largest value type (i.e. a V128).
165    #[inline]
166    fn maximum_value_size(&self) -> u8 {
167        self.size_of_vmglobal_definition()
168    }
169
170    // Offsets within `VMStoreContext`
171
172    /// Return the offset of the `fuel_consumed` field of `VMStoreContext`
173    #[inline]
174    fn vmstore_context_fuel_consumed(&self) -> u8 {
175        0
176    }
177
178    /// Return the offset of the `epoch_deadline` field of `VMStoreContext`
179    #[inline]
180    fn vmstore_context_epoch_deadline(&self) -> u8 {
181        self.vmstore_context_fuel_consumed() + 8
182    }
183
184    /// Return the offset of the `stack_limit` field of `VMStoreContext`
185    #[inline]
186    fn vmstore_context_stack_limit(&self) -> u8 {
187        self.vmstore_context_epoch_deadline() + 8
188    }
189
190    /// Return the offset of the `gc_heap` field of `VMStoreContext`.
191    #[inline]
192    fn vmstore_context_gc_heap(&self) -> u8 {
193        self.vmstore_context_stack_limit() + self.size()
194    }
195
196    /// Return the offset of the `gc_heap.base` field within a `VMStoreContext`.
197    fn vmstore_context_gc_heap_base(&self) -> u8 {
198        let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_base();
199        debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
200        offset
201    }
202
203    /// Return the offset of the `gc_heap.current_length` field within a `VMStoreContext`.
204    fn vmstore_context_gc_heap_current_length(&self) -> u8 {
205        let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_current_length();
206        debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
207        offset
208    }
209
210    /// Return the offset of the `last_wasm_exit_trampoline_fp` field
211    /// of `VMStoreContext`.
212    fn vmstore_context_last_wasm_exit_trampoline_fp(&self) -> u8 {
213        self.vmstore_context_gc_heap() + self.size_of_vmmemory_definition()
214    }
215
216    /// Return the offset of the `last_wasm_exit_pc` field of `VMStoreContext`.
217    fn vmstore_context_last_wasm_exit_pc(&self) -> u8 {
218        self.vmstore_context_last_wasm_exit_trampoline_fp() + self.size()
219    }
220
221    /// Return the offset of the `last_wasm_entry_sp` field of `VMStoreContext`.
222    fn vmstore_context_last_wasm_entry_sp(&self) -> u8 {
223        self.vmstore_context_last_wasm_exit_pc() + self.size()
224    }
225
226    /// Return the offset of the `last_wasm_entry_fp` field of `VMStoreContext`.
227    fn vmstore_context_last_wasm_entry_fp(&self) -> u8 {
228        self.vmstore_context_last_wasm_entry_sp() + self.size()
229    }
230
231    /// Return the offset of the `last_wasm_entry_trap_handler` field of `VMStoreContext`.
232    fn vmstore_context_last_wasm_entry_trap_handler(&self) -> u8 {
233        self.vmstore_context_last_wasm_entry_fp() + self.size()
234    }
235
236    /// Return the offset of the `stack_chain` field of `VMStoreContext`.
237    fn vmstore_context_stack_chain(&self) -> u8 {
238        self.vmstore_context_last_wasm_entry_trap_handler() + self.size()
239    }
240
241    /// Return the offset of the `stack_chain` field of `VMStoreContext`.
242    fn vmstore_context_store_data(&self) -> u8 {
243        self.vmstore_context_stack_chain() + self.size_of_vmstack_chain()
244    }
245
246    // Offsets within `VMMemoryDefinition`
247
248    /// The offset of the `base` field.
249    #[inline]
250    fn vmmemory_definition_base(&self) -> u8 {
251        0 * self.size()
252    }
253
254    /// The offset of the `current_length` field.
255    #[inline]
256    fn vmmemory_definition_current_length(&self) -> u8 {
257        1 * self.size()
258    }
259
260    /// Return the size of `VMMemoryDefinition`.
261    #[inline]
262    fn size_of_vmmemory_definition(&self) -> u8 {
263        2 * self.size()
264    }
265
266    /// Return the size of `*mut VMMemoryDefinition`.
267    #[inline]
268    fn size_of_vmmemory_pointer(&self) -> u8 {
269        self.size()
270    }
271
272    // Offsets within `VMArrayCallHostFuncContext`.
273
274    /// Return the offset of `VMArrayCallHostFuncContext::func_ref`.
275    fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
276        u8::try_from(align(
277            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
278            u32::from(self.size()),
279        ))
280        .unwrap()
281    }
282
283    /// Return the size of `VMStackChain`.
284    fn size_of_vmstack_chain(&self) -> u8 {
285        2 * self.size()
286    }
287
288    // Offsets within `VMStackLimits`
289
290    /// Return the offset of `VMStackLimits::stack_limit`.
291    fn vmstack_limits_stack_limit(&self) -> u8 {
292        0
293    }
294
295    /// Return the offset of `VMStackLimits::last_wasm_entry_fp`.
296    fn vmstack_limits_last_wasm_entry_fp(&self) -> u8 {
297        self.size()
298    }
299
300    // Offsets within `VMHostArray`
301
302    /// Return the offset of `VMHostArray::length`.
303    fn vmhostarray_length(&self) -> u8 {
304        0
305    }
306
307    /// Return the offset of `VMHostArray::capacity`.
308    fn vmhostarray_capacity(&self) -> u8 {
309        4
310    }
311
312    /// Return the offset of `VMHostArray::data`.
313    fn vmhostarray_data(&self) -> u8 {
314        8
315    }
316
317    /// Return the size of `VMHostArray`.
318    fn size_of_vmhostarray(&self) -> u8 {
319        8 + self.size()
320    }
321
322    // Offsets within `VMCommonStackInformation`
323
324    /// Return the offset of `VMCommonStackInformation::limits`.
325    fn vmcommon_stack_information_limits(&self) -> u8 {
326        0 * self.size()
327    }
328
329    /// Return the offset of `VMCommonStackInformation::state`.
330    fn vmcommon_stack_information_state(&self) -> u8 {
331        2 * self.size()
332    }
333
334    /// Return the offset of `VMCommonStackInformation::handlers`.
335    fn vmcommon_stack_information_handlers(&self) -> u8 {
336        u8::try_from(align(
337            self.vmcommon_stack_information_state() as u32 + 4,
338            u32::from(self.size()),
339        ))
340        .unwrap()
341    }
342
343    /// Return the offset of `VMCommonStackInformation::first_switch_handler_index`.
344    fn vmcommon_stack_information_first_switch_handler_index(&self) -> u8 {
345        self.vmcommon_stack_information_handlers() + self.size_of_vmhostarray()
346    }
347
348    /// Return the size of `VMCommonStackInformation`.
349    fn size_of_vmcommon_stack_information(&self) -> u8 {
350        u8::try_from(align(
351            self.vmcommon_stack_information_first_switch_handler_index() as u32 + 4,
352            u32::from(self.size()),
353        ))
354        .unwrap()
355    }
356
357    // Offsets within `VMContObj`
358
359    /// Return the offset of `VMContObj::contref`
360    fn vmcontobj_contref(&self) -> u8 {
361        0
362    }
363
364    /// Return the offset of `VMContObj::revision`
365    fn vmcontobj_revision(&self) -> u8 {
366        self.size()
367    }
368
369    /// Return the size of `VMContObj`.
370    fn size_of_vmcontobj(&self) -> u8 {
371        u8::try_from(align(
372            u32::from(self.vmcontobj_revision())
373                + u32::try_from(core::mem::size_of::<usize>()).unwrap(),
374            u32::from(self.size()),
375        ))
376        .unwrap()
377    }
378
379    // Offsets within `VMContRef`
380
381    /// Return the offset of `VMContRef::common_stack_information`.
382    fn vmcontref_common_stack_information(&self) -> u8 {
383        0 * self.size()
384    }
385
386    /// Return the offset of `VMContRef::parent_chain`.
387    fn vmcontref_parent_chain(&self) -> u8 {
388        u8::try_from(align(
389            (self.vmcontref_common_stack_information() + self.size_of_vmcommon_stack_information())
390                as u32,
391            u32::from(self.size()),
392        ))
393        .unwrap()
394    }
395
396    /// Return the offset of `VMContRef::last_ancestor`.
397    fn vmcontref_last_ancestor(&self) -> u8 {
398        self.vmcontref_parent_chain() + 2 * self.size()
399    }
400
401    /// Return the offset of `VMContRef::revision`.
402    fn vmcontref_revision(&self) -> u8 {
403        self.vmcontref_last_ancestor() + self.size()
404    }
405
406    /// Return the offset of `VMContRef::stack`.
407    fn vmcontref_stack(&self) -> u8 {
408        self.vmcontref_revision() + self.size()
409    }
410
411    /// Return the offset of `VMContRef::args`.
412    fn vmcontref_args(&self) -> u8 {
413        self.vmcontref_stack() + 3 * self.size()
414    }
415
416    /// Return the offset of `VMContRef::values`.
417    fn vmcontref_values(&self) -> u8 {
418        self.vmcontref_args() + self.size_of_vmhostarray()
419    }
420
421    /// Return the offset to the `magic` value in this `VMContext`.
422    #[inline]
423    fn vmctx_magic(&self) -> u8 {
424        // This is required by the implementation of `VMContext::instance` and
425        // `VMContext::instance_mut`. If this value changes then those locations
426        // need to be updated.
427        0
428    }
429
430    /// Return the offset to the `VMStoreContext` structure
431    #[inline]
432    fn vmctx_store_context(&self) -> u8 {
433        self.vmctx_magic() + self.size()
434    }
435
436    /// Return the offset to the `VMBuiltinFunctionsArray` structure
437    #[inline]
438    fn vmctx_builtin_functions(&self) -> u8 {
439        self.vmctx_store_context() + self.size()
440    }
441
442    /// Return the offset to the `*const AtomicU64` epoch-counter
443    /// pointer.
444    #[inline]
445    fn vmctx_epoch_ptr(&self) -> u8 {
446        self.vmctx_builtin_functions() + self.size()
447    }
448
449    /// Return the offset to the `*mut T` collector-specific data.
450    ///
451    /// This is a pointer that different collectors can use however they see
452    /// fit.
453    #[inline]
454    fn vmctx_gc_heap_data(&self) -> u8 {
455        self.vmctx_epoch_ptr() + self.size()
456    }
457
458    /// The offset of the `type_ids` array pointer.
459    #[inline]
460    fn vmctx_type_ids_array(&self) -> u8 {
461        self.vmctx_gc_heap_data() + self.size()
462    }
463
464    /// The end of statically known offsets in `VMContext`.
465    ///
466    /// Data after this is dynamically sized.
467    #[inline]
468    fn vmctx_dynamic_data_start(&self) -> u8 {
469        self.vmctx_type_ids_array() + self.size()
470    }
471}
472
473/// Type representing the size of a pointer for the current compilation host
474#[derive(Clone, Copy)]
475pub struct HostPtr;
476
477impl PtrSize for HostPtr {
478    #[inline]
479    fn size(&self) -> u8 {
480        core::mem::size_of::<usize>() as u8
481    }
482}
483
484impl PtrSize for u8 {
485    #[inline]
486    fn size(&self) -> u8 {
487        *self
488    }
489}
490
491/// Used to construct a `VMOffsets`
492#[derive(Debug, Clone, Copy)]
493pub struct VMOffsetsFields<P> {
494    /// The size in bytes of a pointer on the target.
495    pub ptr: P,
496    /// The number of imported functions in the module.
497    pub num_imported_functions: u32,
498    /// The number of imported tables in the module.
499    pub num_imported_tables: u32,
500    /// The number of imported memories in the module.
501    pub num_imported_memories: u32,
502    /// The number of imported globals in the module.
503    pub num_imported_globals: u32,
504    /// The number of imported tags in the module.
505    pub num_imported_tags: u32,
506    /// The number of defined tables in the module.
507    pub num_defined_tables: u32,
508    /// The number of defined memories in the module.
509    pub num_defined_memories: u32,
510    /// The number of memories owned by the module instance.
511    pub num_owned_memories: u32,
512    /// The number of defined globals in the module.
513    pub num_defined_globals: u32,
514    /// The number of defined tags in the module.
515    pub num_defined_tags: u32,
516    /// The number of escaped functions in the module, the size of the function
517    /// references array.
518    pub num_escaped_funcs: u32,
519}
520
521impl<P: PtrSize> VMOffsets<P> {
522    /// Return a new `VMOffsets` instance, for a given pointer size.
523    pub fn new(ptr: P, module: &Module) -> Self {
524        let num_owned_memories = module
525            .memories
526            .iter()
527            .skip(module.num_imported_memories)
528            .filter(|p| !p.1.shared)
529            .count()
530            .try_into()
531            .unwrap();
532        VMOffsets::from(VMOffsetsFields {
533            ptr,
534            num_imported_functions: cast_to_u32(module.num_imported_funcs),
535            num_imported_tables: cast_to_u32(module.num_imported_tables),
536            num_imported_memories: cast_to_u32(module.num_imported_memories),
537            num_imported_globals: cast_to_u32(module.num_imported_globals),
538            num_imported_tags: cast_to_u32(module.num_imported_tags),
539            num_defined_tables: cast_to_u32(module.num_defined_tables()),
540            num_defined_memories: cast_to_u32(module.num_defined_memories()),
541            num_owned_memories,
542            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
543            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
544            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
545        })
546    }
547
548    /// Returns the size, in bytes, of the target
549    #[inline]
550    pub fn pointer_size(&self) -> u8 {
551        self.ptr.size()
552    }
553
554    /// Returns an iterator which provides a human readable description and a
555    /// byte size. The iterator returned will iterate over the bytes allocated
556    /// to the entire `VMOffsets` structure to explain where each byte size is
557    /// coming from.
558    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
559        macro_rules! calculate_sizes {
560            ($($name:ident: $desc:tt,)*) => {{
561                let VMOffsets {
562                    // These fields are metadata not talking about specific
563                    // offsets of specific fields.
564                    ptr: _,
565                    num_imported_functions: _,
566                    num_imported_tables: _,
567                    num_imported_memories: _,
568                    num_imported_globals: _,
569                    num_imported_tags: _,
570                    num_defined_tables: _,
571                    num_defined_globals: _,
572                    num_defined_memories: _,
573                    num_defined_tags: _,
574                    num_owned_memories: _,
575                    num_escaped_funcs: _,
576
577                    // used as the initial size below
578                    size,
579
580                    // exhaustively match the rest of the fields with input from
581                    // the macro
582                    $($name,)*
583                } = *self;
584
585                // calculate the size of each field by relying on the inputs to
586                // the macro being in reverse order and determining the size of
587                // the field as the offset from the field to the last field.
588                let mut last = size;
589                $(
590                    assert!($name <= last);
591                    let tmp = $name;
592                    let $name = last - $name;
593                    last = tmp;
594                )*
595                assert_ne!(last, 0);
596                IntoIterator::into_iter([
597                    $(($desc, $name),)*
598                    ("static vmctx data", last),
599                ])
600            }};
601        }
602
603        calculate_sizes! {
604            defined_func_refs: "module functions",
605            defined_tags: "defined tags",
606            defined_globals: "defined globals",
607            defined_tables: "defined tables",
608            imported_tags: "imported tags",
609            imported_globals: "imported globals",
610            imported_tables: "imported tables",
611            imported_functions: "imported functions",
612            owned_memories: "owned memories",
613            defined_memories: "defined memories",
614            imported_memories: "imported memories",
615        }
616    }
617}
618
619impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
620    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
621        let mut ret = Self {
622            ptr: fields.ptr,
623            num_imported_functions: fields.num_imported_functions,
624            num_imported_tables: fields.num_imported_tables,
625            num_imported_memories: fields.num_imported_memories,
626            num_imported_globals: fields.num_imported_globals,
627            num_imported_tags: fields.num_imported_tags,
628            num_defined_tables: fields.num_defined_tables,
629            num_defined_memories: fields.num_defined_memories,
630            num_owned_memories: fields.num_owned_memories,
631            num_defined_globals: fields.num_defined_globals,
632            num_defined_tags: fields.num_defined_tags,
633            num_escaped_funcs: fields.num_escaped_funcs,
634            imported_functions: 0,
635            imported_tables: 0,
636            imported_memories: 0,
637            imported_globals: 0,
638            imported_tags: 0,
639            defined_tables: 0,
640            defined_memories: 0,
641            owned_memories: 0,
642            defined_globals: 0,
643            defined_tags: 0,
644            defined_func_refs: 0,
645            size: 0,
646        };
647
648        // Convenience functions for checked addition and multiplication.
649        // As side effect this reduces binary size by using only a single
650        // `#[track_caller]` location for each function instead of one for
651        // each individual invocation.
652        #[inline]
653        fn cadd(count: u32, size: u32) -> u32 {
654            count.checked_add(size).unwrap()
655        }
656
657        #[inline]
658        fn cmul(count: u32, size: u8) -> u32 {
659            count.checked_mul(u32::from(size)).unwrap()
660        }
661
662        let mut next_field_offset = u32::from(ret.ptr.vmctx_dynamic_data_start());
663
664        macro_rules! fields {
665            (size($field:ident) = $size:expr, $($rest:tt)*) => {
666                ret.$field = next_field_offset;
667                next_field_offset = cadd(next_field_offset, u32::from($size));
668                fields!($($rest)*);
669            };
670            (align($align:expr), $($rest:tt)*) => {
671                next_field_offset = align(next_field_offset, $align);
672                fields!($($rest)*);
673            };
674            () => {};
675        }
676
677        fields! {
678            size(imported_memories)
679                = cmul(ret.num_imported_memories, ret.size_of_vmmemory_import()),
680            size(defined_memories)
681                = cmul(ret.num_defined_memories, ret.ptr.size_of_vmmemory_pointer()),
682            size(owned_memories)
683                = cmul(ret.num_owned_memories, ret.ptr.size_of_vmmemory_definition()),
684            size(imported_functions)
685                = cmul(ret.num_imported_functions, ret.size_of_vmfunction_import()),
686            size(imported_tables)
687                = cmul(ret.num_imported_tables, ret.size_of_vmtable_import()),
688            size(imported_globals)
689                = cmul(ret.num_imported_globals, ret.size_of_vmglobal_import()),
690            size(imported_tags)
691                = cmul(ret.num_imported_tags, ret.size_of_vmtag_import()),
692            size(defined_tables)
693                = cmul(ret.num_defined_tables, ret.size_of_vmtable_definition()),
694            align(16),
695            size(defined_globals)
696                = cmul(ret.num_defined_globals, ret.ptr.size_of_vmglobal_definition()),
697            size(defined_tags)
698                = cmul(ret.num_defined_tags, ret.ptr.size_of_vmtag_definition()),
699            size(defined_func_refs) = cmul(
700                ret.num_escaped_funcs,
701                ret.ptr.size_of_vm_func_ref(),
702            ),
703        }
704
705        ret.size = next_field_offset;
706
707        return ret;
708    }
709}
710
711impl<P: PtrSize> VMOffsets<P> {
712    /// The offset of the `wasm_call` field.
713    #[inline]
714    pub fn vmfunction_import_wasm_call(&self) -> u8 {
715        0 * self.pointer_size()
716    }
717
718    /// The offset of the `array_call` field.
719    #[inline]
720    pub fn vmfunction_import_array_call(&self) -> u8 {
721        1 * self.pointer_size()
722    }
723
724    /// The offset of the `vmctx` field.
725    #[inline]
726    pub fn vmfunction_import_vmctx(&self) -> u8 {
727        2 * self.pointer_size()
728    }
729
730    /// Return the size of `VMFunctionImport`.
731    #[inline]
732    pub fn size_of_vmfunction_import(&self) -> u8 {
733        3 * self.pointer_size()
734    }
735}
736
737/// Offsets for `*const VMFunctionBody`.
738impl<P: PtrSize> VMOffsets<P> {
739    /// The size of the `current_elements` field.
740    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
741        1 * self.pointer_size()
742    }
743}
744
745/// Offsets for `VMTableImport`.
746impl<P: PtrSize> VMOffsets<P> {
747    /// The offset of the `from` field.
748    #[inline]
749    pub fn vmtable_import_from(&self) -> u8 {
750        0 * self.pointer_size()
751    }
752
753    /// The offset of the `vmctx` field.
754    #[inline]
755    pub fn vmtable_import_vmctx(&self) -> u8 {
756        1 * self.pointer_size()
757    }
758
759    /// The offset of the `index` field.
760    #[inline]
761    pub fn vmtable_import_index(&self) -> u8 {
762        2 * self.pointer_size()
763    }
764
765    /// Return the size of `VMTableImport`.
766    #[inline]
767    pub fn size_of_vmtable_import(&self) -> u8 {
768        3 * self.pointer_size()
769    }
770}
771
772/// Offsets for `VMTableDefinition`.
773impl<P: PtrSize> VMOffsets<P> {
774    /// The offset of the `base` field.
775    #[inline]
776    pub fn vmtable_definition_base(&self) -> u8 {
777        0 * self.pointer_size()
778    }
779
780    /// The offset of the `current_elements` field.
781    pub fn vmtable_definition_current_elements(&self) -> u8 {
782        1 * self.pointer_size()
783    }
784
785    /// The size of the `current_elements` field.
786    #[inline]
787    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
788        self.pointer_size()
789    }
790
791    /// Return the size of `VMTableDefinition`.
792    #[inline]
793    pub fn size_of_vmtable_definition(&self) -> u8 {
794        2 * self.pointer_size()
795    }
796}
797
798/// Offsets for `VMMemoryImport`.
799impl<P: PtrSize> VMOffsets<P> {
800    /// The offset of the `from` field.
801    #[inline]
802    pub fn vmmemory_import_from(&self) -> u8 {
803        0 * self.pointer_size()
804    }
805
806    /// The offset of the `vmctx` field.
807    #[inline]
808    pub fn vmmemory_import_vmctx(&self) -> u8 {
809        1 * self.pointer_size()
810    }
811
812    /// The offset of the `index` field.
813    #[inline]
814    pub fn vmmemory_import_index(&self) -> u8 {
815        2 * self.pointer_size()
816    }
817
818    /// Return the size of `VMMemoryImport`.
819    #[inline]
820    pub fn size_of_vmmemory_import(&self) -> u8 {
821        3 * self.pointer_size()
822    }
823}
824
825/// Offsets for `VMGlobalImport`.
826impl<P: PtrSize> VMOffsets<P> {
827    /// The offset of the `from` field.
828    #[inline]
829    pub fn vmglobal_import_from(&self) -> u8 {
830        0 * self.pointer_size()
831    }
832
833    /// Return the size of `VMGlobalImport`.
834    #[inline]
835    pub fn size_of_vmglobal_import(&self) -> u8 {
836        // `VMGlobalImport` has two pointers plus 8 bytes for `VMGlobalKind`
837        2 * self.pointer_size() + 8
838    }
839}
840
841/// Offsets for `VMSharedTypeIndex`.
842impl<P: PtrSize> VMOffsets<P> {
843    /// Return the size of `VMSharedTypeIndex`.
844    #[inline]
845    pub fn size_of_vmshared_type_index(&self) -> u8 {
846        4
847    }
848}
849
850/// Offsets for `VMTagImport`.
851impl<P: PtrSize> VMOffsets<P> {
852    /// The offset of the `from` field.
853    #[inline]
854    pub fn vmtag_import_from(&self) -> u8 {
855        0 * self.pointer_size()
856    }
857
858    /// The offset of the `vmctx` field.
859    #[inline]
860    pub fn vmtag_import_vmctx(&self) -> u8 {
861        1 * self.pointer_size()
862    }
863
864    /// The offset of the `index` field.
865    #[inline]
866    pub fn vmtag_import_index(&self) -> u8 {
867        2 * self.pointer_size()
868    }
869
870    /// Return the size of `VMTagImport`.
871    #[inline]
872    pub fn size_of_vmtag_import(&self) -> u8 {
873        3 * self.pointer_size()
874    }
875}
876
877/// Offsets for `VMContext`.
878impl<P: PtrSize> VMOffsets<P> {
879    /// The offset of the `tables` array.
880    #[inline]
881    pub fn vmctx_imported_functions_begin(&self) -> u32 {
882        self.imported_functions
883    }
884
885    /// The offset of the `tables` array.
886    #[inline]
887    pub fn vmctx_imported_tables_begin(&self) -> u32 {
888        self.imported_tables
889    }
890
891    /// The offset of the `memories` array.
892    #[inline]
893    pub fn vmctx_imported_memories_begin(&self) -> u32 {
894        self.imported_memories
895    }
896
897    /// The offset of the `globals` array.
898    #[inline]
899    pub fn vmctx_imported_globals_begin(&self) -> u32 {
900        self.imported_globals
901    }
902
903    /// The offset of the `tags` array.
904    #[inline]
905    pub fn vmctx_imported_tags_begin(&self) -> u32 {
906        self.imported_tags
907    }
908
909    /// The offset of the `tables` array.
910    #[inline]
911    pub fn vmctx_tables_begin(&self) -> u32 {
912        self.defined_tables
913    }
914
915    /// The offset of the `memories` array.
916    #[inline]
917    pub fn vmctx_memories_begin(&self) -> u32 {
918        self.defined_memories
919    }
920
921    /// The offset of the `owned_memories` array.
922    #[inline]
923    pub fn vmctx_owned_memories_begin(&self) -> u32 {
924        self.owned_memories
925    }
926
927    /// The offset of the `globals` array.
928    #[inline]
929    pub fn vmctx_globals_begin(&self) -> u32 {
930        self.defined_globals
931    }
932
933    /// The offset of the `tags` array.
934    #[inline]
935    pub fn vmctx_tags_begin(&self) -> u32 {
936        self.defined_tags
937    }
938
939    /// The offset of the `func_refs` array.
940    #[inline]
941    pub fn vmctx_func_refs_begin(&self) -> u32 {
942        self.defined_func_refs
943    }
944
945    /// Return the size of the `VMContext` allocation.
946    #[inline]
947    pub fn size_of_vmctx(&self) -> u32 {
948        self.size
949    }
950
951    /// Return the offset to `VMFunctionImport` index `index`.
952    #[inline]
953    pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
954        assert!(index.as_u32() < self.num_imported_functions);
955        self.vmctx_imported_functions_begin()
956            + index.as_u32() * u32::from(self.size_of_vmfunction_import())
957    }
958
959    /// Return the offset to `VMTable` index `index`.
960    #[inline]
961    pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
962        assert!(index.as_u32() < self.num_imported_tables);
963        self.vmctx_imported_tables_begin()
964            + index.as_u32() * u32::from(self.size_of_vmtable_import())
965    }
966
967    /// Return the offset to `VMMemoryImport` index `index`.
968    #[inline]
969    pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
970        assert!(index.as_u32() < self.num_imported_memories);
971        self.vmctx_imported_memories_begin()
972            + index.as_u32() * u32::from(self.size_of_vmmemory_import())
973    }
974
975    /// Return the offset to `VMGlobalImport` index `index`.
976    #[inline]
977    pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
978        assert!(index.as_u32() < self.num_imported_globals);
979        self.vmctx_imported_globals_begin()
980            + index.as_u32() * u32::from(self.size_of_vmglobal_import())
981    }
982
983    /// Return the offset to `VMTagImport` index `index`.
984    #[inline]
985    pub fn vmctx_vmtag_import(&self, index: TagIndex) -> u32 {
986        assert!(index.as_u32() < self.num_imported_tags);
987        self.vmctx_imported_tags_begin() + index.as_u32() * u32::from(self.size_of_vmtag_import())
988    }
989
990    /// Return the offset to `VMTableDefinition` index `index`.
991    #[inline]
992    pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
993        assert!(index.as_u32() < self.num_defined_tables);
994        self.vmctx_tables_begin() + index.as_u32() * u32::from(self.size_of_vmtable_definition())
995    }
996
997    /// Return the offset to the `*mut VMMemoryDefinition` at index `index`.
998    #[inline]
999    pub fn vmctx_vmmemory_pointer(&self, index: DefinedMemoryIndex) -> u32 {
1000        assert!(index.as_u32() < self.num_defined_memories);
1001        self.vmctx_memories_begin()
1002            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_pointer())
1003    }
1004
1005    /// Return the offset to the owned `VMMemoryDefinition` at index `index`.
1006    #[inline]
1007    pub fn vmctx_vmmemory_definition(&self, index: OwnedMemoryIndex) -> u32 {
1008        assert!(index.as_u32() < self.num_owned_memories);
1009        self.vmctx_owned_memories_begin()
1010            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_definition())
1011    }
1012
1013    /// Return the offset to the `VMGlobalDefinition` index `index`.
1014    #[inline]
1015    pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
1016        assert!(index.as_u32() < self.num_defined_globals);
1017        self.vmctx_globals_begin()
1018            + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
1019    }
1020
1021    /// Return the offset to the `VMTagDefinition` index `index`.
1022    #[inline]
1023    pub fn vmctx_vmtag_definition(&self, index: DefinedTagIndex) -> u32 {
1024        assert!(index.as_u32() < self.num_defined_tags);
1025        self.vmctx_tags_begin() + index.as_u32() * u32::from(self.ptr.size_of_vmtag_definition())
1026    }
1027
1028    /// Return the offset to the `VMFuncRef` for the given function
1029    /// index (either imported or defined).
1030    #[inline]
1031    pub fn vmctx_func_ref(&self, index: FuncRefIndex) -> u32 {
1032        assert!(!index.is_reserved_value());
1033        assert!(index.as_u32() < self.num_escaped_funcs);
1034        self.vmctx_func_refs_begin() + index.as_u32() * u32::from(self.ptr.size_of_vm_func_ref())
1035    }
1036
1037    /// Return the offset to the `wasm_call` field in `*const VMFunctionBody` index `index`.
1038    #[inline]
1039    pub fn vmctx_vmfunction_import_wasm_call(&self, index: FuncIndex) -> u32 {
1040        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_wasm_call())
1041    }
1042
1043    /// Return the offset to the `array_call` field in `*const VMFunctionBody` index `index`.
1044    #[inline]
1045    pub fn vmctx_vmfunction_import_array_call(&self, index: FuncIndex) -> u32 {
1046        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_array_call())
1047    }
1048
1049    /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
1050    #[inline]
1051    pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
1052        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
1053    }
1054
1055    /// Return the offset to the `from` field in the imported `VMTable` at index
1056    /// `index`.
1057    #[inline]
1058    pub fn vmctx_vmtable_from(&self, index: TableIndex) -> u32 {
1059        self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_from())
1060    }
1061
1062    /// Return the offset to the `base` field in `VMTableDefinition` index `index`.
1063    #[inline]
1064    pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
1065        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
1066    }
1067
1068    /// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
1069    #[inline]
1070    pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
1071        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
1072    }
1073
1074    /// Return the offset to the `from` field in `VMMemoryImport` index `index`.
1075    #[inline]
1076    pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 {
1077        self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_from())
1078    }
1079
1080    /// Return the offset to the `base` field in `VMMemoryDefinition` index `index`.
1081    #[inline]
1082    pub fn vmctx_vmmemory_definition_base(&self, index: OwnedMemoryIndex) -> u32 {
1083        self.vmctx_vmmemory_definition(index) + u32::from(self.ptr.vmmemory_definition_base())
1084    }
1085
1086    /// Return the offset to the `current_length` field in `VMMemoryDefinition` index `index`.
1087    #[inline]
1088    pub fn vmctx_vmmemory_definition_current_length(&self, index: OwnedMemoryIndex) -> u32 {
1089        self.vmctx_vmmemory_definition(index)
1090            + u32::from(self.ptr.vmmemory_definition_current_length())
1091    }
1092
1093    /// Return the offset to the `from` field in `VMGlobalImport` index `index`.
1094    #[inline]
1095    pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 {
1096        self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_from())
1097    }
1098
1099    /// Return the offset to the `from` field in `VMTagImport` index `index`.
1100    #[inline]
1101    pub fn vmctx_vmtag_import_from(&self, index: TagIndex) -> u32 {
1102        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_from())
1103    }
1104
1105    /// Return the offset to the `vmctx` field in `VMTagImport` index `index`.
1106    #[inline]
1107    pub fn vmctx_vmtag_import_vmctx(&self, index: TagIndex) -> u32 {
1108        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_vmctx())
1109    }
1110
1111    /// Return the offset to the `index` field in `VMTagImport` index `index`.
1112    #[inline]
1113    pub fn vmctx_vmtag_import_index(&self, index: TagIndex) -> u32 {
1114        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_index())
1115    }
1116}
1117
1118/// Offsets for `VMGcHeader`.
1119impl<P: PtrSize> VMOffsets<P> {
1120    /// Return the offset for the `VMGcHeader::kind` field.
1121    #[inline]
1122    pub fn vm_gc_header_kind(&self) -> u32 {
1123        0
1124    }
1125
1126    /// Return the offset for the `VMGcHeader`'s reserved bits.
1127    #[inline]
1128    pub fn vm_gc_header_reserved_bits(&self) -> u32 {
1129        // NB: The reserved bits are the unused `VMGcKind` bits.
1130        self.vm_gc_header_kind()
1131    }
1132
1133    /// Return the offset for the `VMGcHeader::ty` field.
1134    #[inline]
1135    pub fn vm_gc_header_ty(&self) -> u32 {
1136        self.vm_gc_header_kind() + 4
1137    }
1138}
1139
1140/// Offsets for `VMDrcHeader`.
1141///
1142/// Should only be used when the DRC collector is enabled.
1143impl<P: PtrSize> VMOffsets<P> {
1144    /// Return the offset for `VMDrcHeader::ref_count`.
1145    #[inline]
1146    pub fn vm_drc_header_ref_count(&self) -> u32 {
1147        8
1148    }
1149
1150    /// Return the offset for `VMDrcHeader::next_over_approximated_stack_root`.
1151    #[inline]
1152    pub fn vm_drc_header_next_over_approximated_stack_root(&self) -> u32 {
1153        self.vm_drc_header_ref_count() + 8
1154    }
1155}
1156
1157/// Magic value for core Wasm VM contexts.
1158///
1159/// This is stored at the start of all `VMContext` structures.
1160pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
1161
1162/// Equivalent of `VMCONTEXT_MAGIC` except for array-call host functions.
1163///
1164/// This is stored at the start of all `VMArrayCallHostFuncContext` structures
1165/// and double-checked on `VMArrayCallHostFuncContext::from_opaque`.
1166pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
1167
1168#[cfg(test)]
1169mod tests {
1170    use crate::vmoffsets::align;
1171
1172    #[test]
1173    fn alignment() {
1174        fn is_aligned(x: u32) -> bool {
1175            x % 16 == 0
1176        }
1177        assert!(is_aligned(align(0, 16)));
1178        assert!(is_aligned(align(32, 16)));
1179        assert!(is_aligned(align(33, 16)));
1180        assert!(is_aligned(align(31, 16)));
1181    }
1182}