/* * 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 "src/binding-hash.h" #include "src/common.h" #include "src/intrusive-list.h" #include "src/opcode.h" #include "src/string-view.h" namespace wabt { enum class VarType { Index, Name, }; struct Var { explicit Var(Index index = kInvalidIndex, const Location& loc = Location()); explicit Var(string_view name, const Location& loc = Location()); 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(string_view); Location loc; private: void Destroy(); VarType type_; union { Index index_; std::string name_; }; }; typedef std::vector VarVector; struct Const { Const() : Const(I32Tag(), 0, Location()) {} static Const I32(uint32_t val = 0, const Location& loc = Location()) { return Const(I32Tag(), val, loc); } static Const I64(uint64_t val = 0, const Location& loc = Location()) { return Const(I64Tag(), val, loc); } static Const F32(uint32_t val = 0, const Location& loc = Location()) { return Const(F32Tag(), val, loc); } static Const F64(uint64_t val = 0, const Location& loc = Location()) { return Const(F64Tag(), val, loc); } Location loc; Type type; union { uint32_t u32; uint64_t u64; uint32_t f32_bits; uint64_t f64_bits; }; private: // Struct tags to differentiate constructors. struct I32Tag {}; struct I64Tag {}; struct F32Tag {}; struct F64Tag {}; Const(I32Tag, uint32_t val = 0, const Location& loc = Location()); Const(I64Tag, uint64_t val = 0, const Location& loc = Location()); Const(F32Tag, uint32_t val = 0, const Location& loc = Location()); Const(F64Tag, uint64_t val = 0, const Location& loc = Location()); }; typedef std::vector ConstVector; enum class ExprType { AtomicLoad, AtomicStore, AtomicRmw, AtomicRmwCmpxchg, Binary, Block, Br, BrIf, BrTable, Call, CallIndirect, Compare, Const, Convert, CurrentMemory, Drop, GetGlobal, GetLocal, GrowMemory, If, Load, Loop, Nop, Rethrow, Return, Select, SetGlobal, SetLocal, Store, TeeLocal, Throw, TryBlock, Unary, Unreachable, Wait, Wake, First = Binary, Last = Wake }; const char* GetExprTypeName(ExprType type); typedef TypeVector BlockSignature; class Expr; typedef intrusive_list ExprList; struct Block { Block() = default; explicit Block(ExprList exprs) : exprs(std::move(exprs)) {} std::string label; BlockSignature sig; ExprList exprs; }; 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; } }; typedef std::vector CatchVector; 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) {} }; typedef ExprMixin CurrentMemoryExpr; typedef ExprMixin DropExpr; typedef ExprMixin GrowMemoryExpr; typedef ExprMixin NopExpr; typedef ExprMixin ReturnExpr; typedef ExprMixin SelectExpr; typedef ExprMixin UnreachableExpr; template class OpcodeExpr : public ExprMixin { public: OpcodeExpr(Opcode opcode, const Location& loc = Location()) : ExprMixin(loc), opcode(opcode) {} Opcode opcode; }; typedef OpcodeExpr BinaryExpr; typedef OpcodeExpr CompareExpr; typedef OpcodeExpr ConvertExpr; typedef OpcodeExpr UnaryExpr; template class VarExpr : public ExprMixin { public: VarExpr(const Var& var, const Location& loc = Location()) : ExprMixin(loc), var(var) {} Var var; }; typedef VarExpr BrExpr; typedef VarExpr BrIfExpr; typedef VarExpr CallExpr; typedef VarExpr CallIndirectExpr; typedef VarExpr GetGlobalExpr; typedef VarExpr GetLocalExpr; typedef VarExpr RethrowExpr; typedef VarExpr SetGlobalExpr; typedef VarExpr SetLocalExpr; typedef VarExpr TeeLocalExpr; typedef VarExpr ThrowExpr; template class BlockExprBase : public ExprMixin { public: explicit BlockExprBase(const Location& loc = Location()) : ExprMixin(loc) {} Block block; }; typedef BlockExprBase BlockExpr; typedef BlockExprBase LoopExpr; class IfExpr : public ExprMixin { public: explicit IfExpr(const Location& loc = Location()) : ExprMixin(loc) {} Block true_; ExprList false_; }; class TryExpr : public ExprMixin { public: explicit TryExpr(const Location& loc = Location()) : ExprMixin(loc) {} Block block; CatchVector catches; }; 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 ExprMixin { public: LoadStoreExpr(Opcode opcode, Address align, uint32_t offset, const Location& loc = Location()) : ExprMixin(loc), opcode(opcode), align(align), offset(offset) {} Opcode opcode; Address align; uint32_t offset; }; typedef LoadStoreExpr LoadExpr; typedef LoadStoreExpr StoreExpr; typedef LoadStoreExpr AtomicLoadExpr; typedef LoadStoreExpr AtomicStoreExpr; typedef LoadStoreExpr AtomicRmwExpr; typedef LoadStoreExpr AtomicRmwCmpxchgExpr; typedef LoadStoreExpr WaitExpr; typedef LoadStoreExpr WakeExpr; struct Exception { Exception() = default; explicit Exception(string_view name) : name(name.to_string()) {} std::string name; TypeVector sig; }; struct FuncSignature { TypeVector param_types; TypeVector result_types; 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; }; struct FuncType { FuncType() = default; explicit FuncType(string_view name) : name(name.to_string()) {} 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); } std::string name; FuncSignature sig; }; 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; }; struct Func { Func() = default; explicit Func(string_view name) : name(name.to_string()) {} Type GetParamType(Index index) const { return decl.GetParamType(index); } Type GetResultType(Index index) const { return decl.GetResultType(index); } 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; TypeVector local_types; BindingHash param_bindings; BindingHash local_bindings; ExprList exprs; }; struct Global { Global() = default; explicit Global(string_view name) : name(name.to_string()) {} std::string name; Type type = Type::Void; bool mutable_ = false; ExprList init_expr; }; struct Table { Table() = default; explicit Table(string_view name) : name(name.to_string()) {} std::string name; Limits elem_limits; }; struct ElemSegment { Var table_var; ExprList offset; VarVector vars; }; struct Memory { Memory() = default; explicit Memory(string_view name) : name(name.to_string()) {} std::string name; Limits page_limits; }; struct DataSegment { 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(string_view name = string_view()) : ImportMixin(), func(name) {} Func func; }; class TableImport : public ImportMixin { public: explicit TableImport(string_view name = string_view()) : ImportMixin(), table(name) {} Table table; }; class MemoryImport : public ImportMixin { public: explicit MemoryImport(string_view name = string_view()) : ImportMixin(), memory(name) {} Memory memory; }; class GlobalImport : public ImportMixin { public: explicit GlobalImport(string_view name = string_view()) : ImportMixin(), global(name) {} Global global; }; class ExceptionImport : public ImportMixin { public: explicit ExceptionImport(string_view name = string_view()) : ImportMixin(), except(name) {} Exception except; }; struct Export { std::string name; ExternalKind kind; Var var; }; enum class ModuleFieldType { Func, Global, Import, Export, FuncType, Table, ElemSegment, Memory, DataSegment, Start, Except }; 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_; }; typedef intrusive_list ModuleFieldList; 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(), string_view name = string_view()) : ModuleFieldMixin(loc), func(name) {} Func func; }; class GlobalModuleField : public ModuleFieldMixin { public: explicit GlobalModuleField(const Location& loc = Location(), string_view name = 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 FuncTypeModuleField : public ModuleFieldMixin { public: explicit FuncTypeModuleField(const Location& loc = Location(), string_view name = string_view()) : ModuleFieldMixin(loc), func_type(name) {} FuncType func_type; }; class TableModuleField : public ModuleFieldMixin { public: explicit TableModuleField(const Location& loc = Location(), string_view name = string_view()) : ModuleFieldMixin(loc), table(name) {} Table table; }; class ElemSegmentModuleField : public ModuleFieldMixin { public: explicit ElemSegmentModuleField(const Location& loc = Location()) : ModuleFieldMixin(loc) {} ElemSegment elem_segment; }; class MemoryModuleField : public ModuleFieldMixin { public: explicit MemoryModuleField(const Location& loc = Location(), string_view name = string_view()) : ModuleFieldMixin(loc), memory(name) {} Memory memory; }; class DataSegmentModuleField : public ModuleFieldMixin { public: explicit DataSegmentModuleField( const Location& loc = Location()) : ModuleFieldMixin(loc) {} DataSegment data_segment; }; class ExceptionModuleField : public ModuleFieldMixin { public: explicit ExceptionModuleField(const Location& loc = Location(), string_view name = string_view()) : ModuleFieldMixin(loc), except(name) {} Exception except; }; 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; Table* GetTable(const Var&); Index GetMemoryIndex(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(string_view) const; Exception* GetExcept(const Var&) const; Index GetExceptIndex(const Var&) const; // 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_except_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 excepts; std::vector funcs; std::vector globals; std::vector imports; std::vector exports; std::vector func_types; std::vector tables; std::vector elem_segments; std::vector memories; std::vector data_segments; std::vector starts; BindingHash except_bindings; BindingHash func_bindings; BindingHash global_bindings; BindingHash export_bindings; BindingHash func_type_bindings; BindingHash table_bindings; BindingHash memory_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 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; }; typedef DataScriptModule BinaryScriptModule; typedef DataScriptModule QuotedScriptModule; 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_; }; typedef std::unique_ptr ActionPtr; 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, Action, Register, AssertMalformed, AssertInvalid, AssertUnlinkable, AssertUninstantiable, AssertReturn, AssertReturnCanonicalNan, AssertReturnArithmeticNan, AssertTrap, AssertExhaustion, First = Module, Last = AssertExhaustion, }; static const 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; }; template class ActionCommandBase : public CommandMixin { public: ActionPtr action; }; typedef ActionCommandBase ActionCommand; typedef ActionCommandBase AssertReturnCanonicalNanCommand; typedef ActionCommandBase AssertReturnArithmeticNanCommand; class RegisterCommand : public CommandMixin { public: RegisterCommand(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; ConstVector expected; }; template class AssertTrapCommandBase : public CommandMixin { public: ActionPtr action; std::string text; }; typedef AssertTrapCommandBase AssertTrapCommand; typedef AssertTrapCommandBase AssertExhaustionCommand; template class AssertModuleCommand : public CommandMixin { public: std::unique_ptr module; std::string text; }; typedef AssertModuleCommand AssertMalformedCommand; typedef AssertModuleCommand AssertInvalidCommand; typedef AssertModuleCommand AssertUnlinkableCommand; typedef AssertModuleCommand AssertUninstantiableCommand; typedef std::unique_ptr CommandPtr; typedef std::vector CommandPtrVector; 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( const TypeVector& types, const BindingHash& bindings, std::vector* out_reverse_mapping); } // namespace wabt #endif /* WABT_IR_H_ */