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