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