diff options
Diffstat (limited to 'src/wasm-s-parser.h')
-rw-r--r-- | src/wasm-s-parser.h | 289 |
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])); } } |