diff options
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 3 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc-tnh.wast | 160 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 62 |
3 files changed, 113 insertions, 112 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 3a5f56be9..e71519012 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2227,6 +2227,9 @@ struct OptimizeInstructions } assert(curr->op == RefAsNonNull); + if (trapOnNull(curr, curr->value)) { + return; + } skipNonNullCast(curr->value, curr); if (!curr->value->type.isNullable()) { replaceCurrent(curr->value); diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index 7902c2161..846ae4283 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -398,18 +398,10 @@ ;; TNH: (func $null.arm.null.effects (type $void) ;; TNH-NEXT: (block ;; (replaces something unreachable we can't emit) ;; TNH-NEXT: (drop - ;; TNH-NEXT: (block (result nullref) - ;; TNH-NEXT: (drop - ;; TNH-NEXT: (ref.as_non_null - ;; TNH-NEXT: (ref.null none) - ;; TNH-NEXT: ) - ;; TNH-NEXT: ) - ;; TNH-NEXT: (block (result nullref) - ;; TNH-NEXT: (drop - ;; TNH-NEXT: (call $get-i32) - ;; TNH-NEXT: ) - ;; TNH-NEXT: (ref.null none) - ;; TNH-NEXT: ) + ;; TNH-NEXT: (select + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: (ref.null none) + ;; TNH-NEXT: (call $get-i32) ;; TNH-NEXT: ) ;; TNH-NEXT: ) ;; TNH-NEXT: (drop @@ -417,17 +409,42 @@ ;; TNH-NEXT: ) ;; TNH-NEXT: (unreachable) ;; TNH-NEXT: ) + ;; TNH-NEXT: (block + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result nullref) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (call $get-i32) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (ref.null none) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) ;; TNH-NEXT: ) ;; NO_TNH: (func $null.arm.null.effects (type $void) - ;; NO_TNH-NEXT: (struct.set $struct 0 - ;; NO_TNH-NEXT: (select (result (ref null $struct)) - ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (select + ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: (call $get-i32) ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: (call $get-i32) ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (block + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result nullref) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (call $get-i32) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) (func $null.arm.null.effects @@ -443,6 +460,15 @@ ) (i32.const 1) ) + ;; The same, but without ref.as_non_null. + (struct.set $struct 0 + (select (result (ref null $struct)) + (ref.null none) + (ref.null none) + (call $get-i32) + ) + (i32.const 1) + ) ) ;; TNH: (func $set-get-cast (type $structref_=>_none) (param $ref structref) @@ -529,7 +555,7 @@ ) ) - ;; TNH: (func $cast-if-null (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; TNH: (func $cast-if-null (type $ref|none|_=>_ref|$struct|) (param $x (ref none)) (result (ref $struct)) ;; TNH-NEXT: (block ;; (replaces something unreachable we can't emit) ;; TNH-NEXT: (drop ;; TNH-NEXT: (block @@ -542,34 +568,30 @@ ;; TNH-NEXT: (unreachable) ;; TNH-NEXT: ) ;; TNH-NEXT: ) - ;; NO_TNH: (func $cast-if-null (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; NO_TNH: (func $cast-if-null (type $ref|none|_=>_ref|$struct|) (param $x (ref none)) (result (ref $struct)) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (if (result (ref none)) ;; NO_TNH-NEXT: (i32.const 1) ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $x) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) - (func $cast-if-null (result (ref $struct)) + (func $cast-if-null (param $x (ref none)) (result (ref $struct)) ;; We can remove the unreachable arm of the if here in TNH mode. While doing ;; so we must refinalize properly or else we'll hit an error in pass-debug ;; mode. (ref.cast $struct (if (result (ref none)) - (i32.const 1) - (unreachable) - (ref.as_non_null - (ref.null none) - ) + (i32.const 1) + (unreachable) + (local.get $x) ) ) ) - ;; TNH: (func $cast-if-null-flip (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; TNH: (func $cast-if-null-flip (type $ref|none|_=>_ref|$struct|) (param $x (ref none)) (result (ref $struct)) ;; TNH-NEXT: (block ;; (replaces something unreachable we can't emit) ;; TNH-NEXT: (drop ;; TNH-NEXT: (block @@ -582,27 +604,23 @@ ;; TNH-NEXT: (unreachable) ;; TNH-NEXT: ) ;; TNH-NEXT: ) - ;; NO_TNH: (func $cast-if-null-flip (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; NO_TNH: (func $cast-if-null-flip (type $ref|none|_=>_ref|$struct|) (param $x (ref none)) (result (ref $struct)) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (if (result (ref none)) ;; NO_TNH-NEXT: (i32.const 1) - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $x) ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) - (func $cast-if-null-flip (result (ref $struct)) + (func $cast-if-null-flip (param $x (ref none)) (result (ref $struct)) ;; As above but with arms flipped. (ref.cast $struct (if (result (ref none)) - (i32.const 1) - (ref.as_non_null - (ref.null none) - ) - (unreachable) + (i32.const 1) + (local.get $x) + (unreachable) ) ) ) @@ -772,32 +790,23 @@ ) ) - ;; TNH: (func $select.unreachable.child (type $none_=>_ref|$struct|) (result (ref $struct)) - ;; TNH-NEXT: (select - ;; TNH-NEXT: (ref.as_non_null - ;; TNH-NEXT: (ref.null none) + ;; TNH: (func $select.unreachable.child (type $ref|$struct|_=>_ref|$struct|) (param $x (ref $struct)) (result (ref $struct)) + ;; TNH-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (unreachable) ;; TNH-NEXT: ) ;; TNH-NEXT: (unreachable) - ;; TNH-NEXT: (i32.const 1) ;; TNH-NEXT: ) ;; TNH-NEXT: ) - ;; NO_TNH: (func $select.unreachable.child (type $none_=>_ref|$struct|) (result (ref $struct)) - ;; NO_TNH-NEXT: (select - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (block - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) + ;; NO_TNH: (func $select.unreachable.child (type $ref|$struct|_=>_ref|$struct|) (param $x (ref $struct)) (result (ref $struct)) + ;; NO_TNH-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) - (func $select.unreachable.child (result (ref $struct)) + (func $select.unreachable.child (param $x (ref $struct)) (result (ref $struct)) ;; We will turn the false arm of the select into an unreachable first, and ;; then process the select. While doing so we must not error, as the select ;; itself will still have a reachable type (a full refinalize only @@ -807,50 +816,31 @@ (ref.as_non_null (ref.null none) ) - (ref.cast $struct - (ref.as_non_null - (ref.null none) - ) - ) + (local.get $x) (i32.const 1) ) ) ) - ;; TNH: (func $select.unreachable.child.flip (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; TNH: (func $select.unreachable.child.flip (type $ref|$struct|_=>_ref|$struct|) (param $x (ref $struct)) (result (ref $struct)) ;; TNH-NEXT: (select + ;; TNH-NEXT: (local.get $x) ;; TNH-NEXT: (unreachable) - ;; TNH-NEXT: (ref.as_non_null - ;; TNH-NEXT: (ref.null none) - ;; TNH-NEXT: ) ;; TNH-NEXT: (i32.const 1) ;; TNH-NEXT: ) ;; TNH-NEXT: ) - ;; NO_TNH: (func $select.unreachable.child.flip (type $none_=>_ref|$struct|) (result (ref $struct)) + ;; NO_TNH: (func $select.unreachable.child.flip (type $ref|$struct|_=>_ref|$struct|) (param $x (ref $struct)) (result (ref $struct)) ;; NO_TNH-NEXT: (select - ;; NO_TNH-NEXT: (block - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $x) + ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: (i32.const 1) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) - (func $select.unreachable.child.flip (result (ref $struct)) + (func $select.unreachable.child.flip (param $x (ref $struct)) (result (ref $struct)) ;; Flip case of the above. (ref.cast $struct (select (result (ref $struct)) - (ref.cast $struct - (ref.as_non_null - (ref.null none) - ) - ) + (local.get $x) (ref.as_non_null (ref.null none) ) diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index c7238b353..adcf654d6 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -28,17 +28,17 @@ (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) - ;; CHECK: (type $void (func)) - ;; CHECK: (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) - ;; NOMNL: (type $void (func)) - ;; NOMNL: (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) (type $empty (struct)) + ;; CHECK: (type $void (func)) + ;; CHECK: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) + ;; NOMNL: (type $void (func)) + ;; NOMNL: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) @@ -908,32 +908,32 @@ ) ) - ;; CHECK: (func $flip-tee-of-as-non-null-non-nullable (type $ref|any|_=>_none) (param $x (ref any)) + ;; CHECK: (func $flip-tee-of-as-non-null-non-nullable (type $ref|any|_anyref_=>_none) (param $x (ref any)) (param $y anyref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; NOMNL: (func $flip-tee-of-as-non-null-non-nullable (type $ref|any|_=>_none) (param $x (ref any)) + ;; NOMNL: (func $flip-tee-of-as-non-null-non-nullable (type $ref|any|_anyref_=>_none) (param $x (ref any)) (param $y anyref) ;; NOMNL-NEXT: (drop ;; NOMNL-NEXT: (local.tee $x ;; NOMNL-NEXT: (ref.as_non_null - ;; NOMNL-NEXT: (ref.null none) + ;; NOMNL-NEXT: (local.get $y) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) - (func $flip-tee-of-as-non-null-non-nullable (param $x (ref any)) + (func $flip-tee-of-as-non-null-non-nullable (param $x (ref any)) (param $y (ref null any)) (drop (local.tee $x ;; this *cannnot* be moved through the tee outward, as the param is in ;; fact non-nullable, and we depend on the ref.as_non_null in order to ;; get a valid type to assign to it (ref.as_non_null - (ref.null any) + (local.get $y) ) ) ) @@ -1579,7 +1579,7 @@ ) ) - ;; CHECK: (func $incompatible-cast-of-null (type $void) + ;; CHECK: (func $incompatible-cast-of-null (type $ref?|$struct|_=>_none) (param $x (ref null $struct)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) @@ -1587,14 +1587,14 @@ ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; NOMNL: (func $incompatible-cast-of-null (type $void) + ;; NOMNL: (func $incompatible-cast-of-null (type $ref?|$struct|_=>_none) (param $x (ref null $struct)) ;; NOMNL-NEXT: (drop ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) @@ -1602,14 +1602,14 @@ ;; NOMNL-NEXT: (block ;; NOMNL-NEXT: (drop ;; NOMNL-NEXT: (ref.as_non_null - ;; NOMNL-NEXT: (ref.null none) + ;; NOMNL-NEXT: (local.get $x) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) - (func $incompatible-cast-of-null + (func $incompatible-cast-of-null (param $x (ref null $struct)) (drop (ref.cast $array ;; The child is null, so the cast will trap. Replace it with an @@ -1623,7 +1623,7 @@ ;; transformation. In practice this code will trap before getting to our ;; new unreachable. (ref.as_non_null - (ref.null $struct) + (local.get $x) ) ) ) @@ -3160,27 +3160,35 @@ ;; CHECK: (func $struct.set.null.fallthrough (type $void) ;; CHECK-NEXT: (local $temp (ref null $struct)) - ;; CHECK-NEXT: (struct.set $struct $i8 - ;; CHECK-NEXT: (local.tee $temp - ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $temp + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; NOMNL: (func $struct.set.null.fallthrough (type $void) ;; NOMNL-NEXT: (local $temp (ref null $struct)) - ;; NOMNL-NEXT: (struct.set $struct $i8 - ;; NOMNL-NEXT: (local.tee $temp - ;; NOMNL-NEXT: (ref.null none) + ;; NOMNL-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.tee $temp + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (i32.const 100) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (i32.const 100) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) (func $struct.set.null.fallthrough (local $temp (ref null $struct)) - ;; The value falling through the tee shows the local.set will trap. We can + ;; The value falling through the tee shows the struct.set will trap. We can ;; append an unreachable after it. While doing so we must not emit a drop of ;; the struct.set (which would be valid for a struct.get etc.). (struct.set $struct 0 |