wasm: Add support for passive data and element segments (#1389)
This is part of the bulk memory and reference types proposals.
This commit is contained in:
@@ -21,7 +21,7 @@ serde = { version = "1.0.94", features = ["derive"], optional = true }
|
|||||||
thiserror = "1.0.4"
|
thiserror = "1.0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wat = "1.0.7"
|
wat = "1.0.9"
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use crate::environ::{
|
|||||||
use crate::func_translator::FuncTranslator;
|
use crate::func_translator::FuncTranslator;
|
||||||
use crate::state::ModuleTranslationState;
|
use crate::state::ModuleTranslationState;
|
||||||
use crate::translation_utils::{
|
use crate::translation_utils::{
|
||||||
DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table,
|
DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, PassiveDataIndex,
|
||||||
TableIndex,
|
PassiveElemIndex, SignatureIndex, Table, TableIndex,
|
||||||
};
|
};
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
@@ -605,6 +605,22 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn declare_passive_element(
|
||||||
|
&mut self,
|
||||||
|
_elem_index: PassiveElemIndex,
|
||||||
|
_segments: Box<[FuncIndex]>,
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_passive_data(
|
||||||
|
&mut self,
|
||||||
|
_elem_index: PassiveDataIndex,
|
||||||
|
_segments: &'data [u8],
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
|
fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
|
||||||
self.info.memories.push(Exportable::new(memory));
|
self.info.memories.push(Exportable::new(memory));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
use crate::state::{FuncTranslationState, ModuleTranslationState};
|
use crate::state::{FuncTranslationState, ModuleTranslationState};
|
||||||
use crate::translation_utils::{
|
use crate::translation_utils::{
|
||||||
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
|
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, PassiveDataIndex, PassiveElemIndex,
|
||||||
|
SignatureIndex, Table, TableIndex,
|
||||||
};
|
};
|
||||||
use core::convert::From;
|
use core::convert::From;
|
||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
@@ -600,6 +601,29 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||||||
elements: Box<[FuncIndex]>,
|
elements: Box<[FuncIndex]>,
|
||||||
) -> WasmResult<()>;
|
) -> WasmResult<()>;
|
||||||
|
|
||||||
|
/// Declare a passive element segment.
|
||||||
|
fn declare_passive_element(
|
||||||
|
&mut self,
|
||||||
|
index: PassiveElemIndex,
|
||||||
|
elements: Box<[FuncIndex]>,
|
||||||
|
) -> WasmResult<()>;
|
||||||
|
|
||||||
|
/// Provides the number of passive data segments up front.
|
||||||
|
///
|
||||||
|
/// By default this does nothing, but implementations may use this to
|
||||||
|
/// pre-allocate memory if desired.
|
||||||
|
fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> {
|
||||||
|
let _ = count;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declare a passive data segment.
|
||||||
|
fn declare_passive_data(
|
||||||
|
&mut self,
|
||||||
|
data_index: PassiveDataIndex,
|
||||||
|
data: &'data [u8],
|
||||||
|
) -> WasmResult<()>;
|
||||||
|
|
||||||
/// Provides the contents of a function body.
|
/// Provides the contents of a function body.
|
||||||
///
|
///
|
||||||
/// Note there's no `reserve_function_bodies` function because the number of
|
/// Note there's no `reserve_function_bodies` function because the number of
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ pub use crate::state::module_state::ModuleTranslationState;
|
|||||||
pub use crate::translation_utils::{
|
pub use crate::translation_utils::{
|
||||||
get_vmctx_value_label, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
|
get_vmctx_value_label, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
|
||||||
DefinedTableIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
|
DefinedTableIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
|
||||||
SignatureIndex, Table, TableElementType, TableIndex,
|
PassiveDataIndex, PassiveElemIndex, SignatureIndex, Table, TableElementType, TableIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Version number of this crate.
|
/// Version number of this crate.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
||||||
//! to deal with each part of it.
|
//! to deal with each part of it.
|
||||||
use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
|
use crate::environ::{ModuleEnvironment, WasmResult};
|
||||||
use crate::sections_translator::{
|
use crate::sections_translator::{
|
||||||
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
|
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
|
||||||
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
|
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
|
||||||
@@ -67,11 +67,8 @@ pub fn translate_module<'data>(
|
|||||||
parse_data_section(data, environ)?;
|
parse_data_section(data, environ)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
SectionContent::DataCount(_) => {
|
SectionContent::DataCount(count) => {
|
||||||
return Err(WasmError::InvalidWebAssembly {
|
environ.reserve_passive_data(count)?;
|
||||||
message: "don't know how to handle the data count section yet",
|
|
||||||
offset: reader.current_position(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SectionContent::Custom {
|
SectionContent::Custom {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
|
|||||||
use crate::state::ModuleTranslationState;
|
use crate::state::ModuleTranslationState;
|
||||||
use crate::translation_utils::{
|
use crate::translation_utils::{
|
||||||
tabletype_to_type, type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory,
|
tabletype_to_type, type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory,
|
||||||
MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
|
MemoryIndex, PassiveDataIndex, PassiveElemIndex, SignatureIndex, Table, TableElementType,
|
||||||
|
TableIndex,
|
||||||
};
|
};
|
||||||
use crate::{wasm_unsupported, HashMap};
|
use crate::{wasm_unsupported, HashMap};
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
@@ -19,10 +20,11 @@ use cranelift_codegen::ir::immediates::V128Imm;
|
|||||||
use cranelift_codegen::ir::{self, AbiParam, Signature};
|
use cranelift_codegen::ir::{self, AbiParam, Signature};
|
||||||
use cranelift_entity::packed_option::ReservedValue;
|
use cranelift_entity::packed_option::ReservedValue;
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
|
use std::boxed::Box;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementKind,
|
self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems,
|
||||||
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FuncType,
|
ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind, FuncType,
|
||||||
FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType,
|
FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType,
|
||||||
ImportSectionReader, MemorySectionReader, MemoryType, NameSectionReader, Naming, NamingReader,
|
ImportSectionReader, MemorySectionReader, MemoryType, NameSectionReader, Naming, NamingReader,
|
||||||
Operator, TableSectionReader, Type, TypeSectionReader,
|
Operator, TableSectionReader, Type, TypeSectionReader,
|
||||||
@@ -288,6 +290,19 @@ pub fn parse_start_section(index: u32, environ: &mut dyn ModuleEnvironment) -> W
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
|
||||||
|
let items_reader = items.get_items_reader()?;
|
||||||
|
let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
|
||||||
|
for item in items_reader {
|
||||||
|
let elem = match item? {
|
||||||
|
ElementItem::Null => FuncIndex::reserved_value(),
|
||||||
|
ElementItem::Func(index) => FuncIndex::from_u32(index),
|
||||||
|
};
|
||||||
|
elems.push(elem);
|
||||||
|
}
|
||||||
|
Ok(elems.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses the Element section of the wasm module.
|
/// Parses the Element section of the wasm module.
|
||||||
pub fn parse_element_section<'data>(
|
pub fn parse_element_section<'data>(
|
||||||
elements: ElementSectionReader<'data>,
|
elements: ElementSectionReader<'data>,
|
||||||
@@ -295,7 +310,7 @@ pub fn parse_element_section<'data>(
|
|||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
environ.reserve_table_elements(elements.get_count())?;
|
environ.reserve_table_elements(elements.get_count())?;
|
||||||
|
|
||||||
for entry in elements {
|
for (index, entry) in elements.into_iter().enumerate() {
|
||||||
let Element { kind, items, ty } = entry?;
|
let Element { kind, items, ty } = entry?;
|
||||||
if ty != Type::AnyFunc {
|
if ty != Type::AnyFunc {
|
||||||
return Err(wasm_unsupported!(
|
return Err(wasm_unsupported!(
|
||||||
@@ -303,41 +318,37 @@ pub fn parse_element_section<'data>(
|
|||||||
ty
|
ty
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let ElementKind::Active {
|
let segments = read_elems(&items)?;
|
||||||
table_index,
|
match kind {
|
||||||
init_expr,
|
ElementKind::Active {
|
||||||
} = kind
|
table_index,
|
||||||
{
|
init_expr,
|
||||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
} => {
|
||||||
let (base, offset) = match init_expr_reader.read_operator()? {
|
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||||
Operator::I32Const { value } => (None, value as u32 as usize),
|
let (base, offset) = match init_expr_reader.read_operator()? {
|
||||||
Operator::GlobalGet { global_index } => {
|
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||||
(Some(GlobalIndex::from_u32(global_index)), 0)
|
Operator::GlobalGet { global_index } => {
|
||||||
}
|
(Some(GlobalIndex::from_u32(global_index)), 0)
|
||||||
ref s => {
|
}
|
||||||
return Err(wasm_unsupported!(
|
ref s => {
|
||||||
"unsupported init expr in element section: {:?}",
|
return Err(wasm_unsupported!(
|
||||||
s
|
"unsupported init expr in element section: {:?}",
|
||||||
));
|
s
|
||||||
}
|
));
|
||||||
};
|
}
|
||||||
let items_reader = items.get_items_reader()?;
|
|
||||||
let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
|
|
||||||
for item in items_reader {
|
|
||||||
let elem = match item? {
|
|
||||||
ElementItem::Null => FuncIndex::reserved_value(),
|
|
||||||
ElementItem::Func(index) => FuncIndex::from_u32(index),
|
|
||||||
};
|
};
|
||||||
elems.push(elem);
|
environ.declare_table_elements(
|
||||||
|
TableIndex::from_u32(table_index),
|
||||||
|
base,
|
||||||
|
offset,
|
||||||
|
segments,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
environ.declare_table_elements(
|
ElementKind::Passive => {
|
||||||
TableIndex::from_u32(table_index),
|
let index = PassiveElemIndex::from_u32(index as u32);
|
||||||
base,
|
environ.declare_passive_element(index, segments)?;
|
||||||
offset,
|
}
|
||||||
elems.into_boxed_slice(),
|
ElementKind::Declared => return Err(wasm_unsupported!("element kind declared")),
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
return Err(wasm_unsupported!("unsupported passive elements section",));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -365,37 +376,37 @@ pub fn parse_data_section<'data>(
|
|||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
environ.reserve_data_initializers(data.get_count())?;
|
environ.reserve_data_initializers(data.get_count())?;
|
||||||
|
|
||||||
for entry in data {
|
for (index, entry) in data.into_iter().enumerate() {
|
||||||
let Data { kind, data } = entry?;
|
let Data { kind, data } = entry?;
|
||||||
if let DataKind::Active {
|
match kind {
|
||||||
memory_index,
|
DataKind::Active {
|
||||||
init_expr,
|
memory_index,
|
||||||
} = kind
|
init_expr,
|
||||||
{
|
} => {
|
||||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||||
let (base, offset) = match init_expr_reader.read_operator()? {
|
let (base, offset) = match init_expr_reader.read_operator()? {
|
||||||
Operator::I32Const { value } => (None, value as u32 as usize),
|
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||||
Operator::GlobalGet { global_index } => {
|
Operator::GlobalGet { global_index } => {
|
||||||
(Some(GlobalIndex::from_u32(global_index)), 0)
|
(Some(GlobalIndex::from_u32(global_index)), 0)
|
||||||
}
|
}
|
||||||
ref s => {
|
ref s => {
|
||||||
return Err(wasm_unsupported!(
|
return Err(wasm_unsupported!(
|
||||||
"unsupported init expr in data section: {:?}",
|
"unsupported init expr in data section: {:?}",
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
environ.declare_data_initialization(
|
environ.declare_data_initialization(
|
||||||
MemoryIndex::from_u32(memory_index),
|
MemoryIndex::from_u32(memory_index),
|
||||||
base,
|
base,
|
||||||
offset,
|
offset,
|
||||||
data,
|
data,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
}
|
||||||
return Err(wasm_unsupported!(
|
DataKind::Passive => {
|
||||||
"unsupported passive data section: {:?}",
|
let index = PassiveDataIndex::from_u32(index as u32);
|
||||||
kind
|
environ.declare_passive_data(index, data)?;
|
||||||
));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,16 @@ entity_impl!(MemoryIndex);
|
|||||||
pub struct SignatureIndex(u32);
|
pub struct SignatureIndex(u32);
|
||||||
entity_impl!(SignatureIndex);
|
entity_impl!(SignatureIndex);
|
||||||
|
|
||||||
|
/// Index type of a passive data segment inside the WebAssembly module.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
|
pub struct PassiveDataIndex(u32);
|
||||||
|
entity_impl!(PassiveDataIndex);
|
||||||
|
|
||||||
|
/// Index type of a passive element segment inside the WebAssembly module.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
|
pub struct PassiveElemIndex(u32);
|
||||||
|
entity_impl!(PassiveElemIndex);
|
||||||
|
|
||||||
/// WebAssembly global.
|
/// WebAssembly global.
|
||||||
#[derive(Debug, Clone, Copy, Hash)]
|
#[derive(Debug, Clone, Copy, Hash)]
|
||||||
pub struct Global {
|
pub struct Global {
|
||||||
|
|||||||
11
cranelift/wasmtests/passive-data.wat
Normal file
11
cranelift/wasmtests/passive-data.wat
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
(module
|
||||||
|
(data $passive "this is a passive data segment")
|
||||||
|
|
||||||
|
(func (export "init") (param i32 i32 i32)
|
||||||
|
local.get 0 ;; dst
|
||||||
|
local.get 1 ;; src
|
||||||
|
local.get 2 ;; cnt
|
||||||
|
memory.init $passive)
|
||||||
|
|
||||||
|
(func (export "drop")
|
||||||
|
data.drop $passive))
|
||||||
22
cranelift/wasmtests/table-copy.wat
Normal file
22
cranelift/wasmtests/table-copy.wat
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
(module $n
|
||||||
|
(table $t (import "m" "t") 6 funcref)
|
||||||
|
|
||||||
|
(func $i (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 3))
|
||||||
|
(func $j (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 4))
|
||||||
|
(func $k (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 5))
|
||||||
|
|
||||||
|
(table $u (export "u") funcref (elem $i $j $k $i $j $k))
|
||||||
|
|
||||||
|
(func (export "copy_to_t_from_u") (param i32 i32 i32 i32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
local.get 1
|
||||||
|
local.get 2
|
||||||
|
local.get 3
|
||||||
|
table.copy $t $u)
|
||||||
|
|
||||||
|
(func (export "copy_to_u_from_t") (param i32 i32 i32 i32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
local.get 1
|
||||||
|
local.get 2
|
||||||
|
local.get 3
|
||||||
|
table.copy $u $t))
|
||||||
Reference in New Issue
Block a user