summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-07-26 13:55:24 -0700
committerGitHub <noreply@github.com>2023-07-26 13:55:24 -0700
commitf2a8b86725c0d1ee3890dc80f8d3b4c094db38ac (patch)
tree8dc63693a898ec9b93c5e900f901f4f97b414fdb /test
parentf701d96b5412b6e4db856081acd7431faeafdb98 (diff)
downloadbinaryen-f2a8b86725c0d1ee3890dc80f8d3b4c094db38ac.tar.gz
binaryen-f2a8b86725c0d1ee3890dc80f8d3b4c094db38ac.tar.bz2
binaryen-f2a8b86725c0d1ee3890dc80f8d3b4c094db38ac.zip
TypeRefining: Add casts when we must (#5840)
See the example in the code and test for a situation that requires this for validation. To fix validation we add a cast. That should practically always be removed by later optimizations, and the fact it took the fuzzer this long to even find such a situation also adds confidence that this won't be adding overhead (and in this situation, the optimizer will definitely remove the cast).
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/type-refining.wast109
1 files changed, 109 insertions, 0 deletions
diff --git a/test/lit/passes/type-refining.wast b/test/lit/passes/type-refining.wast
index 9c2241edd..fb9f29568 100644
--- a/test/lit/passes/type-refining.wast
+++ b/test/lit/passes/type-refining.wast
@@ -1222,3 +1222,112 @@
)
)
)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $ref|$A|_externref_=>_none (func (param (ref $A) externref)))
+
+ ;; CHECK: (type $A (struct (field (mut (ref noextern)))))
+ (type $A (struct (field (mut externref))))
+
+ ;; CHECK: (type $externref_=>_anyref (func (param externref) (result anyref)))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (tag $tag (param))
+ (tag $tag)
+
+ ;; CHECK: (func $struct.new (type $externref_=>_anyref) (param $extern externref) (result anyref)
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (ref.cast noextern
+ ;; CHECK-NEXT: (try $try (result externref)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null noextern)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $tag
+ ;; CHECK-NEXT: (local.get $extern)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct.new (param $extern externref) (result anyref)
+ ;; A noextern is written into the struct field and then read. Note that the
+ ;; try's catch is never reached, since the body cannot throw, so the
+ ;; fallthrough of the try is the struct.get, which leads into a struct.new, so
+ ;; we have a copy of that field. For that reason TypeRefining thinks it can
+ ;; refine the type of the field from externref to noextern. However, the
+ ;; validation rule for try-catch prevents the try from being refined so,
+ ;; since the catch has to be taken into account, and it has a less refined
+ ;; type than the body.
+ ;;
+ ;; In such situations we rely on other optimizations to improve things, like
+ ;; getting rid of the catch in this case. In this pass we add a cast to get
+ ;; things to validate, which should be removable by other passes later on.
+ (struct.new $A
+ (try (result externref)
+ (do
+ (struct.get $A 0
+ (struct.new $A
+ (ref.as_non_null
+ (ref.null noextern)
+ )
+ )
+ )
+ )
+ (catch $tag
+ (local.get $extern)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $struct.set (type $ref|$A|_externref_=>_none) (param $ref (ref $A)) (param $extern externref)
+ ;; CHECK-NEXT: (struct.set $A 0
+ ;; CHECK-NEXT: (local.get $ref)
+ ;; CHECK-NEXT: (ref.cast noextern
+ ;; CHECK-NEXT: (try $try (result externref)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null noextern)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $tag
+ ;; CHECK-NEXT: (local.get $extern)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct.set (param $ref (ref $A)) (param $extern externref)
+ (struct.set $A 0
+ (local.get $ref)
+ (try (result externref)
+ (do
+ (struct.get $A 0
+ (struct.new $A
+ (ref.as_non_null
+ (ref.null noextern)
+ )
+ )
+ )
+ )
+ (catch $tag
+ (local.get $extern)
+ )
+ )
+ )
+ )
+)