summaryrefslogtreecommitdiff
path: root/src/wasm/wat-parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wat-parser.cpp')
-rw-r--r--src/wasm/wat-parser.cpp285
1 files changed, 269 insertions, 16 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index 1cc2623ca..16f33d2d7 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -158,6 +158,50 @@ struct ParseInput {
return false;
}
+ std::optional<uint64_t> takeOffset() {
+ if (auto t = peek()) {
+ if (auto keyword = t->getKeyword()) {
+ if (keyword->substr(0, 7) != "offset="sv) {
+ return {};
+ }
+ Lexer subLexer(keyword->substr(7));
+ if (subLexer == subLexer.end()) {
+ return {};
+ }
+ if (auto o = subLexer->getU64()) {
+ ++subLexer;
+ if (subLexer == subLexer.end()) {
+ ++lexer;
+ return o;
+ }
+ }
+ }
+ }
+ return {};
+ }
+
+ std::optional<uint32_t> takeAlign() {
+ if (auto t = peek()) {
+ if (auto keyword = t->getKeyword()) {
+ if (keyword->substr(0, 6) != "align="sv) {
+ return {};
+ }
+ Lexer subLexer(keyword->substr(6));
+ if (subLexer == subLexer.end()) {
+ return {};
+ }
+ if (auto a = subLexer->getU32()) {
+ ++subLexer;
+ if (subLexer == subLexer.end()) {
+ ++lexer;
+ return a;
+ }
+ }
+ }
+ }
+ return {};
+ }
+
std::optional<uint64_t> takeU64() {
if (auto t = peek()) {
if (auto n = t->getU64()) {
@@ -335,6 +379,11 @@ struct MemType {
bool shared;
};
+struct Memarg {
+ uint64_t offset;
+ uint32_t align;
+};
+
// RAII utility for temporarily changing the parsing position of a parsing
// context.
template<typename Ctx> struct WithPosition {
@@ -614,6 +663,8 @@ struct NullInstrParserCtx {
using GlobalT = Ok;
using MemoryT = Ok;
+ using MemargT = Ok;
+
InstrsT makeInstrs() { return Ok{}; }
void appendInstr(InstrsT&, InstrT) {}
InstrsT finishInstrs(InstrsT&) { return Ok{}; }
@@ -627,6 +678,8 @@ struct NullInstrParserCtx {
MemoryT getMemoryFromIdx(uint32_t) { return Ok{}; }
MemoryT getMemoryFromName(Name) { return Ok{}; }
+ MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; }
+
InstrT makeUnreachable(Index) { return Ok{}; }
InstrT makeNop(Index) { return Ok{}; }
InstrT makeBinary(Index, BinaryOp) { return Ok{}; }
@@ -647,12 +700,27 @@ struct NullInstrParserCtx {
InstrT makeI64Const(Index, uint64_t) { return Ok{}; }
InstrT makeF32Const(Index, float) { return Ok{}; }
InstrT makeF64Const(Index, double) { return Ok{}; }
-
+ InstrT makeLoad(Index, Type, bool, int, bool, MemoryT*, MemargT) {
+ return Ok{};
+ }
+ InstrT makeStore(Index, Type, int, bool, MemoryT*, MemargT) { return Ok{}; }
+ InstrT makeAtomicRMW(Index, AtomicRMWOp, Type, int, MemoryT*, MemargT) {
+ return Ok{};
+ }
+ InstrT makeAtomicCmpxchg(Index, Type, int, MemoryT*, MemargT) { return Ok{}; }
+ InstrT makeAtomicWait(Index, Type, MemoryT*, MemargT) { return Ok{}; }
+ InstrT makeAtomicNotify(Index, MemoryT*, MemargT) { return Ok{}; }
+ InstrT makeAtomicFence(Index) { return Ok{}; }
InstrT makeSIMDExtract(Index, SIMDExtractOp, uint8_t) { return Ok{}; }
InstrT makeSIMDReplace(Index, SIMDReplaceOp, uint8_t) { return Ok{}; }
InstrT makeSIMDShuffle(Index, const std::array<uint8_t, 16>&) { return Ok{}; }
InstrT makeSIMDTernary(Index, SIMDTernaryOp) { return Ok{}; }
InstrT makeSIMDShift(Index, SIMDShiftOp) { return Ok{}; }
+ InstrT makeSIMDLoad(Index, SIMDLoadOp, MemoryT*, MemargT) { return Ok{}; }
+ InstrT makeSIMDLoadStoreLane(
+ Index, SIMDLoadStoreLaneOp, MemoryT*, MemargT, uint8_t) {
+ return Ok{};
+ }
template<typename HeapTypeT> InstrT makeRefNull(Index, HeapTypeT) {
return {};
@@ -670,6 +738,8 @@ template<typename Ctx> struct InstrParserCtx : TypeParserCtx<Ctx> {
using GlobalT = Name;
using MemoryT = Name;
+ using MemargT = Memarg;
+
Builder builder;
// The stack of parsed expressions, used as the children of newly parsed
@@ -782,6 +852,8 @@ template<typename Ctx> struct InstrParserCtx : TypeParserCtx<Ctx> {
return std::move(exprStack);
}
+ Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; }
+
ExprT makeExpr(InstrsT& instrs) {
switch (instrs.size()) {
case 0:
@@ -856,6 +928,95 @@ template<typename Ctx> struct InstrParserCtx : TypeParserCtx<Ctx> {
Result<> makeF64Const(Index pos, double c) {
return push(pos, builder.makeConst(Literal(c)));
}
+ Result<> makeLoad(Index pos,
+ Type type,
+ bool signed_,
+ int bytes,
+ bool isAtomic,
+ Name* mem,
+ Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ if (isAtomic) {
+ return push(pos,
+ builder.makeAtomicLoad(bytes, memarg.offset, *ptr, type, *m));
+ }
+ return push(pos,
+ builder.makeLoad(
+ bytes, signed_, memarg.offset, memarg.align, *ptr, type, *m));
+ }
+ Result<> makeStore(
+ Index pos, Type type, int bytes, bool isAtomic, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto val = pop(pos);
+ CHECK_ERR(val);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ if (isAtomic) {
+ return push(
+ pos,
+ builder.makeAtomicStore(bytes, memarg.offset, *ptr, *val, type, *m));
+ }
+ return push(pos,
+ builder.makeStore(
+ bytes, memarg.offset, memarg.align, *ptr, *val, type, *m));
+ }
+ Result<> makeAtomicRMW(
+ Index pos, AtomicRMWOp op, Type type, int bytes, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto val = pop(pos);
+ CHECK_ERR(val);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(
+ pos,
+ builder.makeAtomicRMW(op, bytes, memarg.offset, *ptr, *val, type, *m));
+ }
+ Result<>
+ makeAtomicCmpxchg(Index pos, Type type, int bytes, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto replacement = pop(pos);
+ CHECK_ERR(replacement);
+ auto expected = pop(pos);
+ CHECK_ERR(expected);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(
+ pos,
+ builder.makeAtomicCmpxchg(
+ bytes, memarg.offset, *ptr, *expected, *replacement, type, *m));
+ }
+ Result<> makeAtomicWait(Index pos, Type type, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto timeout = pop(pos);
+ CHECK_ERR(timeout);
+ auto expected = pop(pos);
+ CHECK_ERR(expected);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(pos,
+ builder.makeAtomicWait(
+ *ptr, *expected, *timeout, type, memarg.offset, *m));
+ }
+ Result<> makeAtomicNotify(Index pos, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto count = pop(pos);
+ CHECK_ERR(count);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(pos, builder.makeAtomicNotify(*ptr, *count, memarg.offset, *m));
+ }
+ Result<> makeAtomicFence(Index pos) {
+ return push(pos, builder.makeAtomicFence());
+ }
+
Result<> makeRefNull(Index pos, HeapType type) {
return push(pos, builder.makeRefNull(type));
}
@@ -1507,6 +1668,28 @@ struct ParseDefsCtx : InstrParserCtx<ParseDefsCtx> {
CHECK_ERR(vec);
return push(pos, builder.makeSIMDShift(op, *vec, *shift));
}
+
+ Result<> makeSIMDLoad(Index pos, SIMDLoadOp op, Name* mem, Memarg memarg) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(
+ pos, builder.makeSIMDLoad(op, memarg.offset, memarg.align, *ptr, *m));
+ }
+
+ Result<> makeSIMDLoadStoreLane(
+ Index pos, SIMDLoadStoreLaneOp op, Name* mem, Memarg memarg, uint8_t lane) {
+ auto m = self().getMemory(pos, mem);
+ CHECK_ERR(m);
+ auto vec = pop(pos);
+ CHECK_ERR(vec);
+ auto ptr = pop(pos);
+ CHECK_ERR(ptr);
+ return push(pos,
+ builder.makeSIMDLoadStoreLane(
+ op, memarg.offset, memarg.align, lane, *ptr, *vec, *m));
+ }
};
// ================
@@ -1534,6 +1717,7 @@ template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx&);
template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx&);
template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx&);
+template<typename Ctx> Result<typename Ctx::MemargT> memarg(Ctx&, uint32_t);
template<typename Ctx>
Result<typename Ctx::InstrT> makeUnreachable(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeNop(Ctx&, Index);
@@ -1585,10 +1769,11 @@ Result<typename Ctx::InstrT> makeSIMDTernary(Ctx&, Index, SIMDTernaryOp op);
template<typename Ctx>
Result<typename Ctx::InstrT> makeSIMDShift(Ctx&, Index, SIMDShiftOp op);
template<typename Ctx>
-Result<typename Ctx::InstrT> makeSIMDLoad(Ctx&, Index, SIMDLoadOp op);
+Result<typename Ctx::InstrT>
+makeSIMDLoad(Ctx&, Index, SIMDLoadOp op, int bytes);
template<typename Ctx>
Result<typename Ctx::InstrT>
-makeSIMDLoadStoreLane(Ctx&, Index, SIMDLoadStoreLaneOp op);
+makeSIMDLoadStoreLane(Ctx&, Index, SIMDLoadStoreLaneOp op, int bytes);
template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryInit(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeDataDrop(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryCopy(Ctx&, Index);
@@ -2046,8 +2231,6 @@ template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx& ctx) {
return {};
}
- auto op = *keyword;
-
#define NEW_INSTRUCTION_PARSER
#define NEW_WAT_PARSER
#include <gen-s-parser.inc>
@@ -2123,6 +2306,22 @@ template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx& ctx) {
return ctx.makeExpr(*insts);
}
+// memarg_n ::= o:offset a:align_n
+// offset ::= 'offset='o:u64 => o | _ => 0
+// align_n ::= 'align='a:u32 => a | _ => n
+template<typename Ctx>
+Result<typename Ctx::MemargT> memarg(Ctx& ctx, uint32_t n) {
+ uint64_t offset = 0;
+ uint32_t align = n;
+ if (auto o = ctx.in.takeOffset()) {
+ offset = *o;
+ }
+ if (auto a = ctx.in.takeAlign()) {
+ align = *a;
+ }
+ return ctx.getMemarg(offset, align);
+}
+
template<typename Ctx>
Result<typename Ctx::InstrT> makeUnreachable(Ctx& ctx, Index pos) {
return ctx.makeUnreachable(pos);
@@ -2250,40 +2449,64 @@ Result<typename Ctx::InstrT> makeConst(Ctx& ctx, Index pos, Type type) {
template<typename Ctx>
Result<typename Ctx::InstrT> makeLoad(
Ctx& ctx, Index pos, Type type, bool signed_, int bytes, bool isAtomic) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeLoad(pos, type, signed_, bytes, isAtomic, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT>
makeStore(Ctx& ctx, Index pos, Type type, int bytes, bool isAtomic) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeStore(pos, type, bytes, isAtomic, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT>
makeAtomicRMW(Ctx& ctx, Index pos, AtomicRMWOp op, Type type, uint8_t bytes) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicRMW(pos, op, type, bytes, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT>
makeAtomicCmpxchg(Ctx& ctx, Index pos, Type type, uint8_t bytes) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicCmpxchg(pos, type, bytes, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT> makeAtomicWait(Ctx& ctx, Index pos, Type type) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, type == Type::i32 ? 4 : 8);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicWait(pos, type, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT> makeAtomicNotify(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, 4);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicNotify(pos, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT> makeAtomicFence(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
+ return ctx.makeAtomicFence(pos);
}
template<typename Ctx>
@@ -2332,14 +2555,44 @@ makeSIMDShift(Ctx& ctx, Index pos, SIMDShiftOp op) {
}
template<typename Ctx>
-Result<typename Ctx::InstrT> makeSIMDLoad(Ctx& ctx, Index pos, SIMDLoadOp op) {
- return ctx.in.err("unimplemented instruction");
+Result<typename Ctx::InstrT>
+makeSIMDLoad(Ctx& ctx, Index pos, SIMDLoadOp op, int bytes) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeSIMDLoad(pos, op, mem.getPtr(), *arg);
}
template<typename Ctx>
Result<typename Ctx::InstrT>
-makeSIMDLoadStoreLane(Ctx& ctx, Index pos, SIMDLoadStoreLaneOp op) {
- return ctx.in.err("unimplemented instruction");
+makeSIMDLoadStoreLane(Ctx& ctx, Index pos, SIMDLoadStoreLaneOp op, int bytes) {
+ auto reset = ctx.in.getPos();
+
+ auto retry = [&]() -> Result<typename Ctx::InstrT> {
+ // We failed to parse. Maybe the lane index was accidentally parsed as the
+ // optional memory index. Try again without parsing a memory index.
+ WithPosition with(ctx, reset);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return ctx.in.err("expected lane index");
+ }
+ return ctx.makeSIMDLoadStoreLane(pos, op, nullptr, *arg, *lane);
+ };
+
+ auto mem = maybeMemidx(ctx);
+ if (mem.getErr()) {
+ return retry();
+ }
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return retry();
+ }
+ return ctx.makeSIMDLoadStoreLane(pos, op, mem.getPtr(), *arg, *lane);
}
template<typename Ctx>