summaryrefslogtreecommitdiff
path: root/src/wasm-s-parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-s-parser.h')
-rw-r--r--src/wasm-s-parser.h289
1 files changed, 196 insertions, 93 deletions
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index d0206bcc2..95f8a280d 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -23,12 +23,15 @@
#define wasm_wasm_s_parser_h
#include <cmath>
+#include <cctype>
+#include <limits>
#include "wasm.h"
#include "mixed_arena.h"
#include "shared-constants.h"
#include "parsing.h"
#include "asm_v_wasm.h"
+#include "ast_utils.h"
namespace wasm {
@@ -124,47 +127,37 @@ public:
SExpressionParser(char* input) : input(input) {
root = nullptr;
while (!root) { // keep parsing until we pass an initial comment
- root = parseInnerList();
+ root = parse();
}
}
Element* root;
private:
- // parses the internal part of a list, inside the parens.
- Element* parseInnerList() {
- if (input[0] == ';') {
- // comment
- input++;
- if (input[0] == ';') {
- while (input[0] != '\n') input++;
- return nullptr;
- }
- input = strstr(input, ";)");
- assert(input);
- return nullptr;
- }
- auto ret = allocator.alloc<Element>();
- while (1) {
- Element* curr = parse();
- if (!curr) return ret;
- ret->list().push_back(curr);
- }
- }
-
Element* parse() {
- skipWhitespace();
- if (input[0] == 0 || input[0] == ')') return nullptr;
- if (input[0] == '(') {
- // a list
- input++;
- auto ret = parseInnerList();
+ std::vector<Element *> stack;
+ Element *curr = allocator.alloc<Element>();
+ while (1) {
skipWhitespace();
- assert(input[0] == ')');
- input++;
- return ret;
+ if (input[0] == 0)
+ break;
+ if (input[0] == '(') {
+ input++;
+ stack.push_back(curr);
+ curr = allocator.alloc<Element>();
+ } else if (input[0] == ')') {
+ input++;
+ auto last = curr;
+ curr = stack.back();
+ assert(stack.size());
+ stack.pop_back();
+ curr->list().push_back(last);
+ } else {
+ curr->list().push_back(parseString());
+ }
}
- return parseString();
+ assert(stack.size() == 0);
+ return curr;
}
void skipWhitespace() {
@@ -173,7 +166,26 @@ private:
if (input[0] == ';' && input[1] == ';') {
while (input[0] && input[0] != '\n') input++;
} else if (input[0] == '(' && input[1] == ';') {
- input = strstr(input, ";)") + 2;
+ // Skip nested block comments.
+ input += 2;
+ int depth = 1;
+ while (1) {
+ if (input[0] == 0) {
+ return;
+ }
+ if (input[0] == '(' && input[1] == ';') {
+ input += 2;
+ depth++;
+ } else if (input[0] == ';' && input[1] == ')') {
+ input += 2;
+ --depth;
+ if (depth == 0) {
+ break;
+ }
+ } else {
+ input++;
+ }
+ }
} else {
return;
}
@@ -307,7 +319,20 @@ private:
return IString((prefix + std::to_string(otherIndex++)).c_str(), false);
}
- void parseStart(Element& s) { wasm.addStart(s[1]->str()); }
+ Name getFunctionName(Element& s) {
+ if (s.dollared()) {
+ return s.str();
+ } else {
+ // index
+ size_t offset = atoi(s.str().c_str());
+ if (offset >= functionNames.size()) onError();
+ return functionNames[offset];
+ }
+ }
+
+ void parseStart(Element& s) {
+ wasm.addStart(getFunctionName(*s[1]));
+ }
void parseFunction(Element& s) {
auto func = currFunction = allocator.alloc<Function>();
@@ -380,6 +405,7 @@ private:
if (!autoBlock) {
autoBlock = allocator.alloc<Block>();
autoBlock->list.push_back(func->body);
+ autoBlock->finalize();
func->body = autoBlock;
}
autoBlock->list.push_back(ex);
@@ -427,8 +453,8 @@ public:
// type.operation (e.g. i32.add)
WasmType type = stringToWasmType(str, false, true);
// Local copy to index into op without bounds checking.
- constexpr size_t maxNameSize = 15;
- char op[maxNameSize + 1] = { '\0' };
+ enum { maxNameSize = 15 };
+ char op[maxNameSize + 1] = {'\0'};
strncpy(op, dot + 1, maxNameSize);
switch (op[0]) {
case 'a': {
@@ -462,7 +488,10 @@ public:
abort_on(op);
}
case 'e': {
- if (op[1] == 'q') return makeBinary(s, BinaryOp::Eq, type);
+ if (op[1] == 'q') {
+ if (op[2] == 0) return makeBinary(s, BinaryOp::Eq, type);
+ if (op[2] == 'z') return makeUnary(s, UnaryOp::EqZ, i32);
+ }
if (op[1] == 'x') return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type);
abort_on(op);
}
@@ -521,10 +550,12 @@ public:
if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BinaryOp::RemU : BinaryOp::RemS, type);
if (op[2] == 'i') return makeUnary(s, isWasmTypeFloat(type) ? UnaryOp::ReinterpretInt : UnaryOp::ReinterpretFloat, type);
}
+ if (op[1] == 'o' && op[2] == 't') {
+ return makeBinary(s, op[3] == 'l' ? BinaryOp::RotL : BinaryOp::RotR, type);
+ }
abort_on(op);
}
case 's': {
- if (op[1] == 'e') return makeSelect(s, type);
if (op[1] == 'h') {
if (op[2] == 'l') return makeBinary(s, BinaryOp::Shl, type);
return makeBinary(s, op[4] == 'u' ? BinaryOp::ShrU : BinaryOp::ShrS, type);
@@ -557,7 +588,10 @@ public:
switch (str[0]) {
case 'b': {
if (str[1] == 'l') return makeBlock(s);
- if (str[1] == 'r') return makeBreak(s);
+ if (str[1] == 'r') {
+ if (str[2] == '_' && str[3] == 't') return makeBreakTable(s);
+ return makeBreak(s);
+ }
abort_on(str);
}
case 'c': {
@@ -568,6 +602,10 @@ public:
}
abort_on(str);
}
+ case 'e': {
+ if (str[1] == 'l') return makeThenOrElse(s);
+ abort_on(str);
+ }
case 'g': {
if (str[1] == 'e') return makeGetLocal(s);
if (str[1] == 'r') return makeHost(s, HostOp::GrowMemory);
@@ -598,7 +636,8 @@ public:
abort_on(str);
}
case 's': {
- if (str[1] == 'e') return makeSetLocal(s);
+ if (str[1] == 'e' && str[2] == 't') return makeSetLocal(s);
+ if (str[1] == 'e' && str[2] == 'l') return makeSelect(s);
abort_on(str);
}
case 'r': {
@@ -606,7 +645,7 @@ public:
abort_on(str);
}
case 't': {
- if (str[1] == 'a') return makeSwitch(s); // aka tableswitch
+ if (str[1] == 'h') return makeThenOrElse(s);
abort_on(str);
}
case 'u': {
@@ -637,12 +676,12 @@ private:
return ret;
}
- Expression* makeSelect(Element& s, WasmType type) {
+ Expression* makeSelect(Element& s) {
auto ret = allocator.alloc<Select>();
ret->ifTrue = parseExpression(s[1]);
ret->ifFalse = parseExpression(s[2]);
ret->condition = parseExpression(s[3]);
- ret->type = type;
+ ret->finalize();
return ret;
}
@@ -686,20 +725,68 @@ private:
}
Expression* makeBlock(Element& s) {
+ // special-case Block, because Block nesting (in their first element) can be incredibly deep
+ auto curr = allocator.alloc<Block>();
+ auto* sp = &s;
+ std::vector<std::pair<Element*, Block*>> stack;
+ while (1) {
+ stack.emplace_back(sp, curr);
+ auto& s = *sp;
+ size_t i = 1;
+ if (i < s.size() && s[i]->isStr()) {
+ curr->name = s[i]->str();
+ i++;
+ } else {
+ curr->name = getPrefixedName("block");
+ }
+ labelStack.push_back(curr->name);
+ if (i >= s.size()) break; // labeled empty block
+ auto& first = *s[i];
+ if (first[0]->str() == BLOCK) {
+ // recurse
+ curr = allocator.alloc<Block>();
+ 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].first;
+ auto* curr = stack[t].second;
+ auto& s = *sp;
+ size_t i = 1;
+ if (i < s.size()) {
+ if (s[i]->isStr()) {
+ i++;
+ }
+ if (t < int(stack.size()) - 1) {
+ // first child is one of our recursions
+ curr->list.push_back(stack[t + 1].second);
+ i++;
+ }
+ for (; i < s.size(); i++) {
+ curr->list.push_back(parseExpression(s[i]));
+ }
+ }
+ assert(labelStack.back() == curr->name);
+ labelStack.pop_back();
+ curr->finalize();
+ }
+ return stack[0].second;
+ }
+
+ // Similar to block, but the label is handled by the enclosing if (since there might not be a then or else, ick)
+ Expression* makeThenOrElse(Element& s) {
auto ret = allocator.alloc<Block>();
size_t i = 1;
if (s[1]->isStr()) {
- ret->name = s[1]->str();
i++;
- } else {
- ret->name = getPrefixedName("block");
}
- labelStack.push_back(ret->name);
for (; i < s.size(); i++) {
ret->list.push_back(parseExpression(s[i]));
}
- labelStack.pop_back();
- if (ret->list.size() > 0) ret->type = ret->list.back()->type;
+ ret->finalize();
return ret;
}
@@ -739,8 +826,8 @@ private:
ret->align = atoi(eq);
} else if (str[0] == 'o') {
uint64_t offset = atoll(eq);
- if (offset > 0xffffffff) onError();
- ret->offset = offset;
+ if (offset > std::numeric_limits<uint32_t>::max()) onError();
+ ret->offset = (uint32_t)offset;
} else onError();
i++;
}
@@ -788,9 +875,38 @@ private:
Expression* makeIf(Element& s) {
auto ret = allocator.alloc<If>();
ret->condition = parseExpression(s[1]);
- ret->ifTrue = parseExpression(s[2]);
+
+ // ifTrue and ifFalse may get implicit blocks
+ auto handle = [&](const char* title, Element& s) {
+ Name name = getPrefixedName(title);
+ bool explicitThenElse = false;
+ if (s[0]->str() == THEN || s[0]->str() == ELSE) {
+ explicitThenElse = true;
+ if (s[1]->dollared()) {
+ name = s[1]->str();
+ }
+ }
+ labelStack.push_back(name);
+ auto* ret = parseExpression(&s);
+ labelStack.pop_back();
+ if (explicitThenElse) {
+ ret->dyn_cast<Block>()->name = name;
+ } else {
+ // add a block if we must
+ if (BreakSeeker::has(ret, name)) {
+ auto* block = allocator.alloc<Block>();
+ block->name = name;
+ block->list.push_back(ret);
+ block->finalize();
+ ret = block;
+ }
+ }
+ return ret;
+ };
+
+ ret->ifTrue = handle("if-true", *s[2]);
if (s.size() == 4) {
- ret->ifFalse = parseExpression(s[3]);
+ ret->ifFalse = handle("if-else", *s[3]);
ret->finalize();
}
return ret;
@@ -802,9 +918,7 @@ private:
for (; i < s.size() && i < stopAt; i++) {
ret->list.push_back(parseExpression(s[i]));
}
- if (ret->list.size() > 0) {
- ret->type = ret->list.back()->type;
- }
+ ret->finalize();
// Note that we do not name these implicit/synthetic blocks. They
// are the effects of syntactic sugar, and nothing can branch to
// them anyhow.
@@ -899,6 +1013,22 @@ private:
return ret;
}
+ Expression* makeBreakTable(Element& s) {
+ auto ret = allocator.alloc<Switch>();
+ size_t i = 1;
+ while (!s[i]->isList()) {
+ ret->targets.push_back(getLabel(*s[i++]));
+ }
+ 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* makeReturn(Element& s) {
auto ret = allocator.alloc<Return>();
if (s.size() >= 2) {
@@ -907,39 +1037,11 @@ private:
return ret;
}
- Expression* makeSwitch(Element& s) {
- auto ret = allocator.alloc<Switch>();
- size_t i = 1;
- if (s[i]->isStr()) {
- ret->name = s[i]->str();
- i++;
- } else {
- ret->name = getPrefixedName("switch");
- }
- labelStack.push_back(ret->name);
- ret->value = parseExpression(s[i]);
- i++;
- Element& table = *s[i];
- i++;
- for (size_t j = 1; j < table.size(); j++) {
- Element& curr = *table[j];
- ret->targets.push_back(getLabel(*curr[1]));
- }
- Element& curr = *s[i];
- ret->default_ = getLabel(*curr[1]);
- i++;
- for (; i < s.size(); i++) {
- Element& curr = *s[i];
- assert(curr[0]->str() == CASE);
- if (curr.size() < 2) onError();
- ret->cases.emplace_back(curr[1]->str(), makeMaybeBlock(curr, 2, curr.size()));
- }
- ret->type = ret->cases.size() > 0 ? ret->cases[0].body->type : none;
- labelStack.pop_back();
- return ret;
- }
+ bool hasMemory = false;
void parseMemory(Element& s) {
+ hasMemory = true;
+
wasm.memory.initial = atoi(s[1]->c_str());
if (s.size() == 2) return;
size_t i = 2;
@@ -991,6 +1093,12 @@ private:
}
void parseExport(Element& s) {
+ if (!s[2]->dollared() && !std::isdigit(s[2]->str()[0])) {
+ assert(s[2]->str() == MEMORY);
+ if (!hasMemory) onError();
+ wasm.memory.exportName = s[1]->str();
+ return;
+ }
auto ex = allocator.alloc<Export>();
ex->name = s[1]->str();
ex->value = s[2]->str();
@@ -1038,12 +1146,7 @@ private:
void parseTable(Element& s) {
for (size_t i = 1; i < s.size(); i++) {
- Name name = s[i]->str();
- if (!s[i]->dollared()) {
- // index, we haven't
- name = functionNames[atoi(name.str)];
- }
- wasm.table.names.push_back(name);
+ wasm.table.names.push_back(getFunctionName(*s[i]));
}
}