Skip to content

Commit 4d9f6f5

Browse files
authored
[Stack Switching] Fix GUFA on continuations (#7824)
We must mark the blocks that Resumes target as receiving values, or else GUFA will think they are unreachable, and optimize them away.
1 parent 2a608fd commit 4d9f6f5

File tree

3 files changed

+164
-5
lines changed

3 files changed

+164
-5
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
'vacuum-stack-switching.wast',
115115
'cont.wast',
116116
'cont_simple.wast',
117+
'gufa-cont.wast',
117118
'cont_many_unhandled.wast',
118119
# TODO: fix split_wast() on tricky escaping situations like a string ending
119120
# in \\" (the " is not escaped - there is an escaped \ before it)

src/ir/possible-contents.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,14 +1291,40 @@ struct InfoCollector
12911291
// TODO: optimize when possible
12921292
addRoot(curr);
12931293
}
1294-
void visitResume(Resume* curr) {
1295-
// TODO: optimize when possible
1296-
addRoot(curr);
1297-
}
1298-
void visitResumeThrow(ResumeThrow* curr) {
1294+
1295+
template<typename T> void handleResume(T* curr) {
12991296
// TODO: optimize when possible
13001297
addRoot(curr);
1298+
1299+
// Connect handled tags with their branch targets, and materialize non-null
1300+
// continuation values.
1301+
auto numTags = curr->handlerTags.size();
1302+
for (Index tagIndex = 0; tagIndex < numTags; tagIndex++) {
1303+
auto tag = curr->handlerTags[tagIndex];
1304+
auto target = curr->handlerBlocks[tagIndex];
1305+
auto params = getModule()->getTag(tag)->params();
1306+
1307+
// Add the values from the tag.
1308+
for (Index i = 0; i < params.size(); i++) {
1309+
if (isRelevant(params[i])) {
1310+
info.links.push_back(
1311+
{TagLocation{tag, i}, getBreakTargetLocation(target, i)});
1312+
}
1313+
}
1314+
1315+
// Add the continuation. Its type is determined by the block we break to,
1316+
// as the last result.
1317+
auto targetType = findBreakTarget(target)->type;
1318+
assert(targetType.size() >= 1);
1319+
auto contType = targetType[targetType.size() - 1];
1320+
auto location = getRootLocation(contType);
1321+
info.links.push_back(
1322+
{location, getBreakTargetLocation(target, params.size())});
1323+
}
13011324
}
1325+
1326+
void visitResume(Resume* curr) { handleResume(curr); }
1327+
void visitResumeThrow(ResumeThrow* curr) { handleResume(curr); }
13021328
void visitStackSwitch(StackSwitch* curr) {
13031329
// TODO: optimize when possible
13041330
addRoot(curr);

test/lit/passes/gufa-cont.wast

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
2+
3+
;; RUN: foreach %s %t wasm-opt -all --gufa -S -o - | filecheck %s
4+
5+
(module
6+
;; CHECK: (type $func (func))
7+
(type $func (func))
8+
;; CHECK: (type $cont (cont $func))
9+
(type $cont (cont $func))
10+
11+
;; CHECK: (type $func-i32 (func (result i32)))
12+
(type $func-i32 (func (result i32)))
13+
;; CHECK: (type $cont-i32 (cont $func-i32))
14+
(type $cont-i32 (cont $func-i32))
15+
16+
;; CHECK: (type $4 (func (result i32 (ref $cont))))
17+
18+
;; CHECK: (elem declare func $cont $cont-i32)
19+
20+
;; CHECK: (tag $tag (type $func))
21+
(tag $tag (type $func))
22+
23+
;; CHECK: (tag $tag-i32 (type $func-i32) (result i32))
24+
(tag $tag-i32 (type $func-i32))
25+
26+
;; CHECK: (export "resume" (func $resume))
27+
28+
;; CHECK: (export "resume_throw" (func $resume_throw))
29+
30+
;; CHECK: (export "resume-i32" (func $resume-i32))
31+
32+
;; CHECK: (func $cont (type $func)
33+
;; CHECK-NEXT: (suspend $tag)
34+
;; CHECK-NEXT: )
35+
(func $cont
36+
;; Helper for below.
37+
(suspend $tag)
38+
)
39+
40+
;; CHECK: (func $resume (type $func)
41+
;; CHECK-NEXT: (drop
42+
;; CHECK-NEXT: (block $block (result (ref $cont))
43+
;; CHECK-NEXT: (resume $cont (on $tag $block)
44+
;; CHECK-NEXT: (cont.new $cont
45+
;; CHECK-NEXT: (ref.func $cont)
46+
;; CHECK-NEXT: )
47+
;; CHECK-NEXT: )
48+
;; CHECK-NEXT: (return)
49+
;; CHECK-NEXT: )
50+
;; CHECK-NEXT: )
51+
;; CHECK-NEXT: )
52+
(func $resume (export "resume")
53+
;; A continuation is created, it suspends, and we handle that. There is
54+
;; nothing to optimize or change here.
55+
(drop
56+
(block $block (result (ref $cont))
57+
(resume $cont (on $tag $block)
58+
(cont.new $cont
59+
(ref.func $cont)
60+
)
61+
)
62+
(return)
63+
)
64+
)
65+
)
66+
67+
;; CHECK: (func $resume_throw (type $func)
68+
;; CHECK-NEXT: (drop
69+
;; CHECK-NEXT: (block $block (result (ref $cont))
70+
;; CHECK-NEXT: (resume_throw $cont $tag (on $tag $block)
71+
;; CHECK-NEXT: (cont.new $cont
72+
;; CHECK-NEXT: (ref.func $cont)
73+
;; CHECK-NEXT: )
74+
;; CHECK-NEXT: )
75+
;; CHECK-NEXT: (return)
76+
;; CHECK-NEXT: )
77+
;; CHECK-NEXT: )
78+
;; CHECK-NEXT: )
79+
(func $resume_throw (export "resume_throw")
80+
;; As above, but with resume_throw.
81+
(drop
82+
(block $block (result (ref $cont))
83+
(resume_throw $cont $tag (on $tag $block)
84+
(cont.new $cont
85+
(ref.func $cont)
86+
)
87+
)
88+
(return)
89+
)
90+
)
91+
)
92+
93+
;; CHECK: (func $cont-i32 (type $func-i32) (result i32)
94+
;; CHECK-NEXT: (suspend $tag)
95+
;; CHECK-NEXT: (unreachable)
96+
;; CHECK-NEXT: (unreachable)
97+
;; CHECK-NEXT: )
98+
(func $cont-i32 (result i32)
99+
;; Helper for below.
100+
(suspend $tag)
101+
(unreachable)
102+
)
103+
104+
;; CHECK: (func $resume-i32 (type $func)
105+
;; CHECK-NEXT: (tuple.drop 2
106+
;; CHECK-NEXT: (block $block (type $4) (result i32 (ref $cont))
107+
;; CHECK-NEXT: (drop
108+
;; CHECK-NEXT: (resume $cont-i32 (on $tag-i32 $block)
109+
;; CHECK-NEXT: (cont.new $cont-i32
110+
;; CHECK-NEXT: (ref.func $cont-i32)
111+
;; CHECK-NEXT: )
112+
;; CHECK-NEXT: )
113+
;; CHECK-NEXT: )
114+
;; CHECK-NEXT: (return)
115+
;; CHECK-NEXT: )
116+
;; CHECK-NEXT: )
117+
;; CHECK-NEXT: )
118+
(func $resume-i32 (export "resume-i32")
119+
;; As above, but with more values sent than just the continuation.
120+
(tuple.drop 2
121+
(block $block (result i32 (ref $cont))
122+
(resume $cont-i32 (on $tag-i32 $block)
123+
(cont.new $cont-i32
124+
(ref.func $cont-i32)
125+
)
126+
)
127+
(return)
128+
)
129+
)
130+
)
131+
)
132+

0 commit comments

Comments
 (0)