Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: lib/compiler/implementation/ssa/variable_allocator.dart

Issue 10440087: Compute liveness of instructions to get better and fewer variable names. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « lib/compiler/implementation/ssa/ssa.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /**
6 * The [LiveRange] class covers a range where an instruction is live.
7 */
8 class LiveRange {
9 final int start;
10 // [end] is not final because it can be updated due to loops.
11 int end;
12 LiveRange(this.start, this.end) {
13 assert(start <= end);
14 }
15
16 String toString() => '[$start $end[';
17 }
18
19 /**
20 * The [LiveInterval] class contains the list of ranges where an
21 * instruction is live.
22 */
23 class LiveInterval {
24 /**
25 * The id where there instruction is defined.
26 */
27 int start;
28 final List<LiveRange> ranges;
29 LiveInterval() : ranges = <LiveRange>[];
30
31 /**
32 * Update all ranges that are contained in [start, end[ to
33 * die at [end].
34 */
35 void loopUpdate(int start, int end) {
36 for (LiveRange range in ranges) {
37 if (start <= range.start && range.end < end) {
38 range.end = end;
39 }
40 }
41 }
42
43 /**
44 * Add a new range to this interval.
45 */
46 void add(LiveRange interval) {
47 ranges.add(interval);
48 }
49
50 /**
51 * Returns true if one of the ranges of this interval dies at [at].
52 */
53 bool diesAt(int at) {
54 for (LiveRange range in ranges) {
55 if (range.end == at) return true;
56 }
57 return false;
58 }
59
60 String toString() {
61 List<String> res = new List<String>();
62 for (final interval in ranges) res.add(interval.toString());
63 return '(${Strings.join(res, ', ')})';
64 }
65 }
66
67 /**
68 * The [LiveEnvironment] class contains the liveIn set of a basic
69 * block. A liveIn set of a block contains the instructions that are
70 * live when entering that block.
71 */
72 class LiveEnvironment {
73 /**
74 * The instruction id where the basic block starts. See
75 * [SsaLiveIntervalBuilder.instructionId].
76 */
77 int startId;
78
79 /**
80 * The instruction id where the basic block ends.
81 */
82 final int endId;
83
84 /**
85 * Loop markers that will be updated once the loop header is
86 * visited. The liveIn set of the loop header will be merged into this
87 * environment. [loopMarkers] is a mapping from block header to the
88 * end instruction id of the loop exit block.
89 */
90 final Map<HBasicBlock, int> loopMarkers;
91
92 /**
93 * The instructions that are live in this basic block. The values of
94 * the map contain the instruction ids where the instructions die.
95 * It will be used when adding a range to the live interval of an
96 * instruction.
97 */
98 final Map<HInstruction, int> liveInstructions;
99
100 /**
101 * Map containing the live intervals of instructions.
102 */
103 final Map<HInstruction, LiveInterval> liveIntervals;
104
105 LiveEnvironment(this.liveIntervals, this.endId)
106 : liveInstructions = new Map<HInstruction, int>(),
107 loopMarkers = new Map<HBasicBlock, int>();
108
109 /**
110 * Remove an instruction from the liveIn set. This method also
111 * updates the live interval of [instruction] to contain the new
112 * range: [id, / id contained in [liveInstructions] /].
113 */
114 void remove(HInstruction instruction, int id) {
115 // Special case the HCheck instruction to have the same live
116 // interval as the instruction it is checking.
117 if (instruction is HCheck) {
118 HInstruction input = instruction.checkedInput;
119 while (input is HCheck) input = input.checkedInput;
120 liveIntervals.putIfAbsent(input, () => new LiveInterval());
121 liveIntervals.putIfAbsent(instruction, () => liveIntervals[input]);
122 return;
123 }
124 LiveInterval range = liveIntervals.putIfAbsent(
125 instruction, () => new LiveInterval());
126 int lastId = liveInstructions[instruction];
127 // If [lastId] is null, then this instruction is not being used.
128 range.add(new LiveRange(id, lastId == null ? id : lastId));
129 // The instruction is defined at [id].
130 range.start = id;
131 liveInstructions.remove(instruction);
132 }
133
134 /**
135 * Add [instruction] to the liveIn set. If the instruction is not
136 * already in the set, we save the id where it dies.
137 */
138 void add(HInstruction instruction, int userId) {
139 // Special case the HCheck instruction to use the actual checked
140 // instruction.
141 while (instruction is HCheck) instruction = instruction.checkedInput;
142 // Note that we are visiting the grap in post-dominator order, so
143 // the first time we see a variable is when it dies.
144 liveInstructions.putIfAbsent(instruction, () => userId);
145 }
146
147 /**
148 * Merge this environment with [other]. Update the end id of
149 * instructions in case they are different between this and [other].
150 */
151 void mergeWith(LiveEnvironment other) {
152 other.liveInstructions.forEach((HInstruction instruction, int existingId) {
153 // If both environments have the same instruction id of where
154 // [instruction] dies, there is no need to update the live
155 // interval of [instruction]. For example the if block and the
156 // else block have the same end id for an instruction that is
157 // being used in the join block and defined before the if/else.
158 if (existingId == endId) return;
159 LiveInterval range = liveIntervals.putIfAbsent(
160 instruction, () => new LiveInterval());
161 range.add(new LiveRange(other.startId, existingId));
162 liveInstructions[instruction] = endId;
163 });
164 other.loopMarkers.forEach((k, v) { loopMarkers[k] = v; });
165 }
166
167 void addLoopMarker(HBasicBlock header, int id) {
168 assert(!loopMarkers.containsKey(header));
169 loopMarkers[header] = id;
170 }
171
172 void removeLoopMarker(HBasicBlock header) {
173 assert(loopMarkers.containsKey(header));
174 loopMarkers.remove(header);
175 }
176
177 bool isEmpty() => liveInstructions.isEmpty() && loopMarkers.isEmpty();
178 bool contains(HInstruction instruction) => liveInstructions.containsKey(instru ction);
179 String toString() => liveInstructions.toString();
180 }
181
182 /**
183 * Builds the live intervals of each instruction. The algorithm visits
184 * the graph post-dominator tree to find the last uses of an
185 * instruction, and computes the liveIns of each basic block.
186 */
187 class SsaLiveIntervalBuilder extends HBaseVisitor {
188 final Compiler compiler;
189
190 /**
191 * A counter to assign start and end ids to live ranges. The initial
192 * value is not relevant. Note that instructionId goes downward to ease
193 * reasoning about live ranges (the first instruction of a graph has
194 * the lowest id).
195 */
196 int instructionId = 0;
197
198 /**
199 * The liveIns of basic blocks.
200 */
201 final Map<HBasicBlock, LiveEnvironment> liveInstructions;
202
203 /**
204 * The live intervals of instructions.
205 */
206 final Map<HInstruction, LiveInterval> liveIntervals;
207
208 SsaLiveIntervalBuilder(this.compiler)
209 : liveInstructions = new Map<HBasicBlock, LiveEnvironment>(),
210 liveIntervals = new Map<HInstruction, LiveInterval>();
211
212 void visitGraph(HGraph graph) {
213 visitPostDominatorTree(graph);
214 if (!liveInstructions[graph.entry].isEmpty()) {
215 compiler.internalError('LiveIntervalBuilder',
216 node: compiler.currentElement.parseNode(compiler));
217 }
218 }
219
220 void visitBasicBlock(HBasicBlock block) {
221 LiveEnvironment environment = new LiveEnvironment(liveIntervals, instruction Id);
222
223 // Add to the environment the liveIn of its successor, as well as
224 // the inputs of the phis of the successor that flow from this block.
225 for (int i = 0; i < block.successors.length; i++) {
226 HBasicBlock successor = block.successors[i];
227 LiveEnvironment successorEnv = liveInstructions[successor];
228 if (successorEnv !== null) {
229 environment.mergeWith(successorEnv);
230 } else {
231 environment.addLoopMarker(successor, instructionId);
232 }
233
234 int index = successor.predecessors.indexOf(block);
235 for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) {
236 environment.add(phi.inputs[index], instructionId);
237 }
238 }
239
240 // Iterate over all instructions to remove an instruction from the
241 // environment and add its inputs.
242 HInstruction instruction = block.last;
243 while (instruction != null) {
244 environment.remove(instruction, instructionId);
245 for (int i = 0, len = instruction.inputs.length; i < len; i++) {
246 environment.add(instruction.inputs[i], instructionId);
247 }
248 instruction = instruction.previous;
249 instructionId--;
250 }
251
252 // We just remove the phis from the environment. The inputs of the
253 // phis will be put in the environment of the predecessors.
254 for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
255 environment.remove(phi, instructionId);
256 }
257
258 // Save the liveInstructions of that block.
259 environment.startId = instructionId + 1;
260 liveInstructions[block] = environment;
261
262 // If the block is a loop header, we can remove the loop marker,
263 // because it will just recompute the loop phis.
264 if (block.isLoopHeader()) {
265 updateLoopMarker(block);
266 }
267 }
268
269 void updateLoopMarker(HBasicBlock header) {
270 LiveEnvironment env = liveInstructions[header];
271 int lastId = env.loopMarkers[header];
272 // Update all instructions that are liveIns in [header] to have a
273 // range that covers the loop.
274 env.liveInstructions.forEach((HInstruction instruction, int id) {
275 LiveInterval range = env.liveIntervals.putIfAbsent(
276 instruction, () => new LiveInterval());
277 range.loopUpdate(env.startId, lastId);
278 env.liveInstructions[instruction] = lastId;
279 });
280
281 env.removeLoopMarker(header);
282
283 // Update all liveIns set to contain the liveIns of [header].
284 liveInstructions.forEach((HBasicBlock block, LiveEnvironment other) {
285 if (other.loopMarkers.containsKey(header)) {
286 env.liveInstructions.forEach((HInstruction instruction, int id) {
287 other.liveInstructions[instruction] = id;
288 });
289 other.removeLoopMarker(header);
290 env.loopMarkers.forEach((k, v) { other.loopMarkers[k] = v; });
291 }
292 });
293 }
294 }
295
296 /**
297 * Represents a copy from one instruction to another. The codegen
298 * also uses this class to represent a copy from one variable to
299 * another.
300 */
301 class Copy {
302 final source;
303 final destination;
304 Copy(this.source, this.destination);
305 String toString() => '$destination <- $source';
306 }
307
308 /**
309 * A copy handler contains the copies that a basic block needs to do
310 * after executing all its instructions.
311 */
312 class CopyHandler {
313 /**
314 * The copies from an instruction to a phi of the successor.
315 */
316 final List<Copy> copies;
317
318 /**
319 * Assignments from an instruction that does not need a name (e.g. a
320 * constant) to the phi of a successor.
321 */
322 final List<Copy> assignments;
323
324 CopyHandler()
325 : copies = new List<Copy>(),
326 assignments = new List<Copy>();
327
328 void addCopy(HInstruction source, HInstruction destination) {
329 copies.add(new Copy(source, destination));
330 }
331
332 void addAssignment(HInstruction source, HInstruction destination) {
333 assignments.add(new Copy(source, destination));
334 }
335
336 String toString() => 'Copies: $copies, assignments: $assignments';
337 }
338
339 /**
340 * Contains the mapping between instructions and their names for code
341 * generation, as well as the [CopyHandler] for each basic block.
342 */
343 class VariableNames {
344 final Map<HInstruction, String> ownName;
345 final Map<HBasicBlock, CopyHandler> copyHandlers;
346 /**
347 * Name that is being used as a temporary to break cycles in
348 * parallel copies. We make sure this name is not being used
349 * anywhere by reserving it when we allocate names for instructions.
350 * TODO(ngeoffray): This isn't true for parameters, and we should
351 * rename the parameter name, to keep it simple.
352 */
353 static final String SWAP_TEMP = 't0';
354
355 VariableNames()
356 : ownName = new Map<HInstruction, String>(),
357 copyHandlers = new Map<HBasicBlock, CopyHandler>();
358
359 String getName(HInstruction instruction) {
360 return ownName[instruction];
361 }
362
363 CopyHandler getCopyHandler(HBasicBlock block) {
364 return copyHandlers[block];
365 }
366
367 bool hasName(HInstruction instruction) => ownName.containsKey(instruction);
368
369 void addCopy(HBasicBlock block, HInstruction source, HPhi destination) {
370 CopyHandler handler =
371 copyHandlers.putIfAbsent(block, () => new CopyHandler());
372 handler.addCopy(source, destination);
373 }
374
375 void addAssignment(HBasicBlock block, HInstruction source, HPhi destination) {
376 CopyHandler handler =
377 copyHandlers.putIfAbsent(block, () => new CopyHandler());
378 handler.addAssignment(source, destination);
379 }
380 }
381
382 /**
383 * Allocates variable names for instructions, making sure they don't collide.
384 */
385 class VariableNamer {
386 final VariableNames names;
387 final Set<String> usedNames;
388
389 VariableNamer(LiveEnvironment environment, this.names)
390 : usedNames = new Set<String>() {
391 // [VariableNames.SWAP_TEMP] is being used when there is a cycle
392 // in a copy handler. Therefore we make sure no one will use it.
393 usedNames.add(VariableNames.SWAP_TEMP);
394
395 // All liveIns instructions must have a name at this point, so we
396 // add them to the list of used names.
397 environment.liveInstructions.forEach((HInstruction instruction, int index) {
398 String name = names.getName(instruction);
399 if (name !== null) {
400 usedNames.add(name);
401 }
402 });
403 }
404
405 String allocateWithHint(String originalName) {
406 int i = 0;
407 String name = JsNames.getValid(originalName);
408 while (usedNames.contains(name)) {
409 name = JsNames.getValid('$originalName${i++}');
410 }
411 return name;
412 }
413
414 String allocateTemporary() {
415 // Don't start at 0 because t0 is being used for
416 // [VariableNames.SWAP_TEMP].
417 int i = 1;
418 String name = 't${i++}';
419 while (usedNames.contains(name)) name = 't${i++}';
420 return name;
421 }
422
423 HPhi firstPhiUserWithElement(HInstruction instruction) {
424 for (HInstruction user in instruction.usedBy) {
425 if (user is HPhi && user.sourceElement !== null) {
426 return user;
427 }
428 }
429 return null;
430 }
431
432 String allocateName(HInstruction instruction) {
433 String name;
434 if (instruction is HCheck) {
435 // Special case the check instruction to use the name of its
436 // checked instruction.
437 HCheck check = instruction;
438 name = names.ownName[check.checkedInput];
439 // If the name is null, then the checked input is being
440 // generated at use site, and we don't need a name for the check
441 // instruction.
442 if (name == null) return;
443 } else if (instruction is HParameterValue) {
444 HParameterValue parameter = instruction;
445 name = allocateWithHint(parameter.element.name.slowToString());
446 } else if (instruction.sourceElement !== null) {
447 name = allocateWithHint(instruction.sourceElement.name.slowToString());
448 } else {
449 // We could not find an element for the instruction. If the
450 // instruction is used by a phi, try to use the name of the phi.
451 // Otherwise, just allocate a temporary name.
452 HPhi phi = firstPhiUserWithElement(instruction);
453 if (phi !== null) {
454 name = allocateWithHint(phi.sourceElement.name.slowToString());
455 } else {
456 name = allocateTemporary();
457 }
458 }
459 usedNames.add(name);
460 names.ownName[instruction] = name;
461 return name;
462 }
463
464 /**
465 * Frees [instruction]'s name so it can be used for other instructions.
466 */
467 void freeName(HInstruction instruction) {
468 String ownName = names.ownName[instruction];
469 if (ownName != null) {
470 usedNames.remove(ownName);
471 }
472 }
473 }
474
475 /**
476 * Visits all blocks in the graph, sets names to instructions, and
477 * creates the [CopyHandler] for each block. This class needs to have
478 * the liveIns set as well as all the live intervals of instructions.
479 * It visits the graph in dominator order, so that at each entry of a
480 * block, the instructions in its liveIns set have names.
481 *
482 * When visiting a block, it goes through all instructions. For each
483 * instruction, it frees the names of the inputs that die at that
484 * instruction, and allocates a name to the instruction. For each phi,
485 * it adds a copy to the CopyHandler of the corresponding predecessor.
486 */
487 class SsaVariableAllocator extends HBaseVisitor {
488
489 final Compiler compiler;
490 final Map<HBasicBlock, LiveEnvironment> liveInstructions;
491 final Map<HInstruction, LiveInterval> liveIntervals;
492 final Set<HInstruction> generateAtUseSite;
493
494 final VariableNames names;
495
496 SsaVariableAllocator(this.compiler,
497 this.liveInstructions,
498 this.liveIntervals,
499 this.generateAtUseSite)
500 : names = new VariableNames();
501
502 void visitGraph(HGraph graph) {
503 visitDominatorTree(graph);
504 }
505
506 void visitBasicBlock(HBasicBlock block) {
507 VariableNamer namer = new VariableNamer(liveInstructions[block], names);
508
509 block.forEachPhi((HPhi phi) {
510 handlePhi(phi, namer);
511 });
512
513 block.forEachInstruction((HInstruction instruction) {
514 handleInstruction(instruction, namer);
515 });
516 }
517
518 /**
519 * Returns whether [instruction] needs a name. Instructions that
520 * have no users or that are generated at use site does not need a name.
521 */
522 bool needsName(HInstruction instruction) {
523 if (instruction.usedBy.isEmpty()) return false;
524 // TODO(ngeoffray): parameters are being generated at use site,
525 // but we need a name for parameters. We should probably not make
526 // them generate at use site to make things simpler.
527 if (instruction is HParameterValue && instruction is !HThis) return true;
528 if (generateAtUseSite.contains(instruction)) return false;
529 return true;
530 }
531
532 /**
533 * Returns whether [instruction] dies at the instruction [at].
534 */
535 bool diesAt(HInstruction instruction, HInstruction at) {
536 LiveInterval atInterval = liveIntervals[at];
537 LiveInterval instructionInterval = liveIntervals[instruction];
538 int start = atInterval.start;
539 return instructionInterval.diesAt(start);
540 }
541
542 void handleInstruction(HInstruction instruction, VariableNamer namer) {
543 for (int i = 0, len = instruction.inputs.length; i < len; i++) {
544 HInstruction input = instruction.inputs[i];
545 // If [input] has a name, and its use here is the last use, free
546 // its name.
547 if (needsName(input) && diesAt(input, instruction)) {
548 namer.freeName(input);
549 }
550 }
551
552 if (needsName(instruction)) {
553 namer.allocateName(instruction);
554 }
555 }
556
557 void handlePhi(HPhi phi, VariableNamer namer) {
558 if (!needsName(phi)) return;
559
560 for (int i = 0; i < phi.inputs.length; i++) {
561 HInstruction input = phi.inputs[i];
562 HBasicBlock predecessor = phi.block.predecessors[i];
563 if (!needsName(input)) {
564 names.addAssignment(predecessor, input, phi);
565 } else {
566 names.addCopy(predecessor, input, phi);
567 }
568 }
569
570 namer.allocateName(phi);
571 }
572 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/ssa/ssa.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698