Skip to content

Commit 20bdf38

Browse files
rakudramacommit-bot@chromium.org
authored andcommitted
[dart2js] Fix variable allocator live-range bug
The controlling condition of a control-flow operator (?:, &&, ||) needs to have the same live range as the other operands. Change-Id: Iee999a1b400ef35ccbb761fcd7266952c9a95be9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192605 Reviewed-by: Mayank Patke <[email protected]> Commit-Queue: Stephen Adams <[email protected]>
1 parent 93313bf commit 20bdf38

File tree

2 files changed

+76
-20
lines changed

2 files changed

+76
-20
lines changed

pkg/compiler/lib/src/ssa/variable_allocator.dart

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,22 @@ class SsaLiveIntervalBuilder extends HBaseVisitor with CodegenPhase {
168168
int instructionId = 0;
169169

170170
/// The liveIns of basic blocks.
171-
final Map<HBasicBlock, LiveEnvironment> liveInstructions;
171+
final Map<HBasicBlock, LiveEnvironment> liveInstructions = {};
172172

173173
/// The live intervals of instructions.
174-
final Map<HInstruction, LiveInterval> liveIntervals;
174+
final Map<HInstruction, LiveInterval> liveIntervals = {};
175+
176+
/// Controlling conditions for control-flow operators. Control-flow operators,
177+
/// e.g. `c ? a : b`, have a condition input `c` from the HIf node
178+
/// at the top of the control flow diamond as well as the HPhi inputs for `a`
179+
/// and `b` at the bottom of the diamond.
180+
final Map<HInstruction, HInstruction> _phiToCondition = {};
175181

176-
SsaLiveIntervalBuilder(this.generateAtUseSite, this.controlFlowOperators)
177-
: liveInstructions = new Map<HBasicBlock, LiveEnvironment>(),
178-
liveIntervals = new Map<HInstruction, LiveInterval>();
182+
SsaLiveIntervalBuilder(this.generateAtUseSite, this.controlFlowOperators) {
183+
for (HIf ifNode in controlFlowOperators) {
184+
_phiToCondition[ifNode.joinBlock.phis.first] = ifNode.condition;
185+
}
186+
}
179187

180188
@override
181189
void visitGraph(HGraph graph) {
@@ -187,6 +195,12 @@ class SsaLiveIntervalBuilder extends HBaseVisitor with CodegenPhase {
187195

188196
void markInputsAsLiveInEnvironment(
189197
HInstruction instruction, LiveEnvironment environment) {
198+
if (instruction is HPhi) {
199+
HInstruction condition = _phiToCondition[instruction];
200+
if (condition != null) {
201+
markAsLiveInEnvironment(condition, environment);
202+
}
203+
}
190204
for (int i = 0, len = instruction.inputs.length; i < len; i++) {
191205
markAsLiveInEnvironment(instruction.inputs[i], environment);
192206
}
@@ -257,21 +271,7 @@ class SsaLiveIntervalBuilder extends HBaseVisitor with CodegenPhase {
257271

258272
@override
259273
void visitBasicBlock(HBasicBlock block) {
260-
LiveEnvironment environment =
261-
new LiveEnvironment(liveIntervals, instructionId);
262-
263-
// If the control flow instruction in this block will actually be
264-
// inlined in the codegen in the join block, we need to make
265-
// whatever is used by that control flow instruction as live in
266-
// the join block.
267-
if (controlFlowOperators.contains(block.last)) {
268-
HIf ifInstruction = block.last;
269-
HBasicBlock joinBlock = ifInstruction.joinBlock;
270-
if (generateAtUseSite.contains(joinBlock.phis.first)) {
271-
markInputsAsLiveInEnvironment(
272-
ifInstruction, liveInstructions[joinBlock]);
273-
}
274-
}
274+
LiveEnvironment environment = LiveEnvironment(liveIntervals, instructionId);
275275

276276
// Add to the environment the liveIn of its successor, as well as
277277
// the inputs of the phis of the successor that flow from this block.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// @dart=2.10
6+
7+
// Regression test for variable allocator live range bug.
8+
//
9+
// Pre-fix, this program would crash because t2 was reused before it was dead.
10+
//
11+
// algo$1: function(n) {
12+
// var t1 = this.ax,
13+
// t2 = t1 == null;
14+
// if ((t2 ? null : t1.a) == null)
15+
// return;
16+
// t2 = n + 1; // the old value of t2 is used below!
17+
// (t2 ? null : t1.a).parameters.addAll$2(0, t2, t2);
18+
// }
19+
//
20+
// Several things were necessary to tickle the bug:
21+
//
22+
// [A] The repeated test that gets shared from the two null-aware `ax?.a`
23+
// expressions.
24+
// [B] A null-aware `parameters?.` that is optimized away because `parameters`
25+
// is always non-null.
26+
// [C] A repeated expression after the optimized null-aware access.
27+
28+
class Thing {
29+
@pragma('dart2js:noInline')
30+
void addAll(x, y) {}
31+
}
32+
33+
class A {
34+
Thing parameters = Thing();
35+
}
36+
37+
class AX {
38+
A a;
39+
AX(this.a);
40+
}
41+
42+
class Host {
43+
AX ax;
44+
Host(this.ax);
45+
46+
algo(int n) {
47+
if (ax?.a == null) return;
48+
ax?.a.parameters?.addAll(n + 1, n + 1);
49+
}
50+
}
51+
52+
main() {
53+
Host(null).algo(1);
54+
Host(AX(A())).algo(2);
55+
Host(AX(null)).algo(3);
56+
}

0 commit comments

Comments
 (0)