use crate::{
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, FuncIndex,
FuncRefIndex, GlobalIndex, MemoryIndex, Module, ModuleInternedTypeIndex, OwnedMemoryIndex,
RuntimeDataIndex, TableIndex, TagIndex,
};
pub const NUM_COMPONENT_CONTEXT_SLOTS: usize = 2;
#[cfg(target_pointer_width = "32")]
fn cast_to_u32(sz: usize) -> u32 {
u32::try_from(sz).unwrap()
}
#[cfg(target_pointer_width = "64")]
fn cast_to_u32(sz: usize) -> u32 {
u32::try_from(sz).expect("overflow in cast from usize to u32")
}
#[inline]
fn align(offset: u32, width: u32) -> u32 {
(offset + (width - 1)) / width * width
}
macro_rules! define_vm_type_offsets {
(@size ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@size ($p) $inner) };
(@size ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
(@size ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
(@size ($p:expr) AtomicUsize) => { u32::from($p) };
(@size ($p:expr) usize) => { u32::from($p) };
(@size ($p:expr) * mut $g:ty) => { u32::from($p) };
(@size ($p:expr) i64) => { 8u32 };
(@size ($p:expr) u64) => { 8u32 };
(@size ($p:expr) u32) => { 4u32 };
(@size ($p:expr) NonZeroU32) => { 4u32 };
(@size ($p:expr) Option < VMGcRef >) => { 4u32 };
(@size ($p:expr) [u8; 16]) => { 16u32 };
(@size ($p:expr) [u32; $n:expr]) => { 4u32 * u32::try_from($n).unwrap() };
(@size ($p:expr) VMSharedTypeIndex) => { u32::from(($p).size_of_vmshared_type_index()) };
(@size ($p:expr) DefinedTableIndex) => { 4u32 };
(@size ($p:expr) DefinedMemoryIndex) => { 4u32 };
(@size ($p:expr) DefinedTagIndex) => { 4u32 };
(@size ($p:expr) VMGlobalKind) => { 8u32 };
(@size ($p:expr) Range < *mut u8 >) => { 2u32 * u32::from($p) };
(@size ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().size()) };
(@size ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().size()) };
(@size ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().size()) };
(@size ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().size()) };
(@size ($p:expr) VMCommonStackInformation) => {
u32::from(($p).vm_common_stack_information().size())
};
(@size ($p:expr) VMStackChain) => { u32::from(($p).size_of_vmstack_chain()) };
(@size ($p:expr) VMStackState) => { 4u32 };
(@size ($p:expr) VMContinuationStack) => { 3u32 * u32::from($p) };
(@size ($p:expr) PhantomPinned) => { 0u32 };
(@align ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@align ($p) $inner) };
(@align ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
(@align ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
(@align ($p:expr) AtomicUsize) => { u32::from($p) };
(@align ($p:expr) usize) => { u32::from($p) };
(@align ($p:expr) * mut $g:ty) => { u32::from($p) };
(@align ($p:expr) i64) => { 8u32 };
(@align ($p:expr) u64) => { 8u32 };
(@align ($p:expr) u32) => { 4u32 };
(@align ($p:expr) NonZeroU32) => { 4u32 };
(@align ($p:expr) Option < VMGcRef >) => { 4u32 };
(@align ($p:expr) [u8; 16]) => { 16u32 };
(@align ($p:expr) [u32; $n:expr]) => { 4u32 };
(@align ($p:expr) VMSharedTypeIndex) => { u32::from(($p).align_of_vmshared_type_index()) };
(@align ($p:expr) DefinedTableIndex) => { 4u32 };
(@align ($p:expr) DefinedMemoryIndex) => { 4u32 };
(@align ($p:expr) DefinedTagIndex) => { 4u32 };
(@align ($p:expr) VMGlobalKind) => { 4u32 };
(@align ($p:expr) Range < *mut u8 >) => { u32::from($p) };
(@align ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().align()) };
(@align ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().align()) };
(@align ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().align()) };
(@align ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().align()) };
(@align ($p:expr) VMCommonStackInformation) => {
u32::from(($p).vm_common_stack_information().align())
};
(@align ($p:expr) VMStackChain) => { u32::from($p) };
(@align ($p:expr) VMStackState) => { 4u32 };
(@align ($p:expr) VMContinuationStack) => { u32::from($p) };
(@align ($p:expr) PhantomPinned) => { 1u32 };
(@repr_align C) => { 1u32 };
(@repr_align transparent) => { 1u32 };
(@repr_align C, align($n:literal)) => {{ let align: u32 = $n; align }};
(@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )) => {};
(@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )
[ $fname:ident : $($fty:tt)* ]
$($rest:tt)*
) => {
#[doc = concat!(
"The offset of the `", stringify!($fname),
"` field of `", stringify!($Name), "`."
)]
#[inline]
pub fn $fname(&self) -> u8 {
let $p = self.0.size();
let $o: u32 = 0;
$($prefix)*
let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
let _ = $p;
u8::try_from($o).unwrap()
}
define_vm_type_offsets!(@fields $Name ($p, $o)
prefix(
$($prefix)*
let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
let $o = $o + define_vm_type_offsets!(@size ($p) $($fty)*);
)
$($rest)*
);
};
(@impl $Name:ident [$($repr:tt)*] { $( [ $fname:ident : $($fty:tt)* ] )* }) => {
impl<P: PtrSize> $Name<P> {
define_vm_type_offsets!(@fields $Name (p, o) prefix()
$( [ $fname : $($fty)* ] )*
);
#[doc = concat!("The alignment of the `", stringify!($Name), "` type.")]
#[inline]
pub fn align(&self) -> u8 {
let p = self.0.size();
let a: u32 = define_vm_type_offsets!(@repr_align $($repr)*);
$(
let a = core::cmp::max(
a,
define_vm_type_offsets!(@align (p) $($fty)*),
);
)*
let _ = p;
u8::try_from(a).unwrap()
}
#[doc = concat!("The size of the `", stringify!($Name), "` type.")]
#[inline]
pub fn size(&self) -> u8 {
let p = self.0.size();
let o: u32 = 0;
$(
let o = align(o, define_vm_type_offsets!(@align (p) $($fty)*));
let o = o + define_vm_type_offsets!(@size (p) $($fty)*);
)*
let o = align(o, u32::from(self.align()));
let _ = p;
u8::try_from(o).unwrap()
}
}
};
(@impl $Name:ident $repr:tt { $($groups:tt)* }
$(#[$($attr:tt)*])* $fvis:vis $fname:ident : $($rest:tt)*
) => {
define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [] $($rest)*);
};
(@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] , $($rest:tt)*) => {
define_vm_type_offsets!(@impl $Name $repr { $($groups)* [ $fname : $($fty)* ] } $($rest)*);
};
(@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] $tok:tt $($rest:tt)*) => {
define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [ $($fty)* $tok ] $($rest)*);
};
( $(
$(#[doc = $sdoc:literal])*
$(#[cfg($($scfg:tt)*)])?
$(#[derive($($d:ident),*)])?
#[repr($($repr:tt)*)]
#[snake_name = $snake:ident]
$svis:vis struct $Name:ident {
$($body:tt)*
}
)* ) => {
$(
#[doc = concat!("Offsets of fields within the `", stringify!($Name), "` type.")]
pub struct $Name<P: PtrSize>(pub P);
define_vm_type_offsets!(@impl $Name [$($repr)*] {} $($body)*);
)*
};
}
#[allow(
unused_macro_rules,
reason = "only `VMComponentContext` has a `#[ptr_size_offset]` prefix in its \
`dynamic` section, so those arms go unused for `VMContext`"
)]
macro_rules! define_vmctx_static_offsets {
(@chain $p:ident [ $($prev:tt)* ] [] [ $($dyn:tt)* ]) => {
#[inline]
pub fn end_of_static_fields(&self) -> u8 {
let $p = self.0.size();
let _ = $p;
u8::try_from($($prev)*).unwrap()
}
define_vmctx_static_offsets!(@dyn_chain $p [ $($prev)* ] [ $($dyn)* ]);
};
(@chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ] $dyn:tt) => {
define_vmctx_static_offsets!(
@chain $p
[ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
[ $($rest)* ]
$dyn
);
};
(@chain $p:ident [ $($prev:tt)* ] [
field { $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
] $dyn:tt) => {
#[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
#[inline]
pub fn $fname(&self) -> u8 {
let $p = self.0.size();
let _ = $p;
u8::try_from($($prev)*).unwrap()
}
define_vmctx_static_offsets!(
@chain $p
[ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
[ $($rest)* ]
$dyn
);
};
(@dyn_chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ]) => {
define_vmctx_static_offsets!(
@dyn_chain $p
[ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
[ $($rest)* ]
);
};
(@dyn_chain $p:ident [ $($prev:tt)* ] [
field { #[ptr_size_offset] $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
]) => {
#[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
#[inline]
pub fn $fname(&self) -> u32 {
let $p = self.0.size();
let _ = $p;
$($prev)*
}
define_vmctx_static_offsets!(
@dyn_chain $p
[ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
[ $($rest)* ]
);
};
(@dyn_chain $p:ident [ $($prev:tt)* ] [
array {
#[ptr_size_offset] $(# $fattr:tt)*
$fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
} $($rest:tt)*
]) => {
#[doc = concat!(
"The offset of the `index`th element of the `", stringify!($fname),
"` array.\n\nThis is not bounds checked: the array's length is a \
property of the particular module or component being compiled, which \
is exactly what this wrapper does not know."
)]
#[inline]
pub fn $fname(&self, index: impl crate::VmctxArrayIndex) -> u32 {
let $p = self.0.size();
let _ = $p;
let index = index.vmctx_array_index();
($($prev)*) + vmctx_field_size!(($p) $($fty)*) * index
}
};
(@dyn_chain $p:ident [ $($prev:tt)* ] [ $($rest:tt)* ]) => {};
( $(
{
$Name:ident $snake:ident
static { $($stat:tt)* }
dynamic { $($dyn:tt)* }
}
)* ) => {
$(
#[doc = concat!("Offsets of the statically-positioned fields within the `",
stringify!($Name), "` type.")]
pub struct $Name<P: PtrSize>(pub P);
impl<P: PtrSize> $Name<P> {
define_vmctx_static_offsets!(@chain ptr [ 0u32 ] [ $($stat)* ] [ $($dyn)* ]);
}
)*
};
}
pub mod offsets {
use super::{NUM_COMPONENT_CONTEXT_SLOTS, PtrSize, align};
for_each_vm_type!(define_vm_type_offsets);
for_each_vmctx_type!(define_vmctx_static_offsets);
}
impl<P: PtrSize> offsets::VMStoreContext<P> {
pub fn gc_heap_base(&self) -> u8 {
let offset = self.gc_heap() + self.0.vm_memory_definition().base();
debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
offset
}
pub fn gc_heap_current_length(&self) -> u8 {
let offset = self.gc_heap() + self.0.vm_memory_definition().current_length();
debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
offset
}
}
macro_rules! define_ptr_size_vm_type_accessors {
( $(
$(#[doc = $sdoc:literal])*
$(#[cfg($($scfg:tt)*)])?
$(#[derive($($d:ident),*)])?
#[repr($($repr:tt)*)]
#[snake_name = $snake:ident]
$svis:vis struct $Name:ident {
$($body:tt)*
}
)* ) => {
$(
#[doc = concat!("Get the [`offsets::", stringify!($Name), "`] offsets for this pointer size.")]
#[inline]
fn $snake(&self) -> offsets::$Name<&Self> {
offsets::$Name(self)
}
)*
};
}
macro_rules! define_ptr_size_vmctx_type_accessors {
( $(
{
$Name:ident $snake:ident
static { $($stat:tt)* }
dynamic { $($dyn:tt)* }
}
)* ) => {
$(
#[doc = concat!("Get the [`offsets::", stringify!($Name),
"`] offsets for this pointer size.")]
#[inline]
fn $snake(&self) -> offsets::$Name<&Self> {
offsets::$Name(self)
}
)*
};
}
#[derive(Debug, Clone, Copy)]
pub struct VMOffsets<P> {
pub ptr: P,
pub num_imported_functions: u32,
pub num_imported_tables: u32,
pub num_imported_memories: u32,
pub num_imported_globals: u32,
pub num_imported_tags: u32,
pub num_defined_tables: u32,
pub num_defined_memories: u32,
pub num_owned_memories: u32,
pub num_defined_globals: u32,
pub num_defined_tags: u32,
pub num_escaped_funcs: u32,
pub num_runtime_data: u32,
pub has_startup_func: bool,
imported_memories: u32,
memories: u32,
owned_memories: u32,
imported_functions: u32,
imported_tables: u32,
imported_globals: u32,
imported_tags: u32,
tables: u32,
globals: u32,
tags: u32,
func_refs: u32,
startup_func_ref: u32,
runtime_data_bases: u32,
runtime_data_lengths: u32,
size: u32,
}
pub trait PtrSize {
fn size(&self) -> u8;
for_each_vm_type!(define_ptr_size_vm_type_accessors);
for_each_vmctx_type!(define_ptr_size_vmctx_type_accessors);
#[inline]
fn size_of_vmshared_type_index(&self) -> u8 {
4
}
#[inline]
fn align_of_vmshared_type_index(&self) -> u8 {
4
}
#[inline]
fn maximum_value_size(&self) -> u8 {
self.vm_global_definition().size()
}
#[inline]
fn size_of_vmmemory_pointer(&self) -> u8 {
self.size()
}
fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
u8::try_from(align(
u32::try_from(core::mem::size_of::<u32>()).unwrap(),
u32::from(self.size()),
))
.unwrap()
}
fn size_of_vmstack_chain(&self) -> u8 {
2 * self.size()
}
fn vmcontobj_contref(&self) -> u8 {
0
}
fn vmcontobj_revision(&self) -> u8 {
self.size()
}
fn size_of_vmcontobj(&self) -> u8 {
u8::try_from(align(
u32::from(self.vmcontobj_revision())
+ u32::try_from(core::mem::size_of::<usize>()).unwrap(),
u32::from(self.size()),
))
.unwrap()
}
}
pub trait GetPtrSize {
type Ptr: PtrSize;
fn get_ptr_size(&self) -> &Self::Ptr;
}
impl<P> GetPtrSize for P
where
P: PtrSize,
{
type Ptr = Self;
#[inline]
fn get_ptr_size(&self) -> &Self::Ptr {
self
}
}
#[derive(Clone, Copy)]
pub struct HostPtr;
impl PtrSize for HostPtr {
#[inline]
fn size(&self) -> u8 {
core::mem::size_of::<usize>() as u8
}
}
impl PtrSize for u8 {
#[inline]
fn size(&self) -> u8 {
*self
}
}
impl<P> PtrSize for &'_ P
where
P: PtrSize + ?Sized,
{
#[inline]
fn size(&self) -> u8 {
(**self).size()
}
}
#[derive(Debug, Clone, Copy)]
pub struct VMOffsetsFields<P> {
pub ptr: P,
pub num_imported_functions: u32,
pub num_imported_tables: u32,
pub num_imported_memories: u32,
pub num_imported_globals: u32,
pub num_imported_tags: u32,
pub num_defined_tables: u32,
pub num_defined_memories: u32,
pub num_owned_memories: u32,
pub num_defined_globals: u32,
pub num_defined_tags: u32,
pub num_escaped_funcs: u32,
pub num_runtime_data: u32,
pub has_startup_func: bool,
}
impl<P: PtrSize> VMOffsets<P> {
pub fn new(ptr: P, module: &Module) -> Self {
let num_owned_memories = module
.memories
.iter()
.skip(module.num_imported_memories)
.filter(|p| !p.1.shared)
.count()
.try_into()
.unwrap();
VMOffsets::from(VMOffsetsFields {
ptr,
num_imported_functions: cast_to_u32(module.num_imported_funcs),
num_imported_tables: cast_to_u32(module.num_imported_tables),
num_imported_memories: cast_to_u32(module.num_imported_memories),
num_imported_globals: cast_to_u32(module.num_imported_globals),
num_imported_tags: cast_to_u32(module.num_imported_tags),
num_defined_tables: cast_to_u32(module.num_defined_tables()),
num_defined_memories: cast_to_u32(module.num_defined_memories()),
num_owned_memories,
num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
num_runtime_data: cast_to_u32(module.runtime_data.len()),
has_startup_func: !module.startup.is_none(),
})
}
#[inline]
pub fn pointer_size(&self) -> u8 {
self.ptr.size()
}
pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
macro_rules! calculate_sizes {
($($name:ident: $desc:tt,)*) => {{
let VMOffsets {
ptr: _,
num_imported_functions: _,
num_imported_tables: _,
num_imported_memories: _,
num_imported_globals: _,
num_imported_tags: _,
num_defined_tables: _,
num_defined_globals: _,
num_defined_memories: _,
num_defined_tags: _,
num_owned_memories: _,
num_escaped_funcs: _,
num_runtime_data: _,
has_startup_func: _,
size,
$($name,)*
} = *self;
let mut last = size;
$(
assert!($name <= last);
let tmp = $name;
let $name = last - $name;
last = tmp;
)*
assert_ne!(last, 0);
IntoIterator::into_iter([
$(($desc, $name),)*
("static vmctx data", last),
])
}};
}
calculate_sizes! {
runtime_data_lengths: "runtime data lengths",
runtime_data_bases: "runtime data base pointers",
startup_func_ref: "startup funcref",
func_refs: "module functions",
tags: "defined tags",
globals: "defined globals",
tables: "defined tables",
imported_tags: "imported tags",
imported_globals: "imported globals",
imported_tables: "imported tables",
imported_functions: "imported functions",
owned_memories: "owned memories",
memories: "defined memories",
imported_memories: "imported memories",
}
}
}
impl<P: PtrSize> GetPtrSize for VMOffsets<P> {
type Ptr = P;
#[inline]
fn get_ptr_size(&self) -> &Self::Ptr {
&self.ptr
}
}
impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
let mut ret = Self {
ptr: fields.ptr,
num_imported_functions: fields.num_imported_functions,
num_imported_tables: fields.num_imported_tables,
num_imported_memories: fields.num_imported_memories,
num_imported_globals: fields.num_imported_globals,
num_imported_tags: fields.num_imported_tags,
num_defined_tables: fields.num_defined_tables,
num_defined_memories: fields.num_defined_memories,
num_owned_memories: fields.num_owned_memories,
num_defined_globals: fields.num_defined_globals,
num_defined_tags: fields.num_defined_tags,
num_escaped_funcs: fields.num_escaped_funcs,
num_runtime_data: fields.num_runtime_data,
has_startup_func: fields.has_startup_func,
imported_memories: 0,
memories: 0,
owned_memories: 0,
imported_functions: 0,
imported_tables: 0,
imported_globals: 0,
imported_tags: 0,
tables: 0,
globals: 0,
tags: 0,
func_refs: 0,
startup_func_ref: 0,
runtime_data_bases: 0,
runtime_data_lengths: 0,
size: 0,
};
ret.compute_field_offsets();
ret
}
}
impl<P: PtrSize> VMOffsets<P> {
pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
1 * self.pointer_size()
}
}
impl<P: PtrSize> VMOffsets<P> {
#[inline]
pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
self.pointer_size()
}
}
impl<P: PtrSize> VMOffsets<P> {
#[inline]
pub fn size_of_vmshared_type_index(&self) -> u8 {
self.ptr.size_of_vmshared_type_index()
}
}
impl_vmctx_array_index! {
MemoryIndex,
DefinedMemoryIndex,
OwnedMemoryIndex,
FuncIndex,
TableIndex,
DefinedTableIndex,
GlobalIndex,
DefinedGlobalIndex,
TagIndex,
DefinedTagIndex,
FuncRefIndex,
RuntimeDataIndex,
ModuleInternedTypeIndex,
}
macro_rules! define_vmoffsets_dynamic_offsets {
(@one VMContext $snake:ident { $($dyn:tt)* }) => {
impl<P: PtrSize> VMOffsets<P> {
define_vmctx_dynamic_offsets!(@accessors (self) [ $($dyn)* ]);
define_vmctx_dynamic_offsets!(@compute_fn (self, next) $snake [ $($dyn)* ]);
#[inline]
pub fn size_of_vmctx(&self) -> u32 {
self.size
}
}
};
(@one $other:ident $snake:ident { $($dyn:tt)* }) => {};
( $(
{
$Name:ident $snake:ident
static { $($stat:tt)* }
dynamic { $($dyn:tt)* }
}
)* ) => {
$( define_vmoffsets_dynamic_offsets!(@one $Name $snake { $($dyn)* }); )*
};
}
for_each_vmctx_type!(define_vmoffsets_dynamic_offsets);
impl<P: PtrSize> VMOffsets<P> {
#[inline]
pub fn vm_gc_header_kind(&self) -> u32 {
0
}
#[inline]
pub fn vm_gc_header_reserved_bits(&self) -> u32 {
self.vm_gc_header_kind()
}
#[inline]
pub fn vm_gc_header_ty(&self) -> u32 {
self.vm_gc_header_kind() + 4
}
}
impl<P: PtrSize> VMOffsets<P> {
#[inline]
pub fn vm_drc_header_ref_count(&self) -> u32 {
8
}
#[inline]
pub fn vm_drc_header_next_over_approximated_stack_root(&self) -> u32 {
self.vm_drc_header_ref_count() + 8
}
}
pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
#[cfg(test)]
mod tests {
use crate::vmoffsets::align;
#[test]
fn alignment() {
fn is_aligned(x: u32) -> bool {
x % 16 == 0
}
assert!(is_aligned(align(0, 16)));
assert!(is_aligned(align(32, 16)));
assert!(is_aligned(align(33, 16)));
assert!(is_aligned(align(31, 16)));
}
}
pub const VM_LAZY_THREAD_FORCED: u64 = 1;