/* * Copyright 2016 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 WABT_IR_H_ #define WABT_IR_H_ #include #include #include #include #include #include #include #include #include "wabt/binding-hash.h" #include "wabt/common.h" #include "wabt/intrusive-list.h" #include "wabt/opcode.h" namespace wabt { struct Module; enum class VarType { Index, Name, }; struct Var { explicit Var(); explicit Var(Index index, const Location& loc); explicit Var(std::string_view name, const Location& loc); Var(Var&&); Var(const Var&); Var& operator=(const Var&); Var& operator=(Var&&); ~Var(); VarType type() const { return type_; } bool is_index() const { return type_ == VarType::Index; } bool is_name() const { return type_ == VarType::Name; } Index index() const { assert(is_index()); return index_; } const std::string& name() const { assert(is_name()); return name_; } void set_index(Index); void set_name(std::string&&); void set_name(std::string_view); Location loc; private: void Destroy(); VarType type_; union { Index index_; std::string name_; }; }; using VarVector = std::vector; struct Const { static constexpr uintptr_t kRefNullBits = ~uintptr_t(0); Const() : Const(Type::I32, uint32_t(0)) {} static Const I32(uint32_t val = 0, const Location& loc = Location()) { return Const(Type::I32, val, loc); } static Const I64(uint64_t val = 0, const Location& loc = Location()) { return Const(Type::I64, val, loc); } static Const F32(uint32_t val = 0, const Location& loc = Location()) { return Const(Type::F32, val, loc); } static Const F64(uint64_t val = 0, const Location& loc = Location()) { return Const(Type::F64, val, loc); } static Const V128(v128 val, const Location& loc = Location()) { return Const(Type::V128, val, loc); } Type type() const { return type_; } Type lane_type() const { assert(type_ == Type::V128); return lane_type_; } int lane_count() const { switch (lane_type()) { case Type::I8: return 16; case Type::I16: return 8; case Type::I32: return 4; case Type::I64: return 2; case Type::F32: return 4; case Type::F64: return 2; default: WABT_UNREACHABLE; } } uint32_t u32() const { return data_.u32(0); } uint64_t u64() const { return data_.u64(0); } uint32_t f32_bits() const { return data_.f32_bits(0); } uint64_t f64_bits() const { return data_.f64_bits(0); } uintptr_t ref_bits() const { return data_.To(0); } v128 vec128() const { return data_; } template T v128_lane(int lane) const { return data_.To(lane); } void set_u32(uint32_t x) { From(Type::I32, x); } void set_u64(uint64_t x) { From(Type::I64, x); } void set_f32(uint32_t x) { From(Type::F32, x); } void set_f64(uint64_t x) { From(Type::F64, x); } void set_v128_u8(int lane, uint8_t x) { set_v128_lane(lane, Type::I8, x); } void set_v128_u16(int lane, uint16_t x) { set_v128_lane(lane, Type::I16, x); } void set_v128_u32(int lane, uint32_t x) { set_v128_lane(lane, Type::I32, x); } void set_v128_u64(int lane, uint64_t x) { set_v128_lane(lane, Type::I64, x); } void set_v128_f32(int lane, uint32_t x) { set_v128_lane(lane, Type::F32, x); } void set_v128_f64(int lane, uint64_t x) { set_v128_lane(lane, Type::F64, x); } // Only used for expectations. (e.g. wast assertions) void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); } void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); } void set_funcref() { From(Type::FuncRef, 0); } void set_externref(uintptr_t x) { From(Type::ExternRef, x); } void set_null(Type type) { From(type, kRefNullBits); } bool is_expected_nan(int lane = 0) const { return expected_nan(lane) != ExpectedNan::None; } ExpectedNan expected_nan(int lane = 0) const { return lane < 4 ? nan_[lane] : ExpectedNan::None; } void set_expected_nan(int lane, ExpectedNan nan) { if (lane < 4) { nan_[lane] = nan; } } // v128 support Location loc; private: template void set_v128_lane(int lane, Type lane_type, T x) { lane_type_ = lane_type; From(Type::V128, x, lane); set_expected_nan(lane, ExpectedNan::None); } template Const(Type type, T data, const Location& loc = Location()) : loc(loc) { From(type, data); } template void From(Type type, T data, int lane = 0) { static_assert(sizeof(T) <= sizeof(data_), "Invalid cast!"); assert((lane + 1) * sizeof(T) <= sizeof(data_)); type_ = type; data_.From(lane, data); set_expected_nan(lane, ExpectedNan::None); } Type type_; Type lane_type_; // Only valid if type_ == Type::V128. v128 data_; ExpectedNan nan_[4]; }; using ConstVector = std::vector; enum class ExpectationType { Values, Either, }; class Expectation { public: Expectation() = delete; virtual ~Expectation() = default; ExpectationType type() const { return type_; } Location loc; ConstVector expected; protected: explicit Expectation(ExpectationType type, const Location& loc = Location()) : loc(loc), type_(type) {} private: ExpectationType type_; }; template class ExpectationMixin : public Expectation { public: static bool classof(const Expectation* expectation) { return expectation->type() == TypeEnum; } explicit ExpectationMixin(const Location& loc = Location()) : Expectation(TypeEnum, loc) {} }; class ValueExpectation : public ExpectationMixin { public: explicit ValueExpectation(const Location& loc = Location()) : ExpectationMixin(loc) {} }; struct EitherExpectation : public ExpectationMixin { public: explicit EitherExpectation(const Location& loc = Location()) : ExpectationMixin(loc) {} }; typedef std::unique_ptr ExpectationPtr; struct FuncSignature { TypeVector param_types; TypeVector result_types; // Some types can have names, for example (ref $foo) has type $foo. // So to use this type we need to translate its name into // a proper index from the module type section. // This is the mapping from parameter/result index to its name. std::unordered_map param_type_names; std::unordered_map result_type_names; Index GetNumParams() const { return param_types.size(); } Index GetNumResults() const { return result_types.size(); } Type GetParamType(Index index) const { return param_types[index]; } Type GetResultType(Index index) const { return result_types[index]; } bool operator==(const FuncSignature&) const; }; enum class TypeEntryKind { Func, Struct, Array, }; class TypeEntry { public: WABT_DISALLOW_COPY_AND_ASSIGN(TypeEntry); virtual ~TypeEntry() = default; TypeEntryKind kind() const { return kind_; } Location loc; std::string name; protected: explicit TypeEntry(TypeEntryKind kind, std::string_view name = std::string_view(), const Location& loc = Location()) : loc(loc), name(name), kind_(kind) {} TypeEntryKind kind_; }; class FuncType : public TypeEntry { public: static bool classof(const TypeEntry* entry) { return entry->kind() == TypeEntryKind::Func; } explicit FuncType(std::string_view name = std::string_view()) : TypeEntry(TypeEntryKind::Func, name) {} Index GetNumParams() const { return sig.GetNumParams(); } Index GetNumResults() const { return sig.GetNumResults(); } Type GetParamType(Index index) const { return sig.GetParamType(index); } Type GetResultType(Index index) const { return sig.GetResultType(index); } FuncSignature sig; }; struct Field { std::string name; Type type = Type::Void; bool mutable_ = false; }; class StructType : public TypeEntry { public: static bool classof(const TypeEntry* entry) { return entry->kind() == TypeEntryKind::Struct; } explicit StructType(std::string_view name = std::string_view()) : TypeEntry(TypeEntryKind::Struct) {} std::vector fields; }; class ArrayType : public TypeEntry { public: static bool classof(const TypeEntry* entry) { return entry->kind() == TypeEntryKind::Array; } explicit ArrayType(std::string_view name = std::string_view()) : TypeEntry(TypeEntryKind::Array) {} Field field; }; struct FuncDeclaration { Index GetNumParams() const { return sig.GetNumParams(); } Index GetNumResults() const { return sig.GetNumResults(); } Type GetParamType(Index index) const { return sig.GetParamType(index); } Type GetResultType(Index index) const { return sig.GetResultType(index); } bool has_func_type = false; Var type_var; FuncSignature sig; }; enum class ExprType { AtomicLoad, AtomicRmw, AtomicRmwCmpxchg, AtomicStore, AtomicNotify, AtomicFence, AtomicWait, Binary, Block, Br, BrIf, BrTable, Call, CallIndirect, CallRef, CodeMetadata, Compare, Const, Convert, Drop, GlobalGet, GlobalSet, If, Load, LocalGet, LocalSet, LocalTee, Loop, MemoryCopy, DataDrop, MemoryFill, MemoryGrow, MemoryInit, MemorySize, Nop, RefIsNull, RefFunc, RefNull, Rethrow, Return, ReturnCall, ReturnCallIndirect, Select, SimdLaneOp, SimdLoadLane, SimdStoreLane, SimdShuffleOp, LoadSplat, LoadZero, Store, TableCopy, ElemDrop, TableInit, TableGet, TableGrow, TableSize, TableSet, TableFill, Ternary, Throw, Try, Unary, Unreachable, First = AtomicLoad, Last = Unreachable }; const char* GetExprTypeName(ExprType type); class Expr; using ExprList = intrusive_list; using BlockDeclaration = FuncDeclaration; struct Block { Block() = default; explicit Block(ExprList exprs) : exprs(std::move(exprs)) {} std::string label; BlockDeclaration decl; ExprList exprs; Location end_loc; }; struct Catch { explicit Catch(const Location& loc = Location()) : loc(loc) {} explicit Catch(const Var& var, const Location& loc = Location()) : loc(loc), var(var) {} Location loc; Var var; ExprList exprs; bool IsCatchAll() const { return var.is_index() && var.index() == kInvalidIndex; } }; using CatchVector = std::vector; enum class TryKind { Plain, Catch, Delegate }; class Expr : public intrusive_list_base { public: WABT_DISALLOW_COPY_AND_ASSIGN(Expr); Expr() = delete; virtual ~Expr() = default; ExprType type() const { return type_; } Location loc; protected: explicit Expr(ExprType type, const Location& loc = Location()) : loc(loc), type_(type) {} ExprType type_; }; const char* GetExprTypeName(const Expr& expr); template class ExprMixin : public Expr { public: static bool classof(const Expr* expr) { return expr->type() == TypeEnum; } explicit ExprMixin(const Location& loc = Location()) : Expr(TypeEnum, loc) {} }; template class MemoryExpr : public ExprMixin { public: MemoryExpr(Var memidx, const Location& loc = Location()) : ExprMixin(loc), memidx(memidx) {} Var memidx; }; template class MemoryBinaryExpr : public ExprMixin { public: MemoryBinaryExpr(Var srcmemidx, Var destmemidx, const Location& loc = Location()) : ExprMixin(loc), srcmemidx(srcmemidx), destmemidx(destmemidx) {} Var srcmemidx; Var destmemidx; }; using DropExpr = ExprMixin; using NopExpr = ExprMixin; using ReturnExpr = ExprMixin; using UnreachableExpr = ExprMixin; using MemoryGrowExpr = MemoryExpr; using MemorySizeExpr = MemoryExpr; using MemoryFillExpr = MemoryExpr; using MemoryCopyExpr = MemoryBinaryExpr; template class RefTypeExpr : public ExprMixin { public: RefTypeExpr(Type type, const Location& loc = Location()) : ExprMixin(loc), type(type) {} Type type; }; using RefNullExpr = RefTypeExpr; using RefIsNullExpr = ExprMixin; template class OpcodeExpr : public ExprMixin { public: OpcodeExpr(Opcode opcode, const Location& loc = Location()) : ExprMixin(loc), opcode(opcode) {} Opcode opcode; }; using BinaryExpr = OpcodeExpr; using CompareExpr = OpcodeExpr; using ConvertExpr = OpcodeExpr; using UnaryExpr = OpcodeExpr; using TernaryExpr = OpcodeExpr; class SimdLaneOpExpr : public ExprMixin { public: SimdLaneOpExpr(Opcode opcode, uint64_t val, const Location& loc = Location()) : ExprMixin(loc), opcode(opcode), val(val) {} Opcode opcode; uint64_t val; }; class SimdLoadLaneExpr : public MemoryExpr { public: SimdLoadLaneExpr(Opcode opcode, Var memidx, Address align, Address offset, uint64_t val, const Location& loc = Location()) : MemoryExpr(memidx, loc), opcode(opcode), align(align), offset(offset), val(val) {} Opcode opcode; Address align; Address offset; uint64_t val; }; class SimdStoreLaneExpr : public MemoryExpr { public: SimdStoreLaneExpr(Opcode opcode, Var memidx, Address align, Address offset, uint64_t val, const Location& loc = Location()) : MemoryExpr(memidx, loc), opcode(opcode), align(align), offset(offset), val(val) {} Opcode opcode; Address align; Address offset; uint64_t val; }; class SimdShuffleOpExpr : public ExprMixin { public: SimdShuffleOpExpr(Opcode opcode, v128 val, const Location& loc = Location()) : ExprMixin(loc), opcode(opcode), val(val) {} Opcode opcode; v128 val; }; template class VarExpr : public ExprMixin { public: VarExpr(const Var& var, const Location& loc = Location()) : ExprMixin(loc), var(var) {} Var var; }; template class MemoryVarExpr : public MemoryExpr { public: MemoryVarExpr(const Var& var, Var memidx, const Location& loc = Location()) : MemoryExpr(memidx, loc), var(var) {} Var var; }; using BrExpr = VarExpr; using BrIfExpr = VarExpr; using CallExpr = VarExpr; using RefFuncExpr = VarExpr; using GlobalGetExpr = VarExpr; using GlobalSetExpr = VarExpr; using LocalGetExpr = VarExpr; using LocalSetExpr = VarExpr; using LocalTeeExpr = VarExpr; using ReturnCallExpr = VarExpr; using ThrowExpr = VarExpr; using RethrowExpr = VarExpr; using DataDropExpr = VarExpr; using ElemDropExpr = VarExpr; using TableGetExpr = VarExpr; using TableSetExpr = VarExpr; using TableGrowExpr = VarExpr; using TableSizeExpr = VarExpr; using TableFillExpr = VarExpr; using MemoryInitExpr = MemoryVarExpr; class SelectExpr : public ExprMixin { public: SelectExpr(TypeVector type, const Location& loc = Location()) : ExprMixin(loc), result_type(type) {} TypeVector result_type; }; class TableInitExpr : public ExprMixin { public: TableInitExpr(const Var& segment_index, const Var& table_index, const Location& loc = Location()) : ExprMixin(loc), segment_index(segment_index), table_index(table_index) {} Var segment_index; Var table_index; }; class TableCopyExpr : public ExprMixin { public: TableCopyExpr(const Var& dst, const Var& src, const Location& loc = Location()) : ExprMixin(loc), dst_table(dst), src_table(src) {} Var dst_table; Var src_table; }; class CallIndirectExpr : public ExprMixin { public: explicit CallIndirectExpr(const Location& loc = Location()) : ExprMixin(loc) {} FuncDeclaration decl; Var table; }; class CodeMetadataExpr : public ExprMixin { public: explicit CodeMetadataExpr(std::string_view name, std::vector data, const Location& loc = Location()) : ExprMixin(loc), name(std::move(name)), data(std::move(data)) {} std::string_view name; std::vector data; }; class ReturnCallIndirectExpr : public ExprMixin { public: explicit ReturnCallIndirectExpr(const Location& loc = Location()) : ExprMixin(loc) {} FuncDeclaration decl; Var table; }; class CallRefExpr : public ExprMixin { public: explicit CallRefExpr(const Location& loc = Location()) : ExprMixin(loc) {} // This field is setup only during Validate phase, // so keep that in mind when you use it. Var function_type_index; }; template class BlockExprBase : public ExprMixin { public: explicit BlockExprBase(const Location& loc = Location()) : ExprMixin(loc) {} Block block; }; using BlockExpr = BlockExprBase; using LoopExpr = BlockExprBase; class IfExpr : public ExprMixin { public: explicit IfExpr(const Location& loc = Location()) : ExprMixin(loc) {} Block true_; ExprList false_; Location false_end_loc; }; class TryExpr : public ExprMixin { public: explicit TryExpr(const Location& loc = Location()) : ExprMixin(loc), kind(TryKind::Plain) {} TryKind kind; Block block; CatchVector catches; Var delegate_target; }; class BrTableExpr : public ExprMixin { public: BrTableExpr(const Location& loc = Location()) : ExprMixin(loc) {} VarVector targets; Var default_target; }; class ConstExpr : public ExprMixin { public: ConstExpr(const Const& c, const Location& loc = Location()) : ExprMixin(loc), const_(c) {} Const const_; }; // TODO(binji): Rename this, it is used for more than loads/stores now. template class LoadStoreExpr : public MemoryExpr { public: LoadStoreExpr(Opcode opcode, Var memidx, Address align, Address offset, const Location& loc = Location()) : MemoryExpr(memidx, loc), opcode(opcode), align(align), offset(offset) {} Opcode opcode; Address align; Address offset; }; using LoadExpr = LoadStoreExpr; using StoreExpr = LoadStoreExpr; using AtomicLoadExpr = LoadStoreExpr; using AtomicStoreExpr = LoadStoreExpr; using AtomicRmwExpr = LoadStoreExpr; using AtomicRmwCmpxchgExpr = LoadStoreExpr; using AtomicWaitExpr = LoadStoreExpr; using AtomicNotifyExpr = LoadStoreExpr; using LoadSplatExpr = LoadStoreExpr; using LoadZeroExpr = LoadStoreExpr; class AtomicFenceExpr : public ExprMixin { public: explicit AtomicFenceExpr(uint32_t consistency_model, const Location& loc = Location()) : ExprMixin(loc), consistency_model(consistency_model) {} uint32_t consistency_model; }; struct Tag { explicit Tag(std::string_view name) : name(name) {} std::string name; FuncDeclaration decl; }; class LocalTypes { public: using Decl = std::pair; using Decls = std::vector; struct const_iterator { const_iterator(Decls::const_iterator decl, Index index) : decl(decl), index(index) {} Type operator*() const { return decl->first; } const_iterator& operator++(); const_iterator operator++(int); Decls::const_iterator decl; Index index; }; void Set(const TypeVector&); const Decls& decls() const { return decls_; } void AppendDecl(Type type, Index count) { if (count != 0) { decls_.emplace_back(type, count); } } Index size() const; Type operator[](Index) const; const_iterator begin() const { return {decls_.begin(), 0}; } const_iterator end() const { return {decls_.end(), 0}; } private: Decls decls_; }; inline LocalTypes::const_iterator& LocalTypes::const_iterator::operator++() { ++index; if (index >= decl->second) { ++decl; index = 0; } return *this; } inline LocalTypes::const_iterator LocalTypes::const_iterator::operator++(int) { const_iterator result = *this; operator++(); return result; } inline bool operator==(const LocalTypes::const_iterator& lhs, const LocalTypes::const_iterator& rhs) { return lhs.decl == rhs.decl && lhs.index == rhs.index; } inline bool operator!=(const LocalTypes::const_iterator& lhs, const LocalTypes::const_iterator& rhs) { return !operator==(lhs, rhs); } struct Func { explicit Func(std::string_view name) : name(name) {} Type GetParamType(Index index) const { return decl.GetParamType(index); } Type GetResultType(Index index) const { return decl.GetResultType(index); } Type GetLocalType(Index index) const; Type GetLocalType(const Var& var) const; Index GetNumParams() const { return decl.GetNumParams(); } Index GetNumLocals() const { return local_types.size(); } Index GetNumParamsAndLocals() const { return GetNumParams() + GetNumLocals(); } Index GetNumResults() const { return decl.GetNumResults(); } Index GetLocalIndex(const Var&) const; std::string name; FuncDeclaration decl; LocalTypes local_types; BindingHash bindings; ExprList exprs; Location loc; }; struct Global { explicit Global(std::string_view name) : name(name) {} std::string name; Type type = Type::Void; bool mutable_ = false; ExprList init_expr; }; struct Table { explicit Table(std::string_view name) : name(name), elem_type(Type::FuncRef) {} std::string name; Limits elem_limits; Type elem_type; }; using ExprListVector = std::vector; struct ElemSegment { explicit ElemSegment(std::string_view name) : name(name) {} uint8_t GetFlags(const Module*) const; SegmentKind kind = SegmentKind::Active; std::string name; Var table_var; Type elem_type; ExprList offset; ExprListVector elem_exprs; }; struct Memory { explicit Memory(std::string_view name) : name(name) {} std::string name; Limits page_limits; }; struct DataSegment { explicit DataSegment(std::string_view name) : name(name) {} uint8_t GetFlags(const Module*) const; SegmentKind kind = SegmentKind::Active; std::string name; Var memory_var; ExprList offset; std::vector data; }; class Import { public: WABT_DISALLOW_COPY_AND_ASSIGN(Import); Import() = delete; virtual ~Import() = default; ExternalKind kind() const { return kind_; } std::string module_name; std::string field_name; protected: Import(ExternalKind kind) : kind_(kind) {} ExternalKind kind_; }; template class ImportMixin : public Import { public: static bool classof(const Import* import) { return import->kind() == TypeEnum; } ImportMixin() : Import(TypeEnum) {} }; class FuncImport : public ImportMixin { public: explicit FuncImport(std::string_view name = std::string_view()) : ImportMixin(), func(name) {} Func func; }; class TableImport : public ImportMixin { public: explicit TableImport(std::string_view name = std::string_view()) : ImportMixin(), table(name) {} Table table; }; class MemoryImport : public ImportMixin { public: explicit MemoryImport(std::string_view name = std::string_view()) : ImportMixin(), memory(name) {} Memory memory; }; class GlobalImport : public ImportMixin { public: explicit GlobalImport(std::string_view name = std::string_view()) : ImportMixin(), global(name) {} Global global; }; class TagImport : public ImportMixin { public: explicit TagImport(std::string_view name = std::string_view()) : ImportMixin(), tag(name) {} Tag tag; }; struct Export { std::string name; ExternalKind kind; Var var; }; enum class ModuleFieldType { Func, Global, Import, Export, Type, Table, ElemSegment, Memory, DataSegment, Start, Tag }; class ModuleField : public intrusive_list_base { public: WABT_DISALLOW_COPY_AND_ASSIGN(ModuleField); ModuleField() = delete; virtual ~ModuleField() = default; ModuleFieldType type() const { return type_; } Location loc; protected: ModuleField(ModuleFieldType type, const Location& loc) : loc(loc), type_(type) {} ModuleFieldType type_; }; using ModuleFieldList = intrusive_list; template class ModuleFieldMixin : public ModuleField { public: static bool classof(const ModuleField* field) { return field->type() == TypeEnum; } explicit ModuleFieldMixin(const Location& loc) : ModuleField(TypeEnum, loc) {} }; class FuncModuleField : public ModuleFieldMixin { public: explicit FuncModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), func(name) {} Func func; }; class GlobalModuleField : public ModuleFieldMixin { public: explicit GlobalModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), global(name) {} Global global; }; class ImportModuleField : public ModuleFieldMixin { public: explicit ImportModuleField(const Location& loc = Location()) : ModuleFieldMixin(loc) {} explicit ImportModuleField(std::unique_ptr import, const Location& loc = Location()) : ModuleFieldMixin(loc), import(std::move(import)) {} std::unique_ptr import; }; class ExportModuleField : public ModuleFieldMixin { public: explicit ExportModuleField(const Location& loc = Location()) : ModuleFieldMixin(loc) {} Export export_; }; class TypeModuleField : public ModuleFieldMixin { public: explicit TypeModuleField(const Location& loc = Location()) : ModuleFieldMixin(loc) {} std::unique_ptr type; }; class TableModuleField : public ModuleFieldMixin { public: explicit TableModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), table(name) {} Table table; }; class ElemSegmentModuleField : public ModuleFieldMixin { public: explicit ElemSegmentModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), elem_segment(name) {} ElemSegment elem_segment; }; class MemoryModuleField : public ModuleFieldMixin { public: explicit MemoryModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), memory(name) {} Memory memory; }; class DataSegmentModuleField : public ModuleFieldMixin { public: explicit DataSegmentModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), data_segment(name) {} DataSegment data_segment; }; class TagModuleField : public ModuleFieldMixin { public: explicit TagModuleField(const Location& loc = Location(), std::string_view name = std::string_view()) : ModuleFieldMixin(loc), tag(name) {} Tag tag; }; class StartModuleField : public ModuleFieldMixin { public: explicit StartModuleField(Var start = Var(), const Location& loc = Location()) : ModuleFieldMixin(loc), start(start) {} Var start; }; struct Module { Index GetFuncTypeIndex(const Var&) const; Index GetFuncTypeIndex(const FuncDeclaration&) const; Index GetFuncTypeIndex(const FuncSignature&) const; const FuncType* GetFuncType(const Var&) const; FuncType* GetFuncType(const Var&); Index GetFuncIndex(const Var&) const; const Func* GetFunc(const Var&) const; Func* GetFunc(const Var&); Index GetTableIndex(const Var&) const; const Table* GetTable(const Var&) const; Table* GetTable(const Var&); Index GetMemoryIndex(const Var&) const; const Memory* GetMemory(const Var&) const; Memory* GetMemory(const Var&); Index GetGlobalIndex(const Var&) const; const Global* GetGlobal(const Var&) const; Global* GetGlobal(const Var&); const Export* GetExport(std::string_view) const; Tag* GetTag(const Var&) const; Index GetTagIndex(const Var&) const; const DataSegment* GetDataSegment(const Var&) const; DataSegment* GetDataSegment(const Var&); Index GetDataSegmentIndex(const Var&) const; const ElemSegment* GetElemSegment(const Var&) const; ElemSegment* GetElemSegment(const Var&); Index GetElemSegmentIndex(const Var&) const; bool IsImport(ExternalKind kind, const Var&) const; bool IsImport(const Export& export_) const { return IsImport(export_.kind, export_.var); } // TODO(binji): move this into a builder class? void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendField(std::unique_ptr); void AppendFields(ModuleFieldList*); Location loc; std::string name; ModuleFieldList fields; Index num_tag_imports = 0; Index num_func_imports = 0; Index num_table_imports = 0; Index num_memory_imports = 0; Index num_global_imports = 0; // Cached for convenience; the pointers are shared with values that are // stored in either ModuleField or Import. std::vector tags; std::vector funcs; std::vector globals; std::vector imports; std::vector exports; std::vector types; std::vector tables; std::vector elem_segments; std::vector memories; std::vector data_segments; std::vector starts; BindingHash tag_bindings; BindingHash func_bindings; BindingHash global_bindings; BindingHash export_bindings; BindingHash type_bindings; BindingHash table_bindings; BindingHash memory_bindings; BindingHash data_segment_bindings; BindingHash elem_segment_bindings; // For a subset of features, the BinaryReaderIR tracks whether they are // actually used by the module. wasm2c (CWriter) uses this information to // limit its output in some cases. struct { bool simd = false; bool exceptions = false; } features_used; }; enum class ScriptModuleType { Text, Binary, Quoted, }; // A ScriptModule is a module that may not yet be decoded. This allows for text // and binary parsing errors to be deferred until validation time. class ScriptModule { public: WABT_DISALLOW_COPY_AND_ASSIGN(ScriptModule); ScriptModule() = delete; virtual ~ScriptModule() = default; ScriptModuleType type() const { return type_; } virtual const Location& location() const = 0; protected: explicit ScriptModule(ScriptModuleType type) : type_(type) {} ScriptModuleType type_; }; template class ScriptModuleMixin : public ScriptModule { public: static bool classof(const ScriptModule* script_module) { return script_module->type() == TypeEnum; } ScriptModuleMixin() : ScriptModule(TypeEnum) {} }; class TextScriptModule : public ScriptModuleMixin { public: const Location& location() const override { return module.loc; } Module module; }; template class DataScriptModule : public ScriptModuleMixin { public: const Location& location() const override { return loc; } Location loc; std::string name; std::vector data; }; using BinaryScriptModule = DataScriptModule; using QuotedScriptModule = DataScriptModule; enum class ActionType { Invoke, Get, }; class Action { public: WABT_DISALLOW_COPY_AND_ASSIGN(Action); Action() = delete; virtual ~Action() = default; ActionType type() const { return type_; } Location loc; Var module_var; std::string name; protected: explicit Action(ActionType type, const Location& loc = Location()) : loc(loc), type_(type) {} ActionType type_; }; using ActionPtr = std::unique_ptr; template class ActionMixin : public Action { public: static bool classof(const Action* action) { return action->type() == TypeEnum; } explicit ActionMixin(const Location& loc = Location()) : Action(TypeEnum, loc) {} }; class GetAction : public ActionMixin { public: explicit GetAction(const Location& loc = Location()) : ActionMixin(loc) {} }; class InvokeAction : public ActionMixin { public: explicit InvokeAction(const Location& loc = Location()) : ActionMixin(loc) {} ConstVector args; }; enum class CommandType { Module, ScriptModule, Action, Register, AssertMalformed, AssertInvalid, AssertUnlinkable, AssertUninstantiable, AssertReturn, AssertTrap, AssertExhaustion, AssertException, First = Module, Last = AssertException, }; constexpr int kCommandTypeCount = WABT_ENUM_COUNT(CommandType); class Command { public: WABT_DISALLOW_COPY_AND_ASSIGN(Command); Command() = delete; virtual ~Command() = default; CommandType type; protected: explicit Command(CommandType type) : type(type) {} }; template class CommandMixin : public Command { public: static bool classof(const Command* cmd) { return cmd->type == TypeEnum; } CommandMixin() : Command(TypeEnum) {} }; class ModuleCommand : public CommandMixin { public: Module module; }; class ScriptModuleCommand : public CommandMixin { public: // Both the module and the script_module need to be stored since the module // has the parsed information about the module, but the script_module has the // original contents (binary or quoted). Module module; std::unique_ptr script_module; }; template class ActionCommandBase : public CommandMixin { public: ActionPtr action; }; using ActionCommand = ActionCommandBase; class RegisterCommand : public CommandMixin { public: RegisterCommand(std::string_view module_name, const Var& var) : module_name(module_name), var(var) {} std::string module_name; Var var; }; class AssertReturnCommand : public CommandMixin { public: ActionPtr action; ExpectationPtr expected; }; template class AssertTrapCommandBase : public CommandMixin { public: ActionPtr action; std::string text; }; using AssertTrapCommand = AssertTrapCommandBase; using AssertExhaustionCommand = AssertTrapCommandBase; template class AssertModuleCommand : public CommandMixin { public: std::unique_ptr module; std::string text; }; using AssertMalformedCommand = AssertModuleCommand; using AssertInvalidCommand = AssertModuleCommand; using AssertUnlinkableCommand = AssertModuleCommand; using AssertUninstantiableCommand = AssertModuleCommand; class AssertExceptionCommand : public CommandMixin { public: ActionPtr action; }; using CommandPtr = std::unique_ptr; using CommandPtrVector = std::vector; struct Script { WABT_DISALLOW_COPY_AND_ASSIGN(Script); Script() = default; const Module* GetFirstModule() const; Module* GetFirstModule(); const Module* GetModule(const Var&) const; CommandPtrVector commands; BindingHash module_bindings; }; void MakeTypeBindingReverseMapping( size_t num_types, const BindingHash& bindings, std::vector* out_reverse_mapping); } // namespace wabt #endif /* WABT_IR_H_ */