Skip to content

Commit

Permalink
implement table.copy instruction execution
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbepop committed Aug 22, 2023
1 parent 0624543 commit 8d79f31
Show file tree
Hide file tree
Showing 2 changed files with 291 additions and 9 deletions.
148 changes: 140 additions & 8 deletions crates/wasmi/src/engine/regmach/executor/instrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ use crate::{
bytecode2::{AnyConst32, Const32, Instruction, Register, RegisterSpan, RegisterSpanIter},
cache::InstanceCache,
code_map::{CodeMap2 as CodeMap, InstructionPtr2 as InstructionPtr},
config::FuelCosts,
regmach::stack::{CallFrame, CallStack, ValueStack, ValueStackPtr},
},
store::ResourceLimiterRef,
FuelConsumptionMode,
Func,
FuncRef,
Instance,
Expand Down Expand Up @@ -354,14 +356,28 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
Instr::TableSize { result, table } => self.execute_table_size(result, table),
Instr::TableSet { index, value } => self.execute_table_set(index, value)?,
Instr::TableSetAt { index, value } => self.execute_table_set_at(index, value)?,
Instr::TableCopy { dst, src, len } => todo!(),
Instr::TableCopyTo { dst, src, len } => todo!(),
Instr::TableCopyFrom { dst, src, len } => todo!(),
Instr::TableCopyFromTo { dst, src, len } => todo!(),
Instr::TableCopyExact { dst, src, len } => todo!(),
Instr::TableCopyToExact { dst, src, len } => todo!(),
Instr::TableCopyFromExact { dst, src, len } => todo!(),
Instr::TableCopyFromToExact { dst, src, len } => todo!(),
Instr::TableCopy { dst, src, len } => self.execute_table_copy(dst, src, len)?,
Instr::TableCopyTo { dst, src, len } => {
self.execute_table_copy_to(dst, src, len)?
}
Instr::TableCopyFrom { dst, src, len } => {
self.execute_table_copy_from(dst, src, len)?
}
Instr::TableCopyFromTo { dst, src, len } => {
self.execute_table_copy_from_to(dst, src, len)?
}
Instr::TableCopyExact { dst, src, len } => {
self.execute_table_copy_exact(dst, src, len)?
}
Instr::TableCopyToExact { dst, src, len } => {
self.execute_table_copy_to_exact(dst, src, len)?
}
Instr::TableCopyFromExact { dst, src, len } => {
self.execute_table_copy_from_exact(dst, src, len)?
}
Instr::TableCopyFromToExact { dst, src, len } => {
self.execute_table_copy_from_to_exact(dst, src, len)?
}
Instr::TableInit { dst, src, len } => todo!(),
Instr::TableInitTo { dst, src, len } => todo!(),
Instr::TableInitFrom { dst, src, len } => todo!(),
Expand Down Expand Up @@ -815,6 +831,122 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
None => ReturnOutcome::Host,
}
}

