diff options
Diffstat (limited to 'test/lit/passes/local-subtyping.wast')
-rw-r--r-- | test/lit/passes/local-subtyping.wast | 161 |
1 files changed, 145 insertions, 16 deletions
diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast index 37990ad24..a2a5780da 100644 --- a/test/lit/passes/local-subtyping.wast +++ b/test/lit/passes/local-subtyping.wast @@ -1,7 +1,12 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: wasm-opt %s --local-subtyping -all -S -o - \ +;; RUN: wasm-opt %s --remove-unused-names --local-subtyping -all -S -o - \ ;; RUN: | filecheck %s +;; --remove-unused-names is run to avoid adding names to blocks. Block names +;; can prevent non-nullable local validation (we emit named blocks in the binary +;; format, if we need them, but never emit unnamed ones), which affects some +;; testcases. + (module ;; CHECK: (type ${} (struct )) (type ${} (struct_subtype data)) @@ -65,7 +70,7 @@ ;; more specific type. A similar thing with a parameter, however, is not a ;; thing we can optimize. Also, ignore a local with zero assignments. ;; CHECK: (func $simple-local-but-not-param (param $x funcref) - ;; CHECK-NEXT: (local $y (ref null $none_=>_i32)) + ;; CHECK-NEXT: (local $y (ref $none_=>_i32)) ;; CHECK-NEXT: (local $unused funcref) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (ref.func $i32) @@ -87,9 +92,9 @@ ;; CHECK: (func $locals-with-multiple-assignments (param $data dataref) ;; CHECK-NEXT: (local $x eqref) - ;; CHECK-NEXT: (local $y i31ref) + ;; CHECK-NEXT: (local $y (ref i31)) ;; CHECK-NEXT: (local $z dataref) - ;; CHECK-NEXT: (local $w funcref) + ;; CHECK-NEXT: (local $w (ref func)) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i31.new ;; CHECK-NEXT: (i32.const 0) @@ -146,8 +151,9 @@ (local.set $z (local.get $data) ) - ;; w is assigned two different types *without* a new LUB possible, as it - ;; already had the optimal LUB + ;; w is assigned two different types *without* a new LUB heap type possible, + ;; as it already had the optimal LUB heap type (but it can become non- + ;; nullable). (local.set $w (ref.func $i32) ) @@ -159,9 +165,9 @@ ;; In some cases multiple iterations are necessary, as one inferred new type ;; applies to a get which then allows another inference. ;; CHECK: (func $multiple-iterations - ;; CHECK-NEXT: (local $x (ref null $none_=>_i32)) - ;; CHECK-NEXT: (local $y (ref null $none_=>_i32)) - ;; CHECK-NEXT: (local $z (ref null $none_=>_i32)) + ;; CHECK-NEXT: (local $x (ref $none_=>_i32)) + ;; CHECK-NEXT: (local $y (ref $none_=>_i32)) + ;; CHECK-NEXT: (local $z (ref $none_=>_i32)) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (ref.func $i32) ;; CHECK-NEXT: ) @@ -189,9 +195,9 @@ ;; Sometimes a refinalize is necessary in between the iterations. ;; CHECK: (func $multiple-iterations-refinalize (param $i i32) - ;; CHECK-NEXT: (local $x (ref null $none_=>_i32)) - ;; CHECK-NEXT: (local $y (ref null $none_=>_i64)) - ;; CHECK-NEXT: (local $z funcref) + ;; CHECK-NEXT: (local $x (ref $none_=>_i32)) + ;; CHECK-NEXT: (local $y (ref $none_=>_i64)) + ;; CHECK-NEXT: (local $z (ref func)) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (ref.func $i32) ;; CHECK-NEXT: ) @@ -199,7 +205,7 @@ ;; CHECK-NEXT: (ref.func $i64) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $z - ;; CHECK-NEXT: (select (result funcref) + ;; CHECK-NEXT: (select (result (ref func)) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: (local.get $i) @@ -273,13 +279,13 @@ ) ;; CHECK: (func $unreachables (result funcref) - ;; CHECK-NEXT: (local $temp (ref null $none_=>_funcref)) + ;; CHECK-NEXT: (local $temp (ref $none_=>_funcref)) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (ref.func $unreachables) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref null $none_=>_funcref)) + ;; CHECK-NEXT: (block (result (ref $none_=>_funcref)) ;; CHECK-NEXT: (local.tee $temp ;; CHECK-NEXT: (ref.func $unreachables) ;; CHECK-NEXT: ) @@ -308,7 +314,7 @@ ) ;; CHECK: (func $incompatible-sets (result i32) - ;; CHECK-NEXT: (local $temp (ref null $none_=>_i32)) + ;; CHECK-NEXT: (local $temp (ref $none_=>_i32)) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (ref.func $incompatible-sets) ;; CHECK-NEXT: ) @@ -398,4 +404,127 @@ ;; as there is no point to making something less specific in type. (local.set $x (ref.null ${i32})) ) + + ;; CHECK: (func $become-non-nullable + ;; CHECK-NEXT: (local $x (ref $none_=>_none)) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $become-non-nullable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $become-non-nullable + (local $x (ref null func)) + (local.set $x + (ref.func $become-non-nullable) + ) + (drop + (local.get $x) + ) + ) + + ;; CHECK: (func $already-non-nullable + ;; CHECK-NEXT: (local $x (ref $none_=>_none)) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $already-non-nullable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $already-non-nullable + (local $x (ref func)) + (local.set $x + (ref.func $already-non-nullable) + ) + (drop + (local.get $x) + ) + ) + + ;; CHECK: (func $cannot-become-non-nullable + ;; CHECK-NEXT: (local $x (ref null $none_=>_none)) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $become-non-nullable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cannot-become-non-nullable + (local $x (ref null func)) + ;; The set is in a nested scope, so we should not make the local non- + ;; nullable, as it would not validate. (We can refine the heap type, + ;; though.) + (if + (i32.const 1) + (local.set $x + (ref.func $become-non-nullable) + ) + ) + (drop + (local.get $x) + ) + ) + + ;; CHECK: (func $cannot-become-non-nullable-block + ;; CHECK-NEXT: (local $x (ref null $none_=>_none)) + ;; CHECK-NEXT: (block $name + ;; CHECK-NEXT: (br_if $name + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $become-non-nullable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cannot-become-non-nullable-block + (local $x (ref null func)) + ;; A named block prevents us from optimizing here, the same as above. + (block $name + ;; Add a br_if to avoid the name being removed. + (br_if $name + (i32.const 1) + ) + (local.set $x + (ref.func $become-non-nullable) + ) + ) + (drop + (local.get $x) + ) + ) + + ;; CHECK: (func $become-non-nullable-block-unnamed + ;; CHECK-NEXT: (local $x (ref $none_=>_none)) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $become-non-nullable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $become-non-nullable-block-unnamed + (local $x (ref null func)) + ;; An named block does *not* prevent us from optimizing here. Unlike above, + ;; an unnamed block is never emitted in the binary format, so it does not + ;; prevent validation. + (block + (local.set $x + (ref.func $become-non-nullable) + ) + ) + (drop + (local.get $x) + ) + ) ) |