summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/SimplifyLocals.cpp36
-rw-r--r--test/lit/passes/simplify-locals-gc.wast103
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: )