From 0fe05b19eb01c65fedbaee8e102c4f7aa0e5fdda Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 19 Sep 2023 11:08:58 -0700 Subject: [NFC] Split the new wat parser into multiple files (#5960) And put the new files in a new source directory, "parser". This is a rough split and is not yet expected to dramatically improve compile times. The exact organization of the new files is subject to change, but this splitting should be enough to make further parser development more pleasant. --- src/wasm/wat-parser.cpp | 3895 ----------------------------------------------- 1 file changed, 3895 deletions(-) delete mode 100644 src/wasm/wat-parser.cpp (limited to 'src/wasm/wat-parser.cpp') diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp deleted file mode 100644 index b31019811..000000000 --- a/src/wasm/wat-parser.cpp +++ /dev/null @@ -1,3895 +0,0 @@ -/* - * Copyright 2022 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wat-parser.h" -#include "ir/names.h" -#include "support/name.h" -#include "wasm-builder.h" -#include "wasm-ir-builder.h" -#include "wasm-type.h" -#include "wasm.h" -#include "wat-lexer.h" - -// The WebAssembly text format is recursive in the sense that elements may be -// referred to before they are declared. Furthermore, elements may be referred -// to by index or by name. As a result, we need to parse text modules in -// multiple phases. -// -// In the first phase, we find all of the module element declarations and -// record, but do not interpret, the input spans of their corresponding -// definitions. This phase establishes the indices and names of each module -// element so that subsequent phases can look them up. -// -// The second phase parses type definitions to construct the types used in the -// module. This has to be its own phase because we have no way to refer to a -// type before it has been built along with all the other types, unlike for -// other module elements that can be referred to by name before their -// definitions have been parsed. -// -// The third phase further parses and constructs types implicitly defined by -// type uses in functions, blocks, and call_indirect instructions. These -// implicitly defined types may be referred to by index elsewhere. -// -// The fourth phase parses and sets the types of globals, functions, and other -// top-level module elements. These types need to be set before we parse -// instructions because they determine the types of instructions such as -// global.get and ref.func. -// -// The fifth and final phase parses the remaining contents of all module -// elements, including instructions. -// -// Each phase of parsing gets its own context type that is passed to the -// individual parsing functions. There is a parsing function for each element of -// the grammar given in the spec. Parsing functions are templatized so that they -// may be passed the appropriate context type and return the correct result type -// for each phase. - -using namespace std::string_view_literals; - -namespace wasm::WATParser { - -namespace { - -// ============ -// Parser Input -// ============ - -// Wraps a lexer and provides utilities for consuming tokens. -struct ParseInput { - Lexer lexer; - - explicit ParseInput(std::string_view in) : lexer(in) {} - - ParseInput(std::string_view in, size_t index) : lexer(in) { - lexer.setIndex(index); - } - - ParseInput(const ParseInput& other, size_t index) : lexer(other.lexer) { - lexer.setIndex(index); - } - - bool empty() { return lexer.empty(); } - - std::optional peek() { - if (!empty()) { - return *lexer; - } - return {}; - } - - bool takeLParen() { - auto t = peek(); - if (!t || !t->isLParen()) { - return false; - } - ++lexer; - return true; - } - - bool takeRParen() { - auto t = peek(); - if (!t || !t->isRParen()) { - return false; - } - ++lexer; - return true; - } - - bool takeUntilParen() { - while (true) { - auto t = peek(); - if (!t) { - return false; - } - if (t->isLParen() || t->isRParen()) { - return true; - } - ++lexer; - } - } - - std::optional takeID() { - if (auto t = peek()) { - if (auto id = t->getID()) { - ++lexer; - // See comment on takeName. - return Name(std::string(*id)); - } - } - return {}; - } - - std::optional takeKeyword() { - if (auto t = peek()) { - if (auto keyword = t->getKeyword()) { - ++lexer; - return *keyword; - } - } - return {}; - } - - bool takeKeyword(std::string_view expected) { - if (auto t = peek()) { - if (auto keyword = t->getKeyword()) { - if (*keyword == expected) { - ++lexer; - return true; - } - } - } - return false; - } - - std::optional takeOffset() { - if (auto t = peek()) { - if (auto keyword = t->getKeyword()) { - if (keyword->substr(0, 7) != "offset="sv) { - return {}; - } - Lexer subLexer(keyword->substr(7)); - if (subLexer == subLexer.end()) { - return {}; - } - if (auto o = subLexer->getU64()) { - ++subLexer; - if (subLexer == subLexer.end()) { - ++lexer; - return o; - } - } - } - } - return std::nullopt; - } - - std::optional takeAlign() { - if (auto t = peek()) { - if (auto keyword = t->getKeyword()) { - if (keyword->substr(0, 6) != "align="sv) { - return {}; - } - Lexer subLexer(keyword->substr(6)); - if (subLexer == subLexer.end()) { - return {}; - } - if (auto a = subLexer->getU32()) { - ++subLexer; - if (subLexer == subLexer.end()) { - ++lexer; - return a; - } - } - } - } - return {}; - } - - std::optional takeU64() { - if (auto t = peek()) { - if (auto n = t->getU64()) { - ++lexer; - return n; - } - } - return std::nullopt; - } - - std::optional takeS64() { - if (auto t = peek()) { - if (auto n = t->getS64()) { - ++lexer; - return n; - } - } - return {}; - } - - std::optional takeI64() { - if (auto t = peek()) { - if (auto n = t->getI64()) { - ++lexer; - return n; - } - } - return {}; - } - - std::optional takeU32() { - if (auto t = peek()) { - if (auto n = t->getU32()) { - ++lexer; - return n; - } - } - return std::nullopt; - } - - std::optional takeS32() { - if (auto t = peek()) { - if (auto n = t->getS32()) { - ++lexer; - return n; - } - } - return {}; - } - - std::optional takeI32() { - if (auto t = peek()) { - if (auto n = t->getI32()) { - ++lexer; - return n; - } - } - return {}; - } - - std::optional takeU8() { - if (auto t = peek()) { - if (auto n = t->getU32()) { - if (n <= std::numeric_limits::max()) { - ++lexer; - return uint8_t(*n); - } - } - } - return {}; - } - - std::optional takeF64() { - if (auto t = peek()) { - if (auto d = t->getF64()) { - ++lexer; - return d; - } - } - return std::nullopt; - } - - std::optional takeF32() { - if (auto t = peek()) { - if (auto f = t->getF32()) { - ++lexer; - return f; - } - } - return std::nullopt; - } - - std::optional takeString() { - if (auto t = peek()) { - if (auto s = t->getString()) { - ++lexer; - return s; - } - } - return {}; - } - - std::optional takeName() { - // TODO: Move this to lexer and validate UTF. - if (auto str = takeString()) { - // Copy to a std::string to make sure we have a null terminator, otherwise - // the `Name` constructor won't work correctly. - // TODO: Update `Name` to use string_view instead of char* and/or to take - // rvalue strings to avoid this extra copy. - return Name(std::string(*str)); - } - return {}; - } - - bool takeSExprStart(std::string_view expected) { - auto original = lexer; - if (takeLParen() && takeKeyword(expected)) { - return true; - } - lexer = original; - return false; - } - - Index getPos() { - if (auto t = peek()) { - return lexer.getIndex() - t->span.size(); - } - return lexer.getIndex(); - } - - [[nodiscard]] Err err(Index pos, std::string reason) { - std::stringstream msg; - msg << lexer.position(pos) << ": error: " << reason; - return Err{msg.str()}; - } - - [[nodiscard]] Err err(std::string reason) { return err(getPos(), reason); } -}; - -// ========= -// Utilities -// ========= - -// The location, possible name, and index in the respective module index space -// of a module-level definition in the input. -struct DefPos { - Name name; - Index pos; - Index index; -}; - -struct GlobalType { - Mutability mutability; - Type type; -}; - -// A signature type and parameter names (possibly empty), used for parsing -// function types. -struct TypeUse { - HeapType type; - std::vector names; -}; - -struct ImportNames { - Name mod; - Name nm; -}; - -struct Limits { - uint64_t initial; - uint64_t max; -}; - -struct MemType { - Type type; - Limits limits; - bool shared; -}; - -struct Memarg { - uint64_t offset; - uint32_t align; -}; - -// RAII utility for temporarily changing the parsing position of a parsing -// context. -template struct WithPosition { - Ctx& ctx; - Index original; - - WithPosition(Ctx& ctx, Index pos) : ctx(ctx), original(ctx.in.getPos()) { - ctx.in.lexer.setIndex(pos); - } - - ~WithPosition() { ctx.in.lexer.setIndex(original); } -}; - -// Deduction guide to satisfy -Wctad-maybe-unsupported. -template WithPosition(Ctx& ctx, Index) -> WithPosition; - -using IndexMap = std::unordered_map; - -void applyImportNames(Importable& item, ImportNames* names) { - if (names) { - item.module = names->mod; - item.base = names->nm; - } -} - -Result<> addExports(ParseInput& in, - Module& wasm, - const Named* item, - const std::vector& exports, - ExternalKind kind) { - for (auto name : exports) { - if (wasm.getExportOrNull(name)) { - // TODO: Fix error location - return in.err("repeated export name"); - } - wasm.addExport(Builder(wasm).makeExport(name, item->name, kind)); - } - return Ok{}; -} - -Result createIndexMap(ParseInput& in, - const std::vector& defs) { - IndexMap indices; - for (auto& def : defs) { - if (def.name.is()) { - if (!indices.insert({def.name, def.index}).second) { - return in.err(def.pos, "duplicate element name"); - } - } - } - return indices; -} - -std::vector getUnnamedTypes(const std::vector& named) { - std::vector types; - types.reserve(named.size()); - for (auto& t : named) { - types.push_back(t.type); - } - return types; -} - -template -Result<> parseDefs(Ctx& ctx, - const std::vector& defs, - MaybeResult<> (*parser)(Ctx&)) { - for (auto& def : defs) { - ctx.index = def.index; - WithPosition with(ctx, def.pos); - auto parsed = parser(ctx); - CHECK_ERR(parsed); - assert(parsed); - } - return Ok{}; -} - -// =============== -// Parser Contexts -// =============== - -struct NullTypeParserCtx { - using IndexT = Ok; - using HeapTypeT = Ok; - using TypeT = Ok; - using ParamsT = Ok; - using ResultsT = size_t; - using BlockTypeT = Ok; - using SignatureT = Ok; - using StorageT = Ok; - using FieldT = Ok; - using FieldsT = Ok; - using StructT = Ok; - using ArrayT = Ok; - using LimitsT = Ok; - using MemTypeT = Ok; - using GlobalTypeT = Ok; - using TypeUseT = Ok; - using LocalsT = Ok; - using DataStringT = Ok; - - HeapTypeT makeFunc() { return Ok{}; } - HeapTypeT makeAny() { return Ok{}; } - HeapTypeT makeExtern() { return Ok{}; } - HeapTypeT makeEq() { return Ok{}; } - HeapTypeT makeI31() { return Ok{}; } - HeapTypeT makeStructType() { return Ok{}; } - HeapTypeT makeArrayType() { return Ok{}; } - - TypeT makeI32() { return Ok{}; } - TypeT makeI64() { return Ok{}; } - TypeT makeF32() { return Ok{}; } - TypeT makeF64() { return Ok{}; } - TypeT makeV128() { return Ok{}; } - - TypeT makeRefType(HeapTypeT, Nullability) { return Ok{}; } - - ParamsT makeParams() { return Ok{}; } - void appendParam(ParamsT&, Name, TypeT) {} - - // We have to count results because whether or not a block introduces a - // typeuse that may implicitly define a type depends on how many results it - // has. - size_t makeResults() { return 0; } - void appendResult(size_t& results, TypeT) { ++results; } - size_t getResultsSize(size_t results) { return results; } - - SignatureT makeFuncType(ParamsT*, ResultsT*) { return Ok{}; } - - StorageT makeI8() { return Ok{}; } - StorageT makeI16() { return Ok{}; } - StorageT makeStorageType(TypeT) { return Ok{}; } - - FieldT makeFieldType(StorageT, Mutability) { return Ok{}; } - - FieldsT makeFields() { return Ok{}; } - void appendField(FieldsT&, Name, FieldT) {} - - StructT makeStruct(FieldsT&) { return Ok{}; } - - std::optional makeArray(FieldsT&) { return Ok{}; } - - GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } - - LocalsT makeLocals() { return Ok{}; } - void appendLocal(LocalsT&, Name, TypeT) {} - - Result getTypeIndex(Name) { return 1; } - Result getHeapTypeFromIdx(Index) { return Ok{}; } - - DataStringT makeDataString() { return Ok{}; } - void appendDataString(DataStringT&, std::string_view) {} - - MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } - - BlockTypeT getBlockTypeFromResult(size_t results) { return Ok{}; } - - Result<> getBlockTypeFromTypeUse(Index, TypeUseT) { return Ok{}; } -}; - -template struct TypeParserCtx { - using IndexT = Index; - using HeapTypeT = HeapType; - using TypeT = Type; - using ParamsT = std::vector; - using ResultsT = std::vector; - using BlockTypeT = HeapType; - using SignatureT = Signature; - using StorageT = Field; - using FieldT = Field; - using FieldsT = std::pair, std::vector>; - using StructT = std::pair, Struct>; - using ArrayT = Array; - using LimitsT = Ok; - using MemTypeT = Ok; - using LocalsT = std::vector; - using DataStringT = Ok; - - // Map heap type names to their indices. - const IndexMap& typeIndices; - - TypeParserCtx(const IndexMap& typeIndices) : typeIndices(typeIndices) {} - - Ctx& self() { return *static_cast(this); } - - HeapTypeT makeFunc() { return HeapType::func; } - HeapTypeT makeAny() { return HeapType::any; } - HeapTypeT makeExtern() { return HeapType::ext; } - HeapTypeT makeEq() { return HeapType::eq; } - HeapTypeT makeI31() { return HeapType::i31; } - HeapTypeT makeStructType() { return HeapType::struct_; } - HeapTypeT makeArrayType() { return HeapType::array; } - - TypeT makeI32() { return Type::i32; } - TypeT makeI64() { return Type::i64; } - TypeT makeF32() { return Type::f32; } - TypeT makeF64() { return Type::f64; } - TypeT makeV128() { return Type::v128; } - - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return Type(ht, nullability); - } - - TypeT makeTupleType(const std::vector types) { return Tuple(types); } - - ParamsT makeParams() { return {}; } - void appendParam(ParamsT& params, Name id, TypeT type) { - params.push_back({id, type}); - } - - ResultsT makeResults() { return {}; } - void appendResult(ResultsT& results, TypeT type) { results.push_back(type); } - size_t getResultsSize(const ResultsT& results) { return results.size(); } - - SignatureT makeFuncType(ParamsT* params, ResultsT* results) { - std::vector empty; - const auto& paramTypes = params ? getUnnamedTypes(*params) : empty; - const auto& resultTypes = results ? *results : empty; - return Signature(self().makeTupleType(paramTypes), - self().makeTupleType(resultTypes)); - } - - StorageT makeI8() { return Field(Field::i8, Immutable); } - StorageT makeI16() { return Field(Field::i16, Immutable); } - StorageT makeStorageType(TypeT type) { return Field(type, Immutable); } - - FieldT makeFieldType(FieldT field, Mutability mutability) { - if (field.packedType == Field::not_packed) { - return Field(field.type, mutability); - } - return Field(field.packedType, mutability); - } - - FieldsT makeFields() { return {}; } - void appendField(FieldsT& fields, Name name, FieldT field) { - fields.first.push_back(name); - fields.second.push_back(field); - } - - StructT makeStruct(FieldsT& fields) { - return {std::move(fields.first), Struct(std::move(fields.second))}; - } - - std::optional makeArray(FieldsT& fields) { - if (fields.second.size() == 1) { - return Array(fields.second[0]); - } - return {}; - } - - LocalsT makeLocals() { return {}; } - void appendLocal(LocalsT& locals, Name id, TypeT type) { - locals.push_back({id, type}); - } - - Result getTypeIndex(Name id) { - auto it = typeIndices.find(id); - if (it == typeIndices.end()) { - return self().in.err("unknown type identifier"); - } - return it->second; - } - - DataStringT makeDataString() { return Ok{}; } - void appendDataString(DataStringT&, std::string_view) {} - - LimitsT makeLimits(uint64_t, std::optional) { return Ok{}; } - LimitsT getLimitsFromData(DataStringT) { return Ok{}; } - - MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } - - HeapType getBlockTypeFromResult(const std::vector results) { - assert(results.size() == 1); - return HeapType(Signature(Type::none, results[0])); - } -}; - -struct NullInstrParserCtx { - using InstrT = Ok; - using InstrsT = Ok; - using ExprT = Ok; - - using FieldIdxT = Ok; - using LocalIdxT = Ok; - using GlobalIdxT = Ok; - using MemoryIdxT = Ok; - using DataIdxT = Ok; - - using MemargT = Ok; - - InstrsT makeInstrs() { return Ok{}; } - void appendInstr(InstrsT&, InstrT) {} - InstrsT finishInstrs(InstrsT&) { return Ok{}; } - - ExprT makeExpr(InstrsT) { return Ok{}; } - Result instrToExpr(InstrT) { return Ok{}; } - - template FieldIdxT getFieldFromIdx(HeapTypeT, uint32_t) { - return Ok{}; - } - template FieldIdxT getFieldFromName(HeapTypeT, Name) { - return Ok{}; - } - LocalIdxT getLocalFromIdx(uint32_t) { return Ok{}; } - LocalIdxT getLocalFromName(Name) { return Ok{}; } - GlobalIdxT getGlobalFromIdx(uint32_t) { return Ok{}; } - GlobalIdxT getGlobalFromName(Name) { return Ok{}; } - MemoryIdxT getMemoryFromIdx(uint32_t) { return Ok{}; } - MemoryIdxT getMemoryFromName(Name) { return Ok{}; } - DataIdxT getDataFromIdx(uint32_t) { return Ok{}; } - DataIdxT getDataFromName(Name) { return Ok{}; } - - MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; } - - template - InstrT makeBlock(Index, std::optional, BlockTypeT) { - return Ok{}; - } - InstrT finishBlock(Index, InstrsT) { return Ok{}; } - - InstrT makeUnreachable(Index) { return Ok{}; } - InstrT makeNop(Index) { return Ok{}; } - InstrT makeBinary(Index, BinaryOp) { return Ok{}; } - InstrT makeUnary(Index, UnaryOp) { return Ok{}; } - template InstrT makeSelect(Index, ResultsT*) { - return Ok{}; - } - InstrT makeDrop(Index) { return Ok{}; } - InstrT makeMemorySize(Index, MemoryIdxT*) { return Ok{}; } - InstrT makeMemoryGrow(Index, MemoryIdxT*) { return Ok{}; } - InstrT makeLocalGet(Index, LocalIdxT) { return Ok{}; } - InstrT makeLocalTee(Index, LocalIdxT) { return Ok{}; } - InstrT makeLocalSet(Index, LocalIdxT) { return Ok{}; } - InstrT makeGlobalGet(Index, GlobalIdxT) { return Ok{}; } - InstrT makeGlobalSet(Index, GlobalIdxT) { return Ok{}; } - - InstrT makeI32Const(Index, uint32_t) { return Ok{}; } - InstrT makeI64Const(Index, uint64_t) { return Ok{}; } - InstrT makeF32Const(Index, float) { return Ok{}; } - InstrT makeF64Const(Index, double) { return Ok{}; } - InstrT makeLoad(Index, Type, bool, int, bool, MemoryIdxT*, MemargT) { - return Ok{}; - } - InstrT makeStore(Index, Type, int, bool, MemoryIdxT*, MemargT) { - return Ok{}; - } - InstrT makeAtomicRMW(Index, AtomicRMWOp, Type, int, MemoryIdxT*, MemargT) { - return Ok{}; - } - InstrT makeAtomicCmpxchg(Index, Type, int, MemoryIdxT*, MemargT) { - return Ok{}; - } - InstrT makeAtomicWait(Index, Type, MemoryIdxT*, MemargT) { return Ok{}; } - InstrT makeAtomicNotify(Index, MemoryIdxT*, MemargT) { return Ok{}; } - InstrT makeAtomicFence(Index) { return Ok{}; } - InstrT makeSIMDExtract(Index, SIMDExtractOp, uint8_t) { return Ok{}; } - InstrT makeSIMDReplace(Index, SIMDReplaceOp, uint8_t) { return Ok{}; } - InstrT makeSIMDShuffle(Index, const std::array&) { return Ok{}; } - InstrT makeSIMDTernary(Index, SIMDTernaryOp) { return Ok{}; } - InstrT makeSIMDShift(Index, SIMDShiftOp) { return Ok{}; } - InstrT makeSIMDLoad(Index, SIMDLoadOp, MemoryIdxT*, MemargT) { return Ok{}; } - InstrT makeSIMDLoadStoreLane( - Index, SIMDLoadStoreLaneOp, MemoryIdxT*, MemargT, uint8_t) { - return Ok{}; - } - InstrT makeMemoryInit(Index, MemoryIdxT*, DataIdxT) { return Ok{}; } - InstrT makeDataDrop(Index, DataIdxT) { return Ok{}; } - - InstrT makeMemoryCopy(Index, MemoryIdxT*, MemoryIdxT*) { return Ok{}; } - InstrT makeMemoryFill(Index, MemoryIdxT*) { return Ok{}; } - - InstrT makeReturn(Index) { return Ok{}; } - template InstrT makeRefNull(Index, HeapTypeT) { - return Ok{}; - } - InstrT makeRefIsNull(Index) { return Ok{}; } - - InstrT makeRefEq(Index) { return Ok{}; } - - InstrT makeRefI31(Index) { return Ok{}; } - InstrT makeI31Get(Index, bool) { return Ok{}; } - - template InstrT makeStructNew(Index, HeapTypeT) { - return Ok{}; - } - template InstrT makeStructNewDefault(Index, HeapTypeT) { - return Ok{}; - } - template - InstrT makeStructGet(Index, HeapTypeT, FieldIdxT, bool) { - return Ok{}; - } - template - InstrT makeStructSet(Index, HeapTypeT, FieldIdxT) { - return Ok{}; - } - template InstrT makeArrayNew(Index, HeapTypeT) { - return Ok{}; - } - template InstrT makeArrayNewDefault(Index, HeapTypeT) { - return Ok{}; - } - template - InstrT makeArrayNewData(Index, HeapTypeT, DataIdxT) { - return Ok{}; - } - template - InstrT makeArrayNewElem(Index, HeapTypeT, DataIdxT) { - return Ok{}; - } - template InstrT makeArrayGet(Index, HeapTypeT, bool) { - return Ok{}; - } - template InstrT makeArraySet(Index, HeapTypeT) { - return Ok{}; - } - InstrT makeArrayLen(Index) { return Ok{}; } - template - InstrT makeArrayCopy(Index, HeapTypeT, HeapTypeT) { - return Ok{}; - } - template InstrT makeArrayFill(Index, HeapTypeT) { - return Ok{}; - } -}; - -// Phase 1: Parse definition spans for top-level module elements and determine -// their indices and names. -struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { - using DataStringT = std::vector; - using LimitsT = Limits; - using MemTypeT = MemType; - - ParseInput in; - - // At this stage we only look at types to find implicit type definitions, - // which are inserted directly into the context. We cannot materialize or - // validate any types because we don't know what types exist yet. - // - // Declared module elements are inserted into the module, but their bodies are - // not filled out until later parsing phases. - Module& wasm; - - // The module element definitions we are parsing in this phase. - std::vector typeDefs; - std::vector subtypeDefs; - std::vector funcDefs; - std::vector memoryDefs; - std::vector globalDefs; - std::vector dataDefs; - - // Positions of typeuses that might implicitly define new types. - std::vector implicitTypeDefs; - - // Counters used for generating names for module elements. - int funcCounter = 0; - int memoryCounter = 0; - int globalCounter = 0; - int dataCounter = 0; - - // Used to verify that all imports come before all non-imports. - bool hasNonImport = false; - - ParseDeclsCtx(std::string_view in, Module& wasm) : in(in), wasm(wasm) {} - - void addFuncType(SignatureT) {} - void addStructType(StructT) {} - void addArrayType(ArrayT) {} - void setOpen() {} - Result<> addSubtype(Index) { return Ok{}; } - void finishSubtype(Name name, Index pos) { - subtypeDefs.push_back({name, pos, Index(subtypeDefs.size())}); - } - size_t getRecGroupStartIndex() { return 0; } - void addRecGroup(Index, size_t) {} - void finishDeftype(Index pos) { - typeDefs.push_back({{}, pos, Index(typeDefs.size())}); - } - - std::vector makeDataString() { return {}; } - void appendDataString(std::vector& data, std::string_view str) { - data.insert(data.end(), str.begin(), str.end()); - } - - Limits makeLimits(uint64_t n, std::optional m) { - return m ? Limits{n, *m} : Limits{n, Memory::kUnlimitedSize}; - } - Limits getLimitsFromData(const std::vector& data) { - uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize; - return {size, size}; - } - - MemType makeMemType(Type type, Limits limits, bool shared) { - return {type, limits, shared}; - } - - Result - makeTypeUse(Index pos, std::optional type, ParamsT*, ResultsT*) { - if (!type) { - implicitTypeDefs.push_back(pos); - } - return Ok{}; - } - - Result - addFuncDecl(Index pos, Name name, ImportNames* importNames) { - auto f = std::make_unique(); - if (name.is()) { - if (wasm.getFunctionOrNull(name)) { - // TDOO: if the existing function is not explicitly named, fix its name - // and continue. - return in.err(pos, "repeated function name"); - } - f->setExplicitName(name); - } else { - name = (importNames ? "fimport$" : "") + std::to_string(funcCounter++); - name = Names::getValidFunctionName(wasm, name); - f->name = name; - } - applyImportNames(*f, importNames); - return wasm.addFunction(std::move(f)); - } - - Result<> addFunc(Name name, - const std::vector& exports, - ImportNames* import, - TypeUseT type, - std::optional, - std::optional, - Index pos) { - if (import && hasNonImport) { - return in.err(pos, "import after non-import"); - } - auto f = addFuncDecl(pos, name, import); - CHECK_ERR(f); - CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function)); - funcDefs.push_back({name, pos, Index(funcDefs.size())}); - return Ok{}; - } - - Result - addMemoryDecl(Index pos, Name name, ImportNames* importNames, MemType type) { - auto m = std::make_unique(); - m->indexType = type.type; - m->initial = type.limits.initial; - m->max = type.limits.max; - m->shared = type.shared; - if (name) { - // TODO: if the existing memory is not explicitly named, fix its name - // and continue. - if (wasm.getMemoryOrNull(name)) { - return in.err(pos, "repeated memory name"); - } - m->setExplicitName(name); - } else { - name = (importNames ? "mimport$" : "") + std::to_string(memoryCounter++); - name = Names::getValidMemoryName(wasm, name); - m->name = name; - } - applyImportNames(*m, importNames); - return wasm.addMemory(std::move(m)); - } - - Result<> addMemory(Name name, - const std::vector& exports, - ImportNames* import, - MemType type, - Index pos) { - if (import && hasNonImport) { - return in.err(pos, "import after non-import"); - } - auto m = addMemoryDecl(pos, name, import, type); - CHECK_ERR(m); - CHECK_ERR(addExports(in, wasm, *m, exports, ExternalKind::Memory)); - memoryDefs.push_back({name, pos, Index(memoryDefs.size())}); - return Ok{}; - } - - Result<> addImplicitData(DataStringT&& data) { - auto& mem = *wasm.memories.back(); - auto d = std::make_unique(); - d->memory = mem.name; - d->isPassive = false; - d->offset = Builder(wasm).makeConstPtr(0, mem.indexType); - d->data = std::move(data); - d->name = Names::getValidDataSegmentName(wasm, "implicit-data"); - wasm.addDataSegment(std::move(d)); - return Ok{}; - } - - Result - addGlobalDecl(Index pos, Name name, ImportNames* importNames) { - auto g = std::make_unique(); - if (name) { - if (wasm.getGlobalOrNull(name)) { - // TODO: if the existing global is not explicitly named, fix its name - // and continue. - return in.err(pos, "repeated global name"); - } - g->setExplicitName(name); - } else { - name = (importNames ? "gimport$" : "") + std::to_string(globalCounter++); - name = Names::getValidGlobalName(wasm, name); - g->name = name; - } - applyImportNames(*g, importNames); - return wasm.addGlobal(std::move(g)); - } - - Result<> addGlobal(Name name, - const std::vector& exports, - ImportNames* import, - GlobalTypeT, - std::optional, - Index pos) { - if (import && hasNonImport) { - return in.err(pos, "import after non-import"); - } - auto g = addGlobalDecl(pos, name, import); - CHECK_ERR(g); - CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global)); - globalDefs.push_back({name, pos, Index(globalDefs.size())}); - return Ok{}; - } - - Result<> addData(Name name, - MemoryIdxT*, - std::optional, - std::vector&& data, - Index pos) { - auto d = std::make_unique(); - if (name) { - if (wasm.getDataSegmentOrNull(name)) { - // TODO: if the existing segment is not explicitly named, fix its name - // and continue. - return in.err(pos, "repeated data segment name"); - } - d->setExplicitName(name); - } else { - name = std::to_string(dataCounter++); - name = Names::getValidDataSegmentName(wasm, name); - d->name = name; - } - d->data = std::move(data); - dataDefs.push_back({name, pos, Index(wasm.dataSegments.size())}); - wasm.addDataSegment(std::move(d)); - return Ok{}; - } -}; - -// Phase 2: Parse type definitions into a TypeBuilder. -struct ParseTypeDefsCtx : TypeParserCtx { - ParseInput in; - - // We update slots in this builder as we parse type definitions. - TypeBuilder& builder; - - // Parse the names of types and fields as we go. - std::vector names; - - // The index of the subtype definition we are parsing. - Index index = 0; - - ParseTypeDefsCtx(std::string_view in, - TypeBuilder& builder, - const IndexMap& typeIndices) - : TypeParserCtx(typeIndices), in(in), builder(builder), - names(builder.size()) {} - - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return builder.getTempRefType(ht, nullability); - } - - TypeT makeTupleType(const std::vector types) { - return builder.getTempTupleType(types); - } - - Result getHeapTypeFromIdx(Index idx) { - if (idx >= builder.size()) { - return in.err("type index out of bounds"); - } - return builder[idx]; - } - - void addFuncType(SignatureT& type) { builder[index] = type; } - - void addStructType(StructT& type) { - auto& [fieldNames, str] = type; - builder[index] = str; - for (Index i = 0; i < fieldNames.size(); ++i) { - if (auto name = fieldNames[i]; name.is()) { - names[index].fieldNames[i] = name; - } - } - } - - void addArrayType(ArrayT& type) { builder[index] = type; } - - void setOpen() { builder[index].setOpen(); } - - Result<> addSubtype(Index super) { - if (super >= builder.size()) { - return in.err("supertype index out of bounds"); - } - builder[index].subTypeOf(builder[super]); - return Ok{}; - } - - void finishSubtype(Name name, Index pos) { names[index++].name = name; } - - size_t getRecGroupStartIndex() { return index; } - - void addRecGroup(Index start, size_t len) { - builder.createRecGroup(start, len); - } - - void finishDeftype(Index) {} -}; - -// Phase 3: Parse type uses to find implicitly defined types. -struct ParseImplicitTypeDefsCtx : TypeParserCtx { - using TypeUseT = Ok; - - ParseInput in; - - // Types parsed so far. - std::vector& types; - - // Map typeuse positions without an explicit type to the correct type. - std::unordered_map& implicitTypes; - - // Map signatures to the first defined heap type they match. - std::unordered_map sigTypes; - - ParseImplicitTypeDefsCtx(std::string_view in, - std::vector& types, - std::unordered_map& implicitTypes, - const IndexMap& typeIndices) - : TypeParserCtx(typeIndices), in(in), - types(types), implicitTypes(implicitTypes) { - for (auto type : types) { - if (type.isSignature() && type.getRecGroup().size() == 1) { - sigTypes.insert({type.getSignature(), type}); - } - } - } - - Result getHeapTypeFromIdx(Index idx) { - if (idx >= types.size()) { - return in.err("type index out of bounds"); - } - return types[idx]; - } - - Result makeTypeUse(Index pos, - std::optional, - ParamsT* params, - ResultsT* results) { - std::vector paramTypes; - if (params) { - paramTypes = getUnnamedTypes(*params); - } - - std::vector resultTypes; - if (results) { - resultTypes = *results; - } - - auto sig = Signature(Type(paramTypes), Type(resultTypes)); - auto [it, inserted] = sigTypes.insert({sig, HeapType::func}); - if (inserted) { - auto type = HeapType(sig); - it->second = type; - types.push_back(type); - } - implicitTypes.insert({pos, it->second}); - - return Ok{}; - } -}; - -// Phase 4: Parse and set the types of module elements. -struct ParseModuleTypesCtx : TypeParserCtx, - NullInstrParserCtx { - // In this phase we have constructed all the types, so we can materialize and - // validate them when they are used. - - using GlobalTypeT = GlobalType; - using TypeUseT = TypeUse; - - ParseInput in; - - Module& wasm; - - const std::vector& types; - const std::unordered_map& implicitTypes; - - // The index of the current type. - Index index = 0; - - ParseModuleTypesCtx(std::string_view in, - Module& wasm, - const std::vector& types, - const std::unordered_map& implicitTypes, - const IndexMap& typeIndices) - : TypeParserCtx(typeIndices), in(in), wasm(wasm), - types(types), implicitTypes(implicitTypes) {} - - Result getHeapTypeFromIdx(Index idx) { - if (idx >= types.size()) { - return in.err("type index out of bounds"); - } - return types[idx]; - } - - Result makeTypeUse(Index pos, - std::optional type, - ParamsT* params, - ResultsT* results) { - std::vector ids; - if (params) { - ids.reserve(params->size()); - for (auto& p : *params) { - ids.push_back(p.name); - } - } - - if (type) { - return TypeUse{*type, ids}; - } - - auto it = implicitTypes.find(pos); - assert(it != implicitTypes.end()); - - return TypeUse{it->second, ids}; - } - - Result getBlockTypeFromTypeUse(Index pos, TypeUse use) { - assert(use.type.isSignature()); - if (use.type.getSignature().params != Type::none) { - return in.err(pos, "block parameters not yet supported"); - } - // TODO: Once we support block parameters, return an error here if any of - // them are named. - return use.type; - } - - GlobalTypeT makeGlobalType(Mutability mutability, TypeT type) { - return {mutability, type}; - } - - Result<> addFunc(Name name, - const std::vector&, - ImportNames*, - TypeUse type, - std::optional locals, - std::optional, - Index pos) { - auto& f = wasm.functions[index]; - if (!type.type.isSignature()) { - return in.err(pos, "expected signature type"); - } - f->type = type.type; - for (Index i = 0; i < type.names.size(); ++i) { - if (type.names[i].is()) { - f->setLocalName(i, type.names[i]); - } - } - if (locals) { - for (auto& l : *locals) { - Builder::addVar(f.get(), l.name, l.type); - } - } - return Ok{}; - } - - Result<> - addMemory(Name, const std::vector&, ImportNames*, MemTypeT, Index) { - return Ok{}; - } - - Result<> addImplicitData(DataStringT&& data) { return Ok{}; } - - Result<> addGlobal(Name, - const std::vector&, - ImportNames*, - GlobalType type, - std::optional, - Index) { - auto& g = wasm.globals[index]; - g->mutable_ = type.mutability; - g->type = type.type; - return Ok{}; - } -}; - -// Phase 5: Parse module element definitions, including instructions. -struct ParseDefsCtx : TypeParserCtx { - using GlobalTypeT = Ok; - using TypeUseT = HeapType; - - // Keep track of instructions internally rather than letting the general - // parser collect them. - using InstrT = Ok; - using InstrsT = Ok; - using ExprT = Expression*; - - using FieldIdxT = Index; - using LocalIdxT = Index; - using GlobalIdxT = Name; - using MemoryIdxT = Name; - using DataIdxT = Name; - - using MemargT = Memarg; - - ParseInput in; - - Module& wasm; - Builder builder; - - const std::vector& types; - const std::unordered_map& implicitTypes; - - // The index of the current module element. - Index index = 0; - - // The current function being parsed, used to create scratch locals, type - // local.get, etc. - Function* func = nullptr; - - IRBuilder irBuilder; - - void setFunction(Function* func) { - this->func = func; - irBuilder.setFunction(func); - } - - ParseDefsCtx(std::string_view in, - Module& wasm, - const std::vector& types, - const std::unordered_map& implicitTypes, - const IndexMap& typeIndices) - : TypeParserCtx(typeIndices), in(in), wasm(wasm), builder(wasm), - types(types), implicitTypes(implicitTypes), irBuilder(wasm) {} - - template Result withLoc(Index pos, Result res) { - if (auto err = res.getErr()) { - return in.err(pos, err->msg); - } - return res; - } - - template Result withLoc(Result res) { - return withLoc(in.getPos(), res); - } - - HeapType getBlockTypeFromResult(const std::vector results) { - assert(results.size() == 1); - return HeapType(Signature(Type::none, results[0])); - } - - Result getBlockTypeFromTypeUse(Index pos, HeapType type) { - return type; - } - - Ok makeInstrs() { return Ok{}; } - - void appendInstr(Ok&, InstrT instr) {} - - Result finishInstrs(Ok&) { return Ok{}; } - - Result instrToExpr(Ok&) { return irBuilder.build(); } - - GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } - - Result getHeapTypeFromIdx(Index idx) { - if (idx >= types.size()) { - return in.err("type index out of bounds"); - } - return types[idx]; - } - - Result getFieldFromIdx(HeapType type, uint32_t idx) { - if (!type.isStruct()) { - return in.err("expected struct type"); - } - if (idx >= type.getStruct().fields.size()) { - return in.err("struct index out of bounds"); - } - return idx; - } - - Result getFieldFromName(HeapType type, Name name) { - // TODO: Field names - return in.err("symbolic field names note yet supported"); - } - - Result getLocalFromIdx(uint32_t idx) { - if (!func) { - return in.err("cannot access locals outside of a function"); - } - if (idx >= func->getNumLocals()) { - return in.err("local index out of bounds"); - } - return idx; - } - - Result getLocalFromName(Name name) { - if (!func) { - return in.err("cannot access locals outside of a function"); - } - if (!func->hasLocalIndex(name)) { - return in.err("local $" + name.toString() + " does not exist"); - } - return func->getLocalIndex(name); - } - - Result getGlobalFromIdx(uint32_t idx) { - if (idx >= wasm.globals.size()) { - return in.err("global index out of bounds"); - } - return wasm.globals[idx]->name; - } - - Result getGlobalFromName(Name name) { - if (!wasm.getGlobalOrNull(name)) { - return in.err("global $" + name.toString() + " does not exist"); - } - return name; - } - - Result getMemoryFromIdx(uint32_t idx) { - if (idx >= wasm.memories.size()) { - return in.err("memory index out of bounds"); - } - return wasm.memories[idx]->name; - } - - Result getMemoryFromName(Name name) { - if (!wasm.getMemoryOrNull(name)) { - return in.err("memory $" + name.toString() + " does not exist"); - } - return name; - } - - Result getDataFromIdx(uint32_t idx) { - if (idx >= wasm.dataSegments.size()) { - return in.err("data index out of bounds"); - } - return wasm.dataSegments[idx]->name; - } - - Result getDataFromName(Name name) { - if (!wasm.getDataSegmentOrNull(name)) { - return in.err("data $" + name.toString() + " does not exist"); - } - return name; - } - - Result makeTypeUse(Index pos, - std::optional type, - ParamsT* params, - ResultsT* results) { - if (type && (params || results)) { - std::vector paramTypes; - if (params) { - paramTypes = getUnnamedTypes(*params); - } - - std::vector resultTypes; - if (results) { - resultTypes = *results; - } - - auto sig = Signature(Type(paramTypes), Type(resultTypes)); - - if (!type->isSignature() || type->getSignature() != sig) { - return in.err(pos, "type does not match provided signature"); - } - } - - if (type) { - return *type; - } - - auto it = implicitTypes.find(pos); - assert(it != implicitTypes.end()); - return it->second; - } - - Result<> addFunc(Name, - const std::vector&, - ImportNames*, - TypeUseT, - std::optional, - std::optional, - Index pos) { - CHECK_ERR(withLoc(pos, irBuilder.visitEnd())); - auto body = irBuilder.build(); - CHECK_ERR(withLoc(pos, body)); - wasm.functions[index]->body = *body; - return Ok{}; - } - - Result<> addGlobal(Name, - const std::vector&, - ImportNames*, - GlobalTypeT, - std::optional exp, - Index) { - if (exp) { - wasm.globals[index]->init = *exp; - } - return Ok{}; - } - - Result<> addData( - Name, Name* mem, std::optional offset, DataStringT, Index pos) { - auto& d = wasm.dataSegments[index]; - if (offset) { - d->isPassive = false; - d->offset = *offset; - if (mem) { - d->memory = *mem; - } else if (wasm.memories.size() > 0) { - d->memory = wasm.memories[0]->name; - } else { - return in.err(pos, "active segment with no memory"); - } - } else { - d->isPassive = true; - } - return Ok{}; - } - - Result addScratchLocal(Index pos, Type type) { - if (!func) { - return in.err(pos, - "scratch local required, but there is no function context"); - } - Name name = Names::getValidLocalName(*func, "scratch"); - return Builder::addVar(func, name, type); - } - - Result makeExpr(InstrsT& instrs) { return irBuilder.build(); } - - Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; } - - Result getMemory(Index pos, Name* mem) { - if (mem) { - return *mem; - } - if (wasm.memories.empty()) { - return in.err(pos, "memory required, but there is no memory"); - } - return wasm.memories[0]->name; - } - - Result<> makeBlock(Index pos, std::optional label, HeapType type) { - // TODO: validate labels? - // TODO: Move error on input types to here? - return withLoc(pos, - irBuilder.makeBlock(label ? *label : Name{}, - type.getSignature().results)); - } - - Result<> finishBlock(Index pos, InstrsT) { - return withLoc(pos, irBuilder.visitEnd()); - } - - Result<> makeUnreachable(Index pos) { - return withLoc(pos, irBuilder.makeUnreachable()); - } - - Result<> makeNop(Index pos) { return withLoc(pos, irBuilder.makeNop()); } - - Result<> makeBinary(Index pos, BinaryOp op) { - return withLoc(pos, irBuilder.makeBinary(op)); - } - - Result<> makeUnary(Index pos, UnaryOp op) { - return withLoc(pos, irBuilder.makeUnary(op)); - } - - Result<> makeSelect(Index pos, std::vector* res) { - if (res && res->size()) { - if (res->size() > 1) { - return in.err(pos, "select may not have more than one result type"); - } - return withLoc(pos, irBuilder.makeSelect((*res)[0])); - } - return withLoc(pos, irBuilder.makeSelect()); - } - - Result<> makeDrop(Index pos) { return withLoc(pos, irBuilder.makeDrop()); } - - Result<> makeMemorySize(Index pos, Name* mem) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeMemorySize(*m)); - } - - Result<> makeMemoryGrow(Index pos, Name* mem) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeMemoryGrow(*m)); - } - - Result<> makeLocalGet(Index pos, Index local) { - return withLoc(pos, irBuilder.makeLocalGet(local)); - } - - Result<> makeLocalTee(Index pos, Index local) { - return withLoc(pos, irBuilder.makeLocalTee(local)); - } - - Result<> makeLocalSet(Index pos, Index local) { - return withLoc(pos, irBuilder.makeLocalSet(local)); - } - - Result<> makeGlobalGet(Index pos, Name global) { - return withLoc(pos, irBuilder.makeGlobalGet(global)); - } - - Result<> makeGlobalSet(Index pos, Name global) { - assert(wasm.getGlobalOrNull(global)); - return withLoc(pos, irBuilder.makeGlobalSet(global)); - } - - Result<> makeI32Const(Index pos, uint32_t c) { - return withLoc(pos, irBuilder.makeConst(Literal(c))); - } - - Result<> makeI64Const(Index pos, uint64_t c) { - return withLoc(pos, irBuilder.makeConst(Literal(c))); - } - - Result<> makeF32Const(Index pos, float c) { - return withLoc(pos, irBuilder.makeConst(Literal(c))); - } - - Result<> makeF64Const(Index pos, double c) { - return withLoc(pos, irBuilder.makeConst(Literal(c))); - } - - Result<> makeLoad(Index pos, - Type type, - bool signed_, - int bytes, - bool isAtomic, - Name* mem, - Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - if (isAtomic) { - return withLoc(pos, - irBuilder.makeAtomicLoad(bytes, memarg.offset, type, *m)); - } - return withLoc(pos, - irBuilder.makeLoad( - bytes, signed_, memarg.offset, memarg.align, type, *m)); - } - - Result<> makeStore( - Index pos, Type type, int bytes, bool isAtomic, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - if (isAtomic) { - return withLoc(pos, - irBuilder.makeAtomicStore(bytes, memarg.offset, type, *m)); - } - return withLoc( - pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m)); - } - - Result<> makeAtomicRMW( - Index pos, AtomicRMWOp op, Type type, int bytes, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, - irBuilder.makeAtomicRMW(op, bytes, memarg.offset, type, *m)); - } - - Result<> - makeAtomicCmpxchg(Index pos, Type type, int bytes, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, - irBuilder.makeAtomicCmpxchg(bytes, memarg.offset, type, *m)); - } - - Result<> makeAtomicWait(Index pos, Type type, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeAtomicWait(type, memarg.offset, *m)); - } - - Result<> makeAtomicNotify(Index pos, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeAtomicNotify(memarg.offset, *m)); - } - - Result<> makeAtomicFence(Index pos) { - return withLoc(pos, irBuilder.makeAtomicFence()); - } - - Result<> makeSIMDExtract(Index pos, SIMDExtractOp op, uint8_t lane) { - return withLoc(pos, irBuilder.makeSIMDExtract(op, lane)); - } - - Result<> makeSIMDReplace(Index pos, SIMDReplaceOp op, uint8_t lane) { - return withLoc(pos, irBuilder.makeSIMDReplace(op, lane)); - } - - Result<> makeSIMDShuffle(Index pos, const std::array& lanes) { - return withLoc(pos, irBuilder.makeSIMDShuffle(lanes)); - } - - Result<> makeSIMDTernary(Index pos, SIMDTernaryOp op) { - return withLoc(pos, irBuilder.makeSIMDTernary(op)); - } - - Result<> makeSIMDShift(Index pos, SIMDShiftOp op) { - return withLoc(pos, irBuilder.makeSIMDShift(op)); - } - - Result<> makeSIMDLoad(Index pos, SIMDLoadOp op, Name* mem, Memarg memarg) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, - irBuilder.makeSIMDLoad(op, memarg.offset, memarg.align, *m)); - } - - Result<> makeSIMDLoadStoreLane( - Index pos, SIMDLoadStoreLaneOp op, Name* mem, Memarg memarg, uint8_t lane) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, - irBuilder.makeSIMDLoadStoreLane( - op, memarg.offset, memarg.align, lane, *m)); - } - - Result<> makeMemoryInit(Index pos, Name* mem, Name data) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeMemoryInit(data, *m)); - } - - Result<> makeDataDrop(Index pos, Name data) { - return withLoc(pos, irBuilder.makeDataDrop(data)); - } - - Result<> makeMemoryCopy(Index pos, Name* destMem, Name* srcMem) { - auto destMemory = getMemory(pos, destMem); - CHECK_ERR(destMemory); - auto srcMemory = getMemory(pos, srcMem); - CHECK_ERR(srcMemory); - return withLoc(pos, irBuilder.makeMemoryCopy(*destMemory, *srcMemory)); - } - - Result<> makeMemoryFill(Index pos, Name* mem) { - auto m = getMemory(pos, mem); - CHECK_ERR(m); - return withLoc(pos, irBuilder.makeMemoryFill(*m)); - } - - Result<> makeReturn(Index pos) { - return withLoc(pos, irBuilder.makeReturn()); - } - - Result<> makeRefNull(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeRefNull(type)); - } - - Result<> makeRefIsNull(Index pos) { - return withLoc(pos, irBuilder.makeRefIsNull()); - } - - Result<> makeRefEq(Index pos) { return withLoc(pos, irBuilder.makeRefEq()); } - - Result<> makeRefI31(Index pos) { - return withLoc(pos, irBuilder.makeRefI31()); - } - - Result<> makeI31Get(Index pos, bool signed_) { - return withLoc(pos, irBuilder.makeI31Get(signed_)); - } - - Result<> makeStructNew(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeStructNew(type)); - } - - Result<> makeStructNewDefault(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeStructNewDefault(type)); - } - - Result<> makeStructGet(Index pos, HeapType type, Index field, bool signed_) { - return withLoc(pos, irBuilder.makeStructGet(type, field, signed_)); - } - - Result<> makeStructSet(Index pos, HeapType type, Index field) { - return withLoc(pos, irBuilder.makeStructSet(type, field)); - } - - Result<> makeArrayNew(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeArrayNew(type)); - } - - Result<> makeArrayNewDefault(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeArrayNewDefault(type)); - } - - Result<> makeArrayNewData(Index pos, HeapType type, Name data) { - return withLoc(pos, irBuilder.makeArrayNewData(type, data)); - } - - Result<> makeArrayNewElem(Index pos, HeapType type, Name elem) { - return withLoc(pos, irBuilder.makeArrayNewElem(type, elem)); - } - - Result<> makeArrayGet(Index pos, HeapType type, bool signed_) { - return withLoc(pos, irBuilder.makeArrayGet(type, signed_)); - } - - Result<> makeArraySet(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeArraySet(type)); - } - - Result<> makeArrayLen(Index pos) { - return withLoc(pos, irBuilder.makeArrayLen()); - } - - Result<> makeArrayCopy(Index pos, HeapType destType, HeapType srcType) { - return withLoc(pos, irBuilder.makeArrayCopy(destType, srcType)); - } - - Result<> makeArrayFill(Index pos, HeapType type) { - return withLoc(pos, irBuilder.makeArrayFill(type)); - } -}; - -// ================ -// Parser Functions -// ================ - -// Types -template Result heaptype(Ctx&); -template MaybeResult reftype(Ctx&); -template Result valtype(Ctx&); -template MaybeResult params(Ctx&); -template MaybeResult results(Ctx&); -template MaybeResult functype(Ctx&); -template Result storagetype(Ctx&); -template Result fieldtype(Ctx&); -template Result fields(Ctx&); -template MaybeResult structtype(Ctx&); -template MaybeResult arraytype(Ctx&); -template Result limits32(Ctx&); -template Result limits64(Ctx&); -template Result memtype(Ctx&); -template Result globaltype(Ctx&); - -// Instructions -template MaybeResult foldedBlockinstr(Ctx&); -template -MaybeResult unfoldedBlockinstr(Ctx&); -template MaybeResult blockinstr(Ctx&); -template MaybeResult plaininstr(Ctx&); -template MaybeResult instr(Ctx&); -template Result instrs(Ctx&); -template Result expr(Ctx&); -template Result memarg(Ctx&, uint32_t); -template Result blocktype(Ctx&); -template MaybeResult block(Ctx&, bool); -template -Result makeUnreachable(Ctx&, Index); -template Result makeNop(Ctx&, Index); -template -Result makeBinary(Ctx&, Index, BinaryOp op); -template -Result makeUnary(Ctx&, Index, UnaryOp op); -template Result makeSelect(Ctx&, Index); -template Result makeDrop(Ctx&, Index); -template Result makeMemorySize(Ctx&, Index); -template Result makeMemoryGrow(Ctx&, Index); -template Result makeLocalGet(Ctx&, Index); -template Result makeLocalTee(Ctx&, Index); -template Result makeLocalSet(Ctx&, Index); -template Result makeGlobalGet(Ctx&, Index); -template Result makeGlobalSet(Ctx&, Index); -template Result makeBlock(Ctx&, Index); -template Result makeThenOrElse(Ctx&, Index); -template -Result makeConst(Ctx&, Index, Type type); -template -Result -makeLoad(Ctx&, Index, Type type, bool signed_, int bytes, bool isAtomic); -template -Result -makeStore(Ctx&, Index, Type type, int bytes, bool isAtomic); -template -Result -makeAtomicRMW(Ctx&, Index, AtomicRMWOp op, Type type, uint8_t bytes); -template -Result -makeAtomicCmpxchg(Ctx&, Index, Type type, uint8_t bytes); -template -Result makeAtomicWait(Ctx&, Index, Type type); -template -Result makeAtomicNotify(Ctx&, Index); -template -Result makeAtomicFence(Ctx&, Index); -template -Result -makeSIMDExtract(Ctx&, Index, SIMDExtractOp op, size_t lanes); -template -Result -makeSIMDReplace(Ctx&, Index, SIMDReplaceOp op, size_t lanes); -template -Result makeSIMDShuffle(Ctx&, Index); -template -Result makeSIMDTernary(Ctx&, Index, SIMDTernaryOp op); -template -Result makeSIMDShift(Ctx&, Index, SIMDShiftOp op); -template -Result -makeSIMDLoad(Ctx&, Index, SIMDLoadOp op, int bytes); -template -Result -makeSIMDLoadStoreLane(Ctx&, Index, SIMDLoadStoreLaneOp op, int bytes); -template Result makeMemoryInit(Ctx&, Index); -template Result makeDataDrop(Ctx&, Index); -template Result makeMemoryCopy(Ctx&, Index); -template Result makeMemoryFill(Ctx&, Index); -template Result makePop(Ctx&, Index); -template Result makeIf(Ctx&, Index); -template -Result makeMaybeBlock(Ctx&, Index, size_t i, Type type); -template Result makeLoop(Ctx&, Index); -template -Result makeCall(Ctx&, Index, bool isReturn); -template -Result makeCallIndirect(Ctx&, Index, bool isReturn); -template Result makeBreak(Ctx&, Index); -template Result makeBreakTable(Ctx&, Index); -template Result makeReturn(Ctx&, Index); -template Result makeRefNull(Ctx&, Index); -template Result makeRefIsNull(Ctx&, Index); -template Result makeRefFunc(Ctx&, Index); -template Result makeRefEq(Ctx&, Index); -template Result makeTableGet(Ctx&, Index); -template Result makeTableSet(Ctx&, Index); -template Result makeTableSize(Ctx&, Index); -template Result makeTableGrow(Ctx&, Index); -template Result makeTableFill(Ctx&, Index); -template Result makeTry(Ctx&, Index); -template -Result -makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry); -template Result makeThrow(Ctx&, Index); -template Result makeRethrow(Ctx&, Index); -template Result makeTupleMake(Ctx&, Index); -template -Result makeTupleExtract(Ctx&, Index); -template -Result makeCallRef(Ctx&, Index, bool isReturn); -template Result makeRefI31(Ctx&, Index); -template -Result makeI31Get(Ctx&, Index, bool signed_); -template Result makeRefTest(Ctx&, Index); -template Result makeRefCast(Ctx&, Index); -template -Result makeBrOnNull(Ctx&, Index, bool onFail = false); -template -Result makeBrOnCast(Ctx&, Index, bool onFail = false); -template -Result makeStructNew(Ctx&, Index, bool default_); -template -Result makeStructGet(Ctx&, Index, bool signed_ = false); -template Result makeStructSet(Ctx&, Index); -template -Result makeArrayNew(Ctx&, Index, bool default_); -template -Result makeArrayNewData(Ctx&, Index); -template -Result makeArrayNewElem(Ctx&, Index); -template -Result makeArrayNewFixed(Ctx&, Index); -template -Result makeArrayGet(Ctx&, Index, bool signed_ = false); -template Result makeArraySet(Ctx&, Index); -template Result makeArrayLen(Ctx&, Index); -template Result makeArrayCopy(Ctx&, Index); -template Result makeArrayFill(Ctx&, Index); -template -Result makeArrayInitData(Ctx&, Index); -template -Result makeArrayInitElem(Ctx&, Index); -template -Result makeRefAs(Ctx&, Index, RefAsOp op); -template -Result -makeStringNew(Ctx&, Index, StringNewOp op, bool try_); -template -Result makeStringConst(Ctx&, Index); -template -Result makeStringMeasure(Ctx&, Index, StringMeasureOp op); -template -Result makeStringEncode(Ctx&, Index, StringEncodeOp op); -template -Result makeStringConcat(Ctx&, Index); -template -Result makeStringEq(Ctx&, Index, StringEqOp); -template -Result makeStringAs(Ctx&, Index, StringAsOp op); -template -Result makeStringWTF8Advance(Ctx&, Index); -template -Result makeStringWTF16Get(Ctx&, Index); -template -Result makeStringIterNext(Ctx&, Index); -template -Result -makeStringIterMove(Ctx&, Index, StringIterMoveOp op); -template -Result -makeStringSliceWTF(Ctx&, Index, StringSliceWTFOp op); -template -Result makeStringSliceIter(Ctx&, Index); - -// Modules -template MaybeResult maybeTypeidx(Ctx& ctx); -template Result typeidx(Ctx&); -template -Result fieldidx(Ctx&, typename Ctx::HeapTypeT); -template MaybeResult maybeMemidx(Ctx&); -template Result memidx(Ctx&); -template MaybeResult maybeMemuse(Ctx&); -template Result globalidx(Ctx&); -template Result localidx(Ctx&); -template Result typeuse(Ctx&); -MaybeResult inlineImport(ParseInput&); -Result> inlineExports(ParseInput&); -template Result<> strtype(Ctx&); -template MaybeResult subtype(Ctx&); -template MaybeResult<> deftype(Ctx&); -template MaybeResult locals(Ctx&); -template MaybeResult<> func(Ctx&); -template MaybeResult<> memory(Ctx&); -template MaybeResult<> global(Ctx&); -template Result datastring(Ctx&); -template MaybeResult<> data(Ctx&); -MaybeResult<> modulefield(ParseDeclsCtx&); -Result<> module(ParseDeclsCtx&); - -// ===== -// Types -// ===== - -// heaptype ::= x:typeidx => types[x] -// | 'func' => func -// | 'extern' => extern -template Result heaptype(Ctx& ctx) { - if (ctx.in.takeKeyword("func"sv)) { - return ctx.makeFunc(); - } - if (ctx.in.takeKeyword("any"sv)) { - return ctx.makeAny(); - } - if (ctx.in.takeKeyword("extern"sv)) { - return ctx.makeExtern(); - } - if (ctx.in.takeKeyword("eq"sv)) { - return ctx.makeEq(); - } - if (ctx.in.takeKeyword("i31"sv)) { - return ctx.makeI31(); - } - if (ctx.in.takeKeyword("struct"sv)) { - return ctx.makeStructType(); - } - if (ctx.in.takeKeyword("array"sv)) { - return ctx.makeArrayType(); - } - auto type = typeidx(ctx); - CHECK_ERR(type); - return *type; -} - -// reftype ::= 'funcref' => funcref -// | 'externref' => externref -// | 'anyref' => anyref -// | 'eqref' => eqref -// | 'i31ref' => i31ref -// | 'structref' => structref -// | 'arrayref' => arrayref -// | '(' ref null? t:heaptype ')' => ref null? t -template MaybeResult reftype(Ctx& ctx) { - if (ctx.in.takeKeyword("funcref"sv)) { - return ctx.makeRefType(ctx.makeFunc(), Nullable); - } - if (ctx.in.takeKeyword("externref"sv)) { - return ctx.makeRefType(ctx.makeExtern(), Nullable); - } - if (ctx.in.takeKeyword("anyref"sv)) { - return ctx.makeRefType(ctx.makeAny(), Nullable); - } - if (ctx.in.takeKeyword("eqref"sv)) { - return ctx.makeRefType(ctx.makeEq(), Nullable); - } - if (ctx.in.takeKeyword("i31ref"sv)) { - return ctx.makeRefType(ctx.makeI31(), Nullable); - } - if (ctx.in.takeKeyword("structref"sv)) { - return ctx.makeRefType(ctx.makeStructType(), Nullable); - } - if (ctx.in.takeKeyword("arrayref"sv)) { - return ctx.in.err("arrayref not yet supported"); - } - - if (!ctx.in.takeSExprStart("ref"sv)) { - return {}; - } - - auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable; - - auto type = heaptype(ctx); - CHECK_ERR(type); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of reftype"); - } - - return ctx.makeRefType(*type, nullability); -} - -// numtype ::= 'i32' => i32 -// | 'i64' => i64 -// | 'f32' => f32 -// | 'f64' => f64 -// vectype ::= 'v128' => v128 -// valtype ::= t:numtype => t -// | t:vectype => t -// | t:reftype => t -template Result valtype(Ctx& ctx) { - if (ctx.in.takeKeyword("i32"sv)) { - return ctx.makeI32(); - } else if (ctx.in.takeKeyword("i64"sv)) { - return ctx.makeI64(); - } else if (ctx.in.takeKeyword("f32"sv)) { - return ctx.makeF32(); - } else if (ctx.in.takeKeyword("f64"sv)) { - return ctx.makeF64(); - } else if (ctx.in.takeKeyword("v128"sv)) { - return ctx.makeV128(); - } else if (auto type = reftype(ctx)) { - CHECK_ERR(type); - return *type; - } else { - return ctx.in.err("expected valtype"); - } -} - -// param ::= '(' 'param id? t:valtype ')' => [t] -// | '(' 'param t*:valtype* ')' => [t*] -// params ::= param* -template MaybeResult params(Ctx& ctx) { - bool hasAny = false; - auto res = ctx.makeParams(); - while (ctx.in.takeSExprStart("param"sv)) { - hasAny = true; - if (auto id = ctx.in.takeID()) { - // Single named param - auto type = valtype(ctx); - CHECK_ERR(type); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of param"); - } - ctx.appendParam(res, *id, *type); - } else { - // Repeated unnamed params - while (!ctx.in.takeRParen()) { - auto type = valtype(ctx); - CHECK_ERR(type); - ctx.appendParam(res, {}, *type); - } - } - } - if (hasAny) { - return res; - } - return {}; -} - -// result ::= '(' 'result' t*:valtype ')' => [t*] -// results ::= result* -template MaybeResult results(Ctx& ctx) { - bool hasAny = false; - auto res = ctx.makeResults(); - while (ctx.in.takeSExprStart("result"sv)) { - hasAny = true; - while (!ctx.in.takeRParen()) { - auto type = valtype(ctx); - CHECK_ERR(type); - ctx.appendResult(res, *type); - } - } - if (hasAny) { - return res; - } - return {}; -} - -// functype ::= '(' 'func' t1*:vec(param) t2*:vec(result) ')' => [t1*] -> [t2*] -template -MaybeResult functype(Ctx& ctx) { - if (!ctx.in.takeSExprStart("func"sv)) { - return {}; - } - - auto parsedParams = params(ctx); - CHECK_ERR(parsedParams); - - auto parsedResults = results(ctx); - CHECK_ERR(parsedResults); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of functype"); - } - - return ctx.makeFuncType(parsedParams.getPtr(), parsedResults.getPtr()); -} - -// storagetype ::= valtype | packedtype -// packedtype ::= i8 | i16 -template Result storagetype(Ctx& ctx) { - if (ctx.in.takeKeyword("i8"sv)) { - return ctx.makeI8(); - } - if (ctx.in.takeKeyword("i16"sv)) { - return ctx.makeI16(); - } - auto type = valtype(ctx); - CHECK_ERR(type); - return ctx.makeStorageType(*type); -} - -// fieldtype ::= t:storagetype => const t -// | '(' 'mut' t:storagetype ')' => var t -template Result fieldtype(Ctx& ctx) { - auto mutability = Immutable; - if (ctx.in.takeSExprStart("mut"sv)) { - mutability = Mutable; - } - - auto field = storagetype(ctx); - CHECK_ERR(field); - - if (mutability == Mutable) { - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of field type"); - } - } - - return ctx.makeFieldType(*field, mutability); -} - -// field ::= '(' 'field' id t:fieldtype ')' => [(id, t)] -// | '(' 'field' t*:fieldtype* ')' => [(_, t*)*] -// | fieldtype -template Result fields(Ctx& ctx) { - auto res = ctx.makeFields(); - while (true) { - if (auto t = ctx.in.peek(); !t || t->isRParen()) { - return res; - } - if (ctx.in.takeSExprStart("field")) { - if (auto id = ctx.in.takeID()) { - auto field = fieldtype(ctx); - CHECK_ERR(field); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of field"); - } - ctx.appendField(res, *id, *field); - } else { - while (!ctx.in.takeRParen()) { - auto field = fieldtype(ctx); - CHECK_ERR(field); - ctx.appendField(res, {}, *field); - } - } - } else { - auto field = fieldtype(ctx); - CHECK_ERR(field); - ctx.appendField(res, {}, *field); - } - } -} - -// structtype ::= '(' 'struct' field* ')' -template MaybeResult structtype(Ctx& ctx) { - if (!ctx.in.takeSExprStart("struct"sv)) { - return {}; - } - auto namedFields = fields(ctx); - CHECK_ERR(namedFields); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of struct definition"); - } - - return ctx.makeStruct(*namedFields); -} - -// arraytype ::= '(' 'array' field ')' -template MaybeResult arraytype(Ctx& ctx) { - if (!ctx.in.takeSExprStart("array"sv)) { - return {}; - } - auto namedFields = fields(ctx); - CHECK_ERR(namedFields); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of array definition"); - } - - if (auto array = ctx.makeArray(*namedFields)) { - return *array; - } - return ctx.in.err("expected exactly one field in array definition"); -} - -// limits32 ::= n:u32 m:u32? -template Result limits32(Ctx& ctx) { - auto n = ctx.in.takeU32(); - if (!n) { - return ctx.in.err("expected initial size"); - } - std::optional m = ctx.in.takeU32(); - return ctx.makeLimits(uint64_t(*n), m); -} - -// limits64 ::= n:u64 m:u64? -template Result limits64(Ctx& ctx) { - auto n = ctx.in.takeU64(); - if (!n) { - return ctx.in.err("expected initial size"); - } - std::optional m = ctx.in.takeU64(); - return ctx.makeLimits(uint64_t(*n), m); -} - -// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared? -template Result memtype(Ctx& ctx) { - auto type = Type::i32; - if (ctx.in.takeKeyword("i64"sv)) { - type = Type::i64; - } else { - ctx.in.takeKeyword("i32"sv); - } - auto limits = type == Type::i32 ? limits32(ctx) : limits64(ctx); - CHECK_ERR(limits); - bool shared = false; - if (ctx.in.takeKeyword("shared"sv)) { - shared = true; - } - return ctx.makeMemType(type, *limits, shared); -} - -// globaltype ::= t:valtype => const t -// | '(' 'mut' t:valtype ')' => var t -template Result globaltype(Ctx& ctx) { - auto mutability = Immutable; - if (ctx.in.takeSExprStart("mut"sv)) { - mutability = Mutable; - } - - auto type = valtype(ctx); - CHECK_ERR(type); - - if (mutability == Mutable && !ctx.in.takeRParen()) { - return ctx.in.err("expected end of globaltype"); - } - - return ctx.makeGlobalType(mutability, *type); -} - -// ============ -// Instructions -// ============ - -// blockinstr ::= block | loop | if-else | try-catch -template -MaybeResult foldedBlockinstr(Ctx& ctx) { - if (auto i = block(ctx, true)) { - return i; - } - // TODO: Other block instructions - return {}; -} - -template -MaybeResult unfoldedBlockinstr(Ctx& ctx) { - if (auto i = block(ctx, false)) { - return i; - } - // TODO: Other block instructions - return {}; -} - -template MaybeResult blockinstr(Ctx& ctx) { - if (auto i = foldedBlockinstr(ctx)) { - return i; - } - if (auto i = unfoldedBlockinstr(ctx)) { - return i; - } - return {}; -} - -// plaininstr ::= ... all plain instructions ... -template MaybeResult plaininstr(Ctx& ctx) { - auto pos = ctx.in.getPos(); - auto keyword = ctx.in.takeKeyword(); - if (!keyword) { - return {}; - } - -#define NEW_INSTRUCTION_PARSER -#define NEW_WAT_PARSER -#include -} - -// instr ::= plaininstr | blockinstr -template MaybeResult instr(Ctx& ctx) { - // Check for valid strings that are not instructions. - if (auto tok = ctx.in.peek()) { - if (auto keyword = tok->getKeyword()) { - if (keyword == "end"sv) { - return {}; - } - } - } - if (auto i = blockinstr(ctx)) { - return i; - } - if (auto i = plaininstr(ctx)) { - return i; - } - // TODO: Handle folded plain instructions as well. - return {}; -} - -template Result instrs(Ctx& ctx) { - auto insts = ctx.makeInstrs(); - - while (true) { - if (auto blockinst = foldedBlockinstr(ctx)) { - CHECK_ERR(blockinst); - ctx.appendInstr(insts, *blockinst); - continue; - } - // Parse an arbitrary number of folded instructions. - if (ctx.in.takeLParen()) { - // A stack of (start, end) position pairs defining the positions of - // instructions that need to be parsed after their folded children. - std::vector>> foldedInstrs; - - // Begin a folded instruction. Push its start position and a placeholder - // end position. - foldedInstrs.push_back({ctx.in.getPos(), {}}); - while (!foldedInstrs.empty()) { - // Consume everything up to the next paren. This span will be parsed as - // an instruction later after its folded children have been parsed. - if (!ctx.in.takeUntilParen()) { - return ctx.in.err(foldedInstrs.back().first, - "unterminated folded instruction"); - } - - if (!foldedInstrs.back().second) { - // The folded instruction we just started should end here. - foldedInstrs.back().second = ctx.in.getPos(); - } - - // We have either the start of a new folded child or the end of the last - // one. - if (auto blockinst = foldedBlockinstr(ctx)) { - CHECK_ERR(blockinst); - ctx.appendInstr(insts, *blockinst); - } else if (ctx.in.takeLParen()) { - foldedInstrs.push_back({ctx.in.getPos(), {}}); - } else if (ctx.in.takeRParen()) { - auto [start, end] = foldedInstrs.back(); - assert(end && "Should have found end of instruction"); - foldedInstrs.pop_back(); - - WithPosition with(ctx, start); - if (auto inst = plaininstr(ctx)) { - CHECK_ERR(inst); - ctx.appendInstr(insts, *inst); - } else { - return ctx.in.err(start, "expected folded instruction"); - } - - if (ctx.in.getPos() != *end) { - return ctx.in.err("expected end of instruction"); - } - } else { - WASM_UNREACHABLE("expected paren"); - } - } - continue; - } - - // A non-folded instruction. - if (auto inst = instr(ctx)) { - CHECK_ERR(inst); - ctx.appendInstr(insts, *inst); - } else { - break; - } - } - - return ctx.finishInstrs(insts); -} - -template Result expr(Ctx& ctx) { - auto insts = instrs(ctx); - CHECK_ERR(insts); - return ctx.makeExpr(*insts); -} - -// memarg_n ::= o:offset a:align_n -// offset ::= 'offset='o:u64 => o | _ => 0 -// align_n ::= 'align='a:u32 => a | _ => n -template -Result memarg(Ctx& ctx, uint32_t n) { - uint64_t offset = 0; - uint32_t align = n; - if (auto o = ctx.in.takeOffset()) { - offset = *o; - } - if (auto a = ctx.in.takeAlign()) { - align = *a; - } - return ctx.getMemarg(offset, align); -} - -// blocktype ::= (t:result)? => t? | x,I:typeuse => x if I = {} -template Result blocktype(Ctx& ctx) { - auto pos = ctx.in.getPos(); - - if (auto res = results(ctx)) { - CHECK_ERR(res); - if (ctx.getResultsSize(*res) == 1) { - return ctx.getBlockTypeFromResult(*res); - } - } - - // We either had no results or multiple results. Reset and parse again as a - // type use. - ctx.in.lexer.setIndex(pos); - auto use = typeuse(ctx); - CHECK_ERR(use); - - auto type = ctx.getBlockTypeFromTypeUse(pos, *use); - CHECK_ERR(type); - return *type; -} - -// block ::= 'block' label blocktype instr* 'end' id? if id = {} or id = label -// | '(' 'block' label blocktype instr* ')' -template -MaybeResult block(Ctx& ctx, bool folded) { - auto pos = ctx.in.getPos(); - - if (folded) { - if (!ctx.in.takeSExprStart("block"sv)) { - return {}; - } - } else { - if (!ctx.in.takeKeyword("block"sv)) { - return {}; - } - } - - auto label = ctx.in.takeID(); - - auto type = blocktype(ctx); - CHECK_ERR(type); - - ctx.makeBlock(pos, label, *type); - - auto insts = instrs(ctx); - CHECK_ERR(insts); - - if (folded) { - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected ')' at end of block"); - } - } else { - if (!ctx.in.takeKeyword("end"sv)) { - return ctx.in.err("expected 'end' at end of block"); - } - auto id = ctx.in.takeID(); - if (id && id != label) { - return ctx.in.err("end label does not match block label"); - } - } - - return ctx.finishBlock(pos, std::move(*insts)); -} - -template -Result makeUnreachable(Ctx& ctx, Index pos) { - return ctx.makeUnreachable(pos); -} - -template -Result makeNop(Ctx& ctx, Index pos) { - return ctx.makeNop(pos); -} - -template -Result makeBinary(Ctx& ctx, Index pos, BinaryOp op) { - return ctx.makeBinary(pos, op); -} - -template -Result makeUnary(Ctx& ctx, Index pos, UnaryOp op) { - return ctx.makeUnary(pos, op); -} - -template -Result makeSelect(Ctx& ctx, Index pos) { - auto res = results(ctx); - CHECK_ERR(res); - return ctx.makeSelect(pos, res.getPtr()); -} - -template -Result makeDrop(Ctx& ctx, Index pos) { - return ctx.makeDrop(pos); -} - -template -Result makeMemorySize(Ctx& ctx, Index pos) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - return ctx.makeMemorySize(pos, mem.getPtr()); -} - -template -Result makeMemoryGrow(Ctx& ctx, Index pos) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - return ctx.makeMemoryGrow(pos, mem.getPtr()); -} - -template -Result makeLocalGet(Ctx& ctx, Index pos) { - auto local = localidx(ctx); - CHECK_ERR(local); - return ctx.makeLocalGet(pos, *local); -} - -template -Result makeLocalTee(Ctx& ctx, Index pos) { - auto local = localidx(ctx); - CHECK_ERR(local); - return ctx.makeLocalTee(pos, *local); -} - -template -Result makeLocalSet(Ctx& ctx, Index pos) { - auto local = localidx(ctx); - CHECK_ERR(local); - return ctx.makeLocalSet(pos, *local); -} - -template -Result makeGlobalGet(Ctx& ctx, Index pos) { - auto global = globalidx(ctx); - CHECK_ERR(global); - return ctx.makeGlobalGet(pos, *global); -} - -template -Result makeGlobalSet(Ctx& ctx, Index pos) { - auto global = globalidx(ctx); - CHECK_ERR(global); - return ctx.makeGlobalSet(pos, *global); -} - -template -Result makeBlock(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeThenOrElse(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeConst(Ctx& ctx, Index pos, Type type) { - assert(type.isBasic()); - switch (type.getBasic()) { - case Type::i32: - if (auto c = ctx.in.takeI32()) { - return ctx.makeI32Const(pos, *c); - } - return ctx.in.err("expected i32"); - case Type::i64: - if (auto c = ctx.in.takeI64()) { - return ctx.makeI64Const(pos, *c); - } - return ctx.in.err("expected i64"); - case Type::f32: - if (auto c = ctx.in.takeF32()) { - return ctx.makeF32Const(pos, *c); - } - return ctx.in.err("expected f32"); - case Type::f64: - if (auto c = ctx.in.takeF64()) { - return ctx.makeF64Const(pos, *c); - } - return ctx.in.err("expected f64"); - case Type::v128: - return ctx.in.err("unimplemented instruction"); - case Type::none: - case Type::unreachable: - break; - } - WASM_UNREACHABLE("unexpected type"); -} - -template -Result makeLoad( - Ctx& ctx, Index pos, Type type, bool signed_, int bytes, bool isAtomic) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - return ctx.makeLoad(pos, type, signed_, bytes, isAtomic, mem.getPtr(), *arg); -} - -template -Result -makeStore(Ctx& ctx, Index pos, Type type, int bytes, bool isAtomic) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - return ctx.makeStore(pos, type, bytes, isAtomic, mem.getPtr(), *arg); -} - -template -Result -makeAtomicRMW(Ctx& ctx, Index pos, AtomicRMWOp op, Type type, uint8_t bytes) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - return ctx.makeAtomicRMW(pos, op, type, bytes, mem.getPtr(), *arg); -} - -template -Result -makeAtomicCmpxchg(Ctx& ctx, Index pos, Type type, uint8_t bytes) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - return ctx.makeAtomicCmpxchg(pos, type, bytes, mem.getPtr(), *arg); -} - -template -Result makeAtomicWait(Ctx& ctx, Index pos, Type type) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, type == Type::i32 ? 4 : 8); - CHECK_ERR(arg); - return ctx.makeAtomicWait(pos, type, mem.getPtr(), *arg); -} - -template -Result makeAtomicNotify(Ctx& ctx, Index pos) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, 4); - CHECK_ERR(arg); - return ctx.makeAtomicNotify(pos, mem.getPtr(), *arg); -} - -template -Result makeAtomicFence(Ctx& ctx, Index pos) { - return ctx.makeAtomicFence(pos); -} - -template -Result -makeSIMDExtract(Ctx& ctx, Index pos, SIMDExtractOp op, size_t) { - auto lane = ctx.in.takeU8(); - if (!lane) { - return ctx.in.err("expected lane index"); - } - return ctx.makeSIMDExtract(pos, op, *lane); -} - -template -Result -makeSIMDReplace(Ctx& ctx, Index pos, SIMDReplaceOp op, size_t lanes) { - auto lane = ctx.in.takeU8(); - if (!lane) { - return ctx.in.err("expected lane index"); - } - return ctx.makeSIMDReplace(pos, op, *lane); -} - -template -Result makeSIMDShuffle(Ctx& ctx, Index pos) { - std::array lanes; - for (int i = 0; i < 16; ++i) { - auto lane = ctx.in.takeU8(); - if (!lane) { - return ctx.in.err("expected lane index"); - } - lanes[i] = *lane; - } - return ctx.makeSIMDShuffle(pos, lanes); -} - -template -Result -makeSIMDTernary(Ctx& ctx, Index pos, SIMDTernaryOp op) { - return ctx.makeSIMDTernary(pos, op); -} - -template -Result -makeSIMDShift(Ctx& ctx, Index pos, SIMDShiftOp op) { - return ctx.makeSIMDShift(pos, op); -} - -template -Result -makeSIMDLoad(Ctx& ctx, Index pos, SIMDLoadOp op, int bytes) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - return ctx.makeSIMDLoad(pos, op, mem.getPtr(), *arg); -} - -template -Result -makeSIMDLoadStoreLane(Ctx& ctx, Index pos, SIMDLoadStoreLaneOp op, int bytes) { - auto reset = ctx.in.getPos(); - - auto retry = [&]() -> Result { - // We failed to parse. Maybe the lane index was accidentally parsed as the - // optional memory index. Try again without parsing a memory index. - WithPosition with(ctx, reset); - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - auto lane = ctx.in.takeU8(); - if (!lane) { - return ctx.in.err("expected lane index"); - } - return ctx.makeSIMDLoadStoreLane(pos, op, nullptr, *arg, *lane); - }; - - auto mem = maybeMemidx(ctx); - if (mem.getErr()) { - return retry(); - } - auto arg = memarg(ctx, bytes); - CHECK_ERR(arg); - auto lane = ctx.in.takeU8(); - if (!lane) { - return retry(); - } - return ctx.makeSIMDLoadStoreLane(pos, op, mem.getPtr(), *arg, *lane); -} - -template -Result makeMemoryInit(Ctx& ctx, Index pos) { - auto reset = ctx.in.getPos(); - - auto retry = [&]() -> Result { - // We failed to parse. Maybe the data index was accidentally parsed as the - // optional memory index. Try again without parsing a memory index. - WithPosition with(ctx, reset); - auto data = dataidx(ctx); - CHECK_ERR(data); - return ctx.makeMemoryInit(pos, nullptr, *data); - }; - - auto mem = maybeMemidx(ctx); - if (mem.getErr()) { - return retry(); - } - auto data = dataidx(ctx); - if (data.getErr()) { - return retry(); - } - return ctx.makeMemoryInit(pos, mem.getPtr(), *data); -} - -template -Result makeDataDrop(Ctx& ctx, Index pos) { - auto data = dataidx(ctx); - CHECK_ERR(data); - return ctx.makeDataDrop(pos, *data); -} - -template -Result makeMemoryCopy(Ctx& ctx, Index pos) { - auto destMem = maybeMemidx(ctx); - CHECK_ERR(destMem); - std::optional srcMem = std::nullopt; - if (destMem) { - auto mem = memidx(ctx); - CHECK_ERR(mem); - srcMem = *mem; - } - return ctx.makeMemoryCopy(pos, destMem.getPtr(), srcMem ? &*srcMem : nullptr); -} - -template -Result makeMemoryFill(Ctx& ctx, Index pos) { - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - return ctx.makeMemoryFill(pos, mem.getPtr()); -} - -template -Result makePop(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeIf(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeMaybeBlock(Ctx& ctx, Index pos, size_t i, Type type) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeLoop(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeCall(Ctx& ctx, Index pos, bool isReturn) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeCallIndirect(Ctx& ctx, Index pos, bool isReturn) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeBreak(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeBreakTable(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeReturn(Ctx& ctx, Index pos) { - return ctx.makeReturn(pos); -} - -template -Result makeRefNull(Ctx& ctx, Index pos) { - auto t = heaptype(ctx); - CHECK_ERR(t); - return ctx.makeRefNull(pos, *t); -} - -template -Result makeRefIsNull(Ctx& ctx, Index pos) { - return ctx.makeRefIsNull(pos); -} - -template -Result makeRefFunc(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeRefEq(Ctx& ctx, Index pos) { - return ctx.makeRefEq(pos); -} - -template -Result makeTableGet(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTableSet(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTableSize(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTableGrow(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTableFill(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTry(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeThrow(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeRethrow(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTupleMake(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeTupleExtract(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeCallRef(Ctx& ctx, Index pos, bool isReturn) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeRefI31(Ctx& ctx, Index pos) { - return ctx.makeRefI31(pos); -} - -template -Result makeI31Get(Ctx& ctx, Index pos, bool signed_) { - return ctx.makeI31Get(pos, signed_); -} - -template -Result makeRefTest(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeRefCast(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeBrOnNull(Ctx& ctx, Index pos, bool onFail) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeBrOnCast(Ctx& ctx, Index pos, bool onFail) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStructNew(Ctx& ctx, Index pos, bool default_) { - auto type = typeidx(ctx); - CHECK_ERR(type); - if (default_) { - return ctx.makeStructNewDefault(pos, *type); - } - return ctx.makeStructNew(pos, *type); -} - -template -Result makeStructGet(Ctx& ctx, Index pos, bool signed_) { - auto type = typeidx(ctx); - CHECK_ERR(type); - auto field = fieldidx(ctx, *type); - CHECK_ERR(field); - return ctx.makeStructGet(pos, *type, *field, signed_); -} - -template -Result makeStructSet(Ctx& ctx, Index pos) { - auto type = typeidx(ctx); - CHECK_ERR(type); - auto field = fieldidx(ctx, *type); - CHECK_ERR(field); - return ctx.makeStructSet(pos, *type, *field); -} - -template -Result makeArrayNew(Ctx& ctx, Index pos, bool default_) { - auto type = typeidx(ctx); - CHECK_ERR(type); - if (default_) { - return ctx.makeArrayNewDefault(pos, *type); - } - return ctx.makeArrayNew(pos, *type); -} - -template -Result makeArrayNewData(Ctx& ctx, Index pos) { - auto type = typeidx(ctx); - CHECK_ERR(type); - auto data = dataidx(ctx); - CHECK_ERR(data); - return ctx.makeArrayNewData(pos, *type, *data); -} - -template -Result makeArrayNewElem(Ctx& ctx, Index pos) { - auto type = typeidx(ctx); - CHECK_ERR(type); - auto data = dataidx(ctx); - CHECK_ERR(data); - return ctx.makeArrayNewElem(pos, *type, *data); -} - -template -Result makeArrayNewFixed(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeArrayGet(Ctx& ctx, Index pos, bool signed_) { - auto type = typeidx(ctx); - CHECK_ERR(type); - return ctx.makeArrayGet(pos, *type, signed_); -} - -template -Result makeArraySet(Ctx& ctx, Index pos) { - auto type = typeidx(ctx); - CHECK_ERR(type); - return ctx.makeArraySet(pos, *type); -} - -template -Result makeArrayLen(Ctx& ctx, Index pos) { - return ctx.makeArrayLen(pos); -} - -template -Result makeArrayCopy(Ctx& ctx, Index pos) { - auto destType = typeidx(ctx); - CHECK_ERR(destType); - auto srcType = typeidx(ctx); - CHECK_ERR(srcType); - return ctx.makeArrayCopy(pos, *destType, *srcType); -} - -template -Result makeArrayFill(Ctx& ctx, Index pos) { - auto type = typeidx(ctx); - CHECK_ERR(type); - return ctx.makeArrayFill(pos, *type); -} - -template -Result makeArrayInitData(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeArrayInitElem(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeRefAs(Ctx& ctx, Index pos, RefAsOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeStringNew(Ctx& ctx, Index pos, StringNewOp op, bool try_) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringConst(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeStringMeasure(Ctx& ctx, Index pos, StringMeasureOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeStringEncode(Ctx& ctx, Index pos, StringEncodeOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringConcat(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringEq(Ctx& ctx, Index pos, StringEqOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringAs(Ctx& ctx, Index pos, StringAsOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringWTF8Advance(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringWTF16Get(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringIterNext(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeStringIterMove(Ctx& ctx, Index pos, StringIterMoveOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result -makeStringSliceWTF(Ctx& ctx, Index pos, StringSliceWTFOp op) { - return ctx.in.err("unimplemented instruction"); -} - -template -Result makeStringSliceIter(Ctx& ctx, Index pos) { - return ctx.in.err("unimplemented instruction"); -} - -// ======= -// Modules -// ======= - -// typeidx ::= x:u32 => x -// | v:id => x (if types[x] = v) -template MaybeResult maybeTypeidx(Ctx& ctx) { - if (auto x = ctx.in.takeU32()) { - return *x; - } - if (auto id = ctx.in.takeID()) { - // TODO: Fix position to point to start of id, not next element. - auto idx = ctx.getTypeIndex(*id); - CHECK_ERR(idx); - return *idx; - } - return {}; -} - -template Result typeidx(Ctx& ctx) { - if (auto idx = maybeTypeidx(ctx)) { - CHECK_ERR(idx); - return ctx.getHeapTypeFromIdx(*idx); - } - return ctx.in.err("expected type index or identifier"); -} - -// fieldidx_t ::= x:u32 => x -// | v:id => x (if t.fields[x] = v) -template -Result fieldidx(Ctx& ctx, - typename Ctx::HeapTypeT type) { - if (auto x = ctx.in.takeU32()) { - return ctx.getFieldFromIdx(type, *x); - } - if (auto id = ctx.in.takeID()) { - return ctx.getFieldFromName(type, *id); - } - return ctx.in.err("expected field index or identifier"); -} - -// memidx ::= x:u32 => x -// | v:id => x (if memories[x] = v) -template -MaybeResult maybeMemidx(Ctx& ctx) { - if (auto x = ctx.in.takeU32()) { - return ctx.getMemoryFromIdx(*x); - } - if (auto id = ctx.in.takeID()) { - return ctx.getMemoryFromName(*id); - } - return {}; -} - -template Result memidx(Ctx& ctx) { - if (auto idx = maybeMemidx(ctx)) { - CHECK_ERR(idx); - return *idx; - } - return ctx.in.err("expected memory index or identifier"); -} - -// memuse ::= '(' 'memory' x:memidx ')' => x -template -MaybeResult maybeMemuse(Ctx& ctx) { - if (!ctx.in.takeSExprStart("memory"sv)) { - return {}; - } - auto idx = memidx(ctx); - CHECK_ERR(idx); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of memory use"); - } - return *idx; -} - -// globalidx ::= x:u32 => x -// | v:id => x (if globals[x] = v) -template Result globalidx(Ctx& ctx) { - if (auto x = ctx.in.takeU32()) { - return ctx.getGlobalFromIdx(*x); - } - if (auto id = ctx.in.takeID()) { - return ctx.getGlobalFromName(*id); - } - return ctx.in.err("expected global index or identifier"); -} - -// dataidx ::= x:u32 => x -// | v:id => x (if data[x] = v) -template Result dataidx(Ctx& ctx) { - if (auto x = ctx.in.takeU32()) { - return ctx.getDataFromIdx(*x); - } - if (auto id = ctx.in.takeID()) { - return ctx.getDataFromName(*id); - } - return ctx.in.err("expected data index or identifier"); -} - -// localidx ::= x:u32 => x -// | v:id => x (if locals[x] = v) -template Result localidx(Ctx& ctx) { - if (auto x = ctx.in.takeU32()) { - return ctx.getLocalFromIdx(*x); - } - if (auto id = ctx.in.takeID()) { - return ctx.getLocalFromName(*id); - } - return ctx.in.err("expected local index or identifier"); -} - -// typeuse ::= '(' 'type' x:typeidx ')' => x, [] -// (if typedefs[x] = [t1*] -> [t2*] -// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs -// (if typedefs[x] = [t1*] -> [t2*]) -// | ((t1,IDs):param)* (t2:result)* => x, IDs -// (if x is minimum s.t. typedefs[x] = [t1*] -> [t2*]) -template Result typeuse(Ctx& ctx) { - auto pos = ctx.in.getPos(); - std::optional type; - if (ctx.in.takeSExprStart("type"sv)) { - auto x = typeidx(ctx); - CHECK_ERR(x); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of type use"); - } - - type = *x; - } - - auto namedParams = params(ctx); - CHECK_ERR(namedParams); - - auto resultTypes = results(ctx); - CHECK_ERR(resultTypes); - - return ctx.makeTypeUse(pos, type, namedParams.getPtr(), resultTypes.getPtr()); -} - -// ('(' 'import' mod:name nm:name ')')? -MaybeResult inlineImport(ParseInput& in) { - if (!in.takeSExprStart("import"sv)) { - return {}; - } - auto mod = in.takeName(); - if (!mod) { - return in.err("expected import module"); - } - auto nm = in.takeName(); - if (!nm) { - return in.err("expected import name"); - } - if (!in.takeRParen()) { - return in.err("expected end of import"); - } - // TODO: Return Ok when parsing Decls. - return {{*mod, *nm}}; -} - -// ('(' 'export' name ')')* -Result> inlineExports(ParseInput& in) { - std::vector exports; - while (in.takeSExprStart("export"sv)) { - auto name = in.takeName(); - if (!name) { - return in.err("expected export name"); - } - if (!in.takeRParen()) { - return in.err("expected end of import"); - } - exports.push_back(*name); - } - return exports; -} - -// strtype ::= ft:functype => ft -// | st:structtype => st -// | at:arraytype => at -template Result<> strtype(Ctx& ctx) { - if (auto type = functype(ctx)) { - CHECK_ERR(type); - ctx.addFuncType(*type); - return Ok{}; - } - if (auto type = structtype(ctx)) { - CHECK_ERR(type); - ctx.addStructType(*type); - return Ok{}; - } - if (auto type = arraytype(ctx)) { - CHECK_ERR(type); - ctx.addArrayType(*type); - return Ok{}; - } - return ctx.in.err("expected type description"); -} - -// subtype ::= '(' 'type' id? '(' 'sub' typeidx? strtype ')' ')' -// | '(' 'type' id? strtype ')' -template MaybeResult<> subtype(Ctx& ctx) { - auto pos = ctx.in.getPos(); - - if (!ctx.in.takeSExprStart("type"sv)) { - return {}; - } - - Name name; - if (auto id = ctx.in.takeID()) { - name = *id; - } - - if (ctx.in.takeSExprStart("sub"sv)) { - if (ctx.in.takeKeyword("open"sv)) { - ctx.setOpen(); - } - if (auto super = maybeTypeidx(ctx)) { - CHECK_ERR(super); - CHECK_ERR(ctx.addSubtype(*super)); - } - - CHECK_ERR(strtype(ctx)); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of subtype definition"); - } - } else { - CHECK_ERR(strtype(ctx)); - } - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of type definition"); - } - - ctx.finishSubtype(name, pos); - return Ok{}; -} - -// deftype ::= '(' 'rec' subtype* ')' -// | subtype -template MaybeResult<> deftype(Ctx& ctx) { - auto pos = ctx.in.getPos(); - - if (ctx.in.takeSExprStart("rec"sv)) { - size_t startIndex = ctx.getRecGroupStartIndex(); - size_t groupLen = 0; - while (auto type = subtype(ctx)) { - CHECK_ERR(type); - ++groupLen; - } - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected type definition or end of recursion group"); - } - ctx.addRecGroup(startIndex, groupLen); - } else if (auto type = subtype(ctx)) { - CHECK_ERR(type); - } else { - return {}; - } - - ctx.finishDeftype(pos); - return Ok{}; -} - -// local ::= '(' 'local id? t:valtype ')' => [t] -// | '(' 'local t*:valtype* ')' => [t*] -// locals ::= local* -template MaybeResult locals(Ctx& ctx) { - bool hasAny = false; - auto res = ctx.makeLocals(); - while (ctx.in.takeSExprStart("local"sv)) { - hasAny = true; - if (auto id = ctx.in.takeID()) { - // Single named local - auto type = valtype(ctx); - CHECK_ERR(type); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of local"); - } - ctx.appendLocal(res, *id, *type); - } else { - // Repeated unnamed locals - while (!ctx.in.takeRParen()) { - auto type = valtype(ctx); - CHECK_ERR(type); - ctx.appendLocal(res, {}, *type); - } - } - } - if (hasAny) { - return res; - } - return {}; -} - -// func ::= '(' 'func' id? ('(' 'export' name ')')* -// x,I:typeuse t*:vec(local) (in:instr)* ')' -// | '(' 'func' id? ('(' 'export' name ')')* -// '(' 'import' mod:name nm:name ')' typeuse ')' -template MaybeResult<> func(Ctx& ctx) { - auto pos = ctx.in.getPos(); - if (!ctx.in.takeSExprStart("func"sv)) { - return {}; - } - - Name name; - if (auto id = ctx.in.takeID()) { - name = *id; - } - - auto exports = inlineExports(ctx.in); - CHECK_ERR(exports); - - auto import = inlineImport(ctx.in); - CHECK_ERR(import); - - auto type = typeuse(ctx); - CHECK_ERR(type); - - std::optional localVars; - if (!import) { - if (auto l = locals(ctx)) { - CHECK_ERR(l); - localVars = *l; - } - } - - std::optional insts; - if (!import) { - auto i = instrs(ctx); - CHECK_ERR(i); - insts = *i; - } - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of function"); - } - - CHECK_ERR( - ctx.addFunc(name, *exports, import.getPtr(), *type, localVars, insts, pos)); - return Ok{}; -} - -// mem ::= '(' 'memory' id? ('(' 'export' name ')')* -// ('(' 'data' b:datastring ')' | memtype) ')' -// | '(' 'memory' id? ('(' 'export' name ')')* -// '(' 'import' mod:name nm:name ')' memtype ')' -template MaybeResult<> memory(Ctx& ctx) { - auto pos = ctx.in.getPos(); - if (!ctx.in.takeSExprStart("memory"sv)) { - return {}; - } - - Name name; - if (auto id = ctx.in.takeID()) { - name = *id; - } - - auto exports = inlineExports(ctx.in); - CHECK_ERR(exports); - - auto import = inlineImport(ctx.in); - CHECK_ERR(import); - - std::optional mtype; - std::optional data; - if (ctx.in.takeSExprStart("data"sv)) { - if (import) { - return ctx.in.err("imported memories cannot have inline data"); - } - auto datastr = datastring(ctx); - CHECK_ERR(datastr); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of inline data"); - } - mtype = ctx.makeMemType(Type::i32, ctx.getLimitsFromData(*datastr), false); - data = *datastr; - } else { - auto type = memtype(ctx); - CHECK_ERR(type); - mtype = *type; - } - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of memory declaration"); - } - - CHECK_ERR(ctx.addMemory(name, *exports, import.getPtr(), *mtype, pos)); - - if (data) { - CHECK_ERR(ctx.addImplicitData(std::move(*data))); - } - - return Ok{}; -} - -// global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')' -// | '(' 'global' id? ('(' 'export' name ')')* -// '(' 'import' mod:name nm:name ')' gt:globaltype ')' -template MaybeResult<> global(Ctx& ctx) { - auto pos = ctx.in.getPos(); - if (!ctx.in.takeSExprStart("global"sv)) { - return {}; - } - - Name name; - if (auto id = ctx.in.takeID()) { - name = *id; - } - - auto exports = inlineExports(ctx.in); - CHECK_ERR(exports); - - auto import = inlineImport(ctx.in); - CHECK_ERR(import); - - auto type = globaltype(ctx); - CHECK_ERR(type); - - std::optional exp; - if (!import) { - auto e = expr(ctx); - CHECK_ERR(e); - exp = *e; - } - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of global"); - } - - CHECK_ERR(ctx.addGlobal(name, *exports, import.getPtr(), *type, exp, pos)); - return Ok{}; -} - -// datastring ::= (b:string)* => concat(b*) -template Result datastring(Ctx& ctx) { - auto data = ctx.makeDataString(); - while (auto str = ctx.in.takeString()) { - ctx.appendDataString(data, *str); - } - return data; -} - -// data ::= '(' 'data' id? b*:datastring ')' => {init b*, mode passive} -// | '(' 'data' id? x:memuse? ('(' 'offset' e:expr ')' | e:instr) -// b*:datastring ') -// => {init b*, mode active {memory x, offset e}} -template MaybeResult<> data(Ctx& ctx) { - auto pos = ctx.in.getPos(); - if (!ctx.in.takeSExprStart("data"sv)) { - return {}; - } - - Name name; - if (auto id = ctx.in.takeID()) { - name = *id; - } - - auto mem = maybeMemuse(ctx); - CHECK_ERR(mem); - - std::optional offset; - if (ctx.in.takeSExprStart("offset"sv)) { - auto e = expr(ctx); - CHECK_ERR(e); - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of offset expression"); - } - offset = *e; - } else if (ctx.in.takeLParen()) { - auto inst = instr(ctx); - CHECK_ERR(inst); - auto offsetExpr = ctx.instrToExpr(*inst); - CHECK_ERR(offsetExpr); - offset = *offsetExpr; - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of offset instruction"); - } - } - - if (mem && !offset) { - return ctx.in.err("expected offset for active segment"); - } - - auto str = datastring(ctx); - CHECK_ERR(str); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of data segment"); - } - - CHECK_ERR(ctx.addData(name, mem.getPtr(), offset, std::move(*str), pos)); - - return Ok{}; -} - -// modulefield ::= deftype -// | import -// | func -// | table -// | memory -// | global -// | export -// | start -// | elem -// | data -MaybeResult<> modulefield(ParseDeclsCtx& ctx) { - if (auto t = ctx.in.peek(); !t || t->isRParen()) { - return {}; - } - if (auto res = deftype(ctx)) { - CHECK_ERR(res); - return Ok{}; - } - if (auto res = func(ctx)) { - CHECK_ERR(res); - return Ok{}; - } - if (auto res = memory(ctx)) { - CHECK_ERR(res); - return Ok{}; - } - if (auto res = global(ctx)) { - CHECK_ERR(res); - return Ok{}; - } - if (auto res = data(ctx)) { - CHECK_ERR(res); - return Ok{}; - } - return ctx.in.err("unrecognized module field"); -} - -// module ::= '(' 'module' id? (m:modulefield)* ')' -// | (m:modulefield)* eof -Result<> module(ParseDeclsCtx& ctx) { - bool outer = ctx.in.takeSExprStart("module"sv); - - if (outer) { - if (auto id = ctx.in.takeID()) { - ctx.wasm.name = *id; - } - } - - while (auto field = modulefield(ctx)) { - CHECK_ERR(field); - } - - if (outer && !ctx.in.takeRParen()) { - return ctx.in.err("expected end of module"); - } - - return Ok{}; -} - -} // anonymous namespace - -Result<> parseModule(Module& wasm, std::string_view input) { - // Parse module-level declarations. - ParseDeclsCtx decls(input, wasm); - CHECK_ERR(module(decls)); - if (!decls.in.empty()) { - return decls.in.err("Unexpected tokens after module"); - } - - auto typeIndices = createIndexMap(decls.in, decls.subtypeDefs); - CHECK_ERR(typeIndices); - - // Parse type definitions. - std::vector types; - { - TypeBuilder builder(decls.subtypeDefs.size()); - ParseTypeDefsCtx ctx(input, builder, *typeIndices); - for (auto& typeDef : decls.typeDefs) { - WithPosition with(ctx, typeDef.pos); - CHECK_ERR(deftype(ctx)); - } - auto built = builder.build(); - if (auto* err = built.getError()) { - std::stringstream msg; - msg << "invalid type: " << err->reason; - return ctx.in.err(decls.typeDefs[err->index].pos, msg.str()); - } - types = *built; - // Record type names on the module. - for (size_t i = 0; i < types.size(); ++i) { - auto& names = ctx.names[i]; - if (names.name.is() || names.fieldNames.size()) { - wasm.typeNames.insert({types[i], names}); - } - } - } - - // Parse implicit type definitions and map typeuses without explicit types to - // the correct types. - std::unordered_map implicitTypes; - { - ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, *typeIndices); - for (Index pos : decls.implicitTypeDefs) { - WithPosition with(ctx, pos); - CHECK_ERR(typeuse(ctx)); - } - } - - { - // Parse module-level types. - ParseModuleTypesCtx ctx(input, wasm, types, implicitTypes, *typeIndices); - CHECK_ERR(parseDefs(ctx, decls.funcDefs, func)); - CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory)); - CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); - // TODO: Parse types of other module elements. - } - { - // Parse definitions. - // TODO: Parallelize this. - ParseDefsCtx ctx(input, wasm, types, implicitTypes, *typeIndices); - CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); - CHECK_ERR(parseDefs(ctx, decls.dataDefs, data)); - - for (Index i = 0; i < decls.funcDefs.size(); ++i) { - ctx.index = i; - ctx.setFunction(wasm.functions[i].get()); - CHECK_ERR(ctx.irBuilder.makeBlock(Name{}, ctx.func->getResults())); - WithPosition with(ctx, decls.funcDefs[i].pos); - auto parsed = func(ctx); - CHECK_ERR(parsed); - assert(parsed); - } - } - - return Ok{}; -} - -} // namespace wasm::WATParser -- cgit v1.2.3