/// Consume an amount of fuel specified by `delta` if `exec` succeeds.
///
/// # Note
///
/// - `delta` is only evaluated if fuel metering is enabled.
/// - `exec` is only evaluated if the remaining fuel is sufficient
/// for amount of required fuel determined by `delta` or if
/// fuel metering is disabled.
///
/// # Errors
///
/// - If the [`StoreInner`] ran out of fuel.
/// - If the `exec` closure traps.
#[inline(always)]
fn consume_fuel_with<T, E>(
&mut self,
delta: impl FnOnce(&FuelCosts) -> u64,
exec: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E>
where
E: From<TrapCode>,
{
match self.get_fuel_consumption_mode() {
None => exec(self),
Some(mode) => self.consume_fuel_with_mode(mode, delta, exec),
}
}

/// Consume an amount of fuel specified by `delta` and executes `exec`.
///
/// The `mode` determines when and if the fuel determined by `delta` is charged.
///
/// # Errors
///
/// - If the [`StoreInner`] ran out of fuel.
/// - If the `exec` closure traps.
#[inline(always)]
fn consume_fuel_with_mode<T, E>(
&mut self,
mode: FuelConsumptionMode,
delta: impl FnOnce(&FuelCosts) -> u64,
exec: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E>
where
E: From<TrapCode>,
{
let delta = delta(self.fuel_costs());
match mode {
FuelConsumptionMode::Lazy => self.consume_fuel_with_lazy(delta, exec),
FuelConsumptionMode::Eager => self.consume_fuel_with_eager(delta, exec),
}
}

/// Consume an amount of fuel specified by `delta` if `exec` succeeds.
///
/// Prior to executing `exec` it is checked if enough fuel is remaining
/// determined by `delta`. The fuel is charged only after `exec` has been
/// finished successfully.
///
/// # Errors
///
/// - If the [`StoreInner`] ran out of fuel.
/// - If the `exec` closure traps.
#[inline(always)]
fn consume_fuel_with_lazy<T, E>(
&mut self,
delta: u64,
exec: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E>
where
E: From<TrapCode>,
{
self.ctx.fuel().sufficient_fuel(delta)?;
let result = exec(self)?;
self.ctx
.fuel_mut()
.consume_fuel(delta)
.expect("remaining fuel has already been approved prior");
Ok(result)
}

/// Consume an amount of fuel specified by `delta` and executes `exec`.
///
/// # Errors
///
/// - If the [`StoreInner`] ran out of fuel.
/// - If the `exec` closure traps.
#[inline(always)]
fn consume_fuel_with_eager<T, E>(
&mut self,
delta: u64,
exec: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E>
where
E: From<TrapCode>,
{
self.ctx.fuel_mut().consume_fuel(delta)?;
exec(self)
}

/// Returns a shared reference to the [`FuelCosts`] of the [`Engine`].
///
/// [`Engine`]: crate::Engine
#[inline]
fn fuel_costs(&self) -> &FuelCosts {
self.ctx.engine().config().fuel_costs()
}

/// Returns the [`FuelConsumptionMode`] of the [`Engine`].
///
/// [`Engine`]: crate::Engine
#[inline]
fn get_fuel_consumption_mode(&self) -> Option<FuelConsumptionMode> {
self.ctx.engine().config().get_fuel_consumption_mode()
}
}

impl<'ctx, 'engine> Executor<'ctx, 'engine> {
Expand Down
152 changes: 151 additions & 1 deletion crates/wasmi/src/engine/regmach/executor/instrs/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use crate::{
core::TrapCode,
engine::{
bytecode::TableIdx,
bytecode2::{Const32, Instruction, Register},
bytecode2::{Const16, Const32, Instruction, Register},
code_map::InstructionPtr2 as InstructionPtr,
},
table::TableEntity,
};

impl<'engine, 'ctx> Executor<'engine, 'ctx> {
Expand Down Expand Up @@ -87,4 +88,153 @@ impl<'engine, 'ctx> Executor<'engine, 'ctx> {
.map_err(|_| TrapCode::TableOutOfBounds)?;
self.try_next_instr_at(2)
}

/// Executes an [`Instruction::TableCopy`].
#[inline(always)]
pub fn execute_table_copy(
&mut self,
dst: Register,
src: Register,
len: Register,
) -> Result<(), TrapCode> {
let dst: u32 = self.get_register_as(dst);
let src: u32 = self.get_register_as(src);
let len: u32 = self.get_register_as(len);
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyTo`].
#[inline(always)]
pub fn execute_table_copy_to(
&mut self,
dst: Const16<u32>,
src: Register,
len: Register,
) -> Result<(), TrapCode> {
let dst: u32 = dst.into();
let src: u32 = self.get_register_as(src);
let len: u32 = self.get_register_as(len);
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyFrom`].
#[inline(always)]
pub fn execute_table_copy_from(
&mut self,
dst: Register,
src: Const16<u32>,
len: Register,
) -> Result<(), TrapCode> {
let dst: u32 = self.get_register_as(dst);
let src: u32 = src.into();
let len: u32 = self.get_register_as(len);
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyFromTo`].
#[inline(always)]
pub fn execute_table_copy_from_to(
&mut self,
dst: Const16<u32>,
src: Const16<u32>,
len: Register,
) -> Result<(), TrapCode> {
let dst: u32 = dst.into();
let src: u32 = src.into();
let len: u32 = self.get_register_as(len);
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyExact`].
#[inline(always)]
pub fn execute_table_copy_exact(
&mut self,
dst: Register,
src: Register,
len: Const16<u32>,
) -> Result<(), TrapCode> {
let dst: u32 = self.get_register_as(dst);
let src: u32 = self.get_register_as(src);
let len: u32 = len.into();
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyToExact`].
#[inline(always)]
pub fn execute_table_copy_to_exact(
&mut self,
dst: Const16<u32>,
src: Register,
len: Const16<u32>,
) -> Result<(), TrapCode> {
let dst: u32 = dst.into();
let src: u32 = self.get_register_as(src);
let len: u32 = len.into();
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyFromExact`].
#[inline(always)]
pub fn execute_table_copy_from_exact(
&mut self,
dst: Register,
src: Const16<u32>,
len: Const16<u32>,
) -> Result<(), TrapCode> {
let dst: u32 = self.get_register_as(dst);
let src: u32 = src.into();
let len: u32 = len.into();
self.execute_table_copy_impl(dst, src, len)
}

/// Executes an [`Instruction::TableCopyFromToExact`].
#[inline(always)]
pub fn execute_table_copy_from_to_exact(
&mut self,
dst: Const16<u32>,
src: Const16<u32>,
len: Const16<u32>,
) -> Result<(), TrapCode> {
let dst: u32 = dst.into();
let src: u32 = src.into();
let len: u32 = len.into();
self.execute_table_copy_impl(dst, src, len)
}

/// Executes a generic `table.copy` instruction.
fn execute_table_copy_impl(
&mut self,
dst_index: u32,
src_index: u32,
len: u32,
) -> Result<(), TrapCode> {
if len == 0 {
// Case: copying no elements means there is nothing to do
return Ok(());
}
let dst_table_index = self.fetch_table_index(1);
let src_table_index = self.fetch_table_index(2);
self.consume_fuel_with(
|costs| costs.fuel_for_elements(u64::from(len)),
|this| {
if dst_table_index == src_table_index {
// Case: copy within the same table
let table = this.cache.get_table(this.ctx, dst_table_index);
this.ctx
.resolve_table_mut(&table)
.copy_within(dst_index, src_index, len)?;
} else {
// Case: copy between two different tables
let dst_table = this.cache.get_table(this.ctx, dst_table_index);
let src_table = this.cache.get_table(this.ctx, src_table_index);
// Copy from one table to another table:
let (dst_table, src_table) =
this.ctx.resolve_table_pair_mut(&dst_table, &src_table);
TableEntity::copy(dst_table, dst_index, src_table, src_index, len)?;
}
Ok(())
},
)?;
self.try_next_instr_at(3)
}
}

0 comments on commit 8d79f31

Please sign in to comment.