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/parser/contexts.h | 1275 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1275 insertions(+) create mode 100644 src/parser/contexts.h (limited to 'src/parser/contexts.h') diff --git a/src/parser/contexts.h b/src/parser/contexts.h new file mode 100644 index 000000000..210945e8d --- /dev/null +++ b/src/parser/contexts.h @@ -0,0 +1,1275 @@ +/* + * Copyright 2023 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. + */ + +#ifndef parser_context_h +#define parser_context_h + +#include "common.h" +#include "input.h" +#include "ir/names.h" +#include "support/name.h" +#include "support/result.h" +#include "wasm-builder.h" +#include "wasm-ir-builder.h" +#include "wasm.h" + +namespace wasm::WATParser { + +using IndexMap = std::unordered_map; + +inline 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; +} + +struct Limits { + uint64_t initial; + uint64_t max; +}; + +struct MemType { + Type type; + Limits limits; + bool shared; +}; + +struct Memarg { + uint64_t offset; + uint32_t align; +}; + +// 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 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); + Result<> addFunc(Name name, + const std::vector& exports, + ImportNames* import, + TypeUseT type, + std::optional, + std::optional, + Index pos); + + Result + addMemoryDecl(Index pos, Name name, ImportNames* importNames, MemType type); + + Result<> addMemory(Name name, + const std::vector& exports, + ImportNames* import, + MemType type, + Index pos); + + Result<> addImplicitData(DataStringT&& data); + + Result addGlobalDecl(Index pos, Name name, ImportNames* importNames); + + Result<> addGlobal(Name name, + const std::vector& exports, + ImportNames* import, + GlobalTypeT, + std::optional, + Index pos); + + Result<> addData(Name name, + MemoryIdxT*, + std::optional, + std::vector&& data, + Index pos); +}; + +// 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); + Result<> addFunc(Name, + const std::vector&, + ImportNames*, + TypeUseT, + std::optional, + std::optional, + Index pos); + + Result<> addGlobal(Name, + const std::vector&, + ImportNames*, + GlobalTypeT, + std::optional exp, + Index); + Result<> + addData(Name, Name* mem, std::optional offset, DataStringT, Index pos); + 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)); + } +}; + +} // namespace wasm::WATParser + +#endif // parser_context_h -- cgit v1.2.3