Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement bulk-memory Wasm proposal #628

Merged
merged 49 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9bf645e
add bulk-memory-operations Wasm spec tests
Robbepop Jan 21, 2023
8274fc3
add bulk_memory flag to Config
Robbepop Jan 21, 2023
adf388c
refactor Wasm spec test suite runners
Robbepop Jan 21, 2023
7db25a5
refactor define_X_tests macros
Robbepop Jan 21, 2023
18834ef
add comment about mutable-global
Robbepop Jan 21, 2023
1331e14
remove ModuleError::Unsupported
Robbepop Jan 21, 2023
13e7592
cleanup after last commit to use panics instead of unsupported error
Robbepop Jan 21, 2023
12cf30a
Update init_expr.rs
Robbepop Jan 21, 2023
c5dd65b
Merge branch 'master' into rf-implement-bulk-memory
Robbepop Jan 21, 2023
4f05fa9
implement initial skeleton for passive data segments
Robbepop Jan 21, 2023
6f5af22
export DataSegmentKind from the data module and use it
Robbepop Jan 21, 2023
3835643
refactor data segments minimally
Robbepop Jan 21, 2023
aec8b1e
add skeleton for passive Wasm element segments
Robbepop Jan 21, 2023
548d6cb
refactor InitExpr
Robbepop Jan 21, 2023
2c0d4c3
add InstanceDataSegment to InstanceEntity
Robbepop Jan 21, 2023
fe42f6e
add data segments to instance and store
Robbepop Jan 22, 2023
aaea98c
implement memory.init Wasm instruction
Robbepop Jan 22, 2023
1f5a1d5
replace todo!() with unimplemented!()
Robbepop Jan 22, 2023
6b0854b
fix doc links
Robbepop Jan 22, 2023
24d31c2
implement DataDrop wasmi instruction
Robbepop Jan 22, 2023
30d5225
add missing docs
Robbepop Jan 22, 2023
4035267
remove commented out line
Robbepop Jan 22, 2023
5ce1387
add wasmi memory.fill instruction
Robbepop Jan 22, 2023
ff2b71d
implement wasmi memory.copy instruction
Robbepop Jan 22, 2023
0617781
remove unused APIs
Robbepop Jan 22, 2023
c1de7ef
fix bug in bulk-memory instructions implemented so far
Robbepop Jan 22, 2023
5088b4d
move memory query down
Robbepop Jan 22, 2023
45b515d
fix bug in some bulk-memory instruction impls
Robbepop Jan 22, 2023
2f95fcd
adjust InitExpr and module::ElementSegment
Robbepop Jan 22, 2023
716321e
fix bugs
Robbepop Jan 24, 2023
28d4d5d
add element segments to the store
Robbepop Jan 24, 2023
1d0c0b3
implement table.init and elem.drop Wasm instructions
Robbepop Jan 24, 2023
c13b9a3
update wast from 0.44 to 0.45
Robbepop Jan 24, 2023
076780c
update wast 0.45 -> 0.46
Robbepop Jan 24, 2023
377dc1d
update wast 0.46 -> 0.52
Robbepop Jan 24, 2023
087f5fb
implement Wasm table.copy instruction
Robbepop Jan 24, 2023
127c929
implement user facing Table::copy API
Robbepop Jan 24, 2023
d16827c
update Wasm testsuite revision
Robbepop Jan 24, 2023
1cad262
cleanup in tests
Robbepop Jan 24, 2023
95e469d
fix typo
Robbepop Jan 24, 2023
a2b29d4
fix Wasm spectest suite test case
Robbepop Jan 24, 2023
cf047db
remove some forwarding methods in Store
Robbepop Jan 25, 2023
04cc114
Merge branch 'master' into rf-implement-bulk-memory
Robbepop Jan 25, 2023
a1a5d61
fix remaining bugs
Robbepop Jan 25, 2023
49668ce
Merge branch 'master' into rf-implement-bulk-memory
Robbepop Jan 25, 2023
ea1988c
fix code after merge
Robbepop Jan 25, 2023
da2d5c8
rename tests
Robbepop Jan 25, 2023
5e1518d
expect failure for all reference-types related tests
Robbepop Jan 25, 2023
e9c25d3
fix internal doc link
Robbepop Jan 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mod tests;
pub use self::{component_vec::ComponentVec, dedup::DedupArena, guarded::GuardedEntity};
use alloc::vec::Vec;
use core::{
cmp::max,
iter::{DoubleEndedIterator, Enumerate, ExactSizeIterator},
marker::PhantomData,
ops::{Index, IndexMut},
Expand Down Expand Up @@ -149,6 +150,21 @@ where
pub fn get_mut(&mut self, index: Idx) -> Option<&mut T> {
self.entities.get_mut(index.into_usize())
}

/// Returns an exclusive reference to the pair of entities at the given indices if any.
///
/// Returns `None` if `fst` and `snd` refer to the same entity.
/// Returns `None` if either `fst` or `snd` is invalid for this [`Arena`].
#[inline]
pub fn get_pair_mut(&mut self, fst: Idx, snd: Idx) -> Option<(&mut T, &mut T)> {
let fst_index = fst.into_usize();
let snd_index = snd.into_usize();
let max_index = max(fst_index, snd_index);
let (fst_set, snd_set) = self.entities.split_at_mut(max_index);
let fst = fst_set.get_mut(fst_index)?;
let snd = snd_set.get_mut(0)?;
Some((fst, snd))
}
}

