summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/CMakeLists.txt2
-rw-r--r--src/wasm/wat-lexer.cpp1038
-rw-r--r--src/wasm/wat-parser.cpp3895
3 files changed, 0 insertions, 4935 deletions
diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt
index 30f52f9ee..d5b4f6747 100644
--- a/src/wasm/CMakeLists.txt
+++ b/src/wasm/CMakeLists.txt
@@ -13,8 +13,6 @@ set(wasm_SOURCES
wasm-stack.cpp
wasm-type.cpp
wasm-validator.cpp
- wat-lexer.cpp
- wat-parser.cpp
${wasm_HEADERS}
)
# wasm-debug.cpp includes LLVM header using std::iterator (deprecated in C++17)
diff --git a/src/wasm/wat-lexer.cpp b/src/wasm/wat-lexer.cpp
deleted file mode 100644
index 264ffd40c..000000000
--- a/src/wasm/wat-lexer.cpp
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * Copyright 2022 WebAssembly Community Group participants
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cassert>
-#include <cctype>
-#include <cmath>
-#include <iostream>
-#include <optional>
-#include <sstream>
-#include <variant>
-
-#include "wat-lexer.h"
-
-using namespace std::string_view_literals;
-
-namespace wasm::WATParser {
-
-namespace {
-
-// ================
-// Lexical Analysis
-// ================
-
-// The result of lexing a token fragment.
-struct LexResult {
- std::string_view span;
-};
-
-// Lexing context that accumulates lexed input to produce a token fragment.
-struct LexCtx {
-private:
- // The input we are lexing.
- std::string_view input;
-
- // How much of the input we have already lexed.
- size_t lexedSize = 0;
-
-public:
- explicit LexCtx(std::string_view in) : input(in) {}
-
- // Return the fragment that has been lexed so far.
- std::optional<LexResult> lexed() const {
- if (lexedSize > 0) {
- return {LexResult{input.substr(0, lexedSize)}};
- }
- return {};
- }
-
- // The next input that has not already been lexed.
- std::string_view next() const { return input.substr(lexedSize); }
-
- // Get the next character without consuming it.
- uint8_t peek() const { return next()[0]; }
-
- // The size of the unlexed input.
- size_t size() const { return input.size() - lexedSize; }
-
- // Whether there is no more input.
- bool empty() const { return size() == 0; }
-
- // Tokens must be separated by spaces or parentheses.
- bool canFinish() const;
-
- // Whether the unlexed input starts with prefix `sv`.
- size_t startsWith(std::string_view sv) const {
- return next().substr(0, sv.size()) == sv;
- }
-
- // Consume the next `n` characters.
- void take(size_t n) { lexedSize += n; }
-
- // Consume an additional lexed fragment.
- void take(const LexResult& res) { lexedSize += res.span.size(); }
-
- // Consume the prefix and return true if possible.
- bool takePrefix(std::string_view sv) {
- if (startsWith(sv)) {
- take(sv.size());
- return true;
- }
- return false;
- }
-
- // Consume the rest of the input.
- void takeAll() { lexedSize = input.size(); }
-};
-
-enum OverflowBehavior { DisallowOverflow, IgnoreOverflow };
-
-std::optional<int> getDigit(char c) {
- if ('0' <= c && c <= '9') {
- return c - '0';
- }
- return {};
-}
-
-std::optional<int> getHexDigit(char c) {
- if ('0' <= c && c <= '9') {
- return c - '0';
- }
- if ('A' <= c && c <= 'F') {
- return 10 + c - 'A';
- }
- if ('a' <= c && c <= 'f') {
- return 10 + c - 'a';
- }
- return {};
-}
-
-// The result of lexing an integer token fragment.
-struct LexIntResult : LexResult {
- uint64_t n;
- Sign sign;
-};
-
-// Lexing context that accumulates lexed input to produce an integer token
-// fragment.
-struct LexIntCtx : LexCtx {
- using LexCtx::take;
-
-private:
- uint64_t n = 0;
- Sign sign = NoSign;
- bool overflow = false;
-
-public:
- explicit LexIntCtx(std::string_view in) : LexCtx(in) {}
-
- // Lex only the underlying span, ignoring the overflow and value.
- std::optional<LexIntResult> lexedRaw() {
- if (auto basic = LexCtx::lexed()) {
- return LexIntResult{*basic, 0, NoSign};
- }
- return {};
- }
-
- std::optional<LexIntResult> lexed() {
- if (overflow) {
- return {};
- }
- if (auto basic = LexCtx::lexed()) {
- return LexIntResult{*basic, sign == Neg ? -n : n, sign};
- }
- return {};
- }
-
- void takeSign() {
- if (takePrefix("+"sv)) {
- sign = Pos;
- } else if (takePrefix("-"sv)) {
- sign = Neg;
- } else {
- sign = NoSign;
- }
- }
-
- bool takeDigit() {
- if (!empty()) {
- if (auto d = getDigit(peek())) {
- take(1);
- uint64_t newN = n * 10 + *d;
- if (newN < n) {
- overflow = true;
- }
- n = newN;
- return true;
- }
- }
- return false;
- }
-
- bool takeHexdigit() {
- if (!empty()) {
- if (auto h = getHexDigit(peek())) {
- take(1);
- uint64_t newN = n * 16 + *h;
- if (newN < n) {
- overflow = true;
- }
- n = newN;
- return true;
- }
- }
- return false;
- }
-
- void take(const LexIntResult& res) {
- LexCtx::take(res);
- n = res.n;
- }
-};
-
-struct LexFloatResult : LexResult {
- // The payload if we lexed a nan with payload. We cannot store the payload
- // directly in `d` because we do not know at this point whether we are parsing
- // an f32 or f64 and therefore we do not know what the allowable payloads are.
- // No payload with NaN means to use the default payload for the expected float
- // width.
- std::optional<uint64_t> nanPayload;
- double d;
-};
-
-struct LexFloatCtx : LexCtx {
- std::optional<uint64_t> nanPayload;
-
- LexFloatCtx(std::string_view in) : LexCtx(in) {}
-
- std::optional<LexFloatResult> lexed() {
- const double posNan = std::copysign(NAN, 1.0);
- const double negNan = std::copysign(NAN, -1.0);
- assert(!std::signbit(posNan) && "expected positive NaN to be positive");
- assert(std::signbit(negNan) && "expected negative NaN to be negative");
- auto basic = LexCtx::lexed();
- if (!basic) {
- return {};
- }
- // strtod does not return NaNs with the expected signs on all platforms.
- // TODO: use starts_with once we have C++20.
- if (basic->span.substr(0, 3) == "nan"sv ||
- basic->span.substr(0, 4) == "+nan"sv) {
- return LexFloatResult{*basic, nanPayload, posNan};
- }
- if (basic->span.substr(0, 4) == "-nan"sv) {
- return LexFloatResult{*basic, nanPayload, negNan};
- }
- // Do not try to implement fully general and precise float parsing
- // ourselves. Instead, call out to std::strtod to do our parsing. This means
- // we need to strip any underscores since `std::strtod` does not understand
- // them.
- std::stringstream ss;
- for (const char *curr = basic->span.data(),
- *end = curr + basic->span.size();
- curr != end;
- ++curr) {
- if (*curr != '_') {
- ss << *curr;
- }
- }
- std::string str = ss.str();
- char* last;
- double d = std::strtod(str.data(), &last);
- assert(last == str.data() + str.size() && "could not parse float");
- return LexFloatResult{*basic, {}, d};
- }
-};
-
-struct LexStrResult : LexResult {
- // Allocate a string only if there are escape sequences, otherwise just use
- // the original string_view.
- std::optional<std::string> str;
-};
-
-struct LexStrCtx : LexCtx {
-private:
- // Used to build a string with resolved escape sequences. Only used when the
- // parsed string contains escape sequences, otherwise we can just use the
- // parsed string directly.
- std::optional<std::stringstream> escapeBuilder;
-
-public:
- LexStrCtx(std::string_view in) : LexCtx(in) {}
-
- std::optional<LexStrResult> lexed() {
- if (auto basic = LexCtx::lexed()) {
- if (escapeBuilder) {
- return LexStrResult{*basic, {escapeBuilder->str()}};
- } else {
- return LexStrResult{*basic, {}};
- }
- }
- return {};
- }
-
- void takeChar() {
- if (escapeBuilder) {
- *escapeBuilder << peek();
- }
- LexCtx::take(1);
- }
-
- void ensureBuildingEscaped() {
- if (escapeBuilder) {
- return;
- }
- // Drop the opening '"'.
- escapeBuilder = std::stringstream{};
- *escapeBuilder << LexCtx::lexed()->span.substr(1);
- }
-
- void appendEscaped(char c) { *escapeBuilder << c; }
-
- bool appendUnicode(uint64_t u) {
- if ((0xd800 <= u && u < 0xe000) || 0x110000 <= u) {
- return false;
- }
- if (u < 0x80) {
- // 0xxxxxxx
- *escapeBuilder << uint8_t(u);
- } else if (u < 0x800) {
- // 110xxxxx 10xxxxxx
- *escapeBuilder << uint8_t(0b11000000 | ((u >> 6) & 0b00011111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
- } else if (u < 0x10000) {
- // 1110xxxx 10xxxxxx 10xxxxxx
- *escapeBuilder << uint8_t(0b11100000 | ((u >> 12) & 0b00001111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 6) & 0b00111111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
- } else {
- // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- *escapeBuilder << uint8_t(0b11110000 | ((u >> 18) & 0b00000111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 12) & 0b00111111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 6) & 0b00111111));
- *escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
- }
- return true;
- }
-};
-
-std::optional<LexResult> lparen(std::string_view in) {
- LexCtx ctx(in);
- ctx.takePrefix("("sv);
- return ctx.lexed();
-}
-
-std::optional<LexResult> rparen(std::string_view in) {
- LexCtx ctx(in);
- ctx.takePrefix(")"sv);
- return ctx.lexed();
-}
-
-// comment ::= linecomment | blockcomment
-// linecomment ::= ';;' linechar* ('\n' | eof)
-// linechar ::= c:char (if c != '\n')
-// blockcomment ::= '(;' blockchar* ';)'
-// blockchar ::= c:char (if c != ';' and c != '(')
-// | ';' (if the next char is not ')')
-// | '(' (if the next char is not ';')
-// | blockcomment
-std::optional<LexResult> comment(std::string_view in) {
- LexCtx ctx(in);
- if (ctx.size() < 2) {
- return {};
- }
-
- // Line comment
- if (ctx.takePrefix(";;"sv)) {
- if (auto size = ctx.next().find('\n'); size != ""sv.npos) {
- ctx.take(size);
- } else {
- ctx.takeAll();
- }
- return ctx.lexed();
- }
-
- // Block comment (possibly nested!)
- if (ctx.takePrefix("(;"sv)) {
- size_t depth = 1;
- while (depth > 0 && ctx.size() >= 2) {
- if (ctx.takePrefix("(;"sv)) {
- ++depth;
- } else if (ctx.takePrefix(";)"sv)) {
- --depth;
- } else {
- ctx.take(1);
- }
- }
- if (depth > 0) {
- // TODO: Add error production for non-terminated block comment.
- return {};
- }
- return ctx.lexed();
- }
-
- return {};
-}
-
-std::optional<LexResult> spacechar(std::string_view in) {
- LexCtx ctx(in);
- ctx.takePrefix(" "sv) || ctx.takePrefix("\n"sv) || ctx.takePrefix("\r"sv) ||
- ctx.takePrefix("\t"sv);
- return ctx.lexed();
-}
-
-// space ::= (' ' | format | comment)*
-// format ::= '\t' | '\n' | '\r'
-std::optional<LexResult> space(std::string_view in) {
- LexCtx ctx(in);
- while (ctx.size()) {
- if (auto lexed = spacechar(ctx.next())) {
- ctx.take(*lexed);
- } else if (auto lexed = comment(ctx.next())) {
- ctx.take(*lexed);
- } else {
- break;
- }
- }
- return ctx.lexed();
-}
-
-bool LexCtx::canFinish() const {
- // Logically we want to check for eof, parens, and space. But we don't
- // actually want to parse more than a couple characters of space, so check for
- // individual space chars or comment starts instead.
- return empty() || lparen(next()) || rparen(next()) || spacechar(next()) ||
- startsWith(";;"sv);
-}
-
-// num ::= d:digit => d
-// | n:num '_'? d:digit => 10*n + d
-// digit ::= '0' => 0 | ... | '9' => 9
-std::optional<LexIntResult> num(std::string_view in,
- OverflowBehavior overflow = DisallowOverflow) {
- LexIntCtx ctx(in);
- if (ctx.empty()) {
- return {};
- }
- if (!ctx.takeDigit()) {
- return {};
- }
- while (true) {
- bool under = ctx.takePrefix("_"sv);
- if (!ctx.takeDigit()) {
- if (!under) {
- return overflow == DisallowOverflow ? ctx.lexed() : ctx.lexedRaw();
- }
- // TODO: Add error production for trailing underscore.
- return {};
- }
- }
-}
-
-// hexnum ::= h:hexdigit => h
-// | n:hexnum '_'? h:hexdigit => 16*n + h
-// hexdigit ::= d:digit => d
-// | 'A' => 10 | ... | 'F' => 15
-// | 'a' => 10 | ... | 'f' => 15
-std::optional<LexIntResult>
-hexnum(std::string_view in, OverflowBehavior overflow = DisallowOverflow) {
- LexIntCtx ctx(in);
- if (!ctx.takeHexdigit()) {
- return {};
- }
- while (true) {
- bool under = ctx.takePrefix("_"sv);
- if (!ctx.takeHexdigit()) {
- if (!under) {
- return overflow == DisallowOverflow ? ctx.lexed() : ctx.lexedRaw();
- }
- // TODO: Add error production for trailing underscore.
- return {};
- }
- }
-}
-
-// uN ::= n:num => n (if n < 2^N)
-// | '0x' n:hexnum => n (if n < 2^N)
-// sN ::= s:sign n:num => [s]n (if -2^(N-1) <= [s]n < 2^(N-1))
-// | s:sign '0x' n:hexnum => [s]n (if -2^(N-1) <= [s]n < 2^(N-1))
-// sign ::= {} => + | '+' => + | '-' => -
-//
-// Note: Defer bounds and sign checking until we know what kind of integer we
-// expect.
-std::optional<LexIntResult> integer(std::string_view in) {
- LexIntCtx ctx(in);
- ctx.takeSign();
- if (ctx.takePrefix("0x"sv)) {
- if (auto lexed = hexnum(ctx.next())) {
- ctx.take(*lexed);
- if (ctx.canFinish()) {
- return ctx.lexed();
- }
- }
- // TODO: Add error production for unrecognized hexnum.
- return {};
- }
- if (auto lexed = num(ctx.next())) {
- ctx.take(*lexed);
- if (ctx.canFinish()) {
- return ctx.lexed();
- }
- }
- return {};
-}
-
-// float ::= p:num '.'? => p
-// | p:num '.' q:frac => p + q
-// | p:num '.'? ('E'|'e') s:sign e:num => p * 10^([s]e)
-// | p:num '.' q:frac ('E'|'e') s:sign e:num => (p + q) * 10^([s]e)
-// frac ::= d:digit => d/10
-// | d:digit '_'? p:frac => (d + p/10) / 10
-std::optional<LexResult> decfloat(std::string_view in) {
- LexCtx ctx(in);
- if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- } else {
- return {};
- }
- // Optional '.' followed by optional frac
- if (ctx.takePrefix("."sv)) {
- if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- }
- }
- if (ctx.takePrefix("E"sv) || ctx.takePrefix("e"sv)) {
- // Optional sign
- ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
- if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- } else {
- // TODO: Add error production for missing exponent.
- return {};
- }
- }
- return ctx.lexed();
-}
-
-// hexfloat ::= '0x' p:hexnum '.'? => p
-// | '0x' p:hexnum '.' q:hexfrac => p + q
-// | '0x' p:hexnum '.'? ('P'|'p') s:sign e:num => p * 2^([s]e)
-// | '0x' p:hexnum '.' q:hexfrac ('P'|'p') s:sign e:num
-// => (p + q) * 2^([s]e)
-// hexfrac ::= h:hexdigit => h/16
-// | h:hexdigit '_'? p:hexfrac => (h + p/16) / 16
-std::optional<LexResult> hexfloat(std::string_view in) {
- LexCtx ctx(in);
- if (!ctx.takePrefix("0x"sv)) {
- return {};
- }
- if (auto lexed = hexnum(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- } else {
- return {};
- }
- // Optional '.' followed by optional hexfrac
- if (ctx.takePrefix("."sv)) {
- if (auto lexed = hexnum(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- }
- }
- if (ctx.takePrefix("P"sv) || ctx.takePrefix("p"sv)) {
- // Optional sign
- ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
- if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
- ctx.take(*lexed);
- } else {
- // TODO: Add error production for missing exponent.
- return {};
- }
- }
- return ctx.lexed();
-}
-
-// fN ::= s:sign z:fNmag => [s]z
-// fNmag ::= z:float => float_N(z) (if float_N(z) != +/-infinity)
-// | z:hexfloat => float_N(z) (if float_N(z) != +/-infinity)
-// | 'inf' => infinity
-// | 'nan' => nan(2^(signif(N)-1))
-// | 'nan:0x' n:hexnum => nan(n) (if 1 <= n < 2^signif(N))
-std::optional<LexFloatResult> float_(std::string_view in) {
- LexFloatCtx ctx(in);
- // Optional sign
- ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
- if (auto lexed = hexfloat(ctx.next())) {
- ctx.take(*lexed);
- } else if (auto lexed = decfloat(ctx.next())) {
- ctx.take(*lexed);
- } else if (ctx.takePrefix("inf"sv)) {
- // nop
- } else if (ctx.takePrefix("nan"sv)) {
- if (ctx.takePrefix(":0x"sv)) {
- if (auto lexed = hexnum(ctx.next())) {
- ctx.take(*lexed);
- ctx.nanPayload = lexed->n;
- } else {
- // TODO: Add error production for malformed NaN payload.
- return {};
- }
- } else {
- // No explicit payload necessary; we will inject the default payload
- // later.
- }
- } else {
- return {};
- }
- if (ctx.canFinish()) {
- return ctx.lexed();
- }
- return {};
-}
-
-// idchar ::= '0' | ... | '9'
-// | 'A' | ... | 'Z'
-// | 'a' | ... | 'z'
-// | '!' | '#' | '$' | '%' | '&' | ''' | '*' | '+'
-// | '-' | '.' | '/' | ':' | '<' | '=' | '>' | '?'
-// | '@' | '\' | '^' | '_' | '`' | '|' | '~'
-std::optional<LexResult> idchar(std::string_view in) {
- LexCtx ctx(in);
- if (ctx.empty()) {
- return {};
- }
- uint8_t c = ctx.peek();
- if (('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') ||
- ('a' <= c && c <= 'z')) {
- ctx.take(1);
- } else {
- switch (c) {
- case '!':
- case '#':
- case '$':
- case '%':
- case '&':
- case '\'':
- case '*':
- case '+':
- case '-':
- case '.':
- case '/':
- case ':':
- case '<':
- case '=':
- case '>':
- case '?':
- case '@':
- case '\\':
- case '^':
- case '_':
- case '`':
- case '|':
- case '~':
- ctx.take(1);
- }
- }
- return ctx.lexed();
-}
-
-// id ::= '$' idchar+
-std::optional<LexResult> ident(std::string_view in) {
- LexCtx ctx(in);
- if (!ctx.takePrefix("$"sv)) {
- return {};
- }
- if (auto lexed = idchar(ctx.next())) {
- ctx.take(*lexed);
- } else {
- return {};
- }
- while (auto lexed = idchar(ctx.next())) {
- ctx.take(*lexed);
- }
- if (ctx.canFinish()) {
- return ctx.lexed();
- }
- return {};
-}
-
-// string ::= '"' (b*:stringelem)* '"' => concat((b*)*)
-// (if |concat((b*)*)| < 2^32)
-// stringelem ::= c:stringchar => utf8(c)
-// | '\' n:hexdigit m:hexdigit => 16*n + m
-// stringchar ::= c:char => c
-// (if c >= U+20 && c != U+7f && c != '"' && c != '\')
-// | '\t' => \t | '\n' => \n | '\r' => \r
-// | '\\' => \ | '\"' => " | '\'' => '
-// | '\u{' n:hexnum '}' => U+(n)
-// (if n < 0xD800 and 0xE000 <= n <= 0x110000)
-std::optional<LexStrResult> str(std::string_view in) {
- LexStrCtx ctx(in);
- if (!ctx.takePrefix("\""sv)) {
- return {};
- }
- while (!ctx.takePrefix("\""sv)) {
- if (ctx.empty()) {
- // TODO: Add error production for unterminated string.
- return {};
- }
- if (ctx.startsWith("\\"sv)) {
- // Escape sequences
- ctx.ensureBuildingEscaped();
- ctx.take(1);
- if (ctx.takePrefix("t"sv)) {
- ctx.appendEscaped('\t');
- } else if (ctx.takePrefix("n"sv)) {
- ctx.appendEscaped('\n');
- } else if (ctx.takePrefix("r"sv)) {
- ctx.appendEscaped('\r');
- } else if (ctx.takePrefix("\\"sv)) {
- ctx.appendEscaped('\\');
- } else if (ctx.takePrefix("\""sv)) {
- ctx.appendEscaped('"');
- } else if (ctx.takePrefix("'"sv)) {
- ctx.appendEscaped('\'');
- } else if (ctx.takePrefix("u{"sv)) {
- auto lexed = hexnum(ctx.next());
- if (!lexed) {
- // TODO: Add error production for malformed unicode escapes.
- return {};
- }
- ctx.take(*lexed);
- if (!ctx.takePrefix("}"sv)) {
- // TODO: Add error production for malformed unicode escapes.
- return {};
- }
- if (!ctx.appendUnicode(lexed->n)) {
- // TODO: Add error production for invalid unicode values.
- return {};
- }
- } else {
- LexIntCtx ictx(ctx.next());
- if (!ictx.takeHexdigit() || !ictx.takeHexdigit()) {
- // TODO: Add error production for unrecognized escape sequence.
- return {};
- }
- auto lexed = *ictx.lexed();
- ctx.take(lexed);
- ctx.appendEscaped(char(lexed.n));
- }
- } else {
- // Normal characters
- if (uint8_t c = ctx.peek(); c >= 0x20 && c != 0x7F) {
- ctx.takeChar();
- } else {
- // TODO: Add error production for unescaped control characters.
- return {};
- }
- }
- }
- return ctx.lexed();
-}
-
-// keyword ::= ( 'a' | ... | 'z' ) idchar* (if literal terminal in grammar)
-// reserved ::= idchar+
-//
-// The "keyword" token we lex here covers both keywords as well as any reserved
-// tokens that match the keyword format. This saves us from having to enumerate
-// all the valid keywords here. These invalid keywords will still produce
-// errors, just at a higher level of the parser.
-std::optional<LexResult> keyword(std::string_view in) {
- LexCtx ctx(in);
- if (ctx.empty()) {
- return {};
- }
- uint8_t start = ctx.peek();
- if ('a' <= start && start <= 'z') {
- ctx.take(1);
- } else {
- return {};
- }
- while (auto lexed = idchar(ctx.next())) {
- ctx.take(*lexed);
- }
- return ctx.lexed();
-}
-
-} // anonymous namespace
-
-std::optional<uint64_t> Token::getU64() const {
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == NoSign) {
- return tok->n;
- }
- }
- return {};
-}
-
-std::optional<int64_t> Token::getS64() const {
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == Neg) {
- if (uint64_t(INT64_MIN) <= tok->n || tok->n == 0) {
- return int64_t(tok->n);
- }
- // TODO: Add error production for signed underflow.
- } else {
- if (tok->n <= uint64_t(INT64_MAX)) {
- return int64_t(tok->n);
- }
- // TODO: Add error production for signed overflow.
- }
- }
- return {};
-}
-
-std::optional<uint64_t> Token::getI64() const {
- if (auto n = getU64()) {
- return *n;
- }
- if (auto n = getS64()) {
- return *n;
- }
- return {};
-}
-
-std::optional<uint32_t> Token::getU32() const {
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == NoSign && tok->n <= UINT32_MAX) {
- return int32_t(tok->n);
- }
- // TODO: Add error production for unsigned overflow.
- }
- return {};
-}
-
-std::optional<int32_t> Token::getS32() const {
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == Neg) {
- if (uint64_t(INT32_MIN) <= tok->n || tok->n == 0) {
- return int32_t(tok->n);
- }
- } else {
- if (tok->n <= uint64_t(INT32_MAX)) {
- return int32_t(tok->n);
- }
- }
- }
- return {};
-}
-
-std::optional<uint32_t> Token::getI32() const {
- if (auto n = getU32()) {
- return *n;
- }
- if (auto n = getS32()) {
- return uint32_t(*n);
- }
- return {};
-}
-
-std::optional<double> Token::getF64() const {
- constexpr int signif = 52;
- constexpr uint64_t payloadMask = (1ull << signif) - 1;
- constexpr uint64_t nanDefault = 1ull << (signif - 1);
- if (auto* tok = std::get_if<FloatTok>(&data)) {
- double d = tok->d;
- if (std::isnan(d)) {
- // Inject payload.
- uint64_t payload = tok->nanPayload ? *tok->nanPayload : nanDefault;
- if (payload == 0 || payload > payloadMask) {
- // TODO: Add error production for out-of-bounds payload.
- return {};
- }
- uint64_t bits;
- static_assert(sizeof(bits) == sizeof(d));
- memcpy(&bits, &d, sizeof(bits));
- bits = (bits & ~payloadMask) | payload;
- memcpy(&d, &bits, sizeof(bits));
- }
- return d;
- }
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == Neg) {
- if (tok->n == 0) {
- return -0.0;
- }
- return double(int64_t(tok->n));
- }
- return double(tok->n);
- }
- return {};
-}
-
-std::optional<float> Token::getF32() const {
- constexpr int signif = 23;
- constexpr uint32_t payloadMask = (1u << signif) - 1;
- constexpr uint64_t nanDefault = 1ull << (signif - 1);
- if (auto* tok = std::get_if<FloatTok>(&data)) {
- float f = tok->d;
- if (std::isnan(f)) {
- // Validate and inject payload.
- uint64_t payload = tok->nanPayload ? *tok->nanPayload : nanDefault;
- if (payload == 0 || payload > payloadMask) {
- // TODO: Add error production for out-of-bounds payload.
- return {};
- }
- uint32_t bits;
- static_assert(sizeof(bits) == sizeof(f));
- memcpy(&bits, &f, sizeof(bits));
- bits = (bits & ~payloadMask) | payload;
- memcpy(&f, &bits, sizeof(bits));
- }
- return f;
- }
- if (auto* tok = std::get_if<IntTok>(&data)) {
- if (tok->sign == Neg) {
- if (tok->n == 0) {
- return -0.0f;
- }
- return float(int64_t(tok->n));
- }
- return float(tok->n);
- }
- return {};
-}
-
-std::optional<std::string_view> Token::getString() const {
- if (auto* tok = std::get_if<StringTok>(&data)) {
- if (tok->str) {
- return std::string_view(*tok->str);
- }
- return span.substr(1, span.size() - 2);
- }
- return {};
-}
-
-void Lexer::skipSpace() {
- if (auto ctx = space(next())) {
- index += ctx->span.size();
- }
-}
-
-void Lexer::lexToken() {
- // TODO: Ensure we're getting the longest possible match.
- Token tok;
- if (auto t = lparen(next())) {
- tok = Token{t->span, LParenTok{}};
- } else if (auto t = rparen(next())) {
- tok = Token{t->span, RParenTok{}};
- } else if (auto t = ident(next())) {
- tok = Token{t->span, IdTok{}};
- } else if (auto t = integer(next())) {
- tok = Token{t->span, IntTok{t->n, t->sign}};
- } else if (auto t = float_(next())) {
- tok = Token{t->span, FloatTok{t->nanPayload, t->d}};
- } else if (auto t = str(next())) {
- tok = Token{t->span, StringTok{t->str}};
- } else if (auto t = keyword(next())) {
- tok = Token{t->span, KeywordTok{}};
- } else {
- // TODO: Do something about lexing errors.
- curr = std::nullopt;
- return;
- }
- index += tok.span.size();
- curr = {tok};
-}
-
-TextPos Lexer::position(const char* c) const {
- assert(size_t(c - buffer.data()) <= buffer.size());
- TextPos pos{1, 0};
- for (const char* p = buffer.data(); p != c; ++p) {
- if (*p == '\n') {
- pos.line++;
- pos.col = 0;
- } else {
- pos.col++;
- }
- }
- return pos;
-}
-
-bool TextPos::operator==(const TextPos& other) const {
- return line == other.line && col == other.col;
-}
-
-bool IntTok::operator==(const IntTok& other) const {
- return n == other.n && sign == other.sign;
-}
-
-bool FloatTok::operator==(const FloatTok& other) const {
- return std::signbit(d) == std::signbit(other.d) &&
- (d == other.d || (std::isnan(d) && std::isnan(other.d) &&
- nanPayload == other.nanPayload));
-}
-
-bool Token::operator==(const Token& other) const {
- return span == other.span &&
- std::visit(
- [](auto& t1, auto& t2) {
- if constexpr (std::is_same_v<decltype(t1), decltype(t2)>) {
- return t1 == t2;
- } else {
- return false;
- }
- },
- data,
- other.data);
-}
-
-std::ostream& operator<<(std::ostream& os, const TextPos& pos) {
- return os << pos.line << ":" << pos.col;
-}
-
-std::ostream& operator<<(std::ostream& os, const LParenTok&) {
- return os << "'('";
-}
-
-std::ostream& operator<<(std::ostream& os, const RParenTok&) {
- return os << "')'";
-}
-
-std::ostream& operator<<(std::ostream& os, const IdTok&) { return os << "id"; }
-
-std::ostream& operator<<(std::ostream& os, const IntTok& tok) {
- return os << (tok.sign == Pos ? "+" : tok.sign == Neg ? "-" : "") << tok.n;
-}
-
-std::ostream& operator<<(std::ostream& os, const FloatTok& tok) {
- if (std::isnan(tok.d)) {
- os << (std::signbit(tok.d) ? "+" : "-");
- if (tok.nanPayload) {
- return os << "nan:0x" << std::hex << *tok.nanPayload << std::dec;
- }
- return os << "nan";
- }
- return os << tok.d;
-}
-
-std::ostream& operator<<(std::ostream& os, const StringTok& tok) {
- if (tok.str) {
- os << '"' << *tok.str << '"';
- } else {
- os << "(raw string)";
- }
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const KeywordTok&) {
- return os << "keyword";
-}
-
-std::ostream& operator<<(std::ostream& os, const Token& tok) {
- std::visit([&](const auto& t) { os << t; }, tok.data);
- return os << " \"" << tok.span << "\"";
-}
-
-} // namespace wasm::WATParser
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
deleted file mode 100644
index b31019811..000000000
--- a/src/wasm/wat-parser.cpp
+++ /dev/null
@@ -1,3895 +0,0 @@
-/*
- * Copyright 2022 WebAssembly Community Group participants
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "wat-parser.h"
-#include "ir/names.h"
-#include "support/name.h"
-#include "wasm-builder.h"
-#include "wasm-ir-builder.h"
-#include "wasm-type.h"
-#include "wasm.h"
-#include "wat-lexer.h"
-
-// The WebAssembly text format is recursive in the sense that elements may be
-// referred to before they are declared. Furthermore, elements may be referred
-// to by index or by name. As a result, we need to parse text modules in
-// multiple phases.
-//
-// In the first phase, we find all of the module element declarations and
-// record, but do not interpret, the input spans of their corresponding
-// definitions. This phase establishes the indices and names of each module
-// element so that subsequent phases can look them up.
-//
-// The second phase parses type definitions to construct the types used in the
-// module. This has to be its own phase because we have no way to refer to a
-// type before it has been built along with all the other types, unlike for
-// other module elements that can be referred to by name before their
-// definitions have been parsed.
-//
-// The third phase further parses and constructs types implicitly defined by
-// type uses in functions, blocks, and call_indirect instructions. These
-// implicitly defined types may be referred to by index elsewhere.
-//
-// The fourth phase parses and sets the types of globals, functions, and other
-// top-level module elements. These types need to be set before we parse
-// instructions because they determine the types of instructions such as
-// global.get and ref.func.
-//
-// The fifth and final phase parses the remaining contents of all module
-// elements, including instructions.
-//
-// Each phase of parsing gets its own context type that is passed to the
-// individual parsing functions. There is a parsing function for each element of
-// the grammar given in the spec. Parsing functions are templatized so that they
-// may be passed the appropriate context type and return the correct result type
-// for each phase.
-
-using namespace std::string_view_literals;
-
-namespace wasm::WATParser {
-
-namespace {
-
-// ============
-// Parser Input
-// ============
-
-// Wraps a lexer and provides utilities for consuming tokens.
-struct ParseInput {
- Lexer lexer;
-
- explicit ParseInput(std::string_view in) : lexer(in) {}
-
- ParseInput(std::string_view in, size_t index) : lexer(in) {
- lexer.setIndex(index);
- }
-
- ParseInput(const ParseInput& other, size_t index) : lexer(other.lexer) {
- lexer.setIndex(index);
- }
-
- bool empty() { return lexer.empty(); }
-
- std::optional<Token> peek() {
- if (!empty()) {
- return *lexer;
- }
- return {};
- }
-
- bool takeLParen() {
- auto t = peek();
- if (!t || !t->isLParen()) {
- return false;
- }
- ++lexer;
- return true;
- }
-
- bool takeRParen() {
- auto t = peek();
- if (!t || !t->isRParen()) {
- return false;
- }
- ++lexer;
- return true;
- }
-
- bool takeUntilParen() {
- while (true) {
- auto t = peek();
- if (!t) {
- return false;
- }
- if (t->isLParen() || t->isRParen()) {
- return true;
- }
- ++lexer;
- }
- }
-
- std::optional<Name> takeID() {
- if (auto t = peek()) {
- if (auto id = t->getID()) {
- ++lexer;
- // See comment on takeName.
- return Name(std::string(*id));
- }
- }
- return {};
- }
-
- std::optional<std::string_view> takeKeyword() {
- if (auto t = peek()) {
- if (auto keyword = t->getKeyword()) {
- ++lexer;
- return *keyword;
- }
- }
- return {};
- }
-
- bool takeKeyword(std::string_view expected) {
- if (auto t = peek()) {
- if (auto keyword = t->getKeyword()) {
- if (*keyword == expected) {
- ++lexer;
- return true;
- }
- }
- }
- 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::nullopt;
- }
-
- 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()) {
- ++lexer;
- return n;
- }
- }
- return std::nullopt;
- }
-
- std::optional<int64_t> takeS64() {
- if (auto t = peek()) {
- if (auto n = t->getS64()) {
- ++lexer;
- return n;
- }
- }
- return {};
- }
-
- std::optional<int64_t> takeI64() {
- if (auto t = peek()) {
- if (auto n = t->getI64()) {
- ++lexer;
- return n;
- }
- }
- return {};
- }
-
- std::optional<uint32_t> takeU32() {
- if (auto t = peek()) {
- if (auto n = t->getU32()) {
- ++lexer;
- return n;
- }
- }
- return std::nullopt;
- }
-
- std::optional<int32_t> takeS32() {
- if (auto t = peek()) {
- if (auto n = t->getS32()) {
- ++lexer;
- return n;
- }
- }
- return {};
- }
-
- std::optional<int32_t> takeI32() {
- if (auto t = peek()) {
- if (auto n = t->getI32()) {
- ++lexer;
- return n;
- }
- }
- return {};
- }
-
- std::optional<uint8_t> takeU8() {
- if (auto t = peek()) {
- if (auto n = t->getU32()) {
- if (n <= std::numeric_limits<uint8_t>::max()) {
- ++lexer;
- return uint8_t(*n);
- }
- }
- }
- return {};
- }
-
- std::optional<double> takeF64() {
- if (auto t = peek()) {
- if (auto d = t->getF64()) {
- ++lexer;
- return d;
- }
- }
- return std::nullopt;
- }
-
- std::optional<float> takeF32() {
- if (auto t = peek()) {
- if (auto f = t->getF32()) {
- ++lexer;
- return f;
- }
- }
- return std::nullopt;
- }
-
- std::optional<std::string_view> takeString() {
- if (auto t = peek()) {
- if (auto s = t->getString()) {
- ++lexer;
- return s;
- }
- }
- return {};
- }
-
- std::optional<Name> takeName() {
- // TODO: Move this to lexer and validate UTF.
- if (auto str = takeString()) {
- // Copy to a std::string to make sure we have a null terminator, otherwise
- // the `Name` constructor won't work correctly.
- // TODO: Update `Name` to use string_view instead of char* and/or to take
- // rvalue strings to avoid this extra copy.
- return Name(std::string(*str));
- }
- return {};
- }
-
- bool takeSExprStart(std::string_view expected) {
- auto original = lexer;
- if (takeLParen() && takeKeyword(expected)) {
- return true;
- }
- lexer = original;
- return false;
- }
-
- Index getPos() {
- if (auto t = peek()) {
- return lexer.getIndex() - t->span.size();
- }
- return lexer.getIndex();
- }
-
- [[nodiscard]] Err err(Index pos, std::string reason) {
- std::stringstream msg;
- msg << lexer.position(pos) << ": error: " << reason;
- return Err{msg.str()};
- }
-
- [[nodiscard]] Err err(std::string reason) { return err(getPos(), reason); }
-};
-
-// =========
-// Utilities
-// =========
-
-// The location, possible name, and index in the respective module index space
-// of a module-level definition in the input.
-struct DefPos {
- Name name;
- Index pos;
- Index index;
-};
-
-struct GlobalType {
- Mutability mutability;
- Type type;
-};
-
-// A signature type and parameter names (possibly empty), used for parsing
-// function types.
-struct TypeUse {
- HeapType type;
- std::vector<Name> names;
-};
-
-struct ImportNames {
- Name mod;
- Name nm;
-};
-
-struct Limits {
- uint64_t initial;
- uint64_t max;
-};
-
-struct MemType {
- Type type;
- Limits limits;
- 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 {
- Ctx& ctx;
- Index original;
-
- WithPosition(Ctx& ctx, Index pos) : ctx(ctx), original(ctx.in.getPos()) {
- ctx.in.lexer.setIndex(pos);
- }
-
- ~WithPosition() { ctx.in.lexer.setIndex(original); }
-};
-
-// Deduction guide to satisfy -Wctad-maybe-unsupported.
-template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>;
-
-using IndexMap = std::unordered_map<Name, Index>;
-
-void applyImportNames(Importable& item, ImportNames* names) {
- if (names) {
- item.module = names->mod;
- item.base = names->nm;
- }
-}
-
-Result<> addExports(ParseInput& in,
- Module& wasm,
- const Named* item,
- const std::vector<Name>& exports,
- ExternalKind kind) {
- for (auto name : exports) {
- if (wasm.getExportOrNull(name)) {
- // TODO: Fix error location
- return in.err("repeated export name");
- }
- wasm.addExport(Builder(wasm).makeExport(name, item->name, kind));
- }
- return Ok{};
-}
-
-Result<IndexMap> createIndexMap(ParseInput& in,
- const std::vector<DefPos>& defs) {
- IndexMap indices;
- for (auto& def : defs) {
- if (def.name.is()) {
- if (!indices.insert({def.name, def.index}).second) {
- return in.err(def.pos, "duplicate element name");
- }
- }
- }
- return indices;
-}
-
-std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named) {
- std::vector<Type> types;
- types.reserve(named.size());
- for (auto& t : named) {
- types.push_back(t.type);
- }
- return types;
-}
-
-template<typename Ctx>
-Result<> parseDefs(Ctx& ctx,
- const std::vector<DefPos>& defs,
- MaybeResult<> (*parser)(Ctx&)) {
- for (auto& def : defs) {
- ctx.index = def.index;
- WithPosition with(ctx, def.pos);
- auto parsed = parser(ctx);
- CHECK_ERR(parsed);
- assert(parsed);
- }
- return Ok{};
-}
-
-// ===============
-// Parser Contexts
-// ===============
-
-struct NullTypeParserCtx {
- using IndexT = Ok;
- using HeapTypeT = Ok;
- using TypeT = Ok;
- using ParamsT = Ok;
- using ResultsT = size_t;
- using BlockTypeT = Ok;
- using SignatureT = Ok;
- using StorageT = Ok;
- using FieldT = Ok;
- using FieldsT = Ok;
- using StructT = Ok;
- using ArrayT = Ok;
- using LimitsT = Ok;
- using MemTypeT = Ok;
- using GlobalTypeT = Ok;
- using TypeUseT = Ok;
- using LocalsT = Ok;
- using DataStringT = Ok;
-
- HeapTypeT makeFunc() { return Ok{}; }
- HeapTypeT makeAny() { return Ok{}; }
- HeapTypeT makeExtern() { return Ok{}; }
- HeapTypeT makeEq() { return Ok{}; }
- HeapTypeT makeI31() { return Ok{}; }
- HeapTypeT makeStructType() { return Ok{}; }
- HeapTypeT makeArrayType() { return Ok{}; }
-
- TypeT makeI32() { return Ok{}; }
- TypeT makeI64() { return Ok{}; }
- TypeT makeF32() { return Ok{}; }
- TypeT makeF64() { return Ok{}; }
- TypeT makeV128() { return Ok{}; }
-
- TypeT makeRefType(HeapTypeT, Nullability) { return Ok{}; }
-
- ParamsT makeParams() { return Ok{}; }
- void appendParam(ParamsT&, Name, TypeT) {}
-
- // We have to count results because whether or not a block introduces a
- // typeuse that may implicitly define a type depends on how many results it
- // has.
- size_t makeResults() { return 0; }
- void appendResult(size_t& results, TypeT) { ++results; }
- size_t getResultsSize(size_t results) { return results; }
-
- SignatureT makeFuncType(ParamsT*, ResultsT*) { return Ok{}; }
-
- StorageT makeI8() { return Ok{}; }
- StorageT makeI16() { return Ok{}; }
- StorageT makeStorageType(TypeT) { return Ok{}; }
-
- FieldT makeFieldType(StorageT, Mutability) { return Ok{}; }
-
- FieldsT makeFields() { return Ok{}; }
- void appendField(FieldsT&, Name, FieldT) {}
-
- StructT makeStruct(FieldsT&) { return Ok{}; }
-
- std::optional<ArrayT> makeArray(FieldsT&) { return Ok{}; }
-
- GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
-
- LocalsT makeLocals() { return Ok{}; }
- void appendLocal(LocalsT&, Name, TypeT) {}
-
- Result<Index> getTypeIndex(Name) { return 1; }
- Result<HeapTypeT> getHeapTypeFromIdx(Index) { return Ok{}; }
-
- DataStringT makeDataString() { return Ok{}; }
- void appendDataString(DataStringT&, std::string_view) {}
-
- MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
-
- BlockTypeT getBlockTypeFromResult(size_t results) { return Ok{}; }
-
- Result<> getBlockTypeFromTypeUse(Index, TypeUseT) { return Ok{}; }
-};
-
-template<typename Ctx> struct TypeParserCtx {
- using IndexT = Index;
- using HeapTypeT = HeapType;
- using TypeT = Type;
- using ParamsT = std::vector<NameType>;
- using ResultsT = std::vector<Type>;
- using BlockTypeT = HeapType;
- using SignatureT = Signature;
- using StorageT = Field;
- using FieldT = Field;
- using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>;
- using StructT = std::pair<std::vector<Name>, Struct>;
- using ArrayT = Array;
- using LimitsT = Ok;
- using MemTypeT = Ok;
- using LocalsT = std::vector<NameType>;
- using DataStringT = Ok;
-
- // Map heap type names to their indices.
- const IndexMap& typeIndices;
-
- TypeParserCtx(const IndexMap& typeIndices) : typeIndices(typeIndices) {}
-
- Ctx& self() { return *static_cast<Ctx*>(this); }
-
- HeapTypeT makeFunc() { return HeapType::func; }
- HeapTypeT makeAny() { return HeapType::any; }
- HeapTypeT makeExtern() { return HeapType::ext; }
- HeapTypeT makeEq() { return HeapType::eq; }
- HeapTypeT makeI31() { return HeapType::i31; }
- HeapTypeT makeStructType() { return HeapType::struct_; }
- HeapTypeT makeArrayType() { return HeapType::array; }
-
- TypeT makeI32() { return Type::i32; }
- TypeT makeI64() { return Type::i64; }
- TypeT makeF32() { return Type::f32; }
- TypeT makeF64() { return Type::f64; }
- TypeT makeV128() { return Type::v128; }
-
- TypeT makeRefType(HeapTypeT ht, Nullability nullability) {
- return Type(ht, nullability);
- }
-
- TypeT makeTupleType(const std::vector<Type> types) { return Tuple(types); }
-
- ParamsT makeParams() { return {}; }
- void appendParam(ParamsT& params, Name id, TypeT type) {
- params.push_back({id, type});
- }
-
- ResultsT makeResults() { return {}; }
- void appendResult(ResultsT& results, TypeT type) { results.push_back(type); }
- size_t getResultsSize(const ResultsT& results) { return results.size(); }
-
- SignatureT makeFuncType(ParamsT* params, ResultsT* results) {
- std::vector<Type> empty;
- const auto& paramTypes = params ? getUnnamedTypes(*params) : empty;
- const auto& resultTypes = results ? *results : empty;
- return Signature(self().makeTupleType(paramTypes),
- self().makeTupleType(resultTypes));
- }
-
- StorageT makeI8() { return Field(Field::i8, Immutable); }
- StorageT makeI16() { return Field(Field::i16, Immutable); }
- StorageT makeStorageType(TypeT type) { return Field(type, Immutable); }
-
- FieldT makeFieldType(FieldT field, Mutability mutability) {
- if (field.packedType == Field::not_packed) {
- return Field(field.type, mutability);
- }
- return Field(field.packedType, mutability);
- }
-
- FieldsT makeFields() { return {}; }
- void appendField(FieldsT& fields, Name name, FieldT field) {
- fields.first.push_back(name);
- fields.second.push_back(field);
- }
-
- StructT makeStruct(FieldsT& fields) {
- return {std::move(fields.first), Struct(std::move(fields.second))};
- }
-
- std::optional<ArrayT> makeArray(FieldsT& fields) {
- if (fields.second.size() == 1) {
- return Array(fields.second[0]);
- }
- return {};
- }
-
- LocalsT makeLocals() { return {}; }
- void appendLocal(LocalsT& locals, Name id, TypeT type) {
- locals.push_back({id, type});
- }
-
- Result<Index> getTypeIndex(Name id) {
- auto it = typeIndices.find(id);
- if (it == typeIndices.end()) {
- return self().in.err("unknown type identifier");
- }
- return it->second;
- }
-
- DataStringT makeDataString() { return Ok{}; }
- void appendDataString(DataStringT&, std::string_view) {}
-
- LimitsT makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; }
- LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
-
- MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
-
- HeapType getBlockTypeFromResult(const std::vector<Type> results) {
- assert(results.size() == 1);
- return HeapType(Signature(Type::none, results[0]));
- }
-};
-
-struct NullInstrParserCtx {
- using InstrT = Ok;
- using InstrsT = Ok;
- using ExprT = Ok;
-
- using FieldIdxT = Ok;
- using LocalIdxT = Ok;
- using GlobalIdxT = Ok;
- using MemoryIdxT = Ok;
- using DataIdxT = Ok;
-
- using MemargT = Ok;
-
- InstrsT makeInstrs() { return Ok{}; }
- void appendInstr(InstrsT&, InstrT) {}
- InstrsT finishInstrs(InstrsT&) { return Ok{}; }
-
- ExprT makeExpr(InstrsT) { return Ok{}; }
- Result<ExprT> instrToExpr(InstrT) { return Ok{}; }
-
- template<typename HeapTypeT> FieldIdxT getFieldFromIdx(HeapTypeT, uint32_t) {
- return Ok{};
- }
- template<typename HeapTypeT> FieldIdxT getFieldFromName(HeapTypeT, Name) {
- return Ok{};
- }
- LocalIdxT getLocalFromIdx(uint32_t) { return Ok{}; }
- LocalIdxT getLocalFromName(Name) { return Ok{}; }
- GlobalIdxT getGlobalFromIdx(uint32_t) { return Ok{}; }
- GlobalIdxT getGlobalFromName(Name) { return Ok{}; }
- MemoryIdxT getMemoryFromIdx(uint32_t) { return Ok{}; }
- MemoryIdxT getMemoryFromName(Name) { return Ok{}; }
- DataIdxT getDataFromIdx(uint32_t) { return Ok{}; }
- DataIdxT getDataFromName(Name) { return Ok{}; }
-
- MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; }
-
- template<typename BlockTypeT>
- InstrT makeBlock(Index, std::optional<Name>, BlockTypeT) {
- return Ok{};
- }
- InstrT finishBlock(Index, InstrsT) { return Ok{}; }
-
- InstrT makeUnreachable(Index) { return Ok{}; }
- InstrT makeNop(Index) { return Ok{}; }
- InstrT makeBinary(Index, BinaryOp) { return Ok{}; }
- InstrT makeUnary(Index, UnaryOp) { return Ok{}; }
- template<typename ResultsT> InstrT makeSelect(Index, ResultsT*) {
- return Ok{};
- }
- InstrT makeDrop(Index) { return Ok{}; }
- InstrT makeMemorySize(Index, MemoryIdxT*) { return Ok{}; }
- InstrT makeMemoryGrow(Index, MemoryIdxT*) { return Ok{}; }
- InstrT makeLocalGet(Index, LocalIdxT) { return Ok{}; }
- InstrT makeLocalTee(Index, LocalIdxT) { return Ok{}; }
- InstrT makeLocalSet(Index, LocalIdxT) { return Ok{}; }
- InstrT makeGlobalGet(Index, GlobalIdxT) { return Ok{}; }
- InstrT makeGlobalSet(Index, GlobalIdxT) { return Ok{}; }
-
- InstrT makeI32Const(Index, uint32_t) { return Ok{}; }
- 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, MemoryIdxT*, MemargT) {
- return Ok{};
- }
- InstrT makeStore(Index, Type, int, bool, MemoryIdxT*, MemargT) {
- return Ok{};
- }
- InstrT makeAtomicRMW(Index, AtomicRMWOp, Type, int, MemoryIdxT*, MemargT) {
- return Ok{};
- }
- InstrT makeAtomicCmpxchg(Index, Type, int, MemoryIdxT*, MemargT) {
- return Ok{};
- }
- InstrT makeAtomicWait(Index, Type, MemoryIdxT*, MemargT) { return Ok{}; }
- InstrT makeAtomicNotify(Index, MemoryIdxT*, 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, MemoryIdxT*, MemargT) { return Ok{}; }
- InstrT makeSIMDLoadStoreLane(
- Index, SIMDLoadStoreLaneOp, MemoryIdxT*, MemargT, uint8_t) {
- return Ok{};
- }
- InstrT makeMemoryInit(Index, MemoryIdxT*, DataIdxT) { return Ok{}; }
- InstrT makeDataDrop(Index, DataIdxT) { return Ok{}; }
-
- InstrT makeMemoryCopy(Index, MemoryIdxT*, MemoryIdxT*) { return Ok{}; }
- InstrT makeMemoryFill(Index, MemoryIdxT*) { return Ok{}; }
-
- InstrT makeReturn(Index) { return Ok{}; }
- template<typename HeapTypeT> InstrT makeRefNull(Index, HeapTypeT) {
- return Ok{};
- }
- InstrT makeRefIsNull(Index) { return Ok{}; }
-
- InstrT makeRefEq(Index) { return Ok{}; }
-
- InstrT makeRefI31(Index) { return Ok{}; }
- InstrT makeI31Get(Index, bool) { return Ok{}; }
-
- template<typename HeapTypeT> InstrT makeStructNew(Index, HeapTypeT) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeStructNewDefault(Index, HeapTypeT) {
- return Ok{};
- }
- template<typename HeapTypeT>
- InstrT makeStructGet(Index, HeapTypeT, FieldIdxT, bool) {
- return Ok{};
- }
- template<typename HeapTypeT>
- InstrT makeStructSet(Index, HeapTypeT, FieldIdxT) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeArrayNew(Index, HeapTypeT) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeArrayNewDefault(Index, HeapTypeT) {
- return Ok{};
- }
- template<typename HeapTypeT>
- InstrT makeArrayNewData(Index, HeapTypeT, DataIdxT) {
- return Ok{};
- }
- template<typename HeapTypeT>
- InstrT makeArrayNewElem(Index, HeapTypeT, DataIdxT) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeArrayGet(Index, HeapTypeT, bool) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeArraySet(Index, HeapTypeT) {
- return Ok{};
- }
- InstrT makeArrayLen(Index) { return Ok{}; }
- template<typename HeapTypeT>
- InstrT makeArrayCopy(Index, HeapTypeT, HeapTypeT) {
- return Ok{};
- }
- template<typename HeapTypeT> InstrT makeArrayFill(Index, HeapTypeT) {
- return Ok{};
- }
-};
-
-// Phase 1: Parse definition spans for top-level module elements and determine
-// their indices and names.
-struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
- using DataStringT = std::vector<char>;
- using LimitsT = Limits;
- using MemTypeT = MemType;
-
- ParseInput in;
-
- // At this stage we only look at types to find implicit type definitions,
- // which are inserted directly into the context. We cannot materialize or
- // validate any types because we don't know what types exist yet.
- //
- // Declared module elements are inserted into the module, but their bodies are
- // not filled out until later parsing phases.
- Module& wasm;
-
- // The module element definitions we are parsing in this phase.
- std::vector<DefPos> typeDefs;
- std::vector<DefPos> subtypeDefs;
- std::vector<DefPos> funcDefs;
- std::vector<DefPos> memoryDefs;
- std::vector<DefPos> globalDefs;
- std::vector<DefPos> dataDefs;
-
- // Positions of typeuses that might implicitly define new types.
- std::vector<Index> implicitTypeDefs;
-
- // Counters used for generating names for module elements.
- int funcCounter = 0;
- int memoryCounter = 0;
- int globalCounter = 0;
- int dataCounter = 0;
-
- // Used to verify that all imports come before all non-imports.
- bool hasNonImport = false;
-
- ParseDeclsCtx(std::string_view in, Module& wasm) : in(in), wasm(wasm) {}
-
- void addFuncType(SignatureT) {}
- void addStructType(StructT) {}
- void addArrayType(ArrayT) {}
- void setOpen() {}
- Result<> addSubtype(Index) { return Ok{}; }
- void finishSubtype(Name name, Index pos) {
- subtypeDefs.push_back({name, pos, Index(subtypeDefs.size())});
- }
- size_t getRecGroupStartIndex() { return 0; }
- void addRecGroup(Index, size_t) {}
- void finishDeftype(Index pos) {
- typeDefs.push_back({{}, pos, Index(typeDefs.size())});
- }
-
- std::vector<char> makeDataString() { return {}; }
- void appendDataString(std::vector<char>& data, std::string_view str) {
- data.insert(data.end(), str.begin(), str.end());
- }
-
- Limits makeLimits(uint64_t n, std::optional<uint64_t> m) {
- return m ? Limits{n, *m} : Limits{n, Memory::kUnlimitedSize};
- }
- Limits getLimitsFromData(const std::vector<char>& data) {
- uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize;
- return {size, size};
- }
-
- MemType makeMemType(Type type, Limits limits, bool shared) {
- return {type, limits, shared};
- }
-
- Result<TypeUseT>
- makeTypeUse(Index pos, std::optional<HeapTypeT> type, ParamsT*, ResultsT*) {
- if (!type) {
- implicitTypeDefs.push_back(pos);
- }
- return Ok{};
- }
-
- Result<Function*>
- addFuncDecl(Index pos, Name name, ImportNames* importNames) {
- auto f = std::make_unique<Function>();
- if (name.is()) {
- if (wasm.getFunctionOrNull(name)) {
- // TDOO: if the existing function is not explicitly named, fix its name
- // and continue.
- return in.err(pos, "repeated function name");
- }
- f->setExplicitName(name);
- } else {
- name = (importNames ? "fimport$" : "") + std::to_string(funcCounter++);
- name = Names::getValidFunctionName(wasm, name);
- f->name = name;
- }
- applyImportNames(*f, importNames);
- return wasm.addFunction(std::move(f));
- }
-
- Result<> addFunc(Name name,
- const std::vector<Name>& exports,
- ImportNames* import,
- TypeUseT type,
- std::optional<LocalsT>,
- std::optional<InstrsT>,
- Index pos) {
- if (import && hasNonImport) {
- return in.err(pos, "import after non-import");
- }
- auto f = addFuncDecl(pos, name, import);
- CHECK_ERR(f);
- CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function));
- funcDefs.push_back({name, pos, Index(funcDefs.size())});
- return Ok{};
- }
-
- Result<Memory*>
- addMemoryDecl(Index pos, Name name, ImportNames* importNames, MemType type) {
- auto m = std::make_unique<Memory>();
- m->indexType = type.type;
- m->initial = type.limits.initial;
- m->max = type.limits.max;
- m->shared = type.shared;
- if (name) {
- // TODO: if the existing memory is not explicitly named, fix its name
- // and continue.
- if (wasm.getMemoryOrNull(name)) {
- return in.err(pos, "repeated memory name");
- }
- m->setExplicitName(name);
- } else {
- name = (importNames ? "mimport$" : "") + std::to_string(memoryCounter++);
- name = Names::getValidMemoryName(wasm, name);
- m->name = name;
- }
- applyImportNames(*m, importNames);
- return wasm.addMemory(std::move(m));
- }
-
- Result<> addMemory(Name name,
- const std::vector<Name>& exports,
- ImportNames* import,
- MemType type,
- Index pos) {
- if (import && hasNonImport) {
- return in.err(pos, "import after non-import");
- }
- auto m = addMemoryDecl(pos, name, import, type);
- CHECK_ERR(m);
- CHECK_ERR(addExports(in, wasm, *m, exports, ExternalKind::Memory));
- memoryDefs.push_back({name, pos, Index(memoryDefs.size())});
- return Ok{};
- }
-
- Result<> addImplicitData(DataStringT&& data) {
- auto& mem = *wasm.memories.back();
- auto d = std::make_unique<DataSegment>();
- d->memory = mem.name;
- d->isPassive = false;
- d->offset = Builder(wasm).makeConstPtr(0, mem.indexType);
- d->data = std::move(data);
- d->name = Names::getValidDataSegmentName(wasm, "implicit-data");
- wasm.addDataSegment(std::move(d));
- return Ok{};
- }
-
- Result<Global*>
- addGlobalDecl(Index pos, Name name, ImportNames* importNames) {
- auto g = std::make_unique<Global>();
- if (name) {
- if (wasm.getGlobalOrNull(name)) {
- // TODO: if the existing global is not explicitly named, fix its name
- // and continue.
- return in.err(pos, "repeated global name");
- }
- g->setExplicitName(name);
- } else {
- name = (importNames ? "gimport$" : "") + std::to_string(globalCounter++);
- name = Names::getValidGlobalName(wasm, name);
- g->name = name;
- }
- applyImportNames(*g, importNames);
- return wasm.addGlobal(std::move(g));
- }
-
- Result<> addGlobal(Name name,
- const std::vector<Name>& exports,
- ImportNames* import,
- GlobalTypeT,
- std::optional<ExprT>,
- Index pos) {
- if (import && hasNonImport) {
- return in.err(pos, "import after non-import");
- }
- auto g = addGlobalDecl(pos, name, import);
- CHECK_ERR(g);
- CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global));
- globalDefs.push_back({name, pos, Index(globalDefs.size())});
- return Ok{};
- }
-
- Result<> addData(Name name,
- MemoryIdxT*,
- std::optional<ExprT>,
- std::vector<char>&& data,
- Index pos) {
- auto d = std::make_unique<DataSegment>();
- if (name) {
- if (wasm.getDataSegmentOrNull(name)) {
- // TODO: if the existing segment is not explicitly named, fix its name
- // and continue.
- return in.err(pos, "repeated data segment name");
- }
- d->setExplicitName(name);
- } else {
- name = std::to_string(dataCounter++);
- name = Names::getValidDataSegmentName(wasm, name);
- d->name = name;
- }
- d->data = std::move(data);
- dataDefs.push_back({name, pos, Index(wasm.dataSegments.size())});
- wasm.addDataSegment(std::move(d));
- return Ok{};
- }
-};
-
-// Phase 2: Parse type definitions into a TypeBuilder.
-struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {
- ParseInput in;
-
- // We update slots in this builder as we parse type definitions.
- TypeBuilder& builder;
-
- // Parse the names of types and fields as we go.
- std::vector<TypeNames> names;
-
- // The index of the subtype definition we are parsing.
- Index index = 0;
-
- ParseTypeDefsCtx(std::string_view in,
- TypeBuilder& builder,
- const IndexMap& typeIndices)
- : TypeParserCtx<ParseTypeDefsCtx>(typeIndices), in(in), builder(builder),
- names(builder.size()) {}
-
- TypeT makeRefType(HeapTypeT ht, Nullability nullability) {
- return builder.getTempRefType(ht, nullability);
- }
-
- TypeT makeTupleType(const std::vector<Type> types) {
- return builder.getTempTupleType(types);
- }
-
- Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
- if (idx >= builder.size()) {
- return in.err("type index out of bounds");
- }
- return builder[idx];
- }
-
- void addFuncType(SignatureT& type) { builder[index] = type; }
-
- void addStructType(StructT& type) {
- auto& [fieldNames, str] = type;
- builder[index] = str;
- for (Index i = 0; i < fieldNames.size(); ++i) {
- if (auto name = fieldNames[i]; name.is()) {
- names[index].fieldNames[i] = name;
- }
- }
- }
-
- void addArrayType(ArrayT& type) { builder[index] = type; }
-
- void setOpen() { builder[index].setOpen(); }
-
- Result<> addSubtype(Index super) {
- if (super >= builder.size()) {
- return in.err("supertype index out of bounds");
- }
- builder[index].subTypeOf(builder[super]);
- return Ok{};
- }
-
- void finishSubtype(Name name, Index pos) { names[index++].name = name; }
-
- size_t getRecGroupStartIndex() { return index; }
-
- void addRecGroup(Index start, size_t len) {
- builder.createRecGroup(start, len);
- }
-
- void finishDeftype(Index) {}
-};
-
-// Phase 3: Parse type uses to find implicitly defined types.
-struct ParseImplicitTypeDefsCtx : TypeParserCtx<ParseImplicitTypeDefsCtx> {
- using TypeUseT = Ok;
-
- ParseInput in;
-
- // Types parsed so far.
- std::vector<HeapType>& types;
-
- // Map typeuse positions without an explicit type to the correct type.
- std::unordered_map<Index, HeapType>& implicitTypes;
-
- // Map signatures to the first defined heap type they match.
- std::unordered_map<Signature, HeapType> sigTypes;
-
- ParseImplicitTypeDefsCtx(std::string_view in,
- std::vector<HeapType>& types,
- std::unordered_map<Index, HeapType>& implicitTypes,
- const IndexMap& typeIndices)
- : TypeParserCtx<ParseImplicitTypeDefsCtx>(typeIndices), in(in),
- types(types), implicitTypes(implicitTypes) {
- for (auto type : types) {
- if (type.isSignature() && type.getRecGroup().size() == 1) {
- sigTypes.insert({type.getSignature(), type});
- }
- }
- }
-
- Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
- if (idx >= types.size()) {
- return in.err("type index out of bounds");
- }
- return types[idx];
- }
-
- Result<TypeUseT> makeTypeUse(Index pos,
- std::optional<HeapTypeT>,
- ParamsT* params,
- ResultsT* results) {
- std::vector<Type> paramTypes;
- if (params) {
- paramTypes = getUnnamedTypes(*params);
- }
-
- std::vector<Type> resultTypes;
- if (results) {
- resultTypes = *results;
- }
-
- auto sig = Signature(Type(paramTypes), Type(resultTypes));
- auto [it, inserted] = sigTypes.insert({sig, HeapType::func});
- if (inserted) {
- auto type = HeapType(sig);
- it->second = type;
- types.push_back(type);
- }
- implicitTypes.insert({pos, it->second});
-
- return Ok{};
- }
-};
-
-// Phase 4: Parse and set the types of module elements.
-struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
- NullInstrParserCtx {
- // In this phase we have constructed all the types, so we can materialize and
- // validate them when they are used.
-
- using GlobalTypeT = GlobalType;
- using TypeUseT = TypeUse;
-
- ParseInput in;
-
- Module& wasm;
-
- const std::vector<HeapType>& types;
- const std::unordered_map<Index, HeapType>& implicitTypes;
-
- // The index of the current type.
- Index index = 0;
-
- ParseModuleTypesCtx(std::string_view in,
- Module& wasm,
- const std::vector<HeapType>& types,
- const std::unordered_map<Index, HeapType>& implicitTypes,
- const IndexMap& typeIndices)
- : TypeParserCtx<ParseModuleTypesCtx>(typeIndices), in(in), wasm(wasm),
- types(types), implicitTypes(implicitTypes) {}
-
- Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
- if (idx >= types.size()) {
- return in.err("type index out of bounds");
- }
- return types[idx];
- }
-
- Result<TypeUseT> makeTypeUse(Index pos,
- std::optional<HeapTypeT> type,
- ParamsT* params,
- ResultsT* results) {
- std::vector<Name> ids;
- if (params) {
- ids.reserve(params->size());
- for (auto& p : *params) {
- ids.push_back(p.name);
- }
- }
-
- if (type) {
- return TypeUse{*type, ids};
- }
-
- auto it = implicitTypes.find(pos);
- assert(it != implicitTypes.end());
-
- return TypeUse{it->second, ids};
- }
-
- Result<HeapType> getBlockTypeFromTypeUse(Index pos, TypeUse use) {
- assert(use.type.isSignature());
- if (use.type.getSignature().params != Type::none) {
- return in.err(pos, "block parameters not yet supported");
- }
- // TODO: Once we support block parameters, return an error here if any of
- // them are named.
- return use.type;
- }
-
- GlobalTypeT makeGlobalType(Mutability mutability, TypeT type) {
- return {mutability, type};
- }
-
- Result<> addFunc(Name name,
- const std::vector<Name>&,
- ImportNames*,
- TypeUse type,
- std::optional<LocalsT> locals,
- std::optional<InstrsT>,
- Index pos) {
- auto& f = wasm.functions[index];
- if (!type.type.isSignature()) {
- return in.err(pos, "expected signature type");
- }
- f->type = type.type;
- for (Index i = 0; i < type.names.size(); ++i) {
- if (type.names[i].is()) {
- f->setLocalName(i, type.names[i]);
- }
- }
- if (locals) {
- for (auto& l : *locals) {
- Builder::addVar(f.get(), l.name, l.type);
- }
- }
- return Ok{};
- }
-
- Result<>
- addMemory(Name, const std::vector<Name>&, ImportNames*, MemTypeT, Index) {
- return Ok{};
- }
-
- Result<> addImplicitData(DataStringT&& data) { return Ok{}; }
-
- Result<> addGlobal(Name,
- const std::vector<Name>&,
- ImportNames*,
- GlobalType type,
- std::optional<ExprT>,
- Index) {
- auto& g = wasm.globals[index];
- g->mutable_ = type.mutability;
- g->type = type.type;
- return Ok{};
- }
-};
-
-// Phase 5: Parse module element definitions, including instructions.
-struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
- using GlobalTypeT = Ok;
- using TypeUseT = HeapType;
-
- // Keep track of instructions internally rather than letting the general
- // parser collect them.
- using InstrT = Ok;
- using InstrsT = Ok;
- using ExprT = Expression*;
-
- using FieldIdxT = Index;
- using LocalIdxT = Index;
- using GlobalIdxT = Name;
- using MemoryIdxT = Name;
- using DataIdxT = Name;
-
- using MemargT = Memarg;
-
- ParseInput in;
-
- Module& wasm;
- Builder builder;
-
- const std::vector<HeapType>& types;
- const std::unordered_map<Index, HeapType>& implicitTypes;
-
- // The index of the current module element.
- Index index = 0;
-
- // The current function being parsed, used to create scratch locals, type
- // local.get, etc.
- Function* func = nullptr;
-
- IRBuilder irBuilder;
-
- void setFunction(Function* func) {
- this->func = func;
- irBuilder.setFunction(func);
- }
-
- ParseDefsCtx(std::string_view in,
- Module& wasm,
- const std::vector<HeapType>& types,
- const std::unordered_map<Index, HeapType>& implicitTypes,
- const IndexMap& typeIndices)
- : TypeParserCtx(typeIndices), in(in), wasm(wasm), builder(wasm),
- types(types), implicitTypes(implicitTypes), irBuilder(wasm) {}
-
- template<typename T> Result<T> withLoc(Index pos, Result<T> res) {
- if (auto err = res.getErr()) {
- return in.err(pos, err->msg);
- }
- return res;
- }
-
- template<typename T> Result<T> withLoc(Result<T> res) {
- return withLoc(in.getPos(), res);
- }
-
- HeapType getBlockTypeFromResult(const std::vector<Type> results) {
- assert(results.size() == 1);
- return HeapType(Signature(Type::none, results[0]));
- }
-
- Result<HeapType> getBlockTypeFromTypeUse(Index pos, HeapType type) {
- return type;
- }
-
- Ok makeInstrs() { return Ok{}; }
-
- void appendInstr(Ok&, InstrT instr) {}
-
- Result<InstrsT> finishInstrs(Ok&) { return Ok{}; }
-
- Result<Expression*> instrToExpr(Ok&) { return irBuilder.build(); }
-
- GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
-
- Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
- if (idx >= types.size()) {
- return in.err("type index out of bounds");
- }
- return types[idx];
- }
-
- Result<Index> getFieldFromIdx(HeapType type, uint32_t idx) {
- if (!type.isStruct()) {
- return in.err("expected struct type");
- }
- if (idx >= type.getStruct().fields.size()) {
- return in.err("struct index out of bounds");
- }
- return idx;
- }
-
- Result<Index> getFieldFromName(HeapType type, Name name) {
- // TODO: Field names
- return in.err("symbolic field names note yet supported");
- }
-
- Result<Index> getLocalFromIdx(uint32_t idx) {
- if (!func) {
- return in.err("cannot access locals outside of a function");
- }
- if (idx >= func->getNumLocals()) {
- return in.err("local index out of bounds");
- }
- return idx;
- }
-
- Result<Index> getLocalFromName(Name name) {
- if (!func) {
- return in.err("cannot access locals outside of a function");
- }
- if (!func->hasLocalIndex(name)) {
- return in.err("local $" + name.toString() + " does not exist");
- }
- return func->getLocalIndex(name);
- }
-
- Result<Name> getGlobalFromIdx(uint32_t idx) {
- if (idx >= wasm.globals.size()) {
- return in.err("global index out of bounds");
- }
- return wasm.globals[idx]->name;
- }
-
- Result<Name> getGlobalFromName(Name name) {
- if (!wasm.getGlobalOrNull(name)) {
- return in.err("global $" + name.toString() + " does not exist");
- }
- return name;
- }
-
- Result<Name> getMemoryFromIdx(uint32_t idx) {
- if (idx >= wasm.memories.size()) {
- return in.err("memory index out of bounds");
- }
- return wasm.memories[idx]->name;
- }
-
- Result<Name> getMemoryFromName(Name name) {
- if (!wasm.getMemoryOrNull(name)) {
- return in.err("memory $" + name.toString() + " does not exist");
- }
- return name;
- }
-
- Result<Name> getDataFromIdx(uint32_t idx) {
- if (idx >= wasm.dataSegments.size()) {
- return in.err("data index out of bounds");
- }
- return wasm.dataSegments[idx]->name;
- }
-
- Result<Name> getDataFromName(Name name) {
- if (!wasm.getDataSegmentOrNull(name)) {
- return in.err("data $" + name.toString() + " does not exist");
- }
- return name;
- }
-
- Result<TypeUseT> makeTypeUse(Index pos,
- std::optional<HeapTypeT> type,
- ParamsT* params,
- ResultsT* results) {
- if (type && (params || results)) {
- std::vector<Type> paramTypes;
- if (params) {
- paramTypes = getUnnamedTypes(*params);
- }
-
- std::vector<Type> resultTypes;
- if (results) {
- resultTypes = *results;
- }
-
- auto sig = Signature(Type(paramTypes), Type(resultTypes));
-
- if (!type->isSignature() || type->getSignature() != sig) {
- return in.err(pos, "type does not match provided signature");
- }
- }
-
- if (type) {
- return *type;
- }
-
- auto it = implicitTypes.find(pos);
- assert(it != implicitTypes.end());
- return it->second;
- }
-
- Result<> addFunc(Name,
- const std::vector<Name>&,
- ImportNames*,
- TypeUseT,
- std::optional<LocalsT>,
- std::optional<InstrsT>,
- Index pos) {
- CHECK_ERR(withLoc(pos, irBuilder.visitEnd()));
- auto body = irBuilder.build();
- CHECK_ERR(withLoc(pos, body));
- wasm.functions[index]->body = *body;
- return Ok{};
- }
-
- Result<> addGlobal(Name,
- const std::vector<Name>&,
- ImportNames*,
- GlobalTypeT,
- std::optional<ExprT> exp,
- Index) {
- if (exp) {
- wasm.globals[index]->init = *exp;
- }
- return Ok{};
- }
-
- Result<> addData(
- Name, Name* mem, std::optional<ExprT> offset, DataStringT, Index pos) {
- auto& d = wasm.dataSegments[index];
- if (offset) {
- d->isPassive = false;
- d->offset = *offset;
- if (mem) {
- d->memory = *mem;
- } else if (wasm.memories.size() > 0) {
- d->memory = wasm.memories[0]->name;
- } else {
- return in.err(pos, "active segment with no memory");
- }
- } else {
- d->isPassive = true;
- }
- return Ok{};
- }
-
- Result<Index> addScratchLocal(Index pos, Type type) {
- if (!func) {
- return in.err(pos,
- "scratch local required, but there is no function context");
- }
- Name name = Names::getValidLocalName(*func, "scratch");
- return Builder::addVar(func, name, type);
- }
-
- Result<Expression*> makeExpr(InstrsT& instrs) { return irBuilder.build(); }
-
- Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; }
-
- Result<Name> getMemory(Index pos, Name* mem) {
- if (mem) {
- return *mem;
- }
- if (wasm.memories.empty()) {
- return in.err(pos, "memory required, but there is no memory");
- }
- return wasm.memories[0]->name;
- }
-
- Result<> makeBlock(Index pos, std::optional<Name> label, HeapType type) {
- // TODO: validate labels?
- // TODO: Move error on input types to here?
- return withLoc(pos,
- irBuilder.makeBlock(label ? *label : Name{},
- type.getSignature().results));
- }
-
- Result<> finishBlock(Index pos, InstrsT) {
- return withLoc(pos, irBuilder.visitEnd());
- }
-
- Result<> makeUnreachable(Index pos) {
- return withLoc(pos, irBuilder.makeUnreachable());
- }
-
- Result<> makeNop(Index pos) { return withLoc(pos, irBuilder.makeNop()); }
-
- Result<> makeBinary(Index pos, BinaryOp op) {
- return withLoc(pos, irBuilder.makeBinary(op));
- }
-
- Result<> makeUnary(Index pos, UnaryOp op) {
- return withLoc(pos, irBuilder.makeUnary(op));
- }
-
- Result<> makeSelect(Index pos, std::vector<Type>* res) {
- if (res && res->size()) {
- if (res->size() > 1) {
- return in.err(pos, "select may not have more than one result type");
- }
- return withLoc(pos, irBuilder.makeSelect((*res)[0]));
- }
- return withLoc(pos, irBuilder.makeSelect());
- }
-
- Result<> makeDrop(Index pos) { return withLoc(pos, irBuilder.makeDrop()); }
-
- Result<> makeMemorySize(Index pos, Name* mem) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeMemorySize(*m));
- }
-
- Result<> makeMemoryGrow(Index pos, Name* mem) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeMemoryGrow(*m));
- }
-
- Result<> makeLocalGet(Index pos, Index local) {
- return withLoc(pos, irBuilder.makeLocalGet(local));
- }
-
- Result<> makeLocalTee(Index pos, Index local) {
- return withLoc(pos, irBuilder.makeLocalTee(local));
- }
-
- Result<> makeLocalSet(Index pos, Index local) {
- return withLoc(pos, irBuilder.makeLocalSet(local));
- }
-
- Result<> makeGlobalGet(Index pos, Name global) {
- return withLoc(pos, irBuilder.makeGlobalGet(global));
- }
-
- Result<> makeGlobalSet(Index pos, Name global) {
- assert(wasm.getGlobalOrNull(global));
- return withLoc(pos, irBuilder.makeGlobalSet(global));
- }
-
- Result<> makeI32Const(Index pos, uint32_t c) {
- return withLoc(pos, irBuilder.makeConst(Literal(c)));
- }
-
- Result<> makeI64Const(Index pos, uint64_t c) {
- return withLoc(pos, irBuilder.makeConst(Literal(c)));
- }
-
- Result<> makeF32Const(Index pos, float c) {
- return withLoc(pos, irBuilder.makeConst(Literal(c)));
- }
-
- Result<> makeF64Const(Index pos, double c) {
- return withLoc(pos, irBuilder.makeConst(Literal(c)));
- }
-
- Result<> makeLoad(Index pos,
- Type type,
- bool signed_,
- int bytes,
- bool isAtomic,
- Name* mem,
- Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- if (isAtomic) {
- return withLoc(pos,
- irBuilder.makeAtomicLoad(bytes, memarg.offset, type, *m));
- }
- return withLoc(pos,
- irBuilder.makeLoad(
- bytes, signed_, memarg.offset, memarg.align, type, *m));
- }
-
- Result<> makeStore(
- Index pos, Type type, int bytes, bool isAtomic, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- if (isAtomic) {
- return withLoc(pos,
- irBuilder.makeAtomicStore(bytes, memarg.offset, type, *m));
- }
- return withLoc(
- pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));
- }
-
- Result<> makeAtomicRMW(
- Index pos, AtomicRMWOp op, Type type, int bytes, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos,
- irBuilder.makeAtomicRMW(op, bytes, memarg.offset, type, *m));
- }
-
- Result<>
- makeAtomicCmpxchg(Index pos, Type type, int bytes, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos,
- irBuilder.makeAtomicCmpxchg(bytes, memarg.offset, type, *m));
- }
-
- Result<> makeAtomicWait(Index pos, Type type, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeAtomicWait(type, memarg.offset, *m));
- }
-
- Result<> makeAtomicNotify(Index pos, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeAtomicNotify(memarg.offset, *m));
- }
-
- Result<> makeAtomicFence(Index pos) {
- return withLoc(pos, irBuilder.makeAtomicFence());
- }
-
- Result<> makeSIMDExtract(Index pos, SIMDExtractOp op, uint8_t lane) {
- return withLoc(pos, irBuilder.makeSIMDExtract(op, lane));
- }
-
- Result<> makeSIMDReplace(Index pos, SIMDReplaceOp op, uint8_t lane) {
- return withLoc(pos, irBuilder.makeSIMDReplace(op, lane));
- }
-
- Result<> makeSIMDShuffle(Index pos, const std::array<uint8_t, 16>& lanes) {
- return withLoc(pos, irBuilder.makeSIMDShuffle(lanes));
- }
-
- Result<> makeSIMDTernary(Index pos, SIMDTernaryOp op) {
- return withLoc(pos, irBuilder.makeSIMDTernary(op));
- }
-
- Result<> makeSIMDShift(Index pos, SIMDShiftOp op) {
- return withLoc(pos, irBuilder.makeSIMDShift(op));
- }
-
- Result<> makeSIMDLoad(Index pos, SIMDLoadOp op, Name* mem, Memarg memarg) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos,
- irBuilder.makeSIMDLoad(op, memarg.offset, memarg.align, *m));
- }
-
- Result<> makeSIMDLoadStoreLane(
- Index pos, SIMDLoadStoreLaneOp op, Name* mem, Memarg memarg, uint8_t lane) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos,
- irBuilder.makeSIMDLoadStoreLane(
- op, memarg.offset, memarg.align, lane, *m));
- }
-
- Result<> makeMemoryInit(Index pos, Name* mem, Name data) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeMemoryInit(data, *m));
- }
-
- Result<> makeDataDrop(Index pos, Name data) {
- return withLoc(pos, irBuilder.makeDataDrop(data));
- }
-
- Result<> makeMemoryCopy(Index pos, Name* destMem, Name* srcMem) {
- auto destMemory = getMemory(pos, destMem);
- CHECK_ERR(destMemory);
- auto srcMemory = getMemory(pos, srcMem);
- CHECK_ERR(srcMemory);
- return withLoc(pos, irBuilder.makeMemoryCopy(*destMemory, *srcMemory));
- }
-
- Result<> makeMemoryFill(Index pos, Name* mem) {
- auto m = getMemory(pos, mem);
- CHECK_ERR(m);
- return withLoc(pos, irBuilder.makeMemoryFill(*m));
- }
-
- Result<> makeReturn(Index pos) {
- return withLoc(pos, irBuilder.makeReturn());
- }
-
- Result<> makeRefNull(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeRefNull(type));
- }
-
- Result<> makeRefIsNull(Index pos) {
- return withLoc(pos, irBuilder.makeRefIsNull());
- }
-
- Result<> makeRefEq(Index pos) { return withLoc(pos, irBuilder.makeRefEq()); }
-
- Result<> makeRefI31(Index pos) {
- return withLoc(pos, irBuilder.makeRefI31());
- }
-
- Result<> makeI31Get(Index pos, bool signed_) {
- return withLoc(pos, irBuilder.makeI31Get(signed_));
- }
-
- Result<> makeStructNew(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeStructNew(type));
- }
-
- Result<> makeStructNewDefault(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeStructNewDefault(type));
- }
-
- Result<> makeStructGet(Index pos, HeapType type, Index field, bool signed_) {
- return withLoc(pos, irBuilder.makeStructGet(type, field, signed_));
- }
-
- Result<> makeStructSet(Index pos, HeapType type, Index field) {
- return withLoc(pos, irBuilder.makeStructSet(type, field));
- }
-
- Result<> makeArrayNew(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeArrayNew(type));
- }
-
- Result<> makeArrayNewDefault(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeArrayNewDefault(type));
- }
-
- Result<> makeArrayNewData(Index pos, HeapType type, Name data) {
- return withLoc(pos, irBuilder.makeArrayNewData(type, data));
- }
-
- Result<> makeArrayNewElem(Index pos, HeapType type, Name elem) {
- return withLoc(pos, irBuilder.makeArrayNewElem(type, elem));
- }
-
- Result<> makeArrayGet(Index pos, HeapType type, bool signed_) {
- return withLoc(pos, irBuilder.makeArrayGet(type, signed_));
- }
-
- Result<> makeArraySet(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeArraySet(type));
- }
-
- Result<> makeArrayLen(Index pos) {
- return withLoc(pos, irBuilder.makeArrayLen());
- }
-
- Result<> makeArrayCopy(Index pos, HeapType destType, HeapType srcType) {
- return withLoc(pos, irBuilder.makeArrayCopy(destType, srcType));
- }
-
- Result<> makeArrayFill(Index pos, HeapType type) {
- return withLoc(pos, irBuilder.makeArrayFill(type));
- }
-};
-
-// ================
-// Parser Functions
-// ================
-
-// Types
-template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::RefTypeT> reftype(Ctx&);
-template<typename Ctx> Result<typename Ctx::TypeT> valtype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::ParamsT> params(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::SignatureT> functype(Ctx&);
-template<typename Ctx> Result<typename Ctx::FieldT> storagetype(Ctx&);
-template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx&);
-template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::StructT> structtype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx&);
-template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx&);
-template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx&);
-template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx&);
-template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx&);
-
-// Instructions
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> foldedBlockinstr(Ctx&);
-template<typename Ctx>
-MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> blockinstr(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> plaininstr(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::BlockTypeT> blocktype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> block(Ctx&, bool);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeUnreachable(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeNop(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBinary(Ctx&, Index, BinaryOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeUnary(Ctx&, Index, UnaryOp op);
-template<typename Ctx> Result<typename Ctx::InstrT> makeSelect(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeDrop(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeMemorySize(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryGrow(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeLocalGet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeLocalTee(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeLocalSet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeGlobalGet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeGlobalSet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeBlock(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeThenOrElse(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeConst(Ctx&, Index, Type type);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeLoad(Ctx&, Index, Type type, bool signed_, int bytes, bool isAtomic);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStore(Ctx&, Index, Type type, int bytes, bool isAtomic);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeAtomicRMW(Ctx&, Index, AtomicRMWOp op, Type type, uint8_t bytes);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeAtomicCmpxchg(Ctx&, Index, Type type, uint8_t bytes);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeAtomicWait(Ctx&, Index, Type type);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeAtomicNotify(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeAtomicFence(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDExtract(Ctx&, Index, SIMDExtractOp op, size_t lanes);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDReplace(Ctx&, Index, SIMDReplaceOp op, size_t lanes);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeSIMDShuffle(Ctx&, Index);
-template<typename Ctx>
-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, int bytes);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-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);
-template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryFill(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makePop(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeIf(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeMaybeBlock(Ctx&, Index, size_t i, Type type);
-template<typename Ctx> Result<typename Ctx::InstrT> makeLoop(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeCall(Ctx&, Index, bool isReturn);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeCallIndirect(Ctx&, Index, bool isReturn);
-template<typename Ctx> Result<typename Ctx::InstrT> makeBreak(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeBreakTable(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeReturn(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefNull(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefIsNull(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefFunc(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefEq(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTableGet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTableSet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTableSize(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTableGrow(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTableFill(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTry(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
-template<typename Ctx> Result<typename Ctx::InstrT> makeThrow(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRethrow(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeTupleMake(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTupleExtract(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeCallRef(Ctx&, Index, bool isReturn);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefI31(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeI31Get(Ctx&, Index, bool signed_);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeRefCast(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBrOnNull(Ctx&, Index, bool onFail = false);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBrOnCast(Ctx&, Index, bool onFail = false);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStructNew(Ctx&, Index, bool default_);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStructGet(Ctx&, Index, bool signed_ = false);
-template<typename Ctx> Result<typename Ctx::InstrT> makeStructSet(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNew(Ctx&, Index, bool default_);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewData(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewElem(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewFixed(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayGet(Ctx&, Index, bool signed_ = false);
-template<typename Ctx> Result<typename Ctx::InstrT> makeArraySet(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeArrayLen(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeArrayCopy(Ctx&, Index);
-template<typename Ctx> Result<typename Ctx::InstrT> makeArrayFill(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayInitData(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayInitElem(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefAs(Ctx&, Index, RefAsOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringNew(Ctx&, Index, StringNewOp op, bool try_);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringConst(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringMeasure(Ctx&, Index, StringMeasureOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringEncode(Ctx&, Index, StringEncodeOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringConcat(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringEq(Ctx&, Index, StringEqOp);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringAs(Ctx&, Index, StringAsOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringWTF8Advance(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringWTF16Get(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringIterNext(Ctx&, Index);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringIterMove(Ctx&, Index, StringIterMoveOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringSliceWTF(Ctx&, Index, StringSliceWTFOp op);
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringSliceIter(Ctx&, Index);
-
-// Modules
-template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
-template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&);
-template<typename Ctx>
-Result<typename Ctx::FieldIdxT> fieldidx(Ctx&, typename Ctx::HeapTypeT);
-template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemidx(Ctx&);
-template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx&);
-template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx&);
-template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx&);
-template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx&);
-MaybeResult<ImportNames> inlineImport(ParseInput&);
-Result<std::vector<Name>> inlineExports(ParseInput&);
-template<typename Ctx> Result<> strtype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::ModuleNameT> subtype(Ctx&);
-template<typename Ctx> MaybeResult<> deftype(Ctx&);
-template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx&);
-template<typename Ctx> MaybeResult<> func(Ctx&);
-template<typename Ctx> MaybeResult<> memory(Ctx&);
-template<typename Ctx> MaybeResult<> global(Ctx&);
-template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx&);
-template<typename Ctx> MaybeResult<> data(Ctx&);
-MaybeResult<> modulefield(ParseDeclsCtx&);
-Result<> module(ParseDeclsCtx&);
-
-// =====
-// Types
-// =====
-
-// heaptype ::= x:typeidx => types[x]
-// | 'func' => func
-// | 'extern' => extern
-template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
- if (ctx.in.takeKeyword("func"sv)) {
- return ctx.makeFunc();
- }
- if (ctx.in.takeKeyword("any"sv)) {
- return ctx.makeAny();
- }
- if (ctx.in.takeKeyword("extern"sv)) {
- return ctx.makeExtern();
- }
- if (ctx.in.takeKeyword("eq"sv)) {
- return ctx.makeEq();
- }
- if (ctx.in.takeKeyword("i31"sv)) {
- return ctx.makeI31();
- }
- if (ctx.in.takeKeyword("struct"sv)) {
- return ctx.makeStructType();
- }
- if (ctx.in.takeKeyword("array"sv)) {
- return ctx.makeArrayType();
- }
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- return *type;
-}
-
-// reftype ::= 'funcref' => funcref
-// | 'externref' => externref
-// | 'anyref' => anyref
-// | 'eqref' => eqref
-// | 'i31ref' => i31ref
-// | 'structref' => structref
-// | 'arrayref' => arrayref
-// | '(' ref null? t:heaptype ')' => ref null? t
-template<typename Ctx> MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx) {
- if (ctx.in.takeKeyword("funcref"sv)) {
- return ctx.makeRefType(ctx.makeFunc(), Nullable);
- }
- if (ctx.in.takeKeyword("externref"sv)) {
- return ctx.makeRefType(ctx.makeExtern(), Nullable);
- }
- if (ctx.in.takeKeyword("anyref"sv)) {
- return ctx.makeRefType(ctx.makeAny(), Nullable);
- }
- if (ctx.in.takeKeyword("eqref"sv)) {
- return ctx.makeRefType(ctx.makeEq(), Nullable);
- }
- if (ctx.in.takeKeyword("i31ref"sv)) {
- return ctx.makeRefType(ctx.makeI31(), Nullable);
- }
- if (ctx.in.takeKeyword("structref"sv)) {
- return ctx.makeRefType(ctx.makeStructType(), Nullable);
- }
- if (ctx.in.takeKeyword("arrayref"sv)) {
- return ctx.in.err("arrayref not yet supported");
- }
-
- if (!ctx.in.takeSExprStart("ref"sv)) {
- return {};
- }
-
- auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable;
-
- auto type = heaptype(ctx);
- CHECK_ERR(type);
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of reftype");
- }
-
- return ctx.makeRefType(*type, nullability);
-}
-
-// numtype ::= 'i32' => i32
-// | 'i64' => i64
-// | 'f32' => f32
-// | 'f64' => f64
-// vectype ::= 'v128' => v128
-// valtype ::= t:numtype => t
-// | t:vectype => t
-// | t:reftype => t
-template<typename Ctx> Result<typename Ctx::TypeT> valtype(Ctx& ctx) {
- if (ctx.in.takeKeyword("i32"sv)) {
- return ctx.makeI32();
- } else if (ctx.in.takeKeyword("i64"sv)) {
- return ctx.makeI64();
- } else if (ctx.in.takeKeyword("f32"sv)) {
- return ctx.makeF32();
- } else if (ctx.in.takeKeyword("f64"sv)) {
- return ctx.makeF64();
- } else if (ctx.in.takeKeyword("v128"sv)) {
- return ctx.makeV128();
- } else if (auto type = reftype(ctx)) {
- CHECK_ERR(type);
- return *type;
- } else {
- return ctx.in.err("expected valtype");
- }
-}
-
-// param ::= '(' 'param id? t:valtype ')' => [t]
-// | '(' 'param t*:valtype* ')' => [t*]
-// params ::= param*
-template<typename Ctx> MaybeResult<typename Ctx::ParamsT> params(Ctx& ctx) {
- bool hasAny = false;
- auto res = ctx.makeParams();
- while (ctx.in.takeSExprStart("param"sv)) {
- hasAny = true;
- if (auto id = ctx.in.takeID()) {
- // Single named param
- auto type = valtype(ctx);
- CHECK_ERR(type);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of param");
- }
- ctx.appendParam(res, *id, *type);
- } else {
- // Repeated unnamed params
- while (!ctx.in.takeRParen()) {
- auto type = valtype(ctx);
- CHECK_ERR(type);
- ctx.appendParam(res, {}, *type);
- }
- }
- }
- if (hasAny) {
- return res;
- }
- return {};
-}
-
-// result ::= '(' 'result' t*:valtype ')' => [t*]
-// results ::= result*
-template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx& ctx) {
- bool hasAny = false;
- auto res = ctx.makeResults();
- while (ctx.in.takeSExprStart("result"sv)) {
- hasAny = true;
- while (!ctx.in.takeRParen()) {
- auto type = valtype(ctx);
- CHECK_ERR(type);
- ctx.appendResult(res, *type);
- }
- }
- if (hasAny) {
- return res;
- }
- return {};
-}
-
-// functype ::= '(' 'func' t1*:vec(param) t2*:vec(result) ')' => [t1*] -> [t2*]
-template<typename Ctx>
-MaybeResult<typename Ctx::SignatureT> functype(Ctx& ctx) {
- if (!ctx.in.takeSExprStart("func"sv)) {
- return {};
- }
-
- auto parsedParams = params(ctx);
- CHECK_ERR(parsedParams);
-
- auto parsedResults = results(ctx);
- CHECK_ERR(parsedResults);
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of functype");
- }
-
- return ctx.makeFuncType(parsedParams.getPtr(), parsedResults.getPtr());
-}
-
-// storagetype ::= valtype | packedtype
-// packedtype ::= i8 | i16
-template<typename Ctx> Result<typename Ctx::FieldT> storagetype(Ctx& ctx) {
- if (ctx.in.takeKeyword("i8"sv)) {
- return ctx.makeI8();
- }
- if (ctx.in.takeKeyword("i16"sv)) {
- return ctx.makeI16();
- }
- auto type = valtype(ctx);
- CHECK_ERR(type);
- return ctx.makeStorageType(*type);
-}
-
-// fieldtype ::= t:storagetype => const t
-// | '(' 'mut' t:storagetype ')' => var t
-template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx& ctx) {
- auto mutability = Immutable;
- if (ctx.in.takeSExprStart("mut"sv)) {
- mutability = Mutable;
- }
-
- auto field = storagetype(ctx);
- CHECK_ERR(field);
-
- if (mutability == Mutable) {
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of field type");
- }
- }
-
- return ctx.makeFieldType(*field, mutability);
-}
-
-// field ::= '(' 'field' id t:fieldtype ')' => [(id, t)]
-// | '(' 'field' t*:fieldtype* ')' => [(_, t*)*]
-// | fieldtype
-template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx& ctx) {
- auto res = ctx.makeFields();
- while (true) {
- if (auto t = ctx.in.peek(); !t || t->isRParen()) {
- return res;
- }
- if (ctx.in.takeSExprStart("field")) {
- if (auto id = ctx.in.takeID()) {
- auto field = fieldtype(ctx);
- CHECK_ERR(field);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of field");
- }
- ctx.appendField(res, *id, *field);
- } else {
- while (!ctx.in.takeRParen()) {
- auto field = fieldtype(ctx);
- CHECK_ERR(field);
- ctx.appendField(res, {}, *field);
- }
- }
- } else {
- auto field = fieldtype(ctx);
- CHECK_ERR(field);
- ctx.appendField(res, {}, *field);
- }
- }
-}
-
-// structtype ::= '(' 'struct' field* ')'
-template<typename Ctx> MaybeResult<typename Ctx::StructT> structtype(Ctx& ctx) {
- if (!ctx.in.takeSExprStart("struct"sv)) {
- return {};
- }
- auto namedFields = fields(ctx);
- CHECK_ERR(namedFields);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of struct definition");
- }
-
- return ctx.makeStruct(*namedFields);
-}
-
-// arraytype ::= '(' 'array' field ')'
-template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx& ctx) {
- if (!ctx.in.takeSExprStart("array"sv)) {
- return {};
- }
- auto namedFields = fields(ctx);
- CHECK_ERR(namedFields);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of array definition");
- }
-
- if (auto array = ctx.makeArray(*namedFields)) {
- return *array;
- }
- return ctx.in.err("expected exactly one field in array definition");
-}
-
-// limits32 ::= n:u32 m:u32?
-template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx& ctx) {
- auto n = ctx.in.takeU32();
- if (!n) {
- return ctx.in.err("expected initial size");
- }
- std::optional<uint64_t> m = ctx.in.takeU32();
- return ctx.makeLimits(uint64_t(*n), m);
-}
-
-// limits64 ::= n:u64 m:u64?
-template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx& ctx) {
- auto n = ctx.in.takeU64();
- if (!n) {
- return ctx.in.err("expected initial size");
- }
- std::optional<uint64_t> m = ctx.in.takeU64();
- return ctx.makeLimits(uint64_t(*n), m);
-}
-
-// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared?
-template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx& ctx) {
- auto type = Type::i32;
- if (ctx.in.takeKeyword("i64"sv)) {
- type = Type::i64;
- } else {
- ctx.in.takeKeyword("i32"sv);
- }
- auto limits = type == Type::i32 ? limits32(ctx) : limits64(ctx);
- CHECK_ERR(limits);
- bool shared = false;
- if (ctx.in.takeKeyword("shared"sv)) {
- shared = true;
- }
- return ctx.makeMemType(type, *limits, shared);
-}
-
-// globaltype ::= t:valtype => const t
-// | '(' 'mut' t:valtype ')' => var t
-template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx& ctx) {
- auto mutability = Immutable;
- if (ctx.in.takeSExprStart("mut"sv)) {
- mutability = Mutable;
- }
-
- auto type = valtype(ctx);
- CHECK_ERR(type);
-
- if (mutability == Mutable && !ctx.in.takeRParen()) {
- return ctx.in.err("expected end of globaltype");
- }
-
- return ctx.makeGlobalType(mutability, *type);
-}
-
-// ============
-// Instructions
-// ============
-
-// blockinstr ::= block | loop | if-else | try-catch
-template<typename Ctx>
-MaybeResult<typename Ctx::InstrT> foldedBlockinstr(Ctx& ctx) {
- if (auto i = block(ctx, true)) {
- return i;
- }
- // TODO: Other block instructions
- return {};
-}
-
-template<typename Ctx>
-MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx& ctx) {
- if (auto i = block(ctx, false)) {
- return i;
- }
- // TODO: Other block instructions
- return {};
-}
-
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> blockinstr(Ctx& ctx) {
- if (auto i = foldedBlockinstr(ctx)) {
- return i;
- }
- if (auto i = unfoldedBlockinstr(ctx)) {
- return i;
- }
- return {};
-}
-
-// plaininstr ::= ... all plain instructions ...
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> plaininstr(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- auto keyword = ctx.in.takeKeyword();
- if (!keyword) {
- return {};
- }
-
-#define NEW_INSTRUCTION_PARSER
-#define NEW_WAT_PARSER
-#include <gen-s-parser.inc>
-}
-
-// instr ::= plaininstr | blockinstr
-template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx& ctx) {
- // Check for valid strings that are not instructions.
- if (auto tok = ctx.in.peek()) {
- if (auto keyword = tok->getKeyword()) {
- if (keyword == "end"sv) {
- return {};
- }
- }
- }
- if (auto i = blockinstr(ctx)) {
- return i;
- }
- if (auto i = plaininstr(ctx)) {
- return i;
- }
- // TODO: Handle folded plain instructions as well.
- return {};
-}
-
-template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
- auto insts = ctx.makeInstrs();
-
- while (true) {
- if (auto blockinst = foldedBlockinstr(ctx)) {
- CHECK_ERR(blockinst);
- ctx.appendInstr(insts, *blockinst);
- continue;
- }
- // Parse an arbitrary number of folded instructions.
- if (ctx.in.takeLParen()) {
- // A stack of (start, end) position pairs defining the positions of
- // instructions that need to be parsed after their folded children.
- std::vector<std::pair<Index, std::optional<Index>>> foldedInstrs;
-
- // Begin a folded instruction. Push its start position and a placeholder
- // end position.
- foldedInstrs.push_back({ctx.in.getPos(), {}});
- while (!foldedInstrs.empty()) {
- // Consume everything up to the next paren. This span will be parsed as
- // an instruction later after its folded children have been parsed.
- if (!ctx.in.takeUntilParen()) {
- return ctx.in.err(foldedInstrs.back().first,
- "unterminated folded instruction");
- }
-
- if (!foldedInstrs.back().second) {
- // The folded instruction we just started should end here.
- foldedInstrs.back().second = ctx.in.getPos();
- }
-
- // We have either the start of a new folded child or the end of the last
- // one.
- if (auto blockinst = foldedBlockinstr(ctx)) {
- CHECK_ERR(blockinst);
- ctx.appendInstr(insts, *blockinst);
- } else if (ctx.in.takeLParen()) {
- foldedInstrs.push_back({ctx.in.getPos(), {}});
- } else if (ctx.in.takeRParen()) {
- auto [start, end] = foldedInstrs.back();
- assert(end && "Should have found end of instruction");
- foldedInstrs.pop_back();
-
- WithPosition with(ctx, start);
- if (auto inst = plaininstr(ctx)) {
- CHECK_ERR(inst);
- ctx.appendInstr(insts, *inst);
- } else {
- return ctx.in.err(start, "expected folded instruction");
- }
-
- if (ctx.in.getPos() != *end) {
- return ctx.in.err("expected end of instruction");
- }
- } else {
- WASM_UNREACHABLE("expected paren");
- }
- }
- continue;
- }
-
- // A non-folded instruction.
- if (auto inst = instr(ctx)) {
- CHECK_ERR(inst);
- ctx.appendInstr(insts, *inst);
- } else {
- break;
- }
- }
-
- return ctx.finishInstrs(insts);
-}
-
-template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx& ctx) {
- auto insts = instrs(ctx);
- CHECK_ERR(insts);
- 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);
-}
-
-// blocktype ::= (t:result)? => t? | x,I:typeuse => x if I = {}
-template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx& ctx) {
- auto pos = ctx.in.getPos();
-
- if (auto res = results(ctx)) {
- CHECK_ERR(res);
- if (ctx.getResultsSize(*res) == 1) {
- return ctx.getBlockTypeFromResult(*res);
- }
- }
-
- // We either had no results or multiple results. Reset and parse again as a
- // type use.
- ctx.in.lexer.setIndex(pos);
- auto use = typeuse(ctx);
- CHECK_ERR(use);
-
- auto type = ctx.getBlockTypeFromTypeUse(pos, *use);
- CHECK_ERR(type);
- return *type;
-}
-
-// block ::= 'block' label blocktype instr* 'end' id? if id = {} or id = label
-// | '(' 'block' label blocktype instr* ')'
-template<typename Ctx>
-MaybeResult<typename Ctx::InstrT> block(Ctx& ctx, bool folded) {
- auto pos = ctx.in.getPos();
-
- if (folded) {
- if (!ctx.in.takeSExprStart("block"sv)) {
- return {};
- }
- } else {
- if (!ctx.in.takeKeyword("block"sv)) {
- return {};
- }
- }
-
- auto label = ctx.in.takeID();
-
- auto type = blocktype(ctx);
- CHECK_ERR(type);
-
- ctx.makeBlock(pos, label, *type);
-
- auto insts = instrs(ctx);
- CHECK_ERR(insts);
-
- if (folded) {
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected ')' at end of block");
- }
- } else {
- if (!ctx.in.takeKeyword("end"sv)) {
- return ctx.in.err("expected 'end' at end of block");
- }
- auto id = ctx.in.takeID();
- if (id && id != label) {
- return ctx.in.err("end label does not match block label");
- }
- }
-
- return ctx.finishBlock(pos, std::move(*insts));
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeUnreachable(Ctx& ctx, Index pos) {
- return ctx.makeUnreachable(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeNop(Ctx& ctx, Index pos) {
- return ctx.makeNop(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBinary(Ctx& ctx, Index pos, BinaryOp op) {
- return ctx.makeBinary(pos, op);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeUnary(Ctx& ctx, Index pos, UnaryOp op) {
- return ctx.makeUnary(pos, op);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeSelect(Ctx& ctx, Index pos) {
- auto res = results(ctx);
- CHECK_ERR(res);
- return ctx.makeSelect(pos, res.getPtr());
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeDrop(Ctx& ctx, Index pos) {
- return ctx.makeDrop(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeMemorySize(Ctx& ctx, Index pos) {
- auto mem = maybeMemidx(ctx);
- CHECK_ERR(mem);
- return ctx.makeMemorySize(pos, mem.getPtr());
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeMemoryGrow(Ctx& ctx, Index pos) {
- auto mem = maybeMemidx(ctx);
- CHECK_ERR(mem);
- return ctx.makeMemoryGrow(pos, mem.getPtr());
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeLocalGet(Ctx& ctx, Index pos) {
- auto local = localidx(ctx);
- CHECK_ERR(local);
- return ctx.makeLocalGet(pos, *local);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeLocalTee(Ctx& ctx, Index pos) {
- auto local = localidx(ctx);
- CHECK_ERR(local);
- return ctx.makeLocalTee(pos, *local);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeLocalSet(Ctx& ctx, Index pos) {
- auto local = localidx(ctx);
- CHECK_ERR(local);
- return ctx.makeLocalSet(pos, *local);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeGlobalGet(Ctx& ctx, Index pos) {
- auto global = globalidx(ctx);
- CHECK_ERR(global);
- return ctx.makeGlobalGet(pos, *global);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeGlobalSet(Ctx& ctx, Index pos) {
- auto global = globalidx(ctx);
- CHECK_ERR(global);
- return ctx.makeGlobalSet(pos, *global);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBlock(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeThenOrElse(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeConst(Ctx& ctx, Index pos, Type type) {
- assert(type.isBasic());
- switch (type.getBasic()) {
- case Type::i32:
- if (auto c = ctx.in.takeI32()) {
- return ctx.makeI32Const(pos, *c);
- }
- return ctx.in.err("expected i32");
- case Type::i64:
- if (auto c = ctx.in.takeI64()) {
- return ctx.makeI64Const(pos, *c);
- }
- return ctx.in.err("expected i64");
- case Type::f32:
- if (auto c = ctx.in.takeF32()) {
- return ctx.makeF32Const(pos, *c);
- }
- return ctx.in.err("expected f32");
- case Type::f64:
- if (auto c = ctx.in.takeF64()) {
- return ctx.makeF64Const(pos, *c);
- }
- return ctx.in.err("expected f64");
- case Type::v128:
- return ctx.in.err("unimplemented instruction");
- case Type::none:
- case Type::unreachable:
- break;
- }
- WASM_UNREACHABLE("unexpected type");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeLoad(
- Ctx& ctx, Index pos, Type type, bool signed_, int bytes, bool isAtomic) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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.makeAtomicFence(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDExtract(Ctx& ctx, Index pos, SIMDExtractOp op, size_t) {
- auto lane = ctx.in.takeU8();
- if (!lane) {
- return ctx.in.err("expected lane index");
- }
- return ctx.makeSIMDExtract(pos, op, *lane);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDReplace(Ctx& ctx, Index pos, SIMDReplaceOp op, size_t lanes) {
- auto lane = ctx.in.takeU8();
- if (!lane) {
- return ctx.in.err("expected lane index");
- }
- return ctx.makeSIMDReplace(pos, op, *lane);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeSIMDShuffle(Ctx& ctx, Index pos) {
- std::array<uint8_t, 16> lanes;
- for (int i = 0; i < 16; ++i) {
- auto lane = ctx.in.takeU8();
- if (!lane) {
- return ctx.in.err("expected lane index");
- }
- lanes[i] = *lane;
- }
- return ctx.makeSIMDShuffle(pos, lanes);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDTernary(Ctx& ctx, Index pos, SIMDTernaryOp op) {
- return ctx.makeSIMDTernary(pos, op);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeSIMDShift(Ctx& ctx, Index pos, SIMDShiftOp op) {
- return ctx.makeSIMDShift(pos, op);
-}
-
-template<typename Ctx>
-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, 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>
-Result<typename Ctx::InstrT> makeMemoryInit(Ctx& ctx, Index pos) {
- auto reset = ctx.in.getPos();
-
- auto retry = [&]() -> Result<typename Ctx::InstrT> {
- // We failed to parse. Maybe the data index was accidentally parsed as the
- // optional memory index. Try again without parsing a memory index.
- WithPosition with(ctx, reset);
- auto data = dataidx(ctx);
- CHECK_ERR(data);
- return ctx.makeMemoryInit(pos, nullptr, *data);
- };
-
- auto mem = maybeMemidx(ctx);
- if (mem.getErr()) {
- return retry();
- }
- auto data = dataidx(ctx);
- if (data.getErr()) {
- return retry();
- }
- return ctx.makeMemoryInit(pos, mem.getPtr(), *data);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeDataDrop(Ctx& ctx, Index pos) {
- auto data = dataidx(ctx);
- CHECK_ERR(data);
- return ctx.makeDataDrop(pos, *data);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeMemoryCopy(Ctx& ctx, Index pos) {
- auto destMem = maybeMemidx(ctx);
- CHECK_ERR(destMem);
- std::optional<typename Ctx::MemoryIdxT> srcMem = std::nullopt;
- if (destMem) {
- auto mem = memidx(ctx);
- CHECK_ERR(mem);
- srcMem = *mem;
- }
- return ctx.makeMemoryCopy(pos, destMem.getPtr(), srcMem ? &*srcMem : nullptr);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeMemoryFill(Ctx& ctx, Index pos) {
- auto mem = maybeMemidx(ctx);
- CHECK_ERR(mem);
- return ctx.makeMemoryFill(pos, mem.getPtr());
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makePop(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeIf(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeMaybeBlock(Ctx& ctx, Index pos, size_t i, Type type) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeLoop(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeCall(Ctx& ctx, Index pos, bool isReturn) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeCallIndirect(Ctx& ctx, Index pos, bool isReturn) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBreak(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBreakTable(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeReturn(Ctx& ctx, Index pos) {
- return ctx.makeReturn(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefNull(Ctx& ctx, Index pos) {
- auto t = heaptype(ctx);
- CHECK_ERR(t);
- return ctx.makeRefNull(pos, *t);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefIsNull(Ctx& ctx, Index pos) {
- return ctx.makeRefIsNull(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefFunc(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefEq(Ctx& ctx, Index pos) {
- return ctx.makeRefEq(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTableGet(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTableSet(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTableSize(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTableGrow(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTableFill(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTry(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeThrow(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRethrow(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTupleMake(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeTupleExtract(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeCallRef(Ctx& ctx, Index pos, bool isReturn) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefI31(Ctx& ctx, Index pos) {
- return ctx.makeRefI31(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeI31Get(Ctx& ctx, Index pos, bool signed_) {
- return ctx.makeI31Get(pos, signed_);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefTest(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefCast(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBrOnNull(Ctx& ctx, Index pos, bool onFail) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeBrOnCast(Ctx& ctx, Index pos, bool onFail) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStructNew(Ctx& ctx, Index pos, bool default_) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- if (default_) {
- return ctx.makeStructNewDefault(pos, *type);
- }
- return ctx.makeStructNew(pos, *type);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStructGet(Ctx& ctx, Index pos, bool signed_) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- auto field = fieldidx(ctx, *type);
- CHECK_ERR(field);
- return ctx.makeStructGet(pos, *type, *field, signed_);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStructSet(Ctx& ctx, Index pos) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- auto field = fieldidx(ctx, *type);
- CHECK_ERR(field);
- return ctx.makeStructSet(pos, *type, *field);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNew(Ctx& ctx, Index pos, bool default_) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- if (default_) {
- return ctx.makeArrayNewDefault(pos, *type);
- }
- return ctx.makeArrayNew(pos, *type);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewData(Ctx& ctx, Index pos) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- auto data = dataidx(ctx);
- CHECK_ERR(data);
- return ctx.makeArrayNewData(pos, *type, *data);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewElem(Ctx& ctx, Index pos) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- auto data = dataidx(ctx);
- CHECK_ERR(data);
- return ctx.makeArrayNewElem(pos, *type, *data);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewFixed(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayGet(Ctx& ctx, Index pos, bool signed_) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- return ctx.makeArrayGet(pos, *type, signed_);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArraySet(Ctx& ctx, Index pos) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- return ctx.makeArraySet(pos, *type);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayLen(Ctx& ctx, Index pos) {
- return ctx.makeArrayLen(pos);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayCopy(Ctx& ctx, Index pos) {
- auto destType = typeidx(ctx);
- CHECK_ERR(destType);
- auto srcType = typeidx(ctx);
- CHECK_ERR(srcType);
- return ctx.makeArrayCopy(pos, *destType, *srcType);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayFill(Ctx& ctx, Index pos) {
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- return ctx.makeArrayFill(pos, *type);
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayInitData(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayInitElem(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeRefAs(Ctx& ctx, Index pos, RefAsOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringNew(Ctx& ctx, Index pos, StringNewOp op, bool try_) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringConst(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringMeasure(Ctx& ctx, Index pos, StringMeasureOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringEncode(Ctx& ctx, Index pos, StringEncodeOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringConcat(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringEq(Ctx& ctx, Index pos, StringEqOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringAs(Ctx& ctx, Index pos, StringAsOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringWTF8Advance(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringWTF16Get(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringIterNext(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringIterMove(Ctx& ctx, Index pos, StringIterMoveOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeStringSliceWTF(Ctx& ctx, Index pos, StringSliceWTFOp op) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<typename Ctx::InstrT> makeStringSliceIter(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-// =======
-// Modules
-// =======
-
-// typeidx ::= x:u32 => x
-// | v:id => x (if types[x] = v)
-template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx) {
- if (auto x = ctx.in.takeU32()) {
- return *x;
- }
- if (auto id = ctx.in.takeID()) {
- // TODO: Fix position to point to start of id, not next element.
- auto idx = ctx.getTypeIndex(*id);
- CHECK_ERR(idx);
- return *idx;
- }
- return {};
-}
-
-template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx& ctx) {
- if (auto idx = maybeTypeidx(ctx)) {
- CHECK_ERR(idx);
- return ctx.getHeapTypeFromIdx(*idx);
- }
- return ctx.in.err("expected type index or identifier");
-}
-
-// fieldidx_t ::= x:u32 => x
-// | v:id => x (if t.fields[x] = v)
-template<typename Ctx>
-Result<typename Ctx::FieldIdxT> fieldidx(Ctx& ctx,
- typename Ctx::HeapTypeT type) {
- if (auto x = ctx.in.takeU32()) {
- return ctx.getFieldFromIdx(type, *x);
- }
- if (auto id = ctx.in.takeID()) {
- return ctx.getFieldFromName(type, *id);
- }
- return ctx.in.err("expected field index or identifier");
-}
-
-// memidx ::= x:u32 => x
-// | v:id => x (if memories[x] = v)
-template<typename Ctx>
-MaybeResult<typename Ctx::MemoryIdxT> maybeMemidx(Ctx& ctx) {
- if (auto x = ctx.in.takeU32()) {
- return ctx.getMemoryFromIdx(*x);
- }
- if (auto id = ctx.in.takeID()) {
- return ctx.getMemoryFromName(*id);
- }
- return {};
-}
-
-template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx& ctx) {
- if (auto idx = maybeMemidx(ctx)) {
- CHECK_ERR(idx);
- return *idx;
- }
- return ctx.in.err("expected memory index or identifier");
-}
-
-// memuse ::= '(' 'memory' x:memidx ')' => x
-template<typename Ctx>
-MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx& ctx) {
- if (!ctx.in.takeSExprStart("memory"sv)) {
- return {};
- }
- auto idx = memidx(ctx);
- CHECK_ERR(idx);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of memory use");
- }
- return *idx;
-}
-
-// globalidx ::= x:u32 => x
-// | v:id => x (if globals[x] = v)
-template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx& ctx) {
- if (auto x = ctx.in.takeU32()) {
- return ctx.getGlobalFromIdx(*x);
- }
- if (auto id = ctx.in.takeID()) {
- return ctx.getGlobalFromName(*id);
- }
- return ctx.in.err("expected global index or identifier");
-}
-
-// dataidx ::= x:u32 => x
-// | v:id => x (if data[x] = v)
-template<typename Ctx> Result<typename Ctx::DataIdxT> dataidx(Ctx& ctx) {
- if (auto x = ctx.in.takeU32()) {
- return ctx.getDataFromIdx(*x);
- }
- if (auto id = ctx.in.takeID()) {
- return ctx.getDataFromName(*id);
- }
- return ctx.in.err("expected data index or identifier");
-}
-
-// localidx ::= x:u32 => x
-// | v:id => x (if locals[x] = v)
-template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {
- if (auto x = ctx.in.takeU32()) {
- return ctx.getLocalFromIdx(*x);
- }
- if (auto id = ctx.in.takeID()) {
- return ctx.getLocalFromName(*id);
- }
- return ctx.in.err("expected local index or identifier");
-}
-
-// typeuse ::= '(' 'type' x:typeidx ')' => x, []
-// (if typedefs[x] = [t1*] -> [t2*]
-// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs
-// (if typedefs[x] = [t1*] -> [t2*])
-// | ((t1,IDs):param)* (t2:result)* => x, IDs
-// (if x is minimum s.t. typedefs[x] = [t1*] -> [t2*])
-template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- std::optional<typename Ctx::HeapTypeT> type;
- if (ctx.in.takeSExprStart("type"sv)) {
- auto x = typeidx(ctx);
- CHECK_ERR(x);
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of type use");
- }
-
- type = *x;
- }
-
- auto namedParams = params(ctx);
- CHECK_ERR(namedParams);
-
- auto resultTypes = results(ctx);
- CHECK_ERR(resultTypes);
-
- return ctx.makeTypeUse(pos, type, namedParams.getPtr(), resultTypes.getPtr());
-}
-
-// ('(' 'import' mod:name nm:name ')')?
-MaybeResult<ImportNames> inlineImport(ParseInput& in) {
- if (!in.takeSExprStart("import"sv)) {
- return {};
- }
- auto mod = in.takeName();
- if (!mod) {
- return in.err("expected import module");
- }
- auto nm = in.takeName();
- if (!nm) {
- return in.err("expected import name");
- }
- if (!in.takeRParen()) {
- return in.err("expected end of import");
- }
- // TODO: Return Ok when parsing Decls.
- return {{*mod, *nm}};
-}
-
-// ('(' 'export' name ')')*
-Result<std::vector<Name>> inlineExports(ParseInput& in) {
- std::vector<Name> exports;
- while (in.takeSExprStart("export"sv)) {
- auto name = in.takeName();
- if (!name) {
- return in.err("expected export name");
- }
- if (!in.takeRParen()) {
- return in.err("expected end of import");
- }
- exports.push_back(*name);
- }
- return exports;
-}
-
-// strtype ::= ft:functype => ft
-// | st:structtype => st
-// | at:arraytype => at
-template<typename Ctx> Result<> strtype(Ctx& ctx) {
- if (auto type = functype(ctx)) {
- CHECK_ERR(type);
- ctx.addFuncType(*type);
- return Ok{};
- }
- if (auto type = structtype(ctx)) {
- CHECK_ERR(type);
- ctx.addStructType(*type);
- return Ok{};
- }
- if (auto type = arraytype(ctx)) {
- CHECK_ERR(type);
- ctx.addArrayType(*type);
- return Ok{};
- }
- return ctx.in.err("expected type description");
-}
-
-// subtype ::= '(' 'type' id? '(' 'sub' typeidx? strtype ')' ')'
-// | '(' 'type' id? strtype ')'
-template<typename Ctx> MaybeResult<> subtype(Ctx& ctx) {
- auto pos = ctx.in.getPos();
-
- if (!ctx.in.takeSExprStart("type"sv)) {
- return {};
- }
-
- Name name;
- if (auto id = ctx.in.takeID()) {
- name = *id;
- }
-
- if (ctx.in.takeSExprStart("sub"sv)) {
- if (ctx.in.takeKeyword("open"sv)) {
- ctx.setOpen();
- }
- if (auto super = maybeTypeidx(ctx)) {
- CHECK_ERR(super);
- CHECK_ERR(ctx.addSubtype(*super));
- }
-
- CHECK_ERR(strtype(ctx));
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of subtype definition");
- }
- } else {
- CHECK_ERR(strtype(ctx));
- }
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of type definition");
- }
-
- ctx.finishSubtype(name, pos);
- return Ok{};
-}
-
-// deftype ::= '(' 'rec' subtype* ')'
-// | subtype
-template<typename Ctx> MaybeResult<> deftype(Ctx& ctx) {
- auto pos = ctx.in.getPos();
-
- if (ctx.in.takeSExprStart("rec"sv)) {
- size_t startIndex = ctx.getRecGroupStartIndex();
- size_t groupLen = 0;
- while (auto type = subtype(ctx)) {
- CHECK_ERR(type);
- ++groupLen;
- }
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected type definition or end of recursion group");
- }
- ctx.addRecGroup(startIndex, groupLen);
- } else if (auto type = subtype(ctx)) {
- CHECK_ERR(type);
- } else {
- return {};
- }
-
- ctx.finishDeftype(pos);
- return Ok{};
-}
-
-// local ::= '(' 'local id? t:valtype ')' => [t]
-// | '(' 'local t*:valtype* ')' => [t*]
-// locals ::= local*
-template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx& ctx) {
- bool hasAny = false;
- auto res = ctx.makeLocals();
- while (ctx.in.takeSExprStart("local"sv)) {
- hasAny = true;
- if (auto id = ctx.in.takeID()) {
- // Single named local
- auto type = valtype(ctx);
- CHECK_ERR(type);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of local");
- }
- ctx.appendLocal(res, *id, *type);
- } else {
- // Repeated unnamed locals
- while (!ctx.in.takeRParen()) {
- auto type = valtype(ctx);
- CHECK_ERR(type);
- ctx.appendLocal(res, {}, *type);
- }
- }
- }
- if (hasAny) {
- return res;
- }
- return {};
-}
-
-// func ::= '(' 'func' id? ('(' 'export' name ')')*
-// x,I:typeuse t*:vec(local) (in:instr)* ')'
-// | '(' 'func' id? ('(' 'export' name ')')*
-// '(' 'import' mod:name nm:name ')' typeuse ')'
-template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- if (!ctx.in.takeSExprStart("func"sv)) {
- return {};
- }
-
- Name name;
- if (auto id = ctx.in.takeID()) {
- name = *id;
- }
-
- auto exports = inlineExports(ctx.in);
- CHECK_ERR(exports);
-
- auto import = inlineImport(ctx.in);
- CHECK_ERR(import);
-
- auto type = typeuse(ctx);
- CHECK_ERR(type);
-
- std::optional<typename Ctx::LocalsT> localVars;
- if (!import) {
- if (auto l = locals(ctx)) {
- CHECK_ERR(l);
- localVars = *l;
- }
- }
-
- std::optional<typename Ctx::InstrsT> insts;
- if (!import) {
- auto i = instrs(ctx);
- CHECK_ERR(i);
- insts = *i;
- }
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of function");
- }
-
- CHECK_ERR(
- ctx.addFunc(name, *exports, import.getPtr(), *type, localVars, insts, pos));
- return Ok{};
-}
-
-// mem ::= '(' 'memory' id? ('(' 'export' name ')')*
-// ('(' 'data' b:datastring ')' | memtype) ')'
-// | '(' 'memory' id? ('(' 'export' name ')')*
-// '(' 'import' mod:name nm:name ')' memtype ')'
-template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- if (!ctx.in.takeSExprStart("memory"sv)) {
- return {};
- }
-
- Name name;
- if (auto id = ctx.in.takeID()) {
- name = *id;
- }
-
- auto exports = inlineExports(ctx.in);
- CHECK_ERR(exports);
-
- auto import = inlineImport(ctx.in);
- CHECK_ERR(import);
-
- std::optional<typename Ctx::MemTypeT> mtype;
- std::optional<typename Ctx::DataStringT> data;
- if (ctx.in.takeSExprStart("data"sv)) {
- if (import) {
- return ctx.in.err("imported memories cannot have inline data");
- }
- auto datastr = datastring(ctx);
- CHECK_ERR(datastr);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of inline data");
- }
- mtype = ctx.makeMemType(Type::i32, ctx.getLimitsFromData(*datastr), false);
- data = *datastr;
- } else {
- auto type = memtype(ctx);
- CHECK_ERR(type);
- mtype = *type;
- }
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of memory declaration");
- }
-
- CHECK_ERR(ctx.addMemory(name, *exports, import.getPtr(), *mtype, pos));
-
- if (data) {
- CHECK_ERR(ctx.addImplicitData(std::move(*data)));
- }
-
- return Ok{};
-}
-
-// global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')'
-// | '(' 'global' id? ('(' 'export' name ')')*
-// '(' 'import' mod:name nm:name ')' gt:globaltype ')'
-template<typename Ctx> MaybeResult<> global(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- if (!ctx.in.takeSExprStart("global"sv)) {
- return {};
- }
-
- Name name;
- if (auto id = ctx.in.takeID()) {
- name = *id;
- }
-
- auto exports = inlineExports(ctx.in);
- CHECK_ERR(exports);
-
- auto import = inlineImport(ctx.in);
- CHECK_ERR(import);
-
- auto type = globaltype(ctx);
- CHECK_ERR(type);
-
- std::optional<typename Ctx::ExprT> exp;
- if (!import) {
- auto e = expr(ctx);
- CHECK_ERR(e);
- exp = *e;
- }
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of global");
- }
-
- CHECK_ERR(ctx.addGlobal(name, *exports, import.getPtr(), *type, exp, pos));
- return Ok{};
-}
-
-// datastring ::= (b:string)* => concat(b*)
-template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx& ctx) {
- auto data = ctx.makeDataString();
- while (auto str = ctx.in.takeString()) {
- ctx.appendDataString(data, *str);
- }
- return data;
-}
-
-// data ::= '(' 'data' id? b*:datastring ')' => {init b*, mode passive}
-// | '(' 'data' id? x:memuse? ('(' 'offset' e:expr ')' | e:instr)
-// b*:datastring ')
-// => {init b*, mode active {memory x, offset e}}
-template<typename Ctx> MaybeResult<> data(Ctx& ctx) {
- auto pos = ctx.in.getPos();
- if (!ctx.in.takeSExprStart("data"sv)) {
- return {};
- }
-
- Name name;
- if (auto id = ctx.in.takeID()) {
- name = *id;
- }
-
- auto mem = maybeMemuse(ctx);
- CHECK_ERR(mem);
-
- std::optional<typename Ctx::ExprT> offset;
- if (ctx.in.takeSExprStart("offset"sv)) {
- auto e = expr(ctx);
- CHECK_ERR(e);
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of offset expression");
- }
- offset = *e;
- } else if (ctx.in.takeLParen()) {
- auto inst = instr(ctx);
- CHECK_ERR(inst);
- auto offsetExpr = ctx.instrToExpr(*inst);
- CHECK_ERR(offsetExpr);
- offset = *offsetExpr;
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of offset instruction");
- }
- }
-
- if (mem && !offset) {
- return ctx.in.err("expected offset for active segment");
- }
-
- auto str = datastring(ctx);
- CHECK_ERR(str);
-
- if (!ctx.in.takeRParen()) {
- return ctx.in.err("expected end of data segment");
- }
-
- CHECK_ERR(ctx.addData(name, mem.getPtr(), offset, std::move(*str), pos));
-
- return Ok{};
-}
-
-// modulefield ::= deftype
-// | import
-// | func
-// | table
-// | memory
-// | global
-// | export
-// | start
-// | elem
-// | data
-MaybeResult<> modulefield(ParseDeclsCtx& ctx) {
- if (auto t = ctx.in.peek(); !t || t->isRParen()) {
- return {};
- }
- if (auto res = deftype(ctx)) {
- CHECK_ERR(res);
- return Ok{};
- }
- if (auto res = func(ctx)) {
- CHECK_ERR(res);
- return Ok{};
- }
- if (auto res = memory(ctx)) {
- CHECK_ERR(res);
- return Ok{};
- }
- if (auto res = global(ctx)) {
- CHECK_ERR(res);
- return Ok{};
- }
- if (auto res = data(ctx)) {
- CHECK_ERR(res);
- return Ok{};
- }
- return ctx.in.err("unrecognized module field");
-}
-
-// module ::= '(' 'module' id? (m:modulefield)* ')'
-// | (m:modulefield)* eof
-Result<> module(ParseDeclsCtx& ctx) {
- bool outer = ctx.in.takeSExprStart("module"sv);
-
- if (outer) {
- if (auto id = ctx.in.takeID()) {
- ctx.wasm.name = *id;
- }
- }
-
- while (auto field = modulefield(ctx)) {
- CHECK_ERR(field);
- }
-
- if (outer && !ctx.in.takeRParen()) {
- return ctx.in.err("expected end of module");
- }
-
- return Ok{};
-}
-
-} // anonymous namespace
-
-Result<> parseModule(Module& wasm, std::string_view input) {
- // Parse module-level declarations.
- ParseDeclsCtx decls(input, wasm);
- CHECK_ERR(module(decls));
- if (!decls.in.empty()) {
- return decls.in.err("Unexpected tokens after module");
- }
-
- auto typeIndices = createIndexMap(decls.in, decls.subtypeDefs);
- CHECK_ERR(typeIndices);
-
- // Parse type definitions.
- std::vector<HeapType> types;
- {
- TypeBuilder builder(decls.subtypeDefs.size());
- ParseTypeDefsCtx ctx(input, builder, *typeIndices);
- for (auto& typeDef : decls.typeDefs) {
- WithPosition with(ctx, typeDef.pos);
- CHECK_ERR(deftype(ctx));
- }
- auto built = builder.build();
- if (auto* err = built.getError()) {
- std::stringstream msg;
- msg << "invalid type: " << err->reason;
- return ctx.in.err(decls.typeDefs[err->index].pos, msg.str());
- }
- types = *built;
- // Record type names on the module.
- for (size_t i = 0; i < types.size(); ++i) {
- auto& names = ctx.names[i];
- if (names.name.is() || names.fieldNames.size()) {
- wasm.typeNames.insert({types[i], names});
- }
- }
- }
-
- // Parse implicit type definitions and map typeuses without explicit types to
- // the correct types.
- std::unordered_map<Index, HeapType> implicitTypes;
- {
- ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, *typeIndices);
- for (Index pos : decls.implicitTypeDefs) {
- WithPosition with(ctx, pos);
- CHECK_ERR(typeuse(ctx));
- }
- }
-
- {
- // Parse module-level types.
- ParseModuleTypesCtx ctx(input, wasm, types, implicitTypes, *typeIndices);
- CHECK_ERR(parseDefs(ctx, decls.funcDefs, func));
- CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory));
- CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
- // TODO: Parse types of other module elements.
- }
- {
- // Parse definitions.
- // TODO: Parallelize this.
- ParseDefsCtx ctx(input, wasm, types, implicitTypes, *typeIndices);
- CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
- CHECK_ERR(parseDefs(ctx, decls.dataDefs, data));
-
- for (Index i = 0; i < decls.funcDefs.size(); ++i) {
- ctx.index = i;
- ctx.setFunction(wasm.functions[i].get());
- CHECK_ERR(ctx.irBuilder.makeBlock(Name{}, ctx.func->getResults()));
- WithPosition with(ctx, decls.funcDefs[i].pos);
- auto parsed = func(ctx);
- CHECK_ERR(parsed);
- assert(parsed);
- }
- }
-
- return Ok{};
-}
-
-} // namespace wasm::WATParser