Skip to main content

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