#![allow(clippy::indexing_slicing)]
use super::{Execution, StepOutcome};
use crate::canon::Layout;
use crate::instance::Instance;
use crate::module::op::Op;
use crate::store::StoreInner;
use crate::trap::Trap;
use crate::Result;
impl Execution {
pub(super) fn gc_collect_now(&mut self, inner: &mut StoreInner) {
let roots: Vec<_> = self.operand_roots().collect();
inner.gc_collect(&roots);
}
pub(super) fn gc_reserve(
&mut self,
inner: &mut StoreInner,
charge: usize,
ip: u32,
) -> Option<StepOutcome> {
if inner.gc.fits(charge) {
return None;
}
if !inner.gc.is_free_grant(inner.gc.desired_reservation(charge)) && inner.gc.is_collecting()
{
self.gc_collect_now(inner);
if inner.gc.fits(charge) {
return None;
}
}
let reserved_target = inner.gc.desired_reservation(charge);
if inner.gc.is_free_grant(reserved_target) {
let granted = inner.gc.grant(reserved_target);
inner.engine().add_gc_committed(granted);
return None;
}
Some(StepOutcome::DoGcGrow {
reserved_target,
bytes_needed: charge as u64,
return_ip: ip,
})
}
pub(super) fn gc_alloc_charge(
&self,
inner: &StoreInner,
instance: Instance,
op: &Op,
) -> Result<Option<usize>> {
let module = inner.instance(instance).module.clone();
let data_len = match op {
Op::StructNew(ty) | Op::StructNewDefault(ty) => match module.inner().layout(*ty) {
Layout::Struct { size, .. } => *size,
Layout::Array { .. } => unreachable!("struct.new on an array type"),
},
Op::ArrayNew(ty) | Op::ArrayNewDefault(ty) => {
let stride = module.inner().layout(*ty).stride();
array_bytes(self.peek_count(), stride)?
}
Op::ArrayNewFixed { ty, n } => module.inner().layout(*ty).body_size(*n as usize),
Op::ArrayNewData { ty, data } => {
let stride = module.inner().layout(*ty).stride();
let dropped = inner.instance(instance).dropped_data[*data as usize];
let seg = if dropped {
0
} else {
module.inner().datas[*data as usize].bytes.len()
};
seg_clamped_charge(self.peek_count(), stride, seg)
}
Op::ArrayNewElem { ty, elem } => {
let stride = module.inner().layout(*ty).stride();
let seg = inner.instance(instance).elems[*elem as usize]
.len()
.saturating_mul(stride);
seg_clamped_charge(self.peek_count(), stride, seg)
}
_ => return Ok(None),
};
Ok(Some(inner.gc_object_charge(data_len)))
}
fn peek_count(&self) -> usize {
self.top_i32() as u32 as usize
}
}
fn array_bytes(count: usize, stride: usize) -> Result<usize> {
count
.checked_mul(stride)
.ok_or_else(|| Trap::AllocationTooLarge.into())
}
fn seg_clamped_charge(count: usize, stride: usize, seg_bytes: usize) -> usize {
count
.checked_mul(stride)
.map_or(seg_bytes, |v| v.min(seg_bytes))
}