OLD | NEW |
(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 } |
OLD | NEW |