diff options
-rw-r--r-- | src/passes/SimplifyLocals.cpp | 36 | ||||
-rw-r--r-- | test/lit/passes/simplify-locals-gc.wast | 103 |
2 files changed, 118 insertions, 21 deletions
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index c368740f1..7e3b33824 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -1091,24 +1091,28 @@ struct SimplifyLocals } assert(best != Index(-1)); // Due to ordering, the best index may be different from us but have - // the same # of locals - make sure we actually improve. + // the same # of locals - make sure we actually improve, either adding + // more gets, or a more refined type (and never change to a less + // refined type). auto bestType = func->getLocalType(best); auto oldType = func->getLocalType(curr->index); - if (best != curr->index && (getNumGetsIgnoringCurr(best) > - getNumGetsIgnoringCurr(curr->index) || - bestType != oldType)) { - // Update the get counts. - (*numLocalGets)[best]++; - assert((*numLocalGets)[curr->index] >= 1); - (*numLocalGets)[curr->index]--; - // Make the change. - curr->index = best; - anotherCycle = true; - if (bestType != oldType) { - curr->type = func->getLocalType(best); - // We are switching to a more refined type, which might require - // changes in the user of the local.get. - refinalize = true; + if (best != curr->index && Type::isSubType(bestType, oldType)) { + auto hasMoreGets = getNumGetsIgnoringCurr(best) > + getNumGetsIgnoringCurr(curr->index); + if (hasMoreGets || bestType != oldType) { + // Update the get counts. + (*numLocalGets)[best]++; + assert((*numLocalGets)[curr->index] >= 1); + (*numLocalGets)[curr->index]--; + // Make the change. + curr->index = best; + anotherCycle = true; + if (bestType != oldType) { + curr->type = func->getLocalType(best); + // We are switching to a more refined type, which might require + // changes in the user of the local.get. + refinalize = true; + } } } } diff --git a/test/lit/passes/simplify-locals-gc.wast b/test/lit/passes/simplify-locals-gc.wast index 951116a08..80bbe6d90 100644 --- a/test/lit/passes/simplify-locals-gc.wast +++ b/test/lit/passes/simplify-locals-gc.wast @@ -9,19 +9,22 @@ ;; NOMNL: (type $struct (struct_subtype (field (mut i32)) data)) (type $struct (struct (field (mut i32)))) - ;; CHECK: (type $struct-immutable (struct (field i32))) - ;; NOMNL: (type $struct-immutable (struct_subtype (field i32) data)) - (type $struct-immutable (struct (field i32))) - ;; CHECK: (type $B (struct (field (ref data)))) ;; CHECK: (type $A (struct (field dataref))) + + ;; CHECK: (type $struct-immutable (struct (field i32))) ;; NOMNL: (type $A (struct_subtype (field dataref) data)) + + ;; NOMNL: (type $B (struct_subtype (field (ref data)) $A)) + + ;; NOMNL: (type $struct-immutable (struct_subtype (field i32) data)) + (type $struct-immutable (struct (field i32))) + (type $A (struct_subtype (field (ref null data)) data)) ;; $B is a subtype of $A, and its field has a more refined type (it is non- ;; nullable). - ;; NOMNL: (type $B (struct_subtype (field (ref data)) $A)) (type $B (struct_subtype (field (ref data)) $A)) ;; Writes to heap objects cannot be reordered with reads. @@ -590,6 +593,96 @@ ) ) + ;; CHECK: (func $ignore-unrefined (param $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.tee $B + ;; CHECK-NEXT: (ref.cast_static $B + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $ignore-unrefined (type $ref|$A|_=>_none) (param $A (ref $A)) + ;; NOMNL-NEXT: (local $B (ref null $B)) + ;; NOMNL-NEXT: (nop) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (struct.get $A 0 + ;; NOMNL-NEXT: (local.get $A) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (struct.get $B 0 + ;; NOMNL-NEXT: (local.tee $B + ;; NOMNL-NEXT: (ref.cast_static $B + ;; NOMNL-NEXT: (local.get $A) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (struct.get $A 0 + ;; NOMNL-NEXT: (local.get $A) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (struct.get $B 0 + ;; NOMNL-NEXT: (local.get $B) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $ignore-unrefined (param $A (ref $A)) + ;; $A is a supertype, but non-nullable; $B is a subtype, but nullable. We + ;; should not switch any of the gets from $B to $A: that would improve + ;; nullability but not the heap type. + (local $B (ref null $B)) + (local.set $B + (ref.cast_static $B + (local.get $A) + ) + ) + ;; Read from both locals a few times. We should keep reading from the same + ;; locals as before. + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ) + ;; CHECK: (func $use-nn-any (param $nn-any (ref any)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) |