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

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
OLDNEW
(Empty)
1 /**
floitsch 2012/05/30 18:45:03 copyright.
ngeoffray 2012/05/31 08:12:46 Done.
2 * The [LiveRange] class covers a range where an instruction is live.
3 */
4 class LiveRange {
5 final int start;
6 // [end] is not final because it can be udpated due to loops.
kasperl 2012/05/31 05:26:03 udpated -> updated
ngeoffray 2012/05/31 08:12:46 Done.
7 int end;
8 LiveRange(this.start, this.end) {
9 assert(start <= end);
10 }
11
12 String toString() {
kasperl 2012/05/31 05:26:03 =>
ngeoffray 2012/05/31 08:12:46 Done.
13 return '[$start $end[';
14 }
15 }
16
17 /**
18 * The [LiveInterval] class contains the list of ranges where an
19 * instruction is live.
20 */
21 class LiveInterval {
22 /**
23 * The id where there instruction is defined.
24 */
25 int start;
26 final List<LiveRange> ranges;
27 LiveInterval() : ranges = <LiveRange>[];
28
29 /**
30 * Update all ranges that are contained in [start, end[ to have
floitsch 2012/05/30 18:45:03 This will probably confuse the doc-generator, and
kasperl 2012/05/31 05:26:03 to have die? to have died? to die?
ngeoffray 2012/05/31 08:12:46 to die.
ngeoffray 2012/05/31 08:12:46 Let's see how it complains :)
31 * die at [end].
floitsch 2012/05/30 18:45:03 "have die"? either have "end", or just "to die"
ngeoffray 2012/05/31 08:12:46 Done.
32 */
33 void loopUpdate(int start, int end) {
34 for (LiveRange range in ranges) {
35 if (range.start >= start && range.end < end) {
floitsch 2012/05/30 18:45:03 minor, but personally I prefer start <= range.star
ngeoffray 2012/05/31 08:12:46 Done.
36 range.end = end;
37 }
38 }
39 }
40
41 /**
42 * Add a new range to this interval.
43 */
44 void add(LiveRange interval) {
45 ranges.add(interval);
46 }
47
48 /**
49 * Returns true if one of the ranges of this interval dies at [at].
50 */
51 bool diesAt(int at) {
52 for (LiveRange range in ranges) {
floitsch 2012/05/30 18:45:03 Haven't yet looked at the use-sites, but this look
ngeoffray 2012/05/31 08:12:46 An instruction doesn't have that many live ranges.
53 if (range.end == at) return true;
54 }
55 return false;
56 }
57
58 String toString() {
59 List<String> res = new List<String>();
60 for (final interval in ranges) res.add(interval.toString());
61 return '(${Strings.join(res, ', ')})';
62 }
63 }
64
65 /**
66 * The [LiveEnvironment] class contains the live-ins of a basic block.
floitsch 2012/05/30 18:45:03 please rename all "live-in"s with "live-interval"s
ngeoffray 2012/05/31 08:12:46 It's not containing the live-intervals, it's conta
67 */
68 class LiveEnvironment {
69 /**
kasperl 2012/05/31 05:26:03 It would be nice with some better explanations of
ngeoffray 2012/05/31 08:12:46 Done.
70 * The id where the basic block starts.
71 */
72 int startId;
73
74 /**
75 * The id where the basic block ends.
76 */
77 final int endId;
78
79 /**
80 * List of loop markers that will be updated once the loop header is
kasperl 2012/05/31 05:26:03 Maybe explain what it marks (what's the int value
ngeoffray 2012/05/31 08:12:46 Done.
81 * visited. All live-ins of the loop header will be merged into this
82 * environment.
83 */
84 final Map<HBasicBlock, int> loopMarkers;
85
86 /**
87 * The instructions that are live in this basic block. The values of
88 * the map contains the ids where the instructions die.
floitsch 2012/05/30 18:45:03 contain
ngeoffray 2012/05/31 08:12:46 Done.
89 */
90 final Map<HInstruction, int> lives;
floitsch 2012/05/30 18:45:03 could we rename this to "lastUsedAt" ?
kasperl 2012/05/31 05:26:03 lives -> liveUntil? How does this relate to the la
ngeoffray 2012/05/31 08:12:46 It's really a map of live instructions with the in
ngeoffray 2012/05/31 08:12:46 It will be used when adding a range to the live in
91
92 /**
93 * Map containing the live intervals of instructions.
94 */
95 final Map<HInstruction, LiveInterval> liveIntervals;
96
97 LiveEnvironment(this.liveIntervals, this.endId)
98 : lives = new Map<HInstruction, int>(),
99 loopMarkers = new Map<HBasicBlock, int>();
100
101 /**
102 * Remove an instruction from the liveIn set. This method also
103 * updates the live interval of [instruction] to contain the new
104 * range: [id, / id contained in [lives] /].
105 */
106 void remove(HInstruction instruction, int id) {
107 // Special case the HCheck instruction to have the same live
108 // interval as the instruction it is checking.
109 if (instruction is HCheck) {
110 HInstruction input = instruction.checkedInput;
111 while (input is HCheck) input = input.checkedInput;
112 liveIntervals.putIfAbsent(input, () => new LiveInterval());
113 liveIntervals.putIfAbsent(instruction, () => liveIntervals[input]);
114 return;
115 }
116 LiveInterval range = liveIntervals.putIfAbsent(
117 instruction, () => new LiveInterval());
118 int lastId = lives[instruction];
119 // If [lastId] is null, then this instruction is bot being used.
floitsch 2012/05/30 18:45:03 not
kasperl 2012/05/31 05:26:03 is bot -> is not
ngeoffray 2012/05/31 08:12:46 Done.
ngeoffray 2012/05/31 08:12:46 Done.
120 range.add(new LiveRange(id, lastId == null ? id : lastId));
121 // The instruction is defined at [id].
122 range.start = id;
123 lives.remove(instruction);
124 }
125
126 /**
127 * Add [instruction] to the liveIn set. If the instruction is not
128 * already in the set, we save the id where it dies.
129 */
130 void add(HInstruction instruction, int userId) {
131 // Special case the HCheck instruction to use the actual checked
132 // instruction.
133 while (instruction is HCheck) instruction = instruction.checkedInput;
134 lives.putIfAbsent(instruction, () => userId);
floitsch 2012/05/30 18:45:03 Add comment that reminds that we are visiting the
ngeoffray 2012/05/31 08:12:46 Done.
135 }
136
137 /**
138 * Merge this environment with [other]. Update the end id of
139 * instructions in case they are different between this and [other].
140 */
141 void mergeWith(LiveEnvironment other) {
142 other.lives.forEach((HInstruction instruction, int existingId) {
143 if (existingId == endId) return;
floitsch 2012/05/30 18:45:03 Add comment when this can happen.
ngeoffray 2012/05/31 08:12:46 Done.
144 LiveInterval range = liveIntervals.putIfAbsent(
145 instruction, () => new LiveInterval());
146 range.add(new LiveRange(other.startId, existingId));
147 lives[instruction] = endId;
148 });
149 other.loopMarkers.forEach((k, v) { loopMarkers[k] = v; });
150 }
151
152 void addLoopMarker(HBasicBlock header, int id) {
153 assert(!loopMarkers.containsKey(header));
154 loopMarkers[header] = id;
155 }
156
157 void removeLoopMarker(HBasicBlock header) {
158 assert(loopMarkers.containsKey(header));
159 loopMarkers.remove(header);
160 }
161
162 bool isEmpty() => lives.isEmpty() && loopMarkers.isEmpty();
163 bool contains(HInstruction instruction) => lives.containsKey(instruction);
164 String toString() => lives.toString();
165 }
166
167 /**
168 * Builds the live intervals of each instruction. The algorithm visits
169 * the graph post-dominator tree to find the last uses of an
170 * instruction, and computes the liveIns of each basic block.
171 */
172 class SsaLiveIntervalBuilder extends HBaseVisitor {
173 final Compiler compiler;
174
175 /**
176 * A counter to assign start and end ids to live ranges. The initial
177 * value is not relevant.
178 */
179 int counter = 0;
180
181 /**
182 * The liveIns of basic blocks.
183 */
184 final Map<HBasicBlock, LiveEnvironment> liveInstructions;
185
186 /**
187 * The live intervals of instructions.
188 */
189 final Map<HInstruction, LiveInterval> liveIntervals;
190
191 SsaLiveIntervalBuilder(this.compiler)
192 : liveInstructions = new Map<HBasicBlock, LiveEnvironment>(),
193 liveIntervals = new Map<HInstruction, LiveInterval>();
194
195 void visitGraph(HGraph graph) {
196 visitPostDominatorTree(graph);
197 if (!liveInstructions[graph.entry].isEmpty()) {
198 compiler.internalError('LiveIntervalBuilder',
199 node: compiler.currentElement.parseNode(compiler));
200 }
201 }
202
203 void visitBasicBlock(HBasicBlock block) {
204 LiveEnvironment environment = new LiveEnvironment(liveIntervals, counter);
205
206 // Add to the environment the live instructions of its successor, as well as
207 // the inputs of the phis of the successor that flow from this block.
208 for (int i = 0; i < block.successors.length; i++) {
209 HBasicBlock successor = block.successors[i];
210 LiveEnvironment successorEnv = liveInstructions[successor];
211 if (successorEnv !== null) {
212 environment.mergeWith(successorEnv);
213 } else {
214 environment.addLoopMarker(successor, counter);
215 }
216
217 int index = successor.predecessors.indexOf(block);
218 for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) {
219 environment.add(phi.inputs[index], counter);
220 }
221 }
222
223 // Iterate over all instructions to remove an instruction from the
224 // environment and add its inputs.
225 HInstruction instruction = block.last;
226 while (instruction != null) {
227 environment.remove(instruction, counter);
228 for (int i = 0, len = instruction.inputs.length; i < len; i++) {
229 environment.add(instruction.inputs[i], counter);
230 }
231 instruction = instruction.previous;
232 counter--;
kasperl 2012/05/31 05:26:03 This is the only place you change counter? Hmm. Th
ngeoffray 2012/05/31 08:12:46 Changed the name to instructionId, and added a com
233 }
234
235 // We just remove the phis from the environment. The inputs of the
236 // phis will be put in the environment of the predecessors.
237 for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
238 environment.remove(phi, counter);
239 }
240
241 // Save the liveInstructions of that block.
242 environment.startId = counter + 1;
243 liveInstructions[block] = environment;
244
245 // If the block is a loop header, we can remove the loop marker,
246 // because it will just recompute the loop phis.
247 if (block.isLoopHeader()) {
248 updateLoopMarker(block);
249 }
250 }
251
252 void updateLoopMarker(HBasicBlock header) {
253 LiveEnvironment env = liveInstructions[header];
254 int lastId = env.loopMarkers[header];
255 // Update all instructions that are liveIns in [header] to have a
256 // range that covers the loop.
257 env.lives.forEach((HInstruction instruction, int id) {
258 LiveInterval range = env.liveIntervals.putIfAbsent(
259 instruction, () => new LiveInterval());
260 range.loopUpdate(env.startId, lastId);
261 env.lives[instruction] = lastId;
262 });
263
264 env.removeLoopMarker(header);
265
266 // Update all liveIns set to contain the liveIns of [header].
267 liveInstructions.forEach((HBasicBlock block, LiveEnvironment other) {
268 if (other.loopMarkers.containsKey(header)) {
269 env.lives.forEach((HInstruction instruction, int id) {
270 other.lives[instruction] = id;
271 });
272 other.removeLoopMarker(header);
273 env.loopMarkers.forEach((k, v) { other.loopMarkers[k] = v; });
274 }
275 });
276 }
277 }
278
279 /**
280 * Represents a copy from one instruction to another. The codegen
281 * also uses this class to represent a copy from one variable to
282 * another.
283 */
284 class Copy {
Lasse Reichstein Nielsen 2012/05/31 08:24:56 Rename to "Copying" (or "Assignment" if that isn't
ngeoffray 2012/05/31 08:48:22 As discussed, kept the name :)
285 var source;
floitsch 2012/05/30 18:45:03 final HInstruction ?
kasperl 2012/05/31 05:26:03 Types on source and destination? I'm pretty sure t
ngeoffray 2012/05/31 08:12:46 They are here, but will be String in codegen :) Th
ngeoffray 2012/05/31 08:12:46 Made it final, but not typed, as they can also be
286 var destination;
287 Copy(this.source, this.destination);
288 String toString() => '$destination <- $source';
289 }
290
291 /**
292 * A copy handler contains the copies that a basic block needs to do
293 * after executing all its instructions.
294 */
295 class CopyHandler {
296 /**
297 * The copies from an instruction to a phi of the successor.
298 */
299 final List<Copy> copies;
300
301 /**
302 * Trivial assignments from a constant to the phi of a successor.
Lasse Reichstein Nielsen 2012/05/31 08:24:56 How is this different from a copying? I.e., what m
ngeoffray 2012/05/31 08:48:22 Because the source does not need a name. Added a c
303 */
304 final List<Copy> assignments;
305
306 CopyHandler()
307 : copies = new List<Copy>(),
308 assignments = new List<Copy>();
309
310 void addCopy(HInstruction source, HInstruction destination) {
311 copies.add(new Copy(source, destination));
312 }
313
314 void addAssignment(HInstruction source, HInstruction destination) {
315 assignments.add(new Copy(source, destination));
316 }
317
318 String toString() {
kasperl 2012/05/31 05:26:03 =>
ngeoffray 2012/05/31 08:12:46 Done.
319 return 'Copies: $copies, assignments: $assignments';
320 }
321 }
322
323 /**
324 * Contains the mapping between instructions and their names for code
325 * generation, as well as the [CopyHandler] for each basic block.
326 */
327 class VariableNames {
328 final Map<HInstruction, String> ownName;
329 final Map<HBasicBlock, CopyHandler> copies;
kasperl 2012/05/31 05:26:03 This really don't hold copies -- it holds handlers
ngeoffray 2012/05/31 08:12:46 Done.
330 static final String SWAP_TEMP = 't0';
Lasse Reichstein Nielsen 2012/05/31 08:24:56 How do we know this doesn't collide with anything?
ngeoffray 2012/05/31 08:48:22 Done. And added a TODO to deal with parameters.
331
332 VariableNames()
333 : ownName = new Map<HInstruction, String>(),
334 copies = new Map<HBasicBlock, CopyHandler>();
335
336 String getName(HInstruction instruction) {
337 return ownName[instruction];
338 }
339
340 CopyHandler getCopies(HBasicBlock block) {
kasperl 2012/05/31 05:26:03 I would probably call this getCopyHandler.
ngeoffray 2012/05/31 08:12:46 Done.
341 return copies[block];
342 }
343
344 bool hasName(HInstruction instruction) => ownName.containsKey(instruction);
345
346 void addCopy(HBasicBlock block, HInstruction source, HPhi destination) {
347 CopyHandler handler =
348 copies.putIfAbsent(block, () => new CopyHandler());
349 handler.addCopy(source, destination);
350 }
351
352 void addAssignment(HBasicBlock block, HInstruction source, HPhi destination) {
353 CopyHandler handler =
354 copies.putIfAbsent(block, () => new CopyHandler());
355 handler.addAssignment(source, destination);
356 }
357 }
358
359 /**
360 * Allocates variable names for instructions, making sure they don't
361 * collide.
floitsch 2012/05/30 18:45:03 Not that it matters, but this should fit on one li
ngeoffray 2012/05/31 08:12:46 Done.
362 */
363 class VariableNamer {
364 final VariableNames names;
365 final Set<String> usedNames;
366
367 VariableNamer(LiveEnvironment environment, this.names)
368 : usedNames = new Set<String>() {
369 // [VariableNames.SWAP_TEMP] is being used when there is a cycle
370 // in a copy handler. Therefore we make sure no one will use it.
371 usedNames.add(VariableNames.SWAP_TEMP);
372
373 // All liveIns instructions must have a name at this point, so we
374 // add them to the list of used names.
375 environment.lives.forEach((HInstruction instruction, int index) {
376 String name = names.getName(instruction);
377 if (name !== null) {
378 usedNames.add(name);
379 }
380 });
381 }
382
383 String allocateWithHint(String originalName) {
384 int i = 0;
385 String name = JsNames.getValid(originalName);
386 while (usedNames.contains(name)) {
387 name = JsNames.getValid('$originalName${i++}');
388 }
389 return name;
390 }
391
392 String allocateTemporary() {
393 int i = 1;
kasperl 2012/05/31 05:26:03 Add comment about not using t0 because it's SWAP_T
ngeoffray 2012/05/31 08:12:46 Done.
394 String name = 't${i++}';
395 while (usedNames.contains(name)) name = 't${i++}';
396 return name;
397 }
398
399 HPhi firstPhiUserWithElement(HInstruction instruction) {
400 for (HInstruction user in instruction.usedBy) {
401 if (user is HPhi && user.sourceElement !== null) {
402 return user;
403 }
404 }
405 return null;
406 }
407
408 String allocateName(HInstruction instruction) {
409 String name;
410 if (instruction is HCheck) {
411 // Special case the check instruction to use the name of its
412 // checked instruction.
413 HCheck check = instruction;
414 name = names.ownName[check.checkedInput];
415 // If the name is null, then the checked input is being
416 // generated at use site, and we don't need a name for the check
417 // instruction.
418 if (name == null) return;
419 } else if (instruction is HParameterValue) {
420 HParameterValue parameter = instruction;
421 name = allocateWithHint(parameter.element.name.slowToString());
422 } else if (instruction.sourceElement !== null) {
423 name = allocateWithHint(instruction.sourceElement.name.slowToString());
424 } else {
425 // We could not find an element for the instruction. If the
426 // instruction is used by a phi, try to use the name of the phi.
427 // Otherwise, just allocate a temporary name.
428 HPhi phi = firstPhiUserWithElement(instruction);
429 if (phi !== null) {
430 name = allocateWithHint(phi.sourceElement.name.slowToString());
431 } else {
432 name = allocateTemporary();
433 }
434 }
435 usedNames.add(name);
436 names.ownName[instruction] = name;
437 return name;
438 }
439
440 void freeName(HInstruction instruction) {
floitsch 2012/05/30 18:45:03 Add comment: "Frees the [instruction]'s name so it
ngeoffray 2012/05/31 08:12:46 Done.
441 String ownName = names.ownName[instruction];
442 if (ownName != null) {
443 usedNames.remove(ownName);
444 }
445 }
446 }
447
448 /**
449 * Visits all blocks in the graph, sets names to instructions, and
450 * creates the [CopyHandler] for each block. This class needs to have
451 * the liveIns set as well as all the live intervals of instructions.
452 * It visits the graph in dominator order, so that at each entry of a
453 * block, the instructions in its liveIns set have names.
454 *
455 * When visiting a block, it goes through all instructions. For each
456 * instruction, it frees the names of the inputs that die at that
457 * instruction, and allocates a name to the instruction. For each phi,
458 * it adds a copy to the CopyHandler of the corresponding predecessor.
459 */
460 class SsaVariableAllocator extends HBaseVisitor {
461
462 final Compiler compiler;
463 final Map<HBasicBlock, LiveEnvironment> liveInstructions;
464 final Map<HInstruction, LiveInterval> liveIntervals;
465 final Set<HInstruction> generateAtUseSite;
466
467 final VariableNames names;
468
469 SsaVariableAllocator(this.compiler,
470 this.liveInstructions,
471 this.liveIntervals,
472 this.generateAtUseSite)
473 : names = new VariableNames();
474
475 void visitGraph(HGraph graph) {
476 visitDominatorTree(graph);
477 }
478
479 void visitBasicBlock(HBasicBlock block) {
480 VariableNamer namer = new VariableNamer(liveInstructions[block], names);
481
482 block.forEachPhi((HPhi phi) {
483 handlePhi(phi, namer);
484 });
485
486 block.forEachInstruction((HInstruction instruction) {
487 handleInstruction(instruction, namer);
488 });
489 }
490
491 /**
492 * Returns whether [instruction] needs a name. Instructions that
493 * have no users or that are generated at use site does not need a name.
kasperl 2012/05/31 05:26:03 use site does -> use site do. Maybe change method
ngeoffray 2012/05/31 08:12:46 Done.
494 */
495 bool needsVariable(HInstruction instruction) {
496 if (instruction.usedBy.isEmpty()) return false;
497 // TODO(ngeoffray): We need a name for parameters, but we could
kasperl 2012/05/31 05:26:03 The TODO comment isn't easy to understand. The "we
ngeoffray 2012/05/31 08:12:46 The problem is that we're making parameters genera
498 // allocate it before.
499 if (instruction is HParameterValue && instruction is !HThis) return true;
500 if (generateAtUseSite.contains(instruction)) return false;
501 return true;
502 }
503
kasperl 2012/05/31 05:26:03 Too many newlines.
ngeoffray 2012/05/31 08:12:46 Done.
504
505 /**
506 * Retrurns whether [instruction] dies at the instruction [at].
floitsch 2012/05/30 18:45:03 Returns
kasperl 2012/05/31 05:26:03 Retrurns -> Returns
ngeoffray 2012/05/31 08:12:46 Done.
ngeoffray 2012/05/31 08:12:46 Done.
507 */
508 bool diesAt(HInstruction instruction, HInstruction at) {
509 LiveInterval atRange = liveIntervals[at];
kasperl 2012/05/31 05:26:03 You call them ranges in a few places, but the type
ngeoffray 2012/05/31 08:12:46 Done.
510 LiveInterval instructionRange = liveIntervals[instruction];
511 int start = atRange.start;
512 return instructionRange.diesAt(start);
513 }
514
515 void handleInstruction(HInstruction instruction, VariableNamer namer) {
516 for (int i = 0, len = instruction.inputs.length; i < len; i++) {
517 HInstruction input = instruction.inputs[i];
518 if (needsVariable(input) && diesAt(input, instruction)) {
kasperl 2012/05/31 05:26:03 Add a comment here. If we're the last use of somet
ngeoffray 2012/05/31 08:12:46 Done.
519 namer.freeName(input);
520 }
521 }
522
523 if (needsVariable(instruction)) {
524 namer.allocateName(instruction);
525 }
526 }
527
528 void handlePhi(HPhi phi, VariableNamer namer) {
529 if (!needsVariable(phi)) return;
530
531 for (int i = 0; i < phi.inputs.length; i++) {
532 HInstruction input = phi.inputs[i];
533 HBasicBlock predecessor = phi.block.predecessors[i];
534 if (!needsVariable(input)) {
535 names.addAssignment(predecessor, input, phi);
Lasse Reichstein Nielsen 2012/05/31 08:24:56 If it doesn't need a variable, what are we assigni
ngeoffray 2012/05/31 08:48:22 The instruction (eg a constant, or a generate at u
536 } else {
537 names.addCopy(predecessor, input, phi);
538 }
539 }
540
541 namer.allocateName(phi);
542 }
543 }
OLDNEW
« lib/compiler/implementation/ssa/codegen.dart ('K') | « lib/compiler/implementation/ssa/ssa.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698