Skip to content

Commit

Permalink
Add wast parsing support for threads proposal
Browse files Browse the repository at this point in the history
Add support for the new `(thread ...)` and `(wait ...)` constructs
added in the upstream threads proposal.
  • Loading branch information
alexcrichton committed Oct 12, 2023
1 parent a1d01ab commit b318766
Show file tree
Hide file tree
Showing 42 changed files with 1,088 additions and 2 deletions.
2 changes: 2 additions & 0 deletions crates/wast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@ pub mod kw {
custom_keyword!(needed);
custom_keyword!(export_info = "export-info");
custom_keyword!(import_info = "import-info");
custom_keyword!(thread);
custom_keyword!(wait);
}

/// Common annotations used to parse WebAssembly text files.
Expand Down
63 changes: 62 additions & 1 deletion crates/wast/src/wast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ pub enum WastDirective<'a> {
span: Span,
exec: WastExecute<'a>,
},
Thread(WastThread<'a>),
Wait {
span: Span,
thread: Id<'a>,
},
}

impl WastDirective<'_> {
Expand All @@ -119,8 +124,10 @@ impl WastDirective<'_> {
| WastDirective::AssertExhaustion { span, .. }
| WastDirective::AssertUnlinkable { span, .. }
| WastDirective::AssertInvalid { span, .. }
| WastDirective::AssertException { span, .. } => *span,
| WastDirective::AssertException { span, .. }
| WastDirective::Wait { span, .. } => *span,
WastDirective::Invoke(i) => i.span,
WastDirective::Thread(t) => t.span,
}
}
}
Expand Down Expand Up @@ -192,6 +199,14 @@ impl<'a> Parse<'a> for WastDirective<'a> {
span,
exec: parser.parens(|p| p.parse())?,
})
} else if l.peek::<kw::thread>()? {
Ok(WastDirective::Thread(parser.parse()?))
} else if l.peek::<kw::wait>()? {
let span = parser.parse::<kw::wait>()?.0;
Ok(WastDirective::Wait {
span,
thread: parser.parse()?,
})
} else {
Err(l.error())
}
Expand Down Expand Up @@ -363,3 +378,49 @@ impl<'a> Parse<'a> for WastRet<'a> {
}
}
}

#[derive(Debug)]
#[allow(missing_docs)]
pub struct WastThread<'a> {
pub span: Span,
pub name: Id<'a>,
pub shared_module: Option<Id<'a>>,
pub directives: Vec<WastDirective<'a>>,
}

impl<'a> Parse<'a> for WastThread<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.depth_check()?;
let span = parser.parse::<kw::thread>()?.0;
let name = parser.parse()?;

let shared_module = if parser.peek2::<kw::shared>()? {
let name = parser.parens(|p| {
p.parse::<kw::shared>()?;
p.parens(|p| {
p.parse::<kw::module>()?;
p.parse()
})
})?;
Some(name)
} else {
None
};
let mut directives = Vec::new();
while !parser.is_empty() {
directives.push(parser.parens(|p| p.parse())?);
}
Ok(WastThread {
span,
name,
shared_module,
directives,
})

// if parser.peek::<WastRetCore<'_>>()? {
// Ok(WastRet::Core(parser.parse()?))
// } else {
// Ok(WastRet::Component(parser.parse()?))
// }
}
}
62 changes: 62 additions & 0 deletions tests/local/upstream-threads/LB.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
(module $Mem
(memory (export "shared") 1 1 shared)
)

(thread $T1 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 10 shared)
(func (export "run")
(local i32)
(i32.load (i32.const 4))
(local.set 0)
(i32.store (i32.const 0) (i32.const 1))

;; store results for checking
(i32.store (i32.const 24) (local.get 0))
)
)
(invoke "run")
)

(thread $T2 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
(local i32)
(i32.load (i32.const 0))
(local.set 0)
(i32.store (i32.const 4) (i32.const 1))

;; store results for checking
(i32.store (i32.const 32) (local.get 0))
)
)

(invoke "run")
)

(wait $T1)
(wait $T2)

(module $Check
(memory (import "mem" "shared") 1 1 shared)

(func (export "check") (result i32)
(local i32 i32)
(i32.load (i32.const 24))
(local.set 0)
(i32.load (i32.const 32))
(local.set 1)

;; allowed results: (L_0 = 0 || L_0 = 1) && (L_1 = 0 || L_1 = 1)

(i32.or (i32.eq (local.get 0) (i32.const 1)) (i32.eq (local.get 0) (i32.const 0)))
(i32.or (i32.eq (local.get 1) (i32.const 1)) (i32.eq (local.get 0) (i32.const 0)))
(i32.and)
(return)
)
)

(assert_return (invoke $Check "check") (i32.const 1))
64 changes: 64 additions & 0 deletions tests/local/upstream-threads/LB_atomic.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
(module $Mem
(memory (export "shared") 1 1 shared)
)

(thread $T1 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 10 shared)
(func (export "run")
(local i32)
(i32.atomic.load (i32.const 4))
(local.set 0)
(i32.atomic.store (i32.const 0) (i32.const 1))

;; store results for checking
(i32.store (i32.const 24) (local.get 0))
)
)
(invoke "run")
)

(thread $T2 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
(local i32)
(i32.atomic.load (i32.const 0))
(local.set 0)
(i32.atomic.store (i32.const 4) (i32.const 1))

;; store results for checking
(i32.store (i32.const 32) (local.get 0))
)
)

(invoke "run")
)

(wait $T1)
(wait $T2)

(module $Check
(memory (import "mem" "shared") 1 1 shared)

(func (export "check") (result i32)
(local i32 i32)
(i32.load (i32.const 24))
(local.set 0)
(i32.load (i32.const 32))
(local.set 1)

;; allowed results: (L_0 = 0 && L_1 = 0) || (L_0 = 0 && L_1 = 1) || (L_0 = 1 && L_1 = 0)

(i32.and (i32.eq (local.get 0) (i32.const 0)) (i32.eq (local.get 1) (i32.const 0)))
(i32.and (i32.eq (local.get 0) (i32.const 0)) (i32.eq (local.get 1) (i32.const 1)))
(i32.and (i32.eq (local.get 0) (i32.const 1)) (i32.eq (local.get 1) (i32.const 0)))
(i32.or)
(i32.or)
(return)
)
)

(assert_return (invoke $Check "check") (i32.const 1))
59 changes: 59 additions & 0 deletions tests/local/upstream-threads/MP.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
(module $Mem
(memory (export "shared") 1 1 shared)
)

(thread $T1 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 10 shared)
(func (export "run")
(i32.store (i32.const 0) (i32.const 42))
(i32.store (i32.const 4) (i32.const 1))
)
)
(invoke "run")
)

(thread $T2 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
(local i32 i32)
(i32.load (i32.const 4))
(local.set 0)
(i32.load (i32.const 0))
(local.set 1)

;; store results for checking
(i32.store (i32.const 24) (local.get 0))
(i32.store (i32.const 32) (local.get 1))
)
)

(invoke "run")
)

(wait $T1)
(wait $T2)

(module $Check
(memory (import "mem" "shared") 1 1 shared)

(func (export "check") (result i32)
(local i32 i32)
(i32.load (i32.const 24))
(local.set 0)
(i32.load (i32.const 32))
(local.set 1)

;; allowed results: (L_0 = 0 || L_0 = 1) && (L_1 = 0 || L_1 = 42)

(i32.or (i32.eq (local.get 0) (i32.const 1)) (i32.eq (local.get 0) (i32.const 0)))
(i32.or (i32.eq (local.get 1) (i32.const 42)) (i32.eq (local.get 0) (i32.const 0)))
(i32.and)
(return)
)
)

(assert_return (invoke $Check "check") (i32.const 1))
61 changes: 61 additions & 0 deletions tests/local/upstream-threads/MP_atomic.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
(module $Mem
(memory (export "shared") 1 1 shared)
)

(thread $T1 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 10 shared)
(func (export "run")
(i32.atomic.store (i32.const 0) (i32.const 42))
(i32.atomic.store (i32.const 4) (i32.const 1))
)
)
(invoke "run")
)

(thread $T2 (shared (module $Mem))
(register "mem" $Mem)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
(local i32 i32)
(i32.atomic.load (i32.const 4))
(local.set 0)
(i32.atomic.load (i32.const 0))
(local.set 1)

;; store results for checking
(i32.store (i32.const 24) (local.get 0))
(i32.store (i32.const 32) (local.get 1))
)
)

(invoke "run")
)

(wait $T1)
(wait $T2)

(module $Check
(memory (import "mem" "shared") 1 1 shared)

(func (export "check") (result i32)
(local i32 i32)
(i32.load (i32.const 24))
(local.set 0)
(i32.load (i32.const 32))
(local.set 1)

;; allowed results: (L_0 = 1 && L_1 = 42) || (L_0 = 0 && L_1 = 0) || (L_0 = 0 && L_1 = 42)

(i32.and (i32.eq (local.get 0) (i32.const 1)) (i32.eq (local.get 1) (i32.const 42)))
(i32.and (i32.eq (local.get 0) (i32.const 0)) (i32.eq (local.get 1) (i32.const 0)))
(i32.and (i32.eq (local.get 0) (i32.const 0)) (i32.eq (local.get 1) (i32.const 42)))
(i32.or)
(i32.or)
(return)
)
)

(assert_return (invoke $Check "check") (i32.const 1))
Loading

0 comments on commit b318766

Please sign in to comment.