summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-06-05 13:45:19 -0700
committerGitHub <noreply@github.com>2023-06-05 13:45:19 -0700
commit90f471ded4f9cdacb630197d192da93f56d01ee9 (patch)
tree20ed542fc067be3ee048324dd07891370c5ccd82
parent876dbb0eff0544799a8ea2b8e8ae27c285520446 (diff)
downloadbinaryen-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.cpp31
-rw-r--r--test/lit/downgrade-reftypes.wast61
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)
+ )
+ )
+ )
+ )
+)