summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wasm.cpp16
-rw-r--r--test/heap-types.wast.from-wast2
-rw-r--r--test/heap-types.wast.fromBinary2
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo2
-rw-r--r--test/lit/binary/legacy-static-casts.test4
-rw-r--r--test/lit/cast-to-basic.wast8
-rw-r--r--test/lit/heap-types.wast5
-rw-r--r--test/lit/legacy-static-casts.wast4
-rw-r--r--test/lit/passes/gufa-refs.wast10
-rw-r--r--test/lit/passes/gufa-vs-cfp.wast12
-rw-r--r--test/lit/passes/optimize-casts.wast2
-rw-r--r--test/lit/passes/optimize-instructions-gc-iit.wast4
-rw-r--r--test/lit/passes/optimize-instructions-gc-tnh.wast4
-rw-r--r--test/lit/passes/remove-unused-brs-gc.wast2
-rw-r--r--test/lit/passes/type-refining.wast42
15 files changed, 89 insertions, 30 deletions
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index c3f262998..552ec7e57 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -947,10 +947,24 @@ void RefCast::finalize() {
type = Type::unreachable;
return;
}
- // Do not unnecessarily lose non-nullability information.
+
+ // Do not unnecessarily lose non-nullability info. We could leave this for
+ // optimizations, but doing it here as part of finalization/refinalization
+ // ensures that type information flows through in an optimal manner and can be
+ // used as soon as possible.
if (ref->type.isNonNullable() && type.isNullable()) {
type = Type(type.getHeapType(), NonNullable);
}
+
+ // Do not unnecessarily lose heap type info, as above for nullability. Note
+ // that we must check if the ref has a heap type, as we reach this before
+ // validation, which will error if the ref does not in fact have a heap type.
+ // (This is a downside of propagating type information here, as opposed to
+ // leaving it for an optimization pass.)
+ if (ref->type.isRef() &&
+ HeapType::isSubType(ref->type.getHeapType(), type.getHeapType())) {
+ type = Type(ref->type.getHeapType(), type.getNullability());
+ }
}
void BrOn::finalize() {
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index 7d7fd62de..975d89b49 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -371,7 +371,7 @@
)
)
(drop
- (ref.cast null $struct.B
+ (ref.cast null none
(ref.null none)
)
)
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index e69cf99b1..99637f92b 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -324,7 +324,7 @@
)
)
(drop
- (ref.cast null $struct.B
+ (ref.cast null none
(ref.null none)
)
)
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index 6596733ff..3107269be 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -324,7 +324,7 @@
)
)
(drop
- (ref.cast null ${i8_mut:i16_ref|{i32_f32_f64}|_mut:ref|{i32_f32_f64}|}
+ (ref.cast null none
(ref.null none)
)
)
diff --git a/test/lit/binary/legacy-static-casts.test b/test/lit/binary/legacy-static-casts.test
index ad2209594..49ed204a7 100644
--- a/test/lit/binary/legacy-static-casts.test
+++ b/test/lit/binary/legacy-static-casts.test
@@ -15,12 +15,12 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
-;; CHECK-NEXT: (ref.cast null ${}
+;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
-;; CHECK-NEXT: (ref.cast_nop ${}
+;; CHECK-NEXT: (ref.cast_nop none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/cast-to-basic.wast b/test/lit/cast-to-basic.wast
index 23adb3968..aa58e1f8e 100644
--- a/test/lit/cast-to-basic.wast
+++ b/test/lit/cast-to-basic.wast
@@ -16,17 +16,17 @@
)
)
- ;; CHECK: (func $cast (type $none_=>_none)
+ ;; CHECK: (func $cast (type $structref_=>_none) (param $x structref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast null struct
- ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $cast
+ (func $cast (param $x (ref null struct))
(drop
(ref.cast null struct
- (ref.null none)
+ (local.get $x)
)
)
)
diff --git a/test/lit/heap-types.wast b/test/lit/heap-types.wast
index 0c3df85e4..63e12c67e 100644
--- a/test/lit/heap-types.wast
+++ b/test/lit/heap-types.wast
@@ -26,17 +26,18 @@
)
(module
- ;; CHECK: (type $struct.A (struct (field i32)))
(type $struct.A (struct i32))
(type $struct.B (struct i32))
;; CHECK: (func $test (type $none_=>_none)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast null $struct.A
+ ;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
+ ;; Note that this will not round-trip precisely because Binaryen IR will
+ ;; apply the more refined type to the cast automatically (in finalize).
(drop
(ref.cast null $struct.B (ref.null $struct.A))
)
diff --git a/test/lit/legacy-static-casts.wast b/test/lit/legacy-static-casts.wast
index 0cbd30a2d..dfef240d3 100644
--- a/test/lit/legacy-static-casts.wast
+++ b/test/lit/legacy-static-casts.wast
@@ -17,12 +17,12 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast null $struct
+ ;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast_nop $struct
+ ;; CHECK-NEXT: (ref.cast_nop none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast
index fe210e149..89b249f2a 100644
--- a/test/lit/passes/gufa-refs.wast
+++ b/test/lit/passes/gufa-refs.wast
@@ -1101,7 +1101,7 @@
;; CHECK-NEXT: (br $block1
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast null $child
+ ;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -2452,11 +2452,11 @@
(type $substruct (struct_subtype (field i32) (field i32) $struct))
;; CHECK: (type $none_=>_none (func))
- ;; CHECK: (type $i32_=>_none (func (param i32)))
-
;; CHECK: (type $subsubstruct (struct_subtype (field i32) (field i32) (field i32) $substruct))
(type $subsubstruct (struct_subtype (field i32) (field i32) (field i32) $substruct))
+ ;; CHECK: (type $i32_=>_none (func (param i32)))
+
;; CHECK: (type $other (struct ))
(type $other (struct_subtype data))
@@ -2494,7 +2494,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast $substruct
+ ;; CHECK-NEXT: (ref.cast $subsubstruct
;; CHECK-NEXT: (struct.new $subsubstruct
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: (i32.const 4)
@@ -2539,7 +2539,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast null $struct
+ ;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $import)
diff --git a/test/lit/passes/gufa-vs-cfp.wast b/test/lit/passes/gufa-vs-cfp.wast
index 73253aaa7..4a2a6c863 100644
--- a/test/lit/passes/gufa-vs-cfp.wast
+++ b/test/lit/passes/gufa-vs-cfp.wast
@@ -2039,17 +2039,19 @@
)
)
;; CHECK: (func $set (type $none_=>_none)
- ;; CHECK-NEXT: (struct.set $A 0
- ;; CHECK-NEXT: (ref.cast $A
+ ;; CHECK-NEXT: (struct.set $C 0
+ ;; CHECK-NEXT: (ref.cast $C
;; CHECK-NEXT: (call $create-C)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set
- ;; Set of $A, but the reference is actually a $C. We add a cast to make sure
- ;; the type is $A, which should not confuse us: this set does alias the data
- ;; in $C, which means we cannot optimize in the function $get below.
+ ;; Set of $A, but the reference is actually a $C. We add a cast to try to
+ ;; make sure the type is $A, which should not confuse us: this set does
+ ;; alias the data in $C, which means we cannot optimize in the function $get
+ ;; below. (Note that finalize will turn the cast into a cast of $C
+ ;; automatically; that is not part of GUFA.)
(struct.set $A 0
(ref.cast $A
(call $create-C)
diff --git a/test/lit/passes/optimize-casts.wast b/test/lit/passes/optimize-casts.wast
index 527c2d157..27e38f7f5 100644
--- a/test/lit/passes/optimize-casts.wast
+++ b/test/lit/passes/optimize-casts.wast
@@ -218,7 +218,7 @@
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast $A
+ ;; CHECK-NEXT: (ref.cast $B
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/passes/optimize-instructions-gc-iit.wast b/test/lit/passes/optimize-instructions-gc-iit.wast
index 0e315dd5d..23161f1be 100644
--- a/test/lit/passes/optimize-instructions-gc-iit.wast
+++ b/test/lit/passes/optimize-instructions-gc-iit.wast
@@ -39,7 +39,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block (result (ref $other))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $child)
;; CHECK-NEXT: )
@@ -60,7 +60,7 @@
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
- ;; TNH-NEXT: (block
+ ;; TNH-NEXT: (block (result (ref $other))
;; TNH-NEXT: (drop
;; TNH-NEXT: (local.get $child)
;; TNH-NEXT: )
diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast
index 34071f2af..dd5407f31 100644
--- a/test/lit/passes/optimize-instructions-gc-tnh.wast
+++ b/test/lit/passes/optimize-instructions-gc-tnh.wast
@@ -904,7 +904,7 @@
;; TNH: (func $if.null.child.but.no.flow (type $void)
;; TNH-NEXT: (drop
- ;; TNH-NEXT: (block (result (ref func))
+ ;; TNH-NEXT: (block (result (ref nofunc))
;; TNH-NEXT: (drop
;; TNH-NEXT: (if (result (ref nofunc))
;; TNH-NEXT: (i32.const 1)
@@ -918,7 +918,7 @@
;; TNH-NEXT: )
;; NO_TNH: (func $if.null.child.but.no.flow (type $void)
;; NO_TNH-NEXT: (drop
- ;; NO_TNH-NEXT: (block (result (ref func))
+ ;; NO_TNH-NEXT: (block (result (ref nofunc))
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (if (result (ref nofunc))
;; NO_TNH-NEXT: (i32.const 1)
diff --git a/test/lit/passes/remove-unused-brs-gc.wast b/test/lit/passes/remove-unused-brs-gc.wast
index 02dc55cbd..4fe7bd938 100644
--- a/test/lit/passes/remove-unused-brs-gc.wast
+++ b/test/lit/passes/remove-unused-brs-gc.wast
@@ -338,7 +338,7 @@
;; CHECK-NEXT: (if (result anyref)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (ref.null none)
- ;; CHECK-NEXT: (ref.cast null $struct
+ ;; CHECK-NEXT: (ref.cast null none
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/passes/type-refining.wast b/test/lit/passes/type-refining.wast
index ca9b98590..8258a3a54 100644
--- a/test/lit/passes/type-refining.wast
+++ b/test/lit/passes/type-refining.wast
@@ -1125,3 +1125,45 @@
)
)
)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (ref $B))))
+ (type $A (struct (field (ref struct))))
+ ;; CHECK: (type $B (struct ))
+ (type $B (struct))
+ )
+
+ ;; CHECK: (type $none_=>_ref|$A| (func (result (ref $A))))
+
+ ;; CHECK: (func $0 (type $none_=>_ref|$A|) (result (ref $A))
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (ref.cast $B
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (struct.new_default $B)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $0 (result (ref $A))
+ ;; The cast in the middle here will be skipped in the analysis, as we have
+ ;; a copy: a read from $A.0 to a write, with only a cast in the middle
+ ;; (which does not alter the value). While that is valid to do, we need to
+ ;; properly propagate the new output type of the struct.get (which goes from
+ ;; (ref struct) to (ref $B) to the cast, so that the cast has that type as
+ ;; well, as otherwise the struct.new will not validate - we can't write a
+ ;; (ref struct) to a field of (ref $B).
+ (struct.new $A
+ (ref.cast struct
+ (struct.get $A 0
+ (struct.new $A
+ (struct.new_default $B)
+ )
+ )
+ )
+ )
+ )
+)