diff options
-rw-r--r-- | src/binaryen-c.cpp | 2 | ||||
-rw-r--r-- | src/literal.h | 17 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 10 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 60 | ||||
-rw-r--r-- | test/spec/ref_test.wast | 52 |
5 files changed, 92 insertions, 49 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 6beb2b97c..4fbd4cfa0 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -81,8 +81,8 @@ BinaryenLiteral toBinaryenLiteral(Literal x) { case HeapType::i31: WASM_UNREACHABLE("TODO: i31"); case HeapType::ext: - case HeapType::any: WASM_UNREACHABLE("TODO: extern literals"); + case HeapType::any: case HeapType::eq: case HeapType::func: case HeapType::struct_: diff --git a/src/literal.h b/src/literal.h index 9d7ac2222..b484fc3b8 100644 --- a/src/literal.h +++ b/src/literal.h @@ -45,17 +45,17 @@ class Literal { uint8_t v128[16]; // funcref function name. `isNull()` indicates a `null` value. Name func; - // A reference to GC data, either a Struct or an Array. For both of those - // we store the referred data as a Literals object (which is natural for an + // A reference to GC data, either a Struct or an Array. For both of those we + // store the referred data as a Literals object (which is natural for an // Array, and for a Struct, is just the fields in order). The type is used // to indicate whether this is a Struct or an Array, and of what type. We // also use this to store String data, as it is similarly stored on the - // heap. + // heap. For externrefs, the gcData is the same as for the corresponding + // internal references and the values are only differentiated by the type. + // Externalized i31 references have a gcData containing the internal i31 + // reference as its sole value even though internal i31 references do not + // have a gcData. std::shared_ptr<GCData> gcData; - // TODO: Literals of type `anyref` can only be `null` currently but we - // will need to represent external values eventually, to - // 1) run the spec tests and fuzzer with reference types enabled and - // 2) avoid bailing out when seeing a reference typed value in precompute }; public: @@ -665,6 +665,9 @@ public: Literal relaxedFmaF64x2(const Literal& left, const Literal& right) const; Literal relaxedFmsF64x2(const Literal& left, const Literal& right) const; + Literal externalize() const; + Literal internalize() const; + private: Literal addSatSI8(const Literal& other) const; Literal addSatUI8(const Literal& other) const; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index afc193f71..75f2a8338 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1785,16 +1785,16 @@ public: } const auto& value = flow.getSingleValue(); NOTE_EVAL1(value); - if (value.isNull()) { - trap("null ref"); - } switch (curr->op) { case RefAsNonNull: - // We've already checked for a null. + if (value.isNull()) { + trap("null ref"); + } return value; case ExternInternalize: + return value.internalize(); case ExternExternalize: - WASM_UNREACHABLE("unimplemented extern conversion"); + return value.externalize(); } WASM_UNREACHABLE("unimplemented ref.as_*"); } diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 2a7dd1bcf..230eeae66 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -70,10 +70,11 @@ Literal::Literal(const uint8_t init[16]) : type(Type::v128) { } Literal::Literal(std::shared_ptr<GCData> gcData, HeapType type) - : gcData(gcData), type(type, NonNullable) { + : gcData(gcData), type(type, gcData ? NonNullable : Nullable) { // The type must be a proper type for GC data: either a struct, array, or - // string; or a null. - assert((isData() && gcData) || (type.isBottom() && !gcData)); + // string; or an externalized version of the same; or a null. + assert((isData() && gcData) || (type == HeapType::ext && gcData) || + (type.isBottom() && !gcData)); } Literal::Literal(std::string string) @@ -110,7 +111,7 @@ Literal::Literal(const Literal& other) : type(other.type) { new (&gcData) std::shared_ptr<GCData>(); return; } - if (other.isData()) { + if (other.isData() || other.type.getHeapType() == HeapType::ext) { new (&gcData) std::shared_ptr<GCData>(other.gcData); return; } @@ -126,14 +127,14 @@ Literal::Literal(const Literal& other) : type(other.type) { case HeapType::i31: i32 = other.i32; return; + case HeapType::ext: + gcData = other.gcData; + return; case HeapType::none: case HeapType::noext: case HeapType::nofunc: - // Null - return; - case HeapType::ext: + WASM_UNREACHABLE("null literals should already have been handled"); case HeapType::any: - WASM_UNREACHABLE("TODO: extern literals"); case HeapType::eq: case HeapType::func: case HeapType::struct_: @@ -154,7 +155,7 @@ Literal::~Literal() { if (type.isBasic()) { return; } - if (isNull() || isData()) { + if (isNull() || isData() || type.getHeapType() == HeapType::ext) { gcData.~shared_ptr(); } } @@ -584,8 +585,9 @@ std::ostream& operator<<(std::ostream& o, Literal literal) { o << "nullfuncref"; break; case HeapType::ext: + o << "externref"; + break; case HeapType::any: - WASM_UNREACHABLE("TODO: extern literals"); case HeapType::eq: case HeapType::func: case HeapType::struct_: @@ -2590,4 +2592,42 @@ Literal Literal::relaxedFmsF64x2(const Literal& left, return ternary<2, &Literal::getLanesF64x2, &Literal::fms>(*this, left, right); } +Literal Literal::externalize() const { + assert(Type::isSubType(type, Type(HeapType::any, Nullable)) && + "can only externalize internal references"); + if (isNull()) { + return Literal(std::shared_ptr<GCData>{}, HeapType::noext); + } + auto heapType = type.getHeapType(); + if (heapType.isBasic()) { + switch (heapType.getBasic()) { + case HeapType::i31: { + return Literal(std::make_shared<GCData>(HeapType::i31, Literals{*this}), + HeapType::ext); + } + case HeapType::string: + case HeapType::stringview_wtf8: + case HeapType::stringview_wtf16: + case HeapType::stringview_iter: + WASM_UNREACHABLE("TODO: string literals"); + default: + WASM_UNREACHABLE("unexpected type"); + } + } + return Literal(gcData, HeapType::ext); +} + +Literal Literal::internalize() const { + assert(Type::isSubType(type, Type(HeapType::ext, Nullable)) && + "can only internalize external references"); + if (isNull()) { + return Literal(std::shared_ptr<GCData>{}, HeapType::none); + } + if (gcData->type == HeapType::i31) { + assert(gcData->values[0].type.getHeapType() == HeapType::i31); + return gcData->values[0]; + } + return Literal(gcData, gcData->type); +} + } // namespace wasm diff --git a/test/spec/ref_test.wast b/test/spec/ref_test.wast index b0bb1df3d..b5291e179 100644 --- a/test/spec/ref_test.wast +++ b/test/spec/ref_test.wast @@ -19,8 +19,8 @@ (table.set $ta (i32.const 3) (i31.new (i32.const 7))) (table.set $ta (i32.const 4) (struct.new_default $st)) (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0))) - ;; (table.set $ta (i32.const 6) (extern.internalize (extern.externalize (i31.new (i32.const 0))))) - ;; (table.set $ta (i32.const 7) (extern.internalize (ref.null extern))) + (table.set $ta (i32.const 6) (extern.internalize (extern.externalize (i31.new (i32.const 0))))) + (table.set $ta (i32.const 7) (extern.internalize (ref.null extern))) (table.set $tf (i32.const 0) (ref.null nofunc)) (table.set $tf (i32.const 1) (ref.null func)) @@ -28,10 +28,10 @@ (table.set $te (i32.const 0) (ref.null noextern)) (table.set $te (i32.const 1) (ref.null extern)) - ;; (table.set $te (i32.const 2) (extern.externalize (i31.new (i32.const 0)))) - ;; (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8)))) - ;; (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st))) - ;; (table.set $te (i32.const 5) (extern.externalize (ref.null any))) + (table.set $te (i32.const 2) (extern.externalize (i31.new (i32.const 0)))) + (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8)))) + (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st))) + (table.set $te (i32.const 5) (extern.externalize (ref.null any))) ) (func (export "ref_test_null_data") (param $i i32) (result i32) @@ -106,8 +106,8 @@ (assert_return (invoke "ref_test_null_data" (i32.const 3)) (i32.const 0)) (assert_return (invoke "ref_test_null_data" (i32.const 4)) (i32.const 0)) (assert_return (invoke "ref_test_null_data" (i32.const 5)) (i32.const 0)) -;; (assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0)) -;; (assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2)) (assert_return (invoke "ref_test_any" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_any" (i32.const 1)) (i32.const 1)) @@ -115,8 +115,8 @@ (assert_return (invoke "ref_test_any" (i32.const 3)) (i32.const 2)) (assert_return (invoke "ref_test_any" (i32.const 4)) (i32.const 2)) (assert_return (invoke "ref_test_any" (i32.const 5)) (i32.const 2)) -;; (assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2)) -;; (assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1)) (assert_return (invoke "ref_test_eq" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_eq" (i32.const 1)) (i32.const 1)) @@ -124,8 +124,8 @@ (assert_return (invoke "ref_test_eq" (i32.const 3)) (i32.const 2)) (assert_return (invoke "ref_test_eq" (i32.const 4)) (i32.const 2)) (assert_return (invoke "ref_test_eq" (i32.const 5)) (i32.const 2)) -;; (assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 0)) -;; (assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1)) (assert_return (invoke "ref_test_i31" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_i31" (i32.const 1)) (i32.const 1)) @@ -133,8 +133,8 @@ (assert_return (invoke "ref_test_i31" (i32.const 3)) (i32.const 2)) (assert_return (invoke "ref_test_i31" (i32.const 4)) (i32.const 0)) (assert_return (invoke "ref_test_i31" (i32.const 5)) (i32.const 0)) -;; (assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 0)) -;; (assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 2)) +(assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1)) (assert_return (invoke "ref_test_struct" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_struct" (i32.const 1)) (i32.const 1)) @@ -142,8 +142,8 @@ (assert_return (invoke "ref_test_struct" (i32.const 3)) (i32.const 0)) (assert_return (invoke "ref_test_struct" (i32.const 4)) (i32.const 2)) (assert_return (invoke "ref_test_struct" (i32.const 5)) (i32.const 0)) -;; (assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0)) -;; (assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1)) (assert_return (invoke "ref_test_array" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_array" (i32.const 1)) (i32.const 1)) @@ -151,8 +151,8 @@ (assert_return (invoke "ref_test_array" (i32.const 3)) (i32.const 0)) (assert_return (invoke "ref_test_array" (i32.const 4)) (i32.const 0)) (assert_return (invoke "ref_test_array" (i32.const 5)) (i32.const 2)) -;; (assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0)) -;; (assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1)) (assert_return (invoke "ref_test_null_func" (i32.const 0)) (i32.const 2)) (assert_return (invoke "ref_test_null_func" (i32.const 1)) (i32.const 2)) @@ -164,17 +164,17 @@ (assert_return (invoke "ref_test_null_extern" (i32.const 0)) (i32.const 2)) (assert_return (invoke "ref_test_null_extern" (i32.const 1)) (i32.const 2)) -;; (assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0)) -;; (assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0)) -;; (assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0)) -;; (assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2)) +(assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2)) (assert_return (invoke "ref_test_extern" (i32.const 0)) (i32.const 1)) (assert_return (invoke "ref_test_extern" (i32.const 1)) (i32.const 1)) -;; (assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2)) -;; (assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2)) -;; (assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2)) -;; (assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1)) ;; Concrete Types |