impl<Idx, T> FromIterator<T> for Arena<Idx, T> {
Expand Down
100 changes: 100 additions & 0 deletions crates/wasmi/src/element.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::{module, module::FuncIdx, store::Stored, AsContextMut};
use alloc::sync::Arc;
use wasmi_arena::ArenaIndex;

/// A raw index to a element segment entity.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ElementSegmentIdx(u32);

impl ArenaIndex for ElementSegmentIdx {
fn into_usize(self) -> usize {
self.0 as usize
}

fn from_usize(value: usize) -> Self {
let value = value.try_into().unwrap_or_else(|error| {
panic!("index {value} is out of bounds as element segment index: {error}")
});
Self(value)
}
}

/// A Wasm data segment reference.
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct ElementSegment(Stored<ElementSegmentIdx>);

impl ElementSegment {
/// Creates a new linear memory reference.
pub fn from_inner(stored: Stored<ElementSegmentIdx>) -> Self {
Self(stored)
}

/// Returns the underlying stored representation.
pub fn as_inner(&self) -> &Stored<ElementSegmentIdx> {
&self.0
}

/// Allocates a new [`ElementSegment`] on the store.
///
/// # Errors
///
/// If more than [`u32::MAX`] much linear memory is allocated.
pub fn new(mut ctx: impl AsContextMut, segment: &module::ElementSegment) -> Self {
let entity = ElementSegmentEntity::from(segment);
ctx.as_context_mut()
.store
.inner
.alloc_element_segment(entity)
}
}

/// An instantiated [`ElementSegmentEntity`].
///
/// # Note
///
/// With the `bulk-memory` Wasm proposal it is possible to interact
/// with element segments at runtime. Therefore Wasm instances now have
/// a need to have an instantiated representation of data segments.
#[derive(Debug)]
pub struct ElementSegmentEntity {
/// The underlying items of the instance element segment.
///
/// # Note
///
/// These items are just readable after instantiation.
/// Using Wasm `elem.drop` simply replaces the instance
/// with an empty one.
items: Option<Arc<[Option<FuncIdx>]>>,
}

impl From<&'_ module::ElementSegment> for ElementSegmentEntity {
fn from(segment: &'_ module::ElementSegment) -> Self {
match segment.kind() {
module::ElementSegmentKind::Passive => Self {
items: Some(segment.clone_items()),
},
module::ElementSegmentKind::Active(_) => Self::empty(),
}
}
}

impl ElementSegmentEntity {
/// Create an empty [`ElementSegmentEntity`] representing dropped element segments.
fn empty() -> Self {
Self { items: None }
}

/// Returns the items of the [`ElementSegmentEntity`].
pub fn items(&self) -> &[Option<FuncIdx>] {
self.items
.as_ref()
.map(|items| &items[..])
.unwrap_or_else(|| &[])
}

/// Drops the items of the [`ElementSegmentEntity`].
pub fn drop_items(&mut self) {
self.items = None;
}
}
9 changes: 9 additions & 0 deletions crates/wasmi/src/engine/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ mod tests;
pub use self::utils::{
BranchOffset,
BranchParams,
DataSegmentIdx,
DropKeep,
DropKeepError,
ElementSegmentIdx,
FuncIdx,
GlobalIdx,
LocalDepth,
Expand Down Expand Up @@ -70,6 +72,13 @@ pub enum Instruction {
I64Store32(Offset),
MemorySize,
MemoryGrow,
MemoryFill,
MemoryCopy,
MemoryInit(DataSegmentIdx),
DataDrop(DataSegmentIdx),
TableCopy,
TableInit(ElementSegmentIdx),
ElemDrop(ElementSegmentIdx),
Const(UntypedValue),
I32Eqz,
I32Eq,
Expand Down
50 changes: 49 additions & 1 deletion crates/wasmi/src/engine/bytecode/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl LocalDepth {
///
/// Refers to a global variable of a [`Store`].
///
/// [`Store`]: [`crate::v2::Store`]
/// [`Store`]: [`crate::Store`]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct GlobalIdx(u32);
Expand All @@ -148,6 +148,54 @@ impl GlobalIdx {
}
}

/// A data segment index.
///
/// # Note
///
/// Refers to a data segment of a [`Store`].
///
/// [`Store`]: [`crate::Store`]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct DataSegmentIdx(u32);

impl From<u32> for DataSegmentIdx {
fn from(index: u32) -> Self {
Self(index)
}
}

impl DataSegmentIdx {
/// Returns the inner `u32` index.
pub fn into_inner(self) -> u32 {
self.0
}
}

/// An element segment index.
///
/// # Note
///
/// Refers to a data segment of a [`Store`].
///
/// [`Store`]: [`crate::Store`]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct ElementSegmentIdx(u32);

impl From<u32> for ElementSegmentIdx {
fn from(index: u32) -> Self {
Self(index)
}
}

impl ElementSegmentIdx {
/// Returns the inner `u32` index.
pub fn into_inner(self) -> u32 {
self.0
}
}

/// A linear memory access offset.
///
/// # Note
Expand Down
93 changes: 84 additions & 9 deletions crates/wasmi/src/engine/cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::{
element::{ElementSegment, ElementSegmentEntity},
instance::InstanceEntity,
memory::DataSegment,
module::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX},
table::TableEntity,
Func,
Instance,
Memory,
Expand All @@ -9,6 +13,8 @@ use crate::{
use core::ptr::NonNull;
use wasmi_core::UntypedValue;

use super::bytecode::{DataSegmentIdx, ElementSegmentIdx};

/// A cache for frequently used entities of an [`Instance`].
#[derive(Debug)]
pub struct InstanceCache {
Expand Down Expand Up @@ -63,21 +69,89 @@ impl InstanceCache {
self.set_instance(instance);
}

/// Loads the [`DataSegment`] at `index` of the currently used [`Instance`].
///
/// # Panics
///
/// If there is no [`DataSegment`] for the [`Instance`] at the `index`.
#[inline]
pub fn get_data_segment(&mut self, ctx: &mut StoreInner, index: u32) -> DataSegment {
let instance = self.instance();
ctx.resolve_instance(self.instance())
.get_data_segment(index)
.unwrap_or_else(|| {
panic!("missing data segment ({index:?}) for instance: {instance:?}",)
})
}

/// Loads the [`ElementSegment`] at `index` of the currently used [`Instance`].
///
/// # Panics
///
/// If there is no [`ElementSegment`] for the [`Instance`] at the `index`.
#[inline]
pub fn get_element_segment(
&mut self,
ctx: &mut StoreInner,
index: ElementSegmentIdx,
) -> ElementSegment {
let instance = self.instance();
ctx.resolve_instance(self.instance())
.get_element_segment(index.into_inner())
.unwrap_or_else(|| {
panic!("missing element segment ({index:?}) for instance: {instance:?}",)
})
}

/// Loads the [`DataSegment`] at `index` of the currently used [`Instance`].
///
/// # Panics
///
/// If there is no [`DataSegment`] for the [`Instance`] at the `index`.
#[inline]
pub fn get_default_memory_and_data_segment<'a>(
&mut self,
ctx: &'a mut StoreInner,
segment: DataSegmentIdx,
) -> (&'a mut [u8], &'a [u8]) {
let mem = self.default_memory(ctx);
let seg = self.get_data_segment(ctx, segment.into_inner());
let (memory, segment) = ctx.resolve_memory_mut_and_data_segment(&mem, &seg);
(memory.data_mut(), segment.bytes())
}

/// Loads the [`ElementSegment`] at `index` of the currently used [`Instance`].
///
/// # Panics
///
/// If there is no [`ElementSegment`] for the [`Instance`] at the `index`.
#[inline]
pub fn get_default_table_and_element_segment<'a>(
&mut self,
ctx: &'a mut StoreInner,
segment: ElementSegmentIdx,
) -> (
&'a InstanceEntity,
&'a mut TableEntity,
&'a ElementSegmentEntity,
) {
let tab = self.default_table(ctx);
let seg = self.get_element_segment(ctx, segment);
let inst = self.instance();
ctx.resolve_instance_table_element(inst, &tab, &seg)
}

/// Loads the default [`Memory`] of the currently used [`Instance`].
///
/// # Panics
///
/// If the currently used [`Instance`] does not have a default linear memory.
fn load_default_memory(&mut self, ctx: &StoreInner) -> Memory {
let instance = self.instance();
let default_memory = ctx
.resolve_instance(self.instance())
.resolve_instance(instance)
.get_memory(DEFAULT_MEMORY_INDEX)
.unwrap_or_else(|| {
panic!(
"missing default linear memory for instance: {:?}",
self.instance
)
});
.unwrap_or_else(|| panic!("missing default linear memory for instance: {instance:?}",));
self.default_memory = Some(default_memory);
default_memory
}
Expand All @@ -88,10 +162,11 @@ impl InstanceCache {
///
/// If the currently used [`Instance`] does not have a default table.
fn load_default_table(&mut self, ctx: &StoreInner) -> Table {
let instance = self.instance();
let default_table = ctx
.resolve_instance(self.instance())
.resolve_instance(instance)
.get_table(DEFAULT_TABLE_INDEX)
.unwrap_or_else(|| panic!("missing default table for instance: {:?}", self.instance));
.unwrap_or_else(|| panic!("missing default table for instance: {instance:?}"));
self.default_table = Some(default_table);
default_table
}
Expand Down
17 changes: 16 additions & 1 deletion crates/wasmi/src/engine/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct Config {
saturating_float_to_int: bool,
/// Is `true` if the [`multi-value`] Wasm proposal is enabled.
multi_value: bool,
/// Is `true` if the [`bulk-memory`] Wasm proposal is enabled.
bulk_memory: bool,
}

impl Default for Config {
Expand All @@ -32,6 +34,7 @@ impl Default for Config {
sign_extension: true,
saturating_float_to_int: true,
multi_value: true,
bulk_memory: true,
}
}
}
Expand Down Expand Up @@ -112,15 +115,27 @@ impl Config {
self
}

/// Enable or disable the [`bulk-memory`] Wasm proposal for the [`Config`].
///
/// # Note
///
/// Enabled by default.
///
/// [`multi-value`]: https://github.com/WebAssembly/bulk-memory-operations
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.bulk_memory = enable;
self
}

/// Returns the [`WasmFeatures`] represented by the [`Config`].
pub fn wasm_features(&self) -> WasmFeatures {
WasmFeatures {
multi_value: self.multi_value,
mutable_global: self.mutable_global,
saturating_float_to_int: self.saturating_float_to_int,
sign_extension: self.sign_extension,
bulk_memory: self.bulk_memory,
reference_types: false,
bulk_memory: false,
component_model: false,
simd: false,
relaxed_simd: false,
Expand Down
Loading