diff --git a/include/wabt/binary-reader-logging.h b/include/wabt/binary-reader-logging.h index bd7ff33fe1..c6a285a5c5 100644 --- a/include/wabt/binary-reader-logging.h +++ b/include/wabt/binary-reader-logging.h @@ -44,13 +44,20 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result BeginTypeSection(Offset size) override; Result OnTypeCount(Index count) override; + Result OnRecursiveType(Index first_type_index, Index type_count) override; Result OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) override; - Result OnStructType(Index index, Index field_count, TypeMut* fields) override; - Result OnArrayType(Index index, TypeMut field) override; + Type* result_types, + GCTypeExtension* gc_ext) override; + Result OnStructType(Index index, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext) override; + Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) override; Result EndTypeSection() override; Result BeginImportSection(Offset size) override; @@ -96,9 +103,13 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result BeginTableSection(Offset size) override; Result OnTableCount(Index count) override; - Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) override; + Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) override; + Result BeginTableInitExpr(Index index) override; + Result EndTableInitExpr(Index index) override; + Result EndTable(Index index) override; Result EndTableSection() override; Result BeginMemorySection(Offset size) override; @@ -154,6 +165,17 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result OnOpcodeV128(v128 value) override; Result OnOpcodeBlockSig(Type sig_type) override; Result OnOpcodeType(Type type) override; + Result OnArrayCopyExpr(Index dst_type_index, Index src_type_index) override; + Result OnArrayFillExpr(Index type_index) override; + Result OnArrayGetExpr(Opcode opcode, Index type_index) override; + Result OnArrayInitDataExpr(Index type_index, Index data_index) override; + Result OnArrayInitElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewExpr(Index type_index) override; + Result OnArrayNewDataExpr(Index type_index, Index data_index) override; + Result OnArrayNewDefaultExpr(Index type_index) override; + Result OnArrayNewElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewFixedExpr(Index type_index, Index count) override; + Result OnArraySetExpr(Index type_index) override; Result OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -174,6 +196,10 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result OnBlockExpr(Type sig_type) override; Result OnBrExpr(Index depth) override; Result OnBrIfExpr(Index depth) override; + Result OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) override; Result OnBrOnNonNullExpr(Index depth) override; Result OnBrOnNullExpr(Index depth) override; Result OnBrTableExpr(Index num_targets, @@ -193,6 +219,7 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result OnF32ConstExpr(uint32_t value_bits) override; Result OnF64ConstExpr(uint64_t value_bits) override; Result OnV128ConstExpr(v128 value_bits) override; + Result OnGCUnaryExpr(Opcode opcode) override; Result OnGlobalGetExpr(Index global_index) override; Result OnGlobalSetExpr(Index global_index) override; Result OnI32ConstExpr(uint32_t value) override; @@ -221,9 +248,11 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Result OnTableSizeExpr(Index table) override; Result OnTableFillExpr(Index table) override; Result OnRefAsNonNullExpr() override; + Result OnRefCastExpr(Type type) override; Result OnRefFuncExpr(Index index) override; Result OnRefNullExpr(Type type) override; Result OnRefIsNullExpr() override; + Result OnRefTestExpr(Type type) override; Result OnNopExpr() override; Result OnRethrowExpr(Index depth) override; Result OnReturnCallExpr(Index func_index) override; @@ -235,6 +264,12 @@ class BinaryReaderLogging : public BinaryReaderDelegate { Index memidx, Address alignment_log2, Address offset) override; + Result OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) override; + Result OnStructNewExpr(Index type_index) override; + Result OnStructNewDefaultExpr(Index type_index) override; + Result OnStructSetExpr(Index type_index, Index field_index) override; Result OnThrowExpr(Index tag_index) override; Result OnThrowRefExpr() override; Result OnTryExpr(Type sig_type) override; @@ -428,6 +463,7 @@ class BinaryReaderLogging : public BinaryReaderDelegate { void LogType(Type type); void LogTypes(Index type_count, Type* types); void LogTypes(TypeVector& types); + void LogGCInfo(GCTypeExtension* gc_ext); void LogField(TypeMut field); Stream* stream_; @@ -437,4 +473,4 @@ class BinaryReaderLogging : public BinaryReaderDelegate { } // namespace wabt -#endif // WABT_BINARY_READER_LOGGING_H_ +#endif // WABT_BINARY_READER_LOGGING_H_ \ No newline at end of file diff --git a/include/wabt/binary-reader-nop.h b/include/wabt/binary-reader-nop.h index 545db127b2..f57ab3d311 100644 --- a/include/wabt/binary-reader-nop.h +++ b/include/wabt/binary-reader-nop.h @@ -45,20 +45,29 @@ class BinaryReaderNop : public BinaryReaderDelegate { /* Type section */ Result BeginTypeSection(Offset size) override { return Result::Ok; } + Result OnRecursiveType(Index first_type_index, Index type_count) override { + return Result::Ok; + } Result OnTypeCount(Index count) override { return Result::Ok; } Result OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) override { + Type* result_types, + GCTypeExtension* gc_ext) override { return Result::Ok; } Result OnStructType(Index index, Index field_count, - TypeMut* fields) override { + TypeMut* fields, + GCTypeExtension* gc_ext) override { + return Result::Ok; + } + Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) override { return Result::Ok; } - Result OnArrayType(Index index, TypeMut field) override { return Result::Ok; } Result EndTypeSection() override { return Result::Ok; } /* Import section */ @@ -121,11 +130,15 @@ class BinaryReaderNop : public BinaryReaderDelegate { /* Table section */ Result BeginTableSection(Offset size) override { return Result::Ok; } Result OnTableCount(Index count) override { return Result::Ok; } - Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) override { + Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) override { return Result::Ok; } + Result BeginTableInitExpr(Index index) override { return Result::Ok; } + Result EndTableInitExpr(Index index) override { return Result::Ok; } + Result EndTable(Index index) override { return Result::Ok; } Result EndTableSection() override { return Result::Ok; } /* Memory section */ @@ -206,6 +219,31 @@ class BinaryReaderNop : public BinaryReaderDelegate { Result OnOpcodeV128(v128 value) override { return Result::Ok; } Result OnOpcodeBlockSig(Type sig_type) override { return Result::Ok; } Result OnOpcodeType(Type type) override { return Result::Ok; } + Result OnArrayCopyExpr(Index dst_type_index, Index src_type_index) override { + return Result::Ok; + } + Result OnArrayFillExpr(Index type_index) override { return Result::Ok; } + Result OnArrayGetExpr(Opcode opcode, Index type_index) override { + return Result::Ok; + } + Result OnArrayInitDataExpr(Index type_index, Index data_index) override { + return Result::Ok; + } + Result OnArrayInitElemExpr(Index type_index, Index elem_index) override { + return Result::Ok; + } + Result OnArrayNewExpr(Index type_index) override { return Result::Ok; } + Result OnArrayNewDataExpr(Index type_index, Index data_index) override { + return Result::Ok; + } + Result OnArrayNewDefaultExpr(Index type_index) override { return Result::Ok; } + Result OnArrayNewElemExpr(Index type_index, Index elem_index) override { + return Result::Ok; + } + Result OnArrayNewFixedExpr(Index type_index, Index count) override { + return Result::Ok; + } + Result OnArraySetExpr(Index type_index) override { return Result::Ok; } Result OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -241,6 +279,12 @@ class BinaryReaderNop : public BinaryReaderDelegate { Result OnBlockExpr(Type sig_type) override { return Result::Ok; } Result OnBrExpr(Index depth) override { return Result::Ok; } Result OnBrIfExpr(Index depth) override { return Result::Ok; } + Result OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) override { + return Result::Ok; + } Result OnBrOnNonNullExpr(Index depth) override { return Result::Ok; } Result OnBrOnNullExpr(Index depth) override { return Result::Ok; } Result OnBrTableExpr(Index num_targets, @@ -264,6 +308,7 @@ class BinaryReaderNop : public BinaryReaderDelegate { Result OnF32ConstExpr(uint32_t value_bits) override { return Result::Ok; } Result OnF64ConstExpr(uint64_t value_bits) override { return Result::Ok; } Result OnV128ConstExpr(v128 value_bits) override { return Result::Ok; } + Result OnGCUnaryExpr(Opcode opcode) override { return Result::Ok; } Result OnGlobalGetExpr(Index global_index) override { return Result::Ok; } Result OnGlobalSetExpr(Index global_index) override { return Result::Ok; } Result OnI32ConstExpr(uint32_t value) override { return Result::Ok; } @@ -302,9 +347,11 @@ class BinaryReaderNop : public BinaryReaderDelegate { Result OnTableSizeExpr(Index table_index) override { return Result::Ok; } Result OnTableFillExpr(Index table_index) override { return Result::Ok; } Result OnRefAsNonNullExpr() override { return Result::Ok; } + Result OnRefCastExpr(Type type) override { return Result::Ok; } Result OnRefFuncExpr(Index func_index) override { return Result::Ok; } Result OnRefNullExpr(Type type) override { return Result::Ok; } Result OnRefIsNullExpr() override { return Result::Ok; } + Result OnRefTestExpr(Type type) override { return Result::Ok; } Result OnNopExpr() override { return Result::Ok; } Result OnRethrowExpr(Index depth) override { return Result::Ok; } Result OnReturnCallExpr(Index sig_index) override { return Result::Ok; } @@ -322,6 +369,18 @@ class BinaryReaderNop : public BinaryReaderDelegate { Address offset) override { return Result::Ok; } + Result OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) override { + return Result::Ok; + } + Result OnStructNewExpr(Index type_index) override { return Result::Ok; } + Result OnStructNewDefaultExpr(Index type_index) override { + return Result::Ok; + } + Result OnStructSetExpr(Index type_index, Index field_index) override { + return Result::Ok; + } Result OnThrowExpr(Index depth) override { return Result::Ok; } Result OnThrowRefExpr() override { return Result::Ok; } Result OnTryExpr(Type sig_type) override { return Result::Ok; } diff --git a/include/wabt/binary-reader.h b/include/wabt/binary-reader.h index 96455f2251..89b2e31b96 100644 --- a/include/wabt/binary-reader.h +++ b/include/wabt/binary-reader.h @@ -52,13 +52,20 @@ struct ReadBinaryOptions { bool skip_function_bodies = false; }; -// TODO: Move somewhere else? +// TODO: Move both TypeMut and GCTypeInformation somewhere else? struct TypeMut { Type type; bool mutable_; }; using TypeMutVector = std::vector; +// Garbage Collector specific type information +struct GCTypeExtension { + bool is_final_sub_type; + Index sub_type_count; + Index* sub_types; +}; + struct CatchClause { CatchKind kind; Index tag; @@ -99,15 +106,20 @@ class BinaryReaderDelegate { /* Type section */ virtual Result BeginTypeSection(Offset size) = 0; virtual Result OnTypeCount(Index count) = 0; + virtual Result OnRecursiveType(Index first_type_index, Index type_count) = 0; virtual Result OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) = 0; + Type* result_types, + GCTypeExtension* gc_ext) = 0; virtual Result OnStructType(Index index, Index field_count, - TypeMut* fields) = 0; - virtual Result OnArrayType(Index index, TypeMut field) = 0; + TypeMut* fields, + GCTypeExtension* gc_ext) = 0; + virtual Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) = 0; virtual Result EndTypeSection() = 0; /* Import section */ @@ -156,9 +168,13 @@ class BinaryReaderDelegate { /* Table section */ virtual Result BeginTableSection(Offset size) = 0; virtual Result OnTableCount(Index count) = 0; - virtual Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) = 0; + virtual Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) = 0; + virtual Result BeginTableInitExpr(Index index) = 0; + virtual Result EndTableInitExpr(Index index) = 0; + virtual Result EndTable(Index index) = 0; virtual Result EndTableSection() = 0; /* Memory section */ @@ -221,6 +237,17 @@ class BinaryReaderDelegate { virtual Result OnOpcodeV128(v128 value) = 0; virtual Result OnOpcodeBlockSig(Type sig_type) = 0; virtual Result OnOpcodeType(Type type) = 0; + virtual Result OnArrayCopyExpr(Index dst_type_index, Index src_type_index) = 0; + virtual Result OnArrayFillExpr(Index type_index) = 0; + virtual Result OnArrayGetExpr(Opcode opcode, Index type_index) = 0; + virtual Result OnArrayInitDataExpr(Index type_index, Index data_index) = 0; + virtual Result OnArrayInitElemExpr(Index type_index, Index elem_index) = 0; + virtual Result OnArrayNewExpr(Index type_index) = 0; + virtual Result OnArrayNewDataExpr(Index type_index, Index data_index) = 0; + virtual Result OnArrayNewDefaultExpr(Index type_index) = 0; + virtual Result OnArrayNewElemExpr(Index type_index, Index elem_index) = 0; + virtual Result OnArrayNewFixedExpr(Index type_index, Index count) = 0; + virtual Result OnArraySetExpr(Index type_index) = 0; virtual Result OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -250,6 +277,10 @@ class BinaryReaderDelegate { virtual Result OnBlockExpr(Type sig_type) = 0; virtual Result OnBrExpr(Index depth) = 0; virtual Result OnBrIfExpr(Index depth) = 0; + virtual Result OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) = 0; virtual Result OnBrOnNonNullExpr(Index depth) = 0; virtual Result OnBrOnNullExpr(Index depth) = 0; virtual Result OnBrTableExpr(Index num_targets, @@ -269,6 +300,7 @@ class BinaryReaderDelegate { virtual Result OnF32ConstExpr(uint32_t value_bits) = 0; virtual Result OnF64ConstExpr(uint64_t value_bits) = 0; virtual Result OnV128ConstExpr(v128 value_bits) = 0; + virtual Result OnGCUnaryExpr(Opcode opcode) = 0; virtual Result OnGlobalGetExpr(Index global_index) = 0; virtual Result OnGlobalSetExpr(Index global_index) = 0; virtual Result OnI32ConstExpr(uint32_t value) = 0; @@ -297,9 +329,11 @@ class BinaryReaderDelegate { virtual Result OnTableSizeExpr(Index table_index) = 0; virtual Result OnTableFillExpr(Index table_index) = 0; virtual Result OnRefAsNonNullExpr() = 0; + virtual Result OnRefCastExpr(Type type) = 0; virtual Result OnRefFuncExpr(Index func_index) = 0; virtual Result OnRefNullExpr(Type type) = 0; virtual Result OnRefIsNullExpr() = 0; + virtual Result OnRefTestExpr(Type type) = 0; virtual Result OnNopExpr() = 0; virtual Result OnRethrowExpr(Index depth) = 0; virtual Result OnReturnExpr() = 0; @@ -312,6 +346,12 @@ class BinaryReaderDelegate { Index memidx, Address alignment_log2, Address offset) = 0; + virtual Result OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) = 0; + virtual Result OnStructNewExpr(Index type_index) = 0; + virtual Result OnStructNewDefaultExpr(Index type_index) = 0; + virtual Result OnStructSetExpr(Index type_index, Index field_index) = 0; virtual Result OnThrowExpr(Index tag_index) = 0; virtual Result OnThrowRefExpr() = 0; virtual Result OnTryExpr(Type sig_type) = 0; diff --git a/include/wabt/expr-visitor.h b/include/wabt/expr-visitor.h index cef8025baa..e8aa350de5 100644 --- a/include/wabt/expr-visitor.h +++ b/include/wabt/expr-visitor.h @@ -73,6 +73,7 @@ class ExprVisitor::Delegate { virtual Result EndBlockExpr(BlockExpr*) = 0; virtual Result OnBrExpr(BrExpr*) = 0; virtual Result OnBrIfExpr(BrIfExpr*) = 0; + virtual Result OnBrOnCastExpr(BrOnCastExpr*) = 0; virtual Result OnBrOnNonNullExpr(BrOnNonNullExpr*) = 0; virtual Result OnBrOnNullExpr(BrOnNullExpr*) = 0; virtual Result OnBrTableExpr(BrTableExpr*) = 0; @@ -145,6 +146,24 @@ class ExprVisitor::Delegate { virtual Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) = 0; virtual Result OnLoadSplatExpr(LoadSplatExpr*) = 0; virtual Result OnLoadZeroExpr(LoadZeroExpr*) = 0; + virtual Result OnArrayCopyExpr(ArrayCopyExpr*) = 0; + virtual Result OnArrayFillExpr(ArrayFillExpr*) = 0; + virtual Result OnArrayGetExpr(ArrayGetExpr*) = 0; + virtual Result OnArrayInitDataExpr(ArrayInitDataExpr*) = 0; + virtual Result OnArrayInitElemExpr(ArrayInitElemExpr*) = 0; + virtual Result OnArrayNewExpr(ArrayNewExpr*) = 0; + virtual Result OnArrayNewDataExpr(ArrayNewDataExpr*) = 0; + virtual Result OnArrayNewDefaultExpr(ArrayNewDefaultExpr*) = 0; + virtual Result OnArrayNewElemExpr(ArrayNewElemExpr*) = 0; + virtual Result OnArrayNewFixedExpr(ArrayNewFixedExpr*) = 0; + virtual Result OnArraySetExpr(ArraySetExpr*) = 0; + virtual Result OnGCUnaryExpr(GCUnaryExpr*) = 0; + virtual Result OnRefCastExpr(RefCastExpr*) = 0; + virtual Result OnRefTestExpr(RefTestExpr*) = 0; + virtual Result OnStructGetExpr(StructGetExpr*) = 0; + virtual Result OnStructNewExpr(StructNewExpr*) = 0; + virtual Result OnStructNewDefaultExpr(StructNewDefaultExpr*) = 0; + virtual Result OnStructSetExpr(StructSetExpr*) = 0; }; class ExprVisitor::DelegateNop : public ExprVisitor::Delegate { @@ -154,6 +173,7 @@ class ExprVisitor::DelegateNop : public ExprVisitor::Delegate { Result EndBlockExpr(BlockExpr*) override { return Result::Ok; } Result OnBrExpr(BrExpr*) override { return Result::Ok; } Result OnBrIfExpr(BrIfExpr*) override { return Result::Ok; } + Result OnBrOnCastExpr(BrOnCastExpr*) override { return Result::Ok; }; Result OnBrOnNonNullExpr(BrOnNonNullExpr*) override { return Result::Ok; }; Result OnBrOnNullExpr(BrOnNullExpr*) override { return Result::Ok; }; Result OnBrTableExpr(BrTableExpr*) override { return Result::Ok; } @@ -230,8 +250,30 @@ class ExprVisitor::DelegateNop : public ExprVisitor::Delegate { Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override { return Result::Ok; } Result OnLoadSplatExpr(LoadSplatExpr*) override { return Result::Ok; } Result OnLoadZeroExpr(LoadZeroExpr*) override { return Result::Ok; } + Result OnArrayCopyExpr(ArrayCopyExpr*) override { return Result::Ok; } + Result OnArrayFillExpr(ArrayFillExpr*) override { return Result::Ok; } + Result OnArrayGetExpr(ArrayGetExpr*) override { return Result::Ok; } + Result OnArrayInitDataExpr(ArrayInitDataExpr*) override { return Result::Ok; } + Result OnArrayInitElemExpr(ArrayInitElemExpr*) override { return Result::Ok; } + Result OnArrayNewExpr(ArrayNewExpr*) override { return Result::Ok; } + Result OnArrayNewDataExpr(ArrayNewDataExpr*) override { return Result::Ok; } + Result OnArrayNewDefaultExpr(ArrayNewDefaultExpr*) override { + return Result::Ok; + } + Result OnArrayNewElemExpr(ArrayNewElemExpr*) override { return Result::Ok; } + Result OnArrayNewFixedExpr(ArrayNewFixedExpr*) override { return Result::Ok; } + Result OnArraySetExpr(ArraySetExpr*) override { return Result::Ok; } + Result OnGCUnaryExpr(GCUnaryExpr*) override { return Result::Ok; } + Result OnRefCastExpr(RefCastExpr*) override { return Result::Ok; } + Result OnRefTestExpr(RefTestExpr*) override { return Result::Ok; } + Result OnStructGetExpr(StructGetExpr*) override { return Result::Ok; } + Result OnStructNewExpr(StructNewExpr*) override { return Result::Ok; } + Result OnStructNewDefaultExpr(StructNewDefaultExpr*) override { + return Result::Ok; + } + Result OnStructSetExpr(StructSetExpr*) override { return Result::Ok; } }; } // namespace wabt -#endif // WABT_EXPR_VISITOR_H_ +#endif // WABT_EXPR_VISITOR_H_ \ No newline at end of file diff --git a/include/wabt/feature.def b/include/wabt/feature.def index ac96377b43..6b2194e175 100644 --- a/include/wabt/feature.def +++ b/include/wabt/feature.def @@ -28,14 +28,14 @@ WABT_FEATURE(sat_float_to_int, "saturating-float-to-int", true, "Saturatin WABT_FEATURE(sign_extension, "sign-extension", true, "Sign-extension operators") WABT_FEATURE(simd, "simd", true, "SIMD support") WABT_FEATURE(threads, "threads", false, "Threading support") -WABT_FEATURE(function_references, "function-references", false, "Typed function references") +WABT_FEATURE(function_references, "function-references", true, "Typed function references") WABT_FEATURE(multi_value, "multi-value", true, "Multi-value") WABT_FEATURE(tail_call, "tail-call", false, "Tail-call support") WABT_FEATURE(bulk_memory, "bulk-memory", true, "Bulk-memory operations") WABT_FEATURE(reference_types, "reference-types", true, "Reference types (externref)") WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax") WABT_FEATURE(code_metadata, "code-metadata", false, "Code metadata") -WABT_FEATURE(gc, "gc", false, "Garbage collection") +WABT_FEATURE(gc, "gc", true, "Garbage collection") WABT_FEATURE(memory64, "memory64", false, "64-bit memory") WABT_FEATURE(multi_memory, "multi-memory", false, "Multi-memory") WABT_FEATURE(extended_const, "extended-const", false, "Extended constant expressions") diff --git a/include/wabt/interp/interp-inl.h b/include/wabt/interp/interp-inl.h index 074fc9fc9f..75ac533349 100644 --- a/include/wabt/interp/interp-inl.h +++ b/include/wabt/interp/interp-inl.h @@ -32,6 +32,50 @@ inline bool operator!=(Ref lhs, Ref rhs) { return lhs.index != rhs.index; } +// static +inline Ref Ref::CreateI31Val(size_t value) { + assert((value & 0x7fffffff) < kI31Value); + return Ref((value & 0x7fffffff) | kI31Value); +} + +// static +inline Ref Ref::CreateHostVal(size_t value) { + assert(value < kHostValue); + return Ref(value | kHostValue); +} + +inline bool Ref::IsI31Val() const { + return (index & kI31Value) != 0; +} + +inline bool Ref::IsHostVal() const { + return (index & (kI31Value | kHostValue)) == kHostValue; +} + +inline bool Ref::IsI31OrHostVal() const { + return (index & (kI31Value | kHostValue)) != 0; +} + +inline u32 Ref::GetS32Val() const { + assert(IsI31Val()); + return static_cast(static_cast(index) << 1) >> 1; +} + +inline u32 Ref::GetU32Val() const { + assert(IsI31Val()); + return static_cast(index & 0x7fffffff); +} + +inline size_t Ref::GetHostVal() const { + assert(IsHostVal()); + return index ^ kHostValue; +} + +inline size_t Ref::IsAnyHostVal() const { + assert(IsHostVal()); + return index == (kHostValue | kAnyHostValue); +} + //// ExternType //// inline ExternType::ExternType(ExternKind kind) : kind(kind) {} @@ -42,7 +86,30 @@ inline bool FuncType::classof(const ExternType* type) { } inline FuncType::FuncType(ValueTypes params, ValueTypes results) - : ExternType(ExternKind::Func), params(params), results(results), func_types(nullptr) {} + : ExternType(ExternKind::Func), + kind(FuncType::TypeKind::Func), + canonical_index(kInvalidIndex), + canonical_sub_index(kInvalidIndex), + is_final_sub_type(true), + recursive_start(0), + recursive_count(0), + params(params), + results(results), + func_types(nullptr) {} + +inline FuncType::FuncType(TypeKind kind, ValueTypes params, ValueTypes results) + : ExternType(ExternKind::Func), + kind(kind), + canonical_index(kInvalidIndex), + canonical_sub_index(kInvalidIndex), + is_final_sub_type(true), + recursive_start(0), + recursive_count(0), + params(params), + results(results), + func_types(nullptr) { + assert((kind == TypeKind::Struct || kind == TypeKind::Array) && params.size() == results.size()); +} //// TableType //// // static @@ -142,6 +209,7 @@ inline Frame::Frame(Ref func, //// FreeList //// template <> inline bool FreeList::IsUsed(Index index) const { + assert(index < list_.size()); return (list_[index].index & refFreeBit) == 0; } @@ -178,6 +246,7 @@ inline void FreeList::Delete(Index index) { template bool FreeList::IsUsed(Index index) const { + assert(index < list_.size()); return (reinterpret_cast(list_[index]) & ptrFreeBit) == 0; } @@ -422,12 +491,6 @@ void RequireType(ValueType type) { assert(HasType(type)); } -inline bool TypesMatch(ValueType expected, ValueType actual) { - // Currently there is no subtyping, so expected and actual must match - // exactly. In the future this may be expanded. - return expected == actual; -} - //// Value //// inline Value WABT_VECTORCALL Value::Make(s32 val) { Value res; res.i32_ = val; res.SetType(ValueType::I32); return res; } inline Value WABT_VECTORCALL Value::Make(u32 val) { Value res; res.i32_ = val; res.SetType(ValueType::I32); return res; } @@ -480,12 +543,13 @@ template <> inline void WABT_VECTORCALL Value::Set(Ref val) { ref_ = val; S //// Store //// inline bool Store::IsValid(Ref ref) const { - return objects_.IsUsed(ref.index) && objects_.Get(ref.index); + return ref.index >= Ref::kHostValue || objects_.IsUsed(ref.index); } template bool Store::Is(Ref ref) const { - return objects_.IsUsed(ref.index) && isa(objects_.Get(ref.index)); + return ref.index < Ref::kHostValue && objects_.IsUsed(ref.index) && + isa(objects_.Get(ref.index)); } template @@ -682,8 +746,8 @@ inline bool Table::classof(const Object* obj) { } // static -inline Table::Ptr Table::New(Store& store, TableType type) { - return store.Alloc(store, type); +inline Table::Ptr Table::New(Store& store, TableType type, Ref init_ref) { + return store.Alloc
(store, type, init_ref); } inline const ExternType& Table::extern_type() { @@ -885,6 +949,73 @@ inline const TagType& Tag::type() const { return type_; } +//// Array //// +// static +inline bool Array::classof(const Object* obj) { + return obj->kind() == skind; +} + +inline Array::Ptr Array::New(Store& store, + u32 size, + Index type_index, + Module* mod) { + return store.Alloc(store, size, type_index, mod); +} + +inline Index Array::Size() const { + return static_cast(items_.size()); +} + +inline Value Array::GetItem(Index idx) const { + return items_[idx]; +} + +inline void Array::SetItem(Index idx, Value value) { + items_[idx] = value; +} + +inline Values& Array::GetItems() { + return items_; +} + +inline Index Array::GetTypeIndex() const { + return type_index_; +} + +inline Ref Array::GetModule() const { + return module_; +} + +//// Struct //// +// static +inline bool Struct::classof(const Object* obj) { + return obj->kind() == skind; +} + +inline Struct::Ptr Struct::New(Store& store, Index type_index, Module* mod) { + return store.Alloc(store, type_index, mod); +} + +inline Index Struct::Size() const { + return static_cast(fields_.size()); +} + +inline Value Struct::GetField(Index idx) const { + return fields_[idx]; +} + +inline void Struct::SetField(Index idx, Value value) { + fields_[idx] = value; +} + +inline Index Struct::GetTypeIndex() const { + return type_index_; +} + +inline Ref Struct::GetModule() const { + return module_; +} + //// ElemSegment //// inline void ElemSegment::Drop() { elements_.clear(); @@ -998,4 +1129,4 @@ inline Store& Thread::store() { } } // namespace interp -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/include/wabt/interp/interp.h b/include/wabt/interp/interp.h index 4e376eb327..f2a763f230 100644 --- a/include/wabt/interp/interp.h +++ b/include/wabt/interp/interp.h @@ -89,6 +89,8 @@ enum class ObjectKind { Memory, Global, Tag, + Array, + Struct, Module, Instance, @@ -105,10 +107,24 @@ const char* GetName(ObjectKind); struct Ref { static const Ref Null; + // The highest two bits represent special values. + static const size_t kI31Value = static_cast(1) << (sizeof(size_t) * 8 - 1); + static const size_t kHostValue = static_cast(1) << (sizeof(size_t) * 8 - 2); + static const size_t kAnyHostValue = ~static_cast(0) >> 2; Ref() = default; explicit Ref(size_t index); + static Ref CreateI31Val(size_t value); + static Ref CreateHostVal(size_t value); + bool IsI31Val() const; + bool IsHostVal() const; + bool IsI31OrHostVal() const; + u32 GetS32Val() const; + u32 GetU32Val() const; + size_t GetHostVal() const; + size_t IsAnyHostVal() const; + friend bool operator==(Ref, Ref); friend bool operator!=(Ref, Ref); @@ -157,6 +173,10 @@ using u32x2 = Simd; //// Types //// +bool TypesMatch(ValueType expected, ValueType actual); + +//// Limits //// + bool CanGrow(const Limits&, u32 old_size, u32 delta, u32* new_size); Result Match(const Limits& expected, const Limits& actual, @@ -174,7 +194,21 @@ struct FuncType : ExternType { static const ExternKind skind = ExternKind::Func; static bool classof(const ExternType* type); + enum class TypeKind { + Func, + Struct, + Array, + }; + + // To simplify the implementation, FuncType may also represent + // Struct and Array types. To do this, the mutability is stored + // in results, which must have the same size as params. + // This implementation might change in the future. + static const Type::Enum Mutable = Type::I32; + static const Type::Enum Immutable = Type::I64; + explicit FuncType(ValueTypes params, ValueTypes results); + explicit FuncType(TypeKind kind, ValueTypes params, ValueTypes results); std::unique_ptr Clone() const override; @@ -182,6 +216,15 @@ struct FuncType : ExternType { const FuncType& actual, std::string* out_msg); + TypeKind kind; + // These two are needed for fast dynamic type comparison. + Index canonical_index; + Index canonical_sub_index; + // These three are needed for type equality comparisons + // across different modules (import/export validation). + bool is_final_sub_type; + Index recursive_start; + Index recursive_count; ValueTypes params; ValueTypes results; // When params or results contain references, the referenced @@ -334,6 +377,7 @@ struct FuncDesc { struct TableDesc { TableType type; + FuncDesc init_func; }; struct MemoryDesc { @@ -818,7 +862,7 @@ class Table : public Extern { static const char* GetTypeName() { return "Table"; } using Ptr = RefPtr
; - static Table::Ptr New(Store&, TableType); + static Table::Ptr New(Store&, TableType, Ref); Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; @@ -850,7 +894,7 @@ class Table : public Extern { private: friend Store; - explicit Table(Store&, TableType); + explicit Table(Store&, TableType, Ref); void Mark(Store&) override; TableType type_; @@ -971,6 +1015,59 @@ class Tag : public Extern { TagType type_; }; +class Array : public Object { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Array; + static const char* GetTypeName() { return "Array"; } + using Ptr = RefPtr; + + static Array::Ptr New(Store&, u32 size, Index type_index, Module* mod); + + bool IsValidRange(u64 offset, u64 size) const; + + Index Size() const; + Value GetItem(Index idx) const; + void SetItem(Index idx, Value value); + Values& GetItems(); + Index GetTypeIndex() const; + Ref GetModule() const; + + private: + friend Store; + explicit Array(Store&, u32 size, Index type_index, Module* mod); + void Mark(Store&) override; + + Ref module_; + Index type_index_; + Values items_; +}; + +class Struct : public Object { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Struct; + static const char* GetTypeName() { return "Struct"; } + using Ptr = RefPtr; + + static Struct::Ptr New(Store&, Index type_index, Module* mod); + + Index Size() const; + Value GetField(Index idx) const; + void SetField(Index idx, Value value); + Index GetTypeIndex() const; + Ref GetModule() const; + + private: + friend Store; + explicit Struct(Store&, Index type_index, Module* mod); + void Mark(Store&) override; + + Ref module_; + Index type_index_; + Values fields_; +}; + class ElemSegment { public: explicit ElemSegment(Store& store, const ElemDesc*, RefPtr&); @@ -1139,6 +1236,8 @@ class Thread { void Push(Value); void Push(Ref); + bool CheckRefCast(Ref ref, Type expected); + template using UnopFunc = R WABT_VECTORCALL(T); template @@ -1242,6 +1341,23 @@ class Thread { template RunResult DoAtomicRmwCmpxchg(Instr, Trap::Ptr* out_trap); + RunResult DoArrayCopy(Trap::Ptr* out_trap); + RunResult DoArrayFill(Trap::Ptr* out_trap); + RunResult DoArrayGet(Trap::Ptr* out_trap); + RunResult DoArrayInitElem(Instr, Trap::Ptr* out_trap); + RunResult DoArrayInitData(Instr, Trap::Ptr* out_trap); + RunResult DoArrayGetPacked(Instr, Trap::Ptr* out_trap); + RunResult DoArrayNew(Instr); + RunResult DoArrayNewData(Instr, Trap::Ptr* out_trap); + RunResult DoArrayNewElem(Instr, Trap::Ptr* out_trap); + RunResult DoArrayNewFixed(Instr); + RunResult DoArraySet(Trap::Ptr* out_trap); + RunResult DoBrOnCast(Instr); + RunResult DoRefCast(Instr, Trap::Ptr* out_trap); + RunResult DoRefTest(Instr); + RunResult DoStructGetPacked(Instr, Trap::Ptr* out_trap); + RunResult DoStructNew(Instr); + RunResult DoThrow(Exception::Ptr exn_ref); RunResult StepInternal(Trap::Ptr* out_trap); @@ -1283,4 +1399,4 @@ struct Thread::TraceSource : Istream::TraceSource { #include "wabt/interp/interp-inl.h" -#endif // WABT_INTERP_H_ +#endif // WABT_INTERP_H_ \ No newline at end of file diff --git a/include/wabt/interp/istream.h b/include/wabt/interp/istream.h index f8eb6a2679..246951c261 100644 --- a/include/wabt/interp/istream.h +++ b/include/wabt/interp/istream.h @@ -47,14 +47,20 @@ enum class InstrKind { Imm_0_Op_1, // i32.eqz Imm_0_Op_2, // i32.add Imm_0_Op_3, // select + Imm_0_Op_4, // array.fill + Imm_0_Op_5, // array.copy Imm_Jump_Op_0, // br Imm_Jump_Op_1, // br_if Imm_Index_Op_0, // global.get Imm_Index_Op_1, // global.set Imm_Index_Op_2, // table.set Imm_Index_Op_3, // memory.fill + Imm_Index_Op_4, // array.init_elem Imm_Index_Op_N, // call + Imm_Index_Index_Op_1, // struct.get_s + Imm_Index_Index_Op_2, // array.new_data Imm_Index_Index_Op_3, // memory.init + Imm_Index_Index_Op_4, // array.init_data Imm_Index_Index_Op_N, // call_indirect Imm_Index_Offset_Op_1, // i32.load Imm_Index_Offset_Op_2, // i32.store @@ -162,4 +168,4 @@ class Istream { } // namespace interp } // namespace wabt -#endif // WABT_INTERP_ISTREAM_H_ +#endif // WABT_INTERP_ISTREAM_H_ \ No newline at end of file diff --git a/include/wabt/ir.h b/include/wabt/ir.h index 9102297dc8..aa74ef167e 100644 --- a/include/wabt/ir.h +++ b/include/wabt/ir.h @@ -110,6 +110,7 @@ using VarVector = std::vector; struct Const { static constexpr uintptr_t kRefNullBits = ~uintptr_t(0); + static constexpr uintptr_t kRefAnyValueBits = ~uintptr_t(1); Const() : Const(Type::I32, uint32_t(0)) {} @@ -180,9 +181,16 @@ struct Const { set_f64(0); set_expected_nan(0, nan); } - void set_funcref() { From(Type::FuncRef, 0); } + void set_arrayref() { From(Type(Type::ArrayRef, Type::ReferenceNonNull), 0); } + // AnyRef represents ref.host. + void set_anyref(uintptr_t x) { From(Type::AnyRef, x); } + void set_any(uintptr_t x) { From(Type(Type::AnyRef, Type::ReferenceNonNull), x); } + void set_eqref() { From(Type(Type::EqRef, Type::ReferenceNonNull), 0); } void set_externref(uintptr_t x) { From(Type::ExternRef, x); } void set_extern(uintptr_t x) { From(Type(Type::ExternRef, Type::ReferenceNonNull), x); } + void set_funcref() { From(Type::FuncRef, 0); } + void set_i31ref() { From(Type(Type::I31Ref, Type::ReferenceNonNull), 0); } + void set_structref() { From(Type(Type::StructRef, Type::ReferenceNonNull), 0); } void set_null(Type type) { From(type, kRefNullBits); } bool is_expected_nan(int lane = 0) const { @@ -297,6 +305,19 @@ enum class TypeEntryKind { Array, }; +struct TypeEntryGCTypeExtension { + TypeEntryGCTypeExtension(bool is_final_sub_type) + : is_final_sub_type(is_final_sub_type) {} + + void InitSubTypes(Index* sub_type_list, Index sub_type_count); + + bool is_final_sub_type; + // The binary/text format allows any number of subtypes, + // so parsers must handle them. The validator rejects + // lists which size is greater than 1. + VarVector sub_types; +}; + class TypeEntry { public: WABT_DISALLOW_COPY_AND_ASSIGN(TypeEntry); @@ -307,12 +328,17 @@ class TypeEntry { Location loc; std::string name; + TypeEntryGCTypeExtension gc_ext; protected: explicit TypeEntry(TypeEntryKind kind, + bool is_final_sub_type, std::string_view name = std::string_view(), const Location& loc = Location()) - : loc(loc), name(name), kind_(kind) {} + : loc(loc), + name(name), + gc_ext(is_final_sub_type), + kind_(kind) {} TypeEntryKind kind_; }; @@ -323,8 +349,8 @@ class FuncType : public TypeEntry { return entry->kind() == TypeEntryKind::Func; } - explicit FuncType(std::string_view name = std::string_view()) - : TypeEntry(TypeEntryKind::Func, name) {} + explicit FuncType(bool is_final_sub_type, std::string_view name = std::string_view()) + : TypeEntry(TypeEntryKind::Func, is_final_sub_type, name) {} Index GetNumParams() const { return sig.GetNumParams(); } Index GetNumResults() const { return sig.GetNumResults(); } @@ -353,8 +379,8 @@ class StructType : public TypeEntry { return entry->kind() == TypeEntryKind::Struct; } - explicit StructType(std::string_view name = std::string_view()) - : TypeEntry(TypeEntryKind::Struct) {} + explicit StructType(bool is_final_sub_type, std::string_view name = std::string_view()) + : TypeEntry(TypeEntryKind::Struct, is_final_sub_type, name) {} std::vector fields; }; @@ -365,12 +391,19 @@ class ArrayType : public TypeEntry { return entry->kind() == TypeEntryKind::Array; } - explicit ArrayType(std::string_view name = std::string_view()) - : TypeEntry(TypeEntryKind::Array) {} + explicit ArrayType(bool is_final_sub_type, std::string_view name = std::string_view()) + : TypeEntry(TypeEntryKind::Array, is_final_sub_type, name) {} Field field; }; +struct RecursiveRange { + Index first_type_index; + Index type_count; + + Index EndTypeIndex() const { return first_type_index + type_count; } +}; + struct FuncDeclaration { Index GetNumParams() const { return sig.GetNumParams(); } Index GetNumResults() const { return sig.GetNumResults(); } @@ -383,6 +416,17 @@ struct FuncDeclaration { }; enum class ExprType { + ArrayCopy, + ArrayFill, + ArrayGet, + ArrayInitData, + ArrayInitElem, + ArrayNew, + ArrayNewData, + ArrayNewDefault, + ArrayNewElem, + ArrayNewFixed, + ArraySet, AtomicLoad, AtomicRmw, AtomicRmwCmpxchg, @@ -394,6 +438,7 @@ enum class ExprType { Block, Br, BrIf, + BrOnCast, BrOnNonNull, BrOnNull, BrTable, @@ -405,6 +450,7 @@ enum class ExprType { Const, Convert, Drop, + GCUnary, GlobalGet, GlobalSet, If, @@ -421,9 +467,11 @@ enum class ExprType { MemorySize, Nop, RefAsNonNull, + RefCast, RefIsNull, RefFunc, RefNull, + RefTest, Rethrow, Return, ReturnCall, @@ -434,6 +482,10 @@ enum class ExprType { SimdLoadLane, SimdStoreLane, SimdShuffleOp, + StructGet, + StructNew, + StructNewDefault, + StructSet, LoadSplat, LoadZero, Store, @@ -453,7 +505,7 @@ enum class ExprType { Unary, Unreachable, - First = AtomicLoad, + First = ArrayCopy, Last = Unreachable }; @@ -590,6 +642,7 @@ class OpcodeExpr : public ExprMixin { using BinaryExpr = OpcodeExpr; using CompareExpr = OpcodeExpr; using ConvertExpr = OpcodeExpr; +using GCUnaryExpr = OpcodeExpr; using UnaryExpr = OpcodeExpr; using TernaryExpr = OpcodeExpr; using RefAsNonNullExpr = OpcodeExpr; @@ -695,6 +748,15 @@ using TableFillExpr = VarExpr; using MemoryInitExpr = MemoryVarExpr; +using ArrayFillExpr = VarExpr; +using ArrayNewExpr = VarExpr; +using ArrayNewDefaultExpr = VarExpr; +using ArraySetExpr = VarExpr; +using RefCastExpr = VarExpr; +using RefTestExpr = VarExpr; +using StructNewExpr = VarExpr; +using StructNewDefaultExpr = VarExpr; + class SelectExpr : public ExprMixin { public: SelectExpr(const Location& loc = Location()) @@ -873,6 +935,62 @@ class AtomicFenceExpr : public ExprMixin { uint32_t consistency_model; }; +class ArrayGetExpr : public ExprMixin { + public: + ArrayGetExpr(Opcode opcode, const Var& type_var, const Location& loc = Location()) + : ExprMixin(loc), opcode(opcode), type_var(type_var) {} + + Opcode opcode; + Var type_var; +}; + +template +class TypeVarExpr : public ExprMixin { + public: + TypeVarExpr(const Var& type_var, const Var& var, const Location& loc = Location()) + : ExprMixin(loc), type_var(type_var), var(var) {} + + Var type_var; + Var var; +}; + +using ArrayCopyExpr = TypeVarExpr; +using ArrayInitDataExpr = TypeVarExpr; +using ArrayInitElemExpr = TypeVarExpr; +using ArrayNewDataExpr = TypeVarExpr; +using ArrayNewElemExpr = TypeVarExpr; +using StructSetExpr = TypeVarExpr; + +class ArrayNewFixedExpr : public ExprMixin { + public: + ArrayNewFixedExpr(const Var& type_var, Index count, const Location& loc = Location()) + : ExprMixin(loc), type_var(type_var), count(count) {} + + Var type_var; + Index count; +}; + +class StructGetExpr : public ExprMixin { + public: + StructGetExpr(Opcode opcode, const Var& type_var, const Var& var, const Location& loc = Location()) + : ExprMixin(loc), opcode(opcode), type_var(type_var), var(var) {} + + Opcode opcode; + Var type_var; + Var var; +}; + +class BrOnCastExpr : public ExprMixin { + public: + BrOnCastExpr(Opcode opcode, const Var& label_var, const Var& type1_var, const Var& type2_var, const Location& loc = Location()) + : ExprMixin(loc), opcode(opcode), label_var(label_var), type1_var(type1_var), type2_var(type2_var) {} + + Opcode opcode; + Var label_var; + Var type1_var; + Var type2_var; +}; + struct Tag { explicit Tag(std::string_view name) : name(name) {} @@ -987,13 +1105,14 @@ struct Table { std::string name; Limits elem_limits; Type elem_type; + ExprList init_expr; }; using ExprListVector = std::vector; struct ElemSegment { explicit ElemSegment(std::string_view name) : name(name) {} - uint8_t GetFlags(const Module*) const; + uint8_t GetFlags(const Module*, bool) const; SegmentKind kind = SegmentKind::Active; std::string name; @@ -1106,7 +1225,8 @@ enum class ModuleFieldType { Memory, DataSegment, Start, - Tag + Tag, + EmptyRec }; class ModuleField : public intrusive_list_base { @@ -1233,6 +1353,12 @@ class TagModuleField : public ModuleFieldMixin { Tag tag; }; +class EmptyRecModuleField : public ModuleFieldMixin { + public: + explicit EmptyRecModuleField(const Location& loc = Location()) + : ModuleFieldMixin(loc) {} +}; + class StartModuleField : public ModuleFieldMixin { public: explicit StartModuleField(Var start = Var(), const Location& loc = Location()) @@ -1258,6 +1384,10 @@ struct Module { Index GetFuncTypeIndex(const FuncSignature&) const; const FuncType* GetFuncType(const Var&) const; FuncType* GetFuncType(const Var&); + const StructType* GetStructType(const Var&) const; + StructType* GetStructType(const Var&); + const ArrayType* GetArrayType(const Var&) const; + ArrayType* GetArrayType(const Var&); Index GetFuncIndex(const Var&) const; const Func* GetFunc(const Var&) const; Func* GetFunc(const Var&); @@ -1292,6 +1422,7 @@ struct Module { 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); @@ -1318,6 +1449,8 @@ struct Module { std::vector imports; std::vector exports; std::vector types; + // Ordered list of recursive ranges. + std::vector recursive_ranges; std::vector tables; std::vector elem_segments; std::vector memories; diff --git a/include/wabt/opcode.def b/include/wabt/opcode.def index 901c0359a8..f79eb4ab88 100644 --- a/include/wabt/opcode.def +++ b/include/wabt/opcode.def @@ -268,10 +268,45 @@ WABT_OPCODE(___, I32, ___, I32, 0, 0xfc, 0x11, TableFill, "table.fill", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd0, RefNull, "ref.null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd1, RefIsNull, "ref.is_null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd2, RefFunc, "ref.func", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd3, RefEq, "ref.eq", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd4, RefAsNonNull, "ref.as_non_null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd5, BrOnNull, "br_on_null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd6, BrOnNonNull, "br_on_non_null", "") +/* Garbage collection opcodes */ +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x00, StructNew, "struct.new", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x01, StructNewDefault, "struct.new_default", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x02, StructGet, "struct.get", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x03, StructGetS, "struct.get_s", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x04, StructGetU, "struct.get_u", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x05, StructSet, "struct.set", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x06, ArrayNew, "array.new", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0xfb, 0x07, ArrayNewDefault, "array.new_default", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x08, ArrayNewFixed, "array.new_fixed", "") +WABT_OPCODE(___, I32, I32, ___, 0, 0xfb, 0x09, ArrayNewData, "array.new_data", "") +WABT_OPCODE(___, I32, I32, ___, 0, 0xfb, 0x0a, ArrayNewElem, "array.new_elem", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x0b, ArrayGet, "array.get", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x0c, ArrayGetS, "array.get_s", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x0d, ArrayGetU, "array.get_u", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x0e, ArraySet, "array.set", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0xfb, 0x0f, ArrayLen, "array.len", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x10, ArrayFill, "array.fill", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfb, 0x11, ArrayCopy, "array.copy", "") +WABT_OPCODE(___, ___, I32, I32, 0, 0xfb, 0x12, ArrayInitData, "array.init_data", "") +WABT_OPCODE(___, ___, I32, I32, 0, 0xfb, 0x13, ArrayInitElem, "array.init_elem", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0xfb, 0x14, RefTest, "ref.test", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0xfb, 0x15, RefTestNull, "ref.test", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x16, RefCast, "ref.cast", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x17, RefCastNull, "ref.cast", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x18, BrOnCast, "br_on_cast", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x19, BrOnCastFail, "br_on_cast_fail", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x1a, AnyConvertExtern, "any.convert_extern", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfb, 0x1b, ExternConvertAny, "extern.convert_any", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0xfb, 0x1c, RefI31, "ref.i31", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0xfb, 0x1d, I31GetS, "i31.get_s", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0xfb, 0x1e, I31GetU, "i31.get_u", "") + + /* Simd opcodes */ WABT_OPCODE(V128, I32, ___, ___, 16, 0xfd, 0x00, V128Load, "v128.load", "") WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x01, V128Load8X8S, "v128.load8x8_s", "") diff --git a/include/wabt/opcode.h b/include/wabt/opcode.h index a7c7b2495a..e63658852a 100644 --- a/include/wabt/opcode.h +++ b/include/wabt/opcode.h @@ -84,7 +84,7 @@ struct Opcode { Address GetAlignment(Address alignment) const; static bool IsPrefixByte(uint8_t byte) { - return byte == kMathPrefix || byte == kThreadsPrefix || byte == kSimdPrefix; + return byte == kMathPrefix || byte == kThreadsPrefix || byte == kGarbageCollectionPrefix || byte == kSimdPrefix; } bool IsEnabled(const Features& features) const; @@ -93,6 +93,7 @@ struct Opcode { private: static constexpr uint32_t kMathPrefix = 0xfc; static constexpr uint32_t kThreadsPrefix = 0xfe; + static constexpr uint32_t kGarbageCollectionPrefix = 0xfb; static constexpr uint32_t kSimdPrefix = 0xfd; struct Info { diff --git a/include/wabt/shared-validator.h b/include/wabt/shared-validator.h index d568aac805..f8ff897741 100644 --- a/include/wabt/shared-validator.h +++ b/include/wabt/shared-validator.h @@ -29,8 +29,6 @@ #include "wabt/opcode.h" #include "wabt/type-checker.h" -#include "wabt/binary-reader.h" // For TypeMut. - namespace wabt { struct ValidateOptions { @@ -43,7 +41,11 @@ struct ValidateOptions { class SharedValidator { public: WABT_DISALLOW_COPY_AND_ASSIGN(SharedValidator); + using TypeEntry = TypeChecker::TypeEntry; using FuncType = TypeChecker::FuncType; + using StructType = TypeChecker::StructType; + using ArrayType = TypeChecker::ArrayType; + using RecursiveRange = TypeChecker::RecursiveRange; SharedValidator(Errors*, const ValidateOptions& options); // TODO: Move into SharedValidator? @@ -63,19 +65,31 @@ class SharedValidator { Index GetLocalCount() const; + // The canonical index of a type is the index of the first type, + // which is equal to the original type. The canonical index is + // always less or equal than type_index. + Index GetCanonicalTypeIndex(Index type_index); + Result EndModule(); + Result OnRecursiveType(Index first_type_index, Index type_count); Result OnFuncType(const Location&, Index param_count, const Type* param_types, Index result_count, const Type* result_types, - Index type_index); - Result OnStructType(const Location&, Index field_count, TypeMut* fields); - Result OnArrayType(const Location&, TypeMut field); + Index type_index, + GCTypeExtension* gc_ext); + Result OnStructType(const Location&, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext); + Result OnArrayType(const Location&, + TypeMut field, + GCTypeExtension* gc_ext); Result OnFunction(const Location&, Var sig_var); - Result OnTable(const Location&, Type elem_type, const Limits&); + Result OnTable(const Location&, Type elem_type, const Limits&, bool, bool); Result OnMemory(const Location&, const Limits&, uint32_t page_size); Result OnGlobalImport(const Location&, Type type, bool mutable_); Result OnGlobal(const Location&, Type type, bool mutable_); @@ -101,6 +115,17 @@ class SharedValidator { Result EndFunctionBody(const Location&); Result OnLocalDecl(const Location&, Index count, Type type); + Result OnArrayCopy(const Location&, Var dst_type, Var src_type); + Result OnArrayFill(const Location&, Var type); + Result OnArrayGet(const Location&, Opcode, Var type); + Result OnArrayInitData(const Location&, Var type, Var segment_var); + Result OnArrayInitElem(const Location&, Var type, Var segment_var); + Result OnArrayNew(const Location&, Var type); + Result OnArrayNewData(const Location&, Var type, Var segment_var); + Result OnArrayNewDefault(const Location&, Var type); + Result OnArrayNewElem(const Location&, Var type, Var segment_var); + Result OnArrayNewFixed(const Location&, Var type, Index count); + Result OnArraySet(const Location&, Var type); Result OnAtomicFence(const Location&, uint32_t consistency_model); Result OnAtomicLoad(const Location&, Opcode, @@ -136,6 +161,10 @@ class SharedValidator { Result OnBlock(const Location&, Type sig_type); Result OnBr(const Location&, Var depth); Result OnBrIf(const Location&, Var depth); + Result OnBrOnCast(const Location&, + Opcode, Var depth, + Var type1_var, + Var type2_var); Result OnBrOnNonNull(const Location&, Var depth); Result OnBrOnNull(const Location&, Var depth); Result BeginBrTable(const Location&); @@ -154,6 +183,7 @@ class SharedValidator { Result OnElemDrop(const Location&, Var segment_var); Result OnElse(const Location&); Result OnEnd(const Location&); + Result OnGCUnary(const Location&, Opcode); Result OnGlobalGet(const Location&, Var); Result OnGlobalSet(const Location&, Var); Result OnIf(const Location&, Type sig_type); @@ -179,9 +209,11 @@ class SharedValidator { Result OnMemorySize(const Location&, Var memidx); Result OnNop(const Location&); Result OnRefAsNonNull(const Location&); + Result OnRefCast(const Location&, Var type_var); Result OnRefFunc(const Location&, Var func_var); Result OnRefIsNull(const Location&); Result OnRefNull(const Location&, Var func_type_var); + Result OnRefTest(const Location&, Var type_var); Result OnRethrow(const Location&, Var depth); Result OnReturnCall(const Location&, Var func_var); Result OnReturnCallIndirect(const Location&, Var sig_var, Var table_var); @@ -207,6 +239,10 @@ class SharedValidator { Var memidx, Address align, Address offset); + Result OnStructGet(const Location&, Opcode, Var type, Var field); + Result OnStructNew(const Location&, Var type); + Result OnStructNewDefault(const Location&, Var type); + Result OnStructSet(const Location&, Var type, Var field); Result OnTableCopy(const Location&, Var dst_var, Var src_var); Result OnTableFill(const Location&, Var table_var); Result OnTableGet(const Location&, Var table_var); @@ -225,20 +261,6 @@ class SharedValidator { Result OnUnreachable(const Location&); private: - struct StructType { - StructType() = default; - StructType(const TypeMutVector& fields) : fields(fields) {} - - TypeMutVector fields; - }; - - struct ArrayType { - ArrayType() = default; - ArrayType(TypeMut field) : field(field) {} - - TypeMut field; - }; - struct TableType { TableType() = default; TableType(Type element, Limits limits) : element(element), limits(limits) {} @@ -292,7 +314,11 @@ class SharedValidator { Type actual, Type expected, const char* desc); - Result CheckReferenceType(const Location&, Type type, const char* desc); + Result CheckReferenceType(const Location&, + Type type, + Index end_index, + const char* desc); + Result CheckGCTypeExtension(const Location&, GCTypeExtension* gc_ext); Result CheckLimits(const Location&, const Limits&, uint64_t absolute_max, @@ -308,7 +334,9 @@ class SharedValidator { const std::vector& values, T* out, const char* desc); - Result CheckFuncTypeIndex(Var sig_var, FuncType* out = nullptr); + Result CheckFuncTypeIndex(Var sig_var, FuncType* out); + Result CheckStructTypeIndex(Var type_var, Type* out_ref, StructType* out); + Result CheckArrayTypeIndex(Var type_var, Type* out_ref, TypeMut* out); Result CheckFuncIndex(Var func_var, FuncType* out = nullptr); Result CheckTableIndex(Var table_var, TableType* out = nullptr); Result CheckMemoryIndex(Var memory_var, MemoryType* out = nullptr); @@ -337,6 +365,8 @@ class SharedValidator { void RestoreLocalRefs(Result result); void IgnoreLocalRefs(); + Index GetEndIndex(); + ValidateOptions options_; Errors* errors_; TypeChecker typechecker_; // TODO: Move into SharedValidator. @@ -344,10 +374,7 @@ class SharedValidator { Location expr_loc_ = Location(kInvalidOffset); bool in_init_expr_ = false; - Index num_types_ = 0; - std::map func_types_; - std::map struct_types_; - std::map array_types_; + TypeChecker::TypeFields type_fields_; std::vector funcs_; // Includes imported and defined. std::vector tables_; // Includes imported and defined. @@ -358,6 +385,9 @@ class SharedValidator { Index starts_ = 0; Index num_imported_globals_ = 0; Index data_segments_ = 0; + Index last_rec_type_end_ = 0; + // Recursive type checks may enter to infinite loop for invalid values. + Result type_validation_result_ = Result::Ok; // Includes parameters, since this is only used for validating // local.{get,set,tee} instructions. @@ -372,4 +402,4 @@ class SharedValidator { } // namespace wabt -#endif // WABT_SHARED_VALIDATOR_H_ +#endif // WABT_SHARED_VALIDATOR_H_ \ No newline at end of file diff --git a/include/wabt/token.def b/include/wabt/token.def index 22b333cb73..1bf382a66a 100644 --- a/include/wabt/token.def +++ b/include/wabt/token.def @@ -21,7 +21,6 @@ /* Tokens with no additional data (i.e. bare). */ WABT_TOKEN(Invalid, "Invalid") WABT_TOKEN(After, "after") -WABT_TOKEN(Array, "array") WABT_TOKEN(AssertException, "assert_exception") WABT_TOKEN(AssertExhaustion, "assert_exhaustion") WABT_TOKEN(AssertInvalid, "assert_invalid") @@ -42,6 +41,7 @@ WABT_TOKEN(Eof, "EOF") WABT_TOKEN(Tag, "tag") WABT_TOKEN(Export, "export") WABT_TOKEN(Field, "field") +WABT_TOKEN(Final, "final") WABT_TOKEN(Function, "function") WABT_TOKEN(Get, "get") WABT_TOKEN(Global, "global") @@ -61,13 +61,17 @@ WABT_TOKEN(Output, "output") WABT_TOKEN(PageSize, "pagesize") WABT_TOKEN(Param, "param") WABT_TOKEN(Ref, "ref") +WABT_TOKEN(RefArray, "ref.array") +WABT_TOKEN(RefHost, "ref.host") +WABT_TOKEN(RefStruct, "ref.struct") WABT_TOKEN(Quote, "quote") +WABT_TOKEN(Rec, "rec") WABT_TOKEN(Register, "register") WABT_TOKEN(Result, "result") WABT_TOKEN(Rpar, ")") WABT_TOKEN(Shared, "shared") WABT_TOKEN(Start, "start") -WABT_TOKEN(Struct, "struct") +WABT_TOKEN(Sub, "sub") WABT_TOKEN(Table, "table") WABT_TOKEN(Then, "then") WABT_TOKEN(Type, "type") @@ -88,6 +92,19 @@ WABT_TOKEN_FIRST(Literal, Float) WABT_TOKEN_LAST(Literal, Nat) /* Tokens with Opcode data. */ +WABT_TOKEN(ArrayCopy, "array.copy") +WABT_TOKEN(ArrayFill, "array.fill") +WABT_TOKEN(ArrayGet, "array.get") +WABT_TOKEN(ArrayGetS, "array.get_s") +WABT_TOKEN(ArrayGetU, "array.get_u") +WABT_TOKEN(ArrayInitData, "array.init_data") +WABT_TOKEN(ArrayInitElem, "array.init_elem") +WABT_TOKEN(ArrayNew, "array.new") +WABT_TOKEN(ArrayNewData, "array.new_data") +WABT_TOKEN(ArrayNewDefault, "array.new_default") +WABT_TOKEN(ArrayNewFixed, "array.new_fixed") +WABT_TOKEN(ArrayNewElem, "array.new_elem") +WABT_TOKEN(ArraySet, "array.set") WABT_TOKEN(AtomicFence, "atomic.fence") WABT_TOKEN(AtomicLoad, "ATOMIC_LOAD") WABT_TOKEN(AtomicNotify, "ATOMIC_NOTIFY") @@ -99,6 +116,7 @@ WABT_TOKEN(Binary, "BINARY") WABT_TOKEN(Block, "block") WABT_TOKEN(Br, "br") WABT_TOKEN(BrIf, "br_if") +WABT_TOKEN(BrOnCast, "br_on_cast") WABT_TOKEN(BrOnNonNull, "br_on_non_null") WABT_TOKEN(BrOnNull, "br_on_null") WABT_TOKEN(BrTable, "br_table") @@ -119,6 +137,7 @@ WABT_TOKEN(Drop, "drop") WABT_TOKEN(ElemDrop, "elem.drop") WABT_TOKEN(Else, "else") WABT_TOKEN(End, "end") +WABT_TOKEN(GCUnary, "GC_UNARY") WABT_TOKEN(GlobalGet, "global.get") WABT_TOKEN(GlobalSet, "global.set") WABT_TOKEN(If, "if") @@ -134,10 +153,14 @@ WABT_TOKEN(MemoryInit, "memory.init") WABT_TOKEN(MemorySize, "memory.size") WABT_TOKEN(Nop, "nop") WABT_TOKEN(RefAsNonNull, "ref.as_non_null") +WABT_TOKEN(RefCast, "ref.cast") +WABT_TOKEN(RefEq, "ref.eq") WABT_TOKEN(RefExtern, "ref.extern") WABT_TOKEN(RefFunc, "ref.func") +WABT_TOKEN(RefI31, "ref.i31") WABT_TOKEN(RefIsNull, "ref.is_null") WABT_TOKEN(RefNull, "ref.null") +WABT_TOKEN(RefTest, "ref.test") WABT_TOKEN(Rethrow, "rethrow") WABT_TOKEN(ReturnCallIndirect, "return_call_indirect") WABT_TOKEN(ReturnCall, "return_call") @@ -149,6 +172,12 @@ WABT_TOKEN(SimdLoadLane, "SIMDLOADLANE") WABT_TOKEN(SimdStoreLane, "SIMDSTORELANE") WABT_TOKEN(SimdShuffleOp, "i8x16.shuffle") WABT_TOKEN(Store, "STORE") +WABT_TOKEN(StructGet, "struct.get") +WABT_TOKEN(StructGetS, "struct.get_s") +WABT_TOKEN(StructGetU, "struct.get_u") +WABT_TOKEN(StructNew, "struct.new") +WABT_TOKEN(StructNewDefault, "struct.new_default") +WABT_TOKEN(StructSet, "struct.set") WABT_TOKEN(TableCopy, "table.copy") WABT_TOKEN(TableFill, "table.fill") WABT_TOKEN(TableGet, "table.get") @@ -163,7 +192,7 @@ WABT_TOKEN(Try, "try") WABT_TOKEN(TryTable, "try_table") WABT_TOKEN(Unary, "UNARY") WABT_TOKEN(Unreachable, "unreachable") -WABT_TOKEN_FIRST(Opcode, AtomicFence) +WABT_TOKEN_FIRST(Opcode, ArrayCopy) WABT_TOKEN_LAST(Opcode, Unreachable) /* Tokens with string data. */ @@ -182,8 +211,16 @@ WABT_TOKEN_FIRST(Type, ValueType) WABT_TOKEN_LAST(Type, ValueType) /* Tokens with Type data, but are reference kinds. */ +WABT_TOKEN(Any, "any") +WABT_TOKEN(Array, "array") WABT_TOKEN(Func, "func") +WABT_TOKEN(Eq, "eq") WABT_TOKEN(Extern, "extern") WABT_TOKEN(Exn, "exn") -WABT_TOKEN_FIRST(RefKind, Func) -WABT_TOKEN_LAST(RefKind, Exn) +WABT_TOKEN(I31, "i31") +WABT_TOKEN(NoExtern, "noextern") +WABT_TOKEN(NoFunc, "nofunc") +WABT_TOKEN(None, "none") +WABT_TOKEN(Struct, "struct") +WABT_TOKEN_FIRST(RefKind, Any) +WABT_TOKEN_LAST(RefKind, Struct) \ No newline at end of file diff --git a/include/wabt/type-checker.h b/include/wabt/type-checker.h index 3600212a83..121835e52b 100644 --- a/include/wabt/type-checker.h +++ b/include/wabt/type-checker.h @@ -26,12 +26,33 @@ #include "wabt/feature.h" #include "wabt/opcode.h" +#include "wabt/binary-reader.h" // For TypeMut. + namespace wabt { class TypeChecker { public: using ErrorCallback = std::function; + struct TypeEntry { + explicit TypeEntry(Type::Enum kind, Index map_index, Index canonical_index) + : kind(kind), + map_index(map_index), + canonical_index(canonical_index), + is_final_sub_type(true), + first_sub_type(kInvalidIndex) { + assert(kind == Type::FuncRef || kind == Type::StructRef || + kind == Type::ArrayRef); + } + + Type::Enum kind; + Index map_index; + Index canonical_index; + bool is_final_sub_type; + // Currently the sub type list is limited to maximum 1 value. + Index first_sub_type; + }; + struct FuncType { FuncType() = default; FuncType(const TypeVector& params, @@ -44,6 +65,72 @@ class TypeChecker { Index type_index; }; + struct StructType { + StructType() = default; + StructType(const TypeMutVector& fields) : fields(fields) {} + + TypeMutVector fields; + }; + + struct ArrayType { + ArrayType() = default; + ArrayType(TypeMut field) : field(field) {} + + TypeMut field; + }; + + struct RecursiveRange { + RecursiveRange(Index start_index, Index type_count) + : start_index(start_index), type_count(type_count), hash(0) {} + + Index start_index; + Index type_count; + uint32_t hash; + }; + + struct TypeFields { + Index NumTypes() { + return static_cast(type_entries.size()); + } + + void PushFunc(FuncType&& func_type) { + Index map_index = static_cast(func_types.size()); + type_entries.emplace_back(TypeEntry(Type::FuncRef, map_index, + NumTypes())); + func_types.emplace_back(func_type); + } + + void PushStruct(StructType&& struct_type) { + Index map_index = static_cast(struct_types.size()); + type_entries.emplace_back(TypeEntry(Type::StructRef, map_index, + NumTypes())); + struct_types.emplace_back(struct_type); + } + + void PushArray(ArrayType&& array_type) { + Index map_index = static_cast(array_types.size()); + type_entries.emplace_back(TypeEntry(Type::ArrayRef, map_index, + NumTypes())); + array_types.emplace_back(array_type); + } + + Type GetGenericType(Type type) { + if (type.IsReferenceWithIndex()) { + return Type(type_entries[type.GetReferenceIndex()].kind, + type == Type::RefNull); + } + return type; + } + + Type GetGroupType(Type type); + + std::vector type_entries; + std::vector func_types; + std::vector struct_types; + std::vector array_types; + std::vector recursive_ranges; + }; + struct Label { Label(LabelType, const TypeVector& param_types, @@ -62,8 +149,8 @@ class TypeChecker { std::vector local_ref_is_set_; }; - explicit TypeChecker(const Features& features, std::map& func_types) - : features_(features), func_types_(func_types) {} + explicit TypeChecker(const Features& features, TypeFields& type_fields) + : features_(features), type_fields_(type_fields) {} void set_error_callback(const ErrorCallback& error_callback) { error_callback_ = error_callback; @@ -77,6 +164,24 @@ class TypeChecker { Result GetCatchCount(Index depth, Index* out_depth); Result BeginFunction(const TypeVector& sig); + Result OnArrayCopy(Type dst_ref_type, + TypeMut& dst_array_type, + Type src_ref_type, + Type src_array_type); + Result OnArrayFill(Type ref_type, TypeMut& array_type); + Result OnArrayGet(Opcode, Type ref_type, Type array_type); + Result OnArrayInitData(Type ref_type, TypeMut& array_type); + Result OnArrayInitElem(Type ref_type, TypeMut& array_type, Type elem_type); + Result OnArrayNew(Type ref_type, Type array_type); + Result OnArrayNewData(Type ref_type, Type array_type); + Result OnArrayNewDefault(Type ref_type); + Result OnArrayNewElem(Type ref_type, + Type array_type, + Type elem_type); + Result OnArrayNewFixed(Type ref_type, + Type array_type, + Index count); + Result OnArraySet(Type ref_type, const TypeMut& field); Result OnAtomicFence(uint32_t consistency_model); Result OnAtomicLoad(Opcode, const Limits& limits); Result OnAtomicNotify(Opcode, const Limits& limits); @@ -88,6 +193,7 @@ class TypeChecker { Result OnBlock(const TypeVector& param_types, const TypeVector& result_types); Result OnBr(Index depth); Result OnBrIf(Index depth); + Result OnBrOnCast(Opcode opcode, Index depth, Type type1, Type type2); Result OnBrOnNonNull(Index depth); Result OnBrOnNull(Index depth); Result BeginBrTable(); @@ -111,6 +217,7 @@ class TypeChecker { Result OnDrop(); Result OnElse(); Result OnEnd(); + Result OnGCUnary(Opcode); Result OnGlobalGet(Type); Result OnGlobalSet(Type); Result OnIf(const TypeVector& param_types, const TypeVector& result_types); @@ -135,8 +242,10 @@ class TypeChecker { Result OnTableFill(Type elem_type, const Limits& limits); Result OnRefFuncExpr(Index func_type); Result OnRefAsNonNullExpr(); + Result OnRefCast(Type type); Result OnRefNullExpr(Type type); Result OnRefIsNullExpr(); + Result OnRefTest(Type type); Result OnRethrow(Index depth); Result OnReturn(); Result OnSelect(const TypeVector& result_types); @@ -145,6 +254,10 @@ class TypeChecker { Result OnSimdStoreLane(Opcode, const Limits& limits, uint64_t); Result OnSimdShuffleOp(Opcode, v128); Result OnStore(Opcode, const Limits& limits); + Result OnStructGet(Opcode, Type ref_type, const StructType&, Index field); + Result OnStructNew(Type ref_type, const StructType&); + Result OnStructNewDefault(Type ref_type); + Result OnStructSet(Type ref_type, const StructType&, Index field); Result OnTernary(Opcode); Result OnThrow(const TypeVector& sig); Result OnThrowRef(); @@ -160,6 +273,12 @@ class TypeChecker { Result BeginInitExpr(Type type); Result EndInitExpr(); + uint32_t UpdateHash(uint32_t hash, Index type_index, Index rec_start); + bool CheckTypeFields(Index actual, + Index actual_rec_start, + Index expected, + Index expected_rec_start, + bool is_equal); Result CheckType(Type actual, Type expected); private: @@ -197,6 +316,17 @@ class TypeChecker { Type expected2, Type expected3, const char* desc); + Result PopAndCheck4Types(Type expected1, + Type expected2, + Type expected3, + Type expected4, + const char* desc); + Result PopAndCheck5Types(Type expected1, + Type expected2, + Type expected3, + Type expected4, + Type expected5, + const char* desc); Result PopAndCheckReference(Type* actual, const char* desc); Result CheckOpcode1(Opcode opcode, const Limits* limits = nullptr); Result CheckOpcode2(Opcode opcode, const Limits* limits = nullptr); @@ -206,6 +336,25 @@ class TypeChecker { const Limits* limits3 = nullptr); Result OnEnd(Label* label, const char* sig_desc, const char* end_desc); + static uint32_t ComputeHash(uint32_t hash, Index value) { + // Shift-Add-XOR hash + return hash ^ ((hash << 5) + (hash >> 2) + value); + } + + static Type ToUnpackedType(Type type) { + if (type.IsPackedType()) { + return Type::I32; + } + return type; + } + + uint32_t ComputeHash(uint32_t hash, Type& type, Index rec_start); + bool CompareType(Type actual, + Index actual_rec_start, + Type expected, + Index expected_rec_start, + bool is_equal); + template void PrintStackIfFailed(Result result, const char* desc, Args... args) { // Assert all args are Type or Type::Enum. If it's a TypeVector then @@ -230,9 +379,9 @@ class TypeChecker { // to represent "any". TypeVector* br_table_sig_ = nullptr; Features features_; - std::map& func_types_; + TypeFields& type_fields_; }; } // namespace wabt -#endif /* WABT_TYPE_CHECKER_H_ */ +#endif /* WABT_TYPE_CHECKER_H_ */ \ No newline at end of file diff --git a/include/wabt/type.h b/include/wabt/type.h index 8ef73112db..0a70951f85 100644 --- a/include/wabt/type.h +++ b/include/wabt/type.h @@ -40,17 +40,27 @@ class Type { F32 = -0x03, // 0x7d F64 = -0x04, // 0x7c V128 = -0x05, // 0x7b - I8 = -0x06, // 0x7a : packed-type only, used in gc and as v128 lane - I16 = -0x07, // 0x79 : packed-type only, used in gc and as v128 lane - ExnRef = -0x17, // 0x69 + I8 = -0x08, // 0x78 : packed-type only, used in gc and as v128 lane + I16 = -0x09, // 0x77 : packed-type only, used in gc and as v128 lane + NullFuncRef = -0x0d, // 0x73 + NullExternRef = -0x0e, // 0x72 + NullRef = -0x0f, // 0x71 FuncRef = -0x10, // 0x70 ExternRef = -0x11, // 0x6f - Reference = -0x15, // 0x6b + AnyRef = -0x12, // 0x6e + EqRef = -0x13, // 0x6d + I31Ref = -0x14, // 0x6c + StructRef = -0x15, // 0x6b + ArrayRef = -0x16, // 0x6a + ExnRef = -0x17, // 0x69 Ref = -0x1c, // 0x64 RefNull = -0x1d, // 0x63 Func = -0x20, // 0x60 Struct = -0x21, // 0x5f Array = -0x22, // 0x5e + Sub = -0x30, // 0x50 + SubFinal = -0x31, // 0x4f + Rec = -0x32, // 0x4e Void = -0x40, // 0x40 ___ = Void, // Convenient for the opcode table in opcode.h @@ -78,6 +88,10 @@ class Type { assert(IsReferenceWithIndex() || (IsNonTypedRef() && (type_index_ == ReferenceOrNull || type_index_ == ReferenceNonNull))); } + Type(Enum e, bool is_nullable) + : enum_(e), type_index_(is_nullable ? ReferenceOrNull : ReferenceNonNull) { + assert(IsNonTypedRef()); + } constexpr operator Enum() const { return enum_; } @@ -99,20 +113,23 @@ class Type { } bool IsRef() const { - return enum_ == Type::ExternRef || enum_ == Type::FuncRef || - enum_ == Type::Reference || enum_ == Type::ExnRef || - enum_ == Type::RefNull || enum_ == Type::Ref; + return enum_ == Type::NullFuncRef || enum_ == Type::NullExternRef || + enum_ == Type::NullRef || enum_ == Type::FuncRef || + enum_ == Type::ExternRef || enum_ == Type::AnyRef || + enum_ == Type::EqRef || enum_ == Type::I31Ref || + enum_ == Type::StructRef || enum_ == Type::ArrayRef || + enum_ == Type::ExnRef || enum_ == Type::Ref || + enum_ == Type::RefNull; } bool IsNullableRef() const { - return enum_ == Type::Reference || enum_ == Type::ExnRef || - enum_ == Type::RefNull || - ((enum_ == Type::ExternRef || enum_ == Type::FuncRef) && type_index_ == ReferenceOrNull); + return enum_ == Type::ExnRef || enum_ == Type::RefNull || + (EnumIsNonTypedRef(enum_) && type_index_ == ReferenceOrNull); } bool IsNonNullableRef() const { return enum_ == Type::Ref || - ((enum_ == Type::ExternRef || enum_ == Type::FuncRef) && type_index_ != ReferenceOrNull); + (EnumIsNonTypedRef(enum_) && type_index_ != ReferenceOrNull); } bool IsReferenceWithIndex() const { return EnumIsReferenceWithIndex(enum_); } @@ -137,13 +154,33 @@ class Type { case Type::I16: return "i16"; case Type::ExnRef: return "exnref"; case Type::Func: return "func"; + case Type::Struct: return "struct"; + case Type::Array: return "array"; case Type::Void: return "void"; case Type::Any: return "any"; + case Type::NullFuncRef: + return type_index_ == ReferenceOrNull ? "nullfuncref" : "(ref nofunc)"; + case Type::NullExternRef: + return type_index_ == ReferenceOrNull ? "nullexternref" : "(ref noextern)"; + case Type::NullRef: + if (type_index_ == kBottomRef) { + return "(ref something)"; + } + return type_index_ == ReferenceOrNull ? "nullref" : "(ref none)"; case Type::FuncRef: return type_index_ == ReferenceOrNull ? "funcref" : "(ref func)"; case Type::ExternRef: return type_index_ == ReferenceOrNull ? "externref" : "(ref extern)"; - case Type::Reference: + case Type::AnyRef: + return type_index_ == ReferenceOrNull ? "anyref" : "(ref any)"; + case Type::EqRef: + return type_index_ == ReferenceOrNull ? "eqref" : "(ref eq)"; + case Type::I31Ref: + return type_index_ == ReferenceOrNull ? "i31ref" : "(ref i31)"; + case Type::StructRef: + return type_index_ == ReferenceOrNull ? "structref" : "(ref struct)"; + case Type::ArrayRef: + return type_index_ == ReferenceOrNull ? "arrayref" : "(ref array)"; case Type::Ref: return StringPrintf("(ref %d)", type_index_); case Type::RefNull: @@ -155,12 +192,19 @@ class Type { const char* GetRefKindName() const { switch (enum_) { - case Type::FuncRef: return "func"; - case Type::ExternRef: return "extern"; - case Type::ExnRef: return "exn"; - case Type::Struct: return "struct"; - case Type::Array: return "array"; - default: return ""; + case Type::NullFuncRef: return "nofunc"; + case Type::NullExternRef: return "noextern"; + case Type::NullRef: + return (type_index_ == kBottomRef) ? "something" : "none"; + case Type::FuncRef: return "func"; + case Type::ExternRef: return "extern"; + case Type::ExnRef: return "exn"; + case Type::AnyRef: return "any"; + case Type::EqRef: return "eq"; + case Type::I31Ref: return "i31"; + case Type::Struct: return "struct"; + case Type::Array: return "array"; + default: return ""; } } @@ -188,6 +232,10 @@ class Type { return type_index_; } + bool IsPackedType() const { + return enum_ == Type::I8 || enum_ == Type::I16; + } + TypeVector GetInlineVector() const { assert(!IsIndex()); switch (enum_) { @@ -199,10 +247,17 @@ class Type { case Type::F32: case Type::F64: case Type::V128: + case Type::NullFuncRef: + case Type::NullExternRef: + case Type::NullRef: case Type::FuncRef: - case Type::ExnRef: case Type::ExternRef: - case Type::Reference: + case Type::AnyRef: + case Type::EqRef: + case Type::I31Ref: + case Type::StructRef: + case Type::ArrayRef: + case Type::ExnRef: case Type::Ref: case Type::RefNull: return TypeVector(this, this + 1); @@ -213,15 +268,47 @@ class Type { } static bool EnumIsReferenceWithIndex(Enum value) { - return value == Type::Reference || value == Type::Ref || - value == Type::RefNull; + return value == Type::Ref || value == Type::RefNull; + } + + static bool EnumIsNonTypedGCRef(Enum value) { + return value == Type::NullFuncRef || value == Type::NullExternRef || + value == Type::NullRef || value == Type::AnyRef || + value == Type::EqRef || value == Type::I31Ref || + value == Type::StructRef || value == Type::ArrayRef; } static bool EnumIsNonTypedRef(Enum value) { - return value == Type::ExternRef || value == Type::FuncRef; + return value == Type::ExternRef || value == Type::FuncRef || + value == Type::ExnRef || EnumIsNonTypedGCRef(value); + } + + // Bottom references are only used by the shared + // validator. It represents an unknown reference. + // Nullable property is not defined for this type. + static Type BottomRef() { + Type type(NullRef); + type.type_index_ = kBottomRef; + return type; + } + + bool IsBottomRef() const { + return enum_ == NullRef && type_index_ == kBottomRef; + } + + void ConvertRefNullToRef() { + if (IsReferenceWithIndex()) { + enum_ = Type::Ref; + } else { + assert(IsNonTypedRef()); + type_index_ |= ReferenceNonNull; + } } private: + // Special value representing an unknown reference. + static const uint32_t kBottomRef = 0x2 | ReferenceNonNull; + Enum enum_; // This index is 0 for non-references, so a zeroed // memory area represents a valid Type::Any type. @@ -231,4 +318,4 @@ class Type { } // namespace wabt -#endif // WABT_TYPE_H_ +#endif // WABT_TYPE_H_ \ No newline at end of file diff --git a/include/wabt/wast-parser.h b/include/wabt/wast-parser.h index ad1c935da3..0490ed0728 100644 --- a/include/wabt/wast-parser.h +++ b/include/wabt/wast-parser.h @@ -91,6 +91,14 @@ class WastParser { ReferenceVars vars; }; + struct ResolveField { + ResolveField(StructType* target_struct) + : target_struct(target_struct) {} + + StructType* target_struct; + ReferenceVars vars; + }; + void ErrorUnlessOpcodeEnabled(const Token&); // Print an error message listing the expected tokens, as well as an example @@ -171,7 +179,7 @@ class WastParser { bool ParseElemExprListOpt(ExprListVector* out_list); bool ParseElemExprVarListOpt(ExprListVector* out_list); Result ParseRefDeclaration(Var* out_type); - Result ParseValueType(Var* out_type); + Result ParseValueType(Var* out_type, bool is_field = false); Result ParseValueTypeList( TypeVector* out_type_list, ReferenceVars* type_vars); @@ -190,6 +198,8 @@ class WastParser { static Result ResolveTargetRefType(const Module&, Type*, const Var&, Errors*); static Result ResolveTargetTypeVector(const Module&, TypeVector*, ReferenceVars*, Errors*); + static Result ResolveTargetFieldVector(const Module&, StructType*, + ReferenceVars*, Errors* errors); Result ParseModuleFieldList(Module*); Result ParseModuleField(Module*); Result ParseDataModuleField(Module*); @@ -198,6 +208,7 @@ class WastParser { Result ParseExportModuleField(Module*); Result ParseFuncModuleField(Module*); Result ParseTypeModuleField(Module*); + Result ParseRecTypeModuleField(Module*); Result ParseGlobalModuleField(Module*); Result ParseImportModuleField(Module*); Result ParseMemoryModuleField(Module*); @@ -248,11 +259,13 @@ class WastParser { Result ParseCatchExprList(CatchVector* catches); Result ParseGlobalType(Global*); Result ParseField(Field*); - Result ParseFieldList(std::vector*); + Result ParseFieldList(StructType*); template Result ParsePlainInstrVar(Location, std::unique_ptr*); template + Result ParsePlainInstrVarVar(Location, std::unique_ptr*); + template Result ParseMemoryInstrVar(Location, std::unique_ptr*); template Result ParseLoadStoreInstr(Location, Token, std::unique_ptr*); @@ -322,6 +335,11 @@ class WastParser { // following vector. At least one reference must be present for each vector. std::vector resolve_funcs_; + // Structure fields and their corresponding references are + // stored in the following vector. At least one reference + // must be present for each structure. + std::vector resolve_fields_; + // two-element queue of upcoming tokens class TokenQueue { std::array, 2> tokens{}; @@ -351,4 +369,4 @@ Result ParseWastScript(WastLexer* lexer, } // namespace wabt -#endif /* WABT_WAST_PARSER_H_ */ +#endif /* WABT_WAST_PARSER_H_ */ \ No newline at end of file diff --git a/src/apply-names.cc b/src/apply-names.cc index 3d25749ccd..3abad37c2a 100644 --- a/src/apply-names.cc +++ b/src/apply-names.cc @@ -102,6 +102,7 @@ class NameApplier : public ExprVisitor::DelegateNop { Result VisitGlobal(Global* global); Result VisitTag(Tag* tag); Result VisitExport(Index export_index, Export* export_); + Result VisitTable(Table* table); Result VisitElemSegment(Index elem_segment_index, ElemSegment* segment); Result VisitDataSegment(Index data_segment_index, DataSegment* segment); Result VisitStart(Var* start_var); @@ -559,6 +560,11 @@ Result NameApplier::VisitExport(Index export_index, Export* export_) { return Result::Ok; } +Result NameApplier::VisitTable(Table* table) { + CHECK_RESULT(visitor_.VisitExprList(table->init_expr)); + return Result::Ok; +} + Result NameApplier::VisitElemSegment(Index elem_segment_index, ElemSegment* segment) { CHECK_RESULT(UseNameForTableVar(&segment->table_var)); @@ -594,6 +600,8 @@ Result NameApplier::VisitModule(Module* module) { CHECK_RESULT(VisitTag(module->tags[i])); for (size_t i = 0; i < module->exports.size(); ++i) CHECK_RESULT(VisitExport(i, module->exports[i])); + for (size_t i = 0; i < module->tables.size(); ++i) + CHECK_RESULT(VisitTable(module->tables[i])); for (size_t i = 0; i < module->elem_segments.size(); ++i) CHECK_RESULT(VisitElemSegment(i, module->elem_segments[i])); for (size_t i = 0; i < module->data_segments.size(); ++i) @@ -611,4 +619,4 @@ Result ApplyNames(Module* module) { return applier.VisitModule(module); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/binary-reader-ir.cc b/src/binary-reader-ir.cc index bf5c01201d..4e531a9f00 100644 --- a/src/binary-reader-ir.cc +++ b/src/binary-reader-ir.cc @@ -102,13 +102,20 @@ class BinaryReaderIR : public BinaryReaderNop { bool OnError(const Error&) override; Result OnTypeCount(Index count) override; + Result OnRecursiveType(Index first_type_index, Index type_count) override; Result OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) override; - Result OnStructType(Index index, Index field_count, TypeMut* fields) override; - Result OnArrayType(Index index, TypeMut field) override; + Type* result_types, + GCTypeExtension* gc_ext) override; + Result OnStructType(Index index, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext) override; + Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) override; Result OnImportCount(Index count) override; Result OnImportFunc(Index import_index, @@ -144,9 +151,12 @@ class BinaryReaderIR : public BinaryReaderNop { Result OnFunction(Index index, Index sig_index) override; Result OnTableCount(Index count) override; - Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) override; + Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool) override; + Result BeginTableInitExpr(Index index) override; + Result EndTableInitExpr(Index index) override; Result OnMemoryCount(Index count) override; Result OnMemory(Index index, @@ -171,6 +181,17 @@ class BinaryReaderIR : public BinaryReaderNop { Result OnLocalDecl(Index decl_index, Index count, Type type) override; Result OnOpcode(Opcode opcode) override; + Result OnArrayCopyExpr(Index dst_type_index, Index src_type_index) override; + Result OnArrayFillExpr(Index type_index) override; + Result OnArrayGetExpr(Opcode opcode, Index type_index) override; + Result OnArrayInitDataExpr(Index type_index, Index data_index) override; + Result OnArrayInitElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewExpr(Index type_index) override; + Result OnArrayNewDataExpr(Index type_index, Index data_index) override; + Result OnArrayNewDefaultExpr(Index type_index) override; + Result OnArrayNewElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewFixedExpr(Index type_index, Index count) override; + Result OnArraySetExpr(Index type_index) override; Result OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -200,6 +221,10 @@ class BinaryReaderIR : public BinaryReaderNop { Result OnBlockExpr(Type sig_type) override; Result OnBrExpr(Index depth) override; Result OnBrIfExpr(Index depth) override; + Result OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) override; Result OnBrOnNonNullExpr(Index depth) override; Result OnBrOnNullExpr(Index depth) override; Result OnBrTableExpr(Index num_targets, @@ -222,6 +247,7 @@ class BinaryReaderIR : public BinaryReaderNop { Result OnF32ConstExpr(uint32_t value_bits) override; Result OnF64ConstExpr(uint64_t value_bits) override; Result OnV128ConstExpr(v128 value_bits) override; + Result OnGCUnaryExpr(Opcode opcode) override; Result OnGlobalGetExpr(Index global_index) override; Result OnGlobalSetExpr(Index global_index) override; Result OnI32ConstExpr(uint32_t value) override; @@ -250,9 +276,11 @@ class BinaryReaderIR : public BinaryReaderNop { Result OnTableSizeExpr(Index table_index) override; Result OnTableFillExpr(Index table_index) override; Result OnRefAsNonNullExpr() override; + Result OnRefCastExpr(Type type) override; Result OnRefFuncExpr(Index func_index) override; Result OnRefNullExpr(Type type) override; Result OnRefIsNullExpr() override; + Result OnRefTestExpr(Type type) override; Result OnNopExpr() override; Result OnRethrowExpr(Index depth) override; Result OnReturnExpr() override; @@ -261,6 +289,12 @@ class BinaryReaderIR : public BinaryReaderNop { Index memidx, Address alignment_log2, Address offset) override; + Result OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) override; + Result OnStructNewExpr(Index type_index) override; + Result OnStructNewDefaultExpr(Index type_index) override; + Result OnStructSetExpr(Index type_index, Index field_index) override; Result OnThrowExpr(Index tag_index) override; Result OnThrowRefExpr() override; Result OnTryExpr(Type sig_type) override; @@ -520,11 +554,24 @@ Result BinaryReaderIR::OnTypeCount(Index count) { return Result::Ok; } +Result BinaryReaderIR::OnRecursiveType(Index first_type_index, + Index type_count) { + if (type_count == 0) { + auto field = std::make_unique(GetLocation()); + module_->AppendField(std::move(field)); + } else if (type_count > 1) { + module_->recursive_ranges.push_back( + RecursiveRange{first_type_index, type_count}); + } + return Result::Ok; +} + Result BinaryReaderIR::OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) { + Type* result_types, + GCTypeExtension* gc_ext) { if (param_count > kMaxFunctionParams) { PrintError("FuncType param count exceeds maximum value"); return Result::Error; @@ -536,7 +583,8 @@ Result BinaryReaderIR::OnFuncType(Index index, } auto field = std::make_unique(GetLocation()); - auto func_type = std::make_unique(); + auto func_type = std::make_unique(gc_ext->is_final_sub_type); + func_type->gc_ext.InitSubTypes(gc_ext->sub_types, gc_ext->sub_type_count); func_type->sig.param_types.assign(param_types, param_types + param_count); func_type->sig.result_types.assign(result_types, result_types + result_count); @@ -562,9 +610,11 @@ Result BinaryReaderIR::OnFuncType(Index index, Result BinaryReaderIR::OnStructType(Index index, Index field_count, - TypeMut* fields) { + TypeMut* fields, + GCTypeExtension* gc_ext) { auto field = std::make_unique(GetLocation()); - auto struct_type = std::make_unique(); + auto struct_type = std::make_unique(gc_ext->is_final_sub_type); + struct_type->gc_ext.InitSubTypes(gc_ext->sub_types, gc_ext->sub_type_count); struct_type->fields.resize(field_count); for (Index i = 0; i < field_count; ++i) { struct_type->fields[i].type = fields[i].type; @@ -577,9 +627,12 @@ Result BinaryReaderIR::OnStructType(Index index, return Result::Ok; } -Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) { +Result BinaryReaderIR::OnArrayType(Index index, + TypeMut type_mut, + GCTypeExtension* gc_ext) { auto field = std::make_unique(GetLocation()); - auto array_type = std::make_unique(); + auto array_type = std::make_unique(gc_ext->is_final_sub_type); + array_type->gc_ext.InitSubTypes(gc_ext->sub_types, gc_ext->sub_type_count); array_type->field.type = type_mut.type; array_type->field.mutable_ = type_mut.mutable_; module_->features_used.simd |= (type_mut.type == Type::V128); @@ -700,9 +753,10 @@ Result BinaryReaderIR::OnTableCount(Index count) { return Result::Ok; } -Result BinaryReaderIR::OnTable(Index index, - Type elem_type, - const Limits* elem_limits) { +Result BinaryReaderIR::BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool) { auto field = std::make_unique(GetLocation()); Table& table = field->table; table.elem_limits = *elem_limits; @@ -712,6 +766,16 @@ Result BinaryReaderIR::OnTable(Index index, return Result::Ok; } +Result BinaryReaderIR::BeginTableInitExpr(Index index) { + assert(index == module_->tables.size() - 1); + Table* table = module_->tables[index]; + return BeginInitExpr(&table->init_expr); +} + +Result BinaryReaderIR::EndTableInitExpr(Index index) { + return EndInitExpr(); +} + Result BinaryReaderIR::OnMemoryCount(Index count) { WABT_TRY module_->memories.reserve(module_->num_memory_imports + count); @@ -829,6 +893,62 @@ Result BinaryReaderIR::OnOpcode(Opcode opcode) { return Result::Ok; } +Result BinaryReaderIR::OnArrayCopyExpr(Index dst_type_index, + Index src_type_index) { + return AppendExpr(std::make_unique( + Var(dst_type_index, GetLocation()), Var(src_type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayFillExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayGetExpr(Opcode opcode, Index type_index) { + return AppendExpr( + std::make_unique(opcode, Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayInitDataExpr(Index type_index, Index data_index) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), Var(data_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayInitElemExpr(Index type_index, Index elem_index) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), Var(elem_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayNewExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayNewDataExpr(Index type_index, Index data_index) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), Var(data_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayNewDefaultExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayNewElemExpr(Index type_index, Index elem_index) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), Var(elem_index, GetLocation()))); +} + +Result BinaryReaderIR::OnArrayNewFixedExpr(Index type_index, Index count) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), count)); +} + +Result BinaryReaderIR::OnArraySetExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -901,6 +1021,15 @@ Result BinaryReaderIR::OnBrIfExpr(Index depth) { return AppendExpr(std::make_unique(Var(depth, GetLocation()))); } +Result BinaryReaderIR::OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) { + return AppendExpr(std::make_unique( + opcode, Var(depth, GetLocation()), Var(type1, GetLocation()), + Var(type2, GetLocation()))); +} + Result BinaryReaderIR::OnBrOnNonNullExpr(Index depth) { return AppendExpr( std::make_unique(Var(depth, GetLocation()))); @@ -1052,6 +1181,10 @@ Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) { std::make_unique(Const::V128(value_bits, GetLocation()))); } +Result BinaryReaderIR::OnGCUnaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) { return AppendExpr( std::make_unique(Var(global_index, GetLocation()))); @@ -1171,6 +1304,10 @@ Result BinaryReaderIR::OnRefAsNonNullExpr() { std::make_unique(Opcode::RefAsNonNull, GetLocation())); } +Result BinaryReaderIR::OnRefCastExpr(Type type) { + return AppendExpr(std::make_unique(Var(type, GetLocation()))); +} + Result BinaryReaderIR::OnRefFuncExpr(Index func_index) { module_->used_func_refs.insert(func_index); return AppendExpr( @@ -1186,6 +1323,10 @@ Result BinaryReaderIR::OnRefIsNullExpr() { return AppendExpr(std::make_unique()); } +Result BinaryReaderIR::OnRefTestExpr(Type type) { + return AppendExpr(std::make_unique(Var(type, GetLocation()))); +} + Result BinaryReaderIR::OnNopExpr() { return AppendExpr(std::make_unique()); } @@ -1222,6 +1363,28 @@ Result BinaryReaderIR::OnStoreExpr(Opcode opcode, opcode, Var(memidx, GetLocation()), 1ull << alignment_log2, offset)); } +Result BinaryReaderIR::OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) { + return AppendExpr(std::make_unique( + opcode, Var(type_index, GetLocation()), Var(field_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStructNewExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStructNewDefaultExpr(Index type_index) { + return AppendExpr( + std::make_unique(Var(type_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStructSetExpr(Index type_index, Index field_index) { + return AppendExpr(std::make_unique( + Var(type_index, GetLocation()), Var(field_index, GetLocation()))); +} + Result BinaryReaderIR::OnThrowExpr(Index tag_index) { module_->features_used.exceptions = true; return AppendExpr(std::make_unique(Var(tag_index, GetLocation()))); @@ -1729,11 +1892,8 @@ Result BinaryReaderIR::OnCodeMetadataFuncCount(Index count) { } Result BinaryReaderIR::OnCodeMetadataCount(Index function_index, Index count) { - if (function_index < module_->funcs.size()) { - code_metadata_queue_.push_func(module_->funcs[function_index]); - return Result::Ok; - } - return Result::Error; + code_metadata_queue_.push_func(module_->funcs[function_index]); + return Result::Ok; } Result BinaryReaderIR::OnCodeMetadata(Offset offset, @@ -1886,4 +2046,4 @@ Result ReadBinaryIr(const char* filename, return ReadBinary(data, size, &reader, options); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/binary-reader-logging.cc b/src/binary-reader-logging.cc index 89690ca533..181a672035 100644 --- a/src/binary-reader-logging.cc +++ b/src/binary-reader-logging.cc @@ -99,6 +99,19 @@ void BinaryReaderLogging::LogTypes(TypeVector& types) { LogTypes(types.size(), types.data()); } +void BinaryReaderLogging::LogGCInfo(GCTypeExtension* gc_ext) { + if (gc_ext->is_final_sub_type && gc_ext->sub_type_count == 0) { + return; + } + + LOGF_NOINDENT("(sub%s", gc_ext->is_final_sub_type ? " final" : ""); + + for (Index i = 0; i < gc_ext->sub_type_count; i++) { + LOGF_NOINDENT(" %" PRIindex, gc_ext->sub_types[i]); + } + LOGF_NOINDENT("), "); +} + void BinaryReaderLogging::LogField(TypeMut field) { if (field.mutable_) { LOGF_NOINDENT("(mut "); @@ -143,21 +156,26 @@ Result BinaryReaderLogging::OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) { - LOGF("OnFuncType(index: %" PRIindex ", params: ", index); + Type* result_types, + GCTypeExtension* gc_ext) { + LOGF("OnFuncType(index: %" PRIindex ", ", index); + LogGCInfo(gc_ext); + LOGF_NOINDENT("params: "); LogTypes(param_count, param_types); LOGF_NOINDENT(", results: "); LogTypes(result_count, result_types); LOGF_NOINDENT(")\n"); return reader_->OnFuncType(index, param_count, param_types, result_count, - result_types); + result_types, gc_ext); } Result BinaryReaderLogging::OnStructType(Index index, Index field_count, - TypeMut* fields) { - LOGF("OnStructType(index: %" PRIindex ", fields: ", index); - LOGF_NOINDENT("["); + TypeMut* fields, + GCTypeExtension* gc_ext) { + LOGF("OnStructType(index: %" PRIindex ", ", index); + LogGCInfo(gc_ext); + LOGF_NOINDENT("fields: ["); for (Index i = 0; i < field_count; ++i) { LogField(fields[i]); if (i != field_count - 1) { @@ -165,14 +183,18 @@ Result BinaryReaderLogging::OnStructType(Index index, } } LOGF_NOINDENT("])\n"); - return reader_->OnStructType(index, field_count, fields); + return reader_->OnStructType(index, field_count, fields, gc_ext); } -Result BinaryReaderLogging::OnArrayType(Index index, TypeMut field) { - LOGF("OnArrayType(index: %" PRIindex ", field: ", index); +Result BinaryReaderLogging::OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) { + LOGF("OnArrayType(index: %" PRIindex ", ", index); + LogGCInfo(gc_ext); + LOGF_NOINDENT("field: "); LogField(field); LOGF_NOINDENT(")\n"); - return reader_->OnArrayType(index, field); + return reader_->OnArrayType(index, field, gc_ext); } Result BinaryReaderLogging::OnImport(Index index, @@ -255,14 +277,15 @@ Result BinaryReaderLogging::OnImportTag(Index import_index, sig_index); } -Result BinaryReaderLogging::OnTable(Index index, - Type elem_type, - const Limits* elem_limits) { +Result BinaryReaderLogging::BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) { char buf[100]; SPrintLimits(buf, sizeof(buf), elem_limits); LOGF("OnTable(index: %" PRIindex ", elem_type: %s, %s)\n", index, elem_type.GetName().c_str(), buf); - return reader_->OnTable(index, elem_type, elem_limits); + return reader_->BeginTable(index, elem_type, elem_limits, has_init_expr); } Result BinaryReaderLogging::OnMemory(Index index, @@ -320,6 +343,17 @@ Result BinaryReaderLogging::OnBrIfExpr(Index depth) { return reader_->OnBrIfExpr(depth); } +Result BinaryReaderLogging::OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) { + LOGF("OnBrOnCastExpr(opcode: \"%s\" (%u), depth: %" PRIindex + ", type1: %s, type2: %s)\n", + opcode.GetName(), opcode.GetCode(), depth, type1.GetName().c_str(), + type2.GetName().c_str()); + return reader_->OnBrOnCastExpr(opcode, depth, type1, type2); +} + Result BinaryReaderLogging::OnBrOnNonNullExpr(Index depth) { LOGF("OnBrOnNonNullExpr(depth: %" PRIindex ")\n", depth); return reader_->OnBrOnNonNullExpr(depth); @@ -397,6 +431,15 @@ Result BinaryReaderLogging::OnSelectExpr(Index result_count, return reader_->OnSelectExpr(result_count, result_types); } +Result BinaryReaderLogging::OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) { + LOGF("OnStructGetExpr(opcode: \"%s\" (%u), type_index: %" PRIindex + ", field_index: %" PRIindex ")\n", + opcode.GetName(), opcode.GetCode(), type_index, field_index); + return reader_->OnStructGetExpr(opcode, type_index, field_index); +} + Result BinaryReaderLogging::OnTryExpr(Type sig_type) { LOGF("OnTryExpr(sig: "); LogType(sig_type); @@ -770,6 +813,13 @@ Result BinaryReaderLogging::OnGenericCustomSection(std::string_view name, return reader_->name(opcode); \ } +#define DEFINE_OPCODE_INDEX(name, desc) \ + Result BinaryReaderLogging::name(Opcode opcode, Index index) { \ + LOGF(#name "(opcode: \"%s\" (%u), " desc ": %" PRIindex ")\n", \ + opcode.GetName(), opcode.GetCode(), index); \ + return reader_->name(opcode, index); \ + } + #define DEFINE_LOAD_STORE_OPCODE(name) \ Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \ Address alignment_log2, Address offset) { \ @@ -803,6 +853,7 @@ DEFINE_END(EndCustomSection) DEFINE_BEGIN(BeginTypeSection) DEFINE_INDEX(OnTypeCount) +DEFINE_INDEX_INDEX(OnRecursiveType, "first_type_index", "type_count") DEFINE_END(EndTypeSection) DEFINE_BEGIN(BeginImportSection) @@ -816,6 +867,9 @@ DEFINE_END(EndFunctionSection) DEFINE_BEGIN(BeginTableSection) DEFINE_INDEX(OnTableCount) +DEFINE_INDEX(BeginTableInitExpr) +DEFINE_INDEX(EndTableInitExpr) +DEFINE_INDEX(EndTable) DEFINE_END(EndTableSection) DEFINE_BEGIN(BeginMemorySection) @@ -842,6 +896,17 @@ DEFINE_INDEX(OnFunctionBodyCount) DEFINE_INDEX(EndFunctionBody) DEFINE_INDEX(OnLocalDeclCount) DEFINE0(EndLocalDecls) +DEFINE_INDEX_INDEX(OnArrayCopyExpr, "dst_type_index", "src_type_index") +DEFINE_INDEX_DESC(OnArrayFillExpr, "type_index") +DEFINE_OPCODE_INDEX(OnArrayGetExpr, "type_index") +DEFINE_INDEX_INDEX(OnArrayInitDataExpr, "type_index", "data_index") +DEFINE_INDEX_INDEX(OnArrayInitElemExpr, "type_index", "elem_index") +DEFINE_INDEX_DESC(OnArrayNewExpr, "type_index") +DEFINE_INDEX_INDEX(OnArrayNewDataExpr, "type_index", "data_index") +DEFINE_INDEX_DESC(OnArrayNewDefaultExpr, "type_index") +DEFINE_INDEX_INDEX(OnArrayNewElemExpr, "type_index", "elem_index") +DEFINE_INDEX_INDEX(OnArrayNewFixedExpr, "type_index", "count") +DEFINE_INDEX_DESC(OnArraySetExpr, "type_index") DEFINE_LOAD_STORE_OPCODE(OnAtomicLoadExpr); DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwExpr); DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwCmpxchgExpr); @@ -861,6 +926,7 @@ DEFINE_INDEX_DESC(OnDelegateExpr, "depth"); DEFINE0(OnDropExpr) DEFINE0(OnElseExpr) DEFINE0(OnEndExpr) +DEFINE_OPCODE(OnGCUnaryExpr) DEFINE_INDEX_DESC(OnGlobalGetExpr, "index") DEFINE_INDEX_DESC(OnGlobalSetExpr, "index") DEFINE_LOAD_STORE_OPCODE(OnLoadExpr); @@ -882,8 +948,10 @@ DEFINE_INDEX(OnTableGrowExpr) DEFINE_INDEX(OnTableSizeExpr) DEFINE_INDEX_DESC(OnTableFillExpr, "table index") DEFINE0(OnRefAsNonNullExpr) +DEFINE_TYPE(OnRefCastExpr) DEFINE_INDEX(OnRefFuncExpr) DEFINE_TYPE(OnRefNullExpr) +DEFINE_TYPE(OnRefTestExpr) DEFINE0(OnRefIsNullExpr) DEFINE0(OnNopExpr) DEFINE_INDEX_DESC(OnRethrowExpr, "depth"); @@ -895,6 +963,9 @@ DEFINE0(OnReturnExpr) DEFINE_LOAD_STORE_OPCODE(OnLoadSplatExpr); DEFINE_LOAD_STORE_OPCODE(OnLoadZeroExpr); DEFINE_LOAD_STORE_OPCODE(OnStoreExpr); +DEFINE_INDEX_DESC(OnStructNewExpr, "type_index") +DEFINE_INDEX_DESC(OnStructNewDefaultExpr, "type_index") +DEFINE_INDEX_INDEX(OnStructSetExpr, "type_index", "field_index") DEFINE_INDEX_DESC(OnThrowExpr, "tag_index") DEFINE0(OnUnreachableExpr) DEFINE0(OnThrowRefExpr) @@ -1030,4 +1101,4 @@ Result BinaryReaderLogging::OnOpcodeType(Type type) { return reader_->OnOpcodeType(type); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/binary-reader-objdump.cc b/src/binary-reader-objdump.cc index e35d1fe5b8..ba1e4ca5f6 100644 --- a/src/binary-reader-objdump.cc +++ b/src/binary-reader-objdump.cc @@ -265,7 +265,8 @@ class BinaryReaderObjdumpPrepass : public BinaryReaderObjdumpBase { Index param_count, Type* param_types, Index result_count, - Type* result_types) override { + Type* result_types, + GCTypeExtension* gc_ext) override { objdump_state_->function_param_counts[index] = param_count; return Result::Ok; } @@ -1089,9 +1090,15 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase { Index param_count, Type* param_types, Index result_count, - Type* result_types) override; - Result OnStructType(Index index, Index field_count, TypeMut* fields) override; - Result OnArrayType(Index index, TypeMut field) override; + Type* result_types, + GCTypeExtension* gc_ext) override; + Result OnStructType(Index index, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext) override; + Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) override; Result OnImportCount(Index count) override; Result OnImportFunc(Index import_index, @@ -1127,9 +1134,10 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase { Result OnFunction(Index index, Index sig_index) override; Result OnTableCount(Index count) override; - Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) override; + Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool) override; Result OnMemoryCount(Index count) override; Result OnMemory(Index index, @@ -1176,6 +1184,14 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase { Result EndDataSegmentInitExpr(Index index) override { return EndInitExpr(); } + Result BeginTableInitExpr(Index index) override { + reading_table_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndTableInitExpr(Index index) override { return EndInitExpr(); } + Result BeginGlobalInitExpr(Index index) override { reading_global_init_expr_ = true; BeginInitExpr(); @@ -1305,6 +1321,7 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase { Index elem_index_ = 0; Index table_index_ = 0; Index next_data_reloc_ = 0; + bool reading_table_init_expr_ = false; bool reading_elem_init_expr_ = false; bool reading_data_init_expr_ = false; bool reading_global_init_expr_ = false; @@ -1317,8 +1334,9 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase { uint64_t elem_offset_ = 0; bool ReadingInitExpr() { - return reading_elem_init_expr_ || reading_data_init_expr_ || - reading_global_init_expr_ || reading_elem_expr_; + return reading_table_init_expr_ || reading_elem_init_expr_ || + reading_data_init_expr_ || reading_global_init_expr_ || + reading_elem_expr_; } }; @@ -1446,15 +1464,31 @@ Result BinaryReaderObjdump::OnTypeCount(Index count) { return OnCount(count); } +void PrintGCTypeExtension(GCTypeExtension* gc_ext) { + if (gc_ext->is_final_sub_type && gc_ext->sub_type_count == 0) { + return; + } + + printf("(sub%s", gc_ext->is_final_sub_type ? " final" : ""); + + for (Index i = 0; i < gc_ext->sub_type_count; i++) { + printf(" %" PRIindex, gc_ext->sub_types[i]); + } + printf(") "); +} + Result BinaryReaderObjdump::OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) { + Type* result_types, + GCTypeExtension* gc_ext) { if (!ShouldPrintDetails()) { return Result::Ok; } - printf(" - type[%" PRIindex "] (", index); + printf(" - type[%" PRIindex "] ", index); + PrintGCTypeExtension(gc_ext); + printf("("); for (Index i = 0; i < param_count; i++) { if (i != 0) { printf(", "); @@ -1486,11 +1520,14 @@ Result BinaryReaderObjdump::OnFuncType(Index index, Result BinaryReaderObjdump::OnStructType(Index index, Index field_count, - TypeMut* fields) { + TypeMut* fields, + GCTypeExtension* gc_ext) { if (!ShouldPrintDetails()) { return Result::Ok; } - printf(" - type[%" PRIindex "] (struct", index); + printf(" - type[%" PRIindex "] ", index); + PrintGCTypeExtension(gc_ext); + printf("(struct"); for (Index i = 0; i < field_count; i++) { if (fields[i].mutable_) { printf(" (mut"); @@ -1504,11 +1541,15 @@ Result BinaryReaderObjdump::OnStructType(Index index, return Result::Ok; } -Result BinaryReaderObjdump::OnArrayType(Index index, TypeMut field) { +Result BinaryReaderObjdump::OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) { if (!ShouldPrintDetails()) { return Result::Ok; } - printf(" - type[%" PRIindex "] (array", index); + printf(" - type[%" PRIindex "] ", index); + PrintGCTypeExtension(gc_ext); + printf("(array"); if (field.mutable_) { printf(" (mut"); } @@ -1697,9 +1738,10 @@ Result BinaryReaderObjdump::OnTableCount(Index count) { return OnCount(count); } -Result BinaryReaderObjdump::OnTable(Index index, - Type elem_type, - const Limits* elem_limits) { +Result BinaryReaderObjdump::BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool) { PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index, elem_type.GetName().c_str(), elem_limits->initial); if (elem_limits->has_max) { @@ -1922,6 +1964,9 @@ Result BinaryReaderObjdump::EndInitExpr() { if (reading_data_init_expr_) { reading_data_init_expr_ = false; InitExprToConstOffset(current_init_expr_, &data_offset_); + } else if (reading_table_init_expr_) { + reading_table_init_expr_ = false; + InitExprToConstOffset(current_init_expr_, &elem_offset_); } else if (reading_elem_init_expr_) { reading_elem_init_expr_ = false; InitExprToConstOffset(current_init_expr_, &elem_offset_); @@ -2523,4 +2568,4 @@ Result ReadBinaryObjdump(const uint8_t* data, } } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/binary-reader.cc b/src/binary-reader.cc index b9c2e3d5ad..f9cd121687 100644 --- a/src/binary-reader.cc +++ b/src/binary-reader.cc @@ -116,6 +116,9 @@ class BinaryReader { [[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc); [[nodiscard]] Result ReadType(Type* out_value, const char* desc); [[nodiscard]] Result ReadRefType(Type* out_value, const char* desc); + [[nodiscard]] Result ReadHeapType(Type* out_value, + bool is_nullable, + const char* desc); [[nodiscard]] Result ReadExternalKind(ExternalKind* out_value, const char* desc); [[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc); @@ -195,6 +198,7 @@ class BinaryReader { BinaryReaderDelegate::State state_; BinaryReaderLogging logging_delegate_; BinaryReaderDelegate* delegate_ = nullptr; + std::vector sub_types_; TypeVector param_types_; TypeVector result_types_; TypeMutVector fields_; @@ -374,13 +378,13 @@ Result BinaryReader::ReadType(Type* out_value, const char* desc) { if (static_cast(heap_type) < 0 || static_cast(heap_type) >= kInvalidIndex) { Type::Enum heap_type_code = static_cast(heap_type); - ERROR_UNLESS( - heap_type_code == Type::FuncRef || heap_type_code == Type::ExternRef, - "Reference type is limited to func and extern: %s", desc); - type = (static_cast(type) == Type::Ref) - ? Type::ReferenceNonNull - : Type::ReferenceOrNull; - *out_value = Type(heap_type_code, type); + ERROR_UNLESS(heap_type_code == Type::FuncRef || + heap_type_code == Type::ExternRef || + (options_.features.gc_enabled() && + Type::EnumIsNonTypedGCRef(heap_type_code)), + "not allowed reference type: %s", desc); + *out_value = + Type(heap_type_code, static_cast(type) == Type::RefNull); } else { *out_value = Type(static_cast(type), static_cast(heap_type)); @@ -397,6 +401,28 @@ Result BinaryReader::ReadRefType(Type* out_value, const char* desc) { return Result::Ok; } +Result BinaryReader::ReadHeapType(Type* out_value, + bool is_nullable, + const char* desc) { + uint64_t heap_type; + CHECK_RESULT(ReadS64Leb128(&heap_type, "heap type")); + + if (static_cast(heap_type) < 0 || + static_cast(heap_type) >= kInvalidIndex) { + Type::Enum type_code = static_cast(heap_type); + ERROR_UNLESS(IsConcreteReferenceType(type_code), + "expected valid %s type (got " PRItypecode ")", desc, + WABT_PRINTF_TYPE_CODE(type_code)); + *out_value = Type(type_code, is_nullable); + } else { + ERROR_UNLESS(options_.features.function_references_enabled(), + "type references are not enabled for %s", desc); + *out_value = Type(is_nullable ? Type::RefNull : Type::Ref, + static_cast(heap_type)); + } + return Result::Ok; +} + Result BinaryReader::ReadExternalKind(ExternalKind* out_value, const char* desc) { uint8_t value = 0; @@ -556,7 +582,8 @@ Result BinaryReader::ReadField(TypeMut* out_value) { // TODO: Reuse for global header too? Type field_type; CHECK_RESULT(ReadType(&field_type, "field type")); - ERROR_UNLESS(IsConcreteType(field_type), + ERROR_UNLESS(IsConcreteType(field_type) || field_type == Type::I8 || + field_type == Type::I16, "expected valid field type (got " PRItypecode ")", WABT_PRINTF_TYPE_CODE(field_type)); @@ -577,6 +604,16 @@ bool BinaryReader::IsConcreteReferenceType(Type::Enum type) { case Type::ExnRef: return options_.features.exceptions_enabled(); + case Type::NullFuncRef: + case Type::NullExternRef: + case Type::NullRef: + case Type::AnyRef: + case Type::EqRef: + case Type::I31Ref: + case Type::StructRef: + case Type::ArrayRef: + return options_.features.gc_enabled(); + default: return false; } @@ -593,7 +630,6 @@ bool BinaryReader::IsConcreteType(Type type) { case Type::V128: return options_.features.simd_enabled(); - case Type::Reference: case Type::Ref: case Type::RefNull: return options_.features.function_references_enabled(); @@ -1957,23 +1993,8 @@ Result BinaryReader::ReadInstructions(Offset end_offset, const char* context) { } case Opcode::RefNull: { - uint64_t heap_type; Type type; - CHECK_RESULT(ReadS64Leb128(&heap_type, "ref.null type")); - - if (static_cast(heap_type) < 0 || - static_cast(heap_type) >= kInvalidIndex) { - Type::Enum type_code = static_cast(heap_type); - ERROR_UNLESS(IsConcreteReferenceType(type_code), - "expected valid ref.null type (got " PRItypecode ")", - WABT_PRINTF_TYPE_CODE(type_code)); - type = Type(type_code); - } else { - ERROR_UNLESS(options_.features.function_references_enabled(), - "function references are not enabled for ref.null"); - type = Type(Type::RefNull, static_cast(heap_type)); - } - + CHECK_RESULT(ReadHeapType(&type, true, "ref.null")); CALLBACK(OnRefNullExpr, type); CALLBACK(OnOpcodeType, type); break; @@ -2004,6 +2025,184 @@ Result BinaryReader::ReadInstructions(Offset end_offset, const char* context) { break; } + case Opcode::ArrayCopy: { + uint32_t dst_type_index, src_type_index; + CHECK_RESULT(ReadIndex(&dst_type_index, "dst type index")); + CHECK_RESULT(ReadIndex(&src_type_index, "src type index")); + CALLBACK(OnArrayCopyExpr, dst_type_index, src_type_index); + CALLBACK(OnOpcodeIndexIndex, dst_type_index, src_type_index); + break; + } + + case Opcode::ArrayFill: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnArrayFillExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::ArrayGet: + case Opcode::ArrayGetS: + case Opcode::ArrayGetU: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnArrayGetExpr, opcode, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::ArrayInitData: { + uint32_t type_index, data_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&data_index, "data index")); + CALLBACK(OnArrayInitDataExpr, type_index, data_index); + CALLBACK(OnOpcodeIndexIndex, type_index, data_index); + break; + } + + case Opcode::ArrayInitElem: { + uint32_t type_index, elem_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&elem_index, "elem index")); + CALLBACK(OnArrayInitElemExpr, type_index, elem_index); + CALLBACK(OnOpcodeIndexIndex, type_index, elem_index); + break; + } + + case Opcode::ArrayNew: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnArrayNewExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::ArrayNewData: { + uint32_t type_index, data_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&data_index, "data index")); + CALLBACK(OnArrayNewDataExpr, type_index, data_index); + CALLBACK(OnOpcodeIndexIndex, type_index, data_index); + break; + } + + case Opcode::ArrayNewDefault: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnArrayNewDefaultExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::ArrayNewElem: { + uint32_t type_index, elem_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&elem_index, "elem index")); + CALLBACK(OnArrayNewElemExpr, type_index, elem_index); + CALLBACK(OnOpcodeIndexIndex, type_index, elem_index); + break; + } + + case Opcode::ArrayNewFixed: { + uint32_t type_index, count; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&count, "count")); + CALLBACK(OnArrayNewFixedExpr, type_index, count); + CALLBACK(OnOpcodeUint32Uint32, type_index, count); + break; + } + + case Opcode::ArraySet: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnArraySetExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::ArrayLen: + case Opcode::AnyConvertExtern: + case Opcode::ExternConvertAny: + case Opcode::RefI31: + case Opcode::RefEq: + case Opcode::I31GetS: + case Opcode::I31GetU: + CALLBACK(OnGCUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::RefCast: + case Opcode::RefCastNull: { + Type type; + CHECK_RESULT( + ReadHeapType(&type, opcode == Opcode::RefCastNull, "ref.cast")); + CALLBACK(OnRefCastExpr, type); + CALLBACK(OnOpcodeType, type); + break; + } + + case Opcode::RefTest: + case Opcode::RefTestNull: { + Type type; + CHECK_RESULT( + ReadHeapType(&type, opcode == Opcode::RefTestNull, "ref.test")); + CALLBACK(OnRefTestExpr, type); + CALLBACK(OnOpcodeType, type); + break; + } + + case Opcode::BrOnCast: + case Opcode::BrOnCastFail: { + Index depth; + uint8_t flags; + Type type1, type2; + CHECK_RESULT(ReadU8(&flags, "br_on_cast flags")); + CHECK_RESULT(ReadIndex(&depth, "br_on_cast depth")); + CHECK_RESULT( + ReadHeapType(&type1, (flags & 0x1) != 0, "br_on_cast type1")); + CHECK_RESULT( + ReadHeapType(&type2, (flags & 0x2) != 0, "br_on_cast type2")); + CALLBACK(OnBrOnCastExpr, opcode, depth, type1, type2); + break; + } + + case Opcode::StructGet: + case Opcode::StructGetS: + case Opcode::StructGetU: { + uint32_t type_index, field_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&field_index, "field index")); + CALLBACK(OnStructGetExpr, opcode, type_index, field_index); + CALLBACK(OnOpcodeIndexIndex, type_index, field_index); + break; + } + + case Opcode::StructNew: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnStructNewExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::StructNewDefault: { + uint32_t type_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CALLBACK(OnStructNewDefaultExpr, type_index); + CALLBACK(OnOpcodeIndex, type_index); + break; + } + + case Opcode::StructSet: { + uint32_t type_index, field_index; + CHECK_RESULT(ReadIndex(&type_index, "type index")); + CHECK_RESULT(ReadIndex(&field_index, "field index")); + CALLBACK(OnStructSetExpr, type_index, field_index); + CALLBACK(OnOpcodeIndexIndex, type_index, field_index); + break; + } + default: return ReportUnexpectedOpcode(opcode); } @@ -2515,7 +2714,7 @@ Result BinaryReader::ReadCodeMetadataSection(std::string_view name, Index last_function_index = kInvalidIndex; for (Index i = 0; i < num_functions; ++i) { Index function_index; - CHECK_RESULT(ReadIndex(&function_index, "function index")); + CHECK_RESULT(ReadCount(&function_index, "function index")); ERROR_UNLESS(function_index >= num_func_imports_, "function import can't have metadata (got %" PRIindex ")", function_index); @@ -2603,10 +2802,25 @@ Result BinaryReader::ReadTypeSection(Offset section_size) { CHECK_RESULT(ReadCount(&num_signatures, "type count")); CALLBACK(OnTypeCount, num_signatures); + Index type_index = 0; for (Index i = 0; i < num_signatures; ++i) { Type form; + uint32_t recursive_count = 1; + if (options_.features.gc_enabled()) { - CHECK_RESULT(ReadType(&form, "type form")); + CHECK_RESULT(ReadType(&form, "type form or gc info")); + + if (form == Type::Rec) { + CHECK_RESULT(ReadU32Leb128(&recursive_count, "recursive count")); + + CALLBACK(OnRecursiveType, type_index, recursive_count); + + if (recursive_count == 0) { + continue; + } + + CHECK_RESULT(ReadType(&form, "type form or gc info")); + } } else { uint8_t type; CHECK_RESULT(ReadU8(&type, "type form")); @@ -2614,73 +2828,107 @@ Result BinaryReader::ReadTypeSection(Offset section_size) { form = Type::Func; } - switch (form) { - case Type::Func: { - Index num_params; - CHECK_RESULT(ReadCount(&num_params, "function param count")); - - param_types_.resize(num_params); + while (true) { + GCTypeExtension gc_ext; + gc_ext.is_final_sub_type = true; + gc_ext.sub_type_count = 0; + gc_ext.sub_types = nullptr; + + if (options_.features.gc_enabled() && + (form == Type::Sub || form == Type::SubFinal)) { + gc_ext.is_final_sub_type = (form == Type::SubFinal); + CHECK_RESULT(ReadU32Leb128(&gc_ext.sub_type_count, "sub type count")); + sub_types_.resize(gc_ext.sub_type_count); + + for (Index i = 0; i < gc_ext.sub_type_count; i++) { + Index sub_type; + CHECK_RESULT(ReadU32Leb128(&sub_type, "sub type index")); + sub_types_[i] = sub_type; + } - for (Index j = 0; j < num_params; ++j) { - Type param_type; - CHECK_RESULT(ReadType(¶m_type, "function param type")); - ERROR_UNLESS(IsConcreteType(param_type), - "expected valid param type (got " PRItypecode ")", - WABT_PRINTF_TYPE_CODE(param_type)); - param_types_[j] = param_type; + if (gc_ext.sub_type_count > 0) { + gc_ext.sub_types = sub_types_.data(); } - Index num_results; - CHECK_RESULT(ReadCount(&num_results, "function result count")); + CHECK_RESULT(ReadType(&form, "type form")); + } - result_types_.resize(num_results); + switch (form) { + case Type::Func: { + Index num_params; + CHECK_RESULT(ReadCount(&num_params, "function param count")); - for (Index j = 0; j < num_results; ++j) { - Type result_type; - CHECK_RESULT(ReadType(&result_type, "function result type")); - ERROR_UNLESS(IsConcreteType(result_type), - "expected valid result type (got " PRItypecode ")", - WABT_PRINTF_TYPE_CODE(result_type)); - result_types_[j] = result_type; - } + param_types_.resize(num_params); + + for (Index j = 0; j < num_params; ++j) { + Type param_type; + CHECK_RESULT(ReadType(¶m_type, "function param type")); + ERROR_UNLESS(IsConcreteType(param_type), + "expected valid param type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(param_type)); + param_types_[j] = param_type; + } - Type* param_types = num_params ? param_types_.data() : nullptr; - Type* result_types = num_results ? result_types_.data() : nullptr; + Index num_results; + CHECK_RESULT(ReadCount(&num_results, "function result count")); - CALLBACK(OnFuncType, i, num_params, param_types, num_results, - result_types); - break; - } + result_types_.resize(num_results); - case Type::Struct: { - ERROR_UNLESS(options_.features.gc_enabled(), - "invalid type form: struct not allowed"); - Index num_fields; - CHECK_RESULT(ReadCount(&num_fields, "field count")); + for (Index j = 0; j < num_results; ++j) { + Type result_type; + CHECK_RESULT(ReadType(&result_type, "function result type")); + ERROR_UNLESS(IsConcreteType(result_type), + "expected valid result type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(result_type)); + result_types_[j] = result_type; + } - fields_.resize(num_fields); - for (Index j = 0; j < num_fields; ++j) { - CHECK_RESULT(ReadField(&fields_[j])); + Type* param_types = num_params ? param_types_.data() : nullptr; + Type* result_types = num_results ? result_types_.data() : nullptr; + + CALLBACK(OnFuncType, type_index, num_params, param_types, num_results, + result_types, &gc_ext); + break; } - CALLBACK(OnStructType, i, fields_.size(), fields_.data()); - break; - } + case Type::Struct: { + ERROR_UNLESS(options_.features.gc_enabled(), + "invalid type form: struct not allowed"); + Index num_fields; + CHECK_RESULT(ReadCount(&num_fields, "field count")); + + fields_.resize(num_fields); + for (Index j = 0; j < num_fields; ++j) { + CHECK_RESULT(ReadField(&fields_[j])); + } + + CALLBACK(OnStructType, type_index, fields_.size(), fields_.data(), + &gc_ext); + break; + } - case Type::Array: { - ERROR_UNLESS(options_.features.gc_enabled(), - "invalid type form: array not allowed"); + case Type::Array: { + ERROR_UNLESS(options_.features.gc_enabled(), + "invalid type form: array not allowed"); - TypeMut field; - CHECK_RESULT(ReadField(&field)); - CALLBACK(OnArrayType, i, field); + TypeMut field; + CHECK_RESULT(ReadField(&field)); + CALLBACK(OnArrayType, type_index, field, &gc_ext); + break; + }; + + default: + PrintError("unexpected type form (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(form)); + return Result::Error; + } + + type_index++; + if (--recursive_count == 0) { break; - }; + } - default: - PrintError("unexpected type form (got " PRItypecode ")", - WABT_PRINTF_TYPE_CODE(form)); - return Result::Error; + CHECK_RESULT(ReadType(&form, "type form or gc info")); } } CALLBACK0(EndTypeSection); @@ -2787,8 +3035,32 @@ Result BinaryReader::ReadTableSection(Offset section_size) { Index table_index = num_table_imports_ + i; Type elem_type; Limits elem_limits; + bool has_init_expr = false; + + if (options_.features.function_references_enabled() && + state_.offset < read_end_ && state_.data[state_.offset] == 0x40) { + state_.offset++; + has_init_expr = true; + + uint8_t value; + CHECK_RESULT(ReadU8(&value, "table init")); + if (value != 0) { + PrintError("unsupported table intializer: 0x%x\n", + static_cast(value)); + return Result::Error; + } + } + CHECK_RESULT(ReadTable(&elem_type, &elem_limits)); - CALLBACK(OnTable, table_index, elem_type, &elem_limits); + CALLBACK(BeginTable, table_index, elem_type, &elem_limits, has_init_expr); + + if (has_init_expr) { + CALLBACK(BeginTableInitExpr, table_index); + CHECK_RESULT(ReadInitExpr(table_index)); + CALLBACK(EndTableInitExpr, table_index); + } + + CALLBACK(EndTable, table_index); } CALLBACK0(EndTableSection); return Result::Ok; @@ -2879,6 +3151,11 @@ Result BinaryReader::ReadElemSection(Offset section_size) { } Type elem_type = Type::FuncRef; + if (options_.features.function_references_enabled() && + !(flags & SegUseElemExprs)) { + elem_type = Type(Type::FuncRef, Type::ReferenceNonNull); + } + CALLBACK(BeginElemSegment, i, table_index, flags); if (!(flags & SegPassive)) { @@ -2897,7 +3174,6 @@ Result BinaryReader::ReadElemSection(Offset section_size) { ERROR_UNLESS(kind == ExternalKind::Func, "segment elem type must be func (%s)", elem_type.GetName().c_str()); - elem_type = Type::FuncRef; } } @@ -3216,4 +3492,4 @@ Result ReadBinary(const void* data, BinaryReader::ReadModuleOptions{options.stop_on_first_error}); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/binary-writer.cc b/src/binary-writer.cc index 68b1ba9d31..6f60556e6f 100644 --- a/src/binary-writer.cc +++ b/src/binary-writer.cc @@ -1616,7 +1616,8 @@ Result BinaryWriter::WriteModule() { ElemSegment* segment = module_->elem_segments[i]; WriteHeader("elem segment header", i); // 1. flags - uint8_t flags = segment->GetFlags(module_); + uint8_t flags = segment->GetFlags( + module_, options_.features.function_references_enabled()); stream_->WriteU8(flags, "segment flags"); // 2. optional target table if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) { diff --git a/src/expr-visitor.cc b/src/expr-visitor.cc index b1e8b8c37d..de8e80c7f5 100644 --- a/src/expr-visitor.cc +++ b/src/expr-visitor.cc @@ -167,6 +167,54 @@ Result ExprVisitor::VisitFunc(Func* func) { Result ExprVisitor::HandleDefaultState(Expr* expr) { switch (expr->type()) { + case ExprType::ArrayCopy: + CHECK_RESULT(delegate_->OnArrayCopyExpr(cast(expr))); + break; + + case ExprType::ArrayFill: + CHECK_RESULT(delegate_->OnArrayFillExpr(cast(expr))); + break; + + case ExprType::ArrayGet: + CHECK_RESULT(delegate_->OnArrayGetExpr(cast(expr))); + break; + + case ExprType::ArrayInitData: + CHECK_RESULT( + delegate_->OnArrayInitDataExpr(cast(expr))); + break; + + case ExprType::ArrayInitElem: + CHECK_RESULT( + delegate_->OnArrayInitElemExpr(cast(expr))); + break; + + case ExprType::ArrayNew: + CHECK_RESULT(delegate_->OnArrayNewExpr(cast(expr))); + break; + + case ExprType::ArrayNewData: + CHECK_RESULT(delegate_->OnArrayNewDataExpr(cast(expr))); + break; + + case ExprType::ArrayNewDefault: + CHECK_RESULT( + delegate_->OnArrayNewDefaultExpr(cast(expr))); + break; + + case ExprType::ArrayNewElem: + CHECK_RESULT(delegate_->OnArrayNewElemExpr(cast(expr))); + break; + + case ExprType::ArrayNewFixed: + CHECK_RESULT( + delegate_->OnArrayNewFixedExpr(cast(expr))); + break; + + case ExprType::ArraySet: + CHECK_RESULT(delegate_->OnArraySetExpr(cast(expr))); + break; + case ExprType::AtomicLoad: CHECK_RESULT(delegate_->OnAtomicLoadExpr(cast(expr))); break; @@ -215,6 +263,10 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) { CHECK_RESULT(delegate_->OnBrIfExpr(cast(expr))); break; + case ExprType::BrOnCast: + CHECK_RESULT(delegate_->OnBrOnCastExpr(cast(expr))); + break; + case ExprType::BrOnNonNull: CHECK_RESULT(delegate_->OnBrOnNonNullExpr(cast(expr))); break; @@ -259,6 +311,10 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) { CHECK_RESULT(delegate_->OnDropExpr(cast(expr))); break; + case ExprType::GCUnary: + CHECK_RESULT(delegate_->OnGCUnaryExpr(cast(expr))); + break; + case ExprType::GlobalGet: CHECK_RESULT(delegate_->OnGlobalGetExpr(cast(expr))); break; @@ -365,6 +421,10 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) { CHECK_RESULT(delegate_->OnRefAsNonNullExpr(cast(expr))); break; + case ExprType::RefCast: + CHECK_RESULT(delegate_->OnRefCastExpr(cast(expr))); + break; + case ExprType::RefFunc: CHECK_RESULT(delegate_->OnRefFuncExpr(cast(expr))); break; @@ -377,6 +437,10 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) { CHECK_RESULT(delegate_->OnRefIsNullExpr(cast(expr))); break; + case ExprType::RefTest: + CHECK_RESULT(delegate_->OnRefTestExpr(cast(expr))); + break; + case ExprType::Nop: CHECK_RESULT(delegate_->OnNopExpr(cast(expr))); break; @@ -411,6 +475,23 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) { CHECK_RESULT(delegate_->OnStoreExpr(cast(expr))); break; + case ExprType::StructGet: + CHECK_RESULT(delegate_->OnStructGetExpr(cast(expr))); + break; + + case ExprType::StructNew: + CHECK_RESULT(delegate_->OnStructNewExpr(cast(expr))); + break; + + case ExprType::StructNewDefault: + CHECK_RESULT( + delegate_->OnStructNewDefaultExpr(cast(expr))); + break; + + case ExprType::StructSet: + CHECK_RESULT(delegate_->OnStructSetExpr(cast(expr))); + break; + case ExprType::Throw: CHECK_RESULT(delegate_->OnThrowExpr(cast(expr))); break; @@ -509,4 +590,4 @@ void ExprVisitor::PopCatch() { catch_index_stack_.pop_back(); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/feature.cc b/src/feature.cc index 88b474c290..21d958c37e 100644 --- a/src/feature.cc +++ b/src/feature.cc @@ -42,6 +42,11 @@ void Features::UpdateDependencies() { reference_types_enabled_ = true; } + // Garbage collector require function references. + if (gc_enabled_) { + function_references_enabled_ = true; + } + // Function references require reference types. if (function_references_enabled_) { reference_types_enabled_ = true; diff --git a/src/interp/binary-reader-interp.cc b/src/interp/binary-reader-interp.cc index d0dd7863c7..e664ccfb78 100644 --- a/src/interp/binary-reader-interp.cc +++ b/src/interp/binary-reader-interp.cc @@ -84,11 +84,21 @@ class BinaryReaderInterp : public BinaryReaderNop { Result EndModule() override; Result OnTypeCount(Index count) override; + Result OnRecursiveType(Index first_type_index, Index type_count) override; Result OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) override; + Type* result_types, + GCTypeExtension* gc_ext) override; + Result OnStructType(Index index, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext) override; + Result OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) override; + Result EndTypeSection() override; Result OnImportFunc(Index import_index, std::string_view module_name, @@ -123,9 +133,12 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnFunction(Index index, Index sig_index) override; Result OnTableCount(Index count) override; - Result OnTable(Index index, - Type elem_type, - const Limits* elem_limits) override; + Result BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) override; + Result BeginTableInitExpr(Index index) override; + Result EndTableInitExpr(Index index) override; Result OnMemoryCount(Index count) override; Result OnMemory(Index index, @@ -153,6 +166,17 @@ class BinaryReaderInterp : public BinaryReaderNop { Result EndLocalDecls() override; Result OnOpcode(Opcode Opcode) override; + Result OnArrayCopyExpr(Index dst_type_index, Index src_type_index) override; + Result OnArrayFillExpr(Index type_index) override; + Result OnArrayGetExpr(Opcode opcode, Index type_index) override; + Result OnArrayInitDataExpr(Index type_index, Index data_index) override; + Result OnArrayInitElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewExpr(Index type_index) override; + Result OnArrayNewDataExpr(Index type_index, Index data_index) override; + Result OnArrayNewDefaultExpr(Index type_index) override; + Result OnArrayNewElemExpr(Index type_index, Index elem_index) override; + Result OnArrayNewFixedExpr(Index type_index, Index count) override; + Result OnArraySetExpr(Index type_index) override; Result OnAtomicLoadExpr(Opcode opcode, Index memidx, Address alignment_log2, @@ -182,6 +206,10 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnBlockExpr(Type sig_type) override; Result OnBrExpr(Index depth) override; Result OnBrIfExpr(Index depth) override; + Result OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) override; Result OnBrOnNonNullExpr(Index depth) override; Result OnBrOnNullExpr(Index depth) override; Result OnBrTableExpr(Index num_targets, @@ -204,6 +232,7 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnF32ConstExpr(uint32_t value_bits) override; Result OnF64ConstExpr(uint64_t value_bits) override; Result OnV128ConstExpr(v128 value_bits) override; + Result OnGCUnaryExpr(Opcode opcode) override; Result OnGlobalGetExpr(Index global_index) override; Result OnGlobalSetExpr(Index global_index) override; Result OnI32ConstExpr(uint32_t value) override; @@ -224,9 +253,11 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnMemoryInitExpr(Index segment_index, Index memidx) override; Result OnMemorySizeExpr(Index memidx) override; Result OnRefAsNonNullExpr() override; + Result OnRefCastExpr(Type type) override; Result OnRefFuncExpr(Index func_index) override; Result OnRefNullExpr(Type type) override; Result OnRefIsNullExpr() override; + Result OnRefTestExpr(Type type) override; Result OnNopExpr() override; Result OnRethrowExpr(Index depth) override; Result OnReturnExpr() override; @@ -245,6 +276,12 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnElemDropExpr(Index segment_index) override; Result OnTableInitExpr(Index segment_index, Index table_index) override; Result OnTernaryExpr(Opcode opcode) override; + Result OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) override; + Result OnStructNewExpr(Index type_index) override; + Result OnStructNewDefaultExpr(Index type_index) override; + Result OnStructSetExpr(Index type_index, Index field_index) override; Result OnThrowExpr(Index tag_index) override; Result OnThrowRefExpr() override; Result OnTryExpr(Type sig_type) override; @@ -335,6 +372,8 @@ class BinaryReaderInterp : public BinaryReaderNop { Index num_func_imports() const; + void UpdateTypeInfo(GCTypeExtension* gc_ext); + Errors* errors_ = nullptr; ModuleDesc& module_; Istream& istream_; @@ -348,6 +387,8 @@ class BinaryReaderInterp : public BinaryReaderNop { u32 local_decl_count_; u32 local_count_; + Index recursive_start_; + Index recursive_end_; std::vector func_types_; // Includes imported and defined. std::vector table_types_; // Includes imported and defined. @@ -523,21 +564,109 @@ Result BinaryReaderInterp::EndModule() { Result BinaryReaderInterp::OnTypeCount(Index count) { module_.func_types.reserve(count); + recursive_end_ = 0; return Result::Ok; } +Result BinaryReaderInterp::OnRecursiveType(Index first_type_index, + Index type_count) { + CHECK_RESULT(validator_.OnRecursiveType(first_type_index, type_count)); + if (type_count > 0) { + // A non-empty recursive group is found. + recursive_start_ = static_cast(module_.func_types.size()); + recursive_end_ = recursive_start_ + type_count; + } + return Result::Ok; +} + +void BinaryReaderInterp::UpdateTypeInfo(GCTypeExtension* gc_ext) { + FuncType& type = module_.func_types.back(); + Index index = static_cast(module_.func_types.size() - 1); + + if (index < recursive_end_) { + type.recursive_start = recursive_start_; + type.recursive_count = recursive_end_ - recursive_start_; + } else { + type.recursive_start = index; + type.recursive_count = 1; + } + + type.is_final_sub_type = gc_ext->is_final_sub_type; + type.canonical_index = index; + if (gc_ext->sub_type_count > 0) { + type.canonical_sub_index = gc_ext->sub_types[0]; + } else { + type.canonical_sub_index = kInvalidIndex; + } +} + Result BinaryReaderInterp::OnFuncType(Index index, Index param_count, Type* param_types, Index result_count, - Type* result_types) { - Result result = validator_.OnFuncType(GetLocation(), param_count, param_types, - result_count, result_types, index); + Type* result_types, + GCTypeExtension* gc_ext) { + Result result = + validator_.OnFuncType(GetLocation(), param_count, param_types, + result_count, result_types, index, gc_ext); module_.func_types.push_back(FuncType(ToInterp(param_count, param_types), ToInterp(result_count, result_types))); + UpdateTypeInfo(gc_ext); return result; } +Result BinaryReaderInterp::OnStructType(Index index, + Index field_count, + TypeMut* fields, + GCTypeExtension* gc_ext) { + Result result = + validator_.OnStructType(GetLocation(), field_count, fields, gc_ext); + + ValueTypes params; + ValueTypes results; + params.reserve(field_count); + results.reserve(field_count); + + for (Index i = 0; i < field_count; i++) { + params.push_back(fields[i].type); + results.push_back(fields[i].mutable_ ? FuncType::Mutable + : FuncType::Immutable); + } + + module_.func_types.push_back( + FuncType(FuncType::TypeKind::Struct, params, results)); + UpdateTypeInfo(gc_ext); + return result; +} + +Result BinaryReaderInterp::OnArrayType(Index index, + TypeMut field, + GCTypeExtension* gc_ext) { + Result result = validator_.OnArrayType(GetLocation(), field, gc_ext); + + ValueTypes params; + ValueTypes results; + params.reserve(1); + results.reserve(1); + + params.push_back(field.type); + results.push_back(field.mutable_ ? FuncType::Mutable : FuncType::Immutable); + + module_.func_types.push_back( + FuncType(FuncType::TypeKind::Array, params, results)); + UpdateTypeInfo(gc_ext); + return result; +} + +Result BinaryReaderInterp::EndTypeSection() { + for (auto& it : module_.func_types) { + it.canonical_index = validator_.GetCanonicalTypeIndex(it.canonical_index); + it.canonical_sub_index = + validator_.GetCanonicalTypeIndex(it.canonical_sub_index); + } + return Result::Ok; +} + Result BinaryReaderInterp::OnImportFunc(Index import_index, std::string_view module_name, std::string_view field_name, @@ -558,7 +687,8 @@ Result BinaryReaderInterp::OnImportTable(Index import_index, Index table_index, Type elem_type, const Limits* elem_limits) { - CHECK_RESULT(validator_.OnTable(GetLocation(), elem_type, *elem_limits)); + CHECK_RESULT( + validator_.OnTable(GetLocation(), elem_type, *elem_limits, true, false)); TableType table_type{elem_type, *elem_limits}; module_.imports.push_back(ImportDesc{ImportType( std::string(module_name), std::string(field_name), table_type.Clone())}); @@ -627,16 +757,33 @@ Result BinaryReaderInterp::OnTableCount(Index count) { return Result::Ok; } -Result BinaryReaderInterp::OnTable(Index index, - Type elem_type, - const Limits* elem_limits) { - CHECK_RESULT(validator_.OnTable(GetLocation(), elem_type, *elem_limits)); +Result BinaryReaderInterp::BeginTable(Index index, + Type elem_type, + const Limits* elem_limits, + bool has_init_expr) { + CHECK_RESULT(validator_.OnTable(GetLocation(), elem_type, *elem_limits, false, + has_init_expr)); TableType table_type{elem_type, *elem_limits}; - module_.tables.push_back(TableDesc{table_type}); + FuncDesc init_func{ + FuncType{{}, {elem_type}}, {}, Istream::kInvalidOffset, {}}; + module_.tables.push_back(TableDesc{table_type, init_func}); table_types_.push_back(table_type); return Result::Ok; } +Result BinaryReaderInterp::BeginTableInitExpr(Index index) { + TableDesc& table = module_.tables.back(); + return BeginInitExpr(&table.init_func); +} + +Result BinaryReaderInterp::EndTableInitExpr(Index index) { + FixupTopLabel(); + CHECK_RESULT(validator_.EndInitExpr()); + istream_.Emit(Opcode::Return); + PopLabel(); + return Result::Ok; +} + Result BinaryReaderInterp::OnMemoryCount(Index count) { module_.memories.reserve(count); return Result::Ok; @@ -1013,6 +1160,99 @@ Result BinaryReaderInterp::OnLoadZeroExpr(Opcode opcode, return Result::Ok; } +Result BinaryReaderInterp::OnArrayCopyExpr(Index dst_type_index, + Index src_type_index) { + CHECK_RESULT(validator_.OnArrayCopy(GetLocation(), + Var(dst_type_index, GetLocation()), + Var(src_type_index, GetLocation()))); + istream_.Emit(Opcode::ArrayCopy); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayFillExpr(Index type_index) { + CHECK_RESULT( + validator_.OnArrayFill(GetLocation(), Var(type_index, GetLocation()))); + istream_.Emit(Opcode::ArrayFill); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayGetExpr(Opcode opcode, Index type_index) { + CHECK_RESULT(validator_.OnArrayGet(GetLocation(), opcode, + Var(type_index, GetLocation()))); + if (opcode == Opcode::ArrayGet) { + istream_.Emit(opcode); + } else { + Index is_16 = (module_.func_types[type_index].params[0] == Type::I16); + istream_.Emit(opcode, is_16); + } + + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayInitDataExpr(Index type_index, + Index data_index) { + CHECK_RESULT(validator_.OnArrayInitData(GetLocation(), + Var(type_index, GetLocation()), + Var(data_index, GetLocation()))); + istream_.Emit(Opcode::ArrayInitData, type_index, data_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayInitElemExpr(Index type_index, + Index elem_index) { + CHECK_RESULT(validator_.OnArrayInitElem(GetLocation(), + Var(type_index, GetLocation()), + Var(elem_index, GetLocation()))); + istream_.Emit(Opcode::ArrayInitElem, elem_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayNewExpr(Index type_index) { + CHECK_RESULT( + validator_.OnArrayNew(GetLocation(), Var(type_index, GetLocation()))); + istream_.Emit(Opcode::ArrayNew, type_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayNewDataExpr(Index type_index, + Index data_index) { + CHECK_RESULT(validator_.OnArrayNewData(GetLocation(), + Var(type_index, GetLocation()), + Var(data_index, GetLocation()))); + istream_.Emit(Opcode::ArrayNewData, type_index, data_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayNewDefaultExpr(Index type_index) { + CHECK_RESULT(validator_.OnArrayNewDefault(GetLocation(), + Var(type_index, GetLocation()))); + istream_.Emit(Opcode::ArrayNewDefault, type_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayNewElemExpr(Index type_index, + Index elem_index) { + CHECK_RESULT(validator_.OnArrayNewElem(GetLocation(), + Var(type_index, GetLocation()), + Var(elem_index, GetLocation()))); + istream_.Emit(Opcode::ArrayNewElem, type_index, elem_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArrayNewFixedExpr(Index type_index, Index count) { + CHECK_RESULT(validator_.OnArrayNewFixed( + GetLocation(), Var(type_index, GetLocation()), count)); + istream_.Emit(Opcode::ArrayNewFixed, type_index, count); + return Result::Ok; +} + +Result BinaryReaderInterp::OnArraySetExpr(Index type_index) { + CHECK_RESULT( + validator_.OnArraySet(GetLocation(), Var(type_index, GetLocation()))); + istream_.Emit(Opcode::ArraySet); + return Result::Ok; +} + Result BinaryReaderInterp::OnAtomicLoadExpr(Opcode opcode, Index memidx, Address align_log2, @@ -1138,6 +1378,27 @@ Result BinaryReaderInterp::OnBrIfExpr(Index depth) { return EmitBrCond(Opcode::InterpBrUnless, depth); } +Result BinaryReaderInterp::OnBrOnCastExpr(Opcode opcode, + Index depth, + Type type1, + Type type2) { + CHECK_RESULT(validator_.OnBrOnCast( + GetLocation(), opcode, Var(depth, GetLocation()), + Var(type1, GetLocation()), Var(type2, GetLocation()))); + // For simplicity, the reftest result is pushed onto + // the stack, and it is processed by a br_if/br_unless. + Opcode test_opcode = + type2.IsNullableRef() ? Opcode::BrOnCast : Opcode::BrOnCastFail; + uint32_t code = static_cast(type2); + uint32_t value = type2.IsReferenceWithIndex() ? type2.GetReferenceIndex() + : Type::ReferenceNonNull; + istream_.Emit(test_opcode, code, value); + // Opcode is flipped. + return EmitBrCond( + opcode == Opcode::BrOnCast ? Opcode::InterpBrUnless : Opcode::BrIf, + depth); +} + Result BinaryReaderInterp::OnBrOnNonNullExpr(Index depth) { CHECK_RESULT( validator_.OnBrOnNonNull(GetLocation(), Var(depth, GetLocation()))); @@ -1328,6 +1589,15 @@ Result BinaryReaderInterp::OnV128ConstExpr(v128 value_bits) { return Result::Ok; } +Result BinaryReaderInterp::OnGCUnaryExpr(Opcode opcode) { + CHECK_RESULT(validator_.OnGCUnary(GetLocation(), opcode)); + if (opcode != Opcode::AnyConvertExtern && + opcode != Opcode::ExternConvertAny) { + istream_.Emit(opcode); + } + return Result::Ok; +} + Result BinaryReaderInterp::OnGlobalGetExpr(Index global_index) { CHECK_RESULT( validator_.OnGlobalGet(GetLocation(), Var(global_index, GetLocation()))); @@ -1451,6 +1721,16 @@ Result BinaryReaderInterp::OnRefAsNonNullExpr() { return Result::Ok; } +Result BinaryReaderInterp::OnRefCastExpr(Type type) { + CHECK_RESULT(validator_.OnRefCast(GetLocation(), Var(type, GetLocation()))); + Opcode opcode = type.IsNullableRef() ? Opcode::RefCastNull : Opcode::RefCast; + uint32_t code = static_cast(type); + uint32_t value = type.IsReferenceWithIndex() ? type.GetReferenceIndex() + : Type::ReferenceNonNull; + istream_.Emit(opcode, code, value); + return Result::Ok; +} + Result BinaryReaderInterp::OnRefFuncExpr(Index func_index) { CHECK_RESULT( validator_.OnRefFunc(GetLocation(), Var(func_index, GetLocation()))); @@ -1470,6 +1750,16 @@ Result BinaryReaderInterp::OnRefIsNullExpr() { return Result::Ok; } +Result BinaryReaderInterp::OnRefTestExpr(Type type) { + CHECK_RESULT(validator_.OnRefTest(GetLocation(), Var(type, GetLocation()))); + Opcode opcode = type.IsNullableRef() ? Opcode::RefTestNull : Opcode::RefTest; + uint32_t code = static_cast(type); + uint32_t value = type.IsReferenceWithIndex() ? type.GetReferenceIndex() + : Type::ReferenceNonNull; + istream_.Emit(opcode, code, value); + return Result::Ok; +} + Result BinaryReaderInterp::OnNopExpr() { CHECK_RESULT(validator_.OnNop(GetLocation())); return Result::Ok; @@ -1596,6 +1886,45 @@ Result BinaryReaderInterp::OnTableInitExpr(Index segment_index, return Result::Ok; } +Result BinaryReaderInterp::OnStructGetExpr(Opcode opcode, + Index type_index, + Index field_index) { + CHECK_RESULT(validator_.OnStructGet(GetLocation(), opcode, + Var(type_index, GetLocation()), + Var(field_index, GetLocation()))); + if (opcode == Opcode::StructGet) { + istream_.Emit(opcode, field_index); + } else { + Index is_16 = + (module_.func_types[type_index].params[field_index] == Type::I16); + istream_.Emit(opcode, field_index, is_16); + } + return Result::Ok; +} + +Result BinaryReaderInterp::OnStructNewExpr(Index type_index) { + CHECK_RESULT( + validator_.OnStructNew(GetLocation(), Var(type_index, GetLocation()))); + istream_.Emit(Opcode::StructNew, type_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnStructNewDefaultExpr(Index type_index) { + CHECK_RESULT(validator_.OnStructNewDefault(GetLocation(), + Var(type_index, GetLocation()))); + istream_.Emit(Opcode::StructNewDefault, type_index); + return Result::Ok; +} + +Result BinaryReaderInterp::OnStructSetExpr(Index type_index, + Index field_index) { + CHECK_RESULT(validator_.OnStructSet(GetLocation(), + Var(type_index, GetLocation()), + Var(field_index, GetLocation()))); + istream_.Emit(Opcode::StructSet, field_index); + return Result::Ok; +} + Result BinaryReaderInterp::OnThrowExpr(Index tag_index) { CHECK_RESULT( validator_.OnThrow(GetLocation(), Var(tag_index, GetLocation()))); @@ -1798,4 +2127,4 @@ Result ReadBinaryInterp(std::string_view filename, } } // namespace interp -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/interp/interp-util.cc b/src/interp/interp-util.cc index 3d21d6d099..a91abd6994 100644 --- a/src/interp/interp-util.cc +++ b/src/interp/interp-util.cc @@ -52,18 +52,59 @@ std::string TypedValueToString(const TypedValue& tv) { case Type::FuncRef: return StringPrintf("funcref:%" PRIzd, tv.value.Get().index); - case Type::ExternRef: - return StringPrintf("externref:%" PRIzd, tv.value.Get().index); + case Type::ExternRef: { + // Backward compatible print. + if (tv.value.Get() == Ref::Null) { + return "externref:0"; + } + return StringPrintf("externref:%" PRIzd, + tv.value.Get().GetHostVal() + 1); + } case Type::ExnRef: return StringPrintf("exnref:%" PRIzd, tv.value.Get().index); - case Type::Reference: + case Type::AnyRef: + return StringPrintf("anyref:%" PRIzd, tv.value.Get().index); + + case Type::ArrayRef: + return StringPrintf("arrayref:%" PRIzd, tv.value.Get().index); + + case Type::NullRef: + return StringPrintf("botref:0"); + + case Type::NullExternRef: + return StringPrintf("externref:0"); + + case Type::NullFuncRef: + return StringPrintf("funcref:0"); + + case Type::EqRef: + return StringPrintf("eqref:%" PRIzd, tv.value.Get().index); + + case Type::I31Ref: { + if (tv.value.Get() == Ref::Null) { + return "i31ref:null"; + } + return StringPrintf("i31ref:%d", + static_cast(tv.value.Get().GetU32Val())); + } + case Type::Ref: + return StringPrintf("ref:%" PRIindex, tv.type.GetReferenceIndex()); + case Type::RefNull: + return StringPrintf("refnull:%" PRIindex, tv.type.GetReferenceIndex()); + + case Type::StructRef: + return StringPrintf("structref:%" PRIzd, tv.value.Get().index); + case Type::Func: case Type::Struct: case Type::Array: + case Type::Sub: + case Type::SubFinal: + case Type::Rec: case Type::Void: case Type::Any: case Type::I8U: @@ -117,4 +158,4 @@ void WriteCall(Stream* stream, } } // namespace interp -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/interp/interp-wasm-c-api.cc b/src/interp/interp-wasm-c-api.cc index bb4598a472..c0519fccf7 100644 --- a/src/interp/interp-wasm-c-api.cc +++ b/src/interp/interp-wasm-c-api.cc @@ -960,7 +960,8 @@ void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { own wasm_table_t* wasm_table_new(wasm_store_t* store, const wasm_tabletype_t* type, wasm_ref_t* init) { - return new wasm_table_t{Table::New(store->I, *type->As())}; + return new wasm_table_t{ + Table::New(store->I, *type->As(), Ref::Null)}; } own wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { diff --git a/src/interp/interp.cc b/src/interp/interp.cc index f18b4d5ed7..a4b7073e49 100644 --- a/src/interp/interp.cc +++ b/src/interp/interp.cc @@ -40,8 +40,9 @@ const char* GetName(ExternKind kind) { const char* GetName(ObjectKind kind) { static const char* kNames[] = { - "Null", "Foreign", "Trap", "Exception", "DefinedFunc", "HostFunc", - "Table", "Memory", "Global", "Tag", "Module", "Instance", + "Null", "Foreign", "Trap", "Exception", "DefinedFunc", + "HostFunc", "Table", "Memory", "Global", "Tag", + "Array", "Struct", "Module", "Instance", }; WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(kNames) == kCommandTypeCount); @@ -49,6 +50,79 @@ const char* GetName(ObjectKind kind) { return kNames[int(kind)]; } +static u32 GetTypeLog2Size(Type type) { + switch (type) { + case Type::I8: + return 0; + case Type::I16: + return 1; + case Type::I32: + case Type::F32: + return 2; + case Type::I64: + case Type::F64: + return 3; + case Type::V128: + return 4; + default: + WABT_UNREACHABLE; + } +} + +static void ArrayInitFromDataSegment(u32 log2_size, + Array* dst, + u32 dst_offset, + u32 size, + const u8* src) { + // Supports both unaligned access and big endian mode. + switch (log2_size) { + case 0: { + for (u32 i = 0; i < size; i++) { + u32 value = *src++; + dst->SetItem(dst_offset + i, Value::Make(value)); + } + return; + } + case 1: { + for (u32 i = 0; i < size; i++, src += 2) { + u32 value = src[0] | (static_cast(src[1]) << 8); + dst->SetItem(dst_offset + i, Value::Make(value)); + } + return; + } + case 2: { + for (u32 i = 0; i < size; i++, src += 4) { + u32 value = src[0]; + for (u32 j = 1; j < 4; j++) { + value |= static_cast(src[j]) << (j << 3); + } + dst->SetItem(dst_offset + i, Value::Make(value)); + } + return; + } + case 3: { + for (u32 i = 0; i < size; i++, src += 8) { + u64 value = src[0]; + for (u32 j = 1; j < 8; j++) { + value |= static_cast(src[j]) << (j << 3); + } + dst->SetItem(dst_offset + i, Value::Make(value)); + } + return; + } + case 4: { + for (u32 i = 0; i < size; i++, src += 16) { + v128 value; + memcpy(value.v, src, 16); + dst->SetItem(dst_offset + i, Value::Make(value)); + } + return; + } + default: + WABT_UNREACHABLE; + } +} + //// Refs //// // static const Ref Ref::Null{0}; @@ -94,46 +168,103 @@ std::unique_ptr FuncType::Clone() const { return std::make_unique(*this); } -static bool RecursiveMatch(const ValueTypes& expected, +static bool RecursiveMatch(Type expected, + Index expected_recursive_start, std::vector* expected_func_types, - const ValueTypes& actual, + Type actual, + Index actual_recursive_start, std::vector* actual_func_types) { - if (expected_func_types == nullptr || actual_func_types == nullptr) { + assert(expected_func_types != nullptr && actual_func_types != nullptr); + + if (!expected.IsReferenceWithIndex()) { + return expected == actual; + } else if (!actual.IsReferenceWithIndex()) { return false; } - size_t size = expected.size(); - if (size != actual.size()) { + Index expected_index = expected.GetReferenceIndex(); + Index actual_index = actual.GetReferenceIndex(); + + const FuncType& expected_type = (*expected_func_types)[expected_index]; + const FuncType& actual_type = (*actual_func_types)[actual_index]; + + // Relative reference. + if (expected_index >= expected_recursive_start) { + return (actual_index >= actual_recursive_start && + expected_type.recursive_count == actual_type.recursive_count && + expected_index - expected_recursive_start == + actual_index - actual_recursive_start); + } else if (actual_index >= actual_recursive_start) { return false; } - for (size_t i = 0; i < size; i++) { - if (!expected[i].IsReferenceWithIndex()) { - if (expected[i] != actual[i]) { + // Absolute reference. + expected_recursive_start = expected_type.recursive_start; + actual_recursive_start = actual_type.recursive_start; + Index recursive_count = expected_type.recursive_count; + + if (recursive_count != actual_type.recursive_count || + expected_index - expected_recursive_start != + actual_index - actual_recursive_start) { + return false; + } + + // Recursive match of the whole recursive block. + for (Index i = 0; i < recursive_count; i++) { + const FuncType& expected_rec_type = + (*expected_func_types)[expected_recursive_start + i]; + const FuncType& actual_rec_type = + (*actual_func_types)[actual_recursive_start + i]; + + if (expected_rec_type.kind != actual_rec_type.kind || + expected_rec_type.is_final_sub_type != + actual_rec_type.is_final_sub_type) { + return false; + } + + if (expected_rec_type.canonical_sub_index != kInvalidIndex) { + if (actual_rec_type.canonical_sub_index == kInvalidIndex) { return false; } - continue; - } - if (static_cast(expected[i]) != - static_cast(actual[i])) { + Type expected_sub_type(Type::Ref, expected_rec_type.canonical_sub_index); + Type actual_sub_type(Type::Ref, actual_rec_type.canonical_sub_index); + + if (!RecursiveMatch(expected_sub_type, expected_recursive_start, + expected_func_types, actual_sub_type, + actual_recursive_start, actual_func_types)) { + return false; + } + } else if (actual_rec_type.canonical_sub_index != kInvalidIndex) { return false; } - const FuncType& expected_type = - (*expected_func_types)[expected[i].GetReferenceIndex()]; - const FuncType& actual_type = - (*actual_func_types)[actual[i].GetReferenceIndex()]; + size_t size = expected_rec_type.params.size(); + if (size != actual_rec_type.params.size()) { + return false; + } - assert(expected_type.func_types == expected_func_types); - assert(actual_type.func_types == actual_func_types); + for (size_t j = 0; j < size; j++) { + if (!RecursiveMatch(expected_rec_type.params[j], expected_recursive_start, + expected_func_types, actual_rec_type.params[j], + actual_recursive_start, actual_func_types)) { + return false; + } + } - if (!RecursiveMatch(expected_type.params, expected_func_types, - actual_type.params, actual_func_types) || - !RecursiveMatch(expected_type.results, expected_func_types, - actual_type.results, actual_func_types)) { + size = expected_rec_type.results.size(); + if (size != actual_rec_type.results.size()) { return false; } + + for (size_t j = 0; j < size; j++) { + if (!RecursiveMatch(expected_rec_type.results[j], + expected_recursive_start, expected_func_types, + actual_rec_type.results[j], actual_recursive_start, + actual_func_types)) { + return false; + } + } } return true; @@ -142,36 +273,27 @@ static bool RecursiveMatch(const ValueTypes& expected, Result Match(const FuncType& expected, const FuncType& actual, std::string* out_msg) { - bool has_reference = false; + if (expected.kind == actual.kind) { + if (expected.func_types == nullptr || actual.func_types == nullptr) { + // Simple function, can be a callback without module. + if (expected.params == actual.params && + expected.results == actual.results) { + return Result::Ok; + } + } else { + Type expected_type(Type::Ref, expected.canonical_index); + Index actual_index = actual.canonical_index; - for (auto it : expected.params) { - if (it.IsReferenceWithIndex()) { - has_reference = true; - break; - } - } + do { + Type actual_type(Type::Ref, actual_index); - if (!has_reference) { - for (auto it : expected.results) { - if (it.IsReferenceWithIndex()) { - has_reference = true; - break; - } - } - } + if (RecursiveMatch(expected_type, kInvalidIndex, expected.func_types, + actual_type, kInvalidIndex, actual.func_types)) { + return Result::Ok; + } - if (!has_reference) { - // Simple function, can be a callback without module. - if (expected.params == actual.params && - expected.results == actual.results) { - return Result::Ok; - } - } else { - if (RecursiveMatch(expected.params, expected.func_types, actual.params, - actual.func_types) && - RecursiveMatch(expected.results, expected.func_types, actual.results, - actual.func_types)) { - return Result::Ok; + actual_index = ((*actual.func_types)[actual_index]).canonical_sub_index; + } while (actual_index != kInvalidIndex); } } @@ -264,6 +386,46 @@ Result Match(const TagType& expected, return Result::Ok; } +//// Types //// + +bool TypesMatch(ValueType expected, ValueType actual) { + // Currently there is no subtyping, so expected and actual must match + // exactly. In the future this may be expanded. + if (expected == actual) { + return true; + } + + switch (expected) { + case Type::FuncRef: + return actual == Type::Ref || + (expected.IsNullableNonTypedRef() && + (actual == Type::FuncRef || actual == Type::RefNull)); + case Type::RefNull: + return actual == Type::Ref && + actual.GetReferenceIndex() == expected.GetReferenceIndex(); + case Type::AnyRef: + if (actual == Type::AnyRef || actual == Type::EqRef || + actual == Type::I31Ref || actual == Type::StructRef || + actual == Type::ArrayRef) { + break; + } + return false; + case Type::EqRef: + if (actual == Type::EqRef || actual == Type::I31Ref || + actual == Type::StructRef || actual == Type::ArrayRef) { + break; + } + return false; + default: + return false; + } + + if (expected.IsNullableRef() || actual.IsNonNullableRef()) { + return true; + } + return false; +} + //// Limits //// template bool CanGrow(const Limits& limits, T old_size, T delta, T* new_size) { @@ -302,20 +464,36 @@ bool Store::HasValueType(Ref ref, ValueType type) const { if (!IsValid(ref)) { return false; } - if (type == ValueType::ExternRef) { + if (ref == Ref::Null || type == ValueType::ExternRef) { return true; } - if (ref == Ref::Null) { - return true; + if (ref.IsI31OrHostVal()) { + if (ref.IsI31Val()) { + return type == ValueType::AnyRef || type == ValueType::EqRef || + type == ValueType::I31Ref || type == ValueType::ExternRef; + } + return type == ValueType::AnyRef; } Object* obj = objects_.Get(ref.index); switch (type) { + case ValueType::AnyRef: + return obj->kind() == ObjectKind::Array || + obj->kind() == ObjectKind::Struct; + case ValueType::EqRef: + return obj->kind() == ObjectKind::Array || + obj->kind() == ObjectKind::Struct; case ValueType::FuncRef: + case ValueType::Ref: + case ValueType::RefNull: return obj->kind() == ObjectKind::DefinedFunc || obj->kind() == ObjectKind::HostFunc; + case ValueType::ArrayRef: + return obj->kind() == ObjectKind::Array; case ValueType::ExnRef: return obj->kind() == ObjectKind::Exception; + case ValueType::StructRef: + return obj->kind() == ObjectKind::Struct; default: return false; } @@ -549,8 +727,12 @@ Result HostFunc::DoCall(Thread& thread, } //// Table //// -Table::Table(Store&, TableType type) : Extern(skind), type_(type) { +Table::Table(Store& store, TableType type, Ref init_ref) + : Extern(skind), type_(type) { elements_.resize(type.limits.initial); + if (init_ref != Ref::Null) { + Fill(store, 0, init_ref, type.limits.initial); + } } void Table::Mark(Store& store) { @@ -795,6 +977,52 @@ Result Tag::Match(Store& store, return MatchImpl(store, import_type, type_, out_trap); } +//// Array //// +Array::Array(Store& store, u32 size, Index type_index, Module* mod) + : Object(ObjectKind::Array), module_(mod->self()), type_index_(type_index) { + items_.resize(size); +} + +bool Array::IsValidRange(u64 offset, u64 size) const { + size_t array_size = items_.size(); + return size <= array_size && offset <= array_size - size; +} + +void Array::Mark(Store& store) { + store.Mark(module_); + + RefPtr mod = store.UnsafeGet(module_); + Type array_type = mod->desc().func_types[type_index_].params[0]; + + if (array_type.IsRef()) { + for (auto it : items_) { + store.Mark(it.Get()); + } + } +} + +//// Struct //// +Struct::Struct(Store& store, Index type_index, Module* mod) + : Object(ObjectKind::Struct), + module_(mod->self()), + type_index_(type_index) { + fields_.resize(mod->desc().func_types[type_index].params.size()); +} + +void Struct::Mark(Store& store) { + store.Mark(module_); + + RefPtr mod = store.UnsafeGet(module_); + const ValueTypes& field_types = mod->desc().func_types[type_index_].params; + size_t size = fields_.size(); + + for (size_t i = 0; i < size; i++) { + if (field_types[i].IsRef()) { + store.Mark(fields_[i].Get()); + } + } +} + //// ElemSegment //// ElemSegment::ElemSegment(Store& store, const ElemDesc* desc, @@ -914,7 +1142,16 @@ Instance::Ptr Instance::Instantiate(Store& store, // Tables. for (auto&& desc : mod->desc().tables) { - inst->tables_.push_back(Table::New(store, desc.type).ref()); + Ref ref = Ref::Null; + if (desc.init_func.code_offset != Istream::kInvalidOffset) { + Ref func_ref = DefinedFunc::New(store, inst.ref(), desc.init_func).ref(); + Value value; + if (Failed(inst->CallInitFunc(store, func_ref, &value, out_trap))) { + return {}; + } + ref = value.Get(); + } + inst->tables_.push_back(Table::New(store, desc.type, ref).ref()); } // Memories. @@ -1273,6 +1510,94 @@ void Thread::Push(Ref ref) { values_.push_back(Value::Make(ref)); } +bool Thread::CheckRefCast(Ref ref, Type expected) { + assert(expected.IsRef()); + + // Note: "any" and "extern" types represent the same value + // sets in the current implementation, so any.convert_extern + // and extern.convert_any are no operations. + if (ref.IsI31OrHostVal()) { + if (ref.IsI31Val()) { + return expected == Type::AnyRef || expected == Type::EqRef || + expected == Type::I31Ref || expected == Type::ExternRef; + } + return expected == Type::AnyRef || expected == Type::ExternRef; + } + + Object* object = store_.UnsafeGet(ref).get(); + + if (!expected.IsReferenceWithIndex()) { + if (Func::classof(object)) { + return expected == Type::FuncRef; + } + if (Array::classof(object)) { + return expected == Type::ArrayRef || expected == Type::EqRef || + expected == Type::AnyRef || expected == Type::ExternRef; + } + if (Struct::classof(object)) { + return expected == Type::StructRef || expected == Type::EqRef || + expected == Type::AnyRef || expected == Type::ExternRef; + } + return false; + } + + const FuncType& expected_type = + mod_->desc().func_types[expected.GetReferenceIndex()]; + Index expected_type_index = expected_type.canonical_index; + Index actual_type_index; + + switch (expected_type.kind) { + case FuncType::TypeKind::Func: { + if (!Func::classof(object)) { + return false; + } + const FuncType& type = cast(object)->type(); + if (type.func_types != &mod_->desc().func_types) { + return false; + } + actual_type_index = type.canonical_index; + break; + } + case FuncType::TypeKind::Array: { + if (!Array::classof(object)) { + return false; + } + Array* array = cast(object); + if (array->GetModule() != mod_->self()) { + return false; + } + actual_type_index = + mod_->desc().func_types[array->GetTypeIndex()].canonical_index; + break; + } + case FuncType::TypeKind::Struct: { + if (!Struct::classof(object)) { + return false; + } + Struct* struct_ = cast(object); + if (struct_->GetModule() != mod_->self()) { + return false; + } + actual_type_index = + mod_->desc().func_types[struct_->GetTypeIndex()].canonical_index; + break; + } + default: + WABT_UNREACHABLE; + } + + do { + if (expected_type_index == actual_type_index) { + return true; + } + + actual_type_index = + mod_->desc().func_types[actual_type_index].canonical_sub_index; + } while (actual_type_index != kInvalidIndex); + + return false; +} + RunResult Thread::StepInternal(Trap::Ptr* out_trap) { using O = Opcode; @@ -2093,6 +2418,91 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) { case O::I64AtomicRmw16CmpxchgU: return DoAtomicRmwCmpxchg(instr, out_trap); case O::I64AtomicRmw32CmpxchgU: return DoAtomicRmwCmpxchg(instr, out_trap); + case O::ArrayCopy: return DoArrayCopy(out_trap); + case O::ArrayFill: return DoArrayFill(out_trap); + case O::ArrayGet: return DoArrayGet(out_trap); + case O::ArrayInitElem: return DoArrayInitElem(instr, out_trap); + case O::ArrayInitData: return DoArrayInitData(instr, out_trap); + case O::ArrayGetS: + case O::ArrayGetU: return DoArrayGetPacked(instr, out_trap); + case O::ArrayNew: return DoArrayNew(instr); + case O::ArrayNewData: return DoArrayNewData(instr, out_trap); + case O::ArrayNewElem: return DoArrayNewElem(instr, out_trap); + case O::ArrayNewFixed: return DoArrayNewFixed(instr); + case O::ArraySet: return DoArraySet(out_trap); + + case O::ArrayLen: { + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null array ref"); + Push(store_.UnsafeGet(ref)->Size()); + break; + } + + case O::ArrayNewDefault: { + Array::Ptr array = Array::New(store_, Pop(), instr.imm_u32, mod_); + Push(array->self()); + break; + } + + case O::BrOnCast: + case O::BrOnCastFail: return DoBrOnCast(instr); + + case O::I31GetS: { + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null i31 ref"); + Push(ref.GetS32Val()); + break; + } + + case O::I31GetU: { + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null i31 ref"); + Push(ref.GetU32Val()); + break; + } + + case O::RefI31: { + Push(Ref::CreateI31Val(Pop())); + break; + } + + case O::RefCast: + case O::RefCastNull: return DoRefCast(instr, out_trap); + case O::RefTest: + case O::RefTestNull: return DoRefTest(instr); + + case O::RefEq: { + Ref ref1 = Pop(); + Ref ref2 = Pop(); + Push(ref1.index == ref2.index); + break; + } + + case O::StructGetS: + case O::StructGetU: return DoStructGetPacked(instr, out_trap); + case O::StructNew: return DoStructNew(instr); + + case O::StructGet: { + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null struct ref"); + Push(store_.UnsafeGet(ref)->GetField(instr.imm_u32)); + break; + } + + case O::StructNewDefault: { + Struct::Ptr struct_ = Struct::New(store_, instr.imm_u32, mod_); + Push(struct_->self()); + break; + } + + case O::StructSet: { + Value value = Pop(); + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null struct ref"); + store_.UnsafeGet(ref)->SetField(instr.imm_u32, value); + break; + } + case O::Throw: { u32 tag_index = instr.imm_u32; Values params; @@ -2119,8 +2529,10 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) { // The following opcodes are either never generated or should never be // executed. + case O::AnyConvertExtern: case O::Nop: case O::Block: + case O::ExternConvertAny: case O::Loop: case O::If: case O::Else: @@ -2765,6 +3177,255 @@ RunResult Thread::DoAtomicRmwCmpxchg(Instr instr, Trap::Ptr* out_trap) { return RunResult::Ok; } +RunResult Thread::DoArrayCopy(Trap::Ptr* out_trap) { + u32 size = Pop(); + u32 src_offset = Pop(); + Ref src_ref = Pop(); + u32 dst_offset = Pop(); + Ref dst_ref = Pop(); + + TRAP_IF(src_ref == Ref::Null || dst_ref == Ref::Null, "null array ref"); + RefPtr src_array = store_.UnsafeGet(src_ref); + TRAP_IF(!src_array->IsValidRange(src_offset, size), "invalid range"); + RefPtr dst_array = store_.UnsafeGet(dst_ref); + TRAP_IF(!dst_array->IsValidRange(dst_offset, size), "invalid range"); + + if (src_array.get() == dst_array.get() && src_offset < dst_offset) { + std::copy_backward(src_array->GetItems().begin() + src_offset, + src_array->GetItems().begin() + src_offset + size, + dst_array->GetItems().begin() + dst_offset + size); + } else { + std::copy(src_array->GetItems().begin() + src_offset, + src_array->GetItems().begin() + src_offset + size, + dst_array->GetItems().begin() + dst_offset); + } + return RunResult::Ok; +} + +RunResult Thread::DoArrayFill(Trap::Ptr* out_trap) { + u32 size = Pop(); + Value value = Pop(); + u32 offset = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(!array->IsValidRange(offset, size), "invalid range"); + std::fill(array->GetItems().begin() + offset, + array->GetItems().begin() + offset + size, value); + return RunResult::Ok; +} + +RunResult Thread::DoArrayGet(Trap::Ptr* out_trap) { + u32 index = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(index >= array->Size(), "invalid index"); + Push(array->GetItem(index)); + return RunResult::Ok; +} + +RunResult Thread::DoArrayInitElem(Instr instr, Trap::Ptr* out_trap) { + ElemSegment& segment = inst_->elems()[instr.imm_u32]; + u32 size = Pop(); + u32 src_offset = Pop(); + u32 dst_offset = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(!array->IsValidRange(dst_offset, size), "invalid range"); + TRAP_IF(!segment.IsValidRange(src_offset, size), "invalid range"); + for (size_t i = 0; i < size; i++) { + array->SetItem(dst_offset + i, + Value::Make(segment.elements()[src_offset + i])); + } + return RunResult::Ok; +} + +RunResult Thread::DoArrayInitData(Instr instr, Trap::Ptr* out_trap) { + DataSegment& data = inst_->datas()[instr.imm_u32x2.snd]; + u32 size = Pop(); + u32 src_offset = Pop(); + u32 dst_offset = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(!array->IsValidRange(dst_offset, size), "invalid range"); + Type array_type = mod_->desc().func_types[instr.imm_u32x2.fst].params[0]; + u32 log2_size = GetTypeLog2Size(array_type); + TRAP_IF(size >= (~static_cast(0)) >> log2_size, "invalid range"); + TRAP_IF(!data.IsValidRange(src_offset, size << log2_size), "invalid range"); + ArrayInitFromDataSegment(log2_size, array.get(), dst_offset, size, + data.desc().data.data() + src_offset); + return RunResult::Ok; +} + +RunResult Thread::DoArrayGetPacked(Instr instr, Trap::Ptr* out_trap) { + u32 index = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(index >= array->Size(), "invalid index"); + Value value = array->GetItem(index); + + if (instr.op == Opcode::ArrayGetS) { + if (instr.imm_u32) { + value.Set(static_cast(value.Get())); + } else { + value.Set(static_cast(value.Get())); + } + } else if (instr.imm_u32) { + value.Set(static_cast(value.Get())); + } else { + value.Set(static_cast(value.Get())); + } + Push(value); + return RunResult::Ok; +} + +RunResult Thread::DoArrayNew(Instr instr) { + u32 size = Pop(); + Value value = Pop(); + Array::Ptr array = Array::New(store_, size, instr.imm_u32, mod_); + + for (size_t i = array->Size(); i > 0; i--) { + array->SetItem(i - 1, value); + } + Push(array->self()); + return RunResult::Ok; +} + +RunResult Thread::DoArrayNewData(Instr instr, Trap::Ptr* out_trap) { + DataSegment& data = inst_->datas()[instr.imm_u32x2.snd]; + u32 size = Pop(); + u32 offset = Pop(); + Type array_type = mod_->desc().func_types[instr.imm_u32x2.fst].params[0]; + u32 log2_size = GetTypeLog2Size(array_type); + + TRAP_IF(size >= (~static_cast(0)) >> log2_size, "invalid range"); + TRAP_IF(!data.IsValidRange(offset, size << log2_size), "invalid range"); + Array::Ptr array = Array::New(store_, size, instr.imm_u32x2.fst, mod_); + ArrayInitFromDataSegment(log2_size, array.get(), 0, size, + data.desc().data.data() + offset); + Push(array->self()); + return RunResult::Ok; +} + +RunResult Thread::DoArrayNewElem(Instr instr, Trap::Ptr* out_trap) { + ElemSegment& segment = inst_->elems()[instr.imm_u32x2.snd]; + u32 size = Pop(); + u32 offset = Pop(); + + TRAP_IF(!segment.IsValidRange(offset, size), "invalid range"); + Array::Ptr array = Array::New(store_, size, instr.imm_u32x2.fst, mod_); + for (size_t i = 0; i < size; i++) { + array->SetItem(i, Value::Make(segment.elements()[offset + i])); + } + Push(array->self()); + return RunResult::Ok; +} + +RunResult Thread::DoArrayNewFixed(Instr instr) { + u32 size = instr.imm_u32x2.snd; + Array::Ptr array = Array::New(store_, size, instr.imm_u32x2.fst, mod_); + + for (size_t i = size; i > 0; i--) { + array->SetItem(i - 1, Pop()); + } + Push(array->self()); + return RunResult::Ok; +} + +RunResult Thread::DoArraySet(Trap::Ptr* out_trap) { + Value value = Pop(); + u32 index = Pop(); + Ref ref = Pop(); + + TRAP_IF(ref == Ref::Null, "null array ref"); + RefPtr array = store_.UnsafeGet(ref); + TRAP_IF(index >= array->Size(), "invalid index"); + array->SetItem(index, value); + return RunResult::Ok; +} + +RunResult Thread::DoBrOnCast(Instr instr) { + Ref ref = Pick(1).Get(); + u32 result = 0; + + if (ref == Ref::Null) { + result = (instr.op == Opcode::BrOnCast); + } else { + Type type(static_cast(instr.imm_u32x2.fst), + instr.imm_u32x2.snd); + result = CheckRefCast(ref, type); + } + Push(result); + return RunResult::Ok; +} + +RunResult Thread::DoRefCast(Instr instr, Trap::Ptr* out_trap) { + Ref ref = Pick(1).Get(); + if (ref == Ref::Null) { + TRAP_IF(instr.op == Opcode::RefCast, "null reference"); + return RunResult::Ok; + } + + Type type(static_cast(instr.imm_u32x2.fst), instr.imm_u32x2.snd); + TRAP_IF(!CheckRefCast(ref, type), "type error: invalid type cast"); + return RunResult::Ok; +} + +RunResult Thread::DoRefTest(Instr instr) { + Ref ref = Pop(); + u32 result = 0; + + if (ref == Ref::Null) { + result = (instr.op == Opcode::RefTestNull); + } else { + Type type(static_cast(instr.imm_u32x2.fst), + instr.imm_u32x2.snd); + result = CheckRefCast(ref, type); + } + Push(result); + return RunResult::Ok; +} + +RunResult Thread::DoStructGetPacked(Instr instr, Trap::Ptr* out_trap) { + Ref ref = Pop(); + TRAP_IF(ref == Ref::Null, "null struct ref"); + Value value = store_.UnsafeGet(ref)->GetField(instr.imm_u32x2.fst); + + if (instr.op == Opcode::StructGetS) { + if (instr.imm_u32x2.snd) { + value.Set(static_cast(value.Get())); + } else { + value.Set(static_cast(value.Get())); + } + } else if (instr.imm_u32x2.snd) { + value.Set(static_cast(value.Get())); + } else { + value.Set(static_cast(value.Get())); + } + Push(value); + return RunResult::Ok; +} + +RunResult Thread::DoStructNew(Instr instr) { + Struct::Ptr struct_ = Struct::New(store_, instr.imm_u32, mod_); + Struct* struct_ptr = struct_.get(); + + for (size_t i = struct_ptr->Size(); i > 0; i--) { + struct_ptr->SetField(i - 1, Pop()); + } + Push(struct_->self()); + return RunResult::Ok; +} + RunResult Thread::DoThrow(Exception::Ptr exn) { Istream::Offset target_offset = Istream::kInvalidOffset; u32 target_values, target_exceptions; @@ -2967,4 +3628,4 @@ ValueType Thread::TraceSource::GetTableElementType(Index index) { } } // namespace interp -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/interp/istream.cc b/src/interp/istream.cc index 827d1b7c30..bce76e0ec6 100644 --- a/src/interp/istream.cc +++ b/src/interp/istream.cc @@ -130,6 +130,9 @@ Instr Istream::Read(Offset* offset) const { instr.kind = InstrKind::Imm_0_Op_0; break; + case Opcode::ArrayLen: + case Opcode::AnyConvertExtern: + case Opcode::ExternConvertAny: case Opcode::F32Abs: case Opcode::F32Ceil: case Opcode::F32ConvertI32S: @@ -182,6 +185,8 @@ Instr Istream::Read(Offset* offset) const { case Opcode::I16X8ExtendHighI8X16U: case Opcode::I16X8ExtendLowI8X16S: case Opcode::I16X8ExtendLowI8X16U: + case Opcode::I31GetS: + case Opcode::I31GetU: case Opcode::I32Clz: case Opcode::I32Ctz: case Opcode::I32Eqz: @@ -245,6 +250,7 @@ Instr Istream::Read(Offset* offset) const { case Opcode::F64X2ConvertLowI32X4S: case Opcode::F64X2ConvertLowI32X4U: case Opcode::I8X16Splat: + case Opcode::RefI31: case Opcode::RefIsNull: case Opcode::RefAsNonNull: case Opcode::V128Not: @@ -261,10 +267,12 @@ Instr Istream::Read(Offset* offset) const { case Opcode::I32X4RelaxedTruncF32X4U: case Opcode::I32X4RelaxedTruncF64X2SZero: case Opcode::I32X4RelaxedTruncF64X2UZero: + case Opcode::RefEq: // 0 immediates, 1 operand. instr.kind = InstrKind::Imm_0_Op_1; break; + case Opcode::ArrayGet: case Opcode::F32Add: case Opcode::F32Copysign: case Opcode::F32Div: @@ -485,6 +493,7 @@ Instr Istream::Read(Offset* offset) const { instr.kind = InstrKind::Imm_0_Op_2; break; + case Opcode::ArraySet: case Opcode::Select: case Opcode::SelectT: case Opcode::F32X4RelaxedMadd: @@ -500,6 +509,16 @@ Instr Istream::Read(Offset* offset) const { instr.kind = InstrKind::Imm_0_Op_3; break; + case Opcode::ArrayFill: + // 0 immediates, 4 operands + instr.kind = InstrKind::Imm_0_Op_4; + break; + + case Opcode::ArrayCopy: + // 0 immediates, 5 operands + instr.kind = InstrKind::Imm_0_Op_5; + break; + case Opcode::Br: // Jump target immediate, 0 operands. instr.kind = InstrKind::Imm_Jump_Op_0; @@ -526,6 +545,7 @@ Instr Istream::Read(Offset* offset) const { case Opcode::DataDrop: case Opcode::ElemDrop: case Opcode::RefFunc: + case Opcode::StructNewDefault: case Opcode::Throw: case Opcode::Rethrow: // Index immediate, 0 operands. @@ -533,16 +553,22 @@ Instr Istream::Read(Offset* offset) const { instr.imm_u32 = ReadAt(offset); break; + case Opcode::ArrayNewDefault: case Opcode::GlobalSet: case Opcode::LocalSet: case Opcode::LocalTee: case Opcode::MemoryGrow: + case Opcode::StructGet: case Opcode::TableGet: // Index immediate, 1 operand. instr.kind = InstrKind::Imm_Index_Op_1; instr.imm_u32 = ReadAt(offset); break; + case Opcode::ArrayNew: + case Opcode::ArrayGetS: + case Opcode::ArrayGetU: + case Opcode::StructSet: case Opcode::TableSet: case Opcode::TableGrow: // Index immediate, 2 operands. @@ -557,8 +583,15 @@ Instr Istream::Read(Offset* offset) const { instr.imm_u32 = ReadAt(offset); break; + case Opcode::ArrayInitElem: + // Index immediate, 3 operands. + instr.kind = InstrKind::Imm_Index_Op_4; + instr.imm_u32 = ReadAt(offset); + break; + case Opcode::Call: case Opcode::InterpCallImport: + case Opcode::StructNew: instr.kind = InstrKind::Imm_Index_Op_N; instr.imm_u32 = ReadAt(offset); break; @@ -571,6 +604,29 @@ Instr Istream::Read(Offset* offset) const { instr.imm_u32x2.snd = ReadAt(offset); break; + case Opcode::ArrayNewFixed: + case Opcode::BrOnCast: + case Opcode::BrOnCastFail: + case Opcode::RefCast: + case Opcode::RefCastNull: + case Opcode::RefTest: + case Opcode::RefTestNull: + case Opcode::StructGetS: + case Opcode::StructGetU: + // Index + index immediates, 1 operand. + instr.kind = InstrKind::Imm_Index_Index_Op_1; + instr.imm_u32x2.fst = ReadAt(offset); + instr.imm_u32x2.snd = ReadAt(offset); + break; + + case Opcode::ArrayNewData: + case Opcode::ArrayNewElem: + // Index + index immediates, 2 operands. + instr.kind = InstrKind::Imm_Index_Index_Op_2; + instr.imm_u32x2.fst = ReadAt(offset); + instr.imm_u32x2.snd = ReadAt(offset); + break; + case Opcode::MemoryInit: case Opcode::TableInit: case Opcode::MemoryCopy: @@ -581,6 +637,13 @@ Instr Istream::Read(Offset* offset) const { instr.imm_u32x2.snd = ReadAt(offset); break; + case Opcode::ArrayInitData: + // Index + index immediates, 4 operands. + instr.kind = InstrKind::Imm_Index_Index_Op_3; + instr.imm_u32x2.fst = ReadAt(offset); + instr.imm_u32x2.snd = ReadAt(offset); + break; + case Opcode::F32Load: case Opcode::F64Load: case Opcode::V128Load8X8S: @@ -863,6 +926,20 @@ Istream::Offset Istream::Trace(Stream* stream, source->Pick(1, instr).c_str()); break; + case InstrKind::Imm_0_Op_4: + stream->Writef(" %s, %s, %s, %s\n", source->Pick(4, instr).c_str(), + source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); + break; + + case InstrKind::Imm_0_Op_5: + stream->Writef( + " %s, %s, %s, %s, %s\n", source->Pick(5, instr).c_str(), + source->Pick(4, instr).c_str(), source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str()); + break; + case InstrKind::Imm_Jump_Op_0: stream->Writef(" @%u\n", instr.imm_u32); break; @@ -893,10 +970,28 @@ Istream::Offset Istream::Trace(Stream* stream, source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str()); break; + case InstrKind::Imm_Index_Op_4: + stream->Writef( + " $%u, %s, %s, %s, %s\n", instr.imm_u32, + source->Pick(4, instr).c_str(), source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str()); + break; + case InstrKind::Imm_Index_Op_N: stream->Writef(" $%u\n", instr.imm_u32); // TODO param/result count? break; + case InstrKind::Imm_Index_Index_Op_1: + stream->Writef(" $%u, $%u, %s\n", instr.imm_u32x2.fst, + instr.imm_u32x2.snd, source->Pick(1, instr).c_str()); + break; + + case InstrKind::Imm_Index_Index_Op_2: + stream->Writef(" $%u, $%u, %s, %s\n", instr.imm_u32x2.fst, + instr.imm_u32x2.snd, source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); + break; + case InstrKind::Imm_Index_Index_Op_3: stream->Writef(" $%u, $%u, %s, %s, %s\n", instr.imm_u32x2.fst, instr.imm_u32x2.snd, source->Pick(3, instr).c_str(), @@ -904,6 +999,14 @@ Istream::Offset Istream::Trace(Stream* stream, source->Pick(1, instr).c_str()); break; + case InstrKind::Imm_Index_Index_Op_4: + stream->Writef(" $%u, $%u, %s, %s, %s, %s\n", instr.imm_u32x2.fst, + instr.imm_u32x2.snd, source->Pick(4, instr).c_str(), + source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); + break; + case InstrKind::Imm_Index_Index_Op_N: stream->Writef(" $%u, $%u\n", instr.imm_u32x2.fst, instr.imm_u32x2.snd); // TODO param/result count? @@ -986,4 +1089,4 @@ Istream::Offset Istream::Trace(Stream* stream, } } // namespace interp -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/ir-util.cc b/src/ir-util.cc index 16a85cd220..25ac38c63e 100644 --- a/src/ir-util.cc +++ b/src/ir-util.cc @@ -97,6 +97,10 @@ void ModuleContext::EndFunc() { ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { switch (expr.type()) { + case ExprType::ArrayGet: + case ExprType::ArrayNew: + case ExprType::ArrayNewData: + case ExprType::ArrayNewElem: case ExprType::AtomicNotify: case ExprType::AtomicRmw: case ExprType::Binary: @@ -107,6 +111,7 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { case ExprType::AtomicStore: case ExprType::Store: case ExprType::TableSet: + case ExprType::StructSet: return {2, 0}; case ExprType::Block: @@ -120,6 +125,11 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { return {arity + 1, arity}; } + case ExprType::BrOnCast: { + Index arity = GetLabelArity(cast(&expr)->label_var); + return {arity + 1, arity + 1}; + } + case ExprType::BrOnNonNull: { Index arity = GetLabelArity(cast(&expr)->var); return {arity + 1, arity}; @@ -172,6 +182,7 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { case ExprType::TableSize: case ExprType::RefNull: case ExprType::RefFunc: + case ExprType::StructNewDefault: return {0, 1}; case ExprType::Unreachable: @@ -183,6 +194,7 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { case ExprType::CodeMetadata: return {0, 0}; + case ExprType::ArraySet: case ExprType::MemoryInit: case ExprType::TableInit: case ExprType::MemoryFill: @@ -191,17 +203,22 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { case ExprType::TableFill: return {3, 0}; + case ExprType::ArrayNewDefault: case ExprType::AtomicLoad: case ExprType::Convert: + case ExprType::GCUnary: case ExprType::Load: case ExprType::LocalTee: case ExprType::MemoryGrow: case ExprType::Unary: case ExprType::TableGet: + case ExprType::RefAsNonNull: + case ExprType::RefCast: case ExprType::RefIsNull: + case ExprType::RefTest: case ExprType::LoadSplat: case ExprType::LoadZero: - case ExprType::RefAsNonNull: + case ExprType::StructGet: return {1, 1}; case ExprType::Drop: @@ -288,7 +305,28 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { case ExprType::SimdShuffleOp: return {2, 1}; + + case ExprType::ArrayCopy: + return {5, 0}; + + case ExprType::ArrayFill: + case ExprType::ArrayInitData: + case ExprType::ArrayInitElem: + return {4, 0}; + + case ExprType::ArrayNewFixed: { + return {cast(&expr)->count, 1}; + } + + case ExprType::StructNew: { + auto struct_new = cast(&expr); + Index operand_count = 0; + if (const StructType* struct_ = module.GetStructType(struct_new->var)) { + operand_count = struct_->fields.size(); + } + return {operand_count, 0}; + } } WABT_UNREACHABLE; -} +} \ No newline at end of file diff --git a/src/ir.cc b/src/ir.cc index 46713db04c..99ca644903 100644 --- a/src/ir.cc +++ b/src/ir.cc @@ -25,17 +25,29 @@ namespace { const char* ExprTypeName[] = { - "AtomicFence", + "ArrayCopy", + "ArrayFill", + "ArrayGet", + "ArrayInitData", + "ArrayInitElem", + "ArrayNew", + "ArrayNewData", + "ArrayNewDefault", + "ArrayNewElem", + "ArrayNewFixed", + "ArraySet", "AtomicLoad", "AtomicRmw", "AtomicRmwCmpxchg", "AtomicStore", "AtomicNotify", + "AtomicFence", "AtomicWait", "Binary", "Block", "Br", "BrIf", + "BrOnCast", "BrOnNonNull", "BrOnNull", "BrTable", @@ -47,6 +59,7 @@ const char* ExprTypeName[] = { "Const", "Convert", "Drop", + "GCUnary", "GlobalGet", "GlobalSet", "If", @@ -63,9 +76,11 @@ const char* ExprTypeName[] = { "MemorySize", "Nop", "RefAsNonNull", + "RefCast", "RefIsNull", "RefFunc", "RefNull", + "RefTest", "Rethrow", "Return", "ReturnCall", @@ -76,6 +91,10 @@ const char* ExprTypeName[] = { "SimdLoadLane", "SimdStoreLane", "SimdShuffleOp", + "StructGet", + "StructNew", + "StructNewDefault", + "StructSet", "LoadSplat", "LoadZero", "Store", @@ -345,8 +364,49 @@ FuncType* Module::GetFuncType(const Var& var) { return dyn_cast(types[index]); } +const StructType* Module::GetStructType(const Var& var) const { + return const_cast(this)->GetStructType(var); +} + +StructType* Module::GetStructType(const Var& var) { + Index index = type_bindings.FindIndex(var); + if (index >= types.size()) { + return nullptr; + } + return dyn_cast(types[index]); +} + +const ArrayType* Module::GetArrayType(const Var& var) const { + return const_cast(this)->GetArrayType(var); +} + +ArrayType* Module::GetArrayType(const Var& var) { + Index index = type_bindings.FindIndex(var); + if (index >= types.size()) { + return nullptr; + } + return dyn_cast(types[index]); +} + Index Module::GetFuncTypeIndex(const FuncSignature& sig) const { + size_t range_index = 0; + size_t range_start = types.size(); + + if (range_index < recursive_ranges.size()) { + range_start = recursive_ranges[range_index].first_type_index; + } + for (size_t i = 0; i < types.size(); ++i) { + if (i == range_start) { + i += recursive_ranges[range_index].type_count - 1; + range_index++; + + if (range_index < recursive_ranges.size()) { + range_start = recursive_ranges[range_index].first_type_index; + } + continue; + } + if (auto* func_type = dyn_cast(types[i])) { if (func_type->sig == sig) { return i; @@ -419,6 +479,10 @@ void Module::AppendField(std::unique_ptr field) { fields.push_back(std::move(field)); } +void Module::AppendField(std::unique_ptr field) { + fields.push_back(std::move(field)); +} + void Module::AppendField(std::unique_ptr field) { Global& global = field->global; if (!global.name.empty()) { @@ -562,6 +626,10 @@ void Module::AppendField(std::unique_ptr field) { case ModuleFieldType::Tag: AppendField(cast(std::move(field))); break; + + case ModuleFieldType::EmptyRec: + AppendField(cast(std::move(field))); + break; } } @@ -703,7 +771,18 @@ void Var::Destroy() { } } -uint8_t ElemSegment::GetFlags(const Module* module) const { +void TypeEntryGCTypeExtension::InitSubTypes(Index* sub_type_list, + Index sub_type_count) { + sub_types.clear(); + sub_types.reserve(sub_type_count); + + for (Index i = 0; i < sub_type_count; i++) { + sub_types.push_back(Var(sub_type_list[i], Location())); + } +} + +uint8_t ElemSegment::GetFlags(const Module* module, + bool function_references_enabled) const { uint8_t flags = 0; switch (kind) { @@ -724,15 +803,20 @@ uint8_t ElemSegment::GetFlags(const Module* module) const { break; } - bool all_ref_func = - elem_type == Type::FuncRef && - std::all_of(elem_exprs.begin(), elem_exprs.end(), - [](const ExprList& elem_expr) { - return elem_expr.front().type() == ExprType::RefFunc; - }); - - if (!all_ref_func) { + if (function_references_enabled && + elem_type != Type(Type::FuncRef, Type::ReferenceNonNull)) { flags |= SegUseElemExprs; + } else { + bool all_ref_func = + elem_type == Type::FuncRef && + std::all_of(elem_exprs.begin(), elem_exprs.end(), + [](const ExprList& elem_expr) { + return elem_expr.front().type() == ExprType::RefFunc; + }); + + if (!all_ref_func) { + flags |= SegUseElemExprs; + } } return flags; @@ -753,4 +837,4 @@ uint8_t DataSegment::GetFlags(const Module* module) const { return flags; } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/lexer-keywords.txt b/src/lexer-keywords.txt index eda61a3b7e..f1f1e3622d 100644 --- a/src/lexer-keywords.txt +++ b/src/lexer-keywords.txt @@ -17,7 +17,25 @@ struct TokenInfo { }; }; %% -array, Type::Array, TokenType::Array +any, Type::AnyRef, TokenType::Any +anyref, Type::AnyRef +any.convert_extern, TokenType::GCUnary, Opcode::AnyConvertExtern +array, Type::ArrayRef, TokenType::Array +arrayref, Type::ArrayRef +array.copy, TokenType::ArrayCopy, Opcode::ArrayCopy +array.fill, TokenType::ArrayFill, Opcode::ArrayFill +array.get, TokenType::ArrayGet, Opcode::ArrayGet +array.get_s, TokenType::ArrayGetS, Opcode::ArrayGetS +array.get_u, TokenType::ArrayGetU, Opcode::ArrayGetU +array.init_data, TokenType::ArrayInitData, Opcode::ArrayInitData +array.init_elem, TokenType::ArrayInitElem, Opcode::ArrayInitElem +array.len, TokenType::GCUnary, Opcode::ArrayLen +array.new, TokenType::ArrayNew, Opcode::ArrayNew +array.new_data, TokenType::ArrayNewData, Opcode::ArrayNewData +array.new_default, TokenType::ArrayNewDefault, Opcode::ArrayNewDefault +array.new_elem, TokenType::ArrayNewElem, Opcode::ArrayNewElem +array.new_fixed, TokenType::ArrayNewFixed, Opcode::ArrayNewFixed +array.set, TokenType::ArraySet, Opcode::ArraySet after, TokenType::After assert_exception, TokenType::AssertException assert_exhaustion, TokenType::AssertExhaustion @@ -31,6 +49,8 @@ before, TokenType::Before binary, TokenType::Bin block, TokenType::Block, Opcode::Block br_if, TokenType::BrIf, Opcode::BrIf +br_on_cast, TokenType::BrOnCast, Opcode::BrOnCast +br_on_cast_fail, TokenType::BrOnCast, Opcode::BrOnCastFail br_on_non_null, TokenType::BrOnNonNull, Opcode::BrOnNonNull br_on_null, TokenType::BrOnNull, Opcode::BrOnNull br_table, TokenType::BrTable, Opcode::BrTable @@ -55,8 +75,11 @@ elem, TokenType::Elem else, TokenType::Else, Opcode::Else end, TokenType::End, Opcode::End tag, TokenType::Tag +eq, Type::EqRef, TokenType::Eq +eqref, Type::EqRef extern, Type::ExternRef, TokenType::Extern externref, Type::ExternRef +extern.convert_any, TokenType::GCUnary, Opcode::ExternConvertAny exn, Type::ExnRef, TokenType::Exn exnref, Type::ExnRef export, TokenType::Export @@ -185,6 +208,7 @@ f64x2.convert_low_i32x4_u, TokenType::Unary, Opcode::F64X2ConvertLowI32X4U f64x2.promote_low_f32x4, TokenType::Unary, Opcode::F64X2PromoteLowF32X4 f64x2, TokenType::F64X2 field, TokenType::Field +final, TokenType::Final func, Type::FuncRef, TokenType::Func funcref, Type::FuncRef function, TokenType::Function @@ -192,6 +216,7 @@ get, TokenType::Get global.get, TokenType::GlobalGet, Opcode::GlobalGet global.set, TokenType::GlobalSet, Opcode::GlobalSet global, TokenType::Global +i16, Type::I16 i16x8.abs, TokenType::Unary, Opcode::I16X8Abs i16x8.add_sat_s, TokenType::Binary, Opcode::I16X8AddSatS i16x8.add_sat_u, TokenType::Binary, Opcode::I16X8AddSatU @@ -244,6 +269,10 @@ i16x8.extend_high_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendHighI8X16S i16x8.extend_high_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendHighI8X16U i16x8.extend_low_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendLowI8X16S i16x8.extend_low_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendLowI8X16U +i31, Type::I31Ref, TokenType::I31 +i31ref, Type::I31Ref +i31.get_s, TokenType::GCUnary, Opcode::I31GetS +i31.get_u, TokenType::GCUnary, Opcode::I31GetU i32.add, TokenType::Binary, Opcode::I32Add i32.and, TokenType::Binary, Opcode::I32And i32.atomic.load16_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad16U @@ -496,6 +525,7 @@ i64x2.extmul_low_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U i64x2.extmul_high_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U i64x2, TokenType::I64X2 i64.xor, TokenType::Binary, Opcode::I64Xor +i8, Type::I8 i8x16.abs, TokenType::Unary, Opcode::I8X16Abs i8x16.add_sat_s, TokenType::Binary, Opcode::I8X16AddSatS i8x16.add_sat_u, TokenType::Binary, Opcode::I8X16AddSatU @@ -557,19 +587,33 @@ module, TokenType::Module mut, TokenType::Mut nan:arithmetic, TokenType::NanArithmetic nan:canonical, TokenType::NanCanonical +noextern, Type::NullExternRef, TokenType::NoExtern +nofunc, Type::NullFuncRef, TokenType::NoFunc +none, Type::NullRef, TokenType::None nop, TokenType::Nop, Opcode::Nop null, TokenType::Null +nullfuncref, Type::NullFuncRef +nullexternref, Type::NullExternRef +nullref, Type::NullRef offset, TokenType::Offset output, TokenType::Output pagesize, TokenType::PageSize param, TokenType::Param +rec, TokenType::Rec ref, TokenType::Ref quote, TokenType::Quote +ref.array, TokenType::RefArray ref.as_non_null, TokenType::RefAsNonNull, Opcode::RefAsNonNull +ref.cast, TokenType::RefCast, Opcode::RefCast +ref.eq, TokenType::RefEq, Opcode::RefEq ref.extern, TokenType::RefExtern ref.func, TokenType::RefFunc, Opcode::RefFunc +ref.host, TokenType::RefHost +ref.i31, TokenType::RefI31, Opcode::RefI31 ref.is_null, TokenType::RefIsNull, Opcode::RefIsNull ref.null, TokenType::RefNull, Opcode::RefNull +ref.struct, TokenType::RefStruct +ref.test, TokenType::RefTest, Opcode::RefTest register, TokenType::Register result, TokenType::Result rethrow, TokenType::Rethrow, Opcode::Rethrow @@ -580,7 +624,15 @@ return, TokenType::Return, Opcode::Return select, TokenType::Select, Opcode::Select shared, TokenType::Shared start, TokenType::Start -struct, Type::Struct, TokenType::Struct +struct, Type::StructRef, TokenType::Struct +structref, Type::StructRef +struct.get, TokenType::StructGet, Opcode::StructGet +struct.get_s, TokenType::StructGetS, Opcode::StructGetS +struct.get_u, TokenType::StructGetU, Opcode::StructGetU +struct.new, TokenType::StructNew, Opcode::StructNew +struct.new_default, TokenType::StructNewDefault, Opcode::StructNewDefault +struct.set, TokenType::StructSet, Opcode::StructSet +sub, TokenType::Sub table.copy, TokenType::TableCopy, Opcode::TableCopy table.fill, TokenType::TableFill, Opcode::TableFill table.get, TokenType::TableGet, Opcode::TableGet @@ -622,4 +674,4 @@ v128.store16_lane, TokenType::SimdStoreLane, Opcode::V128Store16Lane v128.store32_lane, TokenType::SimdStoreLane, Opcode::V128Store32Lane v128.store64_lane, TokenType::SimdStoreLane, Opcode::V128Store64Lane i8x16.shuffle, TokenType::SimdShuffleOp, Opcode::I8X16Shuffle -i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle +i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle \ No newline at end of file diff --git a/src/opcode.cc b/src/opcode.cc index b01e615de5..51ce5de6fa 100644 --- a/src/opcode.cc +++ b/src/opcode.cc @@ -366,6 +366,36 @@ bool Opcode::IsEnabled(const Features& features) const { case Opcode::RefAsNonNull: return features.function_references_enabled(); + case Opcode::RefEq: + case Opcode::StructNew: + case Opcode::StructNewDefault: + case Opcode::StructGet: + case Opcode::StructGetS: + case Opcode::StructGetU: + case Opcode::StructSet: + case Opcode::ArrayNew: + case Opcode::ArrayNewDefault: + case Opcode::ArrayNewFixed: + case Opcode::ArrayNewData: + case Opcode::ArrayNewElem: + case Opcode::ArrayGet: + case Opcode::ArrayGetS: + case Opcode::ArrayGetU: + case Opcode::ArraySet: + case Opcode::ArrayLen: + case Opcode::ArrayFill: + case Opcode::ArrayCopy: + case Opcode::ArrayInitData: + case Opcode::ArrayInitElem: + case Opcode::AnyConvertExtern: + case Opcode::ExternConvertAny: + case Opcode::RefCast: + case Opcode::RefI31: + case Opcode::RefTest: + case Opcode::I31GetS: + case Opcode::I31GetU: + return features.gc_enabled(); + // Interpreter opcodes are never "enabled". case Opcode::InterpAlloca: case Opcode::InterpBrUnless: diff --git a/src/resolve-names.cc b/src/resolve-names.cc index d24f2f5c37..1f4081019a 100644 --- a/src/resolve-names.cc +++ b/src/resolve-names.cc @@ -40,6 +40,7 @@ class NameResolver : public ExprVisitor::DelegateNop { Result EndBlockExpr(BlockExpr*) override; Result OnBrExpr(BrExpr*) override; Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrOnCastExpr(BrOnCastExpr*) override; Result OnBrOnNonNullExpr(BrOnNonNullExpr*) override; Result OnBrOnNullExpr(BrOnNullExpr*) override; Result OnBrTableExpr(BrTableExpr*) override; @@ -86,6 +87,23 @@ class NameResolver : public ExprVisitor::DelegateNop { Result OnRethrowExpr(RethrowExpr*) override; Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + Result OnArrayCopyExpr(ArrayCopyExpr*) override; + Result OnArrayFillExpr(ArrayFillExpr*) override; + Result OnArrayGetExpr(ArrayGetExpr*) override; + Result OnArrayInitDataExpr(ArrayInitDataExpr*) override; + Result OnArrayInitElemExpr(ArrayInitElemExpr*) override; + Result OnArrayNewExpr(ArrayNewExpr*) override; + Result OnArrayNewDataExpr(ArrayNewDataExpr*) override; + Result OnArrayNewDefaultExpr(ArrayNewDefaultExpr*) override; + Result OnArrayNewElemExpr(ArrayNewElemExpr*) override; + Result OnArrayNewFixedExpr(ArrayNewFixedExpr*) override; + Result OnArraySetExpr(ArraySetExpr*) override; + Result OnRefCastExpr(RefCastExpr*) override; + Result OnRefTestExpr(RefTestExpr*) override; + Result OnStructGetExpr(StructGetExpr*) override; + Result OnStructNewExpr(StructNewExpr*) override; + Result OnStructNewDefaultExpr(StructNewDefaultExpr*) override; + Result OnStructSetExpr(StructSetExpr*) override; private: void PrintError(const Location* loc, const char* fmt, ...); @@ -107,10 +125,13 @@ class NameResolver : public ExprVisitor::DelegateNop { void ResolveElemSegmentVar(Var* var); void ResolveLocalVar(Var* var); void ResolveBlockDeclarationVar(BlockDeclaration* decl); + void ResolveStructFieldVar(Var* type, Var* var); void VisitFunc(Func* func); void VisitExport(Export* export_); void VisitGlobal(Global* global); + void VisitType(TypeEntry* type); void VisitTag(Tag* tag); + void VisitTable(Table* table); void VisitElemSegment(ElemSegment* segment); void VisitDataSegment(DataSegment* segment); void VisitScriptModule(ScriptModule* script_module); @@ -248,6 +269,34 @@ void NameResolver::ResolveBlockDeclarationVar(BlockDeclaration* decl) { } } +void NameResolver::ResolveStructFieldVar(Var* type, Var* var) { + ResolveFuncTypeVar(type); + + if (type->is_index() && var->is_name()) { + // If type is still a name, it cannot be resolved. + Index index = type->index(); + + if (index < current_module_->types.size() && + current_module_->types[index]->kind() == TypeEntryKind::Struct) { + StructType* struct_type = cast(current_module_->types[index]); + + Index size = static_cast(struct_type->fields.size()); + + for (Index i = 0; i < size; i++) { + if (struct_type->fields[i].name == var->name()) { + var->set_index(i); + break; + } + } + } + + if (var->is_name()) { + PrintError(&var->loc, "undefined struct field \"%s\"", + var->name().c_str()); + } + } +} + Result NameResolver::BeginBlockExpr(BlockExpr* expr) { PushLabel(expr->block.label); ResolveBlockDeclarationVar(&expr->block.decl); @@ -280,6 +329,13 @@ Result NameResolver::OnBrIfExpr(BrIfExpr* expr) { return Result::Ok; } +Result NameResolver::OnBrOnCastExpr(BrOnCastExpr* expr) { + ResolveLabelVar(&expr->label_var); + ResolveFuncTypeVar(&expr->type1_var); + ResolveFuncTypeVar(&expr->type2_var); + return Result::Ok; +} + Result NameResolver::OnBrOnNonNullExpr(BrOnNonNullExpr* expr) { ResolveLabelVar(&expr->var); return Result::Ok; @@ -531,6 +587,96 @@ Result NameResolver::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { return Result::Ok; } +Result NameResolver::OnArrayCopyExpr(ArrayCopyExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayFillExpr(ArrayFillExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayGetExpr(ArrayGetExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + return Result::Ok; +} + +Result NameResolver::OnArrayInitDataExpr(ArrayInitDataExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + ResolveDataSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayInitElemExpr(ArrayInitElemExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + ResolveElemSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayNewExpr(ArrayNewExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayNewDataExpr(ArrayNewDataExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + ResolveDataSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayNewDefaultExpr(ArrayNewDefaultExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayNewElemExpr(ArrayNewElemExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + ResolveElemSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnArrayNewFixedExpr(ArrayNewFixedExpr* expr) { + ResolveFuncTypeVar(&expr->type_var); + return Result::Ok; +} + +Result NameResolver::OnArraySetExpr(ArraySetExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnRefCastExpr(RefCastExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnRefTestExpr(RefTestExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnStructGetExpr(StructGetExpr* expr) { + ResolveStructFieldVar(&expr->type_var, &expr->var); + return Result::Ok; +} + +Result NameResolver::OnStructNewExpr(StructNewExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnStructNewDefaultExpr(StructNewDefaultExpr* expr) { + ResolveFuncTypeVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnStructSetExpr(StructSetExpr* expr) { + ResolveStructFieldVar(&expr->type_var, &expr->var); + return Result::Ok; +} + void NameResolver::VisitFunc(Func* func) { current_func_ = func; if (func->decl.has_func_type) { @@ -576,20 +722,31 @@ void NameResolver::VisitGlobal(Global* global) { visitor_.VisitExprList(global->init_expr); } +void NameResolver::VisitType(TypeEntry* type) { + size_t size = type->gc_ext.sub_types.size(); + + for (size_t i = 0; i < size; i++) { + ResolveFuncTypeVar(&type->gc_ext.sub_types[i]); + } +} + void NameResolver::VisitTag(Tag* tag) { if (tag->decl.has_func_type) { ResolveFuncTypeVar(&tag->decl.type_var); } } +void NameResolver::VisitTable(Table* table) { + if (!table->init_expr.empty()) { + visitor_.VisitExprList(table->init_expr); + } +} + void NameResolver::VisitElemSegment(ElemSegment* segment) { ResolveTableVar(&segment->table_var); visitor_.VisitExprList(segment->offset); for (ExprList& elem_expr : segment->elem_exprs) { - if (elem_expr.size() == 1 && - elem_expr.front().type() == ExprType::RefFunc) { - ResolveFuncVar(&cast(&elem_expr.front())->var); - } + visitor_.VisitExprList(elem_expr); } } @@ -614,8 +771,12 @@ Result NameResolver::VisitModule(Module* module) { VisitExport(export_); for (Global* global : module->globals) VisitGlobal(global); + for (TypeEntry* type : module->types) + VisitType(type); for (Tag* tag : module->tags) VisitTag(tag); + for (Table* table : module->tables) + VisitTable(table); for (ElemSegment* elem_segment : module->elem_segments) VisitElemSegment(elem_segment); for (DataSegment* data_segment : module->data_segments) @@ -695,4 +856,4 @@ Result ResolveNamesScript(Script* script, Errors* errors) { return resolver.VisitScript(script); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/shared-validator.cc b/src/shared-validator.cc index 5fac43aa72..19a0f3aa00 100644 --- a/src/shared-validator.cc +++ b/src/shared-validator.cc @@ -29,7 +29,7 @@ TypeVector SharedValidator::ToTypeVector(Index count, const Type* types) { SharedValidator::SharedValidator(Errors* errors, const ValidateOptions& options) : options_(options), errors_(errors), - typechecker_(options.features, func_types_) { + typechecker_(options.features, type_fields_) { typechecker_.set_error_callback( [this](const char* msg) { OnTypecheckerError(msg); }); } @@ -46,44 +46,80 @@ void SharedValidator::OnTypecheckerError(const char* msg) { PrintError(expr_loc_, "%s", msg); } +Result SharedValidator::OnRecursiveType(Index first_type_index, + Index type_count) { + if (type_count > 0) { + type_fields_.recursive_ranges.emplace_back( + RecursiveRange(first_type_index, type_count)); + last_rec_type_end_ = first_type_index + type_count; + } + return Result::Ok; +} + Result SharedValidator::OnFuncType(const Location& loc, Index param_count, const Type* param_types, Index result_count, const Type* result_types, - Index type_index) { + Index type_index, + GCTypeExtension* gc_ext) { Result result = Result::Ok; if (!options_.features.multi_value_enabled() && result_count > 1) { result |= PrintError(loc, "multiple result values are not supported without " "multi-value enabled."); } - if (options_.features.reference_types_enabled()) { + + type_fields_.PushFunc(FuncType{ToTypeVector(param_count, param_types), + ToTypeVector(result_count, result_types), + type_index}); + + if (options_.features.function_references_enabled()) { + Index end_index = GetEndIndex(); + for (Index i = 0; i < param_count; i++) { - result |= CheckReferenceType(loc, param_types[i], "params"); + result |= CheckReferenceType(loc, param_types[i], end_index, "params"); } for (Index i = 0; i < result_count; i++) { - result |= CheckReferenceType(loc, result_types[i], "results"); + result |= CheckReferenceType(loc, result_types[i], end_index, "results"); } + + type_validation_result_ |= result; + result |= CheckGCTypeExtension(loc, gc_ext); } - func_types_.emplace( - num_types_++, - FuncType{ToTypeVector(param_count, param_types), - ToTypeVector(result_count, result_types), type_index}); + return result; } -Result SharedValidator::OnStructType(const Location&, +Result SharedValidator::OnStructType(const Location& loc, Index field_count, - TypeMut* fields) { - struct_types_.emplace(num_types_++, StructType{TypeMutVector( - &fields[0], &fields[field_count])}); - return Result::Ok; + TypeMut* fields, + GCTypeExtension* gc_ext) { + type_fields_.PushStruct( + StructType{TypeMutVector(&fields[0], &fields[field_count])}); + + Result result = Result::Ok; + Index end_index = GetEndIndex(); + + for (Index i = 0; i < field_count; i++) { + result |= CheckReferenceType(loc, fields[i].type, end_index, "params"); + } + + type_validation_result_ |= result; + result |= CheckGCTypeExtension(loc, gc_ext); + return result; } -Result SharedValidator::OnArrayType(const Location&, TypeMut field) { - array_types_.emplace(num_types_++, ArrayType{field}); - return Result::Ok; +Result SharedValidator::OnArrayType(const Location& loc, + TypeMut field, + GCTypeExtension* gc_ext) { + type_fields_.PushArray(ArrayType{field}); + + Result result = CheckReferenceType(loc, field.type, GetEndIndex(), "params"); + + type_validation_result_ |= result; + result |= CheckGCTypeExtension(loc, gc_ext); + return result; } Result SharedValidator::OnFunction(const Location& loc, Var sig_var) { @@ -122,7 +158,9 @@ Result SharedValidator::CheckLimits(const Location& loc, Result SharedValidator::OnTable(const Location& loc, Type elem_type, - const Limits& limits) { + const Limits& limits, + bool is_import, + bool has_init_expr) { Result result = Result::Ok; // Must be checked by parser or binary reader. assert(elem_type.IsRef()); @@ -134,18 +172,18 @@ Result SharedValidator::OnTable(const Location& loc, if (limits.is_shared) { result |= PrintError(loc, "tables may not be shared"); } - if (elem_type != Type::FuncRef && - !options_.features.reference_types_enabled()) { + if (options_.features.reference_types_enabled()) { + if (!elem_type.IsRef()) { + result |= PrintError(loc, "tables must have reference types"); + } else if (!is_import && !has_init_expr && !elem_type.IsNullableRef()) { + result |= PrintError(loc, "missing table initializer"); + } + } else if (elem_type != Type::FuncRef) { result |= PrintError(loc, "tables must have funcref type"); } - result |= CheckReferenceType(loc, elem_type, "tables"); - - // TODO: support table initializers - if (elem_type.IsRef() && !elem_type.IsNullableRef()) { - result |= - PrintError(loc, "currently non-nullable references are not supported"); - } + result |= + CheckReferenceType(loc, elem_type, type_fields_.NumTypes(), "tables"); tables_.push_back(TableType{elem_type, limits}); return result; @@ -198,7 +236,8 @@ Result SharedValidator::OnGlobalImport(const Location& loc, Result SharedValidator::OnGlobal(const Location& loc, Type type, bool mutable_) { - CHECK_RESULT(CheckReferenceType(loc, type, "globals")); + CHECK_RESULT( + CheckReferenceType(loc, type, type_fields_.NumTypes(), "globals")); globals_.push_back(GlobalType{type, mutable_}); return Result::Ok; } @@ -217,17 +256,116 @@ Result SharedValidator::CheckType(const Location& loc, Result SharedValidator::CheckReferenceType(const Location& loc, Type type, + Index end_index, const char* desc) { - if (type.IsReferenceWithIndex()) { - Index index = type.GetReferenceIndex(); - auto iter = func_types_.find(index); + if (type.IsReferenceWithIndex() && type.GetReferenceIndex() >= end_index) { + return PrintError(loc, "reference %" PRIindex " is out of range in %s", + type.GetReferenceIndex(), desc); + } + + return Result::Ok; +} + +Result SharedValidator::CheckGCTypeExtension(const Location& loc, + GCTypeExtension* gc_ext) { + assert(options_.features.function_references_enabled()); + + TypeEntry& entry = type_fields_.type_entries.back(); + Index current_index = type_fields_.NumTypes() - 1; + Index end_index; + + if (current_index < last_rec_type_end_) { + end_index = last_rec_type_end_; + } else { + type_fields_.recursive_ranges.emplace_back( + RecursiveRange(current_index, 1)); + end_index = current_index + 1; + } + + // Check default. + assert(entry.canonical_index == current_index && entry.is_final_sub_type && + entry.first_sub_type == kInvalidIndex); + entry.is_final_sub_type = gc_ext->is_final_sub_type; + + if (gc_ext->sub_type_count > 1) { + type_validation_result_ = Result::Error; + return PrintError(loc, "sub type count %" PRIindex " is limited to 1", + gc_ext->sub_type_count); + } + + if (gc_ext->sub_type_count == 1) { + entry.first_sub_type = gc_ext->sub_types[0]; + + if (gc_ext->sub_types[0] >= current_index) { + type_validation_result_ = Result::Error; + return PrintError(loc, "invalid sub type %" PRIindex, + gc_ext->sub_types[0]); + } + + if (type_fields_.type_entries[entry.first_sub_type].is_final_sub_type) { + type_validation_result_ = Result::Error; + return PrintError(loc, "sub type %" PRIindex " has final property", + entry.first_sub_type); + } + } - if (iter == func_types_.end()) { - return PrintError(loc, "reference %d is out of range in %s", - static_cast(index), desc); + if (Failed(type_validation_result_) || end_index != current_index + 1) { + return Result::Ok; + } + + Index start_index = type_fields_.recursive_ranges.back().start_index; + + uint32_t hash = 0; + + // Type checking could be done without computing the canonical_index, + // but runtime and validation checks could be very slow without it. + for (Index i = start_index; i < end_index; i++) { + hash = typechecker_.UpdateHash(hash, i, start_index); + } + + type_fields_.recursive_ranges.back().hash = hash; + + size_t size = type_fields_.recursive_ranges.size() - 1; + Index type_count = end_index - start_index; + + for (Index i = 0; i < size; i++) { + if (type_fields_.recursive_ranges[i].hash == hash && + type_fields_.recursive_ranges[i].type_count == type_count) { + Index base_index = type_fields_.recursive_ranges[i].start_index; + bool is_equal = true; + + for (Index j = 0; j < type_count; j++) { + if (!typechecker_.CheckTypeFields(start_index + j, start_index, + base_index + j, base_index, true)) { + is_equal = false; + break; + } + } + + if (is_equal) { + for (Index j = start_index; j < end_index; j++) { + type_fields_.type_entries[j].canonical_index = base_index++; + } + // An equal recurisve type is present in the list, there is + // no need to compare other recursive types to this type. + type_fields_.recursive_ranges.pop_back(); + break; + } } } + for (Index i = start_index; i < end_index; i++) { + Index first_sub_type = type_fields_.type_entries[i].first_sub_type; + if (first_sub_type != kInvalidIndex && + !typechecker_.CheckTypeFields(i, kInvalidIndex, first_sub_type, + kInvalidIndex, false)) { + PrintError(Location(), + "sub type %" PRIindex " does not match super type %" PRIindex, + type_fields_.type_entries[i].first_sub_type, i); + type_validation_result_ = Result::Error; + return Result::Error; + } + } return Result::Ok; } @@ -321,9 +459,8 @@ Result SharedValidator::OnElemSegmentElemType(const Location& loc, if (elem_type.IsReferenceWithIndex()) { Index index = elem_type.GetReferenceIndex(); - auto iter = func_types_.find(index); - if (iter == func_types_.end()) { + if (index >= type_fields_.NumTypes()) { result |= PrintError(loc, "reference %" PRIindex " is out of range", index); } @@ -403,21 +540,64 @@ Result SharedValidator::CheckLocalIndex(Var local_var, Type* out_type) { } Result SharedValidator::CheckFuncTypeIndex(Var sig_var, FuncType* out) { - Result result = CheckIndex(sig_var, num_types_, "function type"); + Result result = CheckIndex(sig_var, type_fields_.NumTypes(), "function type"); if (Failed(result)) { - *out = FuncType{}; + out->type_index = kInvalidIndex; return Result::Error; } - auto iter = func_types_.find(sig_var.index()); - if (iter == func_types_.end()) { + Index index = sig_var.index(); + assert(index < type_fields_.NumTypes()); + if (type_fields_.type_entries[index].kind != Type::FuncRef) { return PrintError(sig_var.loc, "type %d is not a function", sig_var.index()); } - if (out) { - *out = iter->second; + *out = type_fields_.func_types[type_fields_.type_entries[index].map_index]; + return Result::Ok; +} + +Result SharedValidator::CheckStructTypeIndex(Var type_var, + Type* out_ref, + StructType* out) { + Result result = CheckIndex(type_var, type_fields_.NumTypes(), "struct type"); + if (Failed(result)) { + return Result::Error; } + + Index index = type_var.index(); + assert(index < type_fields_.NumTypes()); + if (type_fields_.type_entries[index].kind != Type::StructRef) { + return PrintError(type_var.loc, "type %d is not a struct type", + type_var.index()); + } + + *out_ref = + Type(out_ref->IsNullableNonTypedRef() ? Type::RefNull : Type::Ref, index); + index = type_fields_.type_entries[index].map_index; + *out = type_fields_.struct_types[index]; + return Result::Ok; +} + +Result SharedValidator::CheckArrayTypeIndex(Var type_var, + Type* out_ref, + TypeMut* out) { + Result result = CheckIndex(type_var, type_fields_.NumTypes(), "array type"); + if (Failed(result)) { + return Result::Error; + } + + Index index = type_var.index(); + assert(index < type_fields_.NumTypes()); + if (type_fields_.type_entries[index].kind != Type::ArrayRef) { + return PrintError(type_var.loc, "type %d is not an array type", + type_var.index()); + } + + *out_ref = + Type(out_ref->IsNullableNonTypedRef() ? Type::RefNull : Type::Ref, index); + index = type_fields_.type_entries[index].map_index; + *out = type_fields_.array_types[index].field; return Result::Ok; } @@ -474,9 +654,8 @@ Result SharedValidator::CheckBlockSignature(const Location& loc, } else { if (sig_type.IsReferenceWithIndex()) { Index index = sig_type.GetReferenceIndex(); - auto iter = func_types_.find(index); - if (iter == func_types_.end()) { + if (index >= type_fields_.NumTypes()) { result |= PrintError(loc, "reference %" PRIindex " is out of range", index); } @@ -521,6 +700,17 @@ void SharedValidator::IgnoreLocalRefs() { } } +Index SharedValidator::GetEndIndex() { + assert(options_.features.reference_types_enabled()); + Index num_types = type_fields_.NumTypes(); + + if (options_.features.gc_enabled()) { + return (last_rec_type_end_ > num_types) ? last_rec_type_end_ : num_types; + } + + return num_types - 1; +} + Result SharedValidator::BeginInitExpr(const Location& loc, Type type) { expr_loc_ = loc; in_init_expr_ = true; @@ -562,7 +752,8 @@ Result SharedValidator::OnLocalDecl(const Location& loc, return Result::Error; } - CHECK_RESULT(CheckReferenceType(loc, type, "locals")); + CHECK_RESULT( + CheckReferenceType(loc, type, type_fields_.NumTypes(), "locals")); Index local_count = GetLocalCount(); @@ -582,6 +773,19 @@ Index SharedValidator::GetLocalCount() const { return locals_.empty() ? 0 : locals_.back().end; } +Index SharedValidator::GetCanonicalTypeIndex(Index type_index) { + if (type_index >= type_fields_.NumTypes()) { + return kInvalidIndex; + } + + if (options_.features.function_references_enabled() && + Succeeded(type_validation_result_)) { + return type_fields_.type_entries[type_index].canonical_index; + } + + return type_index; +} + static bool is_power_of_two(uint32_t x) { return x && ((x & (x - 1)) == 0); } @@ -646,6 +850,14 @@ bool SharedValidator::ValidInitOpcode(Opcode opcode) const { return true; } } + if (options_.features.gc_enabled()) { + if (opcode == Opcode::AnyConvertExtern || opcode == Opcode::ArrayNew || + opcode == Opcode::ArrayNewDefault || opcode == Opcode::ArrayNewFixed || + opcode == Opcode::ExternConvertAny || opcode == Opcode::RefI31 || + opcode == Opcode::StructNew || opcode == Opcode::StructNewDefault) { + return true; + } + } return false; } @@ -661,6 +873,139 @@ Result SharedValidator::CheckInstr(Opcode opcode, const Location& loc) { return Result::Ok; } +Result SharedValidator::OnArrayCopy(const Location& loc, + Var dst_type, + Var src_type) { + Result result = CheckInstr(Opcode::ArrayCopy, loc); + Type dst_ref_type(Type::ArrayRef, Type::ReferenceOrNull); + Type src_ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut dst_array_type, src_array_type; + result |= CheckArrayTypeIndex(dst_type, &dst_ref_type, &dst_array_type); + result |= CheckArrayTypeIndex(src_type, &src_ref_type, &src_array_type); + result |= typechecker_.OnArrayCopy(dst_ref_type, dst_array_type, src_ref_type, + src_array_type.type); + return result; +} + +Result SharedValidator::OnArrayFill(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::ArrayFill, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= typechecker_.OnArrayFill(ref_type, array_type); + return result; +} + +Result SharedValidator::OnArrayGet(const Location& loc, + Opcode opcode, + Var type) { + Result result = CheckInstr(opcode, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= typechecker_.OnArrayGet(opcode, ref_type, array_type.type); + return result; +} + +Result SharedValidator::OnArrayInitData(const Location& loc, + Var type, + Var segment_var) { + Result result = CheckInstr(Opcode::ArrayInitData, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnArrayInitData(ref_type, array_type); + return result; +} + +Result SharedValidator::OnArrayInitElem(const Location& loc, + Var type, + Var segment_var) { + Result result = CheckInstr(Opcode::ArrayInitElem, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut array_type; + ElemType elem_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= CheckElemSegmentIndex(segment_var, &elem_type); + result |= + typechecker_.OnArrayInitElem(ref_type, array_type, elem_type.element); + return result; +} + +Result SharedValidator::OnArrayNew(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::ArrayNew, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceNonNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= typechecker_.OnArrayNew(ref_type, array_type.type); + return result; +} + +Result SharedValidator::OnArrayNewData(const Location& loc, + Var type, + Var segment_var) { + Result result = CheckInstr(Opcode::ArrayNewData, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceNonNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnArrayNewData(ref_type, array_type.type); + return result; +} + +Result SharedValidator::OnArrayNewDefault(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::ArrayNewDefault, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceNonNull); + TypeMut array_type; + + if (Succeeded(CheckArrayTypeIndex(type, &ref_type, &array_type))) { + if (array_type.type.IsNonNullableRef()) { + result = PrintError(loc, "array type has no default value: %" PRIindex, + type.index()); + } + } else { + result = Result::Error; + } + + result |= typechecker_.OnArrayNewDefault(ref_type); + return result; +} + +Result SharedValidator::OnArrayNewElem(const Location& loc, + Var type, + Var segment_var) { + Result result = CheckInstr(Opcode::ArrayNewElem, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceNonNull); + TypeMut array_type; + ElemType elem_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= CheckElemSegmentIndex(segment_var, &elem_type); + result |= + typechecker_.OnArrayNewElem(ref_type, array_type.type, elem_type.element); + return result; +} + +Result SharedValidator::OnArrayNewFixed(const Location& loc, + Var type, + Index count) { + Result result = CheckInstr(Opcode::ArrayNewFixed, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceNonNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= typechecker_.OnArrayNewFixed(ref_type, array_type.type, count); + return result; +} + +Result SharedValidator::OnArraySet(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::ArraySet, loc); + Type ref_type(Type::ArrayRef, Type::ReferenceOrNull); + TypeMut array_type; + result |= CheckArrayTypeIndex(type, &ref_type, &array_type); + result |= typechecker_.OnArraySet(ref_type, array_type); + return result; +} + Result SharedValidator::OnAtomicFence(const Location& loc, uint32_t consistency_model) { Result result = CheckInstr(Opcode::AtomicFence, loc); @@ -786,6 +1131,17 @@ Result SharedValidator::OnBrIf(const Location& loc, Var depth) { return result; } +Result SharedValidator::OnBrOnCast(const Location& loc, + Opcode opcode, + Var depth, + Var type1_var, + Var type2_var) { + Result result = CheckInstr(Opcode::BrOnCast, loc); + result |= typechecker_.OnBrOnCast(opcode, depth.index(), type1_var.to_type(), + type2_var.to_type()); + return result; +} + Result SharedValidator::OnBrOnNonNull(const Location& loc, Var depth) { Result result = CheckInstr(Opcode::BrOnNonNull, loc); result |= typechecker_.OnBrOnNonNull(depth.index()); @@ -931,6 +1287,12 @@ Result SharedValidator::OnEnd(const Location& loc) { return result; } +Result SharedValidator::OnGCUnary(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnGCUnary(opcode); + return result; +} + Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) { Result result = CheckInstr(Opcode::GlobalGet, loc); GlobalType global_type; @@ -1130,6 +1492,12 @@ Result SharedValidator::OnRefAsNonNull(const Location& loc) { return result; } +Result SharedValidator::OnRefCast(const Location& loc, Var type_var) { + Result result = CheckInstr(Opcode::RefCast, loc); + result |= typechecker_.OnRefCast(type_var.to_type()); + return result; +} + Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) { Result result = CheckInstr(Opcode::RefFunc, loc); result |= CheckFuncIndex(func_var); @@ -1158,24 +1526,24 @@ Result SharedValidator::OnRefNull(const Location& loc, Var func_type_var) { Type type = func_type_var.to_type(); - switch (type) { - case Type::RefNull: - result |= CheckIndex(func_type_var, num_types_, "function type"); - break; - case Type::FuncRef: - case Type::ExnRef: - case Type::ExternRef: - break; - default: - result |= PrintError( - loc, "Only ref, externref, exnref, funcref are allowed for ref.null"); - break; + if (type == Type::RefNull) { + result |= + CheckIndex(func_type_var, type_fields_.NumTypes(), "function type"); + } else if (!type.IsNonTypedRef()) { + result |= PrintError(loc, "Only nullable reference types are allowed"); } + assert(!Type::EnumIsNonTypedGCRef(type) || options_.features.gc_enabled()); result |= typechecker_.OnRefNullExpr(type); return result; } +Result SharedValidator::OnRefTest(const Location& loc, Var type_var) { + Result result = CheckInstr(Opcode::RefTest, loc); + result |= typechecker_.OnRefTest(type_var.to_type()); + return result; +} + Result SharedValidator::OnRethrow(const Location& loc, Var depth) { Result result = CheckInstr(Opcode::Rethrow, loc); result |= typechecker_.OnRethrow(depth.index()); @@ -1235,9 +1603,9 @@ Result SharedValidator::OnSelect(const Location& loc, for (Index i = 0; i < result_count; i++) { if (result_types[i].IsReferenceWithIndex()) { Index index = result_types[i].GetReferenceIndex(); - auto iter = func_types_.find(index); - if (iter == func_types_.end()) { + if (index >= type_fields_.NumTypes() || + type_fields_.type_entries[index].kind != Type::FuncRef) { result |= PrintError(loc, "reference %" PRIindex " is out of range", index); } @@ -1314,6 +1682,59 @@ Result SharedValidator::OnStore(const Location& loc, return result; } +Result SharedValidator::OnStructGet(const Location& loc, + Opcode opcode, + Var type, + Var field) { + Result result = CheckInstr(opcode, loc); + Type ref_type(Type::StructRef, Type::ReferenceOrNull); + StructType struct_type; + result |= CheckStructTypeIndex(type, &ref_type, &struct_type); + result |= + typechecker_.OnStructGet(opcode, ref_type, struct_type, field.index()); + return result; +} + +Result SharedValidator::OnStructNew(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::StructNew, loc); + Type ref_type(Type::StructRef, Type::ReferenceNonNull); + StructType struct_type; + result |= CheckStructTypeIndex(type, &ref_type, &struct_type); + result |= typechecker_.OnStructNew(ref_type, struct_type); + return result; +} + +Result SharedValidator::OnStructNewDefault(const Location& loc, Var type) { + Result result = CheckInstr(Opcode::StructNewDefault, loc); + Type ref_type(Type::StructRef, Type::ReferenceNonNull); + StructType struct_type; + + if (Succeeded(CheckStructTypeIndex(type, &ref_type, &struct_type))) { + for (auto it : struct_type.fields) { + if (it.type.IsNonNullableRef()) { + result = + PrintError(loc, "type has field without default value: %" PRIindex, + type.index()); + break; + } + } + } else { + result = Result::Error; + } + + result |= typechecker_.OnStructNewDefault(ref_type); + return result; +} + +Result SharedValidator::OnStructSet(const Location& loc, Var type, Var field) { + Result result = CheckInstr(Opcode::StructSet, loc); + Type ref_type(Type::StructRef, Type::ReferenceOrNull); + StructType struct_type; + result |= CheckStructTypeIndex(type, &ref_type, &struct_type); + result |= typechecker_.OnStructSet(ref_type, struct_type, field.index()); + return result; +} + Result SharedValidator::OnTableCopy(const Location& loc, Var dst_var, Var src_var) { @@ -1457,4 +1878,4 @@ Result SharedValidator::OnUnreachable(const Location& loc) { return result; } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/test-interp.cc b/src/test-interp.cc index 9346b7b166..cf3991227c 100644 --- a/src/test-interp.cc +++ b/src/test-interp.cc @@ -608,9 +608,9 @@ TEST_F(InterpGCTest, Collect_GlobalCycle) { TEST_F(InterpGCTest, Collect_TableCycle) { auto tt = TableType{ValueType::ExternRef, Limits{2}}; - auto t1 = Table::New(store_, tt); - auto t2 = Table::New(store_, tt); - auto t3 = Table::New(store_, tt); + auto t1 = Table::New(store_, tt, Ref::Null); + auto t2 = Table::New(store_, tt, Ref::Null); + auto t3 = Table::New(store_, tt, Ref::Null); t1->Set(store_, 0, t1->self()); // t1 references itself. t2->Set(store_, 0, t3->self()); @@ -659,7 +659,8 @@ TEST_F(InterpGCTest, Collect_InstanceImport) { auto f = HostFunc::New(store_, FuncType{{}, {}}, [](Thread& thread, const Values&, Values&, Trap::Ptr*) -> Result { return Result::Ok; }); - auto t = Table::New(store_, TableType{ValueType::FuncRef, Limits{0}}); + auto t = + Table::New(store_, TableType{ValueType::FuncRef, Limits{0}}, Ref::Null); auto m = Memory::New(store_, MemoryType{Limits{0}, WABT_DEFAULT_PAGE_SIZE}); auto g = Global::New(store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(5)); @@ -709,10 +710,10 @@ TEST_F(InterpGCTest, Collect_DeepRecursion) { // Create a chain of tables, where each contains // a single reference to the next table. - Table::Ptr prev_table = Table::New(store_, tt); + Table::Ptr prev_table = Table::New(store_, tt, Ref::Null); for (size_t i = 1; i < table_count; i++) { - Table::Ptr new_table = Table::New(store_, tt); + Table::Ptr new_table = Table::New(store_, tt, Ref::Null); new_table->Set(store_, 0, prev_table->self()); @@ -732,4 +733,4 @@ TEST_F(InterpGCTest, Collect_DeepRecursion) { // TODO: Test for Thread keeping references alive as locals/params/stack values. // This requires better tracking of references than currently exists in the -// interpreter. (see TODOs in Select/LocalGet/GlobalGet) +// interpreter. (see TODOs in Select/LocalGet/GlobalGet) \ No newline at end of file diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc index d859f14823..6a6fe3c027 100644 --- a/src/tools/spectest-interp.cc +++ b/src/tools/spectest-interp.cc @@ -629,12 +629,24 @@ wabt::Result JSONParser::ParseType(Type* out_type) { *out_type = Type::I8; } else if (type_str == "i16") { *out_type = Type::I16; - } else if (type_str == "funcref") { - *out_type = Type::FuncRef; - } else if (type_str == "externref") { - *out_type = Type::ExternRef; + } else if (type_str == "anyref") { + *out_type = Type::AnyRef; + } else if (type_str == "arrayref") { + *out_type = Type::ArrayRef; + } else if (type_str == "botref") { + *out_type = Type::BottomRef(); + } else if (type_str == "eqref") { + *out_type = Type::EqRef; } else if (type_str == "exnref") { *out_type = Type::ExnRef; + } else if (type_str == "externref") { + *out_type = Type::ExternRef; + } else if (type_str == "funcref") { + *out_type = Type::FuncRef; + } else if (type_str == "i31ref") { + *out_type = Type::I31Ref; + } else if (type_str == "structref") { + *out_type = Type::StructRef; } else { PrintError("unknown type: \"%s\"", type_str.c_str()); return wabt::Result::Error; @@ -853,18 +865,47 @@ wabt::Result JSONParser::ParseConstValue(Type type, } break; + case Type::AnyRef: case Type::ExternRef: if (value_str == "null") { out_value->Set(Ref::Null); + } else if (value_str == "") { + out_value->Set(Ref::CreateHostVal(Ref::kAnyHostValue)); } else { uint32_t value; CHECK_RESULT(ParseI32Value(&value, value_str)); - // TODO: hack, just whatever ref is at this index; but skip null (which - // is always 0). + out_value->Set(Ref::CreateHostVal(value)); + } + break; + + case Type::ArrayRef: + case Type::EqRef: + case Type::StructRef: + if (value_str == "null") { + out_value->Set(Ref::Null); + } else { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + // TODO: these cannot be constructed without a type reference. out_value->Set(Ref{value + 1}); } break; + case Type::NullRef: + assert(type.IsBottomRef()); + out_value->Set(Ref::Null); + break; + + case Type::I31Ref: + if (value_str == "null") { + out_value->Set(Ref::Null); + } else { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + out_value->Set(Ref::CreateI31Val(value)); + } + break; + case Type::ExnRef: if (value_str == "null") { out_value->Set(Ref::Null); @@ -1307,11 +1348,12 @@ CommandRunner::CommandRunner() : store_(s_features) { }); } - spectest["table"] = - interp::Table::New(store_, TableType{ValueType::FuncRef, Limits{10, 20}}); + spectest["table"] = interp::Table::New( + store_, TableType{ValueType::FuncRef, Limits{10, 20}}, Ref::Null); spectest["table64"] = interp::Table::New( - store_, TableType{ValueType::FuncRef, Limits{10, 20, false, true}}); + store_, TableType{ValueType::FuncRef, Limits{10, 20, false, true}}, + Ref::Null); spectest["memory"] = interp::Memory::New( store_, MemoryType{Limits{1, 2}, WABT_DEFAULT_PAGE_SIZE}); @@ -1917,14 +1959,100 @@ wabt::Result CommandRunner::CheckAssertReturnResult( break; } - case Type::FuncRef: - // A funcref expectation only requires that the reference be a function, - // but it doesn't check the actual index. - ok = (actual.type == Type::FuncRef); + case Type::AnyRef: + ok = false; + if (actual.type == Type::AnyRef || actual.type == Type::NullRef) { + Ref actual_ref = actual.value.Get(); + Ref expected_ref = expected.value.value.Get(); + if (expected_ref == Ref::Null) { + ok = (actual_ref == Ref::Null); + } else if (actual_ref != Ref::Null) { + ok = expected_ref.IsAnyHostVal() || + actual_ref.GetHostVal() == expected_ref.GetHostVal(); + } + } + break; + + case Type::ArrayRef: + ok = false; + if (actual.type == Type::ArrayRef) { + ok = actual.value.Get() != Ref::Null; + } else if ((actual.type == Type::AnyRef || actual.type == Type::Ref) && + actual.value.Get() != Ref::Null) { + RefPtr obj = store_.UnsafeGet(actual.value.Get()); + ok = obj->kind() == ObjectKind::Array; + } + break; + + case Type::NullRef: + assert(expected.value.type.IsBottomRef()); + ok = actual.value.Get() == Ref::Null; break; + case Type::EqRef: { + ok = false; + Ref ref = actual.value.Get(); + if (ref == Ref::Null || ref.IsI31Val()) { + ok = true; + } else if (!ref.IsHostVal()) { + if (actual.type == Type::EqRef || actual.type == Type::StructRef || + actual.type == Type::ArrayRef) { + ok = true; + } else if (actual.type == Type::AnyRef || actual.type == Type::Ref) { + RefPtr obj = store_.UnsafeGet(ref); + ok = obj->kind() == ObjectKind::Array || + obj->kind() == ObjectKind::Struct; + } + } + break; + } + case Type::ExternRef: - ok = expected.value.value.Get() == actual.value.Get(); + ok = false; + if (actual.type == Type::ExternRef || + actual.type == Type::NullExternRef) { + Ref actual_ref = actual.value.Get(); + Ref expected_ref = expected.value.value.Get(); + if (expected_ref == Ref::Null) { + ok = (actual_ref == Ref::Null); + } else if (actual_ref != Ref::Null) { + ok = expected_ref.IsAnyHostVal() || + actual_ref.GetHostVal() == expected_ref.GetHostVal(); + } + } + break; + + case Type::FuncRef: + if (actual.type == Type::NullFuncRef) { + ok = actual.value.Get() == Ref::Null; + } else { + // A funcref expectation only requires that the reference be a function, + // but it doesn't check the actual index. + ok = (actual.type == Type::FuncRef || actual.type == Type::RefNull); + } + break; + + case Type::I31Ref: { + ok = false; + Ref ref = actual.value.Get(); + if (actual.type == Type::I31Ref) { + ok = ref != Ref::Null; + } else if ((actual.type == Type::AnyRef || actual.type == Type::Ref) && + ref != Ref::Null) { + ok = ref.IsI31Val(); + } + break; + } + + case Type::StructRef: + ok = false; + if (actual.type == Type::StructRef) { + ok = actual.value.Get() != Ref::Null; + } else if ((actual.type == Type::AnyRef || actual.type == Type::Ref) && + actual.value.Get() != Ref::Null) { + RefPtr obj = store_.UnsafeGet(actual.value.Get()); + ok = obj->kind() == ObjectKind::Struct; + } break; case Type::ExnRef: @@ -2084,4 +2212,4 @@ int main(int argc, char** argv) { WABT_TRY return ProgramMain(argc, argv); WABT_CATCH_BAD_ALLOC_AND_EXIT -} +} \ No newline at end of file diff --git a/src/type-checker.cc b/src/type-checker.cc index c13e3ad4de..4094baba95 100644 --- a/src/type-checker.cc +++ b/src/type-checker.cc @@ -48,6 +48,24 @@ std::string TypesToString(const TypeVector& types, } // end anonymous namespace +Type TypeChecker::TypeFields::GetGroupType(Type type) { + assert(type.IsRef()); + type = GetGenericType(type); + + switch (type) { + case Type::NullFuncRef: + case Type::FuncRef: + return Type::FuncRef; + + case Type::NullExternRef: + case Type::ExternRef: + return Type::ExternRef; + + default: + return Type::Any; + } +} + TypeChecker::Label::Label(LabelType label_type, const TypeVector& param_types, const TypeVector& result_types, @@ -228,96 +246,318 @@ Result TypeChecker::CheckTypeStackEnd(const char* desc) { return result; } -static bool CompareTypeVector( - const std::map& func_types, - const TypeVector& left, - const TypeVector& right) { - size_t size = left.size(); +uint32_t TypeChecker::UpdateHash(uint32_t hash, + Index type_index, + Index rec_start) { + TypeEntry& entry = type_fields_.type_entries[type_index]; + + hash = ComputeHash(hash, entry.kind); + + hash = ComputeHash(hash, static_cast(entry.is_final_sub_type)); + if (entry.first_sub_type != kInvalidIndex) { + Index first_sub_type = entry.first_sub_type; + if (first_sub_type >= rec_start) { + first_sub_type = rec_start - first_sub_type - 1; + } else { + first_sub_type = + type_fields_.type_entries[first_sub_type].canonical_index; + } + hash = ComputeHash(hash, first_sub_type); + } + + switch (entry.kind) { + case Type::FuncRef: { + FuncType& type = type_fields_.func_types[entry.map_index]; + + hash = ComputeHash(hash, static_cast(type.params.size())); + for (auto it : type.params) { + hash = ComputeHash(hash, it, rec_start); + } + + hash = ComputeHash(hash, static_cast(type.results.size())); + for (auto it : type.results) { + hash = ComputeHash(hash, it, rec_start); + } + break; + } + case Type::StructRef: { + StructType& type = type_fields_.struct_types[entry.map_index]; + + hash = ComputeHash(hash, static_cast(type.fields.size())); + for (auto it : type.fields) { + hash = ComputeHash(hash, it.type, rec_start); + hash = ComputeHash(hash, static_cast(it.mutable_)); + } + break; + } + default: { + assert(entry.kind == Type::ArrayRef); + ArrayType& type = type_fields_.array_types[entry.map_index]; + + hash = ComputeHash(hash, type.field.type, rec_start); + hash = ComputeHash(hash, static_cast(type.field.mutable_)); + break; + } + } + + return hash; +} + +bool TypeChecker::CheckTypeFields(Index actual, + Index actual_rec_start, + Index expected, + Index expected_rec_start, + bool is_equal) { + TypeEntry& actual_entry = type_fields_.type_entries[actual]; + TypeEntry& expected_entry = type_fields_.type_entries[expected]; - if (size != right.size()) { + if (actual_entry.kind != expected_entry.kind) { return false; } - for (size_t i = 0; i < size; i++) { - const Type& left_type = left[i]; - const Type& right_type = right[i]; + if (is_equal) { + if (actual_entry.is_final_sub_type != expected_entry.is_final_sub_type) { + return false; + } - if (left_type != right_type) { - if (!left_type.IsReferenceWithIndex() || - left_type != static_cast(right_type)) { + if (actual_entry.first_sub_type == kInvalidIndex || + expected_entry.first_sub_type == kInvalidIndex) { + if (actual_entry.first_sub_type != expected_entry.first_sub_type) { return false; } + } else { + Type sub_type_actual(Type::Ref, actual_entry.first_sub_type); + Type sub_type_expected(Type::Ref, expected_entry.first_sub_type); - const TypeChecker::FuncType& left_func_type = - func_types.at(left_type.GetReferenceIndex()); - const TypeChecker::FuncType& right_func_type = - func_types.at(right_type.GetReferenceIndex()); - - // Circular references were checked during validation. - if (!CompareTypeVector(func_types, left_func_type.params, - right_func_type.params) || - !CompareTypeVector(func_types, left_func_type.results, - right_func_type.results)) { + if (!CompareType(sub_type_actual, actual_rec_start, sub_type_expected, + expected_rec_start, true)) { return false; } } } - return true; + switch (actual_entry.kind) { + case Type::FuncRef: { + FuncType& actual_type = type_fields_.func_types[actual_entry.map_index]; + FuncType& expected_type = + type_fields_.func_types[expected_entry.map_index]; + + if (actual_type.params.size() != expected_type.params.size() || + actual_type.results.size() != expected_type.results.size()) { + return false; + } + + size_t size = expected_type.params.size(); + for (size_t i = 0; i < size; i++) { + // Arguments are checked in reversed order. + if (!CompareType(expected_type.params[i], expected_rec_start, + actual_type.params[i], actual_rec_start, is_equal)) { + return false; + } + } + + size = expected_type.results.size(); + for (size_t i = 0; i < size; i++) { + if (!CompareType(actual_type.results[i], actual_rec_start, + expected_type.results[i], expected_rec_start, + is_equal)) { + return false; + } + } + return true; + } + case Type::StructRef: { + StructType& actual_type = + type_fields_.struct_types[actual_entry.map_index]; + StructType& expected_type = + type_fields_.struct_types[expected_entry.map_index]; + + size_t actual_type_size = actual_type.fields.size(); + size_t expected_type_size = expected_type.fields.size(); + + if (is_equal ? actual_type_size != expected_type_size + : actual_type_size < expected_type_size) { + return false; + } + + for (size_t i = 0; i < expected_type_size; i++) { + bool mutable_ = actual_type.fields[i].mutable_; + if (mutable_ != expected_type.fields[i].mutable_ || + !CompareType(actual_type.fields[i].type, actual_rec_start, + expected_type.fields[i].type, expected_rec_start, + is_equal || mutable_)) { + return false; + } + } + return true; + } + default: { + assert(actual_entry.kind == Type::ArrayRef); + ArrayType& actual_type = type_fields_.array_types[actual_entry.map_index]; + ArrayType& expected_type = + type_fields_.array_types[expected_entry.map_index]; + + bool mutable_ = actual_type.field.mutable_; + return mutable_ == expected_type.field.mutable_ && + CompareType(actual_type.field.type, actual_rec_start, + expected_type.field.type, expected_rec_start, + is_equal || mutable_); + } + } } -Result TypeChecker::CheckType(Type actual, Type expected) { - if (expected == Type::Any || actual == Type::Any) { - return Result::Ok; +uint32_t TypeChecker::ComputeHash(uint32_t hash, Type& type, Index rec_start) { + int32_t code = static_cast(type); + if (type.IsNonTypedRef() && !type.IsNullableNonTypedRef()) { + hash ^= 0x80; + } + hash = ComputeHash(hash, static_cast(code)); + if (type.IsReferenceWithIndex()) { + Index index = type.GetReferenceIndex(); + if (index >= rec_start) { + index = rec_start - index - 1; + } else { + index = type_fields_.type_entries[index].canonical_index; + } + hash = ComputeHash(hash, index); } + return hash; +} + +bool TypeChecker::CompareType(Type actual, + Index actual_rec_start, + Type expected, + Index expected_rec_start, + bool is_equal) { + if (actual == expected) { + if (actual.IsReferenceWithIndex()) { + Index actual_index = actual.GetReferenceIndex(); - Type::Enum actual_type = actual; - Type::Enum expected_type = expected; + return (actual_rec_start == expected_rec_start) || + (actual_index < actual_rec_start && + actual_index < expected_rec_start); + } + return true; + } - if (actual_type == expected_type) { - switch (actual_type) { - case Type::ExternRef: - case Type::FuncRef: - return (expected.IsNullableNonTypedRef() || - !actual.IsNullableNonTypedRef()) - ? Result::Ok - : Result::Error; + if (is_equal) { + if (!expected.IsReferenceWithIndex() || + actual != static_cast(expected)) { + return false; + } + } else { + Type gen_actual = type_fields_.GetGenericType(actual); + Type gen_expected = type_fields_.GetGenericType(expected); + + if (gen_actual != static_cast(gen_expected)) { + switch (gen_expected) { + case Type::FuncRef: + if (gen_actual == Type::NullFuncRef) { + break; + } + [[fallthrough]]; + case Type::NullFuncRef: + if (gen_actual.IsBottomRef()) { + break; + } + return false; + case Type::ExternRef: + if (gen_actual == Type::NullExternRef) { + break; + } + [[fallthrough]]; + case Type::NullExternRef: + if (gen_actual.IsBottomRef()) { + break; + } + return false; + case Type::AnyRef: + if (gen_actual == Type::EqRef) { + break; + } + [[fallthrough]]; + case Type::EqRef: + if (gen_actual == Type::I31Ref || gen_actual == Type::StructRef || + gen_actual == Type::ArrayRef) { + break; + } + [[fallthrough]]; + case Type::I31Ref: + case Type::StructRef: + case Type::ArrayRef: + if (gen_actual == Type::NullRef) { + break; + } + return false; + case Type::ExnRef: + // Note: noexn is not implemented. + if (gen_actual.IsBottomRef()) { + break; + } + return false; + default: + return false; + } + } - case Type::Reference: - case Type::Ref: - case Type::RefNull: - break; + if (!gen_expected.IsNullableNonTypedRef() && + gen_actual.IsNullableNonTypedRef()) { + return false; + } - default: - return Result::Ok; + if (!actual.IsReferenceWithIndex() || !expected.IsReferenceWithIndex()) { + return (!expected.IsReferenceWithIndex() || actual == Type::NullFuncRef || + actual == Type::NullExternRef || actual == Type::NullRef); } } - if (!actual.IsReferenceWithIndex()) { - return Result::Error; + Index expected_index = expected.GetReferenceIndex(); + Index actual_index = actual.GetReferenceIndex(); + + if (expected_index >= expected_rec_start) { + return (actual_index >= actual_rec_start) && + (actual_index - actual_rec_start == + expected_index - expected_rec_start); } - if (expected_type == Type::FuncRef) { - return (actual == Type::Ref || expected.IsNullableNonTypedRef()) - ? Result::Ok - : Result::Error; + expected_index = type_fields_.type_entries[expected_index].canonical_index; + actual_index = type_fields_.type_entries[actual_index].canonical_index; + + if (expected_index == actual_index) { + return true; } - if (!expected.IsReferenceWithIndex()) { - return Result::Error; + if (is_equal) { + return false; } - if (expected_type == Type::Ref && actual_type == Type::RefNull) { - return Result::Error; + while (true) { + actual_index = type_fields_.type_entries[actual_index].first_sub_type; + + if (actual_index == kInvalidIndex) { + return false; + } + + if (expected_index >= expected_rec_start) { + return (actual_index >= actual_rec_start) && + (actual_index - actual_rec_start == + expected_index - expected_rec_start); + } + + actual_index = type_fields_.type_entries[actual_index].canonical_index; + + if (actual_index == expected_index) { + return true; + } } +} - FuncType& actual_func_type = func_types_[actual.GetReferenceIndex()]; - FuncType& expected_func_type = func_types_[expected.GetReferenceIndex()]; +Result TypeChecker::CheckType(Type actual, Type expected) { + if (expected == Type::Any || actual == Type::Any) { + return Result::Ok; + } - if (CompareTypeVector(func_types_, actual_func_type.params, - expected_func_type.params) && - CompareTypeVector(func_types_, actual_func_type.results, - expected_func_type.results)) { + if (CompareType(actual, kInvalidIndex, expected, kInvalidIndex, false)) { return Result::Ok; } @@ -404,14 +644,49 @@ Result TypeChecker::PopAndCheck3Types(Type expected1, return result; } +Result TypeChecker::PopAndCheck4Types(Type expected1, + Type expected2, + Type expected3, + Type expected4, + const char* desc) { + Result result = Result::Ok; + result |= PeekAndCheckType(0, expected4); + result |= PeekAndCheckType(1, expected3); + result |= PeekAndCheckType(2, expected2); + result |= PeekAndCheckType(3, expected1); + PrintStackIfFailed(result, desc, expected1, expected2, expected3, expected4); + result |= DropTypes(4); + return result; +} + +Result TypeChecker::PopAndCheck5Types(Type expected1, + Type expected2, + Type expected3, + Type expected4, + Type expected5, + const char* desc) { + Result result = Result::Ok; + result |= PeekAndCheckType(0, expected5); + result |= PeekAndCheckType(1, expected4); + result |= PeekAndCheckType(2, expected3); + result |= PeekAndCheckType(3, expected2); + result |= PeekAndCheckType(4, expected1); + PrintStackIfFailed(result, desc, expected1, expected2, expected3, expected4, + expected5); + result |= DropTypes(5); + return result; +} + Result TypeChecker::PopAndCheckReference(Type* actual, const char* desc) { - *actual = Type::Any; Result result = PeekType(0, actual); - // Type::Any is a valid value for dead code, and replacing - // it with anything might break the syntax checker. - if (*actual != Type::Any && !actual->IsRef()) { - result = Result::Error; + // Type::Any is a valid value for dead code, and + // it is replaced by an unkown reference. + if (*actual == Type::Any || !actual->IsRef()) { + if (*actual != Type::Any) { + result = Result::Error; + } + *actual = Type::BottomRef(); } PrintStackIfFailed(result, desc, Type::FuncRef); @@ -511,6 +786,148 @@ Result TypeChecker::BeginFunction(const TypeVector& sig) { return Result::Ok; } +Result TypeChecker::OnArrayCopy(Type dst_ref_type, + TypeMut& dst_array_type, + Type src_ref_type, + Type src_array_type) { + Result result = PopAndCheck5Types(dst_ref_type, Type::I32, src_ref_type, + Type::I32, Type::I32, "array.copy"); + if (!dst_array_type.mutable_) { + PrintError("array is immutable"); + result = Result::Error; + } + if (Failed(CheckType(src_array_type, dst_array_type.type))) { + PrintError("type mismatch: array types do not match"); + result = Result::Error; + } + return result; +} + +Result TypeChecker::OnArrayFill(Type ref_type, TypeMut& array_type) { + Result result = + PopAndCheck4Types(ref_type, Type::I32, ToUnpackedType(array_type.type), + Type::I32, "array.fill"); + if (!array_type.mutable_) { + PrintError("array is immutable"); + result = Result::Error; + } + return result; +} + +Result TypeChecker::OnArrayGet(Opcode opcode, Type ref_type, Type array_type) { + Result result = PopAndCheck2Types(ref_type, Type::I32, "array.get"); + bool is_packed_get = (opcode != Opcode::ArrayGet); + + if (array_type.IsPackedType() != is_packed_get) { + PrintError("array is %spacked", is_packed_get ? "not " : ""); + result = Result::Error; + } + + PushType(ToUnpackedType(array_type)); + return result; +} + +Result TypeChecker::OnArrayInitData(Type ref_type, TypeMut& array_type) { + Result result = PopAndCheck4Types(ref_type, Type::I32, Type::I32, Type::I32, + "array.init_data"); + if (!array_type.mutable_) { + PrintError("array is immutable"); + result = Result::Error; + } + if (array_type.type.IsRef()) { + PrintError("type mismatch: array type must be number or vector type"); + result = Result::Error; + } + return result; +} + +Result TypeChecker::OnArrayInitElem(Type ref_type, + TypeMut& array_type, + Type elem_type) { + Result result = PopAndCheck4Types(ref_type, Type::I32, Type::I32, Type::I32, + "array.init_elem"); + if (!array_type.mutable_) { + PrintError("array is immutable"); + result = Result::Error; + } + if (Failed(CheckType(elem_type, array_type.type))) { + PrintError("type mismatch: array type does not match to elem type"); + result = Result::Error; + } + return result; +} + +Result TypeChecker::OnArrayNew(Type ref_type, Type array_type) { + Result result = + PopAndCheck2Types(ToUnpackedType(array_type), Type::I32, "array.new"); + PushType(ToUnpackedType(ref_type)); + return result; +} + +Result TypeChecker::OnArrayNewData(Type ref_type, Type array_type) { + Result result = PopAndCheck2Types(Type::I32, Type::I32, "array.new_elem"); + if (array_type.IsRef()) { + PrintError("type mismatch: array type must be number or vector type"); + result = Result::Error; + } + PushType(ToUnpackedType(ref_type)); + return result; +} + +Result TypeChecker::OnArrayNewDefault(Type ref_type) { + Result result = PopAndCheck1Type(Type::I32, "array.new_default"); + PushType(ToUnpackedType(ref_type)); + return result; +} + +Result TypeChecker::OnArrayNewElem(Type ref_type, + Type array_type, + Type elem_type) { + Result result = PopAndCheck2Types(Type::I32, Type::I32, "array.new_elem"); + if (Failed(CheckType(elem_type, array_type))) { + PrintError("type mismatch: array type does not match to elem type"); + result |= Result::Error; + } + PushType(ToUnpackedType(ref_type)); + return result; +} + +Result TypeChecker::OnArrayNewFixed(Type ref_type, + Type array_type, + Index count) { + Result result = Result::Ok; + array_type = ToUnpackedType(array_type); + for (Index i = 0; i < count; ++i) { + result |= PeekAndCheckType(count - i - 1, array_type); + } + + if (Failed(result)) { + // To improve performance, type vector + // conversion is only done on error. + TypeVector types; + types.reserve(count); + + for (size_t i = 0; i < count; ++i) { + types.push_back(array_type); + } + PrintStackIfFailedV(result, "array.new_fixed", types, /*is_end=*/false); + } + + result |= DropTypes(count); + PushType(ref_type); + return result; +} + +Result TypeChecker::OnArraySet(Type ref_type, const TypeMut& field) { + Result result = PopAndCheck3Types(ref_type, Type::I32, + ToUnpackedType(field.type), "array.set"); + if (!field.mutable_) { + PrintError("array is immutable"); + result = Result::Error; + } + return result; +} + Result TypeChecker::OnAtomicLoad(Opcode opcode, const Limits& limits) { return CheckOpcode1(opcode, &limits); } @@ -569,30 +986,56 @@ Result TypeChecker::OnBrIf(Index depth) { return result; } -static Type convertRefNullToRef(Type type) { - if (type == Type::ExternRef || type == Type::FuncRef) { - return Type(type, Type::ReferenceNonNull); +Result TypeChecker::OnBrOnCast(Opcode opcode, + Index depth, + Type type1, + Type type2) { + Type actual; + Result result = PopAndCheckReference(&actual, opcode.GetName()); + if (Failed(TypeChecker::CheckType(actual, type1))) { + PrintError("type mismatch: %s is not a subtype of %s", + actual.GetName().c_str(), type1.GetName().c_str()); + result = Result::Error; + } + + if (Failed(TypeChecker::CheckType(type2, type1))) { + PrintError("type mismatch: %s is not a subtype of %s", + type2.GetName().c_str(), type1.GetName().c_str()); + result = Result::Error; + } + + // The spec expects a type1 \ type2 subtraction operation. + // Currently this operation is applied only to the Null value. + if (type2.IsNullableRef()) { + type1.ConvertRefNullToRef(); } - assert(type.IsReferenceWithIndex()); - return Type(Type::Ref, type.GetReferenceIndex()); + PushType(opcode == Opcode::BrOnCast ? type2 : type1); + Label* label; + if (Succeeded(GetLabel(depth, &label))) { + result |= PopAndCheckSignature(label->br_types(), opcode.GetName()); + PushTypes(label->br_types()); + } else { + result = Result::Error; + } + result |= DropTypes(1); + PushType(opcode == Opcode::BrOnCast ? type1 : type2); + return result; } Result TypeChecker::OnBrOnNonNull(Index depth) { Type actual; CHECK_RESULT(PopAndCheckReference(&actual, "br_on_non_null")); - if (actual != Type::Any) { - PushType(convertRefNullToRef(actual)); - } + actual.ConvertRefNullToRef(); + + PushType(actual); Label* label; CHECK_RESULT(GetLabel(depth, &label)); Result result = PopAndCheckSignature(label->br_types(), "br_on_non_null"); PushTypes(label->br_types()); - if (actual != Type::Any) { - result |= DropTypes(1); - } + result |= DropTypes(1); return result; } @@ -605,9 +1048,7 @@ Result TypeChecker::OnBrOnNull(Index depth) { Result result = PopAndCheckSignature(label->br_types(), "br_on_null"); PushTypes(label->br_types()); - if (actual != Type::Any) { - actual = convertRefNullToRef(actual); - } + actual.ConvertRefNullToRef(); PushType(actual); return result; } @@ -804,6 +1245,51 @@ Result TypeChecker::OnIf(const TypeVector& param_types, return result; } +Result TypeChecker::OnGCUnary(Opcode opcode) { + Result result; + switch (opcode) { + case Opcode::RefEq: { + result = PopAndCheck2Types(Type::EqRef, Type::EqRef, "ref.eq"); + PushType(Type::I32); + return result; + } + case Opcode::ArrayLen: + result = PopAndCheck1Type(Type::ArrayRef, "array.len"); + PushType(Type::I32); + return result; + case Opcode::AnyConvertExtern: { + Type type; + // Nullability must be copied. + PeekType(0, &type); + result = PopAndCheck1Type(Type::ExternRef, "any.convert_extern"); + PushType(Type(Type::AnyRef, !type.IsNonNullableRef())); + return result; + } + case Opcode::ExternConvertAny: { + Type type; + // Nullability must be copied. + PeekType(0, &type); + result = PopAndCheck1Type(Type::AnyRef, "any.convert_extern"); + PushType(Type(Type::ExternRef, !type.IsNonNullableRef())); + return result; + } + case Opcode::RefI31: { + result = PopAndCheck1Type(Type::I32, "ref.i31"); + PushType(Type(Type::I31Ref, Type::ReferenceNonNull)); + return result; + } + case Opcode::I31GetS: + case Opcode::I31GetU: { + result = PopAndCheck1Type( + Type::I31Ref, opcode == Opcode::I31GetS ? "i31.get_s" : "i31.get_u"); + PushType(Type::I32); + return result; + } + default: + WABT_UNREACHABLE; + } +} + Result TypeChecker::OnGlobalGet(Type type) { PushType(type); return Result::Ok; @@ -926,15 +1412,44 @@ Result TypeChecker::OnTableFill(Type elem_type, const Limits& limits) { Result TypeChecker::OnRefAsNonNullExpr() { Type actual; CHECK_RESULT(PopAndCheckReference(&actual, "ref.as_non_null")); - if (actual != Type::Any) { - actual = convertRefNullToRef(actual); - } + actual.ConvertRefNullToRef(); PushType(actual); return Result::Ok; } +Result TypeChecker::OnRefCast(Type type) { + Result result = Result::Ok; + if (!type.IsRef()) { + PrintError("type mismatch: reference type expected"); + result = Result::Error; + } else { + Type expected = Type::Any; + result |= PeekType(0, &expected); + if (Succeeded(result)) { + if (!expected.IsRef()) { + PrintError("type mismatch: reference type expected, but got %s", + expected.GetName().c_str()); + result = Result::Error; + } else if (type != Type::NullRef && expected != Type::NullRef && + type_fields_.GetGroupType(type) != + type_fields_.GetGroupType(expected)) { + PrintError("type mismatch: %s is not a subtype of %s", + type.GetName().c_str(), expected.GetName().c_str()); + result = Result::Error; + } + } + } + DropTypes(1); + PushType(type); + return Result::Ok; +} + Result TypeChecker::OnRefFuncExpr(Index func_type) { - PushType(Type(Type::Ref, func_type)); + if (func_type == kInvalidIndex) { + PushType(Type(Type::FuncRef, Type::ReferenceNonNull)); + } else { + PushType(Type(Type::Ref, func_type)); + } return Result::Ok; } @@ -947,13 +1462,40 @@ Result TypeChecker::OnRefIsNullExpr() { Type type; Result result = PeekType(0, &type); if (!type.IsRef()) { - type = Type(Type::Reference, kInvalidIndex); + type = Type::FuncRef; } result |= PopAndCheck1Type(type, "ref.is_null"); PushType(Type::I32); return result; } +Result TypeChecker::OnRefTest(Type type) { + Result result = Result::Ok; + if (!type.IsRef()) { + PrintError("type mismatch: reference type expected"); + result = Result::Error; + } else { + Type expected = Type::Any; + result |= PeekType(0, &expected); + if (Succeeded(result)) { + if (!expected.IsRef()) { + PrintError("type mismatch: reference type expected, but got %s", + expected.GetName().c_str()); + result = Result::Error; + } else if (type != Type::NullRef && expected != Type::NullRef && + type_fields_.GetGroupType(type) != + type_fields_.GetGroupType(expected)) { + PrintError("type mismatch: %s is not a subtype of %s", + type.GetName().c_str(), expected.GetName().c_str()); + result = Result::Error; + } + } + } + DropTypes(1); + PushType(Type::I32); + return Result::Ok; +} + Result TypeChecker::OnRethrow(Index depth) { Result result = Result::Ok; Label* label; @@ -1015,6 +1557,80 @@ Result TypeChecker::OnStore(Opcode opcode, const Limits& limits) { return CheckOpcode2(opcode, &limits); } +Result TypeChecker::OnStructGet(Opcode opcode, + Type ref_type, + const StructType& struct_type, + Index field) { + Result result = PopAndCheck1Type(ref_type, "struct.get"); + if (field >= struct_type.fields.size()) { + PrintError("unknown field: %" PRIindex, field); + result = Result::Error; + } else { + Type type = struct_type.fields[field].type; + bool is_packed_get = (opcode != Opcode::StructGet); + + if (type.IsPackedType() != is_packed_get) { + PrintError("field %" PRIindex " is %spacked", field, + is_packed_get ? "not " : ""); + result = Result::Error; + } + + PushType(ToUnpackedType(type)); + } + return result; +} + +Result TypeChecker::OnStructNew(Type ref_type, const StructType& struct_type) { + Result result = Result::Ok; + size_t size = struct_type.fields.size(); + + for (size_t i = 0; i < size; ++i) { + result |= PeekAndCheckType(size - i - 1, + ToUnpackedType(struct_type.fields[i].type)); + } + + if (Failed(result)) { + // To improve performance, type vector + // conversion is only done on error. + TypeVector types; + types.reserve(size); + + for (size_t i = 0; i < size; ++i) { + types.push_back(ToUnpackedType(struct_type.fields[i].type)); + } + PrintStackIfFailedV(result, "struct.new", types, /*is_end=*/false); + } + result |= DropTypes(size); + PushType(ref_type); + return result; +} + +Result TypeChecker::OnStructNewDefault(Type ref_type) { + PushType(ref_type); + return Result::Ok; +} + +Result TypeChecker::OnStructSet(Type ref_type, + const StructType& struct_type, + Index field) { + Result result = Result::Ok; + + if (field >= struct_type.fields.size() || + !struct_type.fields[field].mutable_) { + const char* message = field >= struct_type.fields.size() + ? "unknown field: %" PRIindex + : "field %" PRIindex " is immutable"; + PrintError(message, field); + DropTypes(1); + PopAndCheck1Type(ref_type, "struct.set"); + result = Result::Error; + } else { + Type expected_type = ToUnpackedType(struct_type.fields[field].type); + result |= PopAndCheck2Types(ref_type, expected_type, "struct.set"); + } + return result; +} + Result TypeChecker::OnTry(const TypeVector& param_types, const TypeVector& result_types) { Result result = PopAndCheckSignature(param_types, "try"); @@ -1162,4 +1778,4 @@ Result TypeChecker::EndInitExpr() { return result; } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/validator.cc b/src/validator.cc index 76b8f31c40..36bec3a4de 100644 --- a/src/validator.cc +++ b/src/validator.cc @@ -98,6 +98,7 @@ class Validator : public ExprVisitor::Delegate { Result EndBlockExpr(BlockExpr*) override; Result OnBrExpr(BrExpr*) override; Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrOnCastExpr(BrOnCastExpr*) override; Result OnBrOnNonNullExpr(BrOnNonNullExpr*) override; Result OnBrOnNullExpr(BrOnNullExpr*) override; Result OnBrTableExpr(BrTableExpr*) override; @@ -170,6 +171,24 @@ class Validator : public ExprVisitor::Delegate { Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override; Result OnLoadSplatExpr(LoadSplatExpr*) override; Result OnLoadZeroExpr(LoadZeroExpr*) override; + Result OnArrayCopyExpr(ArrayCopyExpr*) override; + Result OnArrayFillExpr(ArrayFillExpr*) override; + Result OnArrayGetExpr(ArrayGetExpr*) override; + Result OnArrayInitDataExpr(ArrayInitDataExpr*) override; + Result OnArrayInitElemExpr(ArrayInitElemExpr*) override; + Result OnArrayNewExpr(ArrayNewExpr*) override; + Result OnArrayNewDataExpr(ArrayNewDataExpr*) override; + Result OnArrayNewDefaultExpr(ArrayNewDefaultExpr*) override; + Result OnArrayNewElemExpr(ArrayNewElemExpr*) override; + Result OnArrayNewFixedExpr(ArrayNewFixedExpr*) override; + Result OnArraySetExpr(ArraySetExpr*) override; + Result OnGCUnaryExpr(GCUnaryExpr*) override; + Result OnRefCastExpr(RefCastExpr*) override; + Result OnRefTestExpr(RefTestExpr*) override; + Result OnStructGetExpr(StructGetExpr*) override; + Result OnStructNewExpr(StructNewExpr*) override; + Result OnStructNewDefaultExpr(StructNewDefaultExpr*) override; + Result OnStructSetExpr(StructSetExpr*) override; private: Type GetDeclarationType(const FuncDeclaration&); @@ -203,28 +222,62 @@ static Result CheckType(Type actual, Type expected) { Type::Enum expected_type = expected; if (actual_type == expected_type) { - switch (actual_type) { - case Type::ExternRef: - case Type::FuncRef: - return (expected.IsNullableNonTypedRef() || - !actual.IsNullableNonTypedRef()) - ? Result::Ok - : Result::Error; - - case Type::Reference: - case Type::Ref: - case Type::RefNull: - if (actual == expected) { - return Result::Ok; - } - break; + return Result::Ok; + } + + if (!expected.IsRef() || !actual.IsRef()) { + return Result::Error; + } - default: + if (actual.IsBottomRef()) { + return Result::Ok; + } + + switch (expected_type) { + case Type::RefNull: + if (actual_type == Type::FuncRef) { + // Specification tests pass expected functions + // directly, their type is not relevant. return Result::Ok; - } + } + return Result::Error; + case Type::Ref: + if (actual_type == Type::ArrayRef || actual_type == Type::EqRef || + actual_type == Type::StructRef) { + return Result::Ok; + } + return Result::Error; + case Type::NullFuncRef: + case Type::FuncRef: + if (actual_type == Type::FuncRef) { + break; + } + return Result::Error; + case Type::NullExternRef: + case Type::ExternRef: + if (actual_type == Type::ExternRef) { + break; + } + return Result::Error; + case Type::NullRef: + case Type::AnyRef: + if (actual_type == Type::EqRef) { + break; + } + [[fallthrough]]; + case Type::EqRef: + if (actual_type == Type::I31Ref || actual_type == Type::StructRef || + actual_type == Type::ArrayRef) { + break; + } + break; + default: + return Result::Error; } - return Result::Error; + return (expected.IsNullableNonTypedRef() || !actual.IsNullableNonTypedRef()) + ? Result::Ok + : Result::Error; } void ScriptValidator::CheckTypeIndex(const Location* loc, @@ -337,6 +390,12 @@ Result Validator::OnBrIfExpr(BrIfExpr* expr) { return Result::Ok; } +Result Validator::OnBrOnCastExpr(BrOnCastExpr* expr) { + result_ |= validator_.OnBrOnCast(expr->loc, expr->opcode, expr->label_var, + expr->type1_var, expr->type2_var); + return Result::Ok; +} + Result Validator::OnBrOnNonNullExpr(BrOnNonNullExpr* expr) { result_ |= validator_.OnBrOnNonNull(expr->loc, expr->var); return Result::Ok; @@ -740,6 +799,97 @@ Result Validator::OnLoadZeroExpr(LoadZeroExpr* expr) { return Result::Ok; } +Result Validator::OnArrayCopyExpr(ArrayCopyExpr* expr) { + result_ |= validator_.OnArrayCopy(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayFillExpr(ArrayFillExpr* expr) { + result_ |= validator_.OnArrayFill(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayGetExpr(ArrayGetExpr* expr) { + result_ |= validator_.OnArrayGet(expr->loc, expr->opcode, expr->type_var); + return Result::Ok; +} + +Result Validator::OnArrayInitDataExpr(ArrayInitDataExpr* expr) { + result_ |= validator_.OnArrayInitData(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayInitElemExpr(ArrayInitElemExpr* expr) { + result_ |= validator_.OnArrayInitElem(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayNewExpr(ArrayNewExpr* expr) { + result_ |= validator_.OnArrayNew(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayNewDataExpr(ArrayNewDataExpr* expr) { + result_ |= validator_.OnArrayNewData(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayNewDefaultExpr(ArrayNewDefaultExpr* expr) { + result_ |= validator_.OnArrayNewDefault(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayNewElemExpr(ArrayNewElemExpr* expr) { + result_ |= validator_.OnArrayNewElem(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + +Result Validator::OnArrayNewFixedExpr(ArrayNewFixedExpr* expr) { + result_ |= validator_.OnArrayNewFixed(expr->loc, expr->type_var, expr->count); + return Result::Ok; +} + +Result Validator::OnArraySetExpr(ArraySetExpr* expr) { + result_ |= validator_.OnArraySet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnGCUnaryExpr(GCUnaryExpr* expr) { + result_ |= validator_.OnGCUnary(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::OnRefCastExpr(RefCastExpr* expr) { + result_ |= validator_.OnRefCast(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnRefTestExpr(RefTestExpr* expr) { + result_ |= validator_.OnRefTest(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnStructGetExpr(StructGetExpr* expr) { + result_ |= validator_.OnStructGet(expr->loc, expr->opcode, expr->type_var, + expr->var); + return Result::Ok; +} + +Result Validator::OnStructNewExpr(StructNewExpr* expr) { + result_ |= validator_.OnStructNew(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnStructNewDefaultExpr(StructNewDefaultExpr* expr) { + result_ |= validator_.OnStructNewDefault(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnStructSetExpr(StructSetExpr* expr) { + result_ |= validator_.OnStructSet(expr->loc, expr->type_var, expr->var); + return Result::Ok; +} + Validator::Validator(Errors* errors, const Module* module, const ValidateOptions& options) @@ -748,46 +898,96 @@ Validator::Validator(Errors* errors, validator_(errors_, options_), current_module_(module) {} +static void GetGCTypeExtension(const TypeEntryGCTypeExtension& gc_ext_in, + GCTypeExtension* gc_ext_out, + std::vector& sub_types) { + Index sub_type_count = static_cast(gc_ext_in.sub_types.size()); + + gc_ext_out->is_final_sub_type = gc_ext_in.is_final_sub_type; + gc_ext_out->sub_type_count = sub_type_count; + + sub_types.resize(sub_type_count); + for (Index i = 0; i < sub_type_count; i++) { + sub_types[i] = gc_ext_in.sub_types[i].index(); + } + + gc_ext_out->sub_types = sub_types.data(); +} + Result Validator::CheckModule() { const Module* module = current_module_; // Type section. + Index type_index = 0; + Index first_type_index = kInvalidIndex; + Index range_index = 0; + GCTypeExtension gc_ext; + std::vector sub_types; + + if (!module->recursive_ranges.empty()) { + first_type_index = module->recursive_ranges[0].first_type_index; + } + for (const ModuleField& field : module->fields) { if (auto* f = dyn_cast(&field)) { + if (type_index == first_type_index) { + const RecursiveRange& range = module->recursive_ranges[range_index]; + validator_.OnRecursiveType(range.first_type_index, range.type_count); + + first_type_index = kInvalidIndex; + if (++range_index < module->recursive_ranges.size()) { + first_type_index = + module->recursive_ranges[range_index].first_type_index; + } + } + switch (f->type->kind()) { case TypeEntryKind::Func: { FuncType* func_type = cast(f->type.get()); + GetGCTypeExtension(func_type->gc_ext, &gc_ext, sub_types); + result_ |= validator_.OnFuncType( field.loc, func_type->sig.param_types.size(), func_type->sig.param_types.data(), func_type->sig.result_types.size(), - func_type->sig.result_types.data(), - module->GetFuncTypeIndex(func_type->sig)); + func_type->sig.result_types.data(), type_index, &gc_ext); break; } case TypeEntryKind::Struct: { StructType* struct_type = cast(f->type.get()); + GetGCTypeExtension(struct_type->gc_ext, &gc_ext, sub_types); TypeMutVector type_muts; for (auto&& field : struct_type->fields) { type_muts.push_back(TypeMut{field.type, field.mutable_}); } result_ |= validator_.OnStructType(field.loc, type_muts.size(), - type_muts.data()); + type_muts.data(), &gc_ext); break; } case TypeEntryKind::Array: { ArrayType* array_type = cast(f->type.get()); + GetGCTypeExtension(array_type->gc_ext, &gc_ext, sub_types); result_ |= validator_.OnArrayType( field.loc, - TypeMut{array_type->field.type, array_type->field.mutable_}); + TypeMut{array_type->field.type, array_type->field.mutable_}, + &gc_ext); break; } } + + type_index++; } } + while (range_index < module->recursive_ranges.size()) { + // Should be 0 for valid modules. + const RecursiveRange& range = module->recursive_ranges[range_index]; + validator_.OnRecursiveType(range.first_type_index, range.type_count); + ++range_index; + } + // Import section. for (const ModuleField& field : module->fields) { if (auto* f = dyn_cast(&field)) { @@ -801,8 +1001,8 @@ Result Validator::CheckModule() { case ExternalKind::Table: { auto&& table = cast(f->import.get())->table; - result_ |= - validator_.OnTable(field.loc, table.elem_type, table.elem_limits); + result_ |= validator_.OnTable(field.loc, table.elem_type, + table.elem_limits, true, false); break; } @@ -841,8 +1041,18 @@ Result Validator::CheckModule() { // Table section. for (const ModuleField& field : module->fields) { if (auto* f = dyn_cast(&field)) { + bool has_init_expr = !f->table.init_expr.empty(); result_ |= validator_.OnTable(field.loc, f->table.elem_type, - f->table.elem_limits); + f->table.elem_limits, false, has_init_expr); + + // Init expr. + if (has_init_expr) { + result_ |= validator_.BeginInitExpr(field.loc, f->table.elem_type); + ExprVisitor visitor(this); + result_ |= + visitor.VisitExprList(const_cast(f->table.init_expr)); + result_ |= validator_.EndInitExpr(); + } } } @@ -1158,4 +1368,4 @@ Result ValidateModule(const Module* module, return validator.CheckModule(); } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/wast-parser.cc b/src/wast-parser.cc index ed0b155125..e6cfbffbd6 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -157,6 +157,7 @@ bool IsPlainInstr(TokenType token_type) { case TokenType::Select: case TokenType::Br: case TokenType::BrIf: + case TokenType::BrOnCast: case TokenType::BrOnNonNull: case TokenType::BrOnNull: case TokenType::BrTable: @@ -212,6 +213,30 @@ bool IsPlainInstr(TokenType token_type) { case TokenType::SimdLoadLane: case TokenType::SimdStoreLane: case TokenType::SimdShuffleOp: + case TokenType::ArrayCopy: + case TokenType::ArrayFill: + case TokenType::ArrayGet: + case TokenType::ArrayGetS: + case TokenType::ArrayGetU: + case TokenType::ArrayInitData: + case TokenType::ArrayInitElem: + case TokenType::ArrayNew: + case TokenType::ArrayNewData: + case TokenType::ArrayNewDefault: + case TokenType::ArrayNewFixed: + case TokenType::ArrayNewElem: + case TokenType::ArraySet: + case TokenType::GCUnary: + case TokenType::RefCast: + case TokenType::RefEq: + case TokenType::RefI31: + case TokenType::RefTest: + case TokenType::StructGet: + case TokenType::StructGetS: + case TokenType::StructGetU: + case TokenType::StructNew: + case TokenType::StructNewDefault: + case TokenType::StructSet: return true; default: return false; @@ -269,6 +294,7 @@ bool IsModuleField(TokenTypePair pair) { case TokenType::Export: case TokenType::Func: case TokenType::Type: + case TokenType::Rec: case TokenType::Global: case TokenType::Import: case TokenType::Memory: @@ -331,7 +357,7 @@ void ResolveImplicitlyDefinedFunctionType(const Location& loc, Index func_type_index = module->GetFuncTypeIndex(decl.sig); if (func_type_index == kInvalidIndex) { auto func_type_field = std::make_unique(loc); - auto func_type = std::make_unique(); + auto func_type = std::make_unique(true); func_type->sig = decl.sig; func_type_field->type = std::move(func_type); module->AppendField(std::move(func_type_field)); @@ -339,6 +365,29 @@ void ResolveImplicitlyDefinedFunctionType(const Location& loc, } } +bool IsTypeEnabled(Type::Enum type, WastParseOptions* options_) { + switch (type) { + case Type::V128: + return options_->features.simd_enabled(); + case Type::FuncRef: + case Type::ExternRef: + return options_->features.reference_types_enabled(); + case Type::ExnRef: + return options_->features.exceptions_enabled(); + case Type::NullFuncRef: + case Type::NullExternRef: + case Type::NullRef: + case Type::AnyRef: + case Type::EqRef: + case Type::I31Ref: + case Type::StructRef: + case Type::ArrayRef: + return options_->features.gc_enabled(); + default: + return true; + } +} + Result CheckTypeIndex(const Location& loc, Type actual, Type expected, @@ -886,20 +935,41 @@ Result WastParser::ParseRefDeclaration(Var* out_type) { EXPECT(Lpar); EXPECT(Ref); - Type::Enum opt_type = Type::Reference; + Type::Enum opt_type = Type::Ref; - if (options_->features.function_references_enabled()) { - opt_type = Type::Ref; + if (options_->features.function_references_enabled() && + Match(TokenType::Null)) { + opt_type = Type::RefNull; + } - if (Match(TokenType::Null)) { - opt_type = Type::RefNull; - } + TokenType token = Peek(0); + bool is_generic_ref = false; + + switch (token) { + case TokenType::Func: + case TokenType::Extern: + is_generic_ref = true; + break; + + case TokenType::Any: + case TokenType::Array: + case TokenType::Eq: + case TokenType::I31: + case TokenType::NoExtern: + case TokenType::NoFunc: + case TokenType::None: + case TokenType::Struct: + if (options_->features.gc_enabled()) { + is_generic_ref = true; + } + break; + + default: + break; } - if (PeekMatch(TokenType::Func) || PeekMatch(TokenType::Extern)) { - TokenType token = Consume().token_type(); - out_type->set_opt_type(token == TokenType::Func ? Type::FuncRef - : Type::ExternRef); + if (is_generic_ref) { + out_type->set_opt_type(Consume().type()); out_type->set_index(opt_type == Type::Ref ? Type::ReferenceNonNull : Type::ReferenceOrNull); } else { @@ -911,7 +981,7 @@ Result WastParser::ParseRefDeclaration(Var* out_type) { return Result::Ok; } -Result WastParser::ParseValueType(Var* out_type) { +Result WastParser::ParseValueType(Var* out_type, bool is_field) { WABT_TRACE(ParseValueType); if (PeekMatchRefType()) { @@ -919,30 +989,20 @@ Result WastParser::ParseValueType(Var* out_type) { } if (!PeekMatch(TokenType::ValueType)) { + if (is_field) { + return ErrorExpected({"i8", "i16", "i32", "i64", "f32", "f64", "v128", + "anyref", "arrayref", "eqref", "externref", + "exnref", "funcref", "i31ref", "nullref", + "nullexternref", "nullfuncref"}); + } return ErrorExpected( {"i32", "i64", "f32", "f64", "v128", "externref", "exnref", "funcref"}); } Token token = Consume(); Type type = token.type(); - bool is_enabled; - switch (type) { - case Type::V128: - is_enabled = options_->features.simd_enabled(); - break; - case Type::FuncRef: - case Type::ExternRef: - is_enabled = options_->features.reference_types_enabled(); - break; - case Type::ExnRef: - is_enabled = options_->features.exceptions_enabled(); - break; - default: - is_enabled = true; - break; - } - if (!is_enabled) { + if ((!is_field || !type.IsPackedType()) && !IsTypeEnabled(type, options_)) { Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); return Result::Error; } @@ -988,16 +1048,17 @@ Result WastParser::ParseRefKind(Var* out_type) { } if (!IsTokenTypeRefKind(Peek())) { + if (options_->features.gc_enabled()) { + return ErrorExpected({"any", "array", "func", "eq", "extern", "exn", + "i31", "noextern", "nofunc", "none", "struct"}); + } return ErrorExpected({"func", "extern", "exn"}); } Token token = Consume(); Type type = token.type(); - if ((type == Type::ExternRef && - !options_->features.reference_types_enabled()) || - ((type == Type::Struct || type == Type::Array) && - !options_->features.gc_enabled())) { + if (!IsTypeEnabled(type, options_)) { Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); return Result::Error; } @@ -1344,7 +1405,7 @@ Result WastParser::ResolveTargetRefType(const Module& module, assert(type->IsReferenceWithIndex() && !var.is_index()); if (type->GetReferenceIndex() != kInvalidIndex) { - // Resolved earlier. + // Index or resolved earlier. return Result::Ok; } @@ -1385,6 +1446,30 @@ Result WastParser::ResolveTargetTypeVector(const Module& module, return Result::Ok; } +Result WastParser::ResolveTargetFieldVector(const Module& module, + StructType* target_struct, + ReferenceVars* ref_vars, + Errors* errors) { + Result result = Result::Ok; + + for (auto& ref_var : *ref_vars) { + uint32_t index = ref_var.index; + + // The index of resolved variables is converted to kInvalidIndex. + if (index == kInvalidIndex) { + continue; + } + + ref_var.index = kInvalidIndex; + + assert(index < target_struct->fields.size()); + result |= ResolveTargetRefType(module, &target_struct->fields[index].type, + ref_var.var, errors); + } + + return Result::Ok; +} + Result WastParser::ParseModuleFieldList(Module* module) { WABT_TRACE(ParseModuleFieldList); @@ -1392,6 +1477,7 @@ Result WastParser::ParseModuleFieldList(Module* module) { resolve_ref_types_.clear(); resolve_type_vectors_.clear(); resolve_funcs_.clear(); + resolve_fields_.clear(); while (IsModuleField(PeekPair()) || PeekIsCustom()) { if (PeekIsCustom()) { @@ -1420,6 +1506,11 @@ Result WastParser::ParseModuleFieldList(Module* module) { it.target_func->local_types.Set(it.types); } + for (auto it : resolve_fields_) { + result |= + ResolveTargetFieldVector(*module, it.target_struct, &it.vars, errors_); + } + CHECK_RESULT(result); CHECK_RESULT(ResolveFuncTypes(module, errors_)); CHECK_RESULT(ResolveNamesModule(module, errors_)); @@ -1435,6 +1526,8 @@ Result WastParser::ParseModuleField(Module* module) { case TokenType::Export: return ParseExportModuleField(module); case TokenType::Func: return ParseFuncModuleField(module); case TokenType::Type: return ParseTypeModuleField(module); + case TokenType::Rec: + return ParseRecTypeModuleField(module); case TokenType::Global: return ParseGlobalModuleField(module); case TokenType::Import: return ParseImportModuleField(module); case TokenType::Memory: return ParseMemoryModuleField(module); @@ -1538,7 +1631,10 @@ Result WastParser::ParseElemModuleField(Module* module) { VarToType(elem_type, &field->elem_segment.elem_type); ParseElemExprListOpt(&field->elem_segment.elem_exprs); } else { - field->elem_segment.elem_type = Type::FuncRef; + field->elem_segment.elem_type = + Type(Type::FuncRef, options_->features.function_references_enabled() + ? Type::ReferenceNonNull + : Type::ReferenceOrNull); if (PeekMatch(TokenType::Func)) { EXPECT(Func); } @@ -1660,78 +1756,173 @@ Result WastParser::ParseTypeModuleField(Module* module) { EXPECT(Type); std::string name; + bool has_sub_type = false; + bool is_final_sub_type = true; + VarVector sub_types; + ParseBindVarOpt(&name); + + if (options_->features.gc_enabled() && MatchLpar(TokenType::Sub)) { + has_sub_type = true; + + if (!Match(TokenType::Final)) { + is_final_sub_type = false; + } + + Var var; + + while (ParseVarOpt(&var)) { + sub_types.emplace_back(var); + } + } + EXPECT(Lpar); Location loc = GetLocation(); if (Match(TokenType::Func)) { - auto func_type = std::make_unique(name); + auto func_type = std::make_unique(is_final_sub_type, name); BindingHash bindings; CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings)); CHECK_RESULT(ErrorIfLpar({"param", "result"})); + func_type->gc_ext.sub_types = std::move(sub_types); field->type = std::move(func_type); } else if (Match(TokenType::Struct)) { if (!options_->features.gc_enabled()) { Error(loc, "struct not allowed"); return Result::Error; } - auto struct_type = std::make_unique(name); - CHECK_RESULT(ParseFieldList(&struct_type->fields)); + auto struct_type = std::make_unique(is_final_sub_type, name); + CHECK_RESULT(ParseFieldList(struct_type.get())); + struct_type->gc_ext.sub_types = std::move(sub_types); field->type = std::move(struct_type); } else if (Match(TokenType::Array)) { if (!options_->features.gc_enabled()) { Error(loc, "array type not allowed"); } - auto array_type = std::make_unique(name); + auto array_type = std::make_unique(is_final_sub_type, name); CHECK_RESULT(ParseField(&array_type->field)); + array_type->gc_ext.sub_types = std::move(sub_types); field->type = std::move(array_type); } else { return ErrorExpected({"func", "struct", "array"}); } EXPECT(Rpar); + if (has_sub_type) { + EXPECT(Rpar); + } EXPECT(Rpar); module->AppendField(std::move(field)); return Result::Ok; } +Result WastParser::ParseRecTypeModuleField(Module* module) { + WABT_TRACE(ParseTypeModuleField); + + Location loc = Consume().loc; + + if (!options_->features.gc_enabled()) { + errors_->emplace_back(ErrorLevel::Error, loc, + StringPrintf("garbage collection not enabled")); + return Result::Error; + } + + EXPECT(Rec); + + Index start_index = static_cast(module->types.size()); + + while (PeekMatchLpar(TokenType::Type)) { + CHECK_RESULT(ParseTypeModuleField(module)); + } + + Index type_count = static_cast(module->types.size()) - start_index; + if (type_count == 0) { + auto field = std::make_unique(loc); + module->AppendField(std::move(field)); + } else if (type_count > 1) { + module->recursive_ranges.push_back(RecursiveRange{start_index, type_count}); + } + + EXPECT(Rpar); + return Result::Ok; +} + Result WastParser::ParseField(Field* field) { WABT_TRACE(ParseField); - auto parse_mut_valuetype = [&]() -> Result { - // TODO: Share with ParseGlobalType? - if (MatchLpar(TokenType::Mut)) { - field->mutable_ = true; - Var type; - CHECK_RESULT(ParseValueType(&type)); - field->type = Type(type.opt_type()); - EXPECT(Rpar); - } else { - field->mutable_ = false; - Var type; - CHECK_RESULT(ParseValueType(&type)); - field->type = Type(type.opt_type()); - } - return Result::Ok; - }; - if (MatchLpar(TokenType::Field)) { - ParseBindVarOpt(&field->name); - CHECK_RESULT(parse_mut_valuetype()); + field->mutable_ = MatchLpar(TokenType::Mut); + + Var type; + CHECK_RESULT(ParseValueType(&type, true)); + VarToType(type, &field->type); + + if (field->mutable_) { EXPECT(Rpar); - } else { - CHECK_RESULT(parse_mut_valuetype()); } return Result::Ok; } -Result WastParser::ParseFieldList(std::vector* fields) { +Result WastParser::ParseFieldList(StructType* struct_type) { WABT_TRACE(ParseFieldList); - while (PeekMatch(TokenType::ValueType) || PeekMatch(TokenType::Lpar)) { + + std::set known_fields; + ResolveField references(struct_type); + + while (MatchLpar(TokenType::Field)) { + if (Match(TokenType::Rpar)) { + continue; + } + Field field; - CHECK_RESULT(ParseField(&field)); - fields->push_back(field); + bool has_name = false; + Location loc; + + if (PeekMatch(TokenType::Var)) { + Token token = Consume(); + field.name = std::string(token.text()); + + if (!known_fields.insert(field.name).second) { + errors_->emplace_back( + ErrorLevel::Error, loc, + StringPrintf("duplicate field %s", field.name.c_str())); + return Result::Error; + } + + has_name = true; + loc = token.loc; + } + + do { + field.mutable_ = MatchLpar(TokenType::Mut); + + Var type; + CHECK_RESULT(ParseValueType(&type, true)); + + if (field.mutable_) { + EXPECT(Rpar); + } + + if (type.is_index()) { + field.type = type.to_type(); + } else { + assert(type.is_name()); + assert(options_->features.gc_enabled()); + references.vars.push_back( + ReferenceVar(struct_type->fields.size(), type)); + field.type = Type(type.opt_type(), kInvalidIndex); + } + + struct_type->fields.push_back(field); + } while (!has_name && !PeekMatch(TokenType::Rpar)); + + EXPECT(Rpar); } + + if (!references.vars.empty()) { + resolve_fields_.push_back(references); + } + return Result::Ok; } @@ -2001,6 +2192,9 @@ Result WastParser::ParseTableModuleField(Module* module) { Var elem_type; CHECK_RESULT(ParseRefType(&elem_type)); VarToType(elem_type, &table.elem_type); + if (PeekMatch(TokenType::Lpar)) { + CHECK_RESULT(ParseTerminatingInstrList(&table.init_expr)); + } module->AppendField(std::move(field)); } } @@ -2233,6 +2427,16 @@ Result WastParser::ParsePlainInstrVar(Location loc, return Result::Ok; } +template +Result WastParser::ParsePlainInstrVarVar(Location loc, + std::unique_ptr* out_expr) { + Var first_var, second_var; + CHECK_RESULT(ParseVar(&first_var)); + CHECK_RESULT(ParseVar(&second_var)); + out_expr->reset(new T(first_var, second_var, loc)); + return Result::Ok; +} + template Result WastParser::ParseMemoryInstrVar(Location loc, std::unique_ptr* out_expr) { @@ -2406,6 +2610,18 @@ Result WastParser::ParsePlainInstr(std::unique_ptr* out_expr) { CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); break; + case TokenType::BrOnCast: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var label, type1, type2; + CHECK_RESULT(ParseVar(&label)); + CHECK_RESULT(ParseRefType(&type1)); + CHECK_RESULT(ParseRefType(&type2)); + out_expr->reset( + new BrOnCastExpr(token.opcode(), label, type1, type2, loc)); + break; + } + case TokenType::BrOnNonNull: Consume(); CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); @@ -2745,6 +2961,152 @@ Result WastParser::ParsePlainInstr(std::unique_ptr* out_expr) { break; } + case TokenType::ArrayCopy: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + + case TokenType::ArrayFill: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::ArrayInitData: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + + case TokenType::ArrayInitElem: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + + case TokenType::ArrayGet: + case TokenType::ArrayGetS: + case TokenType::ArrayGetU: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var type; + CHECK_RESULT(ParseVar(&type)); + out_expr->reset(new ArrayGetExpr(token.opcode(), type, loc)); + break; + } + + case TokenType::ArrayNew: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::ArrayNewData: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + + case TokenType::ArrayNewDefault: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::ArrayNewFixed: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var type; + uint64_t count; + CHECK_RESULT(ParseVar(&type)); + CHECK_RESULT(ParseNat(&count, false)); + out_expr->reset( + new ArrayNewFixedExpr(type, static_cast(count), loc)); + break; + } + + case TokenType::ArrayNewElem: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + + case TokenType::ArraySet: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::GCUnary: + case TokenType::RefEq: + case TokenType::RefI31: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + out_expr->reset(new GCUnaryExpr(token.opcode(), loc)); + break; + } + + case TokenType::RefCast: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var type; + CHECK_RESULT(ParseRefType(&type)); + out_expr->reset(new RefCastExpr(type, loc)); + break; + } + + case TokenType::RefTest: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var type; + CHECK_RESULT(ParseRefType(&type)); + out_expr->reset(new RefTestExpr(type, loc)); + break; + } + + case TokenType::StructGet: + case TokenType::StructGetS: + case TokenType::StructGetU: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + Var type, field; + CHECK_RESULT(ParseVar(&type)); + CHECK_RESULT(ParseVar(&field)); + out_expr->reset(new StructGetExpr(token.opcode(), type, field, loc)); + break; + } + + case TokenType::StructNew: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::StructNewDefault: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVar(loc, out_expr)); + break; + } + + case TokenType::StructSet: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParsePlainInstrVarVar(loc, out_expr)); + break; + } + case TokenType::SimdLaneOp: { Token token = Consume(); ErrorUnlessOpcodeEnabled(token); @@ -3059,7 +3421,7 @@ Result WastParser::ParseExternref(Const* const_) { WABT_TRACE(ParseExternref); Token token = Consume(); if (!options_->features.reference_types_enabled()) { - Error(token.loc, "externref not allowed"); + Error(token.loc, "externref/hostref not allowed"); return Result::Error; } @@ -3068,24 +3430,35 @@ Result WastParser::ParseExternref(Const* const_) { const_->loc = GetLocation(); TokenType token_type = Peek(); + uint64_t ref_bits = Const::kRefAnyValueBits; + Result result = Result::Ok; + switch (token_type) { case TokenType::Nat: case TokenType::Int: { literal = Consume().literal(); sv = literal.text; + result = ParseInt64(sv, &ref_bits, ParseIntType::UnsignedOnly); break; } + case TokenType::Rpar: + break; default: return ErrorExpected({"a numeric literal"}, "123"); } - uint64_t ref_bits; - Result result = ParseInt64(sv, &ref_bits, ParseIntType::UnsignedOnly); - - if (ref_bits != 0 && options_->features.function_references_enabled()) { - const_->set_extern(static_cast(ref_bits)); + if (options_->features.function_references_enabled()) { + if (token.token_type() == TokenType::RefExtern) { + const_->set_extern(static_cast(ref_bits)); + } else { + const_->set_any(static_cast(ref_bits)); + } } else { - const_->set_externref(static_cast(ref_bits)); + if (token.token_type() == TokenType::RefExtern) { + const_->set_externref(static_cast(ref_bits)); + } else { + const_->set_anyref(static_cast(ref_bits)); + } } if (Failed(result)) { @@ -3100,9 +3473,13 @@ Result WastParser::ParseExternref(Const* const_) { Result WastParser::ParseConstList(ConstVector* consts, ConstType type) { WABT_TRACE(ParseConstList); - while (PeekMatchLpar(TokenType::Const) || PeekMatchLpar(TokenType::RefNull) || - PeekMatchLpar(TokenType::RefExtern) || - PeekMatchLpar(TokenType::RefFunc)) { + while ( + PeekMatchLpar(TokenType::Const) || PeekMatchLpar(TokenType::RefNull) || + PeekMatchLpar(TokenType::RefArray) || PeekMatchLpar(TokenType::RefEq) || + PeekMatchLpar(TokenType::RefExtern) || + PeekMatchLpar(TokenType::RefHost) || PeekMatchLpar(TokenType::RefI31) || + PeekMatchLpar(TokenType::RefFunc) || + PeekMatchLpar(TokenType::RefStruct)) { Consume(); Const const_; switch (Peek()) { @@ -3111,11 +3488,58 @@ Result WastParser::ParseConstList(ConstVector* consts, ConstType type) { break; case TokenType::RefNull: { auto token = Consume(); - Var type; - CHECK_RESULT(ParseRefKind(&type)); + Type type = Type::BottomRef(); + + if (Peek() != TokenType::Rpar) { + Var var; + CHECK_RESULT(ParseRefKind(&var)); + type = var.opt_type(); + // Nulls are represented by "null" string. + if (type == Type::NullRef) { + type = Type::AnyRef; + } else if (type == Type::NullExternRef) { + type = Type::ExternRef; + } else if (type == Type::NullFuncRef) { + type = Type::FuncRef; + } + } ErrorUnlessOpcodeEnabled(token); const_.loc = GetLocation(); - const_.set_null(type.opt_type()); + const_.set_null(type); + break; + } + case TokenType::RefArray: { + auto token = Consume(); + if (!options_->features.gc_enabled()) { + Error(token.loc, "ref.array not allowed"); + return Result::Error; + } + const_.loc = GetLocation(); + const_.set_arrayref(); + break; + } + case TokenType::RefEq: { + auto token = Consume(); + if (!options_->features.gc_enabled()) { + Error(token.loc, "ref.eq not allowed"); + return Result::Error; + } + const_.loc = GetLocation(); + const_.set_eqref(); + break; + } + case TokenType::RefExtern: + case TokenType::RefHost: + CHECK_RESULT(ParseExternref(&const_)); + break; + case TokenType::RefI31: { + auto token = Consume(); + if (!options_->features.gc_enabled()) { + Error(token.loc, "ref.i31 not allowed"); + return Result::Error; + } + const_.loc = GetLocation(); + const_.set_i31ref(); break; } case TokenType::RefFunc: { @@ -3125,9 +3549,16 @@ Result WastParser::ParseConstList(ConstVector* consts, ConstType type) { const_.set_funcref(); break; } - case TokenType::RefExtern: - CHECK_RESULT(ParseExternref(&const_)); + case TokenType::RefStruct: { + auto token = Consume(); + if (!options_->features.gc_enabled()) { + Error(token.loc, "ref.struct not allowed"); + return Result::Error; + } + const_.loc = GetLocation(); + const_.set_structref(); break; + } default: assert(!"unreachable"); return Result::Error; @@ -3971,6 +4402,15 @@ bool WastParser::CheckRefType(Type::Enum type) { return options_->features.reference_types_enabled(); case Type::ExnRef: return options_->features.exceptions_enabled(); + case Type::NullFuncRef: + case Type::NullExternRef: + case Type::NullRef: + case Type::AnyRef: + case Type::EqRef: + case Type::I31Ref: + case Type::StructRef: + case Type::ArrayRef: + return options_->features.gc_enabled(); default: assert(!Type::EnumIsNonTypedRef(type)); return false; @@ -4041,4 +4481,4 @@ Result ParseWastScript(WastLexer* lexer, return Result::Ok; } -} // namespace wabt +} // namespace wabt \ No newline at end of file diff --git a/src/wat-writer.cc b/src/wat-writer.cc index 5da55e8535..fa245e36df 100644 --- a/src/wat-writer.cc +++ b/src/wat-writer.cc @@ -128,7 +128,7 @@ class WatWriter : ModuleContext { const Var& destmemidx, NextChar next_char); void WriteBrVar(const Var& var, NextChar next_char); - void WriteRefKind(Type type, NextChar next_char); + void WriteRef(const Var& var, NextChar next_char); void WriteType(Type type, NextChar next_char); void WriteTypes(const TypeVector& types, const char* name); void WriteFuncSigSpace(const FuncSignature& func_sig); @@ -196,6 +196,7 @@ class WatWriter : ModuleContext { Index table_index_ = 0; Index memory_index_ = 0; Index type_index_ = 0; + Index recursive_range_index_ = 0; Index tag_index_ = 0; Index data_segment_index_ = 0; Index elem_segment_index_ = 0; @@ -418,8 +419,17 @@ void WatWriter::WriteBrVar(const Var& var, NextChar next_char) { } } -void WatWriter::WriteRefKind(Type type, NextChar next_char) { - WritePuts(type.GetRefKindName(), next_char); +void WatWriter::WriteRef(const Var& var, NextChar next_char) { + Type type = var.to_type(); + + if (!type.IsReferenceWithIndex()) { + WritePuts(type.GetName().c_str(), next_char); + return; + } + + WritePuts(type == Type::Ref ? "(ref" : "(ref null", NextChar::Space); + WriteVar(var, NextChar::None); + WritePuts(")", next_char); } void WatWriter::WriteType(Type type, NextChar next_char) { @@ -554,6 +564,7 @@ class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate { Result EndBlockExpr(BlockExpr*) override; Result OnBrExpr(BrExpr*) override; Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrOnCastExpr(BrOnCastExpr*) override; Result OnBrOnNonNullExpr(BrOnNonNullExpr*) override; Result OnBrOnNullExpr(BrOnNullExpr*) override; Result OnBrTableExpr(BrTableExpr*) override; @@ -626,6 +637,24 @@ class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate { Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override; Result OnLoadSplatExpr(LoadSplatExpr*) override; Result OnLoadZeroExpr(LoadZeroExpr*) override; + Result OnArrayCopyExpr(ArrayCopyExpr*) override; + Result OnArrayFillExpr(ArrayFillExpr*) override; + Result OnArrayGetExpr(ArrayGetExpr*) override; + Result OnArrayInitDataExpr(ArrayInitDataExpr*) override; + Result OnArrayInitElemExpr(ArrayInitElemExpr*) override; + Result OnArrayNewExpr(ArrayNewExpr*) override; + Result OnArrayNewDataExpr(ArrayNewDataExpr*) override; + Result OnArrayNewDefaultExpr(ArrayNewDefaultExpr*) override; + Result OnArrayNewElemExpr(ArrayNewElemExpr*) override; + Result OnArrayNewFixedExpr(ArrayNewFixedExpr*) override; + Result OnArraySetExpr(ArraySetExpr*) override; + Result OnGCUnaryExpr(GCUnaryExpr*) override; + Result OnRefCastExpr(RefCastExpr*) override; + Result OnRefTestExpr(RefTestExpr*) override; + Result OnStructGetExpr(StructGetExpr*) override; + Result OnStructNewExpr(StructNewExpr*) override; + Result OnStructNewDefaultExpr(StructNewDefaultExpr*) override; + Result OnStructSetExpr(StructSetExpr*) override; private: WatWriter* writer_; @@ -659,6 +688,14 @@ Result WatWriter::ExprVisitorDelegate::OnBrIfExpr(BrIfExpr* expr) { return Result::Ok; } +Result WatWriter::ExprVisitorDelegate::OnBrOnCastExpr(BrOnCastExpr* expr) { + writer_->WritePutsSpace(expr->opcode.GetName()); + writer_->WriteBrVar(expr->label_var, NextChar::Space); + writer_->WriteRef(expr->type1_var, NextChar::Space); + writer_->WriteRef(expr->type2_var, NextChar::Newline); + return Result::Ok; +} + Result WatWriter::ExprVisitorDelegate::OnBrOnNonNullExpr( BrOnNonNullExpr* expr) { writer_->WritePutsSpace(Opcode::BrOnNonNull_Opcode.GetName()); @@ -914,9 +951,11 @@ Result WatWriter::ExprVisitorDelegate::OnRefFuncExpr(RefFuncExpr* expr) { Result WatWriter::ExprVisitorDelegate::OnRefNullExpr(RefNullExpr* expr) { writer_->WritePutsSpace(Opcode::RefNull_Opcode.GetName()); - if (expr->type.opt_type() != Type::RefNull) { - assert(!Type(expr->type.opt_type()).IsReferenceWithIndex()); - writer_->WriteRefKind(expr->type.opt_type(), NextChar::Newline); + Type type = expr->type.to_type(); + + if (type != Type::RefNull) { + assert(!type.IsReferenceWithIndex()); + writer_->WritePuts(type.GetRefKindName(), NextChar::Newline); } else { writer_->WriteVar(expr->type, NextChar::Newline); } @@ -1192,6 +1231,128 @@ Result WatWriter::ExprVisitorDelegate::OnLoadZeroExpr(LoadZeroExpr* expr) { return Result::Ok; } +Result WatWriter::ExprVisitorDelegate::OnArrayCopyExpr(ArrayCopyExpr* expr) { + writer_->WritePuts("array.copy", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayFillExpr(ArrayFillExpr* expr) { + writer_->WritePuts("array.fill", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayGetExpr(ArrayGetExpr* expr) { + writer_->WritePuts(expr->opcode.GetName(), NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayInitDataExpr( + ArrayInitDataExpr* expr) { + writer_->WritePuts("array.init_data", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayInitElemExpr( + ArrayInitElemExpr* expr) { + writer_->WritePuts("array.init_elem", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayNewExpr(ArrayNewExpr* expr) { + writer_->WritePuts("array.new", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayNewDataExpr( + ArrayNewDataExpr* expr) { + writer_->WritePuts("array.new_data", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayNewDefaultExpr( + ArrayNewDefaultExpr* expr) { + writer_->WritePuts("array.new_default", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayNewElemExpr( + ArrayNewElemExpr* expr) { + writer_->WritePuts("array.new_elem", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArrayNewFixedExpr( + ArrayNewFixedExpr* expr) { + writer_->WritePuts("array.new_fixed", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(Var(expr->count, Location()), NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnArraySetExpr(ArraySetExpr* expr) { + writer_->WritePuts("array.set", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnGCUnaryExpr(GCUnaryExpr* expr) { + writer_->WritePuts(expr->opcode.GetName(), NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRefCastExpr(RefCastExpr* expr) { + writer_->WritePutsSpace("ref.cast"); + writer_->WriteRef(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRefTestExpr(RefTestExpr* expr) { + writer_->WritePutsSpace("ref.test"); + writer_->WriteRef(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnStructGetExpr(StructGetExpr* expr) { + writer_->WritePuts(expr->opcode.GetName(), NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnStructNewExpr(StructNewExpr* expr) { + writer_->WritePuts("struct.new", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnStructNewDefaultExpr( + StructNewDefaultExpr* expr) { + writer_->WritePuts("struct.new_default", NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnStructSetExpr(StructSetExpr* expr) { + writer_->WritePuts("struct.set", NextChar::Space); + writer_->WriteVar(expr->type_var, NextChar::Space); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + void WatWriter::WriteExpr(const Expr* expr) { WABT_TRACE(WriteExprList); ExprVisitorDelegate delegate(this); @@ -1596,7 +1757,8 @@ void WatWriter::WriteElemSegment(const ElemSegment& segment) { Writef("(;%u;)", elem_segment_index_); } - uint8_t flags = segment.GetFlags(&module); + uint8_t flags = segment.GetFlags( + &module, options_.features.function_references_enabled()); if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) { WriteOpenSpace("table"); @@ -1714,6 +1876,12 @@ void WatWriter::WriteExport(const Export& export_) { } void WatWriter::WriteTypeEntry(const TypeEntry& type) { + if (recursive_range_index_ < module.recursive_ranges.size() && + module.recursive_ranges[recursive_range_index_].first_type_index == + type_index_) { + WriteOpen("rec", NextChar::Newline); + } + WriteOpenSpace("type"); WriteNameOrIndex(type.name, type_index_++, NextChar::Space); switch (type.kind()) { @@ -1747,6 +1915,14 @@ void WatWriter::WriteTypeEntry(const TypeEntry& type) { } } WriteCloseNewline(); + + // The type_index_ is increased above. + if (recursive_range_index_ < module.recursive_ranges.size() && + module.recursive_ranges[recursive_range_index_].EndTypeIndex() == + type_index_) { + WriteCloseNewline(); + recursive_range_index_++; + } } void WatWriter::WriteField(const Field& field) { @@ -1813,6 +1989,9 @@ Result WatWriter::WriteModule() { case ModuleFieldType::Type: WriteTypeEntry(*cast(&field)->type); break; + case ModuleFieldType::EmptyRec: + WritePuts("(rec)", NextChar::Newline); + break; case ModuleFieldType::Start: WriteStartFunction(cast(&field)->start); break; @@ -1958,4 +2137,4 @@ Result WriteWat(Stream* stream, return wat_writer.WriteModule(); } -} // namespace wabt +} // namespace wabt \ No newline at end of file