Skip to content

Commit c399a38

Browse files
committed
Merge branch 'main' into typo_fix
2 parents 1c3caf3 + 3d7eee7 commit c399a38

10 files changed

Lines changed: 368 additions & 127 deletions

src/ir/linear-execution.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> {
251251
if (!self->connectAdjacentBlocks) {
252252
self->pushTask(SubType::doNoteNonLinear, currp);
253253
}
254+
self->maybePushTask(SubType::scan, &curr->cast<BrOn>()->desc);
254255
self->pushTask(SubType::scan, &curr->cast<BrOn>()->ref);
255256
break;
256257
}

src/passes/ConstraintAnalysis.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -568,14 +568,6 @@ struct ConstraintAnalysis
568568
return;
569569
}
570570

571-
// See above on binary action counting limits.
572-
if (auto* binary = set->value->dynCast<Binary>()) {
573-
if (binaryActionCounts[binary]++ >= MaxBinaryActions) {
574-
constraints.setProvesNothing(set->index);
575-
return;
576-
}
577-
}
578-
579571
// Look at the fallthrough. It is valid to do so, because our constraints
580572
// only track two things, constants and locals. For a constant, it does
581573
// not change while falling through. For a local, the only way for the
@@ -600,8 +592,47 @@ struct ConstraintAnalysis
600592
// opportunity to write any other value while falling through. (And, any
601593
// local.tee appearing here would have been reached earlier in the
602594
// traversal, and handled.)
603-
auto* value =
604-
Properties::getFallthrough(set->value, getPassOptions(), *getModule());
595+
auto* value = set->value;
596+
while (1) {
597+
if (value->is<LocalSet>()) {
598+
// We stop at the first tee: we don't need to look any further, and
599+
// will just apply that local's values to ourselves, saving repeated
600+
// work.
601+
break;
602+
}
603+
auto* next = Properties::getImmediateFallthrough(
604+
value, getPassOptions(), *getModule());
605+
if (value == next) {
606+
break;
607+
} else {
608+
value = next;
609+
}
610+
}
611+
612+
// Now that we know the value, check binary action counting limits (see
613+
// above).
614+
if (auto* binary = value->dynCast<Binary>()) {
615+
// The code below will stop calculating this binary once we pass
616+
// MaxBinaryActions operations on it. That is enough to prevent
617+
// unbounded work on this binary, however, we may end up reaching this
618+
// basic block an even larger number of times for other reasons, i.e.,
619+
// just because of a very complex CFG. That should be very rare, but can
620+
// happen. In debug builds we check we do not exceed a very high limit
621+
// there, intending to throw an assert rather than just hang in the case
622+
// of a bug (as assert is easier to diagnose, even if it happens after a
623+
// long delay).
624+
auto& count = binaryActionCounts[binary];
625+
#ifndef NDEBUG
626+
static const Index MaxBasicBlockActions = 1024 * 1024;
627+
assert(count < MaxBasicBlockActions);
628+
#endif
629+
count++;
630+
if (count >= MaxBinaryActions) {
631+
constraints.setProvesNothing(set->index);
632+
return;
633+
}
634+
}
635+
605636
constraints.set(set->index, value);
606637
}
607638
}

test/lit/passes/constraint-analysis-loops.wast

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,4 +1905,59 @@
19051905
)
19061906
)
19071907
)
1908+
1909+
;; CHECK: (func $increment-tee (type $0)
1910+
;; CHECK-NEXT: (local $x i32)
1911+
;; CHECK-NEXT: (local $y i32)
1912+
;; CHECK-NEXT: (loop $label1
1913+
;; CHECK-NEXT: (if
1914+
;; CHECK-NEXT: (i32.gt_s
1915+
;; CHECK-NEXT: (local.get $y)
1916+
;; CHECK-NEXT: (local.get $x)
1917+
;; CHECK-NEXT: )
1918+
;; CHECK-NEXT: (then
1919+
;; CHECK-NEXT: (unreachable)
1920+
;; CHECK-NEXT: )
1921+
;; CHECK-NEXT: )
1922+
;; CHECK-NEXT: (local.set $y
1923+
;; CHECK-NEXT: (local.tee $x
1924+
;; CHECK-NEXT: (i32.add
1925+
;; CHECK-NEXT: (local.get $y)
1926+
;; CHECK-NEXT: (i32.const 1)
1927+
;; CHECK-NEXT: )
1928+
;; CHECK-NEXT: )
1929+
;; CHECK-NEXT: )
1930+
;; CHECK-NEXT: (br $label1)
1931+
;; CHECK-NEXT: )
1932+
;; CHECK-NEXT: )
1933+
(func $increment-tee
1934+
(local $x i32)
1935+
(local $y i32)
1936+
;; A loop, where $y is incremented but there is a tee in the middle. The loop
1937+
;; is unbounded (the exit condition is never hit), so we must be careful to
1938+
;; not keep calculating 1,2,3, without limit. The tee in the middle should not
1939+
;; confuse us: we apply the +=1 operation to x directly, but y reads it
1940+
;; through the tee. We should stop calculating anything about both rather than
1941+
;; hang for a long time.
1942+
(loop $label1
1943+
(if
1944+
(i32.gt_s
1945+
(local.get $y)
1946+
(local.get $x)
1947+
)
1948+
(then
1949+
(unreachable)
1950+
)
1951+
)
1952+
(local.set $y
1953+
(local.tee $x
1954+
(i32.add
1955+
(local.get $y)
1956+
(i32.const 1)
1957+
)
1958+
)
1959+
)
1960+
(br $label1)
1961+
)
1962+
)
19081963
)

test/lit/passes/simplify-locals-desc.wast

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111
(type $desc (describes $struct) (struct))
1212
)
1313

14-
;; CHECK: (func $test (type $2) (param $ref (ref null $struct)) (result (ref $struct))
14+
;; CHECK: (type $none (func))
15+
(type $none (func))
16+
17+
;; CHECK: (tag $tag (type $none))
18+
(tag $tag (type $none))
19+
20+
;; CHECK: (func $test (type $3) (param $ref (ref null $struct)) (result (ref $struct))
1521
;; CHECK-NEXT: (local $temp (ref $desc))
1622
;; CHECK-NEXT: (block $block (result (ref none))
1723
;; CHECK-NEXT: (drop
@@ -49,5 +55,74 @@
4955
(unreachable)
5056
)
5157
)
58+
59+
;; CHECK: (func $br_on_cast_desc_eq_in_block (type $4) (result (ref $struct))
60+
;; CHECK-NEXT: (local $0 (ref null $desc))
61+
;; CHECK-NEXT: (local $1 i32)
62+
;; CHECK-NEXT: (block $out (result (ref $struct))
63+
;; CHECK-NEXT: (if
64+
;; CHECK-NEXT: (i32.const 0)
65+
;; CHECK-NEXT: (then
66+
;; CHECK-NEXT: (block $block
67+
;; CHECK-NEXT: (drop
68+
;; CHECK-NEXT: (block (result i32)
69+
;; CHECK-NEXT: (drop
70+
;; CHECK-NEXT: (br_on_cast_desc_eq_fail $out (ref (exact $struct)) (ref $struct)
71+
;; CHECK-NEXT: (struct.new_default_desc $struct
72+
;; CHECK-NEXT: (ref.null none)
73+
;; CHECK-NEXT: )
74+
;; CHECK-NEXT: (try_table (result (ref $desc)) (catch $tag $block)
75+
;; CHECK-NEXT: (ref.as_non_null
76+
;; CHECK-NEXT: (local.get $0)
77+
;; CHECK-NEXT: )
78+
;; CHECK-NEXT: )
79+
;; CHECK-NEXT: )
80+
;; CHECK-NEXT: )
81+
;; CHECK-NEXT: (i32.const 0)
82+
;; CHECK-NEXT: )
83+
;; CHECK-NEXT: )
84+
;; CHECK-NEXT: )
85+
;; CHECK-NEXT: )
86+
;; CHECK-NEXT: )
87+
;; CHECK-NEXT: (unreachable)
88+
;; CHECK-NEXT: )
89+
;; CHECK-NEXT: )
90+
(func $br_on_cast_desc_eq_in_block (result (ref $struct))
91+
(local $0 (ref null $desc))
92+
(local $1 i32)
93+
;; While simplifying locals here, we must be aware that the br_on
94+
;; instruction has a descriptor, and must visit it. If we do not, then we'd
95+
;; see no branches to $block, and move the tee out of it, breaking
96+
;; validation.
97+
(block $out (result (ref $struct))
98+
(if
99+
(i32.const 0)
100+
(then
101+
(block $block
102+
(drop
103+
(local.tee $1
104+
(block (result i32)
105+
(drop
106+
(br_on_cast_desc_eq_fail $out (ref (exact $struct)) (ref $struct)
107+
(struct.new_default_desc $struct
108+
(ref.null none)
109+
)
110+
(try_table (result (ref $desc)) (catch $tag $block)
111+
(ref.as_non_null
112+
(local.get $0)
113+
)
114+
)
115+
)
116+
)
117+
(i32.const 0)
118+
)
119+
)
120+
)
121+
)
122+
)
123+
)
124+
(unreachable)
125+
)
126+
)
52127
)
53128

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
1-
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
21
;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split
32
;; RUN: wasm-dis -all %t.1.wasm | filecheck %s --check-prefix PRIMARY
43
;; RUN: wasm-dis -all %t.2.wasm | filecheck %s --check-prefix SECONDARY
54

65
(module
7-
;; PRIMARY: (type $0 (func))
8-
9-
;; PRIMARY: (global $g funcref (ref.null nofunc))
106
(global $g funcref (ref.null nofunc))
117

128
;; We should scan this passive element segment's data and correctly mark $g as
139
;; used in the primary module.
14-
;; PRIMARY: (elem $passive-elem funcref (item (global.get $g)))
1510
(elem $passive-elem funcref (item (global.get $g)))
1611

17-
;; PRIMARY: (export "global" (global $g))
18-
19-
;; PRIMARY: (func $keep (type $0)
20-
;; PRIMARY-NEXT: (elem.drop $passive-elem)
21-
;; PRIMARY-NEXT: )
2212
(func $keep
2313
(elem.drop $passive-elem)
2414
)
2515

26-
;; SECONDARY: (type $0 (func))
27-
28-
;; SECONDARY: (import "primary" "global" (global $g funcref))
29-
30-
;; SECONDARY: (func $split (type $0)
31-
;; SECONDARY-NEXT: (drop
32-
;; SECONDARY-NEXT: (global.get $g)
33-
;; SECONDARY-NEXT: )
34-
;; SECONDARY-NEXT: )
3516
(func $split
3617
(drop (global.get $g))
3718
)
3819
)
20+
21+
;; PRIMARY: (module
22+
;; PRIMARY-NEXT: (type $0 (func))
23+
;; PRIMARY-NEXT: (global $g funcref (ref.null nofunc))
24+
;; PRIMARY-NEXT: (elem $passive-elem funcref (item (global.get $g)))
25+
;; PRIMARY-NEXT: (export "global" (global $g))
26+
;; PRIMARY-NEXT: (func $keep (type $0)
27+
;; PRIMARY-NEXT: (elem.drop $passive-elem)
28+
;; PRIMARY-NEXT: )
29+
;; PRIMARY-NEXT: )
30+
31+
;; SECONDARY: (module
32+
;; SECONDARY-NEXT: (type $0 (func))
33+
;; SECONDARY-NEXT: (import "primary" "global" (global $g funcref))
34+
;; SECONDARY-NEXT: (func $split (type $0)
35+
;; SECONDARY-NEXT: (drop
36+
;; SECONDARY-NEXT: (global.get $g)
37+
;; SECONDARY-NEXT: )
38+
;; SECONDARY-NEXT: )
39+
;; SECONDARY-NEXT: )

0 commit comments

Comments
 (0)