Skip to content

Commit

Permalink
Update wasmparser to 0.59 (#172)
Browse files Browse the repository at this point in the history
* Update how we parse modules at a top-level
* Lean on wasmparser for module structure validation
* Sync with wasmparser's implemented opcodes and such
  • Loading branch information
alexcrichton committed Jul 14, 2020
1 parent 628871c commit 58c4c8f
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 139 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -29,7 +29,7 @@ leb128 = "0.2.4"
log = "0.4.8"
rayon = { version = "1.1.0", optional = true }
walrus-macro = { path = './crates/macro', version = '=0.17.0' }
wasmparser = "0.55.0"
wasmparser = "0.59.0"

[features]
parallel = ['rayon', 'id-arena/rayon']
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzz-utils/Cargo.toml
Expand Up @@ -10,7 +10,7 @@ anyhow = "1.0"
env_logger = "0.7.0"
rand = { version = "0.7.0", features = ['small_rng'] }
tempfile = "3.1.0"
wasmparser = "0.55"
wasmparser = "0.59"
wat = "1.0"

[dependencies.walrus]
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzz-utils/src/lib.rs
Expand Up @@ -417,7 +417,7 @@ impl TestCaseGenerator for WasmOptTtf {

// Only generate programs that wat2wasm can handle.
if let Ok(bytes) = wat::parse_bytes(&wat) {
if wasmparser::validate(&bytes, None).is_ok() {
if wasmparser::validate(&bytes).is_ok() {
return String::from_utf8(wat).unwrap();
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/tests/tests/round_trip/anyref1.wat
@@ -1,6 +1,6 @@
(module
(import "x" "y" (func (param anyref)))
(func (export "a") (param anyref)
(import "x" "y" (func (param externref)))
(func (export "a") (param externref)
local.get 0
call 0))

Expand Down
4 changes: 2 additions & 2 deletions crates/tests/tests/round_trip/anyref2.wat
@@ -1,6 +1,6 @@
(module
(table 1 anyref)
(func (export "a") (param i32) (result anyref)
(table 1 externref)
(func (export "a") (param i32) (result externref)
local.get 0
table.get 0))

Expand Down
6 changes: 3 additions & 3 deletions crates/tests/tests/round_trip/anyref3.wat
@@ -1,8 +1,8 @@
(module
(type (func))
(table 1 anyfunc)
(table 1 anyref)
(func (export "a") (param i32) (result anyref)
(table 1 funcref)
(table 1 externref)
(func (export "a") (param i32) (result externref)
local.get 0
call_indirect (type 0)
local.get 0
Expand Down
11 changes: 5 additions & 6 deletions src/ir/mod.rs
Expand Up @@ -527,12 +527,8 @@ pub enum Instr {
ty: ValType,
},

/// `ref.is_null $ty`
RefIsNull {
/// The type of value we're testing for null
#[walrus(skip_visit)]
ty: ValType,
},
/// `ref.is_null`
RefIsNull {},

/// `ref.func`
RefFunc {
Expand Down Expand Up @@ -946,14 +942,17 @@ pub enum UnaryOp {
I8x16Neg,
I8x16AnyTrue,
I8x16AllTrue,
I8x16Bitmask,
I16x8Abs,
I16x8Neg,
I16x8AnyTrue,
I16x8AllTrue,
I16x8Bitmask,
I32x4Abs,
I32x4Neg,
I32x4AnyTrue,
I32x4AllTrue,
I32x4Bitmask,
I64x2Neg,

F32x4Abs,
Expand Down
9 changes: 2 additions & 7 deletions src/module/data.rs
Expand Up @@ -185,20 +185,15 @@ impl Module {
&mut self,
section: wasmparser::DataSectionReader,
ids: &IndicesToIds,
data_count: Option<u32>,
) -> Result<()> {
log::debug!("parse data section");
if let Some(count) = data_count {
if count != section.get_count() {
bail!("data count section mismatches actual data section");
}
}
let preallocated = self.data.arena.len() > 0;
for (i, segment) in section.into_iter().enumerate() {
let segment = segment?;

// If we had the `DataCount` section, then we already pre-allocated
// a data segment. Otherwise, allocate one now.
let id = if data_count.is_some() {
let id = if preallocated {
ids.get_data(i as u32)?
} else {
self.data.arena.alloc_with_id(|id| Data {
Expand Down
3 changes: 3 additions & 0 deletions src/module/exports.rs
Expand Up @@ -142,6 +142,9 @@ impl Module {
Table => ExportItem::Table(ids.get_table(entry.index)?),
Memory => ExportItem::Memory(ids.get_memory(entry.index)?),
Global => ExportItem::Global(ids.get_global(entry.index)?),
Type | Module | Instance => {
unimplemented!("module linking not supported");
}
};
self.exports.arena.alloc_with_id(|id| Export {
id,
Expand Down
6 changes: 4 additions & 2 deletions src/module/functions/local_function/emit.rs
Expand Up @@ -498,11 +498,13 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> {
I8x16Neg => self.simd(0x61),
I8x16AnyTrue => self.simd(0x62),
I8x16AllTrue => self.simd(0x63),
I8x16Bitmask => self.simd(0x64),

I16x8Abs => self.simd(0x80),
I16x8Neg => self.simd(0x81),
I16x8AnyTrue => self.simd(0x82),
I16x8AllTrue => self.simd(0x83),
I16x8Bitmask => self.simd(0x84),
I16x8WidenLowI8x16S => self.simd(0x87),
I16x8WidenHighI8x16S => self.simd(0x88),
I16x8WidenLowI8x16U => self.simd(0x89),
Expand All @@ -512,6 +514,7 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> {
I32x4Neg => self.simd(0xa1),
I32x4AnyTrue => self.simd(0xa2),
I32x4AllTrue => self.simd(0xa3),
I32x4Bitmask => self.simd(0xa4),
I32x4WidenLowI16x8S => self.simd(0xa7),
I32x4WidenHighI16x8S => self.simd(0xa8),
I32x4WidenLowI16x8U => self.simd(0xa9),
Expand Down Expand Up @@ -802,9 +805,8 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> {
self.encoder.byte(0xd0);
e.ty.emit(self.encoder);
}
RefIsNull(e) => {
RefIsNull(_e) => {
self.encoder.byte(0xd1);
e.ty.emit(self.encoder);
}
RefFunc(e) => {
self.encoder.byte(0xd2);
Expand Down
15 changes: 11 additions & 4 deletions src/module/functions/local_function/mod.rs
Expand Up @@ -1198,10 +1198,9 @@ fn validate_instruction<'context>(
ctx.alloc_instr(RefNull { ty }, loc);
ctx.push_operand(Some(ty));
}
Operator::RefIsNull { ty } => {
let ty = ValType::parse(&ty)?;
ctx.pop_operand_expected(Some(ty))?;
ctx.alloc_instr(RefIsNull { ty }, loc);
Operator::RefIsNull => {
// ctx.pop_operand_expected(Some(ty))?;
ctx.alloc_instr(RefIsNull {}, loc);
ctx.push_operand(Some(I32));
}
Operator::RefFunc { function_index } => {
Expand Down Expand Up @@ -1462,6 +1461,10 @@ fn validate_instruction<'context>(
Operator::I32x4MaxS => binop(ctx, V128, BinaryOp::I32x4MaxS)?,
Operator::I32x4MaxU => binop(ctx, V128, BinaryOp::I32x4MaxU)?,

Operator::I8x16Bitmask => one_op(ctx, V128, I32, UnaryOp::I8x16Bitmask)?,
Operator::I16x8Bitmask => one_op(ctx, V128, I32, UnaryOp::I16x8Bitmask)?,
Operator::I32x4Bitmask => one_op(ctx, V128, I32, UnaryOp::I32x4Bitmask)?,

Operator::TableCopy {
src_table,
dst_table,
Expand Down Expand Up @@ -1497,6 +1500,10 @@ fn validate_instruction<'context>(
let elem = ctx.indices.get_element(segment)?;
ctx.alloc_instr(ElemDrop { elem }, loc);
}

Operator::ReturnCall { .. } | Operator::ReturnCallIndirect { .. } => {
unimplemented!("not supported");
}
}
Ok(())
}
33 changes: 7 additions & 26 deletions src/module/functions/mod.rs
Expand Up @@ -12,8 +12,8 @@ use crate::parse::IndicesToIds;
use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
use crate::ty::TypeId;
use crate::ty::ValType;
use anyhow::bail;
use std::cmp;
use wasmparser::{FuncValidator, FunctionBody, OperatorsReader};

#[cfg(feature = "parallel")]
use rayon::prelude::*;
Expand Down Expand Up @@ -323,25 +323,19 @@ impl Module {
/// Add the locally defined functions in the wasm module to this instance.
pub(crate) fn parse_local_functions(
&mut self,
section: wasmparser::CodeSectionReader,
function_section_count: u32,
functions: Vec<(FunctionBody<'_>, FuncValidator, OperatorsReader<'_>)>,
indices: &mut IndicesToIds,
on_instr_pos: Option<&(dyn Fn(&usize) -> InstrLocId + Sync + Send + 'static)>,
) -> Result<()> {
log::debug!("parse code section");
let amt = section.get_count();
if amt != function_section_count {
bail!("code and function sections must have same number of entries")
}
let num_imports = self.funcs.arena.len() - (amt as usize);
let num_imports = self.funcs.arena.len() - functions.len();

// First up serially create corresponding `LocalId` instances for all
// functions as well as extract the operators parser for each function.
// This is pretty tough to parallelize, but we can look into it later if
// necessary and it's a bottleneck!
let mut bodies = Vec::with_capacity(amt as usize);
for (i, body) in section.into_iter().enumerate() {
let body = body?;
let mut bodies = Vec::with_capacity(functions.len());
for (i, (body, _validator, ops)) in functions.into_iter().enumerate() {
let index = (num_imports + i) as u32;
let id = indices.get_func(index)?;
let ty = match self.funcs.arena[id].kind {
Expand Down Expand Up @@ -370,19 +364,7 @@ impl Module {
let results = type_.results().to_vec();
self.types.add_entry_ty(&results);

// WebAssembly local indices are 32 bits, so it's a validation error to
// have more than 2^32 locals. Sure enough there's a spec test for this!
let mut total = 0u32;
for local in body.get_locals_reader()? {
let (count, _) = local?;
total = match total.checked_add(count) {
Some(n) => n,
None => bail!("can't have more than 2^32 locals"),
};
}

// Now that we know we have a reasonable amount of locals, put them in
// our map.
// Next up comes all the locals of the function.
for local in body.get_locals_reader()? {
let (count, ty) = local?;
let ty = ValType::parse(&ty)?;
Expand All @@ -396,8 +378,7 @@ impl Module {
}
}

let body = body.get_operators_reader()?;
bodies.push((id, body, args, ty));
bodies.push((id, ops, args, ty));
}

// Wasm modules can often have a lot of functions and this operation can
Expand Down
16 changes: 12 additions & 4 deletions src/module/imports.rs
Expand Up @@ -117,14 +117,18 @@ impl Module {
match entry.ty {
wasmparser::ImportSectionEntryType::Function(idx) => {
let ty = ids.get_type(idx)?;
let id = self.add_import_func(entry.module, entry.field, ty);
let id = self.add_import_func(
entry.module,
entry.field.expect("module linking not supported"),
ty,
);
ids.push_func(id.0);
}
wasmparser::ImportSectionEntryType::Table(t) => {
let ty = ValType::parse(&t.element_type)?;
let id = self.add_import_table(
entry.module,
entry.field,
entry.field.expect("module linking not supported"),
t.limits.initial,
t.limits.maximum,
ty,
Expand All @@ -134,7 +138,7 @@ impl Module {
wasmparser::ImportSectionEntryType::Memory(m) => {
let id = self.add_import_memory(
entry.module,
entry.field,
entry.field.expect("module linking not supported"),
m.shared,
m.limits.initial,
m.limits.maximum,
Expand All @@ -144,12 +148,16 @@ impl Module {
wasmparser::ImportSectionEntryType::Global(g) => {
let id = self.add_import_global(
entry.module,
entry.field,
entry.field.expect("module linking not supported"),
ValType::parse(&g.content_type)?,
g.mutable,
);
ids.push_global(id.0);
}
wasmparser::ImportSectionEntryType::Module(_)
| wasmparser::ImportSectionEntryType::Instance(_) => {
unimplemented!("module linking not implemented");
}
}
}

Expand Down

0 comments on commit 58c4c8f

Please sign in to comment.