Skip to content

Commit a0a9da7

Browse files
authored
fix: Keep recompiling when unifying local flags in loops
1 parent 93703aa commit a0a9da7

File tree

5 files changed

+173
-6
lines changed

5 files changed

+173
-6
lines changed

src/compiler.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,9 +2410,9 @@ export class Compiler extends DiagnosticEmitter {
24102410
flow.inherit(condFlow);
24112411

24122412
// Detect if local flags are incompatible before and after looping, and
2413-
// if so recompile by unifying local flags between iterations.
2413+
// if so recompile by unifying local flags between iterations. Note that
2414+
// this may be necessary multiple times where locals depend on each other.
24142415
if (Flow.hasIncompatibleLocalStates(flowBefore, flow)) {
2415-
assert(!flowAfter); // should work on the first attempt
24162416
outerFlow.popBreakLabel();
24172417
this.currentFlow = outerFlow;
24182418
return this.doCompileDoStatement(statement, flow);
@@ -2603,9 +2603,9 @@ export class Compiler extends DiagnosticEmitter {
26032603
);
26042604

26052605
// Detect if local flags are incompatible before and after looping, and if
2606-
// so recompile by unifying local flags between iterations.
2606+
// so recompile by unifying local flags between iterations. Note that this
2607+
// may be necessary multiple times where locals depend on each other.
26072608
if (Flow.hasIncompatibleLocalStates(flowBefore, flow)) {
2608-
assert(!flowAfter); // should work on the first attempt
26092609
assert(!bodyFlow.hasScopedLocals);
26102610
flow.freeScopedLocals();
26112611
outerFlow.popBreakLabel();
@@ -3344,10 +3344,10 @@ export class Compiler extends DiagnosticEmitter {
33443344
else flow.inheritBranch(bodyFlow);
33453345

33463346
// Detect if local flags are incompatible before and after looping, and
3347-
// if so recompile by unifying local flags between iterations.
3347+
// if so recompile by unifying local flags between iterations. Note that
3348+
// this may be necessary multiple times where locals depend on each other.
33483349
// Here: Only relevant if flow does not always break.
33493350
if (!breaks && Flow.hasIncompatibleLocalStates(flowBefore, flow)) {
3350-
assert(!flowAfter); // should work on the first attempt
33513351
flow.freeTempLocal(tcond);
33523352
outerFlow.popBreakLabel();
33533353
this.currentFlow = outerFlow;

tests/compiler/unify-local-flags.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"asc_flags": [
3+
"--runtime none"
4+
]
5+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(memory $0 0)
4+
(export "memory" (memory $0))
5+
(export "testFor" (func $unify-local-flags/testFor))
6+
(export "testWhile" (func $unify-local-flags/testFor))
7+
(export "testDo" (func $unify-local-flags/testDo))
8+
(func $unify-local-flags/testFor
9+
(local $0 i32)
10+
loop $for-loop|2
11+
local.get $0
12+
i32.const 255
13+
i32.and
14+
i32.const 255
15+
i32.lt_u
16+
if
17+
local.get $0
18+
i32.const 1
19+
i32.add
20+
local.set $0
21+
br $for-loop|2
22+
end
23+
end
24+
)
25+
(func $unify-local-flags/testDo
26+
(local $0 i32)
27+
loop $do-continue|1
28+
local.get $0
29+
i32.const 1
30+
i32.add
31+
local.tee $0
32+
i32.const 255
33+
i32.and
34+
i32.const 255
35+
i32.lt_u
36+
br_if $do-continue|1
37+
end
38+
)
39+
)

tests/compiler/unify-local-flags.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Test that locals depending on each other don't break recompilation of loops
2+
// when local flags must be unified. In the test cases below, x depends on i's
3+
// wrapped state, so triggers recompilation twice, in this order:
4+
//
5+
// * i WRAPPED, x WRAPPED
6+
// -> recompile with i not WRAPPED due to ++i
7+
// * i not WRAPPED, x WRAPPED
8+
// -> recompile with x not WRAPPED due to x=i
9+
// * i not WRAPPED, x not WRAPPED
10+
// -> success
11+
12+
export function testFor(): void {
13+
let x: u8 = 0;
14+
for (let i: u8 = 0; i < 255; ++i) {
15+
x = i;
16+
}
17+
}
18+
19+
export function testWhile(): void {
20+
let x: u8 = 0;
21+
let i: u8 = 0;
22+
while (i < 255) {
23+
x = i;
24+
++i;
25+
}
26+
}
27+
28+
export function testDo(): void {
29+
let x: u8 = 0;
30+
let i: u8 = 0;
31+
do {
32+
x = i;
33+
} while (++i < 255);
34+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(memory $0 0)
4+
(table $0 1 funcref)
5+
(export "memory" (memory $0))
6+
(export "testFor" (func $unify-local-flags/testFor))
7+
(export "testWhile" (func $unify-local-flags/testWhile))
8+
(export "testDo" (func $unify-local-flags/testDo))
9+
(func $unify-local-flags/testFor
10+
(local $0 i32)
11+
(local $1 i32)
12+
(local $2 i32)
13+
(local $3 i32)
14+
(local $4 i32)
15+
i32.const 0
16+
local.set $0
17+
i32.const 0
18+
local.set $1
19+
loop $for-loop|2
20+
local.get $1
21+
i32.const 255
22+
i32.and
23+
i32.const 255
24+
i32.lt_u
25+
local.set $4
26+
local.get $4
27+
if
28+
local.get $1
29+
local.set $0
30+
local.get $1
31+
i32.const 1
32+
i32.add
33+
local.set $1
34+
br $for-loop|2
35+
end
36+
end
37+
)
38+
(func $unify-local-flags/testWhile
39+
(local $0 i32)
40+
(local $1 i32)
41+
(local $2 i32)
42+
i32.const 0
43+
local.set $0
44+
i32.const 0
45+
local.set $1
46+
loop $while-continue|2
47+
local.get $1
48+
i32.const 255
49+
i32.and
50+
i32.const 255
51+
i32.lt_u
52+
local.set $2
53+
local.get $2
54+
if
55+
local.get $1
56+
local.set $0
57+
local.get $1
58+
i32.const 1
59+
i32.add
60+
local.set $1
61+
br $while-continue|2
62+
end
63+
end
64+
)
65+
(func $unify-local-flags/testDo
66+
(local $0 i32)
67+
(local $1 i32)
68+
(local $2 i32)
69+
i32.const 0
70+
local.set $0
71+
i32.const 0
72+
local.set $1
73+
loop $do-continue|1
74+
local.get $1
75+
local.set $0
76+
local.get $1
77+
i32.const 1
78+
i32.add
79+
local.tee $1
80+
i32.const 255
81+
i32.and
82+
i32.const 255
83+
i32.lt_u
84+
local.set $2
85+
local.get $2
86+
br_if $do-continue|1
87+
end
88+
)
89+
)

0 commit comments

Comments
 (0)