diff options
Diffstat (limited to 'third_party/wasm2c/include/wabt/ir.h')
-rw-r--r-- | third_party/wasm2c/include/wabt/ir.h | 1483 |
1 files changed, 1483 insertions, 0 deletions
diff --git a/third_party/wasm2c/include/wabt/ir.h b/third_party/wasm2c/include/wabt/ir.h new file mode 100644 index 0000000000..68846bc0cc --- /dev/null +++ b/third_party/wasm2c/include/wabt/ir.h @@ -0,0 +1,1483 @@ +/* + * 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 <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> +#include <string_view> +#include <type_traits> +#include <vector> + +#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<Var>; + +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<uintptr_t>(0); } + v128 vec128() const { return data_; } + + template <typename T> + T v128_lane(int lane) const { + return data_.To<T>(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<uintptr_t>(Type::FuncRef, 0); } + void set_externref(uintptr_t x) { From(Type::ExternRef, x); } + void set_null(Type type) { From<uintptr_t>(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 <typename T> + 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 <typename T> + Const(Type type, T data, const Location& loc = Location()) : loc(loc) { + From<T>(type, data); + } + + template <typename T> + 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<T>(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<Const>; + +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 <ExpectationType TypeEnum> +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<ExpectationType::Values> { + public: + explicit ValueExpectation(const Location& loc = Location()) + : ExpectationMixin<ExpectationType::Values>(loc) {} +}; + +struct EitherExpectation : public ExpectationMixin<ExpectationType::Either> { + public: + explicit EitherExpectation(const Location& loc = Location()) + : ExpectationMixin<ExpectationType::Either>(loc) {} +}; + +typedef std::unique_ptr<Expectation> 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<uint32_t, std::string> param_type_names; + std::unordered_map<uint32_t, std::string> 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<Field> 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<Expr>; + +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<Catch>; + +enum class TryKind { Plain, Catch, Delegate }; + +class Expr : public intrusive_list_base<Expr> { + 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 <ExprType TypeEnum> +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 <ExprType TypeEnum> +class MemoryExpr : public ExprMixin<TypeEnum> { + public: + MemoryExpr(Var memidx, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), memidx(memidx) {} + + Var memidx; +}; + +template <ExprType TypeEnum> +class MemoryBinaryExpr : public ExprMixin<TypeEnum> { + public: + MemoryBinaryExpr(Var srcmemidx, + Var destmemidx, + const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), + srcmemidx(srcmemidx), + destmemidx(destmemidx) {} + + Var srcmemidx; + Var destmemidx; +}; + +using DropExpr = ExprMixin<ExprType::Drop>; +using NopExpr = ExprMixin<ExprType::Nop>; +using ReturnExpr = ExprMixin<ExprType::Return>; +using UnreachableExpr = ExprMixin<ExprType::Unreachable>; + +using MemoryGrowExpr = MemoryExpr<ExprType::MemoryGrow>; +using MemorySizeExpr = MemoryExpr<ExprType::MemorySize>; +using MemoryFillExpr = MemoryExpr<ExprType::MemoryFill>; + +using MemoryCopyExpr = MemoryBinaryExpr<ExprType::MemoryCopy>; + +template <ExprType TypeEnum> +class RefTypeExpr : public ExprMixin<TypeEnum> { + public: + RefTypeExpr(Type type, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), type(type) {} + + Type type; +}; + +using RefNullExpr = RefTypeExpr<ExprType::RefNull>; +using RefIsNullExpr = ExprMixin<ExprType::RefIsNull>; + +template <ExprType TypeEnum> +class OpcodeExpr : public ExprMixin<TypeEnum> { + public: + OpcodeExpr(Opcode opcode, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), opcode(opcode) {} + + Opcode opcode; +}; + +using BinaryExpr = OpcodeExpr<ExprType::Binary>; +using CompareExpr = OpcodeExpr<ExprType::Compare>; +using ConvertExpr = OpcodeExpr<ExprType::Convert>; +using UnaryExpr = OpcodeExpr<ExprType::Unary>; +using TernaryExpr = OpcodeExpr<ExprType::Ternary>; + +class SimdLaneOpExpr : public ExprMixin<ExprType::SimdLaneOp> { + public: + SimdLaneOpExpr(Opcode opcode, uint64_t val, const Location& loc = Location()) + : ExprMixin<ExprType::SimdLaneOp>(loc), opcode(opcode), val(val) {} + + Opcode opcode; + uint64_t val; +}; + +class SimdLoadLaneExpr : public MemoryExpr<ExprType::SimdLoadLane> { + public: + SimdLoadLaneExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : MemoryExpr<ExprType::SimdLoadLane>(memidx, loc), + opcode(opcode), + align(align), + offset(offset), + val(val) {} + + Opcode opcode; + Address align; + Address offset; + uint64_t val; +}; + +class SimdStoreLaneExpr : public MemoryExpr<ExprType::SimdStoreLane> { + public: + SimdStoreLaneExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : MemoryExpr<ExprType::SimdStoreLane>(memidx, loc), + opcode(opcode), + align(align), + offset(offset), + val(val) {} + + Opcode opcode; + Address align; + Address offset; + uint64_t val; +}; + +class SimdShuffleOpExpr : public ExprMixin<ExprType::SimdShuffleOp> { + public: + SimdShuffleOpExpr(Opcode opcode, v128 val, const Location& loc = Location()) + : ExprMixin<ExprType::SimdShuffleOp>(loc), opcode(opcode), val(val) {} + + Opcode opcode; + v128 val; +}; + +template <ExprType TypeEnum> +class VarExpr : public ExprMixin<TypeEnum> { + public: + VarExpr(const Var& var, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), var(var) {} + + Var var; +}; + +template <ExprType TypeEnum> +class MemoryVarExpr : public MemoryExpr<TypeEnum> { + public: + MemoryVarExpr(const Var& var, Var memidx, const Location& loc = Location()) + : MemoryExpr<TypeEnum>(memidx, loc), var(var) {} + + Var var; +}; + +using BrExpr = VarExpr<ExprType::Br>; +using BrIfExpr = VarExpr<ExprType::BrIf>; +using CallExpr = VarExpr<ExprType::Call>; +using RefFuncExpr = VarExpr<ExprType::RefFunc>; +using GlobalGetExpr = VarExpr<ExprType::GlobalGet>; +using GlobalSetExpr = VarExpr<ExprType::GlobalSet>; +using LocalGetExpr = VarExpr<ExprType::LocalGet>; +using LocalSetExpr = VarExpr<ExprType::LocalSet>; +using LocalTeeExpr = VarExpr<ExprType::LocalTee>; +using ReturnCallExpr = VarExpr<ExprType::ReturnCall>; +using ThrowExpr = VarExpr<ExprType::Throw>; +using RethrowExpr = VarExpr<ExprType::Rethrow>; + +using DataDropExpr = VarExpr<ExprType::DataDrop>; +using ElemDropExpr = VarExpr<ExprType::ElemDrop>; +using TableGetExpr = VarExpr<ExprType::TableGet>; +using TableSetExpr = VarExpr<ExprType::TableSet>; +using TableGrowExpr = VarExpr<ExprType::TableGrow>; +using TableSizeExpr = VarExpr<ExprType::TableSize>; +using TableFillExpr = VarExpr<ExprType::TableFill>; + +using MemoryInitExpr = MemoryVarExpr<ExprType::MemoryInit>; + +class SelectExpr : public ExprMixin<ExprType::Select> { + public: + SelectExpr(TypeVector type, const Location& loc = Location()) + : ExprMixin<ExprType::Select>(loc), result_type(type) {} + TypeVector result_type; +}; + +class TableInitExpr : public ExprMixin<ExprType::TableInit> { + public: + TableInitExpr(const Var& segment_index, + const Var& table_index, + const Location& loc = Location()) + : ExprMixin<ExprType::TableInit>(loc), + segment_index(segment_index), + table_index(table_index) {} + + Var segment_index; + Var table_index; +}; + +class TableCopyExpr : public ExprMixin<ExprType::TableCopy> { + public: + TableCopyExpr(const Var& dst, + const Var& src, + const Location& loc = Location()) + : ExprMixin<ExprType::TableCopy>(loc), dst_table(dst), src_table(src) {} + + Var dst_table; + Var src_table; +}; + +class CallIndirectExpr : public ExprMixin<ExprType::CallIndirect> { + public: + explicit CallIndirectExpr(const Location& loc = Location()) + : ExprMixin<ExprType::CallIndirect>(loc) {} + + FuncDeclaration decl; + Var table; +}; + +class CodeMetadataExpr : public ExprMixin<ExprType::CodeMetadata> { + public: + explicit CodeMetadataExpr(std::string_view name, + std::vector<uint8_t> data, + const Location& loc = Location()) + : ExprMixin<ExprType::CodeMetadata>(loc), + name(std::move(name)), + data(std::move(data)) {} + + std::string_view name; + std::vector<uint8_t> data; +}; + +class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> { + public: + explicit ReturnCallIndirectExpr(const Location& loc = Location()) + : ExprMixin<ExprType::ReturnCallIndirect>(loc) {} + + FuncDeclaration decl; + Var table; +}; + +class CallRefExpr : public ExprMixin<ExprType::CallRef> { + public: + explicit CallRefExpr(const Location& loc = Location()) + : ExprMixin<ExprType::CallRef>(loc) {} + + // This field is setup only during Validate phase, + // so keep that in mind when you use it. + Var function_type_index; +}; + +template <ExprType TypeEnum> +class BlockExprBase : public ExprMixin<TypeEnum> { + public: + explicit BlockExprBase(const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc) {} + + Block block; +}; + +using BlockExpr = BlockExprBase<ExprType::Block>; +using LoopExpr = BlockExprBase<ExprType::Loop>; + +class IfExpr : public ExprMixin<ExprType::If> { + public: + explicit IfExpr(const Location& loc = Location()) + : ExprMixin<ExprType::If>(loc) {} + + Block true_; + ExprList false_; + Location false_end_loc; +}; + +class TryExpr : public ExprMixin<ExprType::Try> { + public: + explicit TryExpr(const Location& loc = Location()) + : ExprMixin<ExprType::Try>(loc), kind(TryKind::Plain) {} + + TryKind kind; + Block block; + CatchVector catches; + Var delegate_target; +}; + +class BrTableExpr : public ExprMixin<ExprType::BrTable> { + public: + BrTableExpr(const Location& loc = Location()) + : ExprMixin<ExprType::BrTable>(loc) {} + + VarVector targets; + Var default_target; +}; + +class ConstExpr : public ExprMixin<ExprType::Const> { + public: + ConstExpr(const Const& c, const Location& loc = Location()) + : ExprMixin<ExprType::Const>(loc), const_(c) {} + + Const const_; +}; + +// TODO(binji): Rename this, it is used for more than loads/stores now. +template <ExprType TypeEnum> +class LoadStoreExpr : public MemoryExpr<TypeEnum> { + public: + LoadStoreExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + const Location& loc = Location()) + : MemoryExpr<TypeEnum>(memidx, loc), + opcode(opcode), + align(align), + offset(offset) {} + + Opcode opcode; + Address align; + Address offset; +}; + +using LoadExpr = LoadStoreExpr<ExprType::Load>; +using StoreExpr = LoadStoreExpr<ExprType::Store>; + +using AtomicLoadExpr = LoadStoreExpr<ExprType::AtomicLoad>; +using AtomicStoreExpr = LoadStoreExpr<ExprType::AtomicStore>; +using AtomicRmwExpr = LoadStoreExpr<ExprType::AtomicRmw>; +using AtomicRmwCmpxchgExpr = LoadStoreExpr<ExprType::AtomicRmwCmpxchg>; +using AtomicWaitExpr = LoadStoreExpr<ExprType::AtomicWait>; +using AtomicNotifyExpr = LoadStoreExpr<ExprType::AtomicNotify>; +using LoadSplatExpr = LoadStoreExpr<ExprType::LoadSplat>; +using LoadZeroExpr = LoadStoreExpr<ExprType::LoadZero>; + +class AtomicFenceExpr : public ExprMixin<ExprType::AtomicFence> { + public: + explicit AtomicFenceExpr(uint32_t consistency_model, + const Location& loc = Location()) + : ExprMixin<ExprType::AtomicFence>(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<Type, Index>; + using Decls = std::vector<Decl>; + + 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<ExprList>; + +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<uint8_t> 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 <ExternalKind TypeEnum> +class ImportMixin : public Import { + public: + static bool classof(const Import* import) { + return import->kind() == TypeEnum; + } + + ImportMixin() : Import(TypeEnum) {} +}; + +class FuncImport : public ImportMixin<ExternalKind::Func> { + public: + explicit FuncImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Func>(), func(name) {} + + Func func; +}; + +class TableImport : public ImportMixin<ExternalKind::Table> { + public: + explicit TableImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Table>(), table(name) {} + + Table table; +}; + +class MemoryImport : public ImportMixin<ExternalKind::Memory> { + public: + explicit MemoryImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Memory>(), memory(name) {} + + Memory memory; +}; + +class GlobalImport : public ImportMixin<ExternalKind::Global> { + public: + explicit GlobalImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Global>(), global(name) {} + + Global global; +}; + +class TagImport : public ImportMixin<ExternalKind::Tag> { + public: + explicit TagImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Tag>(), 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<ModuleField> { + 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<ModuleField>; + +template <ModuleFieldType TypeEnum> +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<ModuleFieldType::Func> { + public: + explicit FuncModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Func>(loc), func(name) {} + + Func func; +}; + +class GlobalModuleField : public ModuleFieldMixin<ModuleFieldType::Global> { + public: + explicit GlobalModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Global>(loc), global(name) {} + + Global global; +}; + +class ImportModuleField : public ModuleFieldMixin<ModuleFieldType::Import> { + public: + explicit ImportModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Import>(loc) {} + explicit ImportModuleField(std::unique_ptr<Import> import, + const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Import>(loc), + import(std::move(import)) {} + + std::unique_ptr<Import> import; +}; + +class ExportModuleField : public ModuleFieldMixin<ModuleFieldType::Export> { + public: + explicit ExportModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Export>(loc) {} + + Export export_; +}; + +class TypeModuleField : public ModuleFieldMixin<ModuleFieldType::Type> { + public: + explicit TypeModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Type>(loc) {} + + std::unique_ptr<TypeEntry> type; +}; + +class TableModuleField : public ModuleFieldMixin<ModuleFieldType::Table> { + public: + explicit TableModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Table>(loc), table(name) {} + + Table table; +}; + +class ElemSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::ElemSegment> { + public: + explicit ElemSegmentModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::ElemSegment>(loc), + elem_segment(name) {} + + ElemSegment elem_segment; +}; + +class MemoryModuleField : public ModuleFieldMixin<ModuleFieldType::Memory> { + public: + explicit MemoryModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Memory>(loc), memory(name) {} + + Memory memory; +}; + +class DataSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::DataSegment> { + public: + explicit DataSegmentModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::DataSegment>(loc), + data_segment(name) {} + + DataSegment data_segment; +}; + +class TagModuleField : public ModuleFieldMixin<ModuleFieldType::Tag> { + public: + explicit TagModuleField(const Location& loc = Location(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Tag>(loc), tag(name) {} + + Tag tag; +}; + +class StartModuleField : public ModuleFieldMixin<ModuleFieldType::Start> { + public: + explicit StartModuleField(Var start = Var(), const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Start>(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<DataSegmentModuleField>); + void AppendField(std::unique_ptr<ElemSegmentModuleField>); + void AppendField(std::unique_ptr<TagModuleField>); + void AppendField(std::unique_ptr<ExportModuleField>); + void AppendField(std::unique_ptr<FuncModuleField>); + void AppendField(std::unique_ptr<TypeModuleField>); + void AppendField(std::unique_ptr<GlobalModuleField>); + void AppendField(std::unique_ptr<ImportModuleField>); + void AppendField(std::unique_ptr<MemoryModuleField>); + void AppendField(std::unique_ptr<StartModuleField>); + void AppendField(std::unique_ptr<TableModuleField>); + void AppendField(std::unique_ptr<ModuleField>); + 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<Tag*> tags; + std::vector<Func*> funcs; + std::vector<Global*> globals; + std::vector<Import*> imports; + std::vector<Export*> exports; + std::vector<TypeEntry*> types; + std::vector<Table*> tables; + std::vector<ElemSegment*> elem_segments; + std::vector<Memory*> memories; + std::vector<DataSegment*> data_segments; + std::vector<Var*> 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; +}; + +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 <ScriptModuleType TypeEnum> +class ScriptModuleMixin : public ScriptModule { + public: + static bool classof(const ScriptModule* script_module) { + return script_module->type() == TypeEnum; + } + + ScriptModuleMixin() : ScriptModule(TypeEnum) {} +}; + +class TextScriptModule : public ScriptModuleMixin<ScriptModuleType::Text> { + public: + const Location& location() const override { return module.loc; } + + Module module; +}; + +template <ScriptModuleType TypeEnum> +class DataScriptModule : public ScriptModuleMixin<TypeEnum> { + public: + const Location& location() const override { return loc; } + + Location loc; + std::string name; + std::vector<uint8_t> data; +}; + +using BinaryScriptModule = DataScriptModule<ScriptModuleType::Binary>; +using QuotedScriptModule = DataScriptModule<ScriptModuleType::Quoted>; + +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<Action>; + +template <ActionType TypeEnum> +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<ActionType::Get> { + public: + explicit GetAction(const Location& loc = Location()) + : ActionMixin<ActionType::Get>(loc) {} +}; + +class InvokeAction : public ActionMixin<ActionType::Invoke> { + public: + explicit InvokeAction(const Location& loc = Location()) + : ActionMixin<ActionType::Invoke>(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 <CommandType TypeEnum> +class CommandMixin : public Command { + public: + static bool classof(const Command* cmd) { return cmd->type == TypeEnum; } + CommandMixin() : Command(TypeEnum) {} +}; + +class ModuleCommand : public CommandMixin<CommandType::Module> { + public: + Module module; +}; + +class ScriptModuleCommand : public CommandMixin<CommandType::ScriptModule> { + 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<ScriptModule> script_module; +}; + +template <CommandType TypeEnum> +class ActionCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; +}; + +using ActionCommand = ActionCommandBase<CommandType::Action>; + +class RegisterCommand : public CommandMixin<CommandType::Register> { + 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<CommandType::AssertReturn> { + public: + ActionPtr action; + ExpectationPtr expected; +}; + +template <CommandType TypeEnum> +class AssertTrapCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; + std::string text; +}; + +using AssertTrapCommand = AssertTrapCommandBase<CommandType::AssertTrap>; +using AssertExhaustionCommand = + AssertTrapCommandBase<CommandType::AssertExhaustion>; + +template <CommandType TypeEnum> +class AssertModuleCommand : public CommandMixin<TypeEnum> { + public: + std::unique_ptr<ScriptModule> module; + std::string text; +}; + +using AssertMalformedCommand = + AssertModuleCommand<CommandType::AssertMalformed>; +using AssertInvalidCommand = AssertModuleCommand<CommandType::AssertInvalid>; +using AssertUnlinkableCommand = + AssertModuleCommand<CommandType::AssertUnlinkable>; +using AssertUninstantiableCommand = + AssertModuleCommand<CommandType::AssertUninstantiable>; + +class AssertExceptionCommand + : public CommandMixin<CommandType::AssertException> { + public: + ActionPtr action; +}; + +using CommandPtr = std::unique_ptr<Command>; +using CommandPtrVector = std::vector<CommandPtr>; + +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<std::string>* out_reverse_mapping); + +} // namespace wabt + +#endif /* WABT_IR_H_ */ |