Skip to content

Commit

Permalink
Update wabt (bytecodealliance#38)
Browse files Browse the repository at this point in the history
* Add support for proposed select instruction with types

* Overhaul parsing of element segments

Lots of changes in the upstream bulk memory proposal, so let's reflect
the changes here as well:

* Encoding has entirely changed with respect to the flags field
* There's wonkiness to work around when dealing with reference tests
  coming from wabt
* Tidbits of new syntax and ways to parse element segments.

* Update the `wabt` submodule and tests

Add a few ignore tests which are a bit too onerous to get working right
now.

* Make it a parse error to have multiple start sections
  • Loading branch information
alexcrichton authored Dec 17, 2019
1 parent 90cd0e1 commit 723ab9a
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 94 deletions.
25 changes: 24 additions & 1 deletion crates/wast/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ instructions! {
ReturnCall(ast::Index<'a>) : [0x12] : "return_call",
ReturnCallIndirect(CallIndirect<'a>) : [0x13] : "return_call_indirect",
Drop : [0x1a] : "drop",
Select : [0x1b] : "select",
Select(SelectTypes) : [] : "select",
LocalGet(ast::Index<'a>) : [0x20] : "local.get" | "get_local",
LocalSet(ast::Index<'a>) : [0x21] : "local.set" | "set_local",
LocalTee(ast::Index<'a>) : [0x22] : "local.tee" | "tee_local",
Expand Down Expand Up @@ -1032,3 +1032,26 @@ impl<'a> Parse<'a> for V8x16Shuffle {
})
}
}

/// Payload of the `select` instructions
#[derive(Debug)]
pub struct SelectTypes {
#[allow(missing_docs)]
pub tys: Vec<ast::ValType>,
}

impl<'a> Parse<'a> for SelectTypes {
fn parse(parser: Parser<'a>) -> Result<Self> {
let mut tys = Vec::new();
while parser.peek2::<kw::result>() {
parser.parens(|p| {
p.parse::<kw::result>()?;
while !p.is_empty() {
tys.push(p.parse()?);
}
Ok(())
})?;
}
Ok(SelectTypes { tys })
}
}
16 changes: 16 additions & 0 deletions crates/wast/src/ast/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl<'a> Parse<'a> for Wat<'a> {
} else {
parser.parens(|parser| parser.parse())?
};
module.validate(parser)?;
Ok(Wat { module })
}
}
Expand Down Expand Up @@ -109,6 +110,21 @@ impl<'a> Module<'a> {
self.resolve()?;
Ok(crate::binary::encode(self))
}

fn validate(&self, parser: Parser<'_>) -> Result<()> {
let mut starts = 0;
if let ModuleKind::Text(fields) = &self.kind {
for item in fields.iter() {
if let ModuleField::Start(_) = item {
starts += 1;
}
}
}
if starts > 1 {
return Err(parser.error("multiple start sections found"))
}
Ok(())
}
}

impl<'a> Parse<'a> for Module<'a> {
Expand Down
153 changes: 95 additions & 58 deletions crates/wast/src/ast/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub enum TableKind<'a> {
elem: ast::TableElemType,
/// The element table entries to have, and the length of this list is
/// the limits of the table as well.
elems: Vec<ast::Index<'a>>,
payload: ElemPayload<'a>,
},
}

Expand All @@ -53,15 +53,16 @@ impl<'a> Parse<'a> for Table<'a> {
let mut l = parser.lookahead1();
let kind = if l.peek::<ast::TableElemType>() {
let elem = parser.parse()?;
let mut elems = Vec::new();
parser.parens(|p| {
let payload = parser.parens(|p| {
p.parse::<kw::elem>()?;
while !p.is_empty() {
elems.push(p.parse()?);
}
Ok(())
let ty = if parser.peek::<ast::LParen>() {
Some(elem)
} else {
None
};
ElemPayload::parse_tail(parser, ty)
})?;
TableKind::Inline { elem, elems }
TableKind::Inline { elem, payload }
} else if l.peek::<u32>() {
TableKind::Normal(parser.parse()?)
} else if l.peek::<ast::LParen>() {
Expand Down Expand Up @@ -95,88 +96,124 @@ pub struct Elem<'a> {
pub name: Option<ast::Id<'a>>,
/// The way this segment was defined in the module.
pub kind: ElemKind<'a>,
/// The payload of this element segment, typically a list of functions.
pub payload: ElemPayload<'a>,

// FIXME(WebAssembly/wabt#1268) we favor MVP encodings but our reference
// tests against wabt for us to use wabt's encoding, and it seems like we
// should remove this. At least we should remove it eventually for this
// specific library and fix the test harness to ignore the difference.
#[doc(hidden)]
pub force_nonzero_flags: bool,
}

/// Different ways to define an element segment in an mdoule.
#[derive(Debug)]
pub enum ElemKind<'a> {
/// A passive segment that isn't associated with a table and can be used in
/// various bulk-memory instructions.
Passive {
/// The type of elements within this segment.
ty: ast::TableElemType,
/// The function indices (for now) of elements in this segment. `None`
/// entries represent `ref.null` instructions.
elems: Vec<Option<ast::Index<'a>>>,
},
Passive,

/// An active segment associated with a table.
Active {
/// The table this `elem` is initializing.
table: ast::Index<'a>,
/// The offset within `table` that we'll initialize at.
offset: ast::Expression<'a>,
/// The function indices that will be inserted into the table.
elems: Vec<ast::Index<'a>>,
},
}

/// Different ways to define the element segment payload in a module.
#[derive(Debug)]
pub enum ElemPayload<'a> {
/// This element segment has a contiguous list of function indices
Indices(Vec<ast::Index<'a>>),

/// This element segment has a list of optional function indices,
/// represented as expressions using `ref.func` and `ref.null`.
Exprs {
/// The desired type of each expression below.
ty: ast::TableElemType,
/// The expressions, currently optional function indices, in this
/// segment.
exprs: Vec<Option<ast::Index<'a>>>,
},
}

impl<'a> Parse<'a> for Elem<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let span = parser.parse::<kw::elem>()?.0;
let mut name = parser.parse::<Option<_>>()?;
let name = parser.parse()?;
let mut force_nonzero_flags = false;

let kind = if parser.peek::<ast::TableElemType>() {
let ty = parser.parse::<ast::TableElemType>()?;
let mut elems = Vec::new();
if parser.peek::<ast::LParen>() {
while !parser.is_empty() {
elems.push(parser.parens(|p| {
let mut l = p.lookahead1();
if l.peek::<kw::ref_null>() {
p.parse::<kw::ref_null>()?;
Ok(None)
} else if l.peek::<kw::ref_func>() {
p.parse::<kw::ref_func>()?;
Ok(Some(p.parse()?))
} else {
Err(l.error())
}
})?);
}
let kind = if parser.peek::<u32>() || parser.peek::<ast::LParen>() {
let table = if parser.peek2::<kw::table>() {
force_nonzero_flags = true;
Some(parser.parens(|p| {
p.parse::<kw::table>()?;
p.parse()
})?)
} else if parser.peek::<u32>() {
Some(ast::Index::Num(parser.parse()?))
} else {
while !parser.is_empty() {
elems.push(Some(parser.parse()?));
}
}
ElemKind::Passive { ty, elems }
} else {
// TODO: this should be brought up with the bulk memory proposal,
// it's sort of ambiguous but apparently if one name is present it's
// a table name, but if two then it's an element name and a segment
// name. I'm a bit confused but this seems to pass tests.
let mut table = parser.parse::<Option<ast::Index>>()?;
if table.is_none() {
if let Some(name) = name.take() {
table = Some(ast::Index::Id(name));
}
}
None
};
let offset = parser.parens(|parser| {
if parser.peek::<kw::offset>() {
parser.parse::<kw::offset>()?;
}
parser.parse()
})?;
let mut elems = Vec::new();
while !parser.is_empty() {
elems.push(parser.parse()?);
}
ElemKind::Active {
table: table.unwrap_or(ast::Index::Num(0)),
offset,
elems,
}
} else {
ElemKind::Passive
};
Ok(Elem { span, name, kind })
let payload = parser.parse()?;
Ok(Elem {
span,
name,
kind,
payload,
force_nonzero_flags,
})
}
}

impl<'a> Parse<'a> for ElemPayload<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
ElemPayload::parse_tail(parser, parser.parse()?)
}
}

impl<'a> ElemPayload<'a> {
fn parse_tail(parser: Parser<'a>, ty: Option<ast::TableElemType>) -> Result<Self> {
if let Some(ty) = ty {
let mut exprs = Vec::new();
while !parser.is_empty() {
exprs.push(parser.parens(|p| {
let mut l = p.lookahead1();
if l.peek::<kw::ref_null>() {
p.parse::<kw::ref_null>()?;
Ok(None)
} else if l.peek::<kw::ref_func>() {
p.parse::<kw::ref_func>()?;
Ok(Some(p.parse()?))
} else {
Err(l.error())
}
})?);
}
Ok(ElemPayload::Exprs { exprs, ty })
} else {
parser.parse::<Option<kw::func>>()?;
let mut elems = Vec::new();
while !parser.is_empty() {
elems.push(parser.parse()?);
}
Ok(ElemPayload::Indices(elems))
}
}
}
86 changes: 67 additions & 19 deletions crates/wast/src/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,63 @@ impl Encode for Export<'_> {

impl Encode for Elem<'_> {
fn encode(&self, e: &mut Vec<u8>) {
match &self.kind {
ElemKind::Passive { ty, elems } => {
e.push(0x01);
match (&self.kind, &self.payload) {
(
ElemKind::Active {
table: Index::Num(0),
offset,
},
ElemPayload::Indices(_),
) if !self.force_nonzero_flags => {
e.push(0x00);
offset.encode(e);
}
(ElemKind::Passive, ElemPayload::Indices(_)) => {
e.push(0x01); // flags
e.push(0x00); // extern_kind
}
(ElemKind::Active { table, offset }, ElemPayload::Indices(_)) => {
e.push(0x02); // flags
table.encode(e);
offset.encode(e);
e.push(0x00); // extern_kind
}
(
ElemKind::Active {
table: Index::Num(0),
offset,
},
ElemPayload::Exprs {
ty: TableElemType::Funcref,
..
},
) => {
e.push(0x04);
offset.encode(e);
}
(ElemKind::Passive, ElemPayload::Exprs { ty, .. }) => {
e.push(0x05);
ty.encode(e);
}
(ElemKind::Active { table, offset }, ElemPayload::Exprs { ty, .. }) => {
e.push(0x06);
table.encode(e);
offset.encode(e);
ty.encode(e);
elems.len().encode(e);
for idx in elems {
}
}

self.payload.encode(e);
}
}

impl Encode for ElemPayload<'_> {
fn encode(&self, e: &mut Vec<u8>) {
match self {
ElemPayload::Indices(v) => v.encode(e),
ElemPayload::Exprs { exprs, .. } => {
exprs.len().encode(e);
for idx in exprs {
match idx {
Some(idx) => {
Instruction::RefFunc(*idx).encode(e);
Expand All @@ -358,20 +409,6 @@ impl Encode for Elem<'_> {
Instruction::End(None).encode(e);
}
}
ElemKind::Active {
table,
offset,
elems,
} => {
if *table == Index::Num(0) {
e.push(0x00);
} else {
e.push(0x02);
table.encode(e);
}
offset.encode(e);
elems.encode(e);
}
}
}
}
Expand Down Expand Up @@ -615,3 +652,14 @@ impl Encode for V8x16Shuffle {
dst.extend_from_slice(&self.lanes);
}
}

impl Encode for SelectTypes {
fn encode(&self, dst: &mut Vec<u8>) {
if self.tys.len() == 0 {
dst.push(0x1b);
} else {
dst.push(0x1c);
self.tys.encode(dst);
}
}
}
Loading

0 comments on commit 723ab9a

Please sign in to comment.