/* * 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 #include #include #include "ir/branch-utils.h" #include "shared-constants.h" #include "support/string.h" #include "wasm-binary.h" #include "wasm-builder.h" #define abort_on(str) \ { throw ParseException(std::string("abort_on ") + str); } #define element_assert(condition) \ assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0)); using cashew::IString; 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 { static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), I8("i8"), I16("i16"), RTT("rtt"); static Address getAddress(const Element* s) { return atoll(s->c_str()); } static void checkAddress(Address a, const char* errorText, const Element* errorElem) { if (a > std::numeric_limits::max()) { throw ParseException(errorText, errorElem->line, errorElem->col); } } 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 ParseException("expected list", line, col); } return list_; } Element* Element::operator[](unsigned i) { if (!isList()) { throw ParseException("expected list", line, col); } if (i >= list().size()) { throw ParseException("expected more elements in list", line, col); } return list()[i]; } IString Element::str() const { if (!isStr()) { throw ParseException("expected string", line, col); } return str_; } const char* Element::c_str() const { if (!isStr()) { throw ParseException("expected string", line, col); } return str_.str; } 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, Element& e) { if (e.isList_) { o << '('; for (auto item : e.list_) { o << ' ' << *item; } o << " )"; } else { o << e.str_.str; } return o; } void Element::dump() { std::cout << "dumping " << this << " : " << *this << ".\n"; } SExpressionParser::SExpressionParser(char* 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 stack; std::vector stackLocs; Element* curr = allocator.alloc(); while (1) { skipWhitespace(); if (input[0] == 0) { break; } if (input[0] == '(') { input++; stack.push_back(curr); curr = allocator.alloc()->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 ParseException("stack is not empty", curr->line, curr->col); } return curr; } void SExpressionParser::parseDebugLocation() { // Extracting debug location (if valid) char* debugLoc = input + 3; // skipping ";;@" while (debugLoc[0] && debugLoc[0] == ' ') { debugLoc++; } char* debugLocEnd = debugLoc; while (debugLocEnd[0] && debugLocEnd[0] != '\n') { debugLocEnd++; } char* pos = debugLoc; while (pos < debugLocEnd && pos[0] != ':') { pos++; } if (pos >= debugLocEnd) { return; // no line number } std::string name(debugLoc, pos); char* 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* 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() ->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); } char temp = input[0]; input[0] = 0; auto ret = allocator.alloc() ->setString(IString(start, false), dollared, false) ->setMetadata(line, start - lineStart, loc); input[0] = temp; 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(); i++; } if (i < module.size() && module[i]->isStr()) { // these s-expressions contain a binary module, actually std::vector data; while (i < module.size()) { auto str = module[i++]->c_str(); if (auto size = strlen(str)) { stringToBinary(str, size, data); } } WasmBinaryBuilder binaryBuilder(wasm, data); binaryBuilder.read(); return; } Index implementedFunctions = 0; functionCounter = 0; for (unsigned j = i; j < module.size(); j++) { auto& s = *module[j]; preParseFunctionType(s); preParseImports(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 == EVENT) { parseEvent(curr, true /* preParseImport */); } else { throw ParseException( "fancy import we don't support yet", curr.line, curr.col); } } } void SExpressionWasmBuilder::parseModuleElement(Element& curr) { if (isImport(curr)) { return; // already done } IString id = curr[0]->str(); if (id == START) { return parseStart(curr); } if (id == FUNC) { return parseFunction(curr); } if (id == MEMORY) { return parseMemory(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 == EVENT) { return parseEvent(curr); } std::cerr << "bad module element " << id.str << '\n'; throw ParseException("unknown module element", curr.line, curr.col); } Name SExpressionWasmBuilder::getFunctionName(Element& s) { if (s.dollared()) { return s.str(); } else { // index size_t offset = atoi(s.str().c_str()); if (offset >= functionNames.size()) { throw ParseException( "unknown function in getFunctionName", s.line, s.col); } return functionNames[offset]; } } Name SExpressionWasmBuilder::getGlobalName(Element& s) { if (s.dollared()) { return s.str(); } else { // index size_t offset = atoi(s.str().c_str()); if (offset >= globalNames.size()) { throw ParseException("unknown global in getGlobalName", s.line, s.col); } return globalNames[offset]; } } Name SExpressionWasmBuilder::getEventName(Element& s) { if (s.dollared()) { return s.str(); } else { // index size_t offset = atoi(s.str().c_str()); if (offset >= eventNames.size()) { throw ParseException("unknown event in getEventName", s.line, s.col); } return eventNames[offset]; } } // Parse various forms of (param ...) or (local ...) element. This ignores all // parameter or local names when specified. std::vector SExpressionWasmBuilder::parseParamOrLocal(Element& s) { size_t fakeIndex = 0; std::vector namedParams = parseParamOrLocal(s, fakeIndex); std::vector 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 SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) { assert(elementStartsWith(s, PARAM) || elementStartsWith(s, LOCAL)); std::vector 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 ParseException("invalid wasm type", s[i]->line, s[i]->col); } if (i + 1 >= s.size()) { throw ParseException("invalid param entry", s.line, s.col); } name = s[i]->str(); i++; } else { name = Name::fromInt(localIndex); } localIndex++; Type type; type = elementToType(*s[i]); if (elementStartsWith(s, PARAM) && type.isTuple()) { throw ParseException( "params may not have tuple types", s[i]->line, s[i]->col); } namedParams.emplace_back(name, type); } return namedParams; } // Parses (result type) element. (e.g. (result i32)) std::vector SExpressionWasmBuilder::parseResults(Element& s) { assert(elementStartsWith(s, RESULT)); std::vector 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)) Signature SExpressionWasmBuilder::parseTypeRef(Element& s) { assert(elementStartsWith(s, TYPE)); if (s.size() != 2) { throw ParseException("invalid type reference", s.line, s.col); } auto heapType = parseHeapType(*s[1]); if (!heapType.isSignature()) { throw ParseException("expected signature type", s.line, s.col); } return heapType.getSignature(); } // Prases 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, Signature& functionSignature, std::vector& namedParams) { std::vector params, results; size_t i = startPos; bool typeExists = false, paramsOrResultsExist = false; if (i < s.size() && elementStartsWith(*s[i], TYPE)) { typeExists = true; functionSignature = 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) { functionSignature = inlineSig; } else if (paramsOrResultsExist) { // verify that (type) and (params)/(result) match if (inlineSig != functionSignature) { throw ParseException("type and param/result don't match", s[paramPos]->line, s[paramPos]->col); } } // Add implicitly defined type to global list so it has an index auto heapType = HeapType(functionSignature); if (std::find(types.begin(), types.end(), heapType) == types.end()) { types.push_back(heapType); } // If only (type) is specified, populate `namedParams` if (!paramsOrResultsExist) { size_t index = 0; for (const auto& param : functionSignature.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, Signature& functionSignature) { std::vector params; return parseTypeUse(s, startPos, functionSignature, params); } void SExpressionWasmBuilder::preParseFunctionType(Element& s) { IString id = s[0]->str(); if (id == TYPE) { return parseType(s); } 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++; Signature sig; parseTypeUse(s, i, sig); functionSignatures[name] = sig; } 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]->quoted()) { // an export name exportName = s[i]->str(); i++; } else 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 = make_unique(); ex->name = exportName; ex->value = name; ex->kind = ExternalKind::Function; if (wasm.getExportOrNull(ex->name)) { throw ParseException("duplicate export", s.line, s.col); } 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 Signature sig; std::vector params; i = parseTypeUse(s, i, sig, params); // when (import) is inside a (func) element, this is not a function definition // but an import. if (importModule.is()) { if (!importBase.size()) { throw ParseException("module but no base for import", s.line, s.col); } if (!preParseImport) { throw ParseException("!preParseImport in func", s.line, s.col); } auto im = make_unique(); im->setName(name, hasExplicitName); im->module = importModule; im->base = importBase; im->sig = sig; functionSignatures[name] = sig; if (wasm.getFunctionOrNull(im->name)) { throw ParseException("duplicate import", s.line, s.col); } wasm.addFunction(im.release()); if (currFunction) { throw ParseException("import module inside function dec", s.line, s.col); } nameMapper.clear(); return; } // at this point this not an import but a real function definition. if (preParseImport) { throw ParseException("preParseImport in func", s.line, s.col); } size_t localIndex = params.size(); // local index for params and locals // parse locals std::vector 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(Builder(wasm).makeFunction( name, std::move(params), sig.results, std::move(vars))); 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(); 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(sig.results); } if (!currFunction->body) { currFunction->body = allocator.alloc(); } if (s.startLoc) { currFunction->prologLocation.insert(getDebugLocation(*s.startLoc)); } if (s.endLoc) { currFunction->epilogLocation.insert(getDebugLocation(*s.endLoc)); } if (wasm.getFunctionOrNull(currFunction->name)) { throw ParseException("duplicate function", s.line, s.col); } wasm.addFunction(currFunction.release()); nameMapper.clear(); } Type SExpressionWasmBuilder::stringToType(const char* str, bool allowError, bool prefix) { if (str[0] == 'i') { if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) { return Type::i32; } if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) { return Type::i64; } } if (str[0] == 'f') { if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) { return Type::f32; } if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) { return Type::f64; } } if (str[0] == 'v') { if (str[1] == '1' && str[2] == '2' && str[3] == '8' && (prefix || str[4] == 0)) { return Type::v128; } } if (strncmp(str, "funcref", 7) == 0 && (prefix || str[7] == 0)) { return Type::funcref; } if (strncmp(str, "externref", 9) == 0 && (prefix || str[9] == 0)) { return Type::externref; } if (strncmp(str, "exnref", 6) == 0 && (prefix || str[6] == 0)) { return Type::exnref; } if (strncmp(str, "anyref", 6) == 0 && (prefix || str[6] == 0)) { return Type::anyref; } if (strncmp(str, "eqref", 5) == 0 && (prefix || str[5] == 0)) { return Type::eqref; } if (strncmp(str, "i31ref", 6) == 0 && (prefix || str[6] == 0)) { // FIXME: for now, force all inputs to be nullable return Type(HeapType::BasicHeapType::i31, Nullable); } if (allowError) { return Type::none; } throw ParseException(std::string("invalid wasm type: ") + str); } HeapType SExpressionWasmBuilder::stringToHeapType(const char* str, bool prefix) { if (str[0] == 'a') { if (str[1] == 'n' && str[2] == 'y' && (prefix || str[3] == 0)) { return HeapType::any; } } if (str[0] == 'e') { if (str[1] == 'q' && (prefix || str[2] == 0)) { return HeapType::eq; } if (str[1] == 'x') { if (str[2] == 'n' && (prefix || str[3] == 0)) { return HeapType::exn; } if (str[2] == 't' && str[3] == 'e' && str[4] == 'r' && str[5] == 'n' && (prefix || str[6] == 0)) { return HeapType::ext; } } } if (str[0] == 'i') { if (str[1] == '3' && str[2] == '1' && (prefix || str[3] == 0)) { return HeapType::i31; } } if (str[0] == 'f') { if (str[1] == 'u' && str[2] == 'n' && str[3] == 'c' && (prefix || str[4] == 0)) { return HeapType::func; } } throw ParseException(std::string("invalid wasm heap type: ") + str); } 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 ParseException( std::string("invalid reference type size"), s.line, s.col); } if (size == 3 && *list[1] != NULL_) { throw ParseException( std::string("invalid reference type qualifier"), s.line, s.col); } // FIXME: for now, force all inputs to be nullable Nullability nullable = Nullable; size_t i = 1; if (size == 3) { nullable = Nullable; i++; } return Type(parseHeapType(*s[i]), nullable); } if (elementStartsWith(s, RTT)) { // It's an RTT, something like (rtt N $typename) or just (rtt $typename) // if there is no depth. if (s[1]->dollared()) { auto heapType = parseHeapType(*s[1]); return Type(Rtt(heapType)); } else { auto depth = atoi(s[1]->str().c_str()); auto heapType = parseHeapType(*s[2]); return Type(Rtt(depth, heapType)); } } // It's a tuple. std::vector types; for (size_t i = 0; i < s.size(); ++i) { types.push_back(elementToType(*list[i])); } return Type(types); } 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; } 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.c_str()); 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(); } Expression* SExpressionWasmBuilder::makeNop() { return allocator.alloc(); } Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) { auto ret = allocator.alloc(); 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(); ret->op = op; ret->value = parseExpression(s[1]); ret->finalize(); return ret; } Expression* SExpressionWasmBuilder::makeSelect(Element& s) { auto ret = allocator.alloc