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