summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp1
-rw-r--r--src/emscripten-optimizer/parser.cpp4
-rw-r--r--src/emscripten-optimizer/parser.h984
-rw-r--r--src/gen-s-parser.inc3462
-rw-r--r--src/parser/parsers.h2
-rw-r--r--src/passes/RemoveNonJSOps.cpp1
-rw-r--r--src/tools/wasm-opt.cpp14
-rw-r--r--src/tools/wasm-shell.cpp1
-rw-r--r--src/tools/wasm2js.cpp1
-rw-r--r--src/wasm-s-parser.h385
-rw-r--r--src/wasm/CMakeLists.txt1
-rw-r--r--src/wasm/wasm-io.cpp15
-rw-r--r--src/wasm/wasm-s-parser.cpp4124
13 files changed, 3 insertions, 8992 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index f60e5ec59..4e7fb0b61 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -31,7 +31,6 @@
#include "wasm-binary.h"
#include "wasm-builder.h"
#include "wasm-interpreter.h"
-#include "wasm-s-parser.h"
#include "wasm-stack.h"
#include "wasm-validator.h"
#include "wasm.h"
diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp
index 533043232..c5ecfd177 100644
--- a/src/emscripten-optimizer/parser.cpp
+++ b/src/emscripten-optimizer/parser.cpp
@@ -112,10 +112,6 @@ IString STORE("store");
IString GETTER("get");
IString SETTER("set");
-IStringSet
- keywords("var const function if else do while for break continue return "
- "switch case default throw try catch finally true false null new");
-
const char *OPERATOR_INITS = "+-*/%<>&^|~=!,?:.", *SEPARATORS = "([;{}";
int MAX_OPERATOR_SIZE = 3;
diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h
index f1d472863..cc5032fea 100644
--- a/src/emscripten-optimizer/parser.h
+++ b/src/emscripten-optimizer/parser.h
@@ -162,8 +162,6 @@ extern IString STORE;
extern IString GETTER;
extern IString SETTER;
-extern IStringSet keywords;
-
extern const char *OPERATOR_INITS, *SEPARATORS;
extern int MAX_OPERATOR_SIZE, LOWEST_PREC;
@@ -186,988 +184,6 @@ extern std::vector<OperatorClass> operatorClasses;
extern bool isIdentInit(char x);
extern bool isIdentPart(char x);
-// parser
-
-template<class NodeRef, class Builder> class Parser {
-
- static bool isSpace(char x) {
- return x == 32 || x == 9 || x == 10 || x == 13;
- } /* space, tab, linefeed/newline, or return */
- static void skipSpace(char*& curr) {
- while (*curr) {
- if (isSpace(*curr)) {
- curr++;
- continue;
- }
- if (curr[0] == '/' && curr[1] == '/') {
- curr += 2;
- while (*curr && *curr != '\n') {
- curr++;
- }
- if (*curr) {
- curr++;
- }
- continue;
- }
- if (curr[0] == '/' && curr[1] == '*') {
- curr += 2;
- while (*curr && (curr[0] != '*' || curr[1] != '/')) {
- curr++;
- }
- curr += 2;
- continue;
- }
- return;
- }
- }
-
- static bool isDigit(char x) { return x >= '0' && x <= '9'; }
-
- static bool hasChar(const char* list, char x) {
- while (*list) {
- if (*list++ == x) {
- return true;
- }
- }
- return false;
- }
-
- // An atomic fragment of something. Stops at a natural boundary.
- enum FragType {
- KEYWORD = 0,
- OPERATOR = 1,
- IDENT = 2,
- STRING = 3, // without quotes
- INT = 4,
- DOUBLE = 5,
- SEPARATOR = 6
- };
-
- struct Frag {
- // MSVC does not allow unrestricted unions:
- // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
-#ifndef _MSC_VER
- union {
-#endif
- IString str;
- double num;
-#ifndef _MSC_VER
- };
-#endif
- int size;
- FragType type;
-
- bool isNumber() const { return type == INT || type == DOUBLE; }
-
- explicit Frag(char* src) {
- char* start = src;
- if (isIdentInit(*src)) {
- // read an identifier or a keyword
- src++;
- while (isIdentPart(*src)) {
- src++;
- }
- if (*src == 0) {
- str = IString(start);
- } else {
- char temp = *src;
- *src = 0;
- str = IString(start, false);
- *src = temp;
- }
- type = keywords.has(str) ? KEYWORD : IDENT;
- } else if (isDigit(*src) || (src[0] == '.' && isDigit(src[1]))) {
- if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) {
- // Explicitly parse hex numbers of form "0x...", because strtod
- // supports hex number strings only in C++11, and Visual Studio 2013
- // does not yet support that functionality.
- src += 2;
- num = 0;
- while (1) {
- if (*src >= '0' && *src <= '9') {
- num *= 16;
- num += *src - '0';
- } else if (*src >= 'a' && *src <= 'f') {
- num *= 16;
- num += *src - 'a' + 10;
- } else if (*src >= 'A' && *src <= 'F') {
- num *= 16;
- num += *src - 'A' + 10;
- } else {
- break;
- }
- src++;
- }
- } else {
- num = strtod(start, &src);
- }
- // asm.js must have a '.' for double values. however, we also tolerate
- // uglify's tendency to emit without a '.' (and fix it later with a +).
- // for valid asm.js input, the '.' should be enough, and for uglify
- // in the emscripten optimizer pipeline, we use simple_ast where
- // INT/DOUBLE is quite the same at this point anyhow
- type = (std::find(start, src, '.') == src &&
- (wasm::isSInteger32(num) || wasm::isUInteger32(num)))
- ? INT
- : DOUBLE;
- assert(src > start);
- } else if (hasChar(OPERATOR_INITS, *src)) {
- switch (*src) {
- case '!':
- str = src[1] == '=' ? NE : L_NOT;
- break;
- case '%':
- str = MOD;
- break;
- case '&':
- str = AND;
- break;
- case '*':
- str = MUL;
- break;
- case '+':
- str = PLUS;
- break;
- case ',':
- str = COMMA;
- break;
- case '-':
- str = MINUS;
- break;
- case '.':
- str = PERIOD;
- break;
- case '/':
- str = DIV;
- break;
- case ':':
- str = COLON;
- break;
- case '<':
- str = src[1] == '<' ? LSHIFT : (src[1] == '=' ? LE : LT);
- break;
- case '=':
- str = src[1] == '=' ? EQ : SET;
- break;
- case '>':
- str = src[1] == '>' ? (src[2] == '>' ? TRSHIFT : RSHIFT)
- : (src[1] == '=' ? GE : GT);
- break;
- case '?':
- str = QUESTION;
- break;
- case '^':
- str = XOR;
- break;
- case '|':
- str = OR;
- break;
- case '~':
- str = B_NOT;
- break;
- default:
- abort();
- }
- size = str.size();
-#ifndef NDEBUG
- char temp = start[size];
- start[size] = 0;
- assert(str.str == start);
- start[size] = temp;
-#endif
- type = OPERATOR;
- return;
- } else if (hasChar(SEPARATORS, *src)) {
- type = SEPARATOR;
- char temp = src[1];
- src[1] = 0;
- str = IString(src, false);
- src[1] = temp;
- src++;
- } else if (*src == '"' || *src == '\'') {
- char* end = strchr(src + 1, *src);
- *end = 0;
- str = IString(src + 1);
- src = end + 1;
- type = STRING;
- } else {
- dump("frag parsing", src);
- abort();
- }
- size = src - start;
- }
- };
-
- struct ExpressionElement {
- bool isNode;
- // MSVC does not allow unrestricted unions:
- // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
-#ifndef _MSC_VER
- union {
-#endif
- NodeRef node;
- IString op;
-#ifndef _MSC_VER
- };
-#endif
- ExpressionElement(NodeRef n) : isNode(true), node(n) {}
- ExpressionElement(IString o) : isNode(false), op(o) {}
-
- NodeRef getNode() {
- assert(isNode);
- return node;
- }
- IString getOp() {
- assert(!isNode);
- return op;
- }
- };
-
- // This is a list of the current stack of node-operator-node-operator-etc.
- // this works by each parseExpression call appending to the vector; then
- // recursing out, and the toplevel sorts it all
- using ExpressionParts = std::vector<ExpressionElement>;
- std::vector<ExpressionParts> expressionPartsStack;
-
- // Parses an element in a list of such elements, e.g. list of statements in a
- // block, or list of parameters in a call
- NodeRef parseElement(char*& src, const char* seps = ";") {
- // dump("parseElement", src);
- skipSpace(src);
- Frag frag(src);
- src += frag.size;
- switch (frag.type) {
- case KEYWORD: {
- return parseAfterKeyword(frag, src, seps);
- }
- case IDENT: {
- return parseAfterIdent(frag, src, seps);
- }
- case STRING:
- case INT:
- case DOUBLE: {
- return parseExpression(parseFrag(frag), src, seps);
- }
- case SEPARATOR: {
- if (frag.str == OPEN_PAREN) {
- return parseExpression(parseAfterParen(src), src, seps);
- }
- if (frag.str == OPEN_BRACE) {
- return parseExpression(parseAfterBrace(src), src, seps);
- }
- if (frag.str == OPEN_CURLY) {
- return parseExpression(parseAfterCurly(src), src, seps);
- }
- abort();
- }
- case OPERATOR: {
- return parseExpression(frag.str, src, seps);
- }
- default:
- /* dump("parseElement", src); printf("bad frag type: %d\n",frag.type);
- */
- abort();
- }
- return nullptr;
- }
-
- NodeRef parseFrag(Frag& frag) {
- switch (frag.type) {
- case IDENT:
- return Builder::makeName(frag.str);
- case STRING:
- return Builder::makeString(frag.str);
- case INT:
- return Builder::makeInt(uint32_t(frag.num));
- case DOUBLE:
- return Builder::makeDouble(frag.num);
- default:
- abort();
- }
- return nullptr;
- }
-
- NodeRef parseAfterKeyword(Frag& frag, char*& src, const char* seps) {
- skipSpace(src);
- if (frag.str == FUNCTION) {
- return parseFunction(src, seps);
- } else if (frag.str == VAR) {
- return parseVar(src, seps, false);
- } else if (frag.str == CONST) {
- return parseVar(src, seps, true);
- } else if (frag.str == RETURN) {
- return parseReturn(src, seps);
- } else if (frag.str == IF) {
- return parseIf(src, seps);
- } else if (frag.str == DO) {
- return parseDo(src, seps);
- } else if (frag.str == WHILE) {
- return parseWhile(src, seps);
- } else if (frag.str == BREAK) {
- return parseBreak(src, seps);
- } else if (frag.str == CONTINUE) {
- return parseContinue(src, seps);
- } else if (frag.str == SWITCH) {
- return parseSwitch(src, seps);
- } else if (frag.str == NEW) {
- return parseNew(src, seps);
- } else if (frag.str == FOR) {
- return parseFor(src, seps);
- }
- dump(frag.str.str, src);
- abort();
- return nullptr;
- }
-
- NodeRef parseFunction(char*& src, const char* seps) {
- Frag name(src);
- if (name.type == IDENT) {
- src += name.size;
- } else {
- assert(name.type == SEPARATOR && name.str[0] == '(');
- name.str = IString();
- }
- NodeRef ret = Builder::makeFunction(name.str);
- skipSpace(src);
- assert(*src == '(');
- src++;
- while (1) {
- skipSpace(src);
- if (*src == ')') {
- break;
- }
- Frag arg(src);
- assert(arg.type == IDENT);
- src += arg.size;
- Builder::appendArgumentToFunction(ret, arg.str);
- skipSpace(src);
- if (*src == ')') {
- break;
- }
- if (*src == ',') {
- src++;
- continue;
- }
- abort();
- }
- src++;
- Builder::setBlockContent(ret, parseBracketedBlock(src));
- // TODO: parse expression?
- return ret;
- }
-
- NodeRef parseVar(char*& src, const char* seps, bool is_const) {
- NodeRef ret = Builder::makeVar(is_const);
- while (1) {
- skipSpace(src);
- if (*src == ';') {
- break;
- }
- Frag name(src);
- assert(name.type == IDENT);
- NodeRef value;
- src += name.size;
- skipSpace(src);
- if (*src == '=') {
- src++;
- skipSpace(src);
- value = parseElement(src, ";,");
- }
- Builder::appendToVar(ret, name.str, value);
- skipSpace(src);
- if (*src == ';') {
- break;
- }
- if (*src == ',') {
- src++;
- continue;
- }
- abort();
- }
- src++;
- return ret;
- }
-
- NodeRef parseReturn(char*& src, const char* seps) {
- skipSpace(src);
- NodeRef value = !hasChar(seps, *src) ? parseElement(src, seps) : nullptr;
- skipSpace(src);
- assert(hasChar(seps, *src));
- if (*src == ';') {
- src++;
- }
- return Builder::makeReturn(value);
- }
-
- NodeRef parseIf(char*& src, const char* seps) {
- NodeRef condition = parseParenned(src);
- NodeRef ifTrue = parseMaybeBracketed(src, seps);
- skipSpace(src);
- NodeRef ifFalse;
- if (!hasChar(seps, *src)) {
- Frag next(src);
- if (next.type == KEYWORD && next.str == ELSE) {
- src += next.size;
- ifFalse = parseMaybeBracketed(src, seps);
- }
- }
- return Builder::makeIf(condition, ifTrue, ifFalse);
- }
-
- NodeRef parseDo(char*& src, const char* seps) {
- NodeRef body = parseMaybeBracketed(src, seps);
- skipSpace(src);
- Frag next(src);
- assert(next.type == KEYWORD && next.str == WHILE);
- src += next.size;
- NodeRef condition = parseParenned(src);
- return Builder::makeDo(body, condition);
- }
-
- NodeRef parseWhile(char*& src, const char* seps) {
- NodeRef condition = parseParenned(src);
- NodeRef body = parseMaybeBracketed(src, seps);
- return Builder::makeWhile(condition, body);
- }
-
- NodeRef parseFor(char*& src, const char* seps) {
- skipSpace(src);
- assert(*src == '(');
- src++;
- NodeRef init = parseElement(src, ";");
- skipSpace(src);
- assert(*src == ';');
- src++;
- NodeRef condition = parseElement(src, ";");
- skipSpace(src);
- assert(*src == ';');
- src++;
- NodeRef inc = parseElement(src, ")");
- skipSpace(src);
- assert(*src == ')');
- src++;
- NodeRef body = parseMaybeBracketed(src, seps);
- return Builder::makeFor(init, condition, inc, body);
- }
-
- NodeRef parseBreak(char*& src, const char* seps) {
- skipSpace(src);
- Frag next(src);
- if (next.type == IDENT) {
- src += next.size;
- }
- return Builder::makeBreak(next.type == IDENT ? next.str : IString());
- }
-
- NodeRef parseContinue(char*& src, const char* seps) {
- skipSpace(src);
- Frag next(src);
- if (next.type == IDENT) {
- src += next.size;
- }
- return Builder::makeContinue(next.type == IDENT ? next.str : IString());
- }
-
- NodeRef parseSwitch(char*& src, const char* seps) {
- NodeRef ret = Builder::makeSwitch(parseParenned(src));
- skipSpace(src);
- assert(*src == '{');
- src++;
- while (1) {
- // find all cases and possibly a default
- skipSpace(src);
- if (*src == '}') {
- break;
- }
- Frag next(src);
- if (next.type == KEYWORD) {
- if (next.str == CASE) {
- src += next.size;
- skipSpace(src);
- NodeRef arg;
- Frag value(src);
- if (value.isNumber()) {
- arg = parseFrag(value);
- src += value.size;
- } else if (value.type == OPERATOR) {
- // negative number
- assert(value.str == MINUS);
- src += value.size;
- skipSpace(src);
- Frag value2(src);
- assert(value2.isNumber());
- arg = Builder::makePrefix(MINUS, parseFrag(value2));
- src += value2.size;
- } else {
- // identifier and function call
- assert(value.type == IDENT);
- src += value.size;
- skipSpace(src);
- arg = parseCall(parseFrag(value), src);
- }
- Builder::appendCaseToSwitch(ret, arg);
- skipSpace(src);
- assert(*src == ':');
- src++;
- continue;
- } else if (next.str == DEFAULT) {
- src += next.size;
- Builder::appendDefaultToSwitch(ret);
- skipSpace(src);
- assert(*src == ':');
- src++;
- continue;
- }
- // otherwise, may be some keyword that happens to start a block (e.g.
- // case 1: _return_ 5)
- }
- // not case X: or default: or }, so must be some code
- skipSpace(src);
- bool explicitBlock = *src == '{';
- NodeRef subBlock = explicitBlock ? parseBracketedBlock(src)
- : parseBlock(src, ";}", CASE, DEFAULT);
- Builder::appendCodeToSwitch(ret, subBlock, explicitBlock);
- }
- skipSpace(src);
- assert(*src == '}');
- src++;
- return ret;
- }
-
- NodeRef parseNew(char*& src, const char* seps) {
- return Builder::makeNew(parseElement(src, seps));
- }
-
- NodeRef parseAfterIdent(Frag& frag, char*& src, const char* seps) {
- skipSpace(src);
- if (*src == '(') {
- return parseExpression(parseCall(parseFrag(frag), src), src, seps);
- }
- if (*src == '[') {
- return parseExpression(parseIndexing(parseFrag(frag), src), src, seps);
- }
- if (*src == ':' && expressionPartsStack.back().size() == 0) {
- src++;
- skipSpace(src);
- NodeRef inner;
- if (*src == '{') {
- // context lets us know this is not an object, but a block
- inner = parseBracketedBlock(src);
- } else {
- inner = parseElement(src, seps);
- }
- return Builder::makeLabel(frag.str, inner);
- }
- if (*src == '.') {
- return parseExpression(parseDotting(parseFrag(frag), src), src, seps);
- }
- return parseExpression(parseFrag(frag), src, seps);
- }
-
- NodeRef parseCall(NodeRef target, char*& src) {
- expressionPartsStack.resize(expressionPartsStack.size() + 1);
- assert(*src == '(');
- src++;
- NodeRef ret = Builder::makeCall(target);
- while (1) {
- skipSpace(src);
- if (*src == ')') {
- break;
- }
- Builder::appendToCall(ret, parseElement(src, ",)"));
- skipSpace(src);
- if (*src == ')') {
- break;
- }
- if (*src == ',') {
- src++;
- continue;
- }
- abort();
- }
- src++;
- assert(expressionPartsStack.back().size() == 0);
- expressionPartsStack.pop_back();
- return ret;
- }
-
- NodeRef parseIndexing(NodeRef target, char*& src) {
- expressionPartsStack.resize(expressionPartsStack.size() + 1);
- assert(*src == '[');
- src++;
- NodeRef ret = Builder::makeIndexing(target, parseElement(src, "]"));
- skipSpace(src);
- assert(*src == ']');
- src++;
- assert(expressionPartsStack.back().size() == 0);
- expressionPartsStack.pop_back();
- return ret;
- }
-
- NodeRef parseDotting(NodeRef target, char*& src) {
- assert(*src == '.');
- src++;
- Frag key(src);
- assert(key.type == IDENT);
- src += key.size;
- return Builder::makeDot(target, key.str);
- }
-
- NodeRef parseAfterParen(char*& src) {
- expressionPartsStack.resize(expressionPartsStack.size() + 1);
- skipSpace(src);
- NodeRef ret = parseElement(src, ")");
- skipSpace(src);
- assert(*src == ')');
- src++;
- assert(expressionPartsStack.back().size() == 0);
- expressionPartsStack.pop_back();
- return ret;
- }
-
- NodeRef parseAfterBrace(char*& src) {
- expressionPartsStack.resize(expressionPartsStack.size() + 1);
- NodeRef ret = Builder::makeArray();
- while (1) {
- skipSpace(src);
- assert(*src);
- if (*src == ']') {
- break;
- }
- NodeRef element = parseElement(src, ",]");
- Builder::appendToArray(ret, element);
- skipSpace(src);
- if (*src == ']') {
- break;
- }
- if (*src == ',') {
- src++;
- continue;
- }
- abort();
- }
- src++;
- return ret;
- }
-
- NodeRef parseAfterCurly(char*& src) {
- expressionPartsStack.resize(expressionPartsStack.size() + 1);
- NodeRef ret = Builder::makeObject();
- while (1) {
- skipSpace(src);
- assert(*src);
- if (*src == '}') {
- break;
- }
- Frag key(src);
- assert(key.type == IDENT || key.type == STRING);
- src += key.size;
- skipSpace(src);
- assert(*src == ':');
- src++;
- NodeRef value = parseElement(src, ",}");
- Builder::appendToObject(ret, key.str, value);
- skipSpace(src);
- if (*src == '}') {
- break;
- }
- if (*src == ',') {
- src++;
- continue;
- }
- abort();
- }
- src++;
- return ret;
- }
-
- void dumpParts(ExpressionParts& parts, int i) {
- printf("expressionparts: %d (at %d)\n", parts.size(), i);
- printf("| ");
- for (int i = 0; i < parts.size(); i++) {
- if (parts[i].isNode) {
- parts[i].getNode()->stringify(std::cout);
- printf(" ");
- } else {
- printf(" _%s_ ", parts[i].getOp().str);
- }
- }
- printf("|\n");
- }
-
- NodeRef makeBinary(NodeRef left, IString op, NodeRef right) {
- if (op == PERIOD) {
- return Builder::makeDot(left, right);
- } else {
- return Builder::makeBinary(left, op, right);
- }
- }
-
- NodeRef
- parseExpression(ExpressionElement initial, char*& src, const char* seps) {
- // dump("parseExpression", src);
- ExpressionParts& parts = expressionPartsStack.back();
- skipSpace(src);
- if (*src == 0 || hasChar(seps, *src)) {
- if (parts.size() > 0) {
- parts.push_back(initial); // cherry on top of the cake
- }
- return initial.getNode();
- }
- bool top = parts.size() == 0;
- if (initial.isNode) {
- Frag next(src);
- if (next.type == OPERATOR) {
- parts.push_back(initial);
- src += next.size;
- parts.push_back(next.str);
- } else {
- if (*src == '(') {
- initial = parseCall(initial.getNode(), src);
- } else if (*src == '[') {
- initial = parseIndexing(initial.getNode(), src);
- } else {
- dump("bad parseExpression state", src);
- abort();
- }
- return parseExpression(initial, src, seps);
- }
- } else {
- parts.push_back(initial);
- }
- NodeRef last = parseElement(src, seps);
- if (!top) {
- return last;
- }
- {
- // |parts| may have been invalidated by that call
- ExpressionParts& parts = expressionPartsStack.back();
- // we are the toplevel. sort it all out
- // collapse right to left, highest priority first
- // dumpParts(parts, 0);
- for (auto& ops : operatorClasses) {
- if (ops.rtl) {
- // right to left
- for (int i = parts.size() - 1; i >= 0; i--) {
- if (parts[i].isNode) {
- continue;
- }
- IString op = parts[i].getOp();
- if (!ops.ops.has(op)) {
- continue;
- }
- if (ops.type == OperatorClass::Binary && i > 0 &&
- i < (int)parts.size() - 1) {
- parts[i] =
- makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode());
- parts.erase(parts.begin() + i + 1);
- parts.erase(parts.begin() + i - 1);
- } else if (ops.type == OperatorClass::Prefix &&
- i < (int)parts.size() - 1) {
- if (i > 0 && parts[i - 1].isNode) {
- // cannot apply prefix operator if it would join two nodes
- continue;
- }
- parts[i] = Builder::makePrefix(op, parts[i + 1].getNode());
- parts.erase(parts.begin() + i + 1);
- } else if (ops.type == OperatorClass::Tertiary) {
- // we must be at X ? Y : Z
- // ^
- // dumpParts(parts, i);
- if (op != COLON) {
- continue;
- }
- assert(i < (int)parts.size() - 1 && i >= 3);
- if (parts[i - 2].getOp() != QUESTION) {
- continue; // e.g. x ? y ? 1 : 0 : 2
- }
- parts[i - 3] = Builder::makeConditional(parts[i - 3].getNode(),
- parts[i - 1].getNode(),
- parts[i + 1].getNode());
- parts.erase(parts.begin() + i - 2, parts.begin() + i + 2);
- // basically a reset, due to things like x ? y ? 1 : 0 : 2
- i = parts.size();
- } // TODO: postfix
- }
- } else {
- // left to right
- for (int i = 0; i < (int)parts.size(); i++) {
- if (parts[i].isNode) {
- continue;
- }
- IString op = parts[i].getOp();
- if (!ops.ops.has(op)) {
- continue;
- }
- if (ops.type == OperatorClass::Binary && i > 0 &&
- i < (int)parts.size() - 1) {
- parts[i] =
- makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode());
- parts.erase(parts.begin() + i + 1);
- parts.erase(parts.begin() + i - 1);
- i--;
- } else if (ops.type == OperatorClass::Prefix &&
- i < (int)parts.size() - 1) {
- if (i > 0 && parts[i - 1].isNode) {
- // cannot apply prefix operator if it would join two nodes
- continue;
- }
- parts[i] = Builder::makePrefix(op, parts[i + 1].getNode());
- parts.erase(parts.begin() + i + 1);
- // allow a previous prefix operator to cascade
- i = std::max(i - 2, 0);
- } // TODO: tertiary, postfix
- }
- }
- }
- assert(parts.size() == 1);
- NodeRef ret = parts[0].getNode();
- parts.clear();
- return ret;
- }
- }
-
- // Parses a block of code (e.g. a bunch of statements inside {,}, or the top
- // level of o file)
- NodeRef parseBlock(char*& src,
- const char* seps = ";",
- IString keywordSep1 = IString(),
- IString keywordSep2 = IString()) {
- NodeRef block = Builder::makeBlock();
- // dump("parseBlock", src);
- while (1) {
- skipSpace(src);
- if (*src == 0) {
- break;
- }
- if (*src == ';') {
- src++; // skip a statement in this block
- continue;
- }
- if (hasChar(seps, *src)) {
- break;
- }
- if (!!keywordSep1) {
- Frag next(src);
- if (next.type == KEYWORD && next.str == keywordSep1) {
- break;
- }
- }
- if (!!keywordSep2) {
- Frag next(src);
- if (next.type == KEYWORD && next.str == keywordSep2) {
- break;
- }
- }
- NodeRef element = parseElementOrStatement(src, seps);
- Builder::appendToBlock(block, element);
- }
- return block;
- }
-
- NodeRef parseBracketedBlock(char*& src) {
- skipSpace(src);
- assert(*src == '{');
- src++;
- // the two are not symmetrical, ; is just internally separating, } is the
- // final one - parseBlock knows all this
- NodeRef block = parseBlock(src, ";}");
- assert(*src == '}');
- src++;
- return block;
- }
-
- NodeRef parseElementOrStatement(char*& src, const char* seps) {
- skipSpace(src);
- if (*src == ';') {
- src++;
- // we don't need the brackets here, but oh well
- return Builder::makeBlock();
- }
- if (*src == '{') { // detect a trivial {} in a statement context
- char* before = src;
- src++;
- skipSpace(src);
- if (*src == '}') {
- src++;
- // we don't need the brackets here, but oh well
- return Builder::makeBlock();
- }
- src = before;
- }
- NodeRef ret = parseElement(src, seps);
- skipSpace(src);
- if (*src == ';') {
- ret = Builder::makeStatement(ret);
- src++;
- }
- return ret;
- }
-
- NodeRef parseMaybeBracketed(char*& src, const char* seps) {
- skipSpace(src);
- return *src == '{' ? parseBracketedBlock(src)
- : parseElementOrStatement(src, seps);
- }
-
- NodeRef parseParenned(char*& src) {
- skipSpace(src);
- assert(*src == '(');
- src++;
- NodeRef ret = parseElement(src, ")");
- skipSpace(src);
- assert(*src == ')');
- src++;
- return ret;
- }
-
- // Debugging
-
- char* allSource = nullptr;
- int allSize = 0;
-
- static void dump(const char* where, char* curr) {
- /*
- printf("%s:\n=============\n", where);
- for (int i = 0; i < allSize; i++)
- printf("%c", allSource[i] ? allSource[i] :
- '?');
- printf("\n");
- for (int i = 0; i < (curr - allSource); i++) printf(" ");
- printf("^\n=============\n");
- */
- fprintf(stderr, "%s:\n==========\n", where);
- int newlinesLeft = 2;
- int charsLeft = 200;
- while (*curr) {
- if (*curr == '\n') {
- newlinesLeft--;
- if (newlinesLeft == 0) {
- break;
- }
- }
- charsLeft--;
- if (charsLeft == 0) {
- break;
- }
- fprintf(stderr, "%c", *curr++);
- }
- fprintf(stderr, "\n\n");
- }
-
-public:
- Parser() { expressionPartsStack.resize(1); }
-
- // Highest-level parsing, as of a JavaScript script file.
- NodeRef parseToplevel(char* src) {
- allSource = src;
- allSize = strlen(src);
- NodeRef toplevel = Builder::makeToplevel();
- Builder::setBlockContent(toplevel, parseBlock(src));
- return toplevel;
- }
-};
-
} // namespace cashew
#endif // wasm_parser_h
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index a451805db..5cdb29d83 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -2,3467 +2,6 @@
// clang-format off
-#ifdef INSTRUCTION_PARSER
-#undef INSTRUCTION_PARSER
-using namespace std::string_view_literals;
-auto op = s[0]->str().str;
-char buf[33] = {};
-memcpy(buf, op.data(), op.size());
-switch (buf[0]) {
- case 'a': {
- switch (buf[1]) {
- case 'r': {
- switch (buf[6]) {
- case 'c':
- if (op == "array.copy"sv) { return makeArrayCopy(s); }
- goto parse_error;
- case 'f':
- if (op == "array.fill"sv) { return makeArrayFill(s); }
- goto parse_error;
- case 'g': {
- switch (buf[9]) {
- case '\0':
- if (op == "array.get"sv) { return makeArrayGet(s); }
- goto parse_error;
- case '_': {
- switch (buf[10]) {
- case 's':
- if (op == "array.get_s"sv) { return makeArrayGet(s, true); }
- goto parse_error;
- case 'u':
- if (op == "array.get_u"sv) { return makeArrayGet(s, false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'i': {
- switch (buf[11]) {
- case 'd':
- if (op == "array.init_data"sv) { return makeArrayInitData(s); }
- goto parse_error;
- case 'e':
- if (op == "array.init_elem"sv) { return makeArrayInitElem(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l':
- if (op == "array.len"sv) { return makeArrayLen(s); }
- goto parse_error;
- case 'n': {
- switch (buf[9]) {
- case '\0':
- if (op == "array.new"sv) { return makeArrayNew(s, false); }
- goto parse_error;
- case '_': {
- switch (buf[10]) {
- case 'd': {
- switch (buf[11]) {
- case 'a':
- if (op == "array.new_data"sv) { return makeArrayNewData(s); }
- goto parse_error;
- case 'e':
- if (op == "array.new_default"sv) { return makeArrayNew(s, true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e':
- if (op == "array.new_elem"sv) { return makeArrayNewElem(s); }
- goto parse_error;
- case 'f':
- if (op == "array.new_fixed"sv) { return makeArrayNewFixed(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's':
- if (op == "array.set"sv) { return makeArraySet(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "atomic.fence"sv) { return makeAtomicFence(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b': {
- switch (buf[1]) {
- case 'l':
- if (op == "block"sv) { return makeBlock(s); }
- goto parse_error;
- case 'r': {
- switch (buf[2]) {
- case '\0':
- if (op == "br"sv) { return makeBreak(s, false); }
- goto parse_error;
- case '_': {
- switch (buf[3]) {
- case 'i':
- if (op == "br_if"sv) { return makeBreak(s, true); }
- goto parse_error;
- case 'o': {
- switch (buf[6]) {
- case 'c': {
- switch (buf[10]) {
- case '\0':
- if (op == "br_on_cast"sv) { return makeBrOnCast(s); }
- goto parse_error;
- case '_':
- if (op == "br_on_cast_fail"sv) { return makeBrOnCast(s, true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[7]) {
- case 'o':
- if (op == "br_on_non_null"sv) { return makeBrOnNull(s, true); }
- goto parse_error;
- case 'u':
- if (op == "br_on_null"sv) { return makeBrOnNull(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "br_table"sv) { return makeBreakTable(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[1]) {
- case 'a': {
- switch (buf[4]) {
- case '\0':
- if (op == "call"sv) { return makeCall(s, /*isReturn=*/false); }
- goto parse_error;
- case '_': {
- switch (buf[5]) {
- case 'i':
- if (op == "call_indirect"sv) { return makeCallIndirect(s, /*isReturn=*/false); }
- goto parse_error;
- case 'r':
- if (op == "call_ref"sv) { return makeCallRef(s, /*isReturn=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'o': {
- switch (buf[5]) {
- case 'b':
- if (op == "cont.bind"sv) { return makeContBind(s); }
- goto parse_error;
- case 'n':
- if (op == "cont.new"sv) { return makeContNew(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'd': {
- switch (buf[1]) {
- case 'a':
- if (op == "data.drop"sv) { return makeDataDrop(s); }
- goto parse_error;
- case 'r':
- if (op == "drop"sv) { return makeDrop(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[1]) {
- case 'l':
- if (op == "else"sv) { return makeThenOrElse(s); }
- goto parse_error;
- case 'x': {
- switch (buf[7]) {
- case 'e':
- if (op == "extern.externalize"sv) { return makeRefAs(s, ExternExternalize); }
- goto parse_error;
- case 'i':
- if (op == "extern.internalize"sv) { return makeRefAs(s, ExternInternalize); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'f': {
- switch (buf[1]) {
- case '3': {
- switch (buf[3]) {
- case '.': {
- switch (buf[4]) {
- case 'a': {
- switch (buf[5]) {
- case 'b':
- if (op == "f32.abs"sv) { return makeUnary(s, UnaryOp::AbsFloat32); }
- goto parse_error;
- case 'd':
- if (op == "f32.add"sv) { return makeBinary(s, BinaryOp::AddFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[5]) {
- case 'e':
- if (op == "f32.ceil"sv) { return makeUnary(s, UnaryOp::CeilFloat32); }
- goto parse_error;
- case 'o': {
- switch (buf[6]) {
- case 'n': {
- switch (buf[7]) {
- case 's':
- if (op == "f32.const"sv) { return makeConst(s, Type::f32); }
- goto parse_error;
- case 'v': {
- switch (buf[13]) {
- case '3': {
- switch (buf[16]) {
- case 's':
- if (op == "f32.convert_i32_s"sv) { return makeUnary(s, UnaryOp::ConvertSInt32ToFloat32); }
- goto parse_error;
- case 'u':
- if (op == "f32.convert_i32_u"sv) { return makeUnary(s, UnaryOp::ConvertUInt32ToFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[16]) {
- case 's':
- if (op == "f32.convert_i64_s"sv) { return makeUnary(s, UnaryOp::ConvertSInt64ToFloat32); }
- goto parse_error;
- case 'u':
- if (op == "f32.convert_i64_u"sv) { return makeUnary(s, UnaryOp::ConvertUInt64ToFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "f32.copysign"sv) { return makeBinary(s, BinaryOp::CopySignFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'd': {
- switch (buf[5]) {
- case 'e':
- if (op == "f32.demote_f64"sv) { return makeUnary(s, UnaryOp::DemoteFloat64); }
- goto parse_error;
- case 'i':
- if (op == "f32.div"sv) { return makeBinary(s, BinaryOp::DivFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e':
- if (op == "f32.eq"sv) { return makeBinary(s, BinaryOp::EqFloat32); }
- goto parse_error;
- case 'f':
- if (op == "f32.floor"sv) { return makeUnary(s, UnaryOp::FloorFloat32); }
- goto parse_error;
- case 'g': {
- switch (buf[5]) {
- case 'e':
- if (op == "f32.ge"sv) { return makeBinary(s, BinaryOp::GeFloat32); }
- goto parse_error;
- case 't':
- if (op == "f32.gt"sv) { return makeBinary(s, BinaryOp::GtFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[5]) {
- case 'e':
- if (op == "f32.le"sv) { return makeBinary(s, BinaryOp::LeFloat32); }
- goto parse_error;
- case 'o':
- if (op == "f32.load"sv) { return makeLoad(s, Type::f32, /*signed=*/false, 4, /*isAtomic=*/false); }
- goto parse_error;
- case 't':
- if (op == "f32.lt"sv) { return makeBinary(s, BinaryOp::LtFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[5]) {
- case 'a':
- if (op == "f32.max"sv) { return makeBinary(s, BinaryOp::MaxFloat32); }
- goto parse_error;
- case 'i':
- if (op == "f32.min"sv) { return makeBinary(s, BinaryOp::MinFloat32); }
- goto parse_error;
- case 'u':
- if (op == "f32.mul"sv) { return makeBinary(s, BinaryOp::MulFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[6]) {
- case '\0':
- if (op == "f32.ne"sv) { return makeBinary(s, BinaryOp::NeFloat32); }
- goto parse_error;
- case 'a':
- if (op == "f32.nearest"sv) { return makeUnary(s, UnaryOp::NearestFloat32); }
- goto parse_error;
- case 'g':
- if (op == "f32.neg"sv) { return makeUnary(s, UnaryOp::NegFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r':
- if (op == "f32.reinterpret_i32"sv) { return makeUnary(s, UnaryOp::ReinterpretInt32); }
- goto parse_error;
- case 's': {
- switch (buf[5]) {
- case 'q':
- if (op == "f32.sqrt"sv) { return makeUnary(s, UnaryOp::SqrtFloat32); }
- goto parse_error;
- case 't':
- if (op == "f32.store"sv) { return makeStore(s, Type::f32, 4, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "f32.sub"sv) { return makeBinary(s, BinaryOp::SubFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "f32.trunc"sv) { return makeUnary(s, UnaryOp::TruncFloat32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "f32x4.abs"sv) { return makeUnary(s, UnaryOp::AbsVecF32x4); }
- goto parse_error;
- case 'd':
- if (op == "f32x4.add"sv) { return makeBinary(s, BinaryOp::AddVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[7]) {
- case 'e':
- if (op == "f32x4.ceil"sv) { return makeUnary(s, UnaryOp::CeilVecF32x4); }
- goto parse_error;
- case 'o': {
- switch (buf[20]) {
- case 's':
- if (op == "f32x4.convert_i32x4_s"sv) { return makeUnary(s, UnaryOp::ConvertSVecI32x4ToVecF32x4); }
- goto parse_error;
- case 'u':
- if (op == "f32x4.convert_i32x4_u"sv) { return makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'd': {
- switch (buf[7]) {
- case 'e':
- if (op == "f32x4.demote_f64x2_zero"sv) { return makeUnary(s, UnaryOp::DemoteZeroVecF64x2ToVecF32x4); }
- goto parse_error;
- case 'i':
- if (op == "f32x4.div"sv) { return makeBinary(s, BinaryOp::DivVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "f32x4.eq"sv) { return makeBinary(s, BinaryOp::EqVecF32x4); }
- goto parse_error;
- case 'x':
- if (op == "f32x4.extract_lane"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF32x4, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'f':
- if (op == "f32x4.floor"sv) { return makeUnary(s, UnaryOp::FloorVecF32x4); }
- goto parse_error;
- case 'g': {
- switch (buf[7]) {
- case 'e':
- if (op == "f32x4.ge"sv) { return makeBinary(s, BinaryOp::GeVecF32x4); }
- goto parse_error;
- case 't':
- if (op == "f32x4.gt"sv) { return makeBinary(s, BinaryOp::GtVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'e':
- if (op == "f32x4.le"sv) { return makeBinary(s, BinaryOp::LeVecF32x4); }
- goto parse_error;
- case 't':
- if (op == "f32x4.lt"sv) { return makeBinary(s, BinaryOp::LtVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a':
- if (op == "f32x4.max"sv) { return makeBinary(s, BinaryOp::MaxVecF32x4); }
- goto parse_error;
- case 'i':
- if (op == "f32x4.min"sv) { return makeBinary(s, BinaryOp::MinVecF32x4); }
- goto parse_error;
- case 'u':
- if (op == "f32x4.mul"sv) { return makeBinary(s, BinaryOp::MulVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[8]) {
- case '\0':
- if (op == "f32x4.ne"sv) { return makeBinary(s, BinaryOp::NeVecF32x4); }
- goto parse_error;
- case 'a':
- if (op == "f32x4.nearest"sv) { return makeUnary(s, UnaryOp::NearestVecF32x4); }
- goto parse_error;
- case 'g':
- if (op == "f32x4.neg"sv) { return makeUnary(s, UnaryOp::NegVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'p': {
- switch (buf[8]) {
- case 'a':
- if (op == "f32x4.pmax"sv) { return makeBinary(s, BinaryOp::PMaxVecF32x4); }
- goto parse_error;
- case 'i':
- if (op == "f32x4.pmin"sv) { return makeBinary(s, BinaryOp::PMinVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[8]) {
- case 'l': {
- switch (buf[14]) {
- case 'f': {
- switch (buf[16]) {
- case 'a':
- if (op == "f32x4.relaxed_fma"sv) { return makeSIMDTernary(s, SIMDTernaryOp::RelaxedFmaVecF32x4); }
- goto parse_error;
- case 's':
- if (op == "f32x4.relaxed_fms"sv) { return makeSIMDTernary(s, SIMDTernaryOp::RelaxedFmsVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[15]) {
- case 'a':
- if (op == "f32x4.relaxed_max"sv) { return makeBinary(s, BinaryOp::RelaxedMaxVecF32x4); }
- goto parse_error;
- case 'i':
- if (op == "f32x4.relaxed_min"sv) { return makeBinary(s, BinaryOp::RelaxedMinVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "f32x4.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF32x4, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'p':
- if (op == "f32x4.splat"sv) { return makeUnary(s, UnaryOp::SplatVecF32x4); }
- goto parse_error;
- case 'q':
- if (op == "f32x4.sqrt"sv) { return makeUnary(s, UnaryOp::SqrtVecF32x4); }
- goto parse_error;
- case 'u':
- if (op == "f32x4.sub"sv) { return makeBinary(s, BinaryOp::SubVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "f32x4.trunc"sv) { return makeUnary(s, UnaryOp::TruncVecF32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[3]) {
- case '.': {
- switch (buf[4]) {
- case 'a': {
- switch (buf[5]) {
- case 'b':
- if (op == "f64.abs"sv) { return makeUnary(s, UnaryOp::AbsFloat64); }
- goto parse_error;
- case 'd':
- if (op == "f64.add"sv) { return makeBinary(s, BinaryOp::AddFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[5]) {
- case 'e':
- if (op == "f64.ceil"sv) { return makeUnary(s, UnaryOp::CeilFloat64); }
- goto parse_error;
- case 'o': {
- switch (buf[6]) {
- case 'n': {
- switch (buf[7]) {
- case 's':
- if (op == "f64.const"sv) { return makeConst(s, Type::f64); }
- goto parse_error;
- case 'v': {
- switch (buf[13]) {
- case '3': {
- switch (buf[16]) {
- case 's':
- if (op == "f64.convert_i32_s"sv) { return makeUnary(s, UnaryOp::ConvertSInt32ToFloat64); }
- goto parse_error;
- case 'u':
- if (op == "f64.convert_i32_u"sv) { return makeUnary(s, UnaryOp::ConvertUInt32ToFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[16]) {
- case 's':
- if (op == "f64.convert_i64_s"sv) { return makeUnary(s, UnaryOp::ConvertSInt64ToFloat64); }
- goto parse_error;
- case 'u':
- if (op == "f64.convert_i64_u"sv) { return makeUnary(s, UnaryOp::ConvertUInt64ToFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "f64.copysign"sv) { return makeBinary(s, BinaryOp::CopySignFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'd':
- if (op == "f64.div"sv) { return makeBinary(s, BinaryOp::DivFloat64); }
- goto parse_error;
- case 'e':
- if (op == "f64.eq"sv) { return makeBinary(s, BinaryOp::EqFloat64); }
- goto parse_error;
- case 'f':
- if (op == "f64.floor"sv) { return makeUnary(s, UnaryOp::FloorFloat64); }
- goto parse_error;
- case 'g': {
- switch (buf[5]) {
- case 'e':
- if (op == "f64.ge"sv) { return makeBinary(s, BinaryOp::GeFloat64); }
- goto parse_error;
- case 't':
- if (op == "f64.gt"sv) { return makeBinary(s, BinaryOp::GtFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[5]) {
- case 'e':
- if (op == "f64.le"sv) { return makeBinary(s, BinaryOp::LeFloat64); }
- goto parse_error;
- case 'o':
- if (op == "f64.load"sv) { return makeLoad(s, Type::f64, /*signed=*/false, 8, /*isAtomic=*/false); }
- goto parse_error;
- case 't':
- if (op == "f64.lt"sv) { return makeBinary(s, BinaryOp::LtFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[5]) {
- case 'a':
- if (op == "f64.max"sv) { return makeBinary(s, BinaryOp::MaxFloat64); }
- goto parse_error;
- case 'i':
- if (op == "f64.min"sv) { return makeBinary(s, BinaryOp::MinFloat64); }
- goto parse_error;
- case 'u':
- if (op == "f64.mul"sv) { return makeBinary(s, BinaryOp::MulFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[6]) {
- case '\0':
- if (op == "f64.ne"sv) { return makeBinary(s, BinaryOp::NeFloat64); }
- goto parse_error;
- case 'a':
- if (op == "f64.nearest"sv) { return makeUnary(s, UnaryOp::NearestFloat64); }
- goto parse_error;
- case 'g':
- if (op == "f64.neg"sv) { return makeUnary(s, UnaryOp::NegFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "f64.promote_f32"sv) { return makeUnary(s, UnaryOp::PromoteFloat32); }
- goto parse_error;
- case 'r':
- if (op == "f64.reinterpret_i64"sv) { return makeUnary(s, UnaryOp::ReinterpretInt64); }
- goto parse_error;
- case 's': {
- switch (buf[5]) {
- case 'q':
- if (op == "f64.sqrt"sv) { return makeUnary(s, UnaryOp::SqrtFloat64); }
- goto parse_error;
- case 't':
- if (op == "f64.store"sv) { return makeStore(s, Type::f64, 8, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "f64.sub"sv) { return makeBinary(s, BinaryOp::SubFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "f64.trunc"sv) { return makeUnary(s, UnaryOp::TruncFloat64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "f64x2.abs"sv) { return makeUnary(s, UnaryOp::AbsVecF64x2); }
- goto parse_error;
- case 'd':
- if (op == "f64x2.add"sv) { return makeBinary(s, BinaryOp::AddVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[7]) {
- case 'e':
- if (op == "f64x2.ceil"sv) { return makeUnary(s, UnaryOp::CeilVecF64x2); }
- goto parse_error;
- case 'o': {
- switch (buf[24]) {
- case 's':
- if (op == "f64x2.convert_low_i32x4_s"sv) { return makeUnary(s, UnaryOp::ConvertLowSVecI32x4ToVecF64x2); }
- goto parse_error;
- case 'u':
- if (op == "f64x2.convert_low_i32x4_u"sv) { return makeUnary(s, UnaryOp::ConvertLowUVecI32x4ToVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'd':
- if (op == "f64x2.div"sv) { return makeBinary(s, BinaryOp::DivVecF64x2); }
- goto parse_error;
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "f64x2.eq"sv) { return makeBinary(s, BinaryOp::EqVecF64x2); }
- goto parse_error;
- case 'x':
- if (op == "f64x2.extract_lane"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF64x2, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'f':
- if (op == "f64x2.floor"sv) { return makeUnary(s, UnaryOp::FloorVecF64x2); }
- goto parse_error;
- case 'g': {
- switch (buf[7]) {
- case 'e':
- if (op == "f64x2.ge"sv) { return makeBinary(s, BinaryOp::GeVecF64x2); }
- goto parse_error;
- case 't':
- if (op == "f64x2.gt"sv) { return makeBinary(s, BinaryOp::GtVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'e':
- if (op == "f64x2.le"sv) { return makeBinary(s, BinaryOp::LeVecF64x2); }
- goto parse_error;
- case 't':
- if (op == "f64x2.lt"sv) { return makeBinary(s, BinaryOp::LtVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a':
- if (op == "f64x2.max"sv) { return makeBinary(s, BinaryOp::MaxVecF64x2); }
- goto parse_error;
- case 'i':
- if (op == "f64x2.min"sv) { return makeBinary(s, BinaryOp::MinVecF64x2); }
- goto parse_error;
- case 'u':
- if (op == "f64x2.mul"sv) { return makeBinary(s, BinaryOp::MulVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[8]) {
- case '\0':
- if (op == "f64x2.ne"sv) { return makeBinary(s, BinaryOp::NeVecF64x2); }
- goto parse_error;
- case 'a':
- if (op == "f64x2.nearest"sv) { return makeUnary(s, UnaryOp::NearestVecF64x2); }
- goto parse_error;
- case 'g':
- if (op == "f64x2.neg"sv) { return makeUnary(s, UnaryOp::NegVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'p': {
- switch (buf[7]) {
- case 'm': {
- switch (buf[8]) {
- case 'a':
- if (op == "f64x2.pmax"sv) { return makeBinary(s, BinaryOp::PMaxVecF64x2); }
- goto parse_error;
- case 'i':
- if (op == "f64x2.pmin"sv) { return makeBinary(s, BinaryOp::PMinVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r':
- if (op == "f64x2.promote_low_f32x4"sv) { return makeUnary(s, UnaryOp::PromoteLowVecF32x4ToVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[8]) {
- case 'l': {
- switch (buf[14]) {
- case 'f': {
- switch (buf[16]) {
- case 'a':
- if (op == "f64x2.relaxed_fma"sv) { return makeSIMDTernary(s, SIMDTernaryOp::RelaxedFmaVecF64x2); }
- goto parse_error;
- case 's':
- if (op == "f64x2.relaxed_fms"sv) { return makeSIMDTernary(s, SIMDTernaryOp::RelaxedFmsVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[15]) {
- case 'a':
- if (op == "f64x2.relaxed_max"sv) { return makeBinary(s, BinaryOp::RelaxedMaxVecF64x2); }
- goto parse_error;
- case 'i':
- if (op == "f64x2.relaxed_min"sv) { return makeBinary(s, BinaryOp::RelaxedMinVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "f64x2.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF64x2, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'p':
- if (op == "f64x2.splat"sv) { return makeUnary(s, UnaryOp::SplatVecF64x2); }
- goto parse_error;
- case 'q':
- if (op == "f64x2.sqrt"sv) { return makeUnary(s, UnaryOp::SqrtVecF64x2); }
- goto parse_error;
- case 'u':
- if (op == "f64x2.sub"sv) { return makeBinary(s, BinaryOp::SubVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't':
- if (op == "f64x2.trunc"sv) { return makeUnary(s, UnaryOp::TruncVecF64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[7]) {
- case 'g':
- if (op == "global.get"sv) { return makeGlobalGet(s); }
- goto parse_error;
- case 's':
- if (op == "global.set"sv) { return makeGlobalSet(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'i': {
- switch (buf[1]) {
- case '1': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "i16x8.abs"sv) { return makeUnary(s, UnaryOp::AbsVecI16x8); }
- goto parse_error;
- case 'd': {
- switch (buf[9]) {
- case '\0':
- if (op == "i16x8.add"sv) { return makeBinary(s, BinaryOp::AddVecI16x8); }
- goto parse_error;
- case '_': {
- switch (buf[14]) {
- case 's':
- if (op == "i16x8.add_sat_s"sv) { return makeBinary(s, BinaryOp::AddSatSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.add_sat_u"sv) { return makeBinary(s, BinaryOp::AddSatUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l':
- if (op == "i16x8.all_true"sv) { return makeUnary(s, UnaryOp::AllTrueVecI16x8); }
- goto parse_error;
- case 'v':
- if (op == "i16x8.avgr_u"sv) { return makeBinary(s, BinaryOp::AvgrUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b':
- if (op == "i16x8.bitmask"sv) { return makeUnary(s, UnaryOp::BitmaskVecI16x8); }
- goto parse_error;
- case 'd':
- if (op == "i16x8.dot_i8x16_i7x16_s"sv) { return makeBinary(s, BinaryOp::DotI8x16I7x16SToVecI16x8); }
- goto parse_error;
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "i16x8.eq"sv) { return makeBinary(s, BinaryOp::EqVecI16x8); }
- goto parse_error;
- case 'x': {
- switch (buf[9]) {
- case 'a': {
- switch (buf[28]) {
- case 's':
- if (op == "i16x8.extadd_pairwise_i8x16_s"sv) { return makeUnary(s, UnaryOp::ExtAddPairwiseSVecI8x16ToI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extadd_pairwise_i8x16_u"sv) { return makeUnary(s, UnaryOp::ExtAddPairwiseUVecI8x16ToI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i16x8.extend_high_i8x16_s"sv) { return makeUnary(s, UnaryOp::ExtendHighSVecI8x16ToVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extend_high_i8x16_u"sv) { return makeUnary(s, UnaryOp::ExtendHighUVecI8x16ToVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i16x8.extend_low_i8x16_s"sv) { return makeUnary(s, UnaryOp::ExtendLowSVecI8x16ToVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extend_low_i8x16_u"sv) { return makeUnary(s, UnaryOp::ExtendLowUVecI8x16ToVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i16x8.extmul_high_i8x16_s"sv) { return makeBinary(s, BinaryOp::ExtMulHighSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extmul_high_i8x16_u"sv) { return makeBinary(s, BinaryOp::ExtMulHighUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i16x8.extmul_low_i8x16_s"sv) { return makeBinary(s, BinaryOp::ExtMulLowSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extmul_low_i8x16_u"sv) { return makeBinary(s, BinaryOp::ExtMulLowUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[19]) {
- case 's':
- if (op == "i16x8.extract_lane_s"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI16x8, 8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.extract_lane_u"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI16x8, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[7]) {
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i16x8.ge_s"sv) { return makeBinary(s, BinaryOp::GeSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.ge_u"sv) { return makeBinary(s, BinaryOp::GeUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i16x8.gt_s"sv) { return makeBinary(s, BinaryOp::GtSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.gt_u"sv) { return makeBinary(s, BinaryOp::GtUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'a':
- if (op == "i16x8.laneselect"sv) { return makeSIMDTernary(s, SIMDTernaryOp::LaneselectI16x8); }
- goto parse_error;
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i16x8.le_s"sv) { return makeBinary(s, BinaryOp::LeSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.le_u"sv) { return makeBinary(s, BinaryOp::LeUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i16x8.lt_s"sv) { return makeBinary(s, BinaryOp::LtSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.lt_u"sv) { return makeBinary(s, BinaryOp::LtUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[10]) {
- case 's':
- if (op == "i16x8.max_s"sv) { return makeBinary(s, BinaryOp::MaxSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.max_u"sv) { return makeBinary(s, BinaryOp::MaxUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'i': {
- switch (buf[10]) {
- case 's':
- if (op == "i16x8.min_s"sv) { return makeBinary(s, BinaryOp::MinSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.min_u"sv) { return makeBinary(s, BinaryOp::MinUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "i16x8.mul"sv) { return makeBinary(s, BinaryOp::MulVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[19]) {
- case 's':
- if (op == "i16x8.narrow_i32x4_s"sv) { return makeBinary(s, BinaryOp::NarrowSVecI32x4ToVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.narrow_i32x4_u"sv) { return makeBinary(s, BinaryOp::NarrowUVecI32x4ToVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[8]) {
- case '\0':
- if (op == "i16x8.ne"sv) { return makeBinary(s, BinaryOp::NeVecI16x8); }
- goto parse_error;
- case 'g':
- if (op == "i16x8.neg"sv) { return makeUnary(s, UnaryOp::NegVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'q':
- if (op == "i16x8.q15mulr_sat_s"sv) { return makeBinary(s, BinaryOp::Q15MulrSatSVecI16x8); }
- goto parse_error;
- case 'r': {
- switch (buf[8]) {
- case 'l':
- if (op == "i16x8.relaxed_q15mulr_s"sv) { return makeBinary(s, BinaryOp::RelaxedQ15MulrSVecI16x8); }
- goto parse_error;
- case 'p':
- if (op == "i16x8.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI16x8, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'h': {
- switch (buf[8]) {
- case 'l':
- if (op == "i16x8.shl"sv) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI16x8); }
- goto parse_error;
- case 'r': {
- switch (buf[10]) {
- case 's':
- if (op == "i16x8.shr_s"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.shr_u"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i16x8.splat"sv) { return makeUnary(s, UnaryOp::SplatVecI16x8); }
- goto parse_error;
- case 'u': {
- switch (buf[9]) {
- case '\0':
- if (op == "i16x8.sub"sv) { return makeBinary(s, BinaryOp::SubVecI16x8); }
- goto parse_error;
- case '_': {
- switch (buf[14]) {
- case 's':
- if (op == "i16x8.sub_sat_s"sv) { return makeBinary(s, BinaryOp::SubSatSVecI16x8); }
- goto parse_error;
- case 'u':
- if (op == "i16x8.sub_sat_u"sv) { return makeBinary(s, BinaryOp::SubSatUVecI16x8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '3': {
- switch (buf[2]) {
- case '1': {
- switch (buf[4]) {
- case 'g': {
- switch (buf[8]) {
- case 's':
- if (op == "i31.get_s"sv) { return makeI31Get(s, true); }
- goto parse_error;
- case 'u':
- if (op == "i31.get_u"sv) { return makeI31Get(s, false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n':
- if (op == "i31.new"sv) { return makeRefI31(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '2': {
- switch (buf[3]) {
- case '.': {
- switch (buf[4]) {
- case 'a': {
- switch (buf[5]) {
- case 'd':
- if (op == "i32.add"sv) { return makeBinary(s, BinaryOp::AddInt32); }
- goto parse_error;
- case 'n':
- if (op == "i32.and"sv) { return makeBinary(s, BinaryOp::AndInt32); }
- goto parse_error;
- case 't': {
- switch (buf[11]) {
- case 'l': {
- switch (buf[15]) {
- case '\0':
- if (op == "i32.atomic.load"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 4, /*isAtomic=*/true); }
- goto parse_error;
- case '1':
- if (op == "i32.atomic.load16_u"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 2, /*isAtomic=*/true); }
- goto parse_error;
- case '8':
- if (op == "i32.atomic.load8_u"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 1, /*isAtomic=*/true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[14]) {
- case '.': {
- switch (buf[15]) {
- case 'a': {
- switch (buf[16]) {
- case 'd':
- if (op == "i32.atomic.rmw.add"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i32, 4); }
- goto parse_error;
- case 'n':
- if (op == "i32.atomic.rmw.and"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i32, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i32.atomic.rmw.cmpxchg"sv) { return makeAtomicCmpxchg(s, Type::i32, 4); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw.or"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i32, 4); }
- goto parse_error;
- case 's':
- if (op == "i32.atomic.rmw.sub"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i32, 4); }
- goto parse_error;
- case 'x': {
- switch (buf[16]) {
- case 'c':
- if (op == "i32.atomic.rmw.xchg"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i32, 4); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw.xor"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i32, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '1': {
- switch (buf[17]) {
- case 'a': {
- switch (buf[18]) {
- case 'd':
- if (op == "i32.atomic.rmw16.add_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i32, 2); }
- goto parse_error;
- case 'n':
- if (op == "i32.atomic.rmw16.and_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i32, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i32.atomic.rmw16.cmpxchg_u"sv) { return makeAtomicCmpxchg(s, Type::i32, 2); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw16.or_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i32, 2); }
- goto parse_error;
- case 's':
- if (op == "i32.atomic.rmw16.sub_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i32, 2); }
- goto parse_error;
- case 'x': {
- switch (buf[18]) {
- case 'c':
- if (op == "i32.atomic.rmw16.xchg_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i32, 2); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw16.xor_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i32, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[16]) {
- case 'a': {
- switch (buf[17]) {
- case 'd':
- if (op == "i32.atomic.rmw8.add_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i32, 1); }
- goto parse_error;
- case 'n':
- if (op == "i32.atomic.rmw8.and_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i32, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i32.atomic.rmw8.cmpxchg_u"sv) { return makeAtomicCmpxchg(s, Type::i32, 1); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw8.or_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i32, 1); }
- goto parse_error;
- case 's':
- if (op == "i32.atomic.rmw8.sub_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i32, 1); }
- goto parse_error;
- case 'x': {
- switch (buf[17]) {
- case 'c':
- if (op == "i32.atomic.rmw8.xchg_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i32, 1); }
- goto parse_error;
- case 'o':
- if (op == "i32.atomic.rmw8.xor_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i32, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[16]) {
- case '\0':
- if (op == "i32.atomic.store"sv) { return makeStore(s, Type::i32, 4, /*isAtomic=*/true); }
- goto parse_error;
- case '1':
- if (op == "i32.atomic.store16"sv) { return makeStore(s, Type::i32, 2, /*isAtomic=*/true); }
- goto parse_error;
- case '8':
- if (op == "i32.atomic.store8"sv) { return makeStore(s, Type::i32, 1, /*isAtomic=*/true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[5]) {
- case 'l':
- if (op == "i32.clz"sv) { return makeUnary(s, UnaryOp::ClzInt32); }
- goto parse_error;
- case 'o':
- if (op == "i32.const"sv) { return makeConst(s, Type::i32); }
- goto parse_error;
- case 't':
- if (op == "i32.ctz"sv) { return makeUnary(s, UnaryOp::CtzInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'd': {
- switch (buf[8]) {
- case 's':
- if (op == "i32.div_s"sv) { return makeBinary(s, BinaryOp::DivSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.div_u"sv) { return makeBinary(s, BinaryOp::DivUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[5]) {
- case 'q': {
- switch (buf[6]) {
- case '\0':
- if (op == "i32.eq"sv) { return makeBinary(s, BinaryOp::EqInt32); }
- goto parse_error;
- case 'z':
- if (op == "i32.eqz"sv) { return makeUnary(s, UnaryOp::EqZInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[10]) {
- case '1':
- if (op == "i32.extend16_s"sv) { return makeUnary(s, UnaryOp::ExtendS16Int32); }
- goto parse_error;
- case '8':
- if (op == "i32.extend8_s"sv) { return makeUnary(s, UnaryOp::ExtendS8Int32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[7]) {
- case 's':
- if (op == "i32.ge_s"sv) { return makeBinary(s, BinaryOp::GeSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.ge_u"sv) { return makeBinary(s, BinaryOp::GeUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[7]) {
- case 's':
- if (op == "i32.gt_s"sv) { return makeBinary(s, BinaryOp::GtSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.gt_u"sv) { return makeBinary(s, BinaryOp::GtUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[7]) {
- case 's':
- if (op == "i32.le_s"sv) { return makeBinary(s, BinaryOp::LeSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.le_u"sv) { return makeBinary(s, BinaryOp::LeUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'o': {
- switch (buf[8]) {
- case '\0':
- if (op == "i32.load"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 4, /*isAtomic=*/false); }
- goto parse_error;
- case '1': {
- switch (buf[11]) {
- case 's':
- if (op == "i32.load16_s"sv) { return makeLoad(s, Type::i32, /*signed=*/true, 2, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "i32.load16_u"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 2, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[10]) {
- case 's':
- if (op == "i32.load8_s"sv) { return makeLoad(s, Type::i32, /*signed=*/true, 1, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "i32.load8_u"sv) { return makeLoad(s, Type::i32, /*signed=*/false, 1, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[7]) {
- case 's':
- if (op == "i32.lt_s"sv) { return makeBinary(s, BinaryOp::LtSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.lt_u"sv) { return makeBinary(s, BinaryOp::LtUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm':
- if (op == "i32.mul"sv) { return makeBinary(s, BinaryOp::MulInt32); }
- goto parse_error;
- case 'n':
- if (op == "i32.ne"sv) { return makeBinary(s, BinaryOp::NeInt32); }
- goto parse_error;
- case 'o':
- if (op == "i32.or"sv) { return makeBinary(s, BinaryOp::OrInt32); }
- goto parse_error;
- case 'p':
- if (op == "i32.popcnt"sv) { return makeUnary(s, UnaryOp::PopcntInt32); }
- goto parse_error;
- case 'r': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[6]) {
- case 'i':
- if (op == "i32.reinterpret_f32"sv) { return makeUnary(s, UnaryOp::ReinterpretFloat32); }
- goto parse_error;
- case 'm': {
- switch (buf[8]) {
- case 's':
- if (op == "i32.rem_s"sv) { return makeBinary(s, BinaryOp::RemSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.rem_u"sv) { return makeBinary(s, BinaryOp::RemUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'o': {
- switch (buf[7]) {
- case 'l':
- if (op == "i32.rotl"sv) { return makeBinary(s, BinaryOp::RotLInt32); }
- goto parse_error;
- case 'r':
- if (op == "i32.rotr"sv) { return makeBinary(s, BinaryOp::RotRInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[5]) {
- case 'h': {
- switch (buf[6]) {
- case 'l':
- if (op == "i32.shl"sv) { return makeBinary(s, BinaryOp::ShlInt32); }
- goto parse_error;
- case 'r': {
- switch (buf[8]) {
- case 's':
- if (op == "i32.shr_s"sv) { return makeBinary(s, BinaryOp::ShrSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.shr_u"sv) { return makeBinary(s, BinaryOp::ShrUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case '\0':
- if (op == "i32.store"sv) { return makeStore(s, Type::i32, 4, /*isAtomic=*/false); }
- goto parse_error;
- case '1':
- if (op == "i32.store16"sv) { return makeStore(s, Type::i32, 2, /*isAtomic=*/false); }
- goto parse_error;
- case '8':
- if (op == "i32.store8"sv) { return makeStore(s, Type::i32, 1, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "i32.sub"sv) { return makeBinary(s, BinaryOp::SubInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[10]) {
- case 'f': {
- switch (buf[11]) {
- case '3': {
- switch (buf[14]) {
- case 's':
- if (op == "i32.trunc_f32_s"sv) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.trunc_f32_u"sv) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[14]) {
- case 's':
- if (op == "i32.trunc_f64_s"sv) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.trunc_f64_u"sv) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[15]) {
- case '3': {
- switch (buf[18]) {
- case 's':
- if (op == "i32.trunc_sat_f32_s"sv) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.trunc_sat_f32_u"sv) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[18]) {
- case 's':
- if (op == "i32.trunc_sat_f64_s"sv) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32); }
- goto parse_error;
- case 'u':
- if (op == "i32.trunc_sat_f64_u"sv) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'w':
- if (op == "i32.wrap_i64"sv) { return makeUnary(s, UnaryOp::WrapInt64); }
- goto parse_error;
- case 'x':
- if (op == "i32.xor"sv) { return makeBinary(s, BinaryOp::XorInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "i32x4.abs"sv) { return makeUnary(s, UnaryOp::AbsVecI32x4); }
- goto parse_error;
- case 'd':
- if (op == "i32x4.add"sv) { return makeBinary(s, BinaryOp::AddVecI32x4); }
- goto parse_error;
- case 'l':
- if (op == "i32x4.all_true"sv) { return makeUnary(s, UnaryOp::AllTrueVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b':
- if (op == "i32x4.bitmask"sv) { return makeUnary(s, UnaryOp::BitmaskVecI32x4); }
- goto parse_error;
- case 'd': {
- switch (buf[11]) {
- case '1':
- if (op == "i32x4.dot_i16x8_s"sv) { return makeBinary(s, BinaryOp::DotSVecI16x8ToVecI32x4); }
- goto parse_error;
- case '8':
- if (op == "i32x4.dot_i8x16_i7x16_add_s"sv) { return makeSIMDTernary(s, SIMDTernaryOp::DotI8x16I7x16AddSToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "i32x4.eq"sv) { return makeBinary(s, BinaryOp::EqVecI32x4); }
- goto parse_error;
- case 'x': {
- switch (buf[9]) {
- case 'a': {
- switch (buf[28]) {
- case 's':
- if (op == "i32x4.extadd_pairwise_i16x8_s"sv) { return makeUnary(s, UnaryOp::ExtAddPairwiseSVecI16x8ToI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.extadd_pairwise_i16x8_u"sv) { return makeUnary(s, UnaryOp::ExtAddPairwiseUVecI16x8ToI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i32x4.extend_high_i16x8_s"sv) { return makeUnary(s, UnaryOp::ExtendHighSVecI16x8ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.extend_high_i16x8_u"sv) { return makeUnary(s, UnaryOp::ExtendHighUVecI16x8ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i32x4.extend_low_i16x8_s"sv) { return makeUnary(s, UnaryOp::ExtendLowSVecI16x8ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.extend_low_i16x8_u"sv) { return makeUnary(s, UnaryOp::ExtendLowUVecI16x8ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i32x4.extmul_high_i16x8_s"sv) { return makeBinary(s, BinaryOp::ExtMulHighSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.extmul_high_i16x8_u"sv) { return makeBinary(s, BinaryOp::ExtMulHighUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i32x4.extmul_low_i16x8_s"sv) { return makeBinary(s, BinaryOp::ExtMulLowSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.extmul_low_i16x8_u"sv) { return makeBinary(s, BinaryOp::ExtMulLowUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'r':
- if (op == "i32x4.extract_lane"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI32x4, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[7]) {
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i32x4.ge_s"sv) { return makeBinary(s, BinaryOp::GeSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.ge_u"sv) { return makeBinary(s, BinaryOp::GeUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i32x4.gt_s"sv) { return makeBinary(s, BinaryOp::GtSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.gt_u"sv) { return makeBinary(s, BinaryOp::GtUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'a':
- if (op == "i32x4.laneselect"sv) { return makeSIMDTernary(s, SIMDTernaryOp::LaneselectI32x4); }
- goto parse_error;
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i32x4.le_s"sv) { return makeBinary(s, BinaryOp::LeSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.le_u"sv) { return makeBinary(s, BinaryOp::LeUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i32x4.lt_s"sv) { return makeBinary(s, BinaryOp::LtSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.lt_u"sv) { return makeBinary(s, BinaryOp::LtUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[10]) {
- case 's':
- if (op == "i32x4.max_s"sv) { return makeBinary(s, BinaryOp::MaxSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.max_u"sv) { return makeBinary(s, BinaryOp::MaxUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'i': {
- switch (buf[10]) {
- case 's':
- if (op == "i32x4.min_s"sv) { return makeBinary(s, BinaryOp::MinSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.min_u"sv) { return makeBinary(s, BinaryOp::MinUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "i32x4.mul"sv) { return makeBinary(s, BinaryOp::MulVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[8]) {
- case '\0':
- if (op == "i32x4.ne"sv) { return makeBinary(s, BinaryOp::NeVecI32x4); }
- goto parse_error;
- case 'g':
- if (op == "i32x4.neg"sv) { return makeUnary(s, UnaryOp::NegVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[8]) {
- case 'l': {
- switch (buf[21]) {
- case '3': {
- switch (buf[26]) {
- case 's':
- if (op == "i32x4.relaxed_trunc_f32x4_s"sv) { return makeUnary(s, UnaryOp::RelaxedTruncSVecF32x4ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.relaxed_trunc_f32x4_u"sv) { return makeUnary(s, UnaryOp::RelaxedTruncUVecF32x4ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[26]) {
- case 's':
- if (op == "i32x4.relaxed_trunc_f64x2_s_zero"sv) { return makeUnary(s, UnaryOp::RelaxedTruncZeroSVecF64x2ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.relaxed_trunc_f64x2_u_zero"sv) { return makeUnary(s, UnaryOp::RelaxedTruncZeroUVecF64x2ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i32x4.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI32x4, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'h': {
- switch (buf[8]) {
- case 'l':
- if (op == "i32x4.shl"sv) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI32x4); }
- goto parse_error;
- case 'r': {
- switch (buf[10]) {
- case 's':
- if (op == "i32x4.shr_s"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.shr_u"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i32x4.splat"sv) { return makeUnary(s, UnaryOp::SplatVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.sub"sv) { return makeBinary(s, BinaryOp::SubVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[17]) {
- case '3': {
- switch (buf[22]) {
- case 's':
- if (op == "i32x4.trunc_sat_f32x4_s"sv) { return makeUnary(s, UnaryOp::TruncSatSVecF32x4ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.trunc_sat_f32x4_u"sv) { return makeUnary(s, UnaryOp::TruncSatUVecF32x4ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[22]) {
- case 's':
- if (op == "i32x4.trunc_sat_f64x2_s_zero"sv) { return makeUnary(s, UnaryOp::TruncSatZeroSVecF64x2ToVecI32x4); }
- goto parse_error;
- case 'u':
- if (op == "i32x4.trunc_sat_f64x2_u_zero"sv) { return makeUnary(s, UnaryOp::TruncSatZeroUVecF64x2ToVecI32x4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[3]) {
- case '.': {
- switch (buf[4]) {
- case 'a': {
- switch (buf[5]) {
- case 'd':
- if (op == "i64.add"sv) { return makeBinary(s, BinaryOp::AddInt64); }
- goto parse_error;
- case 'n':
- if (op == "i64.and"sv) { return makeBinary(s, BinaryOp::AndInt64); }
- goto parse_error;
- case 't': {
- switch (buf[11]) {
- case 'l': {
- switch (buf[15]) {
- case '\0':
- if (op == "i64.atomic.load"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 8, /*isAtomic=*/true); }
- goto parse_error;
- case '1':
- if (op == "i64.atomic.load16_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 2, /*isAtomic=*/true); }
- goto parse_error;
- case '3':
- if (op == "i64.atomic.load32_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 4, /*isAtomic=*/true); }
- goto parse_error;
- case '8':
- if (op == "i64.atomic.load8_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 1, /*isAtomic=*/true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[14]) {
- case '.': {
- switch (buf[15]) {
- case 'a': {
- switch (buf[16]) {
- case 'd':
- if (op == "i64.atomic.rmw.add"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i64, 8); }
- goto parse_error;
- case 'n':
- if (op == "i64.atomic.rmw.and"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i64, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i64.atomic.rmw.cmpxchg"sv) { return makeAtomicCmpxchg(s, Type::i64, 8); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw.or"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i64, 8); }
- goto parse_error;
- case 's':
- if (op == "i64.atomic.rmw.sub"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i64, 8); }
- goto parse_error;
- case 'x': {
- switch (buf[16]) {
- case 'c':
- if (op == "i64.atomic.rmw.xchg"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i64, 8); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw.xor"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i64, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '1': {
- switch (buf[17]) {
- case 'a': {
- switch (buf[18]) {
- case 'd':
- if (op == "i64.atomic.rmw16.add_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i64, 2); }
- goto parse_error;
- case 'n':
- if (op == "i64.atomic.rmw16.and_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i64, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i64.atomic.rmw16.cmpxchg_u"sv) { return makeAtomicCmpxchg(s, Type::i64, 2); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw16.or_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i64, 2); }
- goto parse_error;
- case 's':
- if (op == "i64.atomic.rmw16.sub_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i64, 2); }
- goto parse_error;
- case 'x': {
- switch (buf[18]) {
- case 'c':
- if (op == "i64.atomic.rmw16.xchg_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i64, 2); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw16.xor_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i64, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '3': {
- switch (buf[17]) {
- case 'a': {
- switch (buf[18]) {
- case 'd':
- if (op == "i64.atomic.rmw32.add_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i64, 4); }
- goto parse_error;
- case 'n':
- if (op == "i64.atomic.rmw32.and_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i64, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i64.atomic.rmw32.cmpxchg_u"sv) { return makeAtomicCmpxchg(s, Type::i64, 4); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw32.or_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i64, 4); }
- goto parse_error;
- case 's':
- if (op == "i64.atomic.rmw32.sub_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i64, 4); }
- goto parse_error;
- case 'x': {
- switch (buf[18]) {
- case 'c':
- if (op == "i64.atomic.rmw32.xchg_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i64, 4); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw32.xor_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i64, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[16]) {
- case 'a': {
- switch (buf[17]) {
- case 'd':
- if (op == "i64.atomic.rmw8.add_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAdd, Type::i64, 1); }
- goto parse_error;
- case 'n':
- if (op == "i64.atomic.rmw8.and_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWAnd, Type::i64, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "i64.atomic.rmw8.cmpxchg_u"sv) { return makeAtomicCmpxchg(s, Type::i64, 1); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw8.or_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWOr, Type::i64, 1); }
- goto parse_error;
- case 's':
- if (op == "i64.atomic.rmw8.sub_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWSub, Type::i64, 1); }
- goto parse_error;
- case 'x': {
- switch (buf[17]) {
- case 'c':
- if (op == "i64.atomic.rmw8.xchg_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXchg, Type::i64, 1); }
- goto parse_error;
- case 'o':
- if (op == "i64.atomic.rmw8.xor_u"sv) { return makeAtomicRMW(s, AtomicRMWOp::RMWXor, Type::i64, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[16]) {
- case '\0':
- if (op == "i64.atomic.store"sv) { return makeStore(s, Type::i64, 8, /*isAtomic=*/true); }
- goto parse_error;
- case '1':
- if (op == "i64.atomic.store16"sv) { return makeStore(s, Type::i64, 2, /*isAtomic=*/true); }
- goto parse_error;
- case '3':
- if (op == "i64.atomic.store32"sv) { return makeStore(s, Type::i64, 4, /*isAtomic=*/true); }
- goto parse_error;
- case '8':
- if (op == "i64.atomic.store8"sv) { return makeStore(s, Type::i64, 1, /*isAtomic=*/true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'c': {
- switch (buf[5]) {
- case 'l':
- if (op == "i64.clz"sv) { return makeUnary(s, UnaryOp::ClzInt64); }
- goto parse_error;
- case 'o':
- if (op == "i64.const"sv) { return makeConst(s, Type::i64); }
- goto parse_error;
- case 't':
- if (op == "i64.ctz"sv) { return makeUnary(s, UnaryOp::CtzInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'd': {
- switch (buf[8]) {
- case 's':
- if (op == "i64.div_s"sv) { return makeBinary(s, BinaryOp::DivSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.div_u"sv) { return makeBinary(s, BinaryOp::DivUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[5]) {
- case 'q': {
- switch (buf[6]) {
- case '\0':
- if (op == "i64.eq"sv) { return makeBinary(s, BinaryOp::EqInt64); }
- goto parse_error;
- case 'z':
- if (op == "i64.eqz"sv) { return makeUnary(s, UnaryOp::EqZInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[10]) {
- case '1':
- if (op == "i64.extend16_s"sv) { return makeUnary(s, UnaryOp::ExtendS16Int64); }
- goto parse_error;
- case '3':
- if (op == "i64.extend32_s"sv) { return makeUnary(s, UnaryOp::ExtendS32Int64); }
- goto parse_error;
- case '8':
- if (op == "i64.extend8_s"sv) { return makeUnary(s, UnaryOp::ExtendS8Int64); }
- goto parse_error;
- case '_': {
- switch (buf[15]) {
- case 's':
- if (op == "i64.extend_i32_s"sv) { return makeUnary(s, UnaryOp::ExtendSInt32); }
- goto parse_error;
- case 'u':
- if (op == "i64.extend_i32_u"sv) { return makeUnary(s, UnaryOp::ExtendUInt32); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[7]) {
- case 's':
- if (op == "i64.ge_s"sv) { return makeBinary(s, BinaryOp::GeSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.ge_u"sv) { return makeBinary(s, BinaryOp::GeUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[7]) {
- case 's':
- if (op == "i64.gt_s"sv) { return makeBinary(s, BinaryOp::GtSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.gt_u"sv) { return makeBinary(s, BinaryOp::GtUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[7]) {
- case 's':
- if (op == "i64.le_s"sv) { return makeBinary(s, BinaryOp::LeSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.le_u"sv) { return makeBinary(s, BinaryOp::LeUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'o': {
- switch (buf[8]) {
- case '\0':
- if (op == "i64.load"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 8, /*isAtomic=*/false); }
- goto parse_error;
- case '1': {
- switch (buf[11]) {
- case 's':
- if (op == "i64.load16_s"sv) { return makeLoad(s, Type::i64, /*signed=*/true, 2, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "i64.load16_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 2, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '3': {
- switch (buf[11]) {
- case 's':
- if (op == "i64.load32_s"sv) { return makeLoad(s, Type::i64, /*signed=*/true, 4, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "i64.load32_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 4, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[10]) {
- case 's':
- if (op == "i64.load8_s"sv) { return makeLoad(s, Type::i64, /*signed=*/true, 1, /*isAtomic=*/false); }
- goto parse_error;
- case 'u':
- if (op == "i64.load8_u"sv) { return makeLoad(s, Type::i64, /*signed=*/false, 1, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[7]) {
- case 's':
- if (op == "i64.lt_s"sv) { return makeBinary(s, BinaryOp::LtSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.lt_u"sv) { return makeBinary(s, BinaryOp::LtUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm':
- if (op == "i64.mul"sv) { return makeBinary(s, BinaryOp::MulInt64); }
- goto parse_error;
- case 'n':
- if (op == "i64.ne"sv) { return makeBinary(s, BinaryOp::NeInt64); }
- goto parse_error;
- case 'o':
- if (op == "i64.or"sv) { return makeBinary(s, BinaryOp::OrInt64); }
- goto parse_error;
- case 'p':
- if (op == "i64.popcnt"sv) { return makeUnary(s, UnaryOp::PopcntInt64); }
- goto parse_error;
- case 'r': {
- switch (buf[5]) {
- case 'e': {
- switch (buf[6]) {
- case 'i':
- if (op == "i64.reinterpret_f64"sv) { return makeUnary(s, UnaryOp::ReinterpretFloat64); }
- goto parse_error;
- case 'm': {
- switch (buf[8]) {
- case 's':
- if (op == "i64.rem_s"sv) { return makeBinary(s, BinaryOp::RemSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.rem_u"sv) { return makeBinary(s, BinaryOp::RemUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'o': {
- switch (buf[7]) {
- case 'l':
- if (op == "i64.rotl"sv) { return makeBinary(s, BinaryOp::RotLInt64); }
- goto parse_error;
- case 'r':
- if (op == "i64.rotr"sv) { return makeBinary(s, BinaryOp::RotRInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[5]) {
- case 'h': {
- switch (buf[6]) {
- case 'l':
- if (op == "i64.shl"sv) { return makeBinary(s, BinaryOp::ShlInt64); }
- goto parse_error;
- case 'r': {
- switch (buf[8]) {
- case 's':
- if (op == "i64.shr_s"sv) { return makeBinary(s, BinaryOp::ShrSInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.shr_u"sv) { return makeBinary(s, BinaryOp::ShrUInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case '\0':
- if (op == "i64.store"sv) { return makeStore(s, Type::i64, 8, /*isAtomic=*/false); }
- goto parse_error;
- case '1':
- if (op == "i64.store16"sv) { return makeStore(s, Type::i64, 2, /*isAtomic=*/false); }
- goto parse_error;
- case '3':
- if (op == "i64.store32"sv) { return makeStore(s, Type::i64, 4, /*isAtomic=*/false); }
- goto parse_error;
- case '8':
- if (op == "i64.store8"sv) { return makeStore(s, Type::i64, 1, /*isAtomic=*/false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "i64.sub"sv) { return makeBinary(s, BinaryOp::SubInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[10]) {
- case 'f': {
- switch (buf[11]) {
- case '3': {
- switch (buf[14]) {
- case 's':
- if (op == "i64.trunc_f32_s"sv) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.trunc_f32_u"sv) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[14]) {
- case 's':
- if (op == "i64.trunc_f64_s"sv) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.trunc_f64_u"sv) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[15]) {
- case '3': {
- switch (buf[18]) {
- case 's':
- if (op == "i64.trunc_sat_f32_s"sv) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.trunc_sat_f32_u"sv) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[18]) {
- case 's':
- if (op == "i64.trunc_sat_f64_s"sv) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt64); }
- goto parse_error;
- case 'u':
- if (op == "i64.trunc_sat_f64_u"sv) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'x':
- if (op == "i64.xor"sv) { return makeBinary(s, BinaryOp::XorInt64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "i64x2.abs"sv) { return makeUnary(s, UnaryOp::AbsVecI64x2); }
- goto parse_error;
- case 'd':
- if (op == "i64x2.add"sv) { return makeBinary(s, BinaryOp::AddVecI64x2); }
- goto parse_error;
- case 'l':
- if (op == "i64x2.all_true"sv) { return makeUnary(s, UnaryOp::AllTrueVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b':
- if (op == "i64x2.bitmask"sv) { return makeUnary(s, UnaryOp::BitmaskVecI64x2); }
- goto parse_error;
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "i64x2.eq"sv) { return makeBinary(s, BinaryOp::EqVecI64x2); }
- goto parse_error;
- case 'x': {
- switch (buf[9]) {
- case 'e': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i64x2.extend_high_i32x4_s"sv) { return makeUnary(s, UnaryOp::ExtendHighSVecI32x4ToVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.extend_high_i32x4_u"sv) { return makeUnary(s, UnaryOp::ExtendHighUVecI32x4ToVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i64x2.extend_low_i32x4_s"sv) { return makeUnary(s, UnaryOp::ExtendLowSVecI32x4ToVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.extend_low_i32x4_u"sv) { return makeUnary(s, UnaryOp::ExtendLowUVecI32x4ToVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[13]) {
- case 'h': {
- switch (buf[24]) {
- case 's':
- if (op == "i64x2.extmul_high_i32x4_s"sv) { return makeBinary(s, BinaryOp::ExtMulHighSVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.extmul_high_i32x4_u"sv) { return makeBinary(s, BinaryOp::ExtMulHighUVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[23]) {
- case 's':
- if (op == "i64x2.extmul_low_i32x4_s"sv) { return makeBinary(s, BinaryOp::ExtMulLowSVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.extmul_low_i32x4_u"sv) { return makeBinary(s, BinaryOp::ExtMulLowUVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'r':
- if (op == "i64x2.extract_lane"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI64x2, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[7]) {
- case 'e':
- if (op == "i64x2.ge_s"sv) { return makeBinary(s, BinaryOp::GeSVecI64x2); }
- goto parse_error;
- case 't':
- if (op == "i64x2.gt_s"sv) { return makeBinary(s, BinaryOp::GtSVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'a':
- if (op == "i64x2.laneselect"sv) { return makeSIMDTernary(s, SIMDTernaryOp::LaneselectI64x2); }
- goto parse_error;
- case 'e':
- if (op == "i64x2.le_s"sv) { return makeBinary(s, BinaryOp::LeSVecI64x2); }
- goto parse_error;
- case 't':
- if (op == "i64x2.lt_s"sv) { return makeBinary(s, BinaryOp::LtSVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm':
- if (op == "i64x2.mul"sv) { return makeBinary(s, BinaryOp::MulVecI64x2); }
- goto parse_error;
- case 'n': {
- switch (buf[8]) {
- case '\0':
- if (op == "i64x2.ne"sv) { return makeBinary(s, BinaryOp::NeVecI64x2); }
- goto parse_error;
- case 'g':
- if (op == "i64x2.neg"sv) { return makeUnary(s, UnaryOp::NegVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'r':
- if (op == "i64x2.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI64x2, 2); }
- goto parse_error;
- case 's': {
- switch (buf[7]) {
- case 'h': {
- switch (buf[8]) {
- case 'l':
- if (op == "i64x2.shl"sv) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI64x2); }
- goto parse_error;
- case 'r': {
- switch (buf[10]) {
- case 's':
- if (op == "i64x2.shr_s"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.shr_u"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i64x2.splat"sv) { return makeUnary(s, UnaryOp::SplatVecI64x2); }
- goto parse_error;
- case 'u':
- if (op == "i64x2.sub"sv) { return makeBinary(s, BinaryOp::SubVecI64x2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[6]) {
- case 'a': {
- switch (buf[7]) {
- case 'b':
- if (op == "i8x16.abs"sv) { return makeUnary(s, UnaryOp::AbsVecI8x16); }
- goto parse_error;
- case 'd': {
- switch (buf[9]) {
- case '\0':
- if (op == "i8x16.add"sv) { return makeBinary(s, BinaryOp::AddVecI8x16); }
- goto parse_error;
- case '_': {
- switch (buf[14]) {
- case 's':
- if (op == "i8x16.add_sat_s"sv) { return makeBinary(s, BinaryOp::AddSatSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.add_sat_u"sv) { return makeBinary(s, BinaryOp::AddSatUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l':
- if (op == "i8x16.all_true"sv) { return makeUnary(s, UnaryOp::AllTrueVecI8x16); }
- goto parse_error;
- case 'v':
- if (op == "i8x16.avgr_u"sv) { return makeBinary(s, BinaryOp::AvgrUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b':
- if (op == "i8x16.bitmask"sv) { return makeUnary(s, UnaryOp::BitmaskVecI8x16); }
- goto parse_error;
- case 'e': {
- switch (buf[7]) {
- case 'q':
- if (op == "i8x16.eq"sv) { return makeBinary(s, BinaryOp::EqVecI8x16); }
- goto parse_error;
- case 'x': {
- switch (buf[19]) {
- case 's':
- if (op == "i8x16.extract_lane_s"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI8x16, 16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.extract_lane_u"sv) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI8x16, 16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'g': {
- switch (buf[7]) {
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i8x16.ge_s"sv) { return makeBinary(s, BinaryOp::GeSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.ge_u"sv) { return makeBinary(s, BinaryOp::GeUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i8x16.gt_s"sv) { return makeBinary(s, BinaryOp::GtSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.gt_u"sv) { return makeBinary(s, BinaryOp::GtUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[7]) {
- case 'a':
- if (op == "i8x16.laneselect"sv) { return makeSIMDTernary(s, SIMDTernaryOp::LaneselectI8x16); }
- goto parse_error;
- case 'e': {
- switch (buf[9]) {
- case 's':
- if (op == "i8x16.le_s"sv) { return makeBinary(s, BinaryOp::LeSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.le_u"sv) { return makeBinary(s, BinaryOp::LeUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[9]) {
- case 's':
- if (op == "i8x16.lt_s"sv) { return makeBinary(s, BinaryOp::LtSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.lt_u"sv) { return makeBinary(s, BinaryOp::LtUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[10]) {
- case 's':
- if (op == "i8x16.max_s"sv) { return makeBinary(s, BinaryOp::MaxSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.max_u"sv) { return makeBinary(s, BinaryOp::MaxUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'i': {
- switch (buf[10]) {
- case 's':
- if (op == "i8x16.min_s"sv) { return makeBinary(s, BinaryOp::MinSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.min_u"sv) { return makeBinary(s, BinaryOp::MinUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[19]) {
- case 's':
- if (op == "i8x16.narrow_i16x8_s"sv) { return makeBinary(s, BinaryOp::NarrowSVecI16x8ToVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.narrow_i16x8_u"sv) { return makeBinary(s, BinaryOp::NarrowUVecI16x8ToVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[8]) {
- case '\0':
- if (op == "i8x16.ne"sv) { return makeBinary(s, BinaryOp::NeVecI8x16); }
- goto parse_error;
- case 'g':
- if (op == "i8x16.neg"sv) { return makeUnary(s, UnaryOp::NegVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i8x16.popcnt"sv) { return makeUnary(s, UnaryOp::PopcntVecI8x16); }
- goto parse_error;
- case 'r': {
- switch (buf[8]) {
- case 'l':
- if (op == "i8x16.relaxed_swizzle"sv) { return makeBinary(s, BinaryOp::RelaxedSwizzleVecI8x16); }
- goto parse_error;
- case 'p':
- if (op == "i8x16.replace_lane"sv) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI8x16, 16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'h': {
- switch (buf[8]) {
- case 'l':
- if (op == "i8x16.shl"sv) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI8x16); }
- goto parse_error;
- case 'r': {
- switch (buf[10]) {
- case 's':
- if (op == "i8x16.shr_s"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.shr_u"sv) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "i8x16.shuffle"sv) { return makeSIMDShuffle(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'p':
- if (op == "i8x16.splat"sv) { return makeUnary(s, UnaryOp::SplatVecI8x16); }
- goto parse_error;
- case 'u': {
- switch (buf[9]) {
- case '\0':
- if (op == "i8x16.sub"sv) { return makeBinary(s, BinaryOp::SubVecI8x16); }
- goto parse_error;
- case '_': {
- switch (buf[14]) {
- case 's':
- if (op == "i8x16.sub_sat_s"sv) { return makeBinary(s, BinaryOp::SubSatSVecI8x16); }
- goto parse_error;
- case 'u':
- if (op == "i8x16.sub_sat_u"sv) { return makeBinary(s, BinaryOp::SubSatUVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'w':
- if (op == "i8x16.swizzle"sv) { return makeBinary(s, BinaryOp::SwizzleVecI8x16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'f':
- if (op == "if"sv) { return makeIf(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'l': {
- switch (buf[2]) {
- case 'c': {
- switch (buf[6]) {
- case 'g':
- if (op == "local.get"sv) { return makeLocalGet(s); }
- goto parse_error;
- case 's':
- if (op == "local.set"sv) { return makeLocalSet(s); }
- goto parse_error;
- case 't':
- if (op == "local.tee"sv) { return makeLocalTee(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'o':
- if (op == "loop"sv) { return makeLoop(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'm': {
- switch (buf[7]) {
- case 'a': {
- switch (buf[14]) {
- case 'n':
- if (op == "memory.atomic.notify"sv) { return makeAtomicNotify(s); }
- goto parse_error;
- case 'w': {
- switch (buf[18]) {
- case '3':
- if (op == "memory.atomic.wait32"sv) { return makeAtomicWait(s, Type::i32); }
- goto parse_error;
- case '6':
- if (op == "memory.atomic.wait64"sv) { return makeAtomicWait(s, Type::i64); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'c':
- if (op == "memory.copy"sv) { return makeMemoryCopy(s); }
- goto parse_error;
- case 'f':
- if (op == "memory.fill"sv) { return makeMemoryFill(s); }
- goto parse_error;
- case 'g':
- if (op == "memory.grow"sv) { return makeMemoryGrow(s); }
- goto parse_error;
- case 'i':
- if (op == "memory.init"sv) { return makeMemoryInit(s); }
- goto parse_error;
- case 's':
- if (op == "memory.size"sv) { return makeMemorySize(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n':
- if (op == "nop"sv) { return makeNop(); }
- goto parse_error;
- case 'p':
- if (op == "pop"sv) { return makePop(s); }
- goto parse_error;
- case 'r': {
- switch (buf[2]) {
- case 'f': {
- switch (buf[4]) {
- case 'a':
- if (op == "ref.as_non_null"sv) { return makeRefAs(s, RefAsNonNull); }
- goto parse_error;
- case 'c':
- if (op == "ref.cast"sv) { return makeRefCast(s); }
- goto parse_error;
- case 'e':
- if (op == "ref.eq"sv) { return makeRefEq(s); }
- goto parse_error;
- case 'f':
- if (op == "ref.func"sv) { return makeRefFunc(s); }
- goto parse_error;
- case 'i': {
- switch (buf[5]) {
- case '3':
- if (op == "ref.i31"sv) { return makeRefI31(s); }
- goto parse_error;
- case 's':
- if (op == "ref.is_null"sv) { return makeRefIsNull(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n':
- if (op == "ref.null"sv) { return makeRefNull(s); }
- goto parse_error;
- case 't':
- if (op == "ref.test"sv) { return makeRefTest(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's':
- if (op == "resume"sv) { return makeResume(s); }
- goto parse_error;
- case 't': {
- switch (buf[3]) {
- case 'h':
- if (op == "rethrow"sv) { return makeRethrow(s); }
- goto parse_error;
- case 'u': {
- switch (buf[6]) {
- case '\0':
- if (op == "return"sv) { return makeReturn(s); }
- goto parse_error;
- case '_': {
- switch (buf[11]) {
- case '\0':
- if (op == "return_call"sv) { return makeCall(s, /*isReturn=*/true); }
- goto parse_error;
- case '_': {
- switch (buf[12]) {
- case 'i':
- if (op == "return_call_indirect"sv) { return makeCallIndirect(s, /*isReturn=*/true); }
- goto parse_error;
- case 'r':
- if (op == "return_call_ref"sv) { return makeCallRef(s, /*isReturn=*/true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[1]) {
- case 'e':
- if (op == "select"sv) { return makeSelect(s); }
- goto parse_error;
- case 't': {
- switch (buf[3]) {
- case 'i': {
- switch (buf[6]) {
- case '.': {
- switch (buf[7]) {
- case 'a':
- if (op == "string.as_wtf16"sv) { return ignore(s); }
- goto parse_error;
- case 'c': {
- switch (buf[9]) {
- case 'm':
- if (op == "string.compare"sv) { return makeStringEq(s, StringEqCompare); }
- goto parse_error;
- case 'n': {
- switch (buf[10]) {
- case 'c':
- if (op == "string.concat"sv) { return makeStringConcat(s); }
- goto parse_error;
- case 's':
- if (op == "string.const"sv) { return makeStringConst(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'e': {
- switch (buf[8]) {
- case 'n': {
- switch (buf[14]) {
- case 'l':
- if (op == "string.encode_lossy_utf8_array"sv) { return makeStringEncode(s, StringEncodeLossyUTF8Array); }
- goto parse_error;
- case 'w':
- if (op == "string.encode_wtf16_array"sv) { return makeStringEncode(s, StringEncodeWTF16Array); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'q':
- if (op == "string.eq"sv) { return makeStringEq(s, StringEqEqual); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'f':
- if (op == "string.from_code_point"sv) { return makeStringNew(s, StringNewFromCodePoint); }
- goto parse_error;
- case 'm': {
- switch (buf[15]) {
- case 'u':
- if (op == "string.measure_utf8"sv) { return makeStringMeasure(s, StringMeasureUTF8); }
- goto parse_error;
- case 'w':
- if (op == "string.measure_wtf16"sv) { return makeStringMeasure(s, StringMeasureWTF16); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[11]) {
- case 'l':
- if (op == "string.new_lossy_utf8_array"sv) { return makeStringNew(s, StringNewLossyUTF8Array); }
- goto parse_error;
- case 'w':
- if (op == "string.new_wtf16_array"sv) { return makeStringNew(s, StringNewWTF16Array); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'v': {
- switch (buf[17]) {
- case 'g':
- if (op == "stringview_wtf16.get_codeunit"sv) { return makeStringWTF16Get(s); }
- goto parse_error;
- case 'l':
- if (op == "stringview_wtf16.length"sv) { return makeStringMeasure(s, StringMeasureWTF16); }
- goto parse_error;
- case 's':
- if (op == "stringview_wtf16.slice"sv) { return makeStringSliceWTF(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'u': {
- switch (buf[7]) {
- case 'g': {
- switch (buf[10]) {
- case '\0':
- if (op == "struct.get"sv) { return makeStructGet(s); }
- goto parse_error;
- case '_': {
- switch (buf[11]) {
- case 's':
- if (op == "struct.get_s"sv) { return makeStructGet(s, true); }
- goto parse_error;
- case 'u':
- if (op == "struct.get_u"sv) { return makeStructGet(s, false); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'n': {
- switch (buf[10]) {
- case '\0':
- if (op == "struct.new"sv) { return makeStructNew(s, false); }
- goto parse_error;
- case '_':
- if (op == "struct.new_default"sv) { return makeStructNew(s, true); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's':
- if (op == "struct.set"sv) { return makeStructSet(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "suspend"sv) { return makeSuspend(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 't': {
- switch (buf[1]) {
- case 'a': {
- switch (buf[6]) {
- case 'c':
- if (op == "table.copy"sv) { return makeTableCopy(s); }
- goto parse_error;
- case 'f':
- if (op == "table.fill"sv) { return makeTableFill(s); }
- goto parse_error;
- case 'g': {
- switch (buf[7]) {
- case 'e':
- if (op == "table.get"sv) { return makeTableGet(s); }
- goto parse_error;
- case 'r':
- if (op == "table.grow"sv) { return makeTableGrow(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 's': {
- switch (buf[7]) {
- case 'e':
- if (op == "table.set"sv) { return makeTableSet(s); }
- goto parse_error;
- case 'i':
- if (op == "table.size"sv) { return makeTableSize(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'h': {
- switch (buf[2]) {
- case 'e':
- if (op == "then"sv) { return makeThenOrElse(s); }
- goto parse_error;
- case 'r': {
- switch (buf[5]) {
- case '\0':
- if (op == "throw"sv) { return makeThrow(s); }
- goto parse_error;
- case '_':
- if (op == "throw_ref"sv) { return makeThrowRef(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'r': {
- switch (buf[3]) {
- case '\0':
- if (op == "try"sv) { return makeTry(s); }
- goto parse_error;
- case '_':
- if (op == "try_table"sv) { return makeTryTable(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'u': {
- switch (buf[6]) {
- case 'd':
- if (op == "tuple.drop"sv) { return makeTupleDrop(s); }
- goto parse_error;
- case 'e':
- if (op == "tuple.extract"sv) { return makeTupleExtract(s); }
- goto parse_error;
- case 'm':
- if (op == "tuple.make"sv) { return makeTupleMake(s); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'u':
- if (op == "unreachable"sv) { return makeUnreachable(); }
- goto parse_error;
- case 'v': {
- switch (buf[5]) {
- case 'a': {
- switch (buf[7]) {
- case 'd': {
- switch (buf[8]) {
- case '\0':
- if (op == "v128.and"sv) { return makeBinary(s, BinaryOp::AndVec128); }
- goto parse_error;
- case 'n':
- if (op == "v128.andnot"sv) { return makeBinary(s, BinaryOp::AndNotVec128); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'y':
- if (op == "v128.any_true"sv) { return makeUnary(s, UnaryOp::AnyTrueVec128); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'b':
- if (op == "v128.bitselect"sv) { return makeSIMDTernary(s, SIMDTernaryOp::Bitselect); }
- goto parse_error;
- case 'c':
- if (op == "v128.const"sv) { return makeConst(s, Type::v128); }
- goto parse_error;
- case 'l': {
- switch (buf[9]) {
- case '\0':
- if (op == "v128.load"sv) { return makeLoad(s, Type::v128, /*signed=*/false, 16, /*isAtomic=*/false); }
- goto parse_error;
- case '1': {
- switch (buf[11]) {
- case '_': {
- switch (buf[12]) {
- case 'l':
- if (op == "v128.load16_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Load16LaneVec128, 2); }
- goto parse_error;
- case 's':
- if (op == "v128.load16_splat"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load16SplatVec128, 2); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[14]) {
- case 's':
- if (op == "v128.load16x4_s"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load16x4SVec128, 8); }
- goto parse_error;
- case 'u':
- if (op == "v128.load16x4_u"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load16x4UVec128, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '3': {
- switch (buf[11]) {
- case '_': {
- switch (buf[12]) {
- case 'l':
- if (op == "v128.load32_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Load32LaneVec128, 4); }
- goto parse_error;
- case 's':
- if (op == "v128.load32_splat"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load32SplatVec128, 4); }
- goto parse_error;
- case 'z':
- if (op == "v128.load32_zero"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load32ZeroVec128, 4); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[14]) {
- case 's':
- if (op == "v128.load32x2_s"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load32x2SVec128, 8); }
- goto parse_error;
- case 'u':
- if (op == "v128.load32x2_u"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load32x2UVec128, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case '6': {
- switch (buf[12]) {
- case 'l':
- if (op == "v128.load64_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Load64LaneVec128, 8); }
- goto parse_error;
- case 's':
- if (op == "v128.load64_splat"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load64SplatVec128, 8); }
- goto parse_error;
- case 'z':
- if (op == "v128.load64_zero"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load64ZeroVec128, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case '8': {
- switch (buf[10]) {
- case '_': {
- switch (buf[11]) {
- case 'l':
- if (op == "v128.load8_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Load8LaneVec128, 1); }
- goto parse_error;
- case 's':
- if (op == "v128.load8_splat"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load8SplatVec128, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x': {
- switch (buf[13]) {
- case 's':
- if (op == "v128.load8x8_s"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load8x8SVec128, 8); }
- goto parse_error;
- case 'u':
- if (op == "v128.load8x8_u"sv) { return makeSIMDLoad(s, SIMDLoadOp::Load8x8UVec128, 8); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- default: goto parse_error;
- }
- }
- case 'n':
- if (op == "v128.not"sv) { return makeUnary(s, UnaryOp::NotVec128); }
- goto parse_error;
- case 'o':
- if (op == "v128.or"sv) { return makeBinary(s, BinaryOp::OrVec128); }
- goto parse_error;
- case 's': {
- switch (buf[10]) {
- case '\0':
- if (op == "v128.store"sv) { return makeStore(s, Type::v128, 16, /*isAtomic=*/false); }
- goto parse_error;
- case '1':
- if (op == "v128.store16_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Store16LaneVec128, 2); }
- goto parse_error;
- case '3':
- if (op == "v128.store32_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Store32LaneVec128, 4); }
- goto parse_error;
- case '6':
- if (op == "v128.store64_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Store64LaneVec128, 8); }
- goto parse_error;
- case '8':
- if (op == "v128.store8_lane"sv) { return makeSIMDLoadStoreLane(s, SIMDLoadStoreLaneOp::Store8LaneVec128, 1); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- case 'x':
- if (op == "v128.xor"sv) { return makeBinary(s, BinaryOp::XorVec128); }
- goto parse_error;
- default: goto parse_error;
- }
- }
- default: goto parse_error;
-}
-parse_error:
- throw ParseException(std::string(op), s.line, s.col);
-#endif // INSTRUCTION_PARSER
-
-#ifdef NEW_INSTRUCTION_PARSER
-#undef NEW_INSTRUCTION_PARSER
auto op = *keyword;
char buf[33] = {};
memcpy(buf, op.data(), op.size());
@@ -8611,6 +5150,5 @@ switch (buf[0]) {
}
parse_error:
return ctx.in.err(pos, "unrecognized instruction");
-#endif // NEW_INSTRUCTION_PARSER
// clang-format on
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 5900deb27..ab990e7b0 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -918,8 +918,6 @@ MaybeResult<> plaininstr(Ctx& ctx, const std::vector<Annotation>& annotations) {
return {};
}
-#define NEW_INSTRUCTION_PARSER
-#define NEW_WAT_PARSER
#include <gen-s-parser.inc>
}
diff --git a/src/passes/RemoveNonJSOps.cpp b/src/passes/RemoveNonJSOps.cpp
index dc8aecfe0..90116f8f3 100644
--- a/src/passes/RemoveNonJSOps.cpp
+++ b/src/passes/RemoveNonJSOps.cpp
@@ -46,7 +46,6 @@
#include "passes/intrinsics-module.h"
#include "support/insert_ordered.h"
#include "wasm-builder.h"
-#include "wasm-s-parser.h"
namespace wasm {
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index 73908afb7..d488171fb 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -34,7 +34,6 @@
#include "wasm-binary.h"
#include "wasm-interpreter.h"
#include "wasm-io.h"
-#include "wasm-s-parser.h"
#include "wasm-stack.h"
#include "wasm-validator.h"
#include "wasm2c-wrapper.h"
@@ -221,19 +220,6 @@ int main(int argc, const char* argv[]) {
[&outputSourceMapUrl](Options* o, const std::string& argument) {
outputSourceMapUrl = argument;
})
- .add(
- "--deprecated-wat-parser",
- "",
- "Use the old, deprecated WAT parser. This option will be removed soon!",
- WasmOptOption,
- Options::Arguments::Zero,
- [](Options*, const std::string&) { useNewWATParser = false; })
- .add("--new-wat-parser",
- "",
- "Use the experimental new WAT parser",
- WasmOptOption,
- Options::Arguments::Zero,
- [](Options*, const std::string&) { useNewWATParser = true; })
.add_positional("INFILE",
Options::Arguments::One,
[](Options* o, const std::string& argument) {
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index d334d3380..c5de531f5 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -32,7 +32,6 @@
#include "support/result.h"
#include "wasm-binary.h"
#include "wasm-interpreter.h"
-#include "wasm-s-parser.h"
#include "wasm-validator.h"
using namespace wasm;
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index a6de1fabd..feba358c4 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -25,7 +25,6 @@
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
-#include "wasm-s-parser.h"
using namespace cashew;
using namespace wasm;
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
deleted file mode 100644
index fe2bda1c7..000000000
--- a/src/wasm-s-parser.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-//
-// Parses WebAssembly code in S-Expression format, as in .wast files
-// such as are in the spec test suite.
-//
-
-#ifndef wasm_wasm_s_parser_h
-#define wasm_wasm_s_parser_h
-
-#include "mixed_arena.h"
-#include "parsing.h" // for UniqueNameMapper. TODO: move dependency to cpp file?
-#include "wasm-builder.h"
-#include "wasm.h"
-
-namespace wasm {
-
-class SourceLocation {
-public:
- IString filename;
- uint32_t line;
- uint32_t column;
- SourceLocation(IString filename_, uint32_t line_, uint32_t column_ = 0)
- : filename(filename_), line(line_), column(column_) {}
-};
-
-//
-// An element in an S-Expression: a list or a string
-//
-class Element {
- using List = ArenaVector<Element*>;
-
- bool isList_ = true;
- List list_;
- IString str_;
- bool dollared_;
- bool quoted_;
-
-public:
- Element(MixedArena& allocator) : list_(allocator) {}
-
- bool isList() const { return isList_; }
- bool isStr() const { return !isList_; }
- bool dollared() const { return isStr() && dollared_; }
- bool quoted() const { return isStr() && quoted_; }
-
- size_t line = -1;
- size_t col = -1;
- // original locations at the start/end of the S-Expression list
- SourceLocation* startLoc = nullptr;
- SourceLocation* endLoc = nullptr;
-
- // list methods
- List& list();
- Element* operator[](unsigned i);
- size_t size() { return list().size(); }
- List::Iterator begin() { return list().begin(); }
- List::Iterator end() { return list().end(); }
-
- // string methods
- IString str() const;
- // convert a string to a string
- std::string toString() const;
- // convert anything to a string
- std::string forceString() const;
- Element* setString(IString str__, bool dollared__, bool quoted__);
- Element* setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_);
-
- // comparisons
- bool operator==(Name name) { return isStr() && str() == name; }
-
- template<typename T> bool operator!=(T t) { return !(*this == t); }
-
- // printing
- friend std::ostream& operator<<(std::ostream& o, const Element& e);
- void dump();
-};
-
-//
-// Generic S-Expression parsing into lists
-//
-class SExpressionParser {
- const char* input;
- size_t line;
- char const* lineStart;
- SourceLocation* loc = nullptr;
-
- MixedArena allocator;
-
-public:
- // Assumes control of and modifies the input.
- SExpressionParser(const char* input);
- Element* root;
-
-private:
- Element* parse();
- void skipWhitespace();
- void parseDebugLocation();
- Element* parseString();
-};
-
-//
-// SExpressions => WebAssembly module
-//
-class SExpressionWasmBuilder {
- Module& wasm;
- MixedArena& allocator;
- IRProfile profile;
-
- // The main list of types declared in the module
- std::vector<HeapType> types;
- std::unordered_map<std::string, size_t> typeIndices;
-
- std::vector<Name> functionNames;
- std::vector<Name> tableNames;
- std::vector<Name> elemSegmentNames;
- std::vector<Name> memoryNames;
- std::vector<Name> dataSegmentNames;
- std::vector<Name> globalNames;
- std::vector<Name> tagNames;
- int functionCounter = 0;
- int globalCounter = 0;
- int tagCounter = 0;
- int tableCounter = 0;
- int elemCounter = 0;
- int memoryCounter = 0;
- int dataCounter = 0;
- // we need to know function return types before we parse their contents
- std::map<Name, HeapType> functionTypes;
- std::unordered_map<IString, Index> debugInfoFileIndices;
-
- // Maps type indexes to a mapping of field index => name. This is not the same
- // as the field names stored on the wasm object, as that maps types after
- // their canonicalization. Canonicalization loses information, which means
- // that structurally identical types cannot have different names. However,
- // while parsing the text format we keep this mapping of type indexes to names
- // which does allow reading such content.
- std::unordered_map<size_t, std::unordered_map<Index, Name>> fieldNames;
-
-public:
- // Assumes control of and modifies the input.
- SExpressionWasmBuilder(Module& wasm, Element& module, IRProfile profile);
-
-private:
- void preParseHeapTypes(Element& module);
- // pre-parse types and function definitions, so we know function return types
- // before parsing their contents
- void preParseFunctionType(Element& s);
- bool isImport(Element& curr);
- void preParseImports(Element& curr);
- void preParseMemory(Element& curr);
- void parseModuleElement(Element& curr);
-
- // function parsing state
- std::unique_ptr<Function> currFunction;
- bool brokeToAutoBlock;
-
- UniqueNameMapper nameMapper;
-
- int parseIndex(Element& s);
-
- Name getFunctionName(Element& s);
- Name getTableName(Element& s);
- Name getElemSegmentName(Element& s);
- Name getMemoryName(Element& s);
- Name getDataSegmentName(Element& s);
- Name getGlobalName(Element& s);
- Name getTagName(Element& s);
- void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1])); }
-
- Name getMemoryNameAtIdx(Index i);
- bool isMemory64(Name memoryName);
- bool hasMemoryIdx(Element& s, Index defaultSize, Index i);
-
- // returns the next index in s
- size_t parseFunctionNames(Element& s, Name& name, Name& exportName);
- void parseFunction(Element& s, bool preParseImport = false);
-
- Type stringToType(IString str, bool allowError = false, bool prefix = false) {
- return stringToType(str.str, allowError, prefix);
- }
- Type stringToType(std::string_view str,
- bool allowError = false,
- bool prefix = false);
- HeapType stringToHeapType(IString str, bool prefix = false) {
- return stringToHeapType(str.str, prefix);
- }
- HeapType stringToHeapType(std::string_view str, bool prefix = false);
- Type elementToType(Element& s);
- // TODO: Use std::string_view for this and similar functions.
- Type stringToLaneType(const char* str);
- bool isType(IString str) { return stringToType(str, true) != Type::none; }
- HeapType getFunctionType(Name name, Element& s);
-
-public:
- Expression* parseExpression(Element* s) { return parseExpression(*s); }
- Expression* parseExpression(Element& s);
-
- Module& getModule() { return wasm; }
-
-private:
- Expression* makeExpression(Element& s);
- Expression* makeUnreachable();
- Expression* makeNop();
- Expression* makeBinary(Element& s, BinaryOp op);
- Expression* makeUnary(Element& s, UnaryOp op);
- Expression* makeSelect(Element& s);
- Expression* makeDrop(Element& s);
- Expression* makeMemorySize(Element& s);
- Expression* makeMemoryGrow(Element& s);
- Index getLocalIndex(Element& s);
- Expression* makeLocalGet(Element& s);
- Expression* makeLocalTee(Element& s);
- Expression* makeLocalSet(Element& s);
- Expression* makeGlobalGet(Element& s);
- Expression* makeGlobalSet(Element& s);
- Expression* makeBlock(Element& s);
- Expression* makeThenOrElse(Element& s);
- Expression* makeConst(Element& s, Type type);
- Expression*
- makeLoad(Element& s, Type type, bool signed_, int bytes, bool isAtomic);
- Expression* makeStore(Element& s, Type type, int bytes, bool isAtomic);
- Expression*
- makeAtomicRMW(Element& s, AtomicRMWOp op, Type type, uint8_t bytes);
- Expression* makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes);
- Expression* makeAtomicWait(Element& s, Type type);
- Expression* makeAtomicNotify(Element& s);
- Expression* makeAtomicFence(Element& s);
- Expression* makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes);
- Expression* makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes);
- Expression* makeSIMDShuffle(Element& s);
- Expression* makeSIMDTernary(Element& s, SIMDTernaryOp op);
- Expression* makeSIMDShift(Element& s, SIMDShiftOp op);
- Expression* makeSIMDLoad(Element& s, SIMDLoadOp op, int bytes);
- Expression*
- makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op, int bytes);
- Expression* makeMemoryInit(Element& s);
- Expression* makeDataDrop(Element& s);
- Expression* makeMemoryCopy(Element& s);
- Expression* makeMemoryFill(Element& s);
- Expression* makePop(Element& s);
- Expression* makeIf(Element& s);
- Expression* makeMaybeBlock(Element& s, size_t i, Type type);
- Expression* makeLoop(Element& s);
- Expression* makeCall(Element& s, bool isReturn);
- Expression* makeCallIndirect(Element& s, bool isReturn);
- template<class T> void parseOperands(Element& s, Index i, Index j, T& list) {
- while (i < j) {
- list.push_back(parseExpression(s[i]));
- i++;
- }
- }
- template<class T>
- void parseCallOperands(Element& s, Index i, Index j, T* call) {
- parseOperands(s, i, j, call->operands);
- }
- enum class LabelType { Break, Exception };
- Name getLabel(Element& s, LabelType labelType = LabelType::Break);
- Expression* makeBreak(Element& s, bool isConditional);
- Expression* makeBreakTable(Element& s);
- Expression* makeReturn(Element& s);
- Expression* makeRefNull(Element& s);
- Expression* makeRefIsNull(Element& s);
- Expression* makeRefFunc(Element& s);
- Expression* makeRefEq(Element& s);
- Expression* makeTableGet(Element& s);
- Expression* makeTableSet(Element& s);
- Expression* makeTableSize(Element& s);
- Expression* makeTableGrow(Element& s);
- Expression* makeTableFill(Element& s);
- Expression* makeTableCopy(Element& s);
- Expression* makeTry(Element& s);
- Expression* makeTryTable(Element& s);
- Expression* makeThrow(Element& s);
- Expression* makeRethrow(Element& s);
- Expression* makeThrowRef(Element& s);
- Expression* makeTupleMake(Element& s);
- Expression* makeTupleExtract(Element& s);
- Expression* makeTupleDrop(Element& s);
- Expression* makeCallRef(Element& s, bool isReturn);
- Expression* makeRefI31(Element& s);
- Expression* makeI31Get(Element& s, bool signed_);
- Expression* makeRefTest(Element& s);
- Expression* makeRefCast(Element& s);
- Expression* makeBrOnNull(Element& s, bool onFail = false);
- Expression* makeBrOnCast(Element& s, bool onFail = false);
- Expression* makeStructNew(Element& s, bool default_);
- Index getStructIndex(Element& type, Element& field);
- Expression* makeStructGet(Element& s, bool signed_ = false);
- Expression* makeStructSet(Element& s);
- Expression* makeArrayNew(Element& s, bool default_);
- Expression* makeArrayNewData(Element& s);
- Expression* makeArrayNewElem(Element& s);
- Expression* makeArrayNewFixed(Element& s);
- Expression* makeArrayGet(Element& s, bool signed_ = false);
- Expression* makeArraySet(Element& s);
- Expression* makeArrayLen(Element& s);
- Expression* makeArrayCopy(Element& s);
- Expression* makeArrayFill(Element& s);
- Expression* makeArrayInitData(Element& s);
- Expression* makeArrayInitElem(Element& s);
- Expression* makeRefAs(Element& s, RefAsOp op);
- Expression* makeRefAsNonNull(Element& s);
- Expression* makeStringNew(Element& s, StringNewOp op);
- Expression* makeStringConst(Element& s);
- Expression* makeStringMeasure(Element& s, StringMeasureOp op);
- Expression* makeStringEncode(Element& s, StringEncodeOp op);
- Expression* makeStringConcat(Element& s);
- Expression* makeStringEq(Element& s, StringEqOp op);
- Expression* makeStringWTF8Advance(Element& s);
- Expression* makeStringWTF16Get(Element& s);
- Expression* makeStringSliceWTF(Element& s);
- Expression* makeContBind(Element& s);
- Expression* makeContNew(Element& s);
- Expression* makeResume(Element& s);
- Expression* makeSuspend(Element& s);
-
- Expression* ignore(Element& s) { WASM_UNREACHABLE("unsupported"); }
-
- // Helper functions
- Type parseBlockType(Element& s, Index& i);
- Index parseMemoryLimits(Element& s, Index i, std::unique_ptr<Memory>& memory);
- Index parseMemoryIndex(Element& s, Index i, std::unique_ptr<Memory>& memory);
- Index parseMemoryForInstruction(const std::string& instrName,
- Memory& memory,
- Element& s,
- Index i);
- std::vector<Type> parseParamOrLocal(Element& s);
- std::vector<NameType> parseParamOrLocal(Element& s, size_t& localIndex);
- std::vector<Type> parseResults(Element& s);
- HeapType parseTypeRef(Element& s);
- size_t parseTypeUse(Element& s,
- size_t startPos,
- HeapType& functionType,
- std::vector<NameType>& namedParams);
- size_t parseTypeUse(Element& s, size_t startPos, HeapType& functionType);
-
- void
- stringToBinary(Element& s, std::string_view str, std::vector<char>& data);
- void parseMemory(Element& s, bool preParseImport = false);
- void parseData(Element& s);
- void parseInnerData(Element& s, Index i, std::unique_ptr<DataSegment>& seg);
- void parseExport(Element& s);
- void parseImport(Element& s);
- void parseGlobal(Element& s, bool preParseImport = false);
- void parseTable(Element& s, bool preParseImport = false);
- void parseElem(Element& s, Table* table = nullptr);
- ElementSegment* parseElemFinish(Element& s,
- std::unique_ptr<ElementSegment>& segment,
- Index i = 1,
- bool usesExpressions = false);
-
- // Parses something like (func ..), (array ..), (struct)
- HeapType parseHeapType(Element& s);
-
- void parseTag(Element& s, bool preParseImport = false);
-
- Function::DebugLocation getDebugLocation(const SourceLocation& loc);
-
- // Struct/Array instructions have an unnecessary heap type that is just for
- // validation (except for the case of unreachability, but that's not a problem
- // anyhow, we can ignore it there). That is, we also have a reference typed
- // child from which we can infer the type anyhow, and we just need to check
- // that type is the same.
- void
- validateHeapTypeUsingChild(Expression* child, HeapType heapType, Element& s);
-};
-
-} // namespace wasm
-
-#endif // wasm_wasm_s_parser_h
diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt
index e01be3b6a..16b6d8aed 100644
--- a/src/wasm/CMakeLists.txt
+++ b/src/wasm/CMakeLists.txt
@@ -9,7 +9,6 @@ set(wasm_SOURCES
wasm-interpreter.cpp
wasm-io.cpp
wasm-ir-builder.cpp
- wasm-s-parser.cpp
wasm-stack.cpp
wasm-stack-opts.cpp
wasm-type.cpp
diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp
index 10b84bb4d..149216e1a 100644
--- a/src/wasm/wasm-io.cpp
+++ b/src/wasm/wasm-io.cpp
@@ -29,24 +29,15 @@
#include "support/debug.h"
#include "support/path.h"
#include "wasm-binary.h"
-#include "wasm-s-parser.h"
namespace wasm {
-bool useNewWATParser = true;
-
#define DEBUG_TYPE "writer"
static void readTextData(std::string& input, Module& wasm, IRProfile profile) {
- if (useNewWATParser) {
- if (auto parsed = WATParser::parseModule(wasm, input);
- auto err = parsed.getErr()) {
- Fatal() << err->msg;
- }
- } else {
- SExpressionParser parser(const_cast<char*>(input.c_str()));
- Element& root = *parser.root;
- SExpressionWasmBuilder builder(wasm, *root[0], profile);
+ if (auto parsed = WATParser::parseModule(wasm, input);
+ auto err = parsed.getErr()) {
+ Fatal() << err->msg;
}
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
deleted file mode 100644
index f9eae14b8..000000000
--- a/src/wasm/wasm-s-parser.cpp
+++ /dev/null
@@ -1,4124 +0,0 @@
-/*
- * Copyright 2015 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 "wasm-s-parser.h"
-
-#include <cctype>
-#include <cmath>
-#include <limits>
-
-#include "ir/branch-utils.h"
-#include "ir/table-utils.h"
-#include "shared-constants.h"
-#include "support/string.h"
-#include "wasm-binary.h"
-#include "wasm-builder.h"
-
-using namespace std::string_literals;
-
-#define abort_on(str) \
- { throw ParseException(std::string("abort_on ") + str); }
-#define element_assert(condition) \
- assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0));
-
-namespace {
-int unhex(char c) {
- if (c >= '0' && c <= '9') {
- return c - '0';
- }
- if (c >= 'a' && c <= 'f') {
- return c - 'a' + 10;
- }
- if (c >= 'A' && c <= 'F') {
- return c - 'A' + 10;
- }
- throw wasm::ParseException("invalid hexadecimal");
-}
-} // namespace
-
-namespace wasm {
-
-// Similar to ParseException but built from an Element.
-struct SParseException : public ParseException {
- // Receive an element and report its contents, line and column.
- SParseException(std::string text, const Element& s)
- : ParseException(text + ": " + s.forceString(), s.line, s.col) {}
-
- // Receive a parent and child element. We print out the full parent for
- // context, but report the child line and column inside it.
- SParseException(std::string text, const Element& parent, const Element& child)
- : ParseException(
- text + ": " + parent.forceString(), child.line, child.col) {}
-};
-
-static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), REC("rec"),
- I8("i8"), I16("i16"), DECLARE("declare"), ITEM("item"), OFFSET("offset"),
- SUB("sub"), FINAL("final");
-
-static Address getAddress(const Element* s) {
- return std::stoll(s->toString());
-}
-
-static void
-checkAddress(Address a, const char* errorText, const Element* errorElem) {
- if (a > std::numeric_limits<Address::address32_t>::max()) {
- throw SParseException(errorText, *errorElem);
- }
-}
-
-static bool elementStartsWith(Element& s, IString str) {
- return s.isList() && s.size() > 0 && s[0]->isStr() && s[0]->str() == str;
-}
-
-static bool elementStartsWith(Element* s, IString str) {
- return elementStartsWith(*s, str);
-}
-
-Element::List& Element::list() {
- if (!isList()) {
- throw SParseException("expected list", *this);
- }
- return list_;
-}
-
-Element* Element::operator[](unsigned i) {
- if (!isList()) {
- throw SParseException("expected list", *this);
- }
- if (i >= list().size()) {
- throw SParseException("expected more elements in list", *this);
- }
- return list()[i];
-}
-
-IString Element::str() const {
- if (!isStr()) {
- throw SParseException("expected string", *this);
- }
- return str_;
-}
-
-std::string Element::toString() const {
- if (!isStr()) {
- throw SParseException("expected string", *this);
- }
- return str_.toString();
-}
-
-std::string Element::forceString() const {
- std::stringstream ss;
- ss << *this;
- // Limit the size to something reasonable for printing out.
- return ss.str().substr(0, 80);
-}
-
-Element* Element::setString(IString str__, bool dollared__, bool quoted__) {
- isList_ = false;
- str_ = str__;
- dollared_ = dollared__;
- quoted_ = quoted__;
- return this;
-}
-
-Element*
-Element::setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_) {
- line = line_;
- col = col_;
- startLoc = startLoc_;
- return this;
-}
-
-std::ostream& operator<<(std::ostream& o, const Element& e) {
- if (e.isList_) {
- o << '(';
- for (auto item : e.list_) {
- o << ' ' << *item;
- }
- o << " )";
- } else {
- if (e.dollared()) {
- o << '$';
- }
- o << e.str_.str;
- }
- return o;
-}
-
-void Element::dump() {
- std::cout << "dumping " << this << " : " << *this << ".\n";
-}
-
-SExpressionParser::SExpressionParser(char const* input) : input(input) {
- root = nullptr;
- line = 1;
- lineStart = input;
- while (!root) { // keep parsing until we pass an initial comment
- root = parse();
- }
-}
-
-Element* SExpressionParser::parse() {
- std::vector<Element*> stack;
- std::vector<SourceLocation*> stackLocs;
- Element* curr = allocator.alloc<Element>();
- while (1) {
- skipWhitespace();
- if (input[0] == 0) {
- break;
- }
- if (input[0] == '(') {
- input++;
- stack.push_back(curr);
- curr = allocator.alloc<Element>()->setMetadata(
- line, input - lineStart - 1, loc);
- stackLocs.push_back(loc);
- assert(stack.size() == stackLocs.size());
- } else if (input[0] == ')') {
- input++;
- curr->endLoc = loc;
- auto last = curr;
- if (stack.empty()) {
- throw ParseException("s-expr stack empty");
- }
- curr = stack.back();
- assert(stack.size() == stackLocs.size());
- stack.pop_back();
- loc = stackLocs.back();
- stackLocs.pop_back();
- curr->list().push_back(last);
- } else {
- curr->list().push_back(parseString());
- }
- }
- if (stack.size() != 0) {
- throw SParseException("stack is not empty", *curr);
- }
- return curr;
-}
-
-void SExpressionParser::parseDebugLocation() {
- // Extracting debug location (if valid)
- char const* debugLoc = input + 3; // skipping ";;@"
- while (debugLoc[0] && debugLoc[0] == ' ') {
- debugLoc++;
- }
- char const* debugLocEnd = debugLoc;
- while (debugLocEnd[0] && debugLocEnd[0] != '\n') {
- debugLocEnd++;
- }
- if (debugLocEnd == debugLoc) {
- loc = nullptr;
- return;
- }
- char const* pos = debugLoc;
- while (pos < debugLocEnd && pos[0] != ':') {
- pos++;
- }
- if (pos >= debugLocEnd) {
- return; // no line number
- }
- std::string name(debugLoc, pos);
- char const* lineStart = ++pos;
- while (pos < debugLocEnd && pos[0] != ':') {
- pos++;
- }
- std::string lineStr(lineStart, pos);
- if (pos >= debugLocEnd) {
- return; // no column number
- }
- std::string colStr(++pos, debugLocEnd);
- void* buf =
- allocator.allocSpace(sizeof(SourceLocation), alignof(SourceLocation));
- loc = new (buf) SourceLocation(
- IString(name.c_str(), false), atoi(lineStr.c_str()), atoi(colStr.c_str()));
-}
-
-void SExpressionParser::skipWhitespace() {
- while (1) {
- while (isspace(input[0])) {
- if (input[0] == '\n') {
- line++;
- lineStart = input + 1;
- }
- input++;
- }
- if (input[0] == ';' && input[1] == ';') {
- if (input[2] == '@') {
- parseDebugLocation();
- }
- while (input[0] && input[0] != '\n') {
- input++;
- }
- line++;
- if (!input[0]) {
- return;
- }
- lineStart = ++input;
- } else if (input[0] == '(' && input[1] == ';') {
- // Skip nested block comments.
- input += 2;
- int depth = 1;
- while (1) {
- if (!input[0]) {
- return;
- }
- if (input[0] == '(' && input[1] == ';') {
- input += 2;
- depth++;
- } else if (input[0] == ';' && input[1] == ')') {
- input += 2;
- --depth;
- if (depth == 0) {
- break;
- }
- } else if (input[0] == '\n') {
- line++;
- lineStart = input;
- input++;
- } else {
- input++;
- }
- }
- } else {
- return;
- }
- }
-}
-
-Element* SExpressionParser::parseString() {
- bool dollared = false;
- if (input[0] == '$') {
- input++;
- dollared = true;
- }
- char const* start = input;
- if (input[0] == '"') {
- // parse escaping \", but leave code escaped - we'll handle escaping in
- // memory segments specifically
- input++;
- std::string str;
- while (1) {
- if (input[0] == 0) {
- throw ParseException("unterminated string", line, start - lineStart);
- }
- if (input[0] == '"') {
- break;
- }
- if (input[0] == '\\') {
- str += input[0];
- if (input[1] == 0) {
- throw ParseException(
- "unterminated string escape", line, start - lineStart);
- }
- str += input[1];
- input += 2;
- continue;
- }
- str += input[0];
- input++;
- }
- input++;
- return allocator.alloc<Element>()
- ->setString(IString(str.c_str(), false), dollared, true)
- ->setMetadata(line, start - lineStart, loc);
- }
- while (input[0] && !isspace(input[0]) && input[0] != ')' && input[0] != '(' &&
- input[0] != ';') {
- input++;
- }
- if (start == input) {
- throw ParseException("expected string", line, input - lineStart);
- }
-
- std::string temp;
- temp.assign(start, input - start);
-
- auto ret = allocator.alloc<Element>()
- ->setString(IString(temp.c_str(), false), dollared, false)
- ->setMetadata(line, start - lineStart, loc);
-
- return ret;
-}
-
-SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm,
- Element& module,
- IRProfile profile)
- : wasm(wasm), allocator(wasm.allocator), profile(profile) {
- if (module.size() == 0) {
- throw ParseException("empty toplevel, expected module");
- }
- if (module[0]->str() != MODULE) {
- throw ParseException("toplevel does not start with module");
- }
- if (module.size() == 1) {
- return;
- }
- Index i = 1;
- if (module[i]->dollared()) {
- wasm.name = module[i]->str();
- if (module.size() == 2) {
- return;
- }
- i++;
- }
-
- // spec tests have a `binary` keyword after the optional module name. Skip it
- Name BINARY("binary");
- if (module[i]->isStr() && module[i]->str() == BINARY &&
- !module[i]->quoted()) {
- i++;
- }
-
- if (i < module.size() && module[i]->isStr()) {
- // these s-expressions contain a binary module, actually
- std::vector<char> data;
- for (; i < module.size(); ++i) {
- stringToBinary(*module[i], module[i]->str().str, data);
- }
- // TODO: support applying features here
- WasmBinaryReader binaryBuilder(wasm, FeatureSet::MVP, data);
- binaryBuilder.read();
- return;
- }
-
- preParseHeapTypes(module);
-
- Index implementedFunctions = 0;
- functionCounter = 0;
- for (unsigned j = i; j < module.size(); j++) {
- auto& s = *module[j];
- preParseFunctionType(s);
- preParseImports(s);
- preParseMemory(s);
- if (elementStartsWith(s, FUNC) && !isImport(s)) {
- implementedFunctions++;
- }
- }
- // we go through the functions again, now parsing them, and the counter begins
- // from where imports ended
- functionCounter -= implementedFunctions;
- for (unsigned j = i; j < module.size(); j++) {
- parseModuleElement(*module[j]);
- }
-}
-
-bool SExpressionWasmBuilder::isImport(Element& curr) {
- for (Index i = 0; i < curr.size(); i++) {
- auto& x = *curr[i];
- if (elementStartsWith(x, IMPORT)) {
- return true;
- }
- }
- return false;
-}
-
-void SExpressionWasmBuilder::preParseImports(Element& curr) {
- IString id = curr[0]->str();
- if (id == IMPORT) {
- parseImport(curr);
- }
- if (isImport(curr)) {
- if (id == FUNC) {
- parseFunction(curr, true /* preParseImport */);
- } else if (id == GLOBAL) {
- parseGlobal(curr, true /* preParseImport */);
- } else if (id == TABLE) {
- parseTable(curr, true /* preParseImport */);
- } else if (id == MEMORY) {
- parseMemory(curr, true /* preParseImport */);
- } else if (id == TAG) {
- parseTag(curr, true /* preParseImport */);
- } else {
- throw SParseException("fancy import we don't support yet", curr);
- }
- }
-}
-
-void SExpressionWasmBuilder::preParseMemory(Element& curr) {
- IString id = curr[0]->str();
- if (id == MEMORY && !isImport(curr)) {
- parseMemory(curr);
- }
-}
-
-void SExpressionWasmBuilder::parseModuleElement(Element& curr) {
- if (isImport(curr)) {
- return; // already done
- }
- IString id = curr[0]->str();
- if (id == MEMORY) {
- return; // already done
- }
- if (id == START) {
- return parseStart(curr);
- }
- if (id == FUNC) {
- return parseFunction(curr);
- }
- if (id == DATA) {
- return parseData(curr);
- }
- if (id == EXPORT) {
- return parseExport(curr);
- }
- if (id == IMPORT) {
- return; // already done
- }
- if (id == GLOBAL) {
- return parseGlobal(curr);
- }
- if (id == TABLE) {
- return parseTable(curr);
- }
- if (id == ELEM) {
- return parseElem(curr);
- }
- if (id == TYPE) {
- return; // already done
- }
- if (id == REC) {
- return; // already done
- }
- if (id == TAG) {
- return parseTag(curr);
- }
- std::cerr << "bad module element " << id.str << '\n';
- throw SParseException("unknown module element", curr);
-}
-
-int SExpressionWasmBuilder::parseIndex(Element& s) {
- try {
- return std::stoi(s.toString());
- } catch (...) {
- throw SParseException("expected integer", s);
- }
-}
-
-Name SExpressionWasmBuilder::getFunctionName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= functionNames.size()) {
- throw SParseException("unknown function in getFunctionName", s);
- }
- return functionNames[offset];
- }
-}
-
-Name SExpressionWasmBuilder::getTableName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= tableNames.size()) {
- throw SParseException("unknown table in getTableName", s);
- }
- return tableNames[offset];
- }
-}
-
-Name SExpressionWasmBuilder::getElemSegmentName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= elemSegmentNames.size()) {
- throw SParseException("unknown elem segment", s);
- }
- return elemSegmentNames[offset];
- }
-}
-
-bool SExpressionWasmBuilder::isMemory64(Name memoryName) {
- auto* memory = wasm.getMemoryOrNull(memoryName);
- if (!memory) {
- throw ParseException("invalid memory name in isMemory64: "s +
- memoryName.toString());
- }
- return memory->is64();
-}
-
-Name SExpressionWasmBuilder::getMemoryNameAtIdx(Index i) {
- if (i >= memoryNames.size()) {
- throw ParseException("unknown memory in getMemoryName: "s +
- std::to_string(i));
- }
- return memoryNames[i];
-}
-
-Name SExpressionWasmBuilder::getMemoryName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- return getMemoryNameAtIdx(offset);
- }
-}
-
-Name SExpressionWasmBuilder::getDataSegmentName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= dataSegmentNames.size()) {
- throw SParseException("unknown data segment", s);
- }
- return dataSegmentNames[offset];
- }
-}
-
-Name SExpressionWasmBuilder::getGlobalName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= globalNames.size()) {
- throw SParseException("unknown global in getGlobalName", s);
- }
- return globalNames[offset];
- }
-}
-
-Name SExpressionWasmBuilder::getTagName(Element& s) {
- if (s.dollared()) {
- return s.str();
- } else {
- // index
- size_t offset = parseIndex(s);
- if (offset >= tagNames.size()) {
- throw SParseException("unknown tag in getTagName", s);
- }
- return tagNames[offset];
- }
-}
-
-// Parse various forms of (param ...) or (local ...) element. This ignores all
-// parameter or local names when specified.
-std::vector<Type> SExpressionWasmBuilder::parseParamOrLocal(Element& s) {
- size_t fakeIndex = 0;
- std::vector<NameType> namedParams = parseParamOrLocal(s, fakeIndex);
- std::vector<Type> params;
- for (auto& p : namedParams) {
- params.push_back(p.type);
- }
- return params;
-}
-
-// Parses various forms of (param ...) or (local ...) element:
-// (param $name type) (e.g. (param $a i32))
-// (param type+) (e.g. (param i32 f64))
-// (local $name type) (e.g. (local $a i32))
-// (local type+) (e.g. (local i32 f64))
-// If the name is unspecified, it will create one using localIndex.
-std::vector<NameType>
-SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) {
- assert(elementStartsWith(s, PARAM) || elementStartsWith(s, LOCAL));
- std::vector<NameType> namedParams;
- if (s.size() == 1) { // (param) or (local)
- return namedParams;
- }
-
- for (size_t i = 1; i < s.size(); i++) {
- IString name;
- if (s[i]->dollared()) {
- if (i != 1) {
- throw SParseException("invalid wasm type", *s[i]);
- }
- if (i + 1 >= s.size()) {
- throw SParseException("invalid param entry", s);
- }
- name = s[i]->str();
- i++;
- } else {
- name = Name::fromInt(localIndex);
- }
- localIndex++;
- Type type;
- type = elementToType(*s[i]);
- if (elementStartsWith(s, PARAM) && type.isTuple()) {
- throw SParseException("params may not have tuple types", *s[i]);
- }
- namedParams.emplace_back(name, type);
- }
- return namedParams;
-}
-
-// Parses (result type) element. (e.g. (result i32))
-std::vector<Type> SExpressionWasmBuilder::parseResults(Element& s) {
- assert(elementStartsWith(s, RESULT));
- std::vector<Type> types;
- for (size_t i = 1; i < s.size(); i++) {
- types.push_back(elementToType(*s[i]));
- }
- return types;
-}
-
-// Parses an element that references an entry in the type section. The element
-// should be in the form of (type name) or (type index).
-// (e.g. (type $a), (type 0))
-HeapType SExpressionWasmBuilder::parseTypeRef(Element& s) {
- assert(elementStartsWith(s, TYPE));
- if (s.size() != 2) {
- throw SParseException("invalid type reference", s);
- }
- auto heapType = parseHeapType(*s[1]);
- if (!heapType.isSignature()) {
- throw SParseException("expected signature type", s);
- }
- return heapType;
-}
-
-// Parses typeuse, a reference to a type definition. It is in the form of either
-// (type index) or (type name), possibly augmented by inlined (param) and
-// (result) nodes. (type) node can be omitted as well. Outputs are returned by
-// parameter references.
-// typeuse ::= (type index|name)+ |
-// (type index|name)+ (param ..)* (result ..)* |
-// (param ..)* (result ..)*
-size_t
-SExpressionWasmBuilder::parseTypeUse(Element& s,
- size_t startPos,
- HeapType& functionType,
- std::vector<NameType>& namedParams) {
- std::vector<Type> params, results;
- size_t i = startPos;
-
- bool typeExists = false, paramsOrResultsExist = false;
- if (i < s.size() && elementStartsWith(*s[i], TYPE)) {
- typeExists = true;
- functionType = parseTypeRef(*s[i++]);
- }
-
- size_t paramPos = i;
- size_t localIndex = 0;
-
- while (i < s.size() && elementStartsWith(*s[i], PARAM)) {
- paramsOrResultsExist = true;
- auto newParams = parseParamOrLocal(*s[i++], localIndex);
- namedParams.insert(namedParams.end(), newParams.begin(), newParams.end());
- for (auto p : newParams) {
- params.push_back(p.type);
- }
- }
-
- while (i < s.size() && elementStartsWith(*s[i], RESULT)) {
- paramsOrResultsExist = true;
- auto newResults = parseResults(*s[i++]);
- results.insert(results.end(), newResults.begin(), newResults.end());
- }
-
- auto inlineSig = Signature(Type(params), Type(results));
-
- // If none of type/param/result exists, this is equivalent to a type that does
- // not have parameters and returns nothing.
- if (!typeExists && !paramsOrResultsExist) {
- paramsOrResultsExist = true;
- }
-
- if (!typeExists) {
- functionType = inlineSig;
- } else if (paramsOrResultsExist) {
- // verify that (type) and (params)/(result) match
- if (inlineSig != functionType.getSignature()) {
- throw SParseException("type and param/result don't match", *s[paramPos]);
- }
- }
-
- // Add implicitly defined type to global list so it has an index
- if (std::find(types.begin(), types.end(), functionType) == types.end()) {
- types.push_back(functionType);
- }
-
- // If only (type) is specified, populate `namedParams`
- if (!paramsOrResultsExist) {
- size_t index = 0;
- assert(functionType.isSignature());
- Signature sig = functionType.getSignature();
- for (const auto& param : sig.params) {
- namedParams.emplace_back(Name::fromInt(index++), param);
- }
- }
-
- return i;
-}
-
-// Parses a typeuse. Use this when only FunctionType* is needed.
-size_t SExpressionWasmBuilder::parseTypeUse(Element& s,
- size_t startPos,
- HeapType& functionType) {
- std::vector<NameType> params;
- return parseTypeUse(s, startPos, functionType, params);
-}
-
-void SExpressionWasmBuilder::preParseHeapTypes(Element& module) {
- // Iterate through each individual type definition, calling `f` with the
- // definition and its recursion group number.
- auto forEachType = [&](auto f) {
- size_t groupNumber = 0;
- for (auto* elemPtr : module) {
- auto& elem = *elemPtr;
- if (elementStartsWith(elem, TYPE)) {
- f(elem, groupNumber++);
- } else if (elementStartsWith(elem, REC)) {
- for (auto* innerPtr : elem) {
- auto& inner = *innerPtr;
- if (elementStartsWith(inner, TYPE)) {
- f(inner, groupNumber);
- }
- }
- ++groupNumber;
- }
- }
- };
-
- // Map type names to indices
- size_t numTypes = 0;
- forEachType([&](Element& elem, size_t) {
- if (elem[1]->dollared()) {
- std::string name = elem[1]->toString();
- if (!typeIndices.insert({name, numTypes}).second) {
- throw SParseException("duplicate function type", elem);
- }
- }
- ++numTypes;
- });
-
- TypeBuilder builder(numTypes);
-
- // Create recursion groups
- size_t currGroup = 0, groupStart = 0, groupLength = 0;
- auto finishGroup = [&]() {
- builder.createRecGroup(groupStart, groupLength);
- groupStart = groupStart + groupLength;
- groupLength = 0;
- };
- forEachType([&](Element&, size_t group) {
- if (group != currGroup) {
- finishGroup();
- currGroup = group;
- }
- ++groupLength;
- });
- finishGroup();
-
- auto parseHeapType = [&](Element& elem) -> HeapType {
- auto name = elem.toString();
- if (elem.dollared()) {
- auto it = typeIndices.find(name);
- if (it == typeIndices.end()) {
- throw SParseException("invalid type name", elem);
- } else {
- return builder[it->second];
- }
- } else if (String::isNumber(name)) {
- size_t index = parseIndex(elem);
- if (index >= numTypes) {
- throw SParseException("invalid type index", elem);
- }
- return builder[index];
- } else {
- return stringToHeapType(elem.str());
- }
- };
-
- auto parseRefType = [&](Element& elem) -> Type {
- // '(' 'ref' 'null'? ht ')'
- auto nullable =
- elem[1]->isStr() && *elem[1] == NULL_ ? Nullable : NonNullable;
- auto& referent = nullable ? *elem[2] : *elem[1];
- auto ht = parseHeapType(referent);
- if (ht.isBasic()) {
- return Type(ht, nullable);
- } else {
- return builder.getTempRefType(ht, nullable);
- }
- };
-
- auto parseValType = [&](Element& elem) {
- if (elem.isStr()) {
- return stringToType(elem.str());
- } else if (*elem[0] == REF) {
- return parseRefType(elem);
- } else {
- throw SParseException("unknown valtype kind", elem);
- }
- };
-
- auto parseParams = [&](Element& elem) {
- auto it = ++elem.begin();
- if (it != elem.end() && (*it)->dollared()) {
- ++it;
- }
- std::vector<Type> params;
- for (auto end = elem.end(); it != end; ++it) {
- params.push_back(parseValType(**it));
- }
- return params;
- };
-
- auto parseResults = [&](Element& elem) {
- std::vector<Type> results;
- for (auto it = ++elem.begin(); it != elem.end(); ++it) {
- results.push_back(parseValType(**it));
- }
- return results;
- };
-
- auto parseSignatureDef = [&](Element& elem, bool nominal) {
- // '(' 'func' vec(param) vec(result) ')'
- // param ::= '(' 'param' id? valtype ')'
- // result ::= '(' 'result' valtype ')'
- std::vector<Type> params, results;
- auto end = elem.end() - (nominal ? 1 : 0);
- for (auto it = ++elem.begin(); it != end; ++it) {
- Element& curr = **it;
- if (elementStartsWith(curr, PARAM)) {
- auto newParams = parseParams(curr);
- params.insert(params.end(), newParams.begin(), newParams.end());
- } else if (elementStartsWith(curr, RESULT)) {
- auto newResults = parseResults(curr);
- results.insert(results.end(), newResults.begin(), newResults.end());
- }
- }
- return Signature(builder.getTempTupleType(params),
- builder.getTempTupleType(results));
- };
-
- auto parseContinuationDef = [&](Element& elem) {
- // '(' 'cont' index ')' | '(' 'cont' name ')'
- HeapType ft = parseHeapType(*elem[1]);
- if (!ft.isSignature()) {
- throw ParseException(
- "cont type must be created from func type", elem.line, elem.col);
- }
- return Continuation(ft);
- };
-
- // Parses a field, and notes the name if one is found.
- auto parseField = [&](Element* elem, Name& name) {
- Mutability mutable_ = Immutable;
- // elem is a list, containing either
- // TYPE
- // or
- // (field TYPE)
- // or
- // (field $name TYPE)
- if (elementStartsWith(elem, FIELD)) {
- if (elem->size() == 3) {
- name = (*elem)[1]->str();
- }
- elem = (*elem)[elem->size() - 1];
- }
- // The element may also be (mut (..)).
- if (elementStartsWith(elem, MUT)) {
- mutable_ = Mutable;
- elem = (*elem)[1];
- }
- if (elem->isStr()) {
- // elem is a simple string name like "i32". It can be a normal wasm
- // type, or one of the special types only available in fields.
- if (*elem == I8) {
- return Field(Field::i8, mutable_);
- } else if (*elem == I16) {
- return Field(Field::i16, mutable_);
- }
- }
- // Otherwise it's an arbitrary type.
- return Field(parseValType(*elem), mutable_);
- };
-
- auto parseStructDef = [&](Element& elem, size_t typeIndex, bool nominal) {
- FieldList fields;
- Index end = elem.size() - (nominal ? 1 : 0);
- for (Index i = 1; i < end; i++) {
- Name name;
- fields.emplace_back(parseField(elem[i], name));
- if (name.is()) {
- // Only add the name to the map if it exists.
- fieldNames[typeIndex][i - 1] = name;
- }
- }
- return Struct(fields);
- };
-
- auto parseArrayDef = [&](Element& elem) {
- Name unused;
- return Array(parseField(elem[1], unused));
- };
-
- size_t index = 0;
- forEachType([&](Element& elem, size_t) {
- Element& def = elem[1]->dollared() ? *elem[2] : *elem[1];
- Element& kind = *def[0];
- Element* super = nullptr;
- if (kind == SUB) {
- Index i = 1;
- if (*def[i] == FINAL) {
- ++i;
- } else {
- builder[index].setOpen();
- }
- if (def[i]->dollared()) {
- super = def[i];
- ++i;
- }
- Element& subtype = *def[i++];
- if (i != def.size()) {
- throw SParseException("invalid 'sub' form", kind);
- }
- if (!subtype.isList() || subtype.size() < 1) {
- throw SParseException("invalid subtype definition", subtype);
- }
- Element& subtypeKind = *subtype[0];
- if (subtypeKind == FUNC) {
- builder[index] = parseSignatureDef(subtype, 0);
- } else if (kind == CONT) {
- builder[index] = parseContinuationDef(subtype);
- } else if (subtypeKind == STRUCT) {
- builder[index] = parseStructDef(subtype, index, 0);
- } else if (subtypeKind == ARRAY) {
- builder[index] = parseArrayDef(subtype);
- } else {
- throw SParseException("unknown subtype kind", subtypeKind);
- }
- } else {
- if (kind == FUNC) {
- builder[index] = parseSignatureDef(def, 0);
- } else if (kind == CONT) {
- builder[index] = parseContinuationDef(def);
- } else if (kind == STRUCT) {
- builder[index] = parseStructDef(def, index, 0);
- } else if (kind == ARRAY) {
- builder[index] = parseArrayDef(def);
- } else {
- throw SParseException("unknown heaptype kind", kind);
- }
- }
- if (super) {
- auto it = typeIndices.find(super->toString());
- if (!super->dollared() || it == typeIndices.end()) {
- throw SParseException("unknown supertype", elem, *super);
- }
- builder[index].subTypeOf(builder[it->second]);
- }
- ++index;
- });
-
- auto result = builder.build();
- if (auto* err = result.getError()) {
- // Find the name to provide a better error message.
- std::stringstream msg;
- msg << "Invalid type: " << err->reason;
- for (auto& [name, index] : typeIndices) {
- if (index == err->index) {
- Fatal() << msg.str() << " at type $" << name;
- }
- }
- // No name, just report the index.
- Fatal() << msg.str() << " at index " << err->index;
- }
- types = *result;
-
- for (auto& [name, index] : typeIndices) {
- auto type = types[index];
- // A type may appear in the type section more than once, but we canonicalize
- // types internally, so there will be a single name chosen for that type. Do
- // so determistically.
- if (wasm.typeNames.count(type) && wasm.typeNames[type].name.str < name) {
- continue;
- }
- auto& currTypeNames = wasm.typeNames[type];
- currTypeNames.name = name;
- if (type.isStruct()) {
- currTypeNames.fieldNames = fieldNames[index];
- }
- }
-}
-
-void SExpressionWasmBuilder::preParseFunctionType(Element& s) {
- IString id = s[0]->str();
- if (id != FUNC) {
- return;
- }
- size_t i = 1;
- Name name, exportName;
- i = parseFunctionNames(s, name, exportName);
- if (!name.is()) {
- // unnamed, use an index
- name = Name::fromInt(functionCounter);
- }
- functionNames.push_back(name);
- functionCounter++;
- parseTypeUse(s, i, functionTypes[name]);
-}
-
-size_t SExpressionWasmBuilder::parseFunctionNames(Element& s,
- Name& name,
- Name& exportName) {
- size_t i = 1;
- while (i < s.size() && i < 3 && s[i]->isStr()) {
- if (s[i]->dollared()) {
- name = s[i]->str();
- i++;
- } else {
- break;
- }
- }
- if (i < s.size() && s[i]->isList()) {
- auto& inner = *s[i];
- if (elementStartsWith(inner, EXPORT)) {
- exportName = inner[1]->str();
- i++;
- }
- }
-#if 0
- if (exportName.is() && !name.is()) {
- name = exportName; // useful for debugging
- }
-#endif
- return i;
-}
-
-void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) {
- brokeToAutoBlock = false;
-
- Name name, exportName;
- size_t i = parseFunctionNames(s, name, exportName);
- bool hasExplicitName = name.is();
- if (!preParseImport) {
- if (!name.is()) {
- // unnamed, use an index
- name = Name::fromInt(functionCounter);
- }
- functionCounter++;
- } else {
- // just preparsing, functionCounter was incremented by preParseFunctionType
- if (!name.is()) {
- // unnamed, use an index
- name = functionNames[functionCounter - 1];
- }
- }
- if (exportName.is()) {
- auto ex = std::make_unique<Export>();
- ex->name = exportName;
- ex->value = name;
- ex->kind = ExternalKind::Function;
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", s);
- }
- wasm.addExport(ex.release());
- }
-
- // parse import
- Name importModule, importBase;
- if (i < s.size() && elementStartsWith(*s[i], IMPORT)) {
- Element& curr = *s[i];
- importModule = curr[1]->str();
- importBase = curr[2]->str();
- i++;
- }
-
- // parse typeuse: type/param/result
- HeapType type;
- std::vector<NameType> params;
- i = parseTypeUse(s, i, type, params);
-
- // when (import) is inside a (func) element, this is not a function definition
- // but an import.
- if (importModule.is()) {
- if (!importBase.size()) {
- throw SParseException("module but no base for import", s);
- }
- if (!preParseImport) {
- throw SParseException("!preParseImport in func", s);
- }
- auto im = std::make_unique<Function>();
- im->setName(name, hasExplicitName);
- im->module = importModule;
- im->base = importBase;
- im->type = type;
- functionTypes[name] = type;
- if (wasm.getFunctionOrNull(im->name)) {
- throw SParseException("duplicate import", s);
- }
- wasm.addFunction(std::move(im));
- if (currFunction) {
- throw SParseException("import module inside function dec", s);
- }
- nameMapper.clear();
- return;
- }
- // at this point this not an import but a real function definition.
- if (preParseImport) {
- throw SParseException("preParseImport in func", s);
- }
-
- size_t localIndex = params.size(); // local index for params and locals
-
- // parse locals
- std::vector<NameType> vars;
- while (i < s.size() && elementStartsWith(*s[i], LOCAL)) {
- auto newVars = parseParamOrLocal(*s[i++], localIndex);
- vars.insert(vars.end(), newVars.begin(), newVars.end());
- }
-
- // make a new function
- currFunction = std::unique_ptr<Function>(
- Builder(wasm).makeFunction(name, std::move(params), type, std::move(vars)));
- currFunction->hasExplicitName = hasExplicitName;
- currFunction->profile = profile;
-
- // parse body
- Block* autoBlock = nullptr; // may need to add a block for the very top level
- auto ensureAutoBlock = [&]() {
- if (!autoBlock) {
- autoBlock = allocator.alloc<Block>();
- autoBlock->list.push_back(currFunction->body);
- currFunction->body = autoBlock;
- }
- };
- while (i < s.size()) {
- Expression* ex = parseExpression(*s[i++]);
- if (!currFunction->body) {
- currFunction->body = ex;
- } else {
- ensureAutoBlock();
- autoBlock->list.push_back(ex);
- }
- }
-
- if (brokeToAutoBlock) {
- ensureAutoBlock();
- autoBlock->name = FAKE_RETURN;
- }
- if (autoBlock) {
- autoBlock->finalize(type.getSignature().results);
- }
- if (!currFunction->body) {
- currFunction->body = allocator.alloc<Nop>();
- }
- if (s.startLoc) {
- currFunction->prologLocation.insert(getDebugLocation(*s.startLoc));
- }
- if (s.endLoc) {
- currFunction->epilogLocation.insert(getDebugLocation(*s.endLoc));
- }
- if (wasm.getFunctionOrNull(currFunction->name)) {
- throw SParseException("duplicate function", s);
- }
- wasm.addFunction(currFunction.release());
- nameMapper.clear();
-}
-
-Type SExpressionWasmBuilder::stringToType(std::string_view str,
- bool allowError,
- bool prefix) {
- if (str.size() >= 3) {
- if (str[0] == 'i') {
- if (str[1] == '3' && str[2] == '2' && (prefix || str.size() == 3)) {
- return Type::i32;
- }
- if (str[1] == '6' && str[2] == '4' && (prefix || str.size() == 3)) {
- return Type::i64;
- }
- }
- if (str[0] == 'f') {
- if (str[1] == '3' && str[2] == '2' && (prefix || str.size() == 3)) {
- return Type::f32;
- }
- if (str[1] == '6' && str[2] == '4' && (prefix || str.size() == 3)) {
- return Type::f64;
- }
- }
- }
- if (str.size() >= 4) {
- if (str[0] == 'v') {
- if (str[1] == '1' && str[2] == '2' && str[3] == '8' &&
- (prefix || str.size() == 4)) {
- return Type::v128;
- }
- }
- }
- if (str.substr(0, 7) == "funcref" && (prefix || str.size() == 7)) {
- return Type(HeapType::func, Nullable);
- }
- if (str.substr(0, 7) == "contref" && (prefix || str.size() == 7)) {
- return Type(HeapType::cont, Nullable);
- }
- if (str.substr(0, 9) == "externref" && (prefix || str.size() == 9)) {
- return Type(HeapType::ext, Nullable);
- }
- if (str.substr(0, 6) == "anyref" && (prefix || str.size() == 6)) {
- return Type(HeapType::any, Nullable);
- }
- if (str.substr(0, 5) == "eqref" && (prefix || str.size() == 5)) {
- return Type(HeapType::eq, Nullable);
- }
- if (str.substr(0, 6) == "i31ref" && (prefix || str.size() == 6)) {
- return Type(HeapType::i31, Nullable);
- }
- if (str.substr(0, 9) == "structref" && (prefix || str.size() == 9)) {
- return Type(HeapType::struct_, Nullable);
- }
- if (str.substr(0, 8) == "arrayref" && (prefix || str.size() == 8)) {
- return Type(HeapType::array, Nullable);
- }
- if (str.substr(0, 6) == "exnref" && (prefix || str.size() == 6)) {
- return Type(HeapType::exn, Nullable);
- }
- if (str.substr(0, 9) == "stringref" && (prefix || str.size() == 9)) {
- return Type(HeapType::string, Nullable);
- }
- if (str.substr(0, 7) == "nullref" && (prefix || str.size() == 7)) {
- return Type(HeapType::none, Nullable);
- }
- if (str.substr(0, 13) == "nullexternref" && (prefix || str.size() == 13)) {
- return Type(HeapType::noext, Nullable);
- }
- if (str.substr(0, 11) == "nullfuncref" && (prefix || str.size() == 11)) {
- return Type(HeapType::nofunc, Nullable);
- }
- if (str.substr(0, 10) == "nullexnref" && (prefix || str.size() == 10)) {
- return Type(HeapType::noexn, Nullable);
- }
- if (str.substr(0, 11) == "nullcontref" && (prefix || str.size() == 11)) {
- return Type(HeapType::nocont, Nullable);
- }
- if (allowError) {
- return Type::none;
- }
- throw ParseException(std::string("invalid wasm type: ") +
- std::string(str.data(), str.size()));
-}
-
-HeapType SExpressionWasmBuilder::stringToHeapType(std::string_view str,
- bool prefix) {
- if (str.substr(0, 4) == "func" && (prefix || str.size() == 4)) {
- return HeapType::func;
- }
- if (str.substr(0, 4) == "cont" && (prefix || str.size() == 4)) {
- return HeapType::cont;
- }
- if (str.substr(0, 2) == "eq" && (prefix || str.size() == 2)) {
- return HeapType::eq;
- }
- if (str.substr(0, 6) == "extern" && (prefix || str.size() == 6)) {
- return HeapType::ext;
- }
- if (str.substr(0, 3) == "any" && (prefix || str.size() == 3)) {
- return HeapType::any;
- }
- if (str.substr(0, 3) == "i31" && (prefix || str.size() == 3)) {
- return HeapType::i31;
- }
- if (str.substr(0, 6) == "struct" && (prefix || str.size() == 6)) {
- return HeapType::struct_;
- }
- if (str.substr(0, 5) == "array" && (prefix || str.size() == 5)) {
- return HeapType::array;
- }
- if (str.substr(0, 3) == "exn" && (prefix || str.size() == 3)) {
- return HeapType::exn;
- }
- if (str.substr(0, 6) == "string" && (prefix || str.size() == 6)) {
- return HeapType::string;
- }
- if (str.substr(0, 4) == "none" && (prefix || str.size() == 4)) {
- return HeapType::none;
- }
- if (str.substr(0, 8) == "noextern" && (prefix || str.size() == 8)) {
- return HeapType::noext;
- }
- if (str.substr(0, 6) == "nofunc" && (prefix || str.size() == 6)) {
- return HeapType::nofunc;
- }
- if (str.substr(0, 6) == "nofunc" && (prefix || str.size() == 6)) {
- return HeapType::nofunc;
- }
- if (str.substr(0, 5) == "noexn" && (prefix || str.size() == 5)) {
- return HeapType::noexn;
- }
- if (str.substr(0, 6) == "nocont" && (prefix || str.size() == 6)) {
- return HeapType::nocont;
- }
- throw ParseException(std::string("invalid wasm heap type: ") +
- std::string(str.data(), str.size()));
-}
-
-Type SExpressionWasmBuilder::elementToType(Element& s) {
- if (s.isStr()) {
- return stringToType(s.str());
- }
- auto& list = s.list();
- auto size = list.size();
- if (elementStartsWith(s, REF)) {
- // It's a reference. It should be in the form
- // (ref $name)
- // or
- // (ref null $name)
- // and also $name can be the expanded structure of the type and not a name,
- // so something like (ref (func (result i32))), etc.
- if (size != 2 && size != 3) {
- throw SParseException(std::string("invalid reference type size"), s);
- }
- if (size == 3 && *list[1] != NULL_) {
- throw SParseException(std::string("invalid reference type qualifier"), s);
- }
- Nullability nullable = NonNullable;
- size_t i = 1;
- if (size == 3) {
- nullable = Nullable;
- i++;
- }
- return Type(parseHeapType(*s[i]), nullable);
- }
- if (elementStartsWith(s, TUPLE)) {
- // It's a tuple.
- std::vector<Type> types;
- for (size_t i = 1; i < s.size(); ++i) {
- types.push_back(elementToType(*list[i]));
- }
- return Type(types);
- }
- throw SParseException(std::string("expected type, got list"), s);
-}
-
-Type SExpressionWasmBuilder::stringToLaneType(const char* str) {
- if (strcmp(str, "i8x16") == 0) {
- return Type::i32;
- }
- if (strcmp(str, "i16x8") == 0) {
- return Type::i32;
- }
- if (strcmp(str, "i32x4") == 0) {
- return Type::i32;
- }
- if (strcmp(str, "i64x2") == 0) {
- return Type::i64;
- }
- if (strcmp(str, "f32x4") == 0) {
- return Type::f32;
- }
- if (strcmp(str, "f64x2") == 0) {
- return Type::f64;
- }
- return Type::none;
-}
-
-HeapType SExpressionWasmBuilder::getFunctionType(Name name, Element& s) {
- auto iter = functionTypes.find(name);
- if (iter == functionTypes.end()) {
- throw SParseException("invalid call target: " + std::string(name.str), s);
- }
- return iter->second;
-}
-
-Function::DebugLocation
-SExpressionWasmBuilder::getDebugLocation(const SourceLocation& loc) {
- IString file = loc.filename;
- auto& debugInfoFileNames = wasm.debugInfoFileNames;
- auto iter = debugInfoFileIndices.find(file);
- if (iter == debugInfoFileIndices.end()) {
- Index index = debugInfoFileNames.size();
- debugInfoFileNames.push_back(file.toString());
- debugInfoFileIndices[file] = index;
- }
- uint32_t fileIndex = debugInfoFileIndices[file];
- return {fileIndex, loc.line, loc.column};
-}
-
-Expression* SExpressionWasmBuilder::parseExpression(Element& s) {
- Expression* result = makeExpression(s);
- if (s.startLoc && currFunction) {
- currFunction->debugLocations[result] = getDebugLocation(*s.startLoc);
- }
- return result;
-}
-
-Expression* SExpressionWasmBuilder::makeExpression(Element& s){
-#define INSTRUCTION_PARSER
-#include "gen-s-parser.inc"
-}
-
-Expression* SExpressionWasmBuilder::makeUnreachable() {
- return allocator.alloc<Unreachable>();
-}
-
-Expression* SExpressionWasmBuilder::makeNop() { return allocator.alloc<Nop>(); }
-
-Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) {
- auto ret = allocator.alloc<Binary>();
- ret->op = op;
- ret->left = parseExpression(s[1]);
- ret->right = parseExpression(s[2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op) {
- auto ret = allocator.alloc<Unary>();
- ret->op = op;
- ret->value = parseExpression(s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSelect(Element& s) {
- auto ret = allocator.alloc<Select>();
- Index i = 1;
- Type type = parseBlockType(s, i);
- ret->ifTrue = parseExpression(s[i++]);
- ret->ifFalse = parseExpression(s[i++]);
- ret->condition = parseExpression(s[i]);
- if (type.isConcrete()) {
- ret->finalize(type);
- } else {
- ret->finalize();
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeDrop(Element& s) {
- auto ret = allocator.alloc<Drop>();
- ret->value = parseExpression(s[1]);
- if (ret->value->type.isTuple()) {
- throw SParseException("expected tuple.drop, found drop", s, *s[0]);
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeMemorySize(Element& s) {
- auto ret = allocator.alloc<MemorySize>();
- Index i = 1;
- Name memory;
- if (s.size() > 1) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- if (isMemory64(memory)) {
- ret->type = Type::i64;
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeMemoryGrow(Element& s) {
- auto ret = allocator.alloc<MemoryGrow>();
- Index i = 1;
- Name memory;
- if (s.size() > 2) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- if (isMemory64(memory)) {
- ret->type = Type::i64;
- }
- ret->delta = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Index SExpressionWasmBuilder::getLocalIndex(Element& s) {
- if (!currFunction) {
- throw SParseException("local access in non-function scope", s);
- }
- if (s.dollared()) {
- auto ret = s.str();
- if (currFunction->localIndices.count(ret) == 0) {
- throw SParseException("bad local name", s);
- }
- return currFunction->getLocalIndex(ret);
- }
- // this is a numeric index
- Index ret = parseIndex(s);
- if (ret >= currFunction->getNumLocals()) {
- throw SParseException("bad local index", s);
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeLocalGet(Element& s) {
- auto ret = allocator.alloc<LocalGet>();
- ret->index = getLocalIndex(*s[1]);
- ret->type = currFunction->getLocalType(ret->index);
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeLocalTee(Element& s) {
- auto ret = allocator.alloc<LocalSet>();
- ret->index = getLocalIndex(*s[1]);
- ret->value = parseExpression(s[2]);
- ret->makeTee(currFunction->getLocalType(ret->index));
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeLocalSet(Element& s) {
- auto ret = allocator.alloc<LocalSet>();
- ret->index = getLocalIndex(*s[1]);
- ret->value = parseExpression(s[2]);
- ret->makeSet();
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeGlobalGet(Element& s) {
- auto ret = allocator.alloc<GlobalGet>();
- ret->name = getGlobalName(*s[1]);
- auto* global = wasm.getGlobalOrNull(ret->name);
- if (!global) {
- throw SParseException("bad global.get name", s);
- }
- ret->type = global->type;
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeGlobalSet(Element& s) {
- auto ret = allocator.alloc<GlobalSet>();
- ret->name = getGlobalName(*s[1]);
- if (wasm.getGlobalOrNull(ret->name) &&
- !wasm.getGlobalOrNull(ret->name)->mutable_) {
- throw SParseException("global.set of immutable", s);
- }
- ret->value = parseExpression(s[2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeBlock(Element& s) {
- if (!currFunction) {
- throw SParseException("block is unallowed outside of functions", s);
- }
- // special-case Block, because Block nesting (in their first element) can be
- // incredibly deep
- auto curr = allocator.alloc<Block>();
- auto* sp = &s;
- // The information we need for the stack of blocks here is the element we are
- // converting, the block we are converting it to, and whether it originally
- // had a name or not (which will be useful later).
- struct Info {
- Element* element;
- Block* block;
- bool hadName;
- };
- std::vector<Info> stack;
- while (1) {
- auto& s = *sp;
- Index i = 1;
- Name sName;
- bool hadName = false;
- if (i < s.size() && s[i]->isStr()) {
- // could be a name or a type
- if (s[i]->dollared() ||
- stringToType(s[i]->str(), true /* allowError */) == Type::none) {
- sName = s[i++]->str();
- hadName = true;
- } else {
- sName = "block";
- }
- } else {
- sName = "block";
- }
- stack.emplace_back(Info{sp, curr, hadName});
- curr->name = nameMapper.pushLabelName(sName);
- // block signature
- curr->type = parseBlockType(s, i);
- if (i >= s.size()) {
- break; // empty block
- }
- auto& first = *s[i];
- if (elementStartsWith(first, BLOCK)) {
- // recurse
- curr = allocator.alloc<Block>();
- if (first.startLoc) {
- currFunction->debugLocations[curr] = getDebugLocation(*first.startLoc);
- }
- sp = &first;
- continue;
- }
- break;
- }
- // we now have a stack of Blocks, with their labels, but no contents yet
- for (int t = int(stack.size()) - 1; t >= 0; t--) {
- auto* sp = stack[t].element;
- auto* curr = stack[t].block;
- auto hadName = stack[t].hadName;
- auto& s = *sp;
- size_t i = 1;
- if (i < s.size()) {
- while (i < s.size() && s[i]->isStr()) {
- i++;
- }
- while (i < s.size() && (elementStartsWith(*s[i], RESULT) ||
- elementStartsWith(*s[i], TYPE))) {
- i++;
- }
- if (t < int(stack.size()) - 1) {
- // first child is one of our recursions
- curr->list.push_back(stack[t + 1].block);
- i++;
- }
- for (; i < s.size(); i++) {
- curr->list.push_back(parseExpression(s[i]));
- }
- }
- nameMapper.popLabelName(curr->name);
- curr->finalize(curr->type);
- // If the block never had a name, and one was not needed in practice (even
- // if one did not exist, perhaps a break targeted it by index), then we can
- // remove the name. Note that we only do this if it never had a name: if it
- // did, we don't want to change anything; we just want to be the same as
- // the code we are loading - if there was no name before, we don't want one
- // now, so that we roundtrip text precisely.
- if (!hadName && !BranchUtils::BranchSeeker::has(curr, curr->name)) {
- curr->name = Name();
- }
- }
- return stack[0].block;
-}
-
-// Similar to block, but the label is handled by the enclosing if (since there
-// might not be a then or else, ick)
-Expression* SExpressionWasmBuilder::makeThenOrElse(Element& s) {
- if (s.size() == 2) {
- return parseExpression(s[1]);
- }
- auto ret = allocator.alloc<Block>();
- for (size_t i = 1; i < s.size(); i++) {
- ret->list.push_back(parseExpression(s[i]));
- }
- ret->finalize();
- return ret;
-}
-
-static Expression* parseConst(IString s, Type type, MixedArena& allocator) {
- const char* str = s.str.data();
- auto ret = allocator.alloc<Const>();
- ret->type = type;
- if (type.isFloat()) {
- if (s == _INFINITY) {
- switch (type.getBasic()) {
- case Type::f32:
- ret->value = Literal(std::numeric_limits<float>::infinity());
- break;
- case Type::f64:
- ret->value = Literal(std::numeric_limits<double>::infinity());
- break;
- default:
- return nullptr;
- }
- return ret;
- }
- if (s == NEG_INFINITY) {
- switch (type.getBasic()) {
- case Type::f32:
- ret->value = Literal(-std::numeric_limits<float>::infinity());
- break;
- case Type::f64:
- ret->value = Literal(-std::numeric_limits<double>::infinity());
- break;
- default:
- return nullptr;
- }
- return ret;
- }
- if (s == _NAN) {
- switch (type.getBasic()) {
- case Type::f32:
- ret->value = Literal(float(std::nan("")));
- break;
- case Type::f64:
- ret->value = Literal(double(std::nan("")));
- break;
- default:
- return nullptr;
- }
- return ret;
- }
- bool negative = str[0] == '-';
- const char* positive = negative ? str + 1 : str;
- if (!negative) {
- if (positive[0] == '+') {
- positive++;
- }
- }
- if (positive[0] == 'n' && positive[1] == 'a' && positive[2] == 'n') {
- const char* modifier = positive[3] == ':' ? positive + 4 : nullptr;
- if (!(modifier ? positive[4] == '0' && positive[5] == 'x' : 1)) {
- throw ParseException("bad nan input: "s + str);
- }
- switch (type.getBasic()) {
- case Type::f32: {
- uint32_t pattern;
- if (modifier) {
- std::istringstream istr(modifier);
- istr >> std::hex >> pattern;
- if (istr.fail()) {
- throw ParseException("invalid f32 format: "s + str);
- }
- pattern |= 0x7f800000U;
- } else {
- pattern = 0x7fc00000U;
- }
- if (negative) {
- pattern |= 0x80000000U;
- }
- if (!std::isnan(bit_cast<float>(pattern))) {
- pattern |= 1U;
- }
- ret->value = Literal(pattern).castToF32();
- break;
- }
- case Type::f64: {
- uint64_t pattern;
- if (modifier) {
- std::istringstream istr(modifier);
- istr >> std::hex >> pattern;
- if (istr.fail()) {
- throw ParseException("invalid f64 format: "s + str);
- }
- pattern |= 0x7ff0000000000000ULL;
- } else {
- pattern = 0x7ff8000000000000UL;
- }
- if (negative) {
- pattern |= 0x8000000000000000ULL;
- }
- if (!std::isnan(bit_cast<double>(pattern))) {
- pattern |= 1ULL;
- }
- ret->value = Literal(pattern).castToF64();
- break;
- }
- default:
- return nullptr;
- }
- // std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
- return ret;
- }
- if (s == NEG_NAN) {
- switch (type.getBasic()) {
- case Type::f32:
- ret->value = Literal(float(-std::nan("")));
- break;
- case Type::f64:
- ret->value = Literal(double(-std::nan("")));
- break;
- default:
- return nullptr;
- }
- // std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
- return ret;
- }
- }
- switch (type.getBasic()) {
- case Type::i32: {
- if ((str[0] == '0' && str[1] == 'x') ||
- (str[0] == '-' && str[1] == '0' && str[2] == 'x')) {
- bool negative = str[0] == '-';
- if (negative) {
- str++;
- }
- std::istringstream istr(str);
- uint32_t temp;
- istr >> std::hex >> temp;
- if (istr.fail()) {
- throw ParseException("invalid i32 format: "s + str);
- }
- ret->value = Literal(negative ? -temp : temp);
- } else {
- std::istringstream istr(str[0] == '-' ? str + 1 : str);
- uint32_t temp;
- istr >> temp;
- if (istr.fail()) {
- throw ParseException("invalid i32 format: "s + str);
- }
- ret->value = Literal(str[0] == '-' ? -temp : temp);
- }
- break;
- }
- case Type::i64: {
- if ((str[0] == '0' && str[1] == 'x') ||
- (str[0] == '-' && str[1] == '0' && str[2] == 'x')) {
- bool negative = str[0] == '-';
- if (negative) {
- str++;
- }
- std::istringstream istr(str);
- uint64_t temp;
- istr >> std::hex >> temp;
- if (istr.fail()) {
- throw ParseException("invalid i64 format: "s + str);
- }
- ret->value = Literal(negative ? -temp : temp);
- } else {
- std::istringstream istr(str[0] == '-' ? str + 1 : str);
- uint64_t temp;
- istr >> temp;
- if (istr.fail()) {
- throw ParseException("invalid i64 format: "s + str);
- }
- ret->value = Literal(str[0] == '-' ? -temp : temp);
- }
- break;
- }
- case Type::f32: {
- char* end;
- ret->value = Literal(strtof(str, &end));
- break;
- }
- case Type::f64: {
- char* end;
- ret->value = Literal(strtod(str, &end));
- break;
- }
- case Type::v128:
- WASM_UNREACHABLE("unexpected const type");
- case Type::none:
- case Type::unreachable: {
- return nullptr;
- }
- }
- if (ret->value.type != type) {
- throw ParseException("parsed type does not match expected type: "s + str);
- }
- return ret;
-}
-
-template<int Lanes>
-static Literal makeLanes(Element& s, MixedArena& allocator, Type lane_t) {
- std::array<Literal, Lanes> lanes;
- for (size_t i = 0; i < Lanes; ++i) {
- Expression* lane = parseConst(s[i + 2]->str(), lane_t, allocator);
- if (lane) {
- lanes[i] = lane->cast<Const>()->value;
- } else {
- throw SParseException("Could not parse v128 lane", s, *s[i + 2]);
- }
- }
- return Literal(lanes);
-}
-
-Expression* SExpressionWasmBuilder::makeConst(Element& s, Type type) {
- if (type != Type::v128) {
- auto ret = parseConst(s[1]->str(), type, allocator);
- if (!ret) {
- throw SParseException("bad const", s, *s[1]);
- }
- return ret;
- }
-
- auto ret = allocator.alloc<Const>();
- Type lane_t = stringToLaneType(s[1]->str().str.data());
- size_t lanes = s.size() - 2;
- switch (lanes) {
- case 2: {
- if (lane_t != Type::i64 && lane_t != Type::f64) {
- throw SParseException("Unexpected v128 literal lane type", s);
- }
- ret->value = makeLanes<2>(s, allocator, lane_t);
- break;
- }
- case 4: {
- if (lane_t != Type::i32 && lane_t != Type::f32) {
- throw SParseException("Unexpected v128 literal lane type", s);
- }
- ret->value = makeLanes<4>(s, allocator, lane_t);
- break;
- }
- case 8: {
- if (lane_t != Type::i32) {
- throw SParseException("Unexpected v128 literal lane type", s);
- }
- ret->value = makeLanes<8>(s, allocator, lane_t);
- break;
- }
- case 16: {
- if (lane_t != Type::i32) {
- throw SParseException("Unexpected v128 literal lane type", s);
- }
- ret->value = makeLanes<16>(s, allocator, lane_t);
- break;
- }
- default:
- throw SParseException("Unexpected number of lanes in v128 literal", s);
- }
- ret->finalize();
- return ret;
-}
-
-static size_t parseMemAttributes(size_t i,
- Element& s,
- Address& offset,
- Address& align,
- bool memory64) {
- // Parse "align=X" and "offset=X" arguments, bailing out on anything else.
- while (!s[i]->isList()) {
- const char* str = s[i]->str().str.data();
- if (strncmp(str, "align", 5) != 0 && strncmp(str, "offset", 6) != 0) {
- return i;
- }
- const char* eq = strchr(str, '=');
- if (!eq) {
- throw SParseException("missing = in memory attribute", s);
- }
- eq++;
- if (*eq == 0) {
- throw SParseException("missing value in memory attribute", s);
- }
- char* endptr;
- uint64_t value = strtoll(eq, &endptr, 10);
- if (*endptr != 0) {
- throw SParseException("bad memory attribute immediate", s);
- }
- if (str[0] == 'a') {
- if (value > std::numeric_limits<uint32_t>::max()) {
- throw SParseException("bad align", s);
- }
- align = value;
- } else if (str[0] == 'o') {
- if (!memory64 && value > std::numeric_limits<uint32_t>::max()) {
- throw SParseException("bad offset", s);
- }
- offset = value;
- } else {
- throw SParseException("bad memory attribute", s);
- }
- i++;
- }
- return i;
-}
-
-bool SExpressionWasmBuilder::hasMemoryIdx(Element& s,
- Index defaultSize,
- Index i) {
- if (s.size() > defaultSize && !s[i]->isList() &&
- strncmp(s[i]->str().str.data(), "align", 5) != 0 &&
- strncmp(s[i]->str().str.data(), "offset", 6) != 0) {
- return true;
- }
- return false;
-}
-
-Expression* SExpressionWasmBuilder::makeLoad(
- Element& s, Type type, bool signed_, int bytes, bool isAtomic) {
- auto* ret = allocator.alloc<Load>();
- ret->type = type;
- ret->bytes = bytes;
- ret->signed_ = signed_;
- ret->offset = 0;
- ret->align = bytes;
- ret->isAtomic = isAtomic;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 2, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory));
- ret->ptr = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeStore(Element& s,
- Type type,
- int bytes,
- bool isAtomic) {
- auto ret = allocator.alloc<Store>();
- ret->bytes = bytes;
- ret->offset = 0;
- ret->align = bytes;
- ret->isAtomic = isAtomic;
- ret->valueType = type;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 3, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory));
- ret->ptr = parseExpression(s[i]);
- ret->value = parseExpression(s[i + 1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s,
- AtomicRMWOp op,
- Type type,
- uint8_t bytes) {
- auto ret = allocator.alloc<AtomicRMW>();
- ret->type = type;
- ret->op = op;
- ret->bytes = bytes;
- ret->offset = 0;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 3, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- Address align = bytes;
- i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory));
- if (align != ret->bytes) {
- throw SParseException("Align of Atomic RMW must match size", s);
- }
- ret->ptr = parseExpression(s[i]);
- ret->value = parseExpression(s[i + 1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s,
- Type type,
- uint8_t bytes) {
- auto ret = allocator.alloc<AtomicCmpxchg>();
- ret->type = type;
- ret->bytes = bytes;
- ret->offset = 0;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 4, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- Address align = ret->bytes;
- i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory));
- if (align != ret->bytes) {
- throw SParseException("Align of Atomic Cmpxchg must match size", s);
- }
- ret->ptr = parseExpression(s[i]);
- ret->expected = parseExpression(s[i + 1]);
- ret->replacement = parseExpression(s[i + 2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, Type type) {
- auto ret = allocator.alloc<AtomicWait>();
- ret->type = Type::i32;
- ret->offset = 0;
- ret->expectedType = type;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 4, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- Address expectedAlign = type == Type::i64 ? 8 : 4;
- Address align = expectedAlign;
- i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory));
- if (align != expectedAlign) {
- throw SParseException("Align of memory.atomic.wait must match size", s);
- }
- ret->ptr = parseExpression(s[i]);
- ret->expected = parseExpression(s[i + 1]);
- ret->timeout = parseExpression(s[i + 2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeAtomicNotify(Element& s) {
- auto ret = allocator.alloc<AtomicNotify>();
- ret->type = Type::i32;
- ret->offset = 0;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 3, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- Address align = 4;
- i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory));
- if (align != 4) {
- throw SParseException("Align of memory.atomic.notify must be 4", s);
- }
- ret->ptr = parseExpression(s[i]);
- ret->notifyCount = parseExpression(s[i + 1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeAtomicFence(Element& s) {
- return allocator.alloc<AtomicFence>();
-}
-
-static uint8_t parseLaneIndex(const Element* s, size_t lanes) {
- const char* str = s->str().str.data();
- char* end;
- auto n = static_cast<unsigned long long>(strtoll(str, &end, 10));
- if (end == str || *end != '\0') {
- throw SParseException("Expected lane index", *s);
- }
- if (n > lanes) {
- throw SParseException(
- "lane index must be less than " + std::to_string(lanes), *s);
- }
- return uint8_t(n);
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s,
- SIMDExtractOp op,
- size_t lanes) {
- auto ret = allocator.alloc<SIMDExtract>();
- ret->op = op;
- ret->index = parseLaneIndex(s[1], lanes);
- ret->vec = parseExpression(s[2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s,
- SIMDReplaceOp op,
- size_t lanes) {
- auto ret = allocator.alloc<SIMDReplace>();
- ret->op = op;
- ret->index = parseLaneIndex(s[1], lanes);
- ret->vec = parseExpression(s[2]);
- ret->value = parseExpression(s[3]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDShuffle(Element& s) {
- auto ret = allocator.alloc<SIMDShuffle>();
- for (size_t i = 0; i < 16; ++i) {
- ret->mask[i] = parseLaneIndex(s[i + 1], 32);
- }
- ret->left = parseExpression(s[17]);
- ret->right = parseExpression(s[18]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDTernary(Element& s,
- SIMDTernaryOp op) {
- auto ret = allocator.alloc<SIMDTernary>();
- ret->op = op;
- ret->a = parseExpression(s[1]);
- ret->b = parseExpression(s[2]);
- ret->c = parseExpression(s[3]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDShift(Element& s, SIMDShiftOp op) {
- auto ret = allocator.alloc<SIMDShift>();
- ret->op = op;
- ret->vec = parseExpression(s[1]);
- ret->shift = parseExpression(s[2]);
- ret->finalize();
- return ret;
-}
-
-Expression*
-SExpressionWasmBuilder::makeSIMDLoad(Element& s, SIMDLoadOp op, int bytes) {
- auto ret = allocator.alloc<SIMDLoad>();
- ret->op = op;
- ret->offset = 0;
- ret->align = bytes;
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 2, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory));
- ret->ptr = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSIMDLoadStoreLane(
- Element& s, SIMDLoadStoreLaneOp op, int bytes) {
- auto* ret = allocator.alloc<SIMDLoadStoreLane>();
- ret->op = op;
- ret->offset = 0;
- ret->align = bytes;
- size_t lanes;
- switch (op) {
- case Load8LaneVec128:
- case Store8LaneVec128:
- lanes = 16;
- break;
- case Load16LaneVec128:
- case Store16LaneVec128:
- lanes = 8;
- break;
- case Load32LaneVec128:
- case Store32LaneVec128:
- lanes = 4;
- break;
- case Load64LaneVec128:
- case Store64LaneVec128:
- lanes = 2;
- break;
- default:
- WASM_UNREACHABLE("Unexpected SIMDLoadStoreLane op");
- }
- Index i = 1;
- Name memory;
- // Check to make sure there are more than the default args & this str isn't
- // the mem attributes
- if (hasMemoryIdx(s, 4, i)) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory));
- ret->index = parseLaneIndex(s[i++], lanes);
- ret->ptr = parseExpression(s[i++]);
- ret->vec = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeMemoryInit(Element& s) {
- auto ret = allocator.alloc<MemoryInit>();
- Index i = 1;
- Name memory;
- if (s.size() > 5) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- ret->segment = getDataSegmentName(*s[i++]);
- ret->dest = parseExpression(s[i++]);
- ret->offset = parseExpression(s[i++]);
- ret->size = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeDataDrop(Element& s) {
- auto ret = allocator.alloc<DataDrop>();
- ret->segment = getDataSegmentName(*s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeMemoryCopy(Element& s) {
- auto ret = allocator.alloc<MemoryCopy>();
- Index i = 1;
- Name destMemory;
- Name sourceMemory;
- if (s.size() > 4) {
- destMemory = getMemoryName(*s[i++]);
- sourceMemory = getMemoryName(*s[i++]);
- } else {
- destMemory = getMemoryNameAtIdx(0);
- sourceMemory = getMemoryNameAtIdx(0);
- }
- ret->destMemory = destMemory;
- ret->sourceMemory = sourceMemory;
- ret->dest = parseExpression(s[i++]);
- ret->source = parseExpression(s[i++]);
- ret->size = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeMemoryFill(Element& s) {
- auto ret = allocator.alloc<MemoryFill>();
- Index i = 1;
- Name memory;
- if (s.size() > 4) {
- memory = getMemoryName(*s[i++]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- ret->memory = memory;
- ret->dest = parseExpression(s[i++]);
- ret->value = parseExpression(s[i++]);
- ret->size = parseExpression(s[i]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makePop(Element& s) {
- auto ret = allocator.alloc<Pop>();
- if (s.size() != 2) {
- throw SParseException("expected 'pop <valtype>'", s);
- }
- ret->type = elementToType(*s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeIf(Element& s) {
- auto ret = allocator.alloc<If>();
- Index i = 1;
- Name sName;
- if (s[i]->dollared()) {
- // the if is labeled
- sName = s[i++]->str();
- } else {
- sName = "if";
- }
- auto label = nameMapper.pushLabelName(sName);
- // if signature
- Type type = parseBlockType(s, i);
- ret->condition = parseExpression(s[i++]);
- if (!elementStartsWith(*s[i], "then")) {
- throw SParseException("expected 'then'", *s[i]);
- }
- ret->ifTrue = parseExpression(*s[i++]);
- if (i < s.size()) {
- if (!elementStartsWith(*s[i], "else")) {
- throw SParseException("expected 'else'", *s[i]);
- }
- ret->ifFalse = parseExpression(*s[i++]);
- }
- ret->finalize(type);
- nameMapper.popLabelName(label);
- // create a break target if we must
- if (BranchUtils::BranchSeeker::has(ret, label)) {
- auto* block = allocator.alloc<Block>();
- block->name = label;
- block->list.push_back(ret);
- block->finalize(type);
- return block;
- }
- return ret;
-}
-
-Expression*
-SExpressionWasmBuilder::makeMaybeBlock(Element& s, size_t i, Type type) {
- Index stopAt = -1;
- if (s.size() == i) {
- return allocator.alloc<Nop>();
- }
- if (s.size() == i + 1) {
- return parseExpression(s[i]);
- }
- auto ret = allocator.alloc<Block>();
- for (; i < s.size() && i < stopAt; i++) {
- ret->list.push_back(parseExpression(s[i]));
- }
- ret->finalize(type);
- // Note that we do not name these implicit/synthetic blocks. They
- // are the effects of syntactic sugar, and nothing can branch to
- // them anyhow.
- return ret;
-}
-
-Type SExpressionWasmBuilder::parseBlockType(Element& s, Index& i) {
- if (s.size() == i) {
- return Type::none;
- }
-
- // TODO(sbc): Remove support for old result syntax (bare streing) once the
- // spec tests are updated.
- if (s[i]->isStr()) {
- return stringToType(s[i++]->str());
- }
-
- Element* results = s[i];
- IString id = (*results)[0]->str();
- std::optional<Signature> usedType;
- if (id == TYPE) {
- auto type = parseHeapType(*(*results)[1]);
- if (!type.isSignature()) {
- throw SParseException("unexpected non-function type", s);
- }
- usedType = type.getSignature();
- if (usedType->params != Type::none) {
- throw SParseException("block input values are not yet supported", s);
- }
- i++;
- results = s[i];
- id = (*results)[0]->str();
- }
-
- if (id == RESULT) {
- i++;
- auto type = Type(parseResults(*results));
- if (usedType && usedType->results != type) {
- throw SParseException("results do not match type", s);
- }
- return type;
- }
-
- if (usedType && usedType->results != Type::none) {
- throw SParseException("results do not match type", s);
- }
- return Type::none;
-}
-
-Expression* SExpressionWasmBuilder::makeLoop(Element& s) {
- auto ret = allocator.alloc<Loop>();
- Index i = 1;
- Name sName;
- if (s.size() > i && s[i]->dollared()) {
- sName = s[i++]->str();
- } else {
- sName = "loop-in";
- }
- ret->name = nameMapper.pushLabelName(sName);
- ret->type = parseBlockType(s, i);
- ret->body = makeMaybeBlock(s, i, ret->type);
- nameMapper.popLabelName(ret->name);
- ret->finalize(ret->type);
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeCall(Element& s, bool isReturn) {
- auto target = getFunctionName(*s[1]);
- auto ret = allocator.alloc<Call>();
- ret->target = target;
- ret->type = getFunctionType(ret->target, s).getSignature().results;
- parseCallOperands(s, 2, s.size(), ret);
- ret->isReturn = isReturn;
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s,
- bool isReturn) {
- if (wasm.tables.empty()) {
- throw SParseException("no tables", s);
- }
- Index i = 1;
- auto ret = allocator.alloc<CallIndirect>();
- if (s[i]->isStr()) {
- ret->table = s[i++]->str();
- } else {
- ret->table = wasm.tables.front()->name;
- }
- HeapType callType;
- i = parseTypeUse(s, i, callType);
- ret->heapType = callType;
- parseCallOperands(s, i, s.size() - 1, ret);
- ret->target = parseExpression(s[s.size() - 1]);
- ret->isReturn = isReturn;
- ret->finalize();
- return ret;
-}
-
-Name SExpressionWasmBuilder::getLabel(Element& s, LabelType labelType) {
- if (s.dollared()) {
- return nameMapper.sourceToUnique(s.str());
- } else {
- // offset, break to nth outside label
- uint64_t offset;
- try {
- offset = std::stoll(s.toString(), nullptr, 0);
- } catch (std::invalid_argument&) {
- throw SParseException("invalid break offset", s);
- } catch (std::out_of_range&) {
- throw SParseException("out of range break offset", s);
- }
- if (offset > nameMapper.labelStack.size()) {
- throw SParseException("invalid label", s);
- }
- if (offset == nameMapper.labelStack.size()) {
- if (labelType == LabelType::Break) {
- // a break to the function's scope. this means we need an automatic
- // block, with a name
- brokeToAutoBlock = true;
- return FAKE_RETURN;
- }
- // This is a delegate that delegates to the caller
- return DELEGATE_CALLER_TARGET;
- }
- return nameMapper.labelStack[nameMapper.labelStack.size() - 1 - offset];
- }
-}
-
-Expression* SExpressionWasmBuilder::makeBreak(Element& s, bool isConditional) {
- auto ret = allocator.alloc<Break>();
- size_t i = 1;
- ret->name = getLabel(*s[i]);
- i++;
- if (i == s.size()) {
- return ret;
- }
- if (isConditional) {
- if (i + 1 < s.size()) {
- ret->value = parseExpression(s[i]);
- i++;
- }
- ret->condition = parseExpression(s[i]);
- } else {
- ret->value = parseExpression(s[i]);
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeBreakTable(Element& s) {
- auto ret = allocator.alloc<Switch>();
- size_t i = 1;
- while (!s[i]->isList()) {
- ret->targets.push_back(getLabel(*s[i++]));
- }
- if (ret->targets.size() == 0) {
- throw SParseException("switch with no targets", s);
- }
- ret->default_ = ret->targets.back();
- ret->targets.pop_back();
- ret->condition = parseExpression(s[i++]);
- if (i < s.size()) {
- ret->value = ret->condition;
- ret->condition = parseExpression(s[i++]);
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeReturn(Element& s) {
- auto ret = allocator.alloc<Return>();
- if (s.size() >= 2) {
- ret->value = parseExpression(s[1]);
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefNull(Element& s) {
- if (s.size() != 2) {
- throw SParseException("invalid heap type reference", s);
- }
- auto ret = allocator.alloc<RefNull>();
- // The heap type may be just "func", that is, the whole thing is just
- // (ref.null func), or it may be the name of a defined type, such as
- // (ref.null $struct.FOO)
- if (s[1]->dollared()) {
- ret->finalize(parseHeapType(*s[1]).getBottom());
- } else {
- ret->finalize(stringToHeapType(s[1]->str()).getBottom());
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefIsNull(Element& s) {
- auto ret = allocator.alloc<RefIsNull>();
- ret->value = parseExpression(s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefFunc(Element& s) {
- auto func = getFunctionName(*s[1]);
- auto ret = allocator.alloc<RefFunc>();
- ret->func = func;
- // To support typed function refs, we give the reference not just a general
- // funcref, but a specific subtype with the actual signature.
- ret->finalize(Type(getFunctionType(func, s), NonNullable));
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefEq(Element& s) {
- auto ret = allocator.alloc<RefEq>();
- ret->left = parseExpression(s[1]);
- ret->right = parseExpression(s[2]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeTableGet(Element& s) {
- auto tableName = s[1]->str();
- auto* index = parseExpression(s[2]);
- auto* table = wasm.getTableOrNull(tableName);
- if (!table) {
- throw SParseException("invalid table name in table.get", s);
- }
- return Builder(wasm).makeTableGet(tableName, index, table->type);
-}
-
-Expression* SExpressionWasmBuilder::makeTableSet(Element& s) {
- auto tableName = s[1]->str();
- auto* table = wasm.getTableOrNull(tableName);
- if (!table) {
- throw SParseException("invalid table name in table.set", s);
- }
- auto* index = parseExpression(s[2]);
- auto* value = parseExpression(s[3]);
- return Builder(wasm).makeTableSet(tableName, index, value);
-}
-
-Expression* SExpressionWasmBuilder::makeTableSize(Element& s) {
- auto tableName = s[1]->str();
- auto* table = wasm.getTableOrNull(tableName);
- if (!table) {
- throw SParseException("invalid table name in table.size", s);
- }
- return Builder(wasm).makeTableSize(tableName);
-}
-
-Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) {
- auto tableName = s[1]->str();
- auto* table = wasm.getTableOrNull(tableName);
- if (!table) {
- throw SParseException("invalid table name in table.grow", s);
- }
- auto* value = parseExpression(s[2]);
- if (!value->type.isRef()) {
- throw SParseException("only reference types are valid for tables", s);
- }
- auto* delta = parseExpression(s[3]);
- return Builder(wasm).makeTableGrow(tableName, value, delta);
-}
-
-Expression* SExpressionWasmBuilder::makeTableFill(Element& s) {
- auto tableName = s[1]->str();
- auto* table = wasm.getTableOrNull(tableName);
- if (!table) {
- throw SParseException("invalid table name in table.fill", s);
- }
- auto* dest = parseExpression(s[2]);
- auto* value = parseExpression(s[3]);
- auto* size = parseExpression(s[4]);
- return Builder(wasm).makeTableFill(tableName, dest, value, size);
-}
-
-Expression* SExpressionWasmBuilder::makeTableCopy(Element& s) {
- auto destTableName = s[1]->str();
- auto* destTable = wasm.getTableOrNull(destTableName);
- if (!destTable) {
- throw SParseException("invalid dest table name in table.copy", s);
- }
- auto sourceTableName = s[2]->str();
- auto* sourceTable = wasm.getTableOrNull(sourceTableName);
- if (!sourceTable) {
- throw SParseException("invalid source table name in table.copy", s);
- }
- auto* dest = parseExpression(s[3]);
- auto* source = parseExpression(s[4]);
- auto* size = parseExpression(s[5]);
- return Builder(wasm).makeTableCopy(
- dest, source, size, destTableName, sourceTableName);
-}
-
-// try can be either in the form of try-catch or try-delegate.
-// try-catch is written in the folded wast format as
-// (try
-// (do
-// ...
-// )
-// (catch $e
-// ...
-// )
-// ...
-// (catch_all
-// ...
-// )
-// )
-// Any number of catch blocks can exist, including none. Zero or one catch_all
-// block can exist, and if it does, it should be at the end. There should be at
-// least one catch or catch_all body per try.
-//
-// try-delegate is written in the folded format as
-// (try
-// (do
-// ...
-// )
-// (delegate $label)
-// )
-Expression* SExpressionWasmBuilder::makeTry(Element& s) {
- auto ret = allocator.alloc<Try>();
- Index i = 1;
- Name sName;
- if (s[i]->dollared()) {
- // the try is labeled
- sName = s[i++]->str();
- } else {
- sName = "try";
- }
- ret->name = nameMapper.pushLabelName(sName);
- Type type = parseBlockType(s, i); // signature
-
- if (!elementStartsWith(*s[i], "do")) {
- throw SParseException("try body should start with 'do'", s, *s[i]);
- }
- ret->body = makeMaybeBlock(*s[i++], 1, type);
-
- while (i < s.size() && elementStartsWith(*s[i], "catch")) {
- Element& inner = *s[i++];
- if (inner.size() < 2) {
- throw SParseException("invalid catch block", s, inner);
- }
- Name tag = getTagName(*inner[1]);
- if (!wasm.getTagOrNull(tag)) {
- throw SParseException("bad tag name", s, inner);
- }
- ret->catchTags.push_back(tag);
- ret->catchBodies.push_back(makeMaybeBlock(inner, 2, type));
- }
-
- if (i < s.size() && elementStartsWith(*s[i], "catch_all")) {
- ret->catchBodies.push_back(makeMaybeBlock(*s[i++], 1, type));
- }
-
- // 'delegate' cannot target its own try. So we pop the name here.
- nameMapper.popLabelName(ret->name);
-
- if (i < s.size() && elementStartsWith(*s[i], "delegate")) {
- Element& inner = *s[i++];
- if (inner.size() != 2) {
- throw SParseException("invalid delegate", s, inner);
- }
- ret->delegateTarget = getLabel(*inner[1], LabelType::Exception);
- }
-
- if (i != s.size()) {
- throw SParseException(
- "there should be at most one catch_all block at the end", s);
- }
-
- ret->finalize(type);
-
- // create a break target if we must
- if (BranchUtils::BranchSeeker::has(ret, ret->name)) {
- auto* block = allocator.alloc<Block>();
- // We create a different name for the wrapping block, because try's name can
- // be used by internal delegates
- block->name = nameMapper.pushLabelName(sName);
- // For simplicity, try's name can only be targeted by delegates and
- // rethrows. Make the branches target the new wrapping block instead.
- BranchUtils::replaceBranchTargets(ret, ret->name, block->name);
- block->list.push_back(ret);
- nameMapper.popLabelName(block->name);
- block->finalize(type);
- return block;
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeTryTable(Element& s) {
- auto ret = allocator.alloc<TryTable>();
- Index i = 1;
- Name sName;
- if (s.size() > i && s[i]->dollared()) {
- // the try_table is labeled
- sName = s[i++]->str();
- } else {
- sName = "try_table";
- }
- auto label = nameMapper.pushLabelName(sName);
- Type type = parseBlockType(s, i); // signature
-
- while (i < s.size()) {
- Element& inner = *s[i];
-
- if (elementStartsWith(inner, "catch") ||
- elementStartsWith(inner, "catch_ref")) {
- bool isRef = elementStartsWith(inner, "catch_ref");
- if (inner.size() < 3) {
- throw SParseException("invalid catch/catch_ref block", s, inner);
- }
- Name tag = getTagName(*inner[1]);
- if (!wasm.getTagOrNull(tag)) {
- throw SParseException("bad tag name", s, inner);
- }
- ret->catchTags.push_back(tag);
- ret->catchDests.push_back(getLabel(*inner[2]));
- ret->catchRefs.push_back(isRef);
- } else if (elementStartsWith(inner, "catch_all") ||
- elementStartsWith(inner, "catch_all_ref")) {
- bool isRef = elementStartsWith(inner, "catch_all_ref");
- if (inner.size() < 2) {
- throw SParseException(
- "invalid catch_all/catch_all_ref block", s, inner);
- }
- ret->catchTags.push_back(Name());
- ret->catchDests.push_back(getLabel(*inner[1]));
- ret->catchRefs.push_back(isRef);
- } else {
- break;
- }
- i++;
- }
-
- ret->body = makeMaybeBlock(s, i, type);
- ret->finalize(type, &wasm);
- nameMapper.popLabelName(label);
- // create a break target if we must
- if (BranchUtils::BranchSeeker::has(ret, label)) {
- auto* block = allocator.alloc<Block>();
- block->name = label;
- block->list.push_back(ret);
- block->finalize(type);
- return block;
- }
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeThrow(Element& s) {
- auto ret = allocator.alloc<Throw>();
- Index i = 1;
-
- ret->tag = getTagName(*s[i++]);
- if (!wasm.getTagOrNull(ret->tag)) {
- throw SParseException("bad tag name", s, *s[i]);
- }
- for (; i < s.size(); i++) {
- ret->operands.push_back(parseExpression(s[i]));
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRethrow(Element& s) {
- auto ret = allocator.alloc<Rethrow>();
- ret->target = getLabel(*s[1], LabelType::Exception);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeThrowRef(Element& s) {
- auto ret = allocator.alloc<ThrowRef>();
- ret->exnref = parseExpression(s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeTupleMake(Element& s) {
- auto ret = allocator.alloc<TupleMake>();
- size_t arity = std::stoll(s[1]->toString());
- if (arity != s.size() - 2) {
- throw SParseException("unexpected number of elements", s, *s[1]);
- }
- parseCallOperands(s, 2, s.size(), ret);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) {
- auto ret = allocator.alloc<TupleExtract>();
- size_t arity = std::stoll(s[1]->toString());
- ret->index = parseIndex(*s[2]);
- ret->tuple = parseExpression(s[3]);
- if (ret->tuple->type != Type::unreachable) {
- if (arity != ret->tuple->type.size()) {
- throw SParseException("Unexpected tuple.extract arity", s, *s[1]);
- }
- if (ret->index >= ret->tuple->type.size()) {
- throw SParseException("Bad index on tuple.extract", s, *s[2]);
- }
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeTupleDrop(Element& s) {
- size_t arity = std::stoll(s[1]->toString());
- auto ret = allocator.alloc<Drop>();
- ret->value = parseExpression(s[2]);
- if (ret->value->type != Type::unreachable &&
- ret->value->type.size() != arity) {
- throw SParseException("unexpected tuple.drop arity", s, *s[1]);
- }
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeCallRef(Element& s, bool isReturn) {
- HeapType sigType = parseHeapType(*s[1]);
- std::vector<Expression*> operands;
- parseOperands(s, 2, s.size() - 1, operands);
- auto* target = parseExpression(s[s.size() - 1]);
-
- if (!sigType.isSignature()) {
- throw SParseException(
- std::string(isReturn ? "return_call_ref" : "call_ref") +
- " type annotation should be a signature",
- s);
- }
- if (!Type::isSubType(target->type, Type(sigType, Nullable))) {
- throw SParseException(
- std::string(isReturn ? "return_call_ref" : "call_ref") +
- " target should match expected type",
- s);
- }
- return Builder(wasm).makeCallRef(
- target, operands, sigType.getSignature().results, isReturn);
-}
-
-Expression* SExpressionWasmBuilder::makeContBind(Element& s) {
- auto ret = allocator.alloc<ContBind>();
-
- ret->contTypeBefore = parseHeapType(*s[1]);
- if (!ret->contTypeBefore.isContinuation()) {
- throw ParseException("expected continuation type", s[1]->line, s[1]->col);
- }
- ret->contTypeAfter = parseHeapType(*s[2]);
- if (!ret->contTypeAfter.isContinuation()) {
- throw ParseException("expected continuation type", s[2]->line, s[2]->col);
- }
-
- Index i = 3;
- while (i < s.size() - 1) {
- ret->operands.push_back(parseExpression(s[i++]));
- }
-
- ret->cont = parseExpression(s[i]);
-
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeContNew(Element& s) {
- auto ret = allocator.alloc<ContNew>();
-
- ret->contType = parseHeapType(*s[1]);
- if (!ret->contType.isContinuation()) {
- throw ParseException("expected continuation type", s[1]->line, s[1]->col);
- }
-
- ret->func = parseExpression(s[2]);
-
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeResume(Element& s) {
- auto ret = allocator.alloc<Resume>();
-
- ret->contType = parseHeapType(*s[1]);
- if (!ret->contType.isContinuation()) {
- throw ParseException("expected continuation type", s[1]->line, s[1]->col);
- }
-
- Index i = 2;
- while (i < s.size() && elementStartsWith(*s[i], "tag")) {
- Element& inner = *s[i++];
- if (inner.size() < 3) {
- throw ParseException("invalid tag block", inner.line, inner.col);
- }
- Name tag = getTagName(*inner[1]);
- if (!wasm.getTagOrNull(tag)) {
- throw ParseException("bad tag name", inner[1]->line, inner[1]->col);
- }
- ret->handlerTags.push_back(tag);
- ret->handlerBlocks.push_back(getLabel(*inner[2]));
- }
-
- while (i < s.size() - 1) {
- ret->operands.push_back(parseExpression(s[i++]));
- }
-
- ret->cont = parseExpression(s[i]);
-
- ret->finalize(&wasm);
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeSuspend(Element& s) {
- auto ret = allocator.alloc<Suspend>();
-
- ret->tag = getTagName(*s[1]);
-
- Index i = 2;
- while (i < s.size()) {
- ret->operands.push_back(parseExpression(s[i++]));
- }
-
- ret->finalize(&wasm);
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefI31(Element& s) {
- auto ret = allocator.alloc<RefI31>();
- ret->value = parseExpression(s[1]);
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) {
- auto ret = allocator.alloc<I31Get>();
- ret->i31 = parseExpression(s[1]);
- ret->signed_ = signed_;
- ret->finalize();
- return ret;
-}
-
-Expression* SExpressionWasmBuilder::makeRefTest(Element& s) {
- Type castType = elementToType(*s[1]);
- auto* ref = parseExpression(*s[2]);
- return Builder(wasm).makeRefTest(ref, castType);
-}
-
-Expression* SExpressionWasmBuilder::makeRefCast(Element& s) {
- Type castType = elementToType(*s[1]);
- auto* ref = parseExpression(*s[2]);
- return Builder(wasm).makeRefCast(ref, castType);
-}
-
-Expression* SExpressionWasmBuilder::makeBrOnNull(Element& s, bool onFail) {
- int i = 1;
- auto name = getLabel(*s[i++]);
- auto* ref = parseExpression(*s[i]);
- auto op = onFail ? BrOnNonNull : BrOnNull;
- return Builder(wasm).makeBrOn(op, name, ref);
-}
-
-Expression* SExpressionWasmBuilder::makeBrOnCast(Element& s, bool onFail) {
- int i = 1;
- auto name = getLabel(*s[i++]);
- auto inputType = elementToType(*s[i++]);
- auto castType = elementToType(*s[i++]);
- if (!Type::isSubType(castType, inputType)) {
- throw SParseException(
- "br_on_cast* cast type must be a subtype of its input type", s);
- }
- auto* ref = parseExpression(*s[i]);
- if (!Type::isSubType(ref->type, inputType)) {
- throw SParseException("br_on_cast* ref type does not match expected type",
- s);
- }
- auto op = onFail ? BrOnCastFail : BrOnCast;
- return Builder(wasm).makeBrOn(op, name, ref, castType);
-}
-
-Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) {
- auto heapType = parseHeapType(*s[1]);
- auto numOperands = s.size() - 2;
- if (default_ && numOperands > 0) {
- throw SParseException("arguments provided for struct.new", s);
- }
- std::vector<Expression*> operands;
- operands.resize(numOperands);
- for (Index i = 0; i < numOperands; i++) {
- operands[i] = parseExpression(*s[i + 2]);
- }
- return Builder(wasm).makeStructNew(heapType, operands);
-}
-
-Index SExpressionWasmBuilder::getStructIndex(Element& type, Element& field) {
- if (field.dollared()) {
- auto name = field.str();
- auto index = typeIndices[type.toString()];
- auto struct_ = types[index].getStruct();
- auto& fields = struct_.fields;
- const auto& names = fieldNames[index];
- for (Index i = 0; i < fields.size(); i++) {
- auto it = names.find(i);
- if (it != names.end() && it->second == name) {
- return i;
- }
- }
- throw SParseException("bad struct field name", field);
- }
- // this is a numeric index
- return parseIndex(field);
-}
-
-Expression* SExpressionWasmBuilder::makeStructGet(Element& s, bool signed_) {
- auto heapType = parseHeapType(*s[1]);
- if (!heapType.isStruct()) {
- throw SParseException("bad struct heap type", s);
- }
- auto index = getStructIndex(*s[1], *s[2]);
- auto type = heapType.getStruct().fields[index].type;
- auto ref = parseExpression(*s[3]);
- validateHeapTypeUsingChild(ref, heapType, s);
- return Builder(wasm).makeStructGet(index, ref, type, signed_);
-}
-
-Expression* SExpressionWasmBuilder::makeStructSet(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- if (!heapType.isStruct()) {
- throw SParseException("bad struct heap type", s);
- }
- auto index = getStructIndex(*s[1], *s[2]);
- auto ref = parseExpression(*s[3]);
- validateHeapTypeUsingChild(ref, heapType, s);
- auto value = parseExpression(*s[4]);
- return Builder(wasm).makeStructSet(index, ref, value);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayNew(Element& s, bool default_) {
- auto heapType = parseHeapType(*s[1]);
- Expression* init = nullptr;
- size_t i = 2;
- if (!default_) {
- init = parseExpression(*s[i++]);
- }
- auto* size = parseExpression(*s[i++]);
- return Builder(wasm).makeArrayNew(heapType, size, init);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayNewData(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- Name seg = getDataSegmentName(*s[2]);
- Expression* offset = parseExpression(*s[3]);
- Expression* size = parseExpression(*s[4]);
- return Builder(wasm).makeArrayNewData(heapType, seg, offset, size);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayNewElem(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- Name seg = getElemSegmentName(*s[2]);
- Expression* offset = parseExpression(*s[3]);
- Expression* size = parseExpression(*s[4]);
- return Builder(wasm).makeArrayNewElem(heapType, seg, offset, size);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayNewFixed(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- if ((size_t)parseIndex(*s[2]) != s.size() - 3) {
- throw SParseException("wrong number of elements in array", s);
- }
- std::vector<Expression*> values;
- for (size_t i = 3; i < s.size(); ++i) {
- values.push_back(parseExpression(*s[i]));
- }
- return Builder(wasm).makeArrayNewFixed(heapType, values);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayGet(Element& s, bool signed_) {
- auto heapType = parseHeapType(*s[1]);
- if (!heapType.isArray()) {
- throw SParseException("bad array heap type", s);
- }
- auto ref = parseExpression(*s[2]);
- auto type = heapType.getArray().element.type;
- validateHeapTypeUsingChild(ref, heapType, s);
- auto index = parseExpression(*s[3]);
- return Builder(wasm).makeArrayGet(ref, index, type, signed_);
-}
-
-Expression* SExpressionWasmBuilder::makeArraySet(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- auto ref = parseExpression(*s[2]);
- validateHeapTypeUsingChild(ref, heapType, s);
- auto index = parseExpression(*s[3]);
- auto value = parseExpression(*s[4]);
- return Builder(wasm).makeArraySet(ref, index, value);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayLen(Element& s) {
- auto ref = parseExpression(*s[1]);
- return Builder(wasm).makeArrayLen(ref);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayCopy(Element& s) {
- auto destHeapType = parseHeapType(*s[1]);
- auto srcHeapType = parseHeapType(*s[2]);
- auto destRef = parseExpression(*s[3]);
- validateHeapTypeUsingChild(destRef, destHeapType, s);
- auto destIndex = parseExpression(*s[4]);
- auto srcRef = parseExpression(*s[5]);
- validateHeapTypeUsingChild(srcRef, srcHeapType, s);
- auto srcIndex = parseExpression(*s[6]);
- auto length = parseExpression(*s[7]);
- return Builder(wasm).makeArrayCopy(
- destRef, destIndex, srcRef, srcIndex, length);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayFill(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- auto ref = parseExpression(*s[2]);
- validateHeapTypeUsingChild(ref, heapType, s);
- auto index = parseExpression(*s[3]);
- auto value = parseExpression(*s[4]);
- auto size = parseExpression(*s[5]);
- return Builder(wasm).makeArrayFill(ref, index, value, size);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayInitData(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- auto seg = getDataSegmentName(*s[2]);
- auto ref = parseExpression(*s[3]);
- validateHeapTypeUsingChild(ref, heapType, s);
- auto index = parseExpression(*s[4]);
- auto offset = parseExpression(*s[5]);
- auto size = parseExpression(*s[6]);
- return Builder(wasm).makeArrayInitData(seg, ref, index, offset, size);
-}
-
-Expression* SExpressionWasmBuilder::makeArrayInitElem(Element& s) {
- auto heapType = parseHeapType(*s[1]);
- auto seg = getElemSegmentName(*s[2]);
- auto ref = parseExpression(*s[3]);
- validateHeapTypeUsingChild(ref, heapType, s);
- auto index = parseExpression(*s[4]);
- auto offset = parseExpression(*s[5]);
- auto size = parseExpression(*s[6]);
- return Builder(wasm).makeArrayInitElem(seg, ref, index, offset, size);
-}
-
-Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) {
- auto* value = parseExpression(s[1]);
- if (!value->type.isRef() && value->type != Type::unreachable) {
- throw SParseException("ref.as child must be a ref", s);
- }
- return Builder(wasm).makeRefAs(op, value);
-}
-
-Expression* SExpressionWasmBuilder::makeStringNew(Element& s, StringNewOp op) {
- if (op == StringNewLossyUTF8Array || op == StringNewWTF16Array) {
- auto* start = parseExpression(s[2]);
- auto* end = parseExpression(s[3]);
- return Builder(wasm).makeStringNew(op, parseExpression(s[1]), start, end);
- } else if (op == StringNewFromCodePoint) {
- return Builder(wasm).makeStringNew(op, parseExpression(s[1]));
- } else {
- throw SParseException("bad string.new op", s);
- }
-}
-
-Expression* SExpressionWasmBuilder::makeStringConst(Element& s) {
- std::vector<char> data;
- stringToBinary(*s[1], s[1]->str().str, data);
- Name str = std::string_view(data.data(), data.size());
- // Re-encode from WTF-8 to WTF-16.
- std::stringstream wtf16;
- if (!String::convertWTF8ToWTF16(wtf16, str.str)) {
- throw SParseException("invalid string constant", s);
- }
- // TODO: Use wtf16.view() once we have C++20.
- return Builder(wasm).makeStringConst(wtf16.str());
-}
-
-Expression* SExpressionWasmBuilder::makeStringMeasure(Element& s,
- StringMeasureOp op) {
- return Builder(wasm).makeStringMeasure(op, parseExpression(s[1]));
-}
-
-Expression* SExpressionWasmBuilder::makeStringEncode(Element& s,
- StringEncodeOp op) {
-
- return Builder(wasm).makeStringEncode(
- op, parseExpression(s[1]), parseExpression(s[2]), parseExpression(s[3]));
-}
-
-Expression* SExpressionWasmBuilder::makeStringConcat(Element& s) {
- return Builder(wasm).makeStringConcat(parseExpression(s[1]),
- parseExpression(s[2]));
-}
-
-Expression* SExpressionWasmBuilder::makeStringEq(Element& s, StringEqOp op) {
- return Builder(wasm).makeStringEq(
- op, parseExpression(s[1]), parseExpression(s[2]));
-}
-
-Expression* SExpressionWasmBuilder::makeStringWTF16Get(Element& s) {
- return Builder(wasm).makeStringWTF16Get(parseExpression(s[1]),
- parseExpression(s[2]));
-}
-
-Expression* SExpressionWasmBuilder::makeStringSliceWTF(Element& s) {
- return Builder(wasm).makeStringSliceWTF(
- parseExpression(s[1]), parseExpression(s[2]), parseExpression(s[3]));
-}
-
-// converts an s-expression string representing binary data into an output
-// sequence of raw bytes this appends to data, which may already contain
-// content.
-void SExpressionWasmBuilder::stringToBinary(Element& s,
- std::string_view str,
- std::vector<char>& data) {
- auto originalSize = data.size();
- data.resize(originalSize + str.size());
- char* write = data.data() + originalSize;
- const char* end = str.data() + str.size();
- for (const char* input = str.data(); input < end;) {
- if (input[0] == '\\') {
- if (input + 1 >= end) {
- throw SParseException("Unterminated escape sequence", s);
- }
- if (input[1] == 't') {
- *write++ = '\t';
- input += 2;
- continue;
- } else if (input[1] == 'n') {
- *write++ = '\n';
- input += 2;
- continue;
- } else if (input[1] == 'r') {
- *write++ = '\r';
- input += 2;
- continue;
- } else if (input[1] == '"') {
- *write++ = '"';
- input += 2;
- continue;
- } else if (input[1] == '\'') {
- *write++ = '\'';
- input += 2;
- continue;
- } else if (input[1] == '\\') {
- *write++ = '\\';
- input += 2;
- continue;
- } else {
- if (input + 2 >= end) {
- throw SParseException("Unterminated escape sequence", s);
- }
- *write++ = (char)(unhex(input[1]) * 16 + unhex(input[2]));
- input += 3;
- continue;
- }
- }
- *write++ = input[0];
- input++;
- }
- assert(write >= data.data());
- size_t actual = write - data.data();
- assert(actual <= data.size());
- data.resize(actual);
-}
-
-Index SExpressionWasmBuilder::parseMemoryIndex(
- Element& s, Index i, std::unique_ptr<Memory>& memory) {
- if (i < s.size() && s[i]->isStr()) {
- if (s[i]->str() == "i64") {
- i++;
- memory->indexType = Type::i64;
- } else if (s[i]->str() == "i32") {
- i++;
- memory->indexType = Type::i32;
- }
- }
- return i;
-}
-
-Index SExpressionWasmBuilder::parseMemoryLimits(
- Element& s, Index i, std::unique_ptr<Memory>& memory) {
- i = parseMemoryIndex(s, i, memory);
- if (i == s.size()) {
- throw SParseException("missing memory limits", s);
- }
- auto initElem = s[i++];
- memory->initial = getAddress(initElem);
- if (!memory->is64()) {
- checkAddress(memory->initial, "excessive memory init", initElem);
- }
- if (i == s.size()) {
- memory->max = Memory::kUnlimitedSize;
- } else {
- auto maxElem = s[i++];
- memory->max = getAddress(maxElem);
- if (!memory->is64() && memory->max > Memory::kMaxSize32) {
- throw SParseException("total memory must be <= 4GB", s, *maxElem);
- }
- }
- return i;
-}
-
-void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) {
- auto memory = std::make_unique<Memory>();
- memory->shared = *s[s.size() - 1] == SHARED;
- Index i = 1;
- if (s[i]->dollared()) {
- memory->setExplicitName(s[i++]->str());
- } else {
- memory->name = Name::fromInt(memoryCounter++);
- }
- memoryNames.push_back(memory->name);
-
- i = parseMemoryIndex(s, i, memory);
- Name importModule, importBase;
- if (s[i]->isList()) {
- auto& inner = *s[i];
- if (elementStartsWith(inner, EXPORT)) {
- auto ex = std::make_unique<Export>();
- ex->name = inner[1]->str();
- ex->value = memory->name;
- ex->kind = ExternalKind::Memory;
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", inner);
- }
- wasm.addExport(ex.release());
- i++;
- } else if (elementStartsWith(inner, IMPORT)) {
- memory->module = inner[1]->str();
- memory->base = inner[2]->str();
- i++;
- } else {
- if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) {
- throw SParseException("bad import ending", inner);
- }
- // (memory (data ..)) format
- auto j = parseMemoryIndex(inner, 1, memory);
- auto offset = allocator.alloc<Const>();
- if (memory->is64()) {
- offset->set(Literal(int64_t(0)));
- } else {
- offset->set(Literal(int32_t(0)));
- }
- auto segName = Name::fromInt(dataCounter++);
- auto seg = Builder::makeDataSegment(segName, memory->name, false, offset);
- dataSegmentNames.push_back(segName);
- parseInnerData(inner, j, seg);
- memory->initial = seg->data.size();
- wasm.addDataSegment(std::move(seg));
- wasm.addMemory(std::move(memory));
- return;
- }
- }
- i = parseMemoryLimits(s, i, memory);
- if (i + int(memory->shared) != s.size()) {
- throw SParseException("expected end of memory", *s[i]);
- }
- wasm.addMemory(std::move(memory));
-}
-
-void SExpressionWasmBuilder::parseData(Element& s) {
- Index i = 1;
- Name name = Name::fromInt(dataCounter++);
- bool hasExplicitName = false;
- Name memory;
- bool isPassive = true;
- Expression* offset = nullptr;
-
- if (s[i]->isStr() && s[i]->dollared()) {
- name = s[i++]->str();
- hasExplicitName = true;
- }
- dataSegmentNames.push_back(name);
-
- if (s[i]->isList()) {
- // Optional (memory <memoryidx>)
- if (elementStartsWith(s[i], MEMORY)) {
- auto& inner = *s[i++];
- memory = getMemoryName(*inner[1]);
- } else {
- memory = getMemoryNameAtIdx(0);
- }
- // Offset expression (offset (<expr>)) | (<expr>)
- auto& inner = *s[i++];
- if (elementStartsWith(inner, OFFSET)) {
- offset = parseExpression(inner[1]);
- } else {
- offset = parseExpression(inner);
- }
- isPassive = false;
- }
-
- auto seg = Builder::makeDataSegment(name, memory, isPassive, offset);
- seg->hasExplicitName = hasExplicitName;
- parseInnerData(s, i, seg);
- wasm.addDataSegment(std::move(seg));
-}
-
-void SExpressionWasmBuilder::parseInnerData(Element& s,
- Index i,
- std::unique_ptr<DataSegment>& seg) {
- std::vector<char> data;
- while (i < s.size()) {
- std::string_view input = s[i++]->str().str;
- stringToBinary(s, input, data);
- }
- seg->data.resize(data.size());
- std::copy_n(data.data(), data.size(), seg->data.begin());
-}
-
-void SExpressionWasmBuilder::parseExport(Element& s) {
- std::unique_ptr<Export> ex = std::make_unique<Export>();
- std::vector<char> nameBytes;
- stringToBinary(*s[1], s[1]->str().str, nameBytes);
- ex->name = std::string(nameBytes.data(), nameBytes.size());
- if (s[2]->isList()) {
- auto& inner = *s[2];
- if (elementStartsWith(inner, FUNC)) {
- ex->kind = ExternalKind::Function;
- ex->value = getFunctionName(*inner[1]);
- } else if (elementStartsWith(inner, MEMORY)) {
- ex->kind = ExternalKind::Memory;
- ex->value = inner[1]->str();
- } else if (elementStartsWith(inner, TABLE)) {
- ex->kind = ExternalKind::Table;
- ex->value = getTableName(*inner[1]);
- } else if (elementStartsWith(inner, GLOBAL)) {
- ex->kind = ExternalKind::Global;
- ex->value = getGlobalName(*inner[1]);
- } else if (inner[0]->str() == TAG) {
- ex->kind = ExternalKind::Tag;
- ex->value = getTagName(*inner[1]);
- } else {
- throw SParseException("invalid export", inner);
- }
- } else {
- // function
- ex->value = s[2]->str();
- ex->kind = ExternalKind::Function;
- }
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", s);
- }
- wasm.addExport(ex.release());
-}
-
-void SExpressionWasmBuilder::parseImport(Element& s) {
- size_t i = 1;
- // (import "env" "STACKTOP" (global $stackTop i32))
- bool newStyle = s.size() == 4 && s[3]->isList();
- auto kind = ExternalKind::Invalid;
- if (newStyle) {
- if (elementStartsWith(*s[3], FUNC)) {
- kind = ExternalKind::Function;
- } else if (elementStartsWith(*s[3], MEMORY)) {
- kind = ExternalKind::Memory;
- } else if (elementStartsWith(*s[3], TABLE)) {
- kind = ExternalKind::Table;
- } else if (elementStartsWith(*s[3], GLOBAL)) {
- kind = ExternalKind::Global;
- } else if ((*s[3])[0]->str() == TAG) {
- kind = ExternalKind::Tag;
- } else {
- newStyle = false; // either (param..) or (result..)
- }
- }
- Index newStyleInner = 1;
- Name name;
- if (s.size() > 3 && s[3]->isStr()) {
- name = s[i++]->str();
- } else if (newStyle && newStyleInner < s[3]->size() &&
- (*s[3])[newStyleInner]->dollared()) {
- name = (*s[3])[newStyleInner++]->str();
- }
- bool hasExplicitName = name.is();
- if (!hasExplicitName) {
- if (kind == ExternalKind::Function) {
- name = Name("fimport$" + std::to_string(functionCounter++));
- functionNames.push_back(name);
- } else if (kind == ExternalKind::Global) {
- // Handled in `parseGlobal`.
- } else if (kind == ExternalKind::Memory) {
- name = Name("mimport$" + std::to_string(memoryCounter++));
- } else if (kind == ExternalKind::Table) {
- name = Name("timport$" + std::to_string(tableCounter++));
- } else if (kind == ExternalKind::Tag) {
- name = Name("eimport$" + std::to_string(tagCounter++));
- tagNames.push_back(name);
- } else {
- throw SParseException("invalid import", s);
- }
- }
- if (!newStyle) {
- kind = ExternalKind::Function;
- }
- std::vector<char> moduleBytes;
- stringToBinary(*s[i], s[i]->str().str, moduleBytes);
- Name module = std::string(moduleBytes.data(), moduleBytes.size());
- i++;
-
- if (!s[i]->isStr()) {
- throw SParseException("no name for import", s, *s[i]);
- }
-
- std::vector<char> baseBytes;
- stringToBinary(*s[i], s[i]->str().str, baseBytes);
- Name base = std::string(baseBytes.data(), baseBytes.size());
- i++;
-
- // parse internals
- Element& inner = newStyle ? *s[3] : s;
- Index j = newStyle ? newStyleInner : i;
- if (kind == ExternalKind::Function) {
- auto func = std::make_unique<Function>();
-
- j = parseTypeUse(inner, j, func->type);
- func->setName(name, hasExplicitName);
- func->module = module;
- func->base = base;
- functionTypes[name] = func->type;
- wasm.addFunction(func.release());
- } else if (kind == ExternalKind::Global) {
- parseGlobal(inner, true);
- j++;
- auto& global = wasm.globals.back();
- global->module = module;
- global->base = base;
- } else if (kind == ExternalKind::Table) {
- auto table = std::make_unique<Table>();
- table->setName(name, hasExplicitName);
- table->module = module;
- table->base = base;
- tableNames.push_back(name);
-
- if (j < inner.size() - 1) {
- auto initElem = inner[j++];
- table->initial = getAddress(initElem);
- checkAddress(table->initial, "excessive table init size", initElem);
- }
- if (j < inner.size() - 1) {
- auto maxElem = inner[j++];
- table->max = getAddress(maxElem);
- checkAddress(table->max, "excessive table max size", maxElem);
- } else {
- table->max = Table::kUnlimitedSize;
- }
-
- // ends with the table element type
- table->type = elementToType(*inner[j++]);
- if (!table->type.isRef()) {
- throw SParseException("Only reference types are valid for tables", s);
- }
-
- wasm.addTable(std::move(table));
- } else if (kind == ExternalKind::Memory) {
- auto memory = std::make_unique<Memory>();
- memory->setName(name, hasExplicitName);
- memory->module = module;
- memory->base = base;
- memoryNames.push_back(name);
-
- j = parseMemoryLimits(inner, j, memory);
- if (j != inner.size() && *inner[j] == SHARED) {
- memory->shared = true;
- j++;
- }
-
- wasm.addMemory(std::move(memory));
- } else if (kind == ExternalKind::Tag) {
- auto tag = std::make_unique<Tag>();
- HeapType tagType;
- j = parseTypeUse(inner, j, tagType);
- tag->sig = tagType.getSignature();
- tag->setName(name, hasExplicitName);
- tag->module = module;
- tag->base = base;
- wasm.addTag(tag.release());
- }
- // If there are more elements, they are invalid
- if (j < inner.size()) {
- throw SParseException("invalid element", inner, *inner[j]);
- }
-}
-
-void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
- std::unique_ptr<Global> global = std::make_unique<Global>();
- size_t i = 1;
- if (s[i]->dollared()) {
- global->setExplicitName(s[i++]->str());
- } else if (preParseImport) {
- global->name = Name("gimport$" + std::to_string(globalCounter));
- } else {
- global->name = Name::fromInt(globalCounter);
- }
- globalCounter++;
- globalNames.push_back(global->name);
- bool mutable_ = false;
- Type type = Type::none;
- Name importModule, importBase;
- while (i < s.size() && s[i]->isList()) {
- auto& inner = *s[i++];
- if (elementStartsWith(inner, EXPORT)) {
- auto ex = std::make_unique<Export>();
- ex->name = inner[1]->str();
- ex->value = global->name;
- ex->kind = ExternalKind::Global;
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", s);
- }
- wasm.addExport(ex.release());
- } else if (elementStartsWith(inner, IMPORT)) {
- importModule = inner[1]->str();
- importBase = inner[2]->str();
- } else if (elementStartsWith(inner, MUT)) {
- mutable_ = true;
- type = elementToType(*inner[1]);
- break;
- } else {
- type = elementToType(inner);
- break;
- }
- }
- if (type == Type::none) {
- type = stringToType(s[i++]->str());
- }
- if (importModule.is()) {
- // this is an import, actually
- if (!importBase.size()) {
- throw SParseException("module but no base for import", s);
- }
- if (!preParseImport) {
- throw SParseException("!preParseImport in global", s);
- }
- auto im = std::make_unique<Global>();
- im->name = global->name;
- im->module = importModule;
- im->base = importBase;
- im->type = type;
- im->mutable_ = mutable_;
- if (wasm.getGlobalOrNull(im->name)) {
- throw SParseException("duplicate import", s);
- }
- wasm.addGlobal(im.release());
- return;
- }
- global->type = type;
- if (i < s.size()) {
- global->init = parseExpression(s[i++]);
- } else if (!preParseImport) {
- throw SParseException("global without init", s);
- }
- global->mutable_ = mutable_;
- if (i != s.size()) {
- throw SParseException("extra import elements", s);
- }
- if (wasm.getGlobalOrNull(global->name)) {
- throw SParseException("duplicate import", s);
- }
- wasm.addGlobal(global.release());
-}
-
-void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
- std::unique_ptr<Table> table = std::make_unique<Table>();
- Index i = 1;
- if (s[i]->dollared()) {
- table->setExplicitName(s[i++]->str());
- } else {
- table->name = Name::fromInt(tableCounter++);
- }
- tableNames.push_back(table->name);
-
- Name importModule, importBase;
- if (s[i]->isList()) {
- auto& inner = *s[i];
- if (elementStartsWith(inner, EXPORT)) {
- auto ex = std::make_unique<Export>();
- ex->name = inner[1]->str();
- ex->value = table->name;
- ex->kind = ExternalKind::Table;
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", inner);
- }
- wasm.addExport(ex.release());
- i++;
- } else if (elementStartsWith(inner, IMPORT)) {
- if (!preParseImport) {
- throw SParseException("!preParseImport in table", inner);
- }
- table->module = inner[1]->str();
- table->base = inner[2]->str();
- i++;
- } else if (!elementStartsWith(inner, REF)) {
- throw SParseException("invalid table", inner);
- }
- }
-
- bool hasExplicitLimit = false;
-
- if (s[i]->isStr() && String::isNumber(s[i]->toString())) {
- table->initial = parseIndex(*s[i++]);
- hasExplicitLimit = true;
- }
- if (s[i]->isStr() && String::isNumber(s[i]->toString())) {
- table->max = parseIndex(*s[i++]);
- }
-
- table->type = elementToType(*s[i++]);
- if (!table->type.isRef()) {
- throw SParseException("Only reference types are valid for tables", s);
- }
-
- if (i < s.size() && s[i]->isList()) {
- if (hasExplicitLimit) {
- throw SParseException(
- "Table cannot have both explicit limits and an inline (elem ...)", s);
- }
- // (table type (elem ..))
- parseElem(*s[i], table.get());
- auto it = std::find_if(wasm.elementSegments.begin(),
- wasm.elementSegments.end(),
- [&](std::unique_ptr<ElementSegment>& segment) {
- return segment->table == table->name;
- });
- if (it != wasm.elementSegments.end()) {
- table->initial = table->max = it->get()->data.size();
- } else {
- table->initial = table->max = 0;
- }
- }
-
- wasm.addTable(std::move(table));
-}
-
-// parses an elem segment
-// elem ::= (elem (table tableidx)? (offset (expr)) reftype vec(item (expr)))
-// | (elem reftype vec(item (expr)))
-// | (elem declare reftype vec(item (expr)))
-//
-// abbreviation:
-// (offset (expr)) ≡ (expr)
-// (item (expr)) ≡ (expr)
-// ϵ ≡ (table 0)
-//
-// funcref vec(ref.func) ≡ func vec(funcidx)
-// (elem (expr) vec(funcidx)) ≡ (elem (table 0) (offset (expr)) func
-// vec(funcidx))
-//
-void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
- Index i = 1;
- Name name = Name::fromInt(elemCounter++);
- bool hasExplicitName = false;
- bool isPassive = true;
- bool usesExpressions = false;
-
- if (table) {
- Expression* offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
- auto segment = std::make_unique<ElementSegment>(table->name, offset);
- segment->setName(name, hasExplicitName);
- elemSegmentNames.push_back(name);
- parseElemFinish(s, segment, i, s[i]->isList());
- return;
- }
-
- if (s[i]->isStr() && s[i]->dollared()) {
- name = s[i++]->str();
- hasExplicitName = true;
- }
- elemSegmentNames.push_back(name);
- if (s[i]->isStr() && s[i]->str() == DECLARE) {
- // We don't store declared segments in the IR
- return;
- }
-
- auto segment = std::make_unique<ElementSegment>();
- segment->setName(name, hasExplicitName);
-
- if (s[i]->isList() && !elementStartsWith(s[i], REF)) {
- // Optional (table <tableidx>)
- if (elementStartsWith(s[i], TABLE)) {
- auto& inner = *s[i++];
- segment->table = getTableName(*inner[1]);
- }
-
- // Offset expression (offset (<expr>)) | (<expr>)
- auto& inner = *s[i++];
- if (elementStartsWith(inner, OFFSET)) {
- if (inner.size() > 2) {
- throw SParseException(
- "Invalid offset for an element segment.", s, *s[i]);
- }
- segment->offset = parseExpression(inner[1]);
- } else {
- segment->offset = parseExpression(inner);
- }
- isPassive = false;
- }
-
- if (i < s.size()) {
- if (s[i]->isStr() && s[i]->dollared()) {
- usesExpressions = false;
- } else if (s[i]->isStr() && s[i]->str() == FUNC) {
- usesExpressions = false;
- i += 1;
- } else {
- segment->type = elementToType(*s[i]);
- usesExpressions = true;
- i += 1;
- }
- }
-
- if (!isPassive && segment->table.isNull()) {
- if (wasm.tables.empty()) {
- throw SParseException("active element without table", s);
- }
- table = wasm.tables.front().get();
- segment->table = table->name;
- }
-
- // We may be post-MVP also due to type reasons or otherwise, as detected by
- // the utility function for Binaryen IR.
- usesExpressions =
- usesExpressions || TableUtils::usesExpressions(segment.get(), &wasm);
-
- parseElemFinish(s, segment, i, usesExpressions);
-}
-
-ElementSegment* SExpressionWasmBuilder::parseElemFinish(
- Element& s,
- std::unique_ptr<ElementSegment>& segment,
- Index i,
- bool usesExpressions) {
-
- for (; i < s.size(); i++) {
- if (!s[i]->isList()) {
- // An MVP-style declaration: just a function name.
- auto func = getFunctionName(*s[i]);
- segment->data.push_back(
- Builder(wasm).makeRefFunc(func, functionTypes[func]));
- continue;
- }
- if (!usesExpressions) {
- throw SParseException("expected an MVP-style $funcname in elem.", s);
- }
- auto& inner = *s[i];
- if (elementStartsWith(inner, ITEM)) {
- if (inner[1]->isList()) {
- // (item (ref.func $f))
- segment->data.push_back(parseExpression(inner[1]));
- } else {
- // (item ref.func $f)
- inner.list().removeAt(0);
- segment->data.push_back(parseExpression(inner));
- }
- } else {
- segment->data.push_back(parseExpression(inner));
- }
- }
- return wasm.addElementSegment(std::move(segment));
-}
-
-HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
- if (s.isStr()) {
- // It's a string.
- if (s.dollared()) {
- auto it = typeIndices.find(s.toString());
- if (it == typeIndices.end()) {
- throw SParseException("unknown dollared function type", s);
- }
- return types[it->second];
- } else {
- // It may be a numerical index, or it may be a built-in type name like
- // "i31".
- auto str = s.toString();
- if (String::isNumber(str)) {
- size_t offset = parseIndex(s);
- if (offset >= types.size()) {
- throw SParseException("unknown indexed function type", s);
- }
- return types[offset];
- }
- return stringToHeapType(s.str(), /* prefix = */ false);
- }
- }
- throw SParseException("invalid heap type", s);
-}
-
-void SExpressionWasmBuilder::parseTag(Element& s, bool preParseImport) {
- auto tag = std::make_unique<Tag>();
- size_t i = 1;
-
- // Parse name
- if (s[i]->isStr() && s[i]->dollared()) {
- auto& inner = *s[i++];
- tag->setExplicitName(inner.str());
- if (wasm.getTagOrNull(tag->name)) {
- throw SParseException("duplicate tag", inner);
- }
- } else {
- tag->name = Name::fromInt(tagCounter);
- assert(!wasm.getTagOrNull(tag->name));
- }
- tagCounter++;
- tagNames.push_back(tag->name);
-
- // Parse import, if any
- if (i < s.size() && elementStartsWith(*s[i], IMPORT)) {
- assert(preParseImport && "import element in non-preParseImport mode");
- auto& importElem = *s[i++];
- if (importElem.size() != 3) {
- throw SParseException("invalid import", importElem);
- }
- if (!importElem[1]->isStr() || importElem[1]->dollared()) {
- throw SParseException("invalid import module name", importElem);
- }
- if (!importElem[2]->isStr() || importElem[2]->dollared()) {
- throw SParseException("invalid import base name", importElem);
- }
- tag->module = importElem[1]->str();
- tag->base = importElem[2]->str();
- }
-
- // Parse export, if any
- if (i < s.size() && elementStartsWith(*s[i], EXPORT)) {
- auto& exportElem = *s[i++];
- if (tag->module.is()) {
- throw SParseException("import and export cannot be specified together",
- exportElem);
- }
- if (exportElem.size() != 2) {
- throw SParseException("invalid export", exportElem);
- }
- if (!exportElem[1]->isStr() || exportElem[1]->dollared()) {
- throw SParseException("invalid export name", exportElem);
- }
- auto ex = std::make_unique<Export>();
- ex->name = exportElem[1]->str();
- if (wasm.getExportOrNull(ex->name)) {
- throw SParseException("duplicate export", exportElem);
- }
- ex->value = tag->name;
- ex->kind = ExternalKind::Tag;
- wasm.addExport(ex.release());
- }
-
- // Parse typeuse
- HeapType tagType;
- i = parseTypeUse(s, i, tagType);
- tag->sig = tagType.getSignature();
-
- // If there are more elements, they are invalid
- if (i < s.size()) {
- throw SParseException("invalid element", *s[i]);
- }
-
- wasm.addTag(tag.release());
-}
-
-void SExpressionWasmBuilder::validateHeapTypeUsingChild(Expression* child,
- HeapType heapType,
- Element& s) {
- if (child->type == Type::unreachable) {
- return;
- }
- if (!child->type.isRef() ||
- !HeapType::isSubType(child->type.getHeapType(), heapType)) {
- throw SParseException("bad heap type: expected " + heapType.toString() +
- " but found " + child->type.toString(),
- s);
- }
-}
-
-} // namespace wasm