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:
Nick Fitzgerald
2020-02-15 14:53:32 -08:00
committed by GitHub
parent 45cc95e60e
commit 9b3ac10ebc
9 changed files with 169 additions and 78 deletions

View File

@@ -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]

View File

@@ -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(())

View File

@@ -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

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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)?;
)); }
} }
} }

View File

@@ -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 {

View 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))

View 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))