Skip to content

Commit ce2abc2

Browse files
authored
Account for exactness when finding the most refined fallthrough (#7544)
Treat exactness as part of the heap type and prefer to refine it over refining nullability. This is a somewhat arbitrary choice, and as shown by the tests, it is possible to construct a situation in which preferring to refine nullability would be preferable.
1 parent 3bcf77b commit ce2abc2

File tree

3 files changed

+79
-8
lines changed

3 files changed

+79
-8
lines changed

scripts/test/fuzzing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
'exact-references-lowering.wast',
115115
'exact-casts.wast',
116116
'exact-casts-trivial.wast',
117+
'optimize-instructions-exact.wast',
117118
'optimize-instructions-all-casts-exact.wast',
118119
'local-subtyping-exact.wast',
119120
'remove-unused-types-exact.wast',

src/ir/properties.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ inline Expression** getMostRefinedFallthrough(Expression** currp,
400400
return currp;
401401
}
402402
auto bestType = curr->type.getHeapType();
403+
auto bestExactness = curr->type.getExactness();
403404
auto bestNullability = curr->type.getNullability();
404405
auto** bestp = currp;
405406
while (1) {
@@ -412,20 +413,23 @@ inline Expression** getMostRefinedFallthrough(Expression** currp,
412413
}
413414
assert(next->type.isRef());
414415
auto nextType = next->type.getHeapType();
416+
auto nextExactness = next->type.getExactness();
415417
auto nextNullability = next->type.getNullability();
416-
if (nextType == bestType) {
418+
if (nextType == bestType && nextExactness == bestExactness) {
417419
// Heap types match: refine nullability if possible.
418420
if (bestNullability == Nullable && nextNullability == NonNullable) {
419421
bestp = nextp;
420422
bestNullability = NonNullable;
421423
}
422-
} else {
423-
// Refine heap type if possible, resetting nullability.
424-
if (HeapType::isSubType(nextType, bestType)) {
425-
bestp = nextp;
426-
bestNullability = nextNullability;
427-
bestType = nextType;
428-
}
424+
} else if ((nextType != bestType &&
425+
HeapType::isSubType(nextType, bestType)) ||
426+
(nextType == bestType && nextExactness == Exact &&
427+
bestExactness == Inexact)) {
428+
// Refine heap, resetting nullability.
429+
bestp = nextp;
430+
bestType = nextType;
431+
bestExactness = nextExactness;
432+
bestNullability = nextNullability;
429433
}
430434
currp = nextp;
431435
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; RUN: wasm-opt %s -all --disable-custom-descriptors --optimize-instructions -S -o - | filecheck %s
4+
5+
(module
6+
;; CHECK: (type $foo (sub (struct)))
7+
(type $foo (sub (struct)))
8+
9+
;; CHECK: (func $ref-cast-exact-fallthrough (type $1) (param $exact (ref (exact $foo))) (result (ref $foo))
10+
;; CHECK-NEXT: (local $inexact (ref $foo))
11+
;; CHECK-NEXT: (local $2 (ref (exact $foo)))
12+
;; CHECK-NEXT: (drop
13+
;; CHECK-NEXT: (local.tee $inexact
14+
;; CHECK-NEXT: (local.tee $2
15+
;; CHECK-NEXT: (local.get $exact)
16+
;; CHECK-NEXT: )
17+
;; CHECK-NEXT: )
18+
;; CHECK-NEXT: )
19+
;; CHECK-NEXT: (local.get $2)
20+
;; CHECK-NEXT: )
21+
(func $ref-cast-exact-fallthrough (param $exact (ref (exact $foo))) (result (ref $foo))
22+
(local $inexact (ref $foo))
23+
;; We should find that the local.get is the most precise fallthrough value and
24+
;; hoist it to eliminate the cast.
25+
(ref.cast (ref $foo)
26+
(local.tee $inexact
27+
(local.get $exact)
28+
)
29+
)
30+
)
31+
32+
;; CHECK: (func $prefer-exactness (type $2) (param $exact-null (ref null (exact $foo))) (result (ref $foo))
33+
;; CHECK-NEXT: (local $inexact-nn (ref $foo))
34+
;; CHECK-NEXT: (local $inexact-null (ref null $foo))
35+
;; CHECK-NEXT: (local $3 (ref null (exact $foo)))
36+
;; CHECK-NEXT: (drop
37+
;; CHECK-NEXT: (local.tee $inexact-nn
38+
;; CHECK-NEXT: (ref.as_non_null
39+
;; CHECK-NEXT: (local.tee $inexact-null
40+
;; CHECK-NEXT: (local.tee $3
41+
;; CHECK-NEXT: (local.get $exact-null)
42+
;; CHECK-NEXT: )
43+
;; CHECK-NEXT: )
44+
;; CHECK-NEXT: )
45+
;; CHECK-NEXT: )
46+
;; CHECK-NEXT: )
47+
;; CHECK-NEXT: (ref.as_non_null
48+
;; CHECK-NEXT: (local.get $3)
49+
;; CHECK-NEXT: )
50+
;; CHECK-NEXT: )
51+
(func $prefer-exactness (param $exact-null (ref null (exact $foo))) (result (ref $foo))
52+
(local $inexact-nn (ref $foo))
53+
(local $inexact-null (ref null $foo))
54+
;; We should prefer to hoist the exact expression and introduce another null
55+
;; check rather than hoisting the non-null, inexact expression.
56+
(ref.cast (ref $foo)
57+
(local.tee $inexact-nn
58+
(ref.as_non_null
59+
(local.tee $inexact-null
60+
(local.get $exact-null)
61+
)
62+
)
63+
)
64+
)
65+
)
66+
)

0 commit comments

Comments
 (0)