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