diff options
author | Thomas Lively <tlively@google.com> | 2023-06-05 13:45:19 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-05 13:45:19 -0700 |
commit | 90f471ded4f9cdacb630197d192da93f56d01ee9 (patch) | |
tree | 20ed542fc067be3ee048324dd07891370c5ccd82 | |
parent | 876dbb0eff0544799a8ea2b8e8ae27c285520446 (diff) | |
download | binaryen-90f471ded4f9cdacb630197d192da93f56d01ee9.tar.gz binaryen-90f471ded4f9cdacb630197d192da93f56d01ee9.tar.bz2 binaryen-90f471ded4f9cdacb630197d192da93f56d01ee9.zip |
Fix emitting of function reference types without GC (#5737)
We previously had logic to emit GC types used in the IR as their corresponding
top types when GC was not enabled (so e.g. nullfuncref would be emitted as
funcref), but the logic was not robust enough and non-null function references
were not properly emitted as funcref.
Refactor the relevant code to be more robust and future-proof, and add a test
demonstrating that the lowering works as intended.
-rw-r--r-- | src/wasm/wasm-binary.cpp | 31 | ||||
-rw-r--r-- | test/lit/downgrade-reftypes.wast | 61 |
2 files changed, 78 insertions, 14 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 91309c906..1d0e99415 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1393,6 +1393,18 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) { void WasmBinaryWriter::writeType(Type type) { if (type.isRef()) { + // The only reference types allowed without GC are funcref and externref. We + // internally use more refined versions of those types, but we cannot emit + // those more refined types. + if (!wasm->features.hasGC()) { + if (Type::isSubType(type, Type(HeapType::func, Nullable))) { + o << S32LEB(BinaryConsts::EncodedType::funcref); + return; + } + assert(Type::isSubType(type, Type(HeapType::ext, Nullable))); + o << S32LEB(BinaryConsts::EncodedType::externref); + return; + } auto heapType = type.getHeapType(); if (heapType.isBasic() && type.isNullable()) { switch (heapType.getBasic()) { @@ -1433,20 +1445,10 @@ void WasmBinaryWriter::writeType(Type type) { o << S32LEB(BinaryConsts::EncodedType::nullref); return; case HeapType::noext: - // See comment on writeHeapType. - if (!wasm->features.hasGC()) { - o << S32LEB(BinaryConsts::EncodedType::externref); - } else { - o << S32LEB(BinaryConsts::EncodedType::nullexternref); - } + o << S32LEB(BinaryConsts::EncodedType::nullexternref); return; case HeapType::nofunc: - // See comment on writeHeapType. - if (!wasm->features.hasGC()) { - o << S32LEB(BinaryConsts::EncodedType::funcref); - } else { - o << S32LEB(BinaryConsts::EncodedType::nullfuncref); - } + o << S32LEB(BinaryConsts::EncodedType::nullfuncref); return; } } @@ -1491,9 +1493,10 @@ void WasmBinaryWriter::writeHeapType(HeapType type) { // only actually valid with GC enabled. When GC is not enabled, emit the // corresponding valid top types instead. if (!wasm->features.hasGC()) { - if (type == HeapType::nofunc || type.isSignature()) { + if (HeapType::isSubType(type, HeapType::func)) { type = HeapType::func; - } else if (type == HeapType::noext) { + } else { + assert(HeapType::isSubType(type, HeapType::ext)); type = HeapType::ext; } } diff --git a/test/lit/downgrade-reftypes.wast b/test/lit/downgrade-reftypes.wast new file mode 100644 index 000000000..94343933f --- /dev/null +++ b/test/lit/downgrade-reftypes.wast @@ -0,0 +1,61 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Write to a binary, lowering away refined GC types. +;; RUN: wasm-as %s -all --disable-gc -g -o %s.wasm + +;; Read it back and verify that the types were lowered away. +;; RUN: wasm-dis %s.wasm -all -o - | filecheck %s + +(module + + ;; CHECK: (type $f (func)) + (type $f (func)) + + ;; CHECK: (func $foo (type $f) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $label$1 (result funcref) + ;; CHECK-NEXT: (br $label$1 + ;; CHECK-NEXT: (ref.func $foo) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $label$2 (result funcref) + ;; CHECK-NEXT: (br $label$2 + ;; CHECK-NEXT: (ref.null nofunc) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $label$3 (result externref) + ;; CHECK-NEXT: (br $label$3 + ;; CHECK-NEXT: (ref.null noextern) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo (type $f) + (drop + (block $l1 (result (ref $f)) + (br $l1 + (ref.func $foo) + ) + ) + ) + (drop + (block $l2 (result nullfuncref) + ;; Branch to ensure the blocks remain in the output. + (br $l2 + (ref.null nofunc) + ) + ) + ) + (drop + (block $l3 (result nullexternref) + (br $l3 + (ref.null noextern) + ) + ) + ) + ) +) |