| /* |
| * 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 "ir/names.h" |
| #include "lexer.h" |
| #include "support/name.h" |
| #include "support/result.h" |
| #include "support/string.h" |
| #include "wasm-builder.h" |
| #include "wasm-ir-builder.h" |
| #include "wasm.h" |
| |
| namespace wasm::WATParser { |
| |
| using IndexMap = std::unordered_map<Name, Index>; |
| |
| inline std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named) { |
| std::vector<Type> types; |
| types.reserve(named.size()); |
| for (auto& t : named) { |
| types.push_back(t.type); |
| } |
| return types; |
| } |
| |
| struct Limits { |
| uint64_t initial; |
| std::optional<uint64_t> max; |
| }; |
| |
| struct MemType { |
| Type addressType; |
| Limits limits; |
| bool shared; |
| }; |
| |
| struct Memarg { |
| uint64_t offset; |
| uint32_t align; |
| }; |
| |
| struct TableType { |
| Type addressType; |
| Limits limits; |
| }; |
| |
| // 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; |
| std::vector<Annotation> annotations; |
| }; |
| |
| 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<Name> names; |
| }; |
| |
| struct NullTypeParserCtx { |
| using IndexT = Ok; |
| using HeapTypeT = Ok; |
| using TupleElemListT = Ok; |
| using TypeT = Ok; |
| using ParamsT = Ok; |
| using ResultsT = size_t; |
| using BlockTypeT = Ok; |
| using SignatureT = Ok; |
| using ContinuationT = 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 ElemListT = Ok; |
| using DataStringT = Ok; |
| |
| HeapTypeT makeFuncType(Shareability) { return Ok{}; } |
| HeapTypeT makeAnyType(Shareability) { return Ok{}; } |
| HeapTypeT makeExternType(Shareability) { return Ok{}; } |
| HeapTypeT makeEqType(Shareability) { return Ok{}; } |
| HeapTypeT makeI31Type(Shareability) { return Ok{}; } |
| HeapTypeT makeStructType(Shareability) { return Ok{}; } |
| HeapTypeT makeArrayType(Shareability) { return Ok{}; } |
| HeapTypeT makeExnType(Shareability) { return Ok{}; } |
| HeapTypeT makeStringType(Shareability) { return Ok{}; } |
| HeapTypeT makeContType(Shareability) { return Ok{}; } |
| HeapTypeT makeNoneType(Shareability) { return Ok{}; } |
| HeapTypeT makeNoextType(Shareability) { return Ok{}; } |
| HeapTypeT makeNofuncType(Shareability) { return Ok{}; } |
| HeapTypeT makeNoexnType(Shareability) { return Ok{}; } |
| HeapTypeT makeNocontType(Shareability) { 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{}; } |
| |
| TupleElemListT makeTupleElemList() { return Ok{}; } |
| void appendTupleElem(TupleElemListT&, TypeT) {} |
| TypeT makeTupleType(TupleElemListT) { 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{}; } |
| ContinuationT makeContType(HeapTypeT) { 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<ArrayT> makeArray(FieldsT&) { return Ok{}; } |
| |
| GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } |
| |
| LocalsT makeLocals() { return Ok{}; } |
| void appendLocal(LocalsT&, Name, TypeT) {} |
| |
| Result<Index> getTypeIndex(Name) { return 1; } |
| Result<HeapTypeT> 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{}; } |
| |
| bool skipFunctionBody() { return false; } |
| }; |
| |
| template<typename Ctx> struct TypeParserCtx { |
| using IndexT = Index; |
| using HeapTypeT = HeapType; |
| using TypeT = Type; |
| using ParamsT = std::vector<NameType>; |
| using ResultsT = std::vector<Type>; |
| using BlockTypeT = HeapType; |
| using SignatureT = Signature; |
| using ContinuationT = Continuation; |
| using StorageT = Field; |
| using FieldT = Field; |
| using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>; |
| using StructT = std::pair<std::vector<Name>, Struct>; |
| using ArrayT = Array; |
| using LimitsT = Ok; |
| using MemTypeT = Ok; |
| using LocalsT = std::vector<NameType>; |
| using DataStringT = Ok; |
| |
| // Map heap type names to their indices. |
| const IndexMap& typeIndices; |
| |
| TypeParserCtx(const IndexMap& typeIndices) : typeIndices(typeIndices) {} |
| |
| Ctx& self() { return *static_cast<Ctx*>(this); } |
| |
| HeapTypeT makeFuncType(Shareability share) { |
| return HeapTypes::func.getBasic(share); |
| } |
| HeapTypeT makeAnyType(Shareability share) { |
| return HeapTypes::any.getBasic(share); |
| } |
| HeapTypeT makeExternType(Shareability share) { |
| return HeapTypes::ext.getBasic(share); |
| } |
| HeapTypeT makeEqType(Shareability share) { |
| return HeapTypes::eq.getBasic(share); |
| } |
| HeapTypeT makeI31Type(Shareability share) { |
| return HeapTypes::i31.getBasic(share); |
| } |
| HeapTypeT makeStructType(Shareability share) { |
| return HeapTypes::struct_.getBasic(share); |
| } |
| HeapTypeT makeArrayType(Shareability share) { |
| return HeapTypes::array.getBasic(share); |
| } |
| HeapTypeT makeExnType(Shareability share) { |
| return HeapTypes::exn.getBasic(share); |
| } |
| HeapTypeT makeStringType(Shareability share) { |
| return HeapTypes::string.getBasic(share); |
| } |
| HeapTypeT makeContType(Shareability share) { |
| return HeapTypes::cont.getBasic(share); |
| } |
| HeapTypeT makeNoneType(Shareability share) { |
| return HeapTypes::none.getBasic(share); |
| } |
| HeapTypeT makeNoextType(Shareability share) { |
| return HeapTypes::noext.getBasic(share); |
| } |
| HeapTypeT makeNofuncType(Shareability share) { |
| return HeapTypes::nofunc.getBasic(share); |
| } |
| HeapTypeT makeNoexnType(Shareability share) { |
| return HeapTypes::noexn.getBasic(share); |
| } |
| HeapTypeT makeNocontType(Shareability share) { |
| return HeapTypes::nocont.getBasic(share); |
| } |
| |
| 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); |
| } |
| |
| std::vector<Type> makeTupleElemList() { return {}; } |
| void appendTupleElem(std::vector<Type>& elems, Type elem) { |
| elems.push_back(elem); |
| } |
| Result<TypeT> makeTupleType(const std::vector<Type>& 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<Type> empty; |
| const auto& paramTypes = params ? getUnnamedTypes(*params) : empty; |
| const auto& resultTypes = results ? *results : empty; |
| return Signature(self().makeTupleType(paramTypes), |
| self().makeTupleType(resultTypes)); |
| } |
| |
| ContinuationT makeContType(HeapTypeT ft) { return Continuation(ft); } |
| |
| 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<ArrayT> 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<Index> 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) {} |
| |
| Result<LimitsT> makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; } |
| LimitsT getLimitsFromData(DataStringT) { return Ok{}; } |
| |
| MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } |
| |
| HeapType getBlockTypeFromResult(const std::vector<Type> results) { |
| assert(results.size() == 1); |
| return HeapType(Signature(Type::none, results[0])); |
| } |
| |
| bool skipFunctionBody() { return false; } |
| }; |
| |
| struct NullInstrParserCtx { |
| using ExprT = Ok; |
| using CatchT = Ok; |
| using CatchListT = Ok; |
| using TagLabelListT = Ok; |
| |
| using FieldIdxT = Ok; |
| using FuncIdxT = Ok; |
| using LocalIdxT = Ok; |
| using TableIdxT = Ok; |
| using MemoryIdxT = Ok; |
| using GlobalIdxT = Ok; |
| using ElemIdxT = Ok; |
| using DataIdxT = Ok; |
| using LabelIdxT = Ok; |
| using TagIdxT = Ok; |
| |
| using MemargT = Ok; |
| |
| Result<> makeExpr() { return Ok{}; } |
| |
| template<typename HeapTypeT> FieldIdxT getFieldFromIdx(HeapTypeT, uint32_t) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> FieldIdxT getFieldFromName(HeapTypeT, Name) { |
| return Ok{}; |
| } |
| FuncIdxT getFuncFromIdx(uint32_t) { return Ok{}; } |
| FuncIdxT getFuncFromName(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{}; } |
| TableIdxT getTableFromIdx(uint32_t) { return Ok{}; } |
| TableIdxT getTableFromName(Name) { return Ok{}; } |
| MemoryIdxT getMemoryFromIdx(uint32_t) { return Ok{}; } |
| MemoryIdxT getMemoryFromName(Name) { return Ok{}; } |
| ElemIdxT getElemFromIdx(uint32_t) { return Ok{}; } |
| ElemIdxT getElemFromName(Name) { return Ok{}; } |
| DataIdxT getDataFromIdx(uint32_t) { return Ok{}; } |
| DataIdxT getDataFromName(Name) { return Ok{}; } |
| LabelIdxT getLabelFromIdx(uint32_t, bool) { return Ok{}; } |
| LabelIdxT getLabelFromName(Name, bool) { return Ok{}; } |
| TagIdxT getTagFromIdx(uint32_t) { return Ok{}; } |
| TagIdxT getTagFromName(Name) { return Ok{}; } |
| |
| MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; } |
| |
| template<typename BlockTypeT> |
| Result<> makeBlock(Index, |
| const std::vector<Annotation>&, |
| std::optional<Name>, |
| BlockTypeT) { |
| return Ok{}; |
| } |
| template<typename BlockTypeT> |
| Result<> makeIf(Index, |
| const std::vector<Annotation>&, |
| std::optional<Name>, |
| BlockTypeT) { |
| return Ok{}; |
| } |
| Result<> visitElse() { return Ok{}; } |
| template<typename BlockTypeT> |
| Result<> makeLoop(Index, |
| const std::vector<Annotation>&, |
| std::optional<Name>, |
| BlockTypeT) { |
| return Ok{}; |
| } |
| template<typename BlockTypeT> |
| Result<> makeTry(Index, |
| const std::vector<Annotation>&, |
| std::optional<Name>, |
| BlockTypeT) { |
| return Ok{}; |
| } |
| Result<> visitCatch(Index, TagIdxT) { return Ok{}; } |
| Result<> visitCatchAll(Index) { return Ok{}; } |
| Result<> visitDelegate(Index, LabelIdxT) { return Ok{}; } |
| Result<> visitEnd() { return Ok{}; } |
| |
| CatchListT makeCatchList() { return Ok{}; } |
| void appendCatch(CatchListT&, CatchT) {} |
| CatchT makeCatch(TagIdxT, LabelIdxT) { return Ok{}; } |
| CatchT makeCatchRef(TagIdxT, LabelIdxT) { return Ok{}; } |
| CatchT makeCatchAll(LabelIdxT) { return Ok{}; } |
| CatchT makeCatchAllRef(LabelIdxT) { return Ok{}; } |
| template<typename BlockTypeT> |
| Result<> makeTryTable(Index, |
| const std::vector<Annotation>&, |
| std::optional<Name>, |
| BlockTypeT, |
| CatchListT) { |
| return Ok{}; |
| } |
| |
| TagLabelListT makeTagLabelList() { return Ok{}; } |
| void appendTagLabel(TagLabelListT&, TagIdxT, LabelIdxT) {} |
| |
| void setSrcLoc(const std::vector<Annotation>&) {} |
| |
| Result<> makeUnreachable(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeNop(Index, const std::vector<Annotation>&) { return Ok{}; } |
| Result<> makeBinary(Index, const std::vector<Annotation>&, BinaryOp) { |
| return Ok{}; |
| } |
| Result<> makeUnary(Index, const std::vector<Annotation>&, UnaryOp) { |
| return Ok{}; |
| } |
| template<typename ResultsT> |
| Result<> makeSelect(Index, const std::vector<Annotation>&, ResultsT*) { |
| return Ok{}; |
| } |
| Result<> makeDrop(Index, const std::vector<Annotation>&) { return Ok{}; } |
| Result<> makeMemorySize(Index, const std::vector<Annotation>&, MemoryIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeMemoryGrow(Index, const std::vector<Annotation>&, MemoryIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeLocalGet(Index, const std::vector<Annotation>&, LocalIdxT) { |
| return Ok{}; |
| } |
| Result<> makeLocalTee(Index, const std::vector<Annotation>&, LocalIdxT) { |
| return Ok{}; |
| } |
| Result<> makeLocalSet(Index, const std::vector<Annotation>&, LocalIdxT) { |
| return Ok{}; |
| } |
| Result<> makeGlobalGet(Index, const std::vector<Annotation>&, GlobalIdxT) { |
| return Ok{}; |
| } |
| Result<> makeGlobalSet(Index, const std::vector<Annotation>&, GlobalIdxT) { |
| return Ok{}; |
| } |
| |
| Result<> makeI32Const(Index, const std::vector<Annotation>&, uint32_t) { |
| return Ok{}; |
| } |
| Result<> makeI64Const(Index, const std::vector<Annotation>&, uint64_t) { |
| return Ok{}; |
| } |
| Result<> makeF32Const(Index, const std::vector<Annotation>&, float) { |
| return Ok{}; |
| } |
| Result<> makeF64Const(Index, const std::vector<Annotation>&, double) { |
| return Ok{}; |
| } |
| Result<> makeI8x16Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<uint8_t, 16>&) { |
| return Ok{}; |
| } |
| Result<> makeI16x8Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<uint16_t, 8>&) { |
| return Ok{}; |
| } |
| Result<> makeI32x4Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<uint32_t, 4>&) { |
| return Ok{}; |
| } |
| Result<> makeI64x2Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<uint64_t, 2>&) { |
| return Ok{}; |
| } |
| Result<> makeF32x4Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<float, 4>&) { |
| return Ok{}; |
| } |
| Result<> makeF64x2Const(Index, |
| const std::vector<Annotation>&, |
| const std::array<double, 2>&) { |
| return Ok{}; |
| } |
| Result<> makeLoad(Index, |
| const std::vector<Annotation>&, |
| Type, |
| bool, |
| int, |
| bool, |
| MemoryIdxT*, |
| MemargT) { |
| return Ok{}; |
| } |
| Result<> makeStore(Index, |
| const std::vector<Annotation>&, |
| Type, |
| int, |
| bool, |
| MemoryIdxT*, |
| MemargT) { |
| return Ok{}; |
| } |
| Result<> makeAtomicRMW(Index, |
| const std::vector<Annotation>&, |
| AtomicRMWOp, |
| Type, |
| int, |
| MemoryIdxT*, |
| MemargT) { |
| return Ok{}; |
| } |
| Result<> makeAtomicCmpxchg( |
| Index, const std::vector<Annotation>&, Type, int, MemoryIdxT*, MemargT) { |
| return Ok{}; |
| } |
| Result<> makeAtomicWait( |
| Index, const std::vector<Annotation>&, Type, MemoryIdxT*, MemargT) { |
| return Ok{}; |
| } |
| Result<> makeAtomicNotify(Index, |
| const std::vector<Annotation>&, |
| MemoryIdxT*, |
| MemargT) { |
| return Ok{}; |
| } |
| Result<> makeAtomicFence(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeSIMDExtract(Index, |
| const std::vector<Annotation>&, |
| SIMDExtractOp, |
| uint8_t) { |
| return Ok{}; |
| } |
| Result<> makeSIMDReplace(Index, |
| const std::vector<Annotation>&, |
| SIMDReplaceOp, |
| uint8_t) { |
| return Ok{}; |
| } |
| Result<> makeSIMDShuffle(Index, |
| const std::vector<Annotation>&, |
| const std::array<uint8_t, 16>&) { |
| return Ok{}; |
| } |
| Result<> |
| makeSIMDTernary(Index, const std::vector<Annotation>&, SIMDTernaryOp) { |
| return Ok{}; |
| } |
| Result<> makeSIMDShift(Index, const std::vector<Annotation>&, SIMDShiftOp) { |
| return Ok{}; |
| } |
| Result<> makeSIMDLoad( |
| Index, const std::vector<Annotation>&, SIMDLoadOp, MemoryIdxT*, MemargT) { |
| return Ok{}; |
| } |
| Result<> makeSIMDLoadStoreLane(Index, |
| const std::vector<Annotation>&, |
| SIMDLoadStoreLaneOp, |
| MemoryIdxT*, |
| MemargT, |
| uint8_t) { |
| return Ok{}; |
| } |
| Result<> |
| makeMemoryInit(Index, const std::vector<Annotation>&, MemoryIdxT*, DataIdxT) { |
| return Ok{}; |
| } |
| Result<> makeDataDrop(Index, const std::vector<Annotation>&, DataIdxT) { |
| return Ok{}; |
| } |
| |
| Result<> makeMemoryCopy(Index, |
| const std::vector<Annotation>&, |
| MemoryIdxT*, |
| MemoryIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeMemoryFill(Index, const std::vector<Annotation>&, MemoryIdxT*) { |
| return Ok{}; |
| } |
| template<typename TypeT> |
| Result<> makePop(Index, const std::vector<Annotation>&, TypeT) { |
| return Ok{}; |
| } |
| Result<> makeCall(Index, const std::vector<Annotation>&, FuncIdxT, bool) { |
| return Ok{}; |
| } |
| template<typename TypeUseT> |
| Result<> makeCallIndirect( |
| Index, const std::vector<Annotation>&, TableIdxT*, TypeUseT, bool) { |
| return Ok{}; |
| } |
| Result<> makeBreak(Index, const std::vector<Annotation>&, LabelIdxT, bool) { |
| return Ok{}; |
| } |
| Result<> makeSwitch(Index, |
| const std::vector<Annotation>&, |
| const std::vector<LabelIdxT>&, |
| LabelIdxT) { |
| return Ok{}; |
| } |
| Result<> makeReturn(Index, const std::vector<Annotation>&) { return Ok{}; } |
| template<typename HeapTypeT> |
| Result<> makeRefNull(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| Result<> makeRefIsNull(Index, const std::vector<Annotation>&) { return Ok{}; } |
| Result<> makeRefFunc(Index, const std::vector<Annotation>&, FuncIdxT) { |
| return Ok{}; |
| } |
| Result<> makeRefEq(Index, const std::vector<Annotation>&) { return Ok{}; } |
| Result<> makeTableGet(Index, const std::vector<Annotation>&, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeTableSet(Index, const std::vector<Annotation>&, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeTableSize(Index, const std::vector<Annotation>&, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeTableGrow(Index, const std::vector<Annotation>&, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> makeTableFill(Index, const std::vector<Annotation>&, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> |
| makeTableCopy(Index, const std::vector<Annotation>&, TableIdxT*, TableIdxT*) { |
| return Ok{}; |
| } |
| Result<> |
| makeTableInit(Index, const std::vector<Annotation>&, TableIdxT*, ElemIdxT) { |
| return Ok{}; |
| } |
| Result<> makeThrow(Index, const std::vector<Annotation>&, TagIdxT) { |
| return Ok{}; |
| } |
| Result<> makeRethrow(Index, const std::vector<Annotation>&, LabelIdxT) { |
| return Ok{}; |
| } |
| Result<> makeThrowRef(Index, const std::vector<Annotation>&) { return Ok{}; } |
| Result<> makeTupleMake(Index, const std::vector<Annotation>&, uint32_t) { |
| return Ok{}; |
| } |
| Result<> |
| makeTupleExtract(Index, const std::vector<Annotation>&, uint32_t, uint32_t) { |
| return Ok{}; |
| } |
| Result<> makeTupleDrop(Index, const std::vector<Annotation>&, uint32_t) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeCallRef(Index, const std::vector<Annotation>&, HeapTypeT, bool) { |
| return Ok{}; |
| } |
| Result<> |
| makeRefI31(Index, const std::vector<Annotation>&, Shareability share) { |
| return Ok{}; |
| } |
| Result<> makeI31Get(Index, const std::vector<Annotation>&, bool) { |
| return Ok{}; |
| } |
| template<typename TypeT> |
| Result<> makeRefTest(Index, const std::vector<Annotation>&, TypeT) { |
| return Ok{}; |
| } |
| template<typename TypeT> |
| Result<> makeRefCast(Index, const std::vector<Annotation>&, TypeT) { |
| return Ok{}; |
| } |
| |
| Result<> makeBrOn(Index, const std::vector<Annotation>&, LabelIdxT, BrOnOp) { |
| return Ok{}; |
| } |
| |
| template<typename TypeT> |
| Result<> makeBrOn( |
| Index, const std::vector<Annotation>&, LabelIdxT, BrOnOp, TypeT, TypeT) { |
| return Ok{}; |
| } |
| |
| template<typename HeapTypeT> |
| Result<> makeStructNew(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeStructNewDefault(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeStructGet( |
| Index, const std::vector<Annotation>&, HeapTypeT, FieldIdxT, bool) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeStructSet(Index, const std::vector<Annotation>&, HeapTypeT, FieldIdxT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArrayNew(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeArrayNewDefault(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeArrayNewData(Index, const std::vector<Annotation>&, HeapTypeT, DataIdxT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeArrayNewElem(Index, const std::vector<Annotation>&, HeapTypeT, ElemIdxT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArrayNewFixed(Index, |
| const std::vector<Annotation>&, |
| HeapTypeT, |
| uint32_t) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeArrayGet(Index, const std::vector<Annotation>&, HeapTypeT, bool) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArraySet(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| Result<> makeArrayLen(Index, const std::vector<Annotation>&) { return Ok{}; } |
| template<typename HeapTypeT> |
| Result<> |
| makeArrayCopy(Index, const std::vector<Annotation>&, HeapTypeT, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArrayFill(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArrayInitData(Index, |
| const std::vector<Annotation>&, |
| HeapTypeT, |
| DataIdxT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeArrayInitElem(Index, |
| const std::vector<Annotation>&, |
| HeapTypeT, |
| ElemIdxT) { |
| return Ok{}; |
| } |
| Result<> makeRefAs(Index, const std::vector<Annotation>&, RefAsOp) { |
| return Ok{}; |
| } |
| Result<> makeStringNew(Index, const std::vector<Annotation>&, StringNewOp) { |
| return Ok{}; |
| } |
| Result<> |
| makeStringConst(Index, const std::vector<Annotation>&, std::string_view) { |
| return Ok{}; |
| } |
| Result<> |
| makeStringMeasure(Index, const std::vector<Annotation>&, StringMeasureOp) { |
| return Ok{}; |
| } |
| Result<> |
| makeStringEncode(Index, const std::vector<Annotation>&, StringEncodeOp) { |
| return Ok{}; |
| } |
| Result<> makeStringConcat(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeStringEq(Index, const std::vector<Annotation>&, StringEqOp) { |
| return Ok{}; |
| } |
| Result<> makeStringWTF8Advance(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeStringWTF16Get(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeStringIterNext(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| Result<> makeStringSliceWTF(Index, const std::vector<Annotation>&) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> |
| makeContBind(Index, const std::vector<Annotation>&, HeapTypeT, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeContNew(Index, const std::vector<Annotation>&, HeapTypeT) { |
| return Ok{}; |
| } |
| template<typename HeapTypeT> |
| Result<> makeResume(Index, |
| const std::vector<Annotation>&, |
| HeapTypeT, |
| const TagLabelListT&) { |
| return Ok{}; |
| } |
| Result<> makeSuspend(Index, const std::vector<Annotation>&, TagIdxT) { |
| return Ok{}; |
| } |
| }; |
| |
| struct NullCtx : NullTypeParserCtx, NullInstrParserCtx { |
| Lexer in; |
| NullCtx(const Lexer& in) : in(in) {} |
| Result<> makeTypeUse(Index, std::optional<HeapTypeT>, ParamsT*, ResultsT*) { |
| return Ok{}; |
| } |
| }; |
| |
| // Phase 1: Parse definition spans for top-level module elements and determine |
| // their indices and names. |
| struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { |
| using ExprT = Ok; |
| using LimitsT = Limits; |
| using ElemListT = Index; |
| using DataStringT = std::vector<char>; |
| using TableTypeT = TableType; |
| using MemTypeT = MemType; |
| |
| Lexer 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<DefPos> recTypeDefs; |
| std::vector<DefPos> typeDefs; |
| std::vector<DefPos> funcDefs; |
| std::vector<DefPos> tableDefs; |
| std::vector<DefPos> memoryDefs; |
| std::vector<DefPos> globalDefs; |
| std::vector<DefPos> startDefs; |
| std::vector<DefPos> elemDefs; |
| std::vector<DefPos> dataDefs; |
| std::vector<DefPos> tagDefs; |
| |
| // Positions of export definitions. |
| std::vector<Index> exportDefs; |
| |
| // Positions of typeuses that might implicitly define new types. |
| std::vector<Index> implicitTypeDefs; |
| |
| // Map table indices to the indices of their implicit, in-line element |
| // segments. We need these to find associated segments in later parsing phases |
| // where we can parse their types and instructions. |
| std::unordered_map<Index, Index> implicitElemIndices; |
| |
| // Counters used for generating names for module elements. |
| int funcCounter = 0; |
| int tableCounter = 0; |
| int memoryCounter = 0; |
| int globalCounter = 0; |
| int elemCounter = 0; |
| int dataCounter = 0; |
| int tagCounter = 0; |
| |
| // Used to verify that all imports come before all non-imports. |
| bool hasNonImport = false; |
| |
| Result<> checkImport(Index pos, ImportNames* import) { |
| if (import) { |
| if (hasNonImport) { |
| return in.err(pos, "import after non-import"); |
| } |
| } else { |
| hasNonImport = true; |
| } |
| return Ok{}; |
| } |
| |
| ParseDeclsCtx(Lexer& in, Module& wasm) : in(in), wasm(wasm) {} |
| |
| void addFuncType(SignatureT) {} |
| void addContType(ContinuationT) {} |
| void addStructType(StructT) {} |
| void addArrayType(ArrayT) {} |
| void setOpen() {} |
| void setShared() {} |
| Result<> addSubtype(HeapTypeT) { return Ok{}; } |
| void finishTypeDef(Name name, Index pos) { |
| // TODO: type annotations |
| typeDefs.push_back({name, pos, Index(typeDefs.size()), {}}); |
| } |
| size_t getRecGroupStartIndex() { return 0; } |
| void addRecGroup(Index, size_t) {} |
| void finishRectype(Index pos) { |
| // TODO: type annotations |
| recTypeDefs.push_back({{}, pos, Index(recTypeDefs.size()), {}}); |
| } |
| |
| Limits makeLimits(uint64_t n, std::optional<uint64_t> m) { |
| return Limits{n, m}; |
| } |
| |
| Index makeElemList(TypeT) { return 0; } |
| Index makeFuncElemList() { return 0; } |
| void appendElem(Index& elems, ExprT) { ++elems; } |
| void appendFuncElem(Index& elems, FuncIdxT) { ++elems; } |
| |
| Limits getLimitsFromElems(Index elems) { return {elems, elems}; } |
| |
| TableType makeTableType(Type addressType, Limits limits, TypeT) { |
| return {addressType, limits}; |
| } |
| |
| std::vector<char> makeDataString() { return {}; } |
| void appendDataString(std::vector<char>& data, std::string_view str) { |
| data.insert(data.end(), str.begin(), str.end()); |
| } |
| |
| Limits getLimitsFromData(const std::vector<char>& data) { |
| uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize; |
| return {size, size}; |
| } |
| |
| MemType makeMemType(Type addressType, Limits limits, bool shared) { |
| return {addressType, limits, shared}; |
| } |
| |
| Result<TypeUseT> |
| makeTypeUse(Index pos, std::optional<HeapTypeT> type, ParamsT*, ResultsT*) { |
| if (!type) { |
| implicitTypeDefs.push_back(pos); |
| } |
| return Ok{}; |
| } |
| |
| Result<Function*> addFuncDecl(Index pos, Name name, ImportNames* importNames); |
| Result<> addFunc(Name name, |
| const std::vector<Name>& exports, |
| ImportNames* import, |
| TypeUseT type, |
| std::optional<LocalsT>, |
| std::vector<Annotation>&&, |
| Index pos); |
| |
| Result<Table*> addTableDecl(Index pos, |
| Name name, |
| ImportNames* importNames, |
| TableType limits); |
| Result<> |
| addTable(Name, const std::vector<Name>&, ImportNames*, TableType, Index); |
| |
| // TODO: Record index of implicit elem for use when parsing types and instrs. |
| Result<> addImplicitElems(TypeT, ElemListT&& elems); |
| |
| Result<Memory*> |
| addMemoryDecl(Index pos, Name name, ImportNames* importNames, MemType type); |
| |
| Result<> addMemory(Name name, |
| const std::vector<Name>& exports, |
| ImportNames* import, |
| MemType type, |
| Index pos); |
| |
| Result<> addImplicitData(DataStringT&& data); |
| |
| Result<Global*> addGlobalDecl(Index pos, Name name, ImportNames* importNames); |
| |
| Result<> addGlobal(Name name, |
| const std::vector<Name>& exports, |
| ImportNames* import, |
| GlobalTypeT, |
| std::optional<ExprT>, |
| Index pos); |
| |
| Result<> addStart(FuncIdxT, Index pos) { |
| if (!startDefs.empty()) { |
| return Err{"unexpected extra 'start' function"}; |
| } |
| // TODO: start function annotations. |
| startDefs.push_back({{}, pos, 0, {}}); |
| return Ok{}; |
| } |
| |
| Result<> addElem(Name, TableIdxT*, std::optional<ExprT>, ElemListT&&, Index); |
| |
| Result<> addDeclareElem(Name, ElemListT&&, Index) { return Ok{}; } |
| |
| Result<> addData(Name name, |
| MemoryIdxT*, |
| std::optional<ExprT>, |
| std::vector<char>&& data, |
| Index pos); |
| |
| Result<Tag*> addTagDecl(Index pos, Name name, ImportNames* importNames); |
| |
| Result<> addTag(Name name, |
| const std::vector<Name>& exports, |
| ImportNames* import, |
| TypeUseT type, |
| Index pos); |
| |
| Result<> addExport(Index pos, Ok, Name, ExternalKind) { |
| exportDefs.push_back(pos); |
| return Ok{}; |
| } |
| }; |
| |
| // Phase 2: Parse type definitions into a TypeBuilder. |
| struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> { |
| Lexer 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<TypeNames> names; |
| |
| // The index of the subtype definition we are parsing. |
| Index index = 0; |
| |
| ParseTypeDefsCtx(Lexer& in, TypeBuilder& builder, const IndexMap& typeIndices) |
| : TypeParserCtx<ParseTypeDefsCtx>(typeIndices), in(in), builder(builder), |
| names(builder.size()) {} |
| |
| TypeT makeRefType(HeapTypeT ht, Nullability nullability) { |
| return builder.getTempRefType(ht, nullability); |
| } |
| |
| TypeT makeTupleType(const std::vector<Type> types) { |
| return builder.getTempTupleType(types); |
| } |
| |
| Result<HeapTypeT> 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 addContType(ContinuationT& 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(); } |
| |
| void setShared() { builder[index].setShared(); } |
| |
| Result<> addSubtype(HeapTypeT super) { |
| builder[index].subTypeOf(super); |
| return Ok{}; |
| } |
| |
| void finishTypeDef(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 finishRectype(Index) {} |
| }; |
| |
| // Phase 3: Parse type uses to find implicitly defined types. |
| struct ParseImplicitTypeDefsCtx : TypeParserCtx<ParseImplicitTypeDefsCtx> { |
| using TypeUseT = Ok; |
| |
| Lexer in; |
| |
| // Types parsed so far. |
| std::vector<HeapType>& types; |
| |
| // Map typeuse positions without an explicit type to the correct type. |
| std::unordered_map<Index, HeapType>& implicitTypes; |
| |
| // Map signatures to the first defined heap type they match. |
| std::unordered_map<Signature, HeapType> sigTypes; |
| |
| ParseImplicitTypeDefsCtx(Lexer& in, |
| std::vector<HeapType>& types, |
| std::unordered_map<Index, HeapType>& implicitTypes, |
| const IndexMap& typeIndices) |
| : TypeParserCtx<ParseImplicitTypeDefsCtx>(typeIndices), in(in), |
| types(types), implicitTypes(implicitTypes) { |
| for (auto type : types) { |
| if (type.isSignature() && type.getRecGroup().size() == 1 && |
| !type.getDeclaredSuperType() && !type.isOpen() && !type.isShared()) { |
| sigTypes.insert({type.getSignature(), type}); |
| } |
| } |
| } |
| |
| Result<HeapTypeT> getHeapTypeFromIdx(Index idx) { |
| if (idx >= types.size()) { |
| return in.err("type index out of bounds"); |
| } |
| return types[idx]; |
| } |
| |
| Result<TypeUseT> makeTypeUse(Index pos, |
| std::optional<HeapTypeT>, |
| ParamsT* params, |
| ResultsT* results) { |
| std::vector<Type> paramTypes; |
| if (params) { |
| paramTypes = getUnnamedTypes(*params); |
| } |
| |
| std::vector<Type> 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<ParseModuleTypesCtx>, |
| 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 TableTypeT = Type; |
| using TypeUseT = TypeUse; |
| |
| using ElemListT = Type; |
| |
| Lexer in; |
| |
| Module& wasm; |
| |
| const std::vector<HeapType>& types; |
| const std::unordered_map<Index, HeapType>& implicitTypes; |
| const std::unordered_map<Index, Index>& implicitElemIndices; |
| |
| // The index of the current type. |
| Index index = 0; |
| |
| ParseModuleTypesCtx( |
| Lexer& in, |
| Module& wasm, |
| const std::vector<HeapType>& types, |
| const std::unordered_map<Index, HeapType>& implicitTypes, |
| const std::unordered_map<Index, Index>& implicitElemIndices, |
| const IndexMap& typeIndices) |
| : TypeParserCtx<ParseModuleTypesCtx>(typeIndices), in(in), wasm(wasm), |
| types(types), implicitTypes(implicitTypes), |
| implicitElemIndices(implicitElemIndices) {} |
| |
| bool skipFunctionBody() { return true; } |
| |
| Result<HeapTypeT> getHeapTypeFromIdx(Index idx) { |
| if (idx >= types.size()) { |
| return in.err("type index out of bounds"); |
| } |
| return types[idx]; |
| } |
| |
| Result<TypeUseT> makeTypeUse(Index pos, |
| std::optional<HeapTypeT> type, |
| ParamsT* params, |
| ResultsT* results) { |
| std::vector<Name> 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<HeapType> getBlockTypeFromTypeUse(Index pos, TypeUse use) { |
| return use.type; |
| } |
| |
| GlobalTypeT makeGlobalType(Mutability mutability, TypeT type) { |
| return {mutability, type}; |
| } |
| |
| Type makeElemList(Type type) { return type; } |
| Type makeFuncElemList() { return Type(HeapType::func, Nullable); } |
| void appendElem(ElemListT&, ExprT) {} |
| void appendFuncElem(ElemListT&, FuncIdxT) {} |
| |
| LimitsT getLimitsFromElems(ElemListT) { return Ok{}; } |
| |
| Type makeTableType(Type addressType, LimitsT, Type type) { return type; } |
| |
| LimitsT getLimitsFromData(DataStringT) { return Ok{}; } |
| MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } |
| |
| Result<> addFunc(Name name, |
| const std::vector<Name>&, |
| ImportNames*, |
| TypeUse type, |
| std::optional<LocalsT> locals, |
| std::vector<Annotation>&&, |
| 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<> addTable( |
| Name, const std::vector<Name>&, ImportNames*, Type ttype, Index pos) { |
| auto& t = wasm.tables[index]; |
| if (!ttype.isRef()) { |
| return in.err(pos, "expected reference type"); |
| } |
| t->type = ttype; |
| return Ok{}; |
| } |
| |
| Result<> addImplicitElems(Type type, ElemListT&&) { |
| auto& t = wasm.tables[index]; |
| auto& e = wasm.elementSegments[implicitElemIndices.at(index)]; |
| e->type = t->type; |
| return Ok{}; |
| } |
| |
| Result<> |
| addMemory(Name, const std::vector<Name>&, ImportNames*, MemTypeT, Index) { |
| return Ok{}; |
| } |
| |
| Result<> addImplicitData(DataStringT&& data) { return Ok{}; } |
| |
| Result<> addGlobal(Name, |
| const std::vector<Name>&, |
| ImportNames*, |
| GlobalType type, |
| std::optional<ExprT>, |
| Index) { |
| auto& g = wasm.globals[index]; |
| g->mutable_ = type.mutability; |
| g->type = type.type; |
| return Ok{}; |
| } |
| |
| Result<> |
| addElem(Name, TableIdxT*, std::optional<ExprT>, ElemListT&& type, Index) { |
| auto& e = wasm.elementSegments[index]; |
| e->type = type; |
| return Ok{}; |
| } |
| |
| Result<> addDeclareElem(Name, ElemListT&&, Index) { return Ok{}; } |
| |
| Result<> |
| addTag(Name, const std::vector<Name>&, ImportNames*, TypeUse use, Index pos) { |
| auto& t = wasm.tags[index]; |
| if (!use.type.isSignature()) { |
| return in.err(pos, "tag type must be a signature"); |
| } |
| t->sig = use.type.getSignature(); |
| return Ok{}; |
| } |
| }; |
| |
| // Phase 5: Parse module element definitions, including instructions. |
| struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { |
| using GlobalTypeT = Ok; |
| using TableTypeT = Ok; |
| using TypeUseT = HeapType; |
| |
| using FieldIdxT = Index; |
| using FuncIdxT = Name; |
| using LocalIdxT = Index; |
| using LabelIdxT = Index; |
| using GlobalIdxT = Name; |
| using TableIdxT = Name; |
| using MemoryIdxT = Name; |
| using ElemIdxT = Name; |
| using DataIdxT = Name; |
| using TagIdxT = Name; |
| |
| using MemargT = Memarg; |
| |
| using ExprT = Expression*; |
| using ElemListT = std::vector<Expression*>; |
| |
| struct CatchInfo; |
| using CatchT = CatchInfo; |
| using CatchListT = std::vector<CatchInfo>; |
| |
| using TagLabelListT = std::vector<std::pair<TagIdxT, LabelIdxT>>; |
| |
| Lexer in; |
| |
| Module& wasm; |
| Builder builder; |
| |
| const std::vector<HeapType>& types; |
| const std::unordered_map<Index, HeapType>& implicitTypes; |
| const std::unordered_map<HeapType, std::unordered_map<Name, Index>>& |
| typeNames; |
| const std::unordered_map<Index, Index>& implicitElemIndices; |
| |
| std::unordered_map<std::string_view, Index> debugSymbolNameIndices; |
| std::unordered_map<std::string_view, Index> debugFileIndices; |
| |
| // 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; |
| |
| Result<> visitFunctionStart(Function* func) { |
| this->func = func; |
| CHECK_ERR(irBuilder.visitFunctionStart(func)); |
| return Ok{}; |
| } |
| |
| ParseDefsCtx( |
| Lexer& in, |
| Module& wasm, |
| const std::vector<HeapType>& types, |
| const std::unordered_map<Index, HeapType>& implicitTypes, |
| const std::unordered_map<HeapType, std::unordered_map<Name, Index>>& |
| typeNames, |
| const std::unordered_map<Index, Index>& implicitElemIndices, |
| const IndexMap& typeIndices) |
| : TypeParserCtx(typeIndices), in(in), wasm(wasm), builder(wasm), |
| types(types), implicitTypes(implicitTypes), typeNames(typeNames), |
| implicitElemIndices(implicitElemIndices), irBuilder(wasm) {} |
| |
| template<typename T> Result<T> withLoc(Index pos, Result<T> res) { |
| if (auto err = res.getErr()) { |
| return in.err(pos, err->msg); |
| } |
| return res; |
| } |
| |
| template<typename T> Result<T> withLoc(Result<T> res) { |
| return withLoc(in.getPos(), res); |
| } |
| |
| HeapType getBlockTypeFromResult(const std::vector<Type> results) { |
| assert(results.size() == 1); |
| return HeapType(Signature(Type::none, results[0])); |
| } |
| |
| Result<HeapType> getBlockTypeFromTypeUse(Index pos, HeapType type) { |
| assert(type.isSignature()); |
| if (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 type; |
| } |
| |
| GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } |
| |
| std::vector<Expression*> makeElemList(TypeT) { return {}; } |
| std::vector<Expression*> makeFuncElemList() { return {}; } |
| void appendElem(std::vector<Expression*>& elems, Expression* expr) { |
| elems.push_back(expr); |
| } |
| void appendFuncElem(std::vector<Expression*>& elems, Name func) { |
| auto type = wasm.getFunction(func)->type; |
| elems.push_back(builder.makeRefFunc(func, type)); |
| } |
| |
| LimitsT getLimitsFromElems(std::vector<Expression*>& elems) { return Ok{}; } |
| |
| TableTypeT makeTableType(Type, LimitsT, Type) { return Ok{}; } |
| |
| struct CatchInfo { |
| Name tag; |
| Index label; |
| bool isRef; |
| }; |
| |
| std::vector<CatchInfo> makeCatchList() { return {}; } |
| void appendCatch(std::vector<CatchInfo>& list, CatchInfo info) { |
| list.push_back(info); |
| } |
| CatchInfo makeCatch(Name tag, Index label) { return {tag, label, false}; } |
| CatchInfo makeCatchRef(Name tag, Index label) { return {tag, label, true}; } |
| CatchInfo makeCatchAll(Index label) { return {{}, label, false}; } |
| CatchInfo makeCatchAllRef(Index label) { return {{}, label, true}; } |
| |
| TagLabelListT makeTagLabelList() { return {}; } |
| void appendTagLabel(TagLabelListT& tagLabels, Name tag, Index label) { |
| tagLabels.push_back({tag, label}); |
| } |
| |
| Result<HeapTypeT> getHeapTypeFromIdx(Index idx) { |
| if (idx >= types.size()) { |
| return in.err("type index out of bounds"); |
| } |
| return types[idx]; |
| } |
| |
| Result<Index> 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<Index> getFieldFromName(HeapType type, Name name) { |
| if (auto typeIt = typeNames.find(type); typeIt != typeNames.end()) { |
| const auto& fieldIdxs = typeIt->second; |
| if (auto fieldIt = fieldIdxs.find(name); fieldIt != fieldIdxs.end()) { |
| return fieldIt->second; |
| } |
| } |
| return in.err("unrecognized field name"); |
| } |
| |
| Result<Index> 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<Name> getFuncFromIdx(uint32_t idx) { |
| if (idx >= wasm.functions.size()) { |
| return in.err("function index out of bounds"); |
| } |
| return wasm.functions[idx]->name; |
| } |
| |
| Result<Name> getFuncFromName(Name name) { |
| if (!wasm.getFunctionOrNull(name)) { |
| return in.err("function $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Index> 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<Name> getGlobalFromIdx(uint32_t idx) { |
| if (idx >= wasm.globals.size()) { |
| return in.err("global index out of bounds"); |
| } |
| return wasm.globals[idx]->name; |
| } |
| |
| Result<Name> getGlobalFromName(Name name) { |
| if (!wasm.getGlobalOrNull(name)) { |
| return in.err("global $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Name> getTableFromIdx(uint32_t idx) { |
| if (idx >= wasm.tables.size()) { |
| return in.err("table index out of bounds"); |
| } |
| return wasm.tables[idx]->name; |
| } |
| |
| Result<Name> getTableFromName(Name name) { |
| if (!wasm.getTableOrNull(name)) { |
| return in.err("table $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Name> getMemoryFromIdx(uint32_t idx) { |
| if (idx >= wasm.memories.size()) { |
| return in.err("memory index out of bounds"); |
| } |
| return wasm.memories[idx]->name; |
| } |
| |
| Result<Name> getMemoryFromName(Name name) { |
| if (!wasm.getMemoryOrNull(name)) { |
| return in.err("memory $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Name> getElemFromIdx(uint32_t idx) { |
| if (idx >= wasm.elementSegments.size()) { |
| return in.err("elem index out of bounds"); |
| } |
| return wasm.elementSegments[idx]->name; |
| } |
| |
| Result<Name> getElemFromName(Name name) { |
| if (!wasm.getElementSegmentOrNull(name)) { |
| return in.err("elem $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Name> getDataFromIdx(uint32_t idx) { |
| if (idx >= wasm.dataSegments.size()) { |
| return in.err("data index out of bounds"); |
| } |
| return wasm.dataSegments[idx]->name; |
| } |
| |
| Result<Name> getDataFromName(Name name) { |
| if (!wasm.getDataSegmentOrNull(name)) { |
| return in.err("data $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<Index> getLabelFromIdx(uint32_t idx, bool) { return idx; } |
| |
| Result<Index> getLabelFromName(Name name, bool inDelegate) { |
| return irBuilder.getLabelIndex(name, inDelegate); |
| } |
| |
| Result<Name> getTagFromIdx(uint32_t idx) { |
| if (idx >= wasm.tags.size()) { |
| return in.err("tag index out of bounds"); |
| } |
| return wasm.tags[idx]->name; |
| } |
| |
| Result<Name> getTagFromName(Name name) { |
| if (!wasm.getTagOrNull(name)) { |
| return in.err("tag $" + name.toString() + " does not exist"); |
| } |
| return name; |
| } |
| |
| Result<TypeUseT> makeTypeUse(Index pos, |
| std::optional<HeapTypeT> type, |
| ParamsT* params, |
| ResultsT* results); |
| |
| Result<> addFunc(Name, |
| const std::vector<Name>&, |
| ImportNames*, |
| TypeUseT, |
| std::optional<LocalsT>, |
| std::vector<Annotation>&&, |
| Index) { |
| return Ok{}; |
| } |
| |
| Result<> |
| addTable(Name, const std::vector<Name>&, ImportNames*, TableTypeT, Index) { |
| return Ok{}; |
| } |
| |
| Result<> |
| addMemory(Name, const std::vector<Name>&, ImportNames*, TableTypeT, Index) { |
| return Ok{}; |
| } |
| |
| Result<> addGlobal(Name, |
| const std::vector<Name>&, |
| ImportNames*, |
| GlobalTypeT, |
| std::optional<ExprT> exp, |
| Index); |
| |
| Result<> addStart(Name name, Index pos) { |
| wasm.start = name; |
| return Ok{}; |
| } |
| |
| Result<> addImplicitElems(Type type, std::vector<Expression*>&& elems); |
| |
| Result<> addDeclareElem(Name, std::vector<Expression*>&&, Index) { |
| // TODO: Validate that referenced functions appear in a declarative element |
| // segment. |
| return Ok{}; |
| } |
| |
| Result<> addElem(Name, |
| Name* table, |
| std::optional<Expression*> offset, |
| std::vector<Expression*>&& elems, |
| Index pos); |
| |
| Result<> |
| addData(Name, Name* mem, std::optional<ExprT> offset, DataStringT, Index pos); |
| |
| Result<> |
| addTag(Name, const std::vector<Name>, ImportNames*, TypeUseT, Index) { |
| return Ok{}; |
| } |
| |
| Result<> addExport(Index pos, Name value, Name name, ExternalKind kind) { |
| if (wasm.getExportOrNull(name)) { |
| return in.err(pos, "duplicate export"); |
| } |
| wasm.addExport(builder.makeExport(name, value, kind)); |
| return Ok{}; |
| } |
| |
| Result<Index> 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<Expression*> makeExpr() { return withLoc(irBuilder.build()); } |
| |
| Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; } |
| |
| Result<Name> getTable(Index pos, Name* table) { |
| if (table) { |
| return *table; |
| } |
| if (wasm.tables.empty()) { |
| return in.err(pos, "table required, but there is no table"); |
| } |
| return wasm.tables[0]->name; |
| } |
| |
| Result<Name> 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; |
| } |
| |
| void setSrcLoc(const std::vector<Annotation>& annotations) { |
| const Annotation* annotation = nullptr; |
| for (auto& a : annotations) { |
| if (a.kind == srcAnnotationKind) { |
| annotation = &a; |
| } |
| } |
| if (!annotation) { |
| return; |
| } |
| Lexer lexer(annotation->contents); |
| if (lexer.empty()) { |
| irBuilder.setDebugLocation(std::nullopt); |
| return; |
| } |
| |
| auto contents = lexer.next(); |
| |
| auto fileSize = contents.find(':'); |
| if (fileSize == 0 || fileSize == contents.npos) { |
| return; |
| } |
| auto file = contents.substr(0, fileSize); |
| contents = contents.substr(fileSize + 1); |
| |
| auto lineSize = contents.find(':'); |
| if (lineSize == contents.npos) { |
| return; |
| } |
| lexer = Lexer(contents.substr(0, lineSize)); |
| auto line = lexer.takeU32(); |
| if (!line || !lexer.empty()) { |
| return; |
| } |
| contents = contents.substr(lineSize + 1); |
| |
| auto colSize = contents.find(':'); |
| if (colSize == contents.npos) { |
| colSize = contents.size(); |
| if (colSize == 0) { |
| return; |
| } |
| } |
| lexer = Lexer(contents.substr(0, colSize)); |
| auto col = lexer.takeU32(); |
| if (!col) { |
| return; |
| } |
| |
| std::optional<BinaryLocation> symbolNameIndex; |
| if (colSize != contents.size()) { |
| contents = contents.substr(colSize + 1); |
| auto symbolName = contents; |
| auto [it, inserted] = debugSymbolNameIndices.insert( |
| {symbolName, debugSymbolNameIndices.size()}); |
| if (inserted) { |
| assert(wasm.debugInfoSymbolNames.size() == it->second); |
| wasm.debugInfoSymbolNames.push_back(std::string(symbolName)); |
| } |
| symbolNameIndex = it->second; |
| } |
| |
| // TODO: If we ever parallelize the parse, access to |
| // `wasm.debugInfoFileNames` will have to be protected by a lock. |
| auto [it, inserted] = |
| debugFileIndices.insert({file, debugFileIndices.size()}); |
| if (inserted) { |
| assert(wasm.debugInfoFileNames.size() == it->second); |
| wasm.debugInfoFileNames.push_back(std::string(file)); |
| } |
| irBuilder.setDebugLocation( |
| Function::DebugLocation({it->second, *line, *col, symbolNameIndex})); |
| } |
| |
| Result<> makeBlock(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::optional<Name> 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<> makeIf(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::optional<Name> label, |
| HeapType type) { |
| // TODO: validate labels? |
| // TODO: Move error on input types to here? |
| return withLoc( |
| pos, |
| irBuilder.makeIf(label ? *label : Name{}, type.getSignature().results)); |
| } |
| |
| Result<> visitElse() { return withLoc(irBuilder.visitElse()); } |
| |
| Result<> makeLoop(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::optional<Name> label, |
| HeapType type) { |
| // TODO: validate labels? |
| // TODO: Move error on input types to here? |
| return withLoc( |
| pos, |
| irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results)); |
| } |
| |
| Result<> makeTry(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::optional<Name> label, |
| HeapType type) { |
| // TODO: validate labels? |
| // TODO: Move error on input types to here? |
| return withLoc( |
| pos, |
| irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results)); |
| } |
| |
| Result<> makeTryTable(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::optional<Name> label, |
| HeapType type, |
| const std::vector<CatchInfo>& info) { |
| std::vector<Name> tags; |
| std::vector<Index> labels; |
| std::vector<bool> isRefs; |
| for (auto& info : info) { |
| tags.push_back(info.tag); |
| labels.push_back(info.label); |
| isRefs.push_back(info.isRef); |
| } |
| return withLoc(pos, |
| irBuilder.makeTryTable(label ? *label : Name{}, |
| type.getSignature().results, |
| tags, |
| labels, |
| isRefs)); |
| } |
| |
| Result<> visitCatch(Index pos, Name tag) { |
| return withLoc(pos, irBuilder.visitCatch(tag)); |
| } |
| |
| Result<> visitCatchAll(Index pos) { |
| return withLoc(pos, irBuilder.visitCatchAll()); |
| } |
| |
| Result<> visitDelegate(Index pos, Index label) { |
| return withLoc(pos, irBuilder.visitDelegate(label)); |
| } |
| |
| Result<> visitEnd() { return withLoc(irBuilder.visitEnd()); } |
| |
| Result<> makeUnreachable(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeUnreachable()); |
| } |
| |
| Result<> makeNop(Index pos, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeNop()); |
| } |
| |
| Result<> makeBinary(Index pos, |
| const std::vector<Annotation>& annotations, |
| BinaryOp op) { |
| return withLoc(pos, irBuilder.makeBinary(op)); |
| } |
| |
| Result<> |
| makeUnary(Index pos, const std::vector<Annotation>& annotations, UnaryOp op) { |
| return withLoc(pos, irBuilder.makeUnary(op)); |
| } |
| |
| Result<> makeSelect(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::vector<Type>* 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, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeDrop()); |
| } |
| |
| Result<> makeMemorySize(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* mem) { |
| auto m = getMemory(pos, mem); |
| CHECK_ERR(m); |
| return withLoc(pos, irBuilder.makeMemorySize(*m)); |
| } |
| |
| Result<> makeMemoryGrow(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* mem) { |
| auto m = getMemory(pos, mem); |
| CHECK_ERR(m); |
| return withLoc(pos, irBuilder.makeMemoryGrow(*m)); |
| } |
| |
| Result<> makeLocalGet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index local) { |
| return withLoc(pos, irBuilder.makeLocalGet(local)); |
| } |
| |
| Result<> makeLocalTee(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index local) { |
| return withLoc(pos, irBuilder.makeLocalTee(local)); |
| } |
| |
| Result<> makeLocalSet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index local) { |
| return withLoc(pos, irBuilder.makeLocalSet(local)); |
| } |
| |
| Result<> makeGlobalGet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name global) { |
| return withLoc(pos, irBuilder.makeGlobalGet(global)); |
| } |
| |
| Result<> makeGlobalSet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name global) { |
| assert(wasm.getGlobalOrNull(global)); |
| return withLoc(pos, irBuilder.makeGlobalSet(global)); |
| } |
| |
| Result<> makeI32Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| uint32_t c) { |
| return withLoc(pos, irBuilder.makeConst(Literal(c))); |
| } |
| |
| Result<> makeI64Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| uint64_t c) { |
| return withLoc(pos, irBuilder.makeConst(Literal(c))); |
| } |
| |
| Result<> |
| makeF32Const(Index pos, const std::vector<Annotation>& annotations, float c) { |
| return withLoc(pos, irBuilder.makeConst(Literal(c))); |
| } |
| |
| Result<> makeF64Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| double c) { |
| return withLoc(pos, irBuilder.makeConst(Literal(c))); |
| } |
| |
| Result<> makeI8x16Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<uint8_t, 16>& vals) { |
| std::array<Literal, 16> lanes; |
| for (size_t i = 0; i < 16; ++i) { |
| lanes[i] = Literal(uint32_t(vals[i])); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeI16x8Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<uint16_t, 8>& vals) { |
| std::array<Literal, 8> lanes; |
| for (size_t i = 0; i < 8; ++i) { |
| lanes[i] = Literal(uint32_t(vals[i])); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeI32x4Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<uint32_t, 4>& vals) { |
| std::array<Literal, 4> lanes; |
| for (size_t i = 0; i < 4; ++i) { |
| lanes[i] = Literal(vals[i]); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeI64x2Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<uint64_t, 2>& vals) { |
| std::array<Literal, 2> lanes; |
| for (size_t i = 0; i < 2; ++i) { |
| lanes[i] = Literal(vals[i]); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeF32x4Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<float, 4>& vals) { |
| std::array<Literal, 4> lanes; |
| for (size_t i = 0; i < 4; ++i) { |
| lanes[i] = Literal(vals[i]); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeF64x2Const(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<double, 2>& vals) { |
| std::array<Literal, 2> lanes; |
| for (size_t i = 0; i < 2; ++i) { |
| lanes[i] = Literal(vals[i]); |
| } |
| return withLoc(pos, irBuilder.makeConst(Literal(lanes))); |
| } |
| |
| Result<> makeLoad(Index pos, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| Name* mem, |
| Memarg memarg) { |
| auto m = getMemory(pos, mem); |
| CHECK_ERR(m); |
| return withLoc(pos, irBuilder.makeAtomicNotify(memarg.offset, *m)); |
| } |
| |
| Result<> makeAtomicFence(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeAtomicFence()); |
| } |
| |
| Result<> makeSIMDExtract(Index pos, |
| const std::vector<Annotation>& annotations, |
| SIMDExtractOp op, |
| uint8_t lane) { |
| return withLoc(pos, irBuilder.makeSIMDExtract(op, lane)); |
| } |
| |
| Result<> makeSIMDReplace(Index pos, |
| const std::vector<Annotation>& annotations, |
| SIMDReplaceOp op, |
| uint8_t lane) { |
| return withLoc(pos, irBuilder.makeSIMDReplace(op, lane)); |
| } |
| |
| Result<> makeSIMDShuffle(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::array<uint8_t, 16>& lanes) { |
| return withLoc(pos, irBuilder.makeSIMDShuffle(lanes)); |
| } |
| |
| Result<> makeSIMDTernary(Index pos, |
| const std::vector<Annotation>& annotations, |
| SIMDTernaryOp op) { |
| return withLoc(pos, irBuilder.makeSIMDTernary(op)); |
| } |
| |
| Result<> makeSIMDShift(Index pos, |
| const std::vector<Annotation>& annotations, |
| SIMDShiftOp op) { |
| return withLoc(pos, irBuilder.makeSIMDShift(op)); |
| } |
| |
| Result<> makeSIMDLoad(Index pos, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| Name* mem, |
| Name data) { |
| auto m = getMemory(pos, mem); |
| CHECK_ERR(m); |
| return withLoc(pos, irBuilder.makeMemoryInit(data, *m)); |
| } |
| |
| Result<> makeDataDrop(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name data) { |
| return withLoc(pos, irBuilder.makeDataDrop(data)); |
| } |
| |
| Result<> makeMemoryCopy(Index pos, |
| const std::vector<Annotation>& annotations, |
| 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, |
| const std::vector<Annotation>& annotations, |
| Name* mem) { |
| auto m = getMemory(pos, mem); |
| CHECK_ERR(m); |
| return withLoc(pos, irBuilder.makeMemoryFill(*m)); |
| } |
| |
| Result<> |
| makePop(Index pos, const std::vector<Annotation>& annotations, Type type) { |
| return withLoc(pos, irBuilder.makePop(type)); |
| } |
| |
| Result<> makeCall(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name func, |
| bool isReturn) { |
| return withLoc(pos, irBuilder.makeCall(func, isReturn)); |
| } |
| |
| Result<> makeCallIndirect(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table, |
| HeapType type, |
| bool isReturn) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeCallIndirect(*t, type, isReturn)); |
| } |
| |
| Result<> makeBreak(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index label, |
| bool isConditional) { |
| return withLoc(pos, irBuilder.makeBreak(label, isConditional)); |
| } |
| |
| Result<> makeSwitch(Index pos, |
| const std::vector<Annotation>& annotations, |
| const std::vector<Index> labels, |
| Index defaultLabel) { |
| return withLoc(pos, irBuilder.makeSwitch(labels, defaultLabel)); |
| } |
| |
| Result<> makeReturn(Index pos, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeReturn()); |
| } |
| |
| Result<> makeRefNull(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeRefNull(type)); |
| } |
| |
| Result<> makeRefIsNull(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeRefIsNull()); |
| } |
| |
| Result<> makeRefFunc(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name func) { |
| return withLoc(pos, irBuilder.makeRefFunc(func)); |
| } |
| |
| Result<> makeRefEq(Index pos, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeRefEq()); |
| } |
| |
| Result<> makeTableGet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableGet(*t)); |
| } |
| |
| Result<> makeTableSet(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableSet(*t)); |
| } |
| |
| Result<> makeTableSize(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableSize(*t)); |
| } |
| |
| Result<> makeTableGrow(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableGrow(*t)); |
| } |
| |
| Result<> makeTableFill(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableFill(*t)); |
| } |
| |
| Result<> makeTableCopy(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* destTable, |
| Name* srcTable) { |
| auto dest = getTable(pos, destTable); |
| CHECK_ERR(dest); |
| auto src = getTable(pos, srcTable); |
| CHECK_ERR(src); |
| return withLoc(pos, irBuilder.makeTableCopy(*dest, *src)); |
| } |
| |
| Result<> makeTableInit(Index pos, |
| const std::vector<Annotation>& annotations, |
| Name* table, |
| Name elem) { |
| auto t = getTable(pos, table); |
| CHECK_ERR(t); |
| return withLoc(pos, irBuilder.makeTableInit(elem, *t)); |
| } |
| |
| Result<> |
| makeThrow(Index pos, const std::vector<Annotation>& annotations, Name tag) { |
| return withLoc(pos, irBuilder.makeThrow(tag)); |
| } |
| |
| Result<> makeRethrow(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index label) { |
| return withLoc(pos, irBuilder.makeRethrow(label)); |
| } |
| |
| Result<> makeThrowRef(Index pos, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeThrowRef()); |
| } |
| |
| Result<> makeTupleMake(Index pos, |
| const std::vector<Annotation>& annotations, |
| uint32_t arity) { |
| return withLoc(pos, irBuilder.makeTupleMake(arity)); |
| } |
| |
| Result<> makeTupleExtract(Index pos, |
| const std::vector<Annotation>& annotations, |
| uint32_t arity, |
| uint32_t index) { |
| return withLoc(pos, irBuilder.makeTupleExtract(arity, index)); |
| } |
| |
| Result<> makeTupleDrop(Index pos, |
| const std::vector<Annotation>& annotations, |
| uint32_t arity) { |
| return withLoc(pos, irBuilder.makeTupleDrop(arity)); |
| } |
| |
| Result<> makeCallRef(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| bool isReturn) { |
| return withLoc(pos, irBuilder.makeCallRef(type, isReturn)); |
| } |
| |
| Result<> makeRefI31(Index pos, |
| const std::vector<Annotation>& annotations, |
| Shareability share) { |
| return withLoc(pos, irBuilder.makeRefI31(share)); |
| } |
| |
| Result<> makeI31Get(Index pos, |
| const std::vector<Annotation>& annotations, |
| bool signed_) { |
| return withLoc(pos, irBuilder.makeI31Get(signed_)); |
| } |
| |
| Result<> makeRefTest(Index pos, |
| const std::vector<Annotation>& annotations, |
| Type type) { |
| return withLoc(pos, irBuilder.makeRefTest(type)); |
| } |
| |
| Result<> makeRefCast(Index pos, |
| const std::vector<Annotation>& annotations, |
| Type type) { |
| return withLoc(pos, irBuilder.makeRefCast(type)); |
| } |
| |
| Result<> makeBrOn(Index pos, |
| const std::vector<Annotation>& annotations, |
| Index label, |
| BrOnOp op, |
| Type in = Type::none, |
| Type out = Type::none) { |
| return withLoc(pos, irBuilder.makeBrOn(label, op, in, out)); |
| } |
| |
| Result<> makeStructNew(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeStructNew(type)); |
| } |
| |
| Result<> makeStructNewDefault(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeStructNewDefault(type)); |
| } |
| |
| Result<> makeStructGet(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Index field, |
| bool signed_) { |
| return withLoc(pos, irBuilder.makeStructGet(type, field, signed_)); |
| } |
| |
| Result<> makeStructSet(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Index field) { |
| return withLoc(pos, irBuilder.makeStructSet(type, field)); |
| } |
| |
| Result<> makeArrayNew(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeArrayNew(type)); |
| } |
| |
| Result<> makeArrayNewDefault(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeArrayNewDefault(type)); |
| } |
| |
| Result<> makeArrayNewData(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Name data) { |
| return withLoc(pos, irBuilder.makeArrayNewData(type, data)); |
| } |
| |
| Result<> makeArrayNewElem(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Name elem) { |
| return withLoc(pos, irBuilder.makeArrayNewElem(type, elem)); |
| } |
| |
| Result<> makeArrayNewFixed(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| uint32_t arity) { |
| return withLoc(pos, irBuilder.makeArrayNewFixed(type, arity)); |
| } |
| |
| Result<> makeArrayGet(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| bool signed_) { |
| return withLoc(pos, irBuilder.makeArrayGet(type, signed_)); |
| } |
| |
| Result<> makeArraySet(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeArraySet(type)); |
| } |
| |
| Result<> makeArrayLen(Index pos, const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeArrayLen()); |
| } |
| |
| Result<> makeArrayCopy(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType destType, |
| HeapType srcType) { |
| return withLoc(pos, irBuilder.makeArrayCopy(destType, srcType)); |
| } |
| |
| Result<> makeArrayFill(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeArrayFill(type)); |
| } |
| |
| Result<> makeArrayInitData(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Name data) { |
| return withLoc(pos, irBuilder.makeArrayInitData(type, data)); |
| } |
| |
| Result<> makeArrayInitElem(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| Name elem) { |
| return withLoc(pos, irBuilder.makeArrayInitElem(type, elem)); |
| } |
| |
| Result<> |
| makeRefAs(Index pos, const std::vector<Annotation>& annotations, RefAsOp op) { |
| return withLoc(pos, irBuilder.makeRefAs(op)); |
| } |
| |
| Result<> makeStringNew(Index pos, |
| const std::vector<Annotation>& annotations, |
| StringNewOp op) { |
| return withLoc(pos, irBuilder.makeStringNew(op)); |
| } |
| |
| Result<> makeStringConst(Index pos, |
| const std::vector<Annotation>& annotations, |
| std::string_view str) { |
| // Re-encode from WTF-8 to WTF-16. |
| std::stringstream wtf16; |
| if (!String::convertWTF8ToWTF16(wtf16, str)) { |
| return in.err(pos, "invalid string constant"); |
| } |
| // TODO: Use wtf16.view() once we have C++20. |
| return withLoc(pos, irBuilder.makeStringConst(wtf16.str())); |
| } |
| |
| Result<> makeStringMeasure(Index pos, |
| const std::vector<Annotation>& annotations, |
| StringMeasureOp op) { |
| return withLoc(pos, irBuilder.makeStringMeasure(op)); |
| } |
| |
| Result<> makeStringEncode(Index pos, |
| const std::vector<Annotation>& annotations, |
| StringEncodeOp op) { |
| return withLoc(pos, irBuilder.makeStringEncode(op)); |
| } |
| |
| Result<> makeStringConcat(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeStringConcat()); |
| } |
| |
| Result<> makeStringEq(Index pos, |
| const std::vector<Annotation>& annotations, |
| StringEqOp op) { |
| return withLoc(pos, irBuilder.makeStringEq(op)); |
| } |
| |
| Result<> makeStringWTF16Get(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeStringWTF16Get()); |
| } |
| |
| Result<> makeStringSliceWTF(Index pos, |
| const std::vector<Annotation>& annotations) { |
| return withLoc(pos, irBuilder.makeStringSliceWTF()); |
| } |
| |
| Result<> makeContBind(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType contTypeBefore, |
| HeapType contTypeAfter) { |
| return withLoc(pos, irBuilder.makeContBind(contTypeBefore, contTypeAfter)); |
| } |
| |
| Result<> makeContNew(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type) { |
| return withLoc(pos, irBuilder.makeContNew(type)); |
| } |
| |
| Result<> makeResume(Index pos, |
| const std::vector<Annotation>& annotations, |
| HeapType type, |
| const TagLabelListT& tagLabels) { |
| std::vector<Name> tags; |
| std::vector<Index> labels; |
| tags.reserve(tagLabels.size()); |
| labels.reserve(tagLabels.size()); |
| for (auto& [tag, label] : tagLabels) { |
| tags.push_back(tag); |
| labels.push_back(label); |
| } |
| return withLoc(pos, irBuilder.makeResume(type, tags, labels)); |
| } |
| |
| Result<> |
| makeSuspend(Index pos, const std::vector<Annotation>& annotations, Name tag) { |
| return withLoc(pos, irBuilder.makeSuspend(tag)); |
| } |
| }; |
| |
| } // namespace wasm::WATParser |
| |
| #endif // parser_context_h |