use super::*;
use core::mem;
pub const HEADER_SIZE: u32 = 16;
pub const ALIGN: u32 = 16;
pub const ARRAY_LENGTH_OFFSET: u32 = HEADER_SIZE;
pub const EXCEPTION_TAG_INSTANCE_OFFSET: u32 = HEADER_SIZE;
pub const EXCEPTION_TAG_DEFINED_OFFSET: u32 = HEADER_SIZE + 4;
pub const HEADER_COPIED_BIT: u32 = 1 << 0;
pub const FORWARDING_REF_OFFSET: u32 = HEADER_SIZE;
pub const MIN_OBJECT_SIZE: u32 = FORWARDING_REF_OFFSET + mem::size_of::<u32>() as u32;
#[derive(Default)]
pub struct CopyingTypeLayouts;
#[derive(Clone, Copy, Debug)]
pub enum InlineTraceInfo {
OutOfLine,
Array {
elems_are_gc_refs: bool,
},
Struct {
gc_ref_bitmap: u32,
},
}
impl InlineTraceInfo {
const IS_INLINE_BIT: u32 = 1 << 1;
const IS_ARRAY_BIT: u32 = 1 << 2;
const ELEMS_ARE_GC_REFS_BIT: u32 = 1 << 3;
const STRUCT_BITMAP_SHIFT: u32 = 3;
const STRUCT_BITMAP_BITS: u32 = 23;
pub const NO_GC_EDGES: InlineTraceInfo = InlineTraceInfo::Struct { gc_ref_bitmap: 0 };
pub fn new(layout: &GcLayout) -> Self {
match layout {
GcLayout::Array(a) => Self::array(a),
GcLayout::Struct(s) => Self::r#struct(s),
}
}
pub fn array(layout: &GcArrayLayout) -> Self {
InlineTraceInfo::Array {
elems_are_gc_refs: layout.elems_are_gc_refs,
}
}
pub fn r#struct(layout: &GcStructLayout) -> Self {
let mut bitmap: u32 = 0;
for f in layout.fields.iter() {
if !f.is_gc_ref {
continue;
}
let Some(data_offset) = f.offset.checked_sub(HEADER_SIZE) else {
return Self::OutOfLine;
};
if data_offset % 4 != 0 {
return Self::OutOfLine;
}
let slot = data_offset / 4;
if slot >= Self::STRUCT_BITMAP_BITS {
return Self::OutOfLine;
}
bitmap |= 1u32 << slot;
}
InlineTraceInfo::Struct {
gc_ref_bitmap: bitmap,
}
}
pub fn encode(&self) -> u32 {
match self {
Self::OutOfLine => 0,
Self::Array { elems_are_gc_refs } => {
Self::IS_INLINE_BIT
| Self::IS_ARRAY_BIT
| if *elems_are_gc_refs {
Self::ELEMS_ARE_GC_REFS_BIT
} else {
0
}
}
Self::Struct { gc_ref_bitmap } => {
Self::IS_INLINE_BIT | (*gc_ref_bitmap << Self::STRUCT_BITMAP_SHIFT)
}
}
}
pub fn decode(reserved: u32) -> Self {
if reserved & Self::IS_INLINE_BIT == 0 {
return Self::OutOfLine;
}
if reserved & Self::IS_ARRAY_BIT != 0 {
Self::Array {
elems_are_gc_refs: reserved & Self::ELEMS_ARE_GC_REFS_BIT != 0,
}
} else {
Self::Struct {
gc_ref_bitmap: (reserved >> Self::STRUCT_BITMAP_SHIFT)
& ((1u32 << Self::STRUCT_BITMAP_BITS) - 1),
}
}
}
}
impl GcTypeLayouts for CopyingTypeLayouts {
fn array_length_field_offset(&self) -> u32 {
ARRAY_LENGTH_OFFSET
}
fn exception_tag_instance_offset(&self) -> u32 {
EXCEPTION_TAG_INSTANCE_OFFSET
}
fn exception_tag_defined_offset(&self) -> u32 {
EXCEPTION_TAG_DEFINED_OFFSET
}
fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout {
let mut layout = common_array_layout(ty, HEADER_SIZE, ALIGN, ARRAY_LENGTH_OFFSET);
debug_assert!(layout.align <= ALIGN);
layout.align = ALIGN;
debug_assert!(layout.base_size >= MIN_OBJECT_SIZE);
layout
}
fn struct_layout(&self, ty: &WasmStructType) -> Result<GcStructLayout, OutOfMemory> {
let mut layout = common_struct_layout(ty, HEADER_SIZE, ALIGN)?;
if layout.size < MIN_OBJECT_SIZE {
layout.size = MIN_OBJECT_SIZE;
}
layout.size = layout.size.next_multiple_of(ALIGN);
debug_assert!(layout.align <= ALIGN);
layout.align = ALIGN;
debug_assert!(layout.size >= MIN_OBJECT_SIZE);
Ok(layout)
}
fn exn_layout(&self, ty: &WasmExnType) -> Result<GcStructLayout, OutOfMemory> {
let mut layout = common_exn_layout(ty, HEADER_SIZE, ALIGN)?;
layout.size = layout.size.next_multiple_of(ALIGN);
debug_assert!(layout.align <= ALIGN);
layout.align = ALIGN;
debug_assert!(layout.size >= MIN_OBJECT_SIZE);
Ok(layout)
}
}