wasmtime_environ/
vmoffsets.rs

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