| 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 part of ssa; | |
| 6 | |
| 7 /** | |
| 8 * The [LiveRange] class covers a range where an instruction is live. | |
| 9 */ | |
| 10 class LiveRange { | |
| 11 final int start; | |
| 12 // [end] is not final because it can be updated due to loops. | |
| 13 int end; | |
| 14 LiveRange(this.start, this.end) { | |
| 15 assert(start <= end); | |
| 16 } | |
| 17 | |
| 18 String toString() => '[$start $end['; | |
| 19 } | |
| 20 | |
| 21 /** | |
| 22 * The [LiveInterval] class contains the list of ranges where an | |
| 23 * instruction is live. | |
| 24 */ | |
| 25 class LiveInterval { | |
| 26 /** | |
| 27 * The id where the instruction is defined. | |
| 28 */ | |
| 29 int start; | |
| 30 final List<LiveRange> ranges; | |
| 31 LiveInterval() : ranges = <LiveRange>[]; | |
| 32 | |
| 33 // We want [HCheck] instructions to have the same name as the | |
| 34 // instruction it checks, so both instructions should share the same | |
| 35 // live ranges. | |
| 36 LiveInterval.forCheck(this.start, LiveInterval checkedInterval) | |
| 37 : ranges = checkedInterval.ranges; | |
| 38 | |
| 39 /** | |
| 40 * Update all ranges that are contained in [from, to[ to | |
| 41 * die at [to]. | |
| 42 */ | |
| 43 void loopUpdate(int from, int to) { | |
| 44 for (LiveRange range in ranges) { | |
| 45 if (from <= range.start && range.end < to) { | |
| 46 range.end = to; | |
| 47 } | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Add a new range to this interval. | |
| 53 */ | |
| 54 void add(LiveRange interval) { | |
| 55 ranges.add(interval); | |
| 56 } | |
| 57 | |
| 58 /** | |
| 59 * Returns true if one of the ranges of this interval dies at [at]. | |
| 60 */ | |
| 61 bool diesAt(int at) { | |
| 62 for (LiveRange range in ranges) { | |
| 63 if (range.end == at) return true; | |
| 64 } | |
| 65 return false; | |
| 66 } | |
| 67 | |
| 68 String toString() { | |
| 69 List<String> res = new List<String>(); | |
| 70 for (final interval in ranges) res.add(interval.toString()); | |
| 71 return '(${res.join(", ")})'; | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 /** | |
| 76 * The [LiveEnvironment] class contains the liveIn set of a basic | |
| 77 * block. A liveIn set of a block contains the instructions that are | |
| 78 * live when entering that block. | |
| 79 */ | |
| 80 class LiveEnvironment { | |
| 81 /** | |
| 82 * The instruction id where the basic block starts. See | |
| 83 * [SsaLiveIntervalBuilder.instructionId]. | |
| 84 */ | |
| 85 int startId; | |
| 86 | |
| 87 /** | |
| 88 * The instruction id where the basic block ends. | |
| 89 */ | |
| 90 final int endId; | |
| 91 | |
| 92 /** | |
| 93 * Loop markers that will be updated once the loop header is | |
| 94 * visited. The liveIn set of the loop header will be merged into this | |
| 95 * environment. [loopMarkers] is a mapping from block header to the | |
| 96 * end instruction id of the loop exit block. | |
| 97 */ | |
| 98 final Map<HBasicBlock, int> loopMarkers; | |
| 99 | |
| 100 /** | |
| 101 * The instructions that are live in this basic block. The values of | |
| 102 * the map contain the instruction ids where the instructions die. | |
| 103 * It will be used when adding a range to the live interval of an | |
| 104 * instruction. | |
| 105 */ | |
| 106 final Map<HInstruction, int> liveInstructions; | |
| 107 | |
| 108 /** | |
| 109 * Map containing the live intervals of instructions. | |
| 110 */ | |
| 111 final Map<HInstruction, LiveInterval> liveIntervals; | |
| 112 | |
| 113 LiveEnvironment(this.liveIntervals, this.endId) | |
| 114 : liveInstructions = new Map<HInstruction, int>(), | |
| 115 loopMarkers = new Map<HBasicBlock, int>(); | |
| 116 | |
| 117 /** | |
| 118 * Remove an instruction from the liveIn set. This method also | |
| 119 * updates the live interval of [instruction] to contain the new | |
| 120 * range: [id, / id contained in [liveInstructions] /]. | |
| 121 */ | |
| 122 void remove(HInstruction instruction, int id) { | |
| 123 LiveInterval interval = liveIntervals.putIfAbsent( | |
| 124 instruction, () => new LiveInterval()); | |
| 125 int lastId = liveInstructions[instruction]; | |
| 126 // If [lastId] is null, then this instruction is not being used. | |
| 127 interval.add(new LiveRange(id, lastId == null ? id : lastId)); | |
| 128 // The instruction is defined at [id]. | |
| 129 interval.start = id; | |
| 130 liveInstructions.remove(instruction); | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * Add [instruction] to the liveIn set. If the instruction is not | |
| 135 * already in the set, we save the id where it dies. | |
| 136 */ | |
| 137 void add(HInstruction instruction, int userId) { | |
| 138 // Note that we are visiting the graph in post-dominator order, so | |
| 139 // the first time we see a variable is when it dies. | |
| 140 liveInstructions.putIfAbsent(instruction, () => userId); | |
| 141 } | |
| 142 | |
| 143 /** | |
| 144 * Merge this environment with [other]. Update the end id of | |
| 145 * instructions in case they are different between this and [other]. | |
| 146 */ | |
| 147 void mergeWith(LiveEnvironment other) { | |
| 148 other.liveInstructions.forEach((HInstruction instruction, int existingId) { | |
| 149 // If both environments have the same instruction id of where | |
| 150 // [instruction] dies, there is no need to update the live | |
| 151 // interval of [instruction]. For example the if block and the | |
| 152 // else block have the same end id for an instruction that is | |
| 153 // being used in the join block and defined before the if/else. | |
| 154 if (existingId == endId) return; | |
| 155 LiveInterval range = liveIntervals.putIfAbsent( | |
| 156 instruction, () => new LiveInterval()); | |
| 157 range.add(new LiveRange(other.startId, existingId)); | |
| 158 liveInstructions[instruction] = endId; | |
| 159 }); | |
| 160 other.loopMarkers.forEach((k, v) { loopMarkers[k] = v; }); | |
| 161 } | |
| 162 | |
| 163 void addLoopMarker(HBasicBlock header, int id) { | |
| 164 assert(!loopMarkers.containsKey(header)); | |
| 165 loopMarkers[header] = id; | |
| 166 } | |
| 167 | |
| 168 void removeLoopMarker(HBasicBlock header) { | |
| 169 assert(loopMarkers.containsKey(header)); | |
| 170 loopMarkers.remove(header); | |
| 171 } | |
| 172 | |
| 173 bool get isEmpty => liveInstructions.isEmpty && loopMarkers.isEmpty; | |
| 174 bool contains(HInstruction instruction) => | |
| 175 liveInstructions.containsKey(instruction); | |
| 176 String toString() => liveInstructions.toString(); | |
| 177 } | |
| 178 | |
| 179 /** | |
| 180 * Builds the live intervals of each instruction. The algorithm visits | |
| 181 * the graph post-dominator tree to find the last uses of an | |
| 182 * instruction, and computes the liveIns of each basic block. | |
| 183 */ | |
| 184 class SsaLiveIntervalBuilder extends HBaseVisitor { | |
| 185 final Compiler compiler; | |
| 186 final Set<HInstruction> generateAtUseSite; | |
| 187 final Set<HInstruction> controlFlowOperators; | |
| 188 | |
| 189 /** | |
| 190 * A counter to assign start and end ids to live ranges. The initial | |
| 191 * value is not relevant. Note that instructionId goes downward to ease | |
| 192 * reasoning about live ranges (the first instruction of a graph has | |
| 193 * the lowest id). | |
| 194 */ | |
| 195 int instructionId = 0; | |
| 196 | |
| 197 /** | |
| 198 * The liveIns of basic blocks. | |
| 199 */ | |
| 200 final Map<HBasicBlock, LiveEnvironment> liveInstructions; | |
| 201 | |
| 202 /** | |
| 203 * The live intervals of instructions. | |
| 204 */ | |
| 205 final Map<HInstruction, LiveInterval> liveIntervals; | |
| 206 | |
| 207 SsaLiveIntervalBuilder( | |
| 208 this.compiler, this.generateAtUseSite, this.controlFlowOperators) | |
| 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(CURRENT_ELEMENT_SPANNABLE, 'LiveIntervalBuilder.'); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void markInputsAsLiveInEnvironment(HInstruction instruction, | |
| 220 LiveEnvironment environment) { | |
| 221 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | |
| 222 markAsLiveInEnvironment(instruction.inputs[i], environment); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // Returns the non-HCheck instruction, or the last [HCheck] in the | |
| 227 // check chain that is not generate at use site. | |
| 228 // | |
| 229 // For example: | |
| 230 // | |
| 231 // t1 = GeneratedAtUseSite instruction | |
| 232 // t2 = check(t1) | |
| 233 // t3 = check(t2) | |
| 234 // t4 = use(t3) | |
| 235 // t5 = use(t3) | |
| 236 // t6 = use(t2) | |
| 237 // | |
| 238 // The t1 is generate-at-use site, and the live-range must thus be on t2 and | |
| 239 // not on the checked instruction t1. | |
| 240 // When looking for the checkedInstructionOrNonGenerateAtUseSite of t3 we must | |
| 241 // return t2. | |
| 242 HInstruction checkedInstructionOrNonGenerateAtUseSite(HCheck check) { | |
| 243 var checked = check.checkedInput; | |
| 244 while (checked is HCheck) { | |
| 245 HInstruction next = checked.checkedInput; | |
| 246 if (generateAtUseSite.contains(next)) break; | |
| 247 checked = next; | |
| 248 } | |
| 249 return checked; | |
| 250 } | |
| 251 | |
| 252 void markAsLiveInEnvironment(HInstruction instruction, | |
| 253 LiveEnvironment environment) { | |
| 254 if (generateAtUseSite.contains(instruction)) { | |
| 255 markInputsAsLiveInEnvironment(instruction, environment); | |
| 256 } else { | |
| 257 environment.add(instruction, instructionId); | |
| 258 // Special case the HCheck instruction to mark the actual | |
| 259 // checked instruction live. The checked instruction and the | |
| 260 // [HCheck] will share the same live ranges. | |
| 261 if (instruction is HCheck) { | |
| 262 HCheck check = instruction; | |
| 263 HInstruction checked = checkedInstructionOrNonGenerateAtUseSite(check); | |
| 264 if (!generateAtUseSite.contains(checked)) { | |
| 265 environment.add(checked, instructionId); | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void removeFromEnvironment(HInstruction instruction, | |
| 272 LiveEnvironment environment) { | |
| 273 environment.remove(instruction, instructionId); | |
| 274 // Special case the HCheck instruction to have the same live | |
| 275 // interval as the instruction it is checking. | |
| 276 if (instruction is HCheck) { | |
| 277 HCheck check = instruction; | |
| 278 HInstruction checked = checkedInstructionOrNonGenerateAtUseSite(check); | |
| 279 if (!generateAtUseSite.contains(checked)) { | |
| 280 liveIntervals.putIfAbsent(checked, () => new LiveInterval()); | |
| 281 // Unconditionally force the live ranges of the HCheck to | |
| 282 // be the live ranges of the instruction it is checking. | |
| 283 liveIntervals[instruction] = | |
| 284 new LiveInterval.forCheck(instructionId, liveIntervals[checked]); | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void visitBasicBlock(HBasicBlock block) { | |
| 290 LiveEnvironment environment = | |
| 291 new LiveEnvironment(liveIntervals, instructionId); | |
| 292 | |
| 293 // If the control flow instruction in this block will actually be | |
| 294 // inlined in the codegen in the join block, we need to make | |
| 295 // whatever is used by that control flow instruction as live in | |
| 296 // the join block. | |
| 297 if (controlFlowOperators.contains(block.last)) { | |
| 298 HIf ifInstruction = block.last; | |
| 299 HBasicBlock joinBlock = ifInstruction.joinBlock; | |
| 300 if (generateAtUseSite.contains(joinBlock.phis.first)) { | |
| 301 markInputsAsLiveInEnvironment( | |
| 302 ifInstruction, liveInstructions[joinBlock]); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 // Add to the environment the liveIn of its successor, as well as | |
| 307 // the inputs of the phis of the successor that flow from this block. | |
| 308 for (int i = 0; i < block.successors.length; i++) { | |
| 309 HBasicBlock successor = block.successors[i]; | |
| 310 LiveEnvironment successorEnv = liveInstructions[successor]; | |
| 311 if (successorEnv != null) { | |
| 312 environment.mergeWith(successorEnv); | |
| 313 } else { | |
| 314 environment.addLoopMarker(successor, instructionId); | |
| 315 } | |
| 316 | |
| 317 int index = successor.predecessors.indexOf(block); | |
| 318 for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) { | |
| 319 markAsLiveInEnvironment(phi.inputs[index], environment); | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 // Iterate over all instructions to remove an instruction from the | |
| 324 // environment and add its inputs. | |
| 325 HInstruction instruction = block.last; | |
| 326 while (instruction != null) { | |
| 327 if (!generateAtUseSite.contains(instruction)) { | |
| 328 removeFromEnvironment(instruction, environment); | |
| 329 markInputsAsLiveInEnvironment(instruction, environment); | |
| 330 } | |
| 331 instructionId--; | |
| 332 instruction = instruction.previous; | |
| 333 } | |
| 334 | |
| 335 // We just remove the phis from the environment. The inputs of the | |
| 336 // phis will be put in the environment of the predecessors. | |
| 337 for (HPhi phi = block.phis.first; phi != null; phi = phi.next) { | |
| 338 if (!generateAtUseSite.contains(phi)) { | |
| 339 environment.remove(phi, instructionId); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 // Save the liveInstructions of that block. | |
| 344 environment.startId = instructionId + 1; | |
| 345 liveInstructions[block] = environment; | |
| 346 | |
| 347 // If the block is a loop header, we can remove the loop marker, | |
| 348 // because it will just recompute the loop phis. | |
| 349 // We also check if this loop header has any back edges. If not, | |
| 350 // we know there is no loop marker for it. | |
| 351 if (block.isLoopHeader() && block.predecessors.length > 1) { | |
| 352 updateLoopMarker(block); | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void updateLoopMarker(HBasicBlock header) { | |
| 357 LiveEnvironment env = liveInstructions[header]; | |
| 358 int lastId = env.loopMarkers[header]; | |
| 359 // Update all instructions that are liveIns in [header] to have a | |
| 360 // range that covers the loop. | |
| 361 env.liveInstructions.forEach((HInstruction instruction, int id) { | |
| 362 LiveInterval range = env.liveIntervals.putIfAbsent( | |
| 363 instruction, () => new LiveInterval()); | |
| 364 range.loopUpdate(env.startId, lastId); | |
| 365 env.liveInstructions[instruction] = lastId; | |
| 366 }); | |
| 367 | |
| 368 env.removeLoopMarker(header); | |
| 369 | |
| 370 // Update all liveIns set to contain the liveIns of [header]. | |
| 371 liveInstructions.forEach((HBasicBlock block, LiveEnvironment other) { | |
| 372 if (other.loopMarkers.containsKey(header)) { | |
| 373 env.liveInstructions.forEach((HInstruction instruction, int id) { | |
| 374 other.liveInstructions[instruction] = id; | |
| 375 }); | |
| 376 other.removeLoopMarker(header); | |
| 377 env.loopMarkers.forEach((k, v) { other.loopMarkers[k] = v; }); | |
| 378 } | |
| 379 }); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 /** | |
| 384 * Represents a copy from one instruction to another. The codegen | |
| 385 * also uses this class to represent a copy from one variable to | |
| 386 * another. | |
| 387 */ | |
| 388 class Copy { | |
| 389 final source; | |
| 390 final destination; | |
| 391 Copy(this.source, this.destination); | |
| 392 String toString() => '$destination <- $source'; | |
| 393 } | |
| 394 | |
| 395 /** | |
| 396 * A copy handler contains the copies that a basic block needs to do | |
| 397 * after executing all its instructions. | |
| 398 */ | |
| 399 class CopyHandler { | |
| 400 /** | |
| 401 * The copies from an instruction to a phi of the successor. | |
| 402 */ | |
| 403 final List<Copy> copies; | |
| 404 | |
| 405 /** | |
| 406 * Assignments from an instruction that does not need a name (e.g. a | |
| 407 * constant) to the phi of a successor. | |
| 408 */ | |
| 409 final List<Copy> assignments; | |
| 410 | |
| 411 CopyHandler() | |
| 412 : copies = new List<Copy>(), | |
| 413 assignments = new List<Copy>(); | |
| 414 | |
| 415 void addCopy(HInstruction source, HInstruction destination) { | |
| 416 copies.add(new Copy(source, destination)); | |
| 417 } | |
| 418 | |
| 419 void addAssignment(HInstruction source, HInstruction destination) { | |
| 420 assignments.add(new Copy(source, destination)); | |
| 421 } | |
| 422 | |
| 423 String toString() => 'Copies: $copies, assignments: $assignments'; | |
| 424 bool get isEmpty => copies.isEmpty && assignments.isEmpty; | |
| 425 } | |
| 426 | |
| 427 /** | |
| 428 * Contains the mapping between instructions and their names for code | |
| 429 * generation, as well as the [CopyHandler] for each basic block. | |
| 430 */ | |
| 431 class VariableNames { | |
| 432 final Map<HInstruction, String> ownName; | |
| 433 final Map<HBasicBlock, CopyHandler> copyHandlers; | |
| 434 | |
| 435 // Used to control heuristic that determines how local variables are declared. | |
| 436 final Set<String> allUsedNames; | |
| 437 /** | |
| 438 * Name that is used as a temporary to break cycles in | |
| 439 * parallel copies. We make sure this name is not being used | |
| 440 * anywhere by reserving it when we allocate names for instructions. | |
| 441 */ | |
| 442 final String swapTemp; | |
| 443 | |
| 444 String getSwapTemp() { | |
| 445 allUsedNames.add(swapTemp); | |
| 446 return swapTemp; | |
| 447 } | |
| 448 | |
| 449 VariableNames() | |
| 450 : ownName = new Map<HInstruction, String>(), | |
| 451 copyHandlers = new Map<HBasicBlock, CopyHandler>(), | |
| 452 allUsedNames = new Set<String>(), | |
| 453 swapTemp = 't0'; | |
| 454 | |
| 455 int get numberOfVariables => allUsedNames.length; | |
| 456 | |
| 457 String getName(HInstruction instruction) { | |
| 458 return ownName[instruction]; | |
| 459 } | |
| 460 | |
| 461 CopyHandler getCopyHandler(HBasicBlock block) { | |
| 462 return copyHandlers[block]; | |
| 463 } | |
| 464 | |
| 465 void addNameUsed(String name) { | |
| 466 allUsedNames.add(name); | |
| 467 } | |
| 468 | |
| 469 bool hasName(HInstruction instruction) => ownName.containsKey(instruction); | |
| 470 | |
| 471 void addCopy(HBasicBlock block, HInstruction source, HPhi destination) { | |
| 472 CopyHandler handler = | |
| 473 copyHandlers.putIfAbsent(block, () => new CopyHandler()); | |
| 474 handler.addCopy(source, destination); | |
| 475 } | |
| 476 | |
| 477 void addAssignment(HBasicBlock block, HInstruction source, HPhi destination) { | |
| 478 CopyHandler handler = | |
| 479 copyHandlers.putIfAbsent(block, () => new CopyHandler()); | |
| 480 handler.addAssignment(source, destination); | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 /** | |
| 485 * Allocates variable names for instructions, making sure they don't collide. | |
| 486 */ | |
| 487 class VariableNamer { | |
| 488 final VariableNames names; | |
| 489 final Compiler compiler; | |
| 490 final Set<String> usedNames; | |
| 491 final List<String> freeTemporaryNames; | |
| 492 int temporaryIndex = 0; | |
| 493 static final RegExp regexp = new RegExp('t[0-9]+'); | |
| 494 | |
| 495 VariableNamer(LiveEnvironment environment, | |
| 496 this.names, | |
| 497 this.compiler) | |
| 498 : usedNames = new Set<String>(), | |
| 499 freeTemporaryNames = new List<String>() { | |
| 500 // [VariableNames.swapTemp] is used when there is a cycle in a copy handler. | |
| 501 // Therefore we make sure no one uses it. | |
| 502 usedNames.add(names.swapTemp); | |
| 503 | |
| 504 // All liveIns instructions must have a name at this point, so we | |
| 505 // add them to the list of used names. | |
| 506 environment.liveInstructions.forEach((HInstruction instruction, int index) { | |
| 507 String name = names.getName(instruction); | |
| 508 if (name != null) { | |
| 509 usedNames.add(name); | |
| 510 names.addNameUsed(name); | |
| 511 } | |
| 512 }); | |
| 513 } | |
| 514 | |
| 515 String allocateWithHint(String originalName) { | |
| 516 int i = 0; | |
| 517 JavaScriptBackend backend = compiler.backend; | |
| 518 String name = backend.namer.safeVariableName(originalName); | |
| 519 while (usedNames.contains(name)) { | |
| 520 name = backend.namer.safeVariableName('$originalName${i++}'); | |
| 521 } | |
| 522 return name; | |
| 523 } | |
| 524 | |
| 525 String allocateTemporary() { | |
| 526 while (!freeTemporaryNames.isEmpty) { | |
| 527 String name = freeTemporaryNames.removeLast(); | |
| 528 if (!usedNames.contains(name)) return name; | |
| 529 } | |
| 530 String name = 't${temporaryIndex++}'; | |
| 531 while (usedNames.contains(name)) name = 't${temporaryIndex++}'; | |
| 532 return name; | |
| 533 } | |
| 534 | |
| 535 HPhi firstPhiUserWithElement(HInstruction instruction) { | |
| 536 for (HInstruction user in instruction.usedBy) { | |
| 537 if (user is HPhi && user.sourceElement != null) { | |
| 538 return user; | |
| 539 } | |
| 540 } | |
| 541 return null; | |
| 542 } | |
| 543 | |
| 544 String allocateName(HInstruction instruction) { | |
| 545 String name; | |
| 546 if (instruction is HCheck) { | |
| 547 // Special case this instruction to use the name of its | |
| 548 // input if it has one. | |
| 549 var temp = instruction; | |
| 550 do { | |
| 551 temp = temp.checkedInput; | |
| 552 name = names.ownName[temp]; | |
| 553 } while (name == null && temp is HCheck); | |
| 554 if (name != null) return addAllocatedName(instruction, name); | |
| 555 } | |
| 556 | |
| 557 if (instruction.sourceElement != null) { | |
| 558 name = allocateWithHint(instruction.sourceElement.name); | |
| 559 } else { | |
| 560 // We could not find an element for the instruction. If the | |
| 561 // instruction is used by a phi, try to use the name of the phi. | |
| 562 // Otherwise, just allocate a temporary name. | |
| 563 HPhi phi = firstPhiUserWithElement(instruction); | |
| 564 if (phi != null) { | |
| 565 name = allocateWithHint(phi.sourceElement.name); | |
| 566 } else { | |
| 567 name = allocateTemporary(); | |
| 568 } | |
| 569 } | |
| 570 return addAllocatedName(instruction, name); | |
| 571 } | |
| 572 | |
| 573 String addAllocatedName(HInstruction instruction, String name) { | |
| 574 usedNames.add(name); | |
| 575 names.addNameUsed(name); | |
| 576 names.ownName[instruction] = name; | |
| 577 return name; | |
| 578 } | |
| 579 | |
| 580 /** | |
| 581 * Frees [instruction]'s name so it can be used for other instructions. | |
| 582 */ | |
| 583 void freeName(HInstruction instruction) { | |
| 584 String ownName = names.ownName[instruction]; | |
| 585 if (ownName != null) { | |
| 586 // We check if we have already looked for temporary names | |
| 587 // because if we haven't, chances are the temporary we allocate | |
| 588 // in this block can match a phi with the same name in the | |
| 589 // successor block. | |
| 590 if (temporaryIndex != 0 && regexp.hasMatch(ownName)) { | |
| 591 freeTemporaryNames.add(ownName); | |
| 592 } | |
| 593 usedNames.remove(ownName); | |
| 594 } | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 /** | |
| 599 * Visits all blocks in the graph, sets names to instructions, and | |
| 600 * creates the [CopyHandler] for each block. This class needs to have | |
| 601 * the liveIns set as well as all the live intervals of instructions. | |
| 602 * It visits the graph in dominator order, so that at each entry of a | |
| 603 * block, the instructions in its liveIns set have names. | |
| 604 * | |
| 605 * When visiting a block, it goes through all instructions. For each | |
| 606 * instruction, it frees the names of the inputs that die at that | |
| 607 * instruction, and allocates a name to the instruction. For each phi, | |
| 608 * it adds a copy to the CopyHandler of the corresponding predecessor. | |
| 609 */ | |
| 610 class SsaVariableAllocator extends HBaseVisitor { | |
| 611 | |
| 612 final Compiler compiler; | |
| 613 final Map<HBasicBlock, LiveEnvironment> liveInstructions; | |
| 614 final Map<HInstruction, LiveInterval> liveIntervals; | |
| 615 final Set<HInstruction> generateAtUseSite; | |
| 616 | |
| 617 final VariableNames names; | |
| 618 | |
| 619 SsaVariableAllocator(this.compiler, | |
| 620 this.liveInstructions, | |
| 621 this.liveIntervals, | |
| 622 this.generateAtUseSite) | |
| 623 : this.names = new VariableNames(); | |
| 624 | |
| 625 void visitGraph(HGraph graph) { | |
| 626 visitDominatorTree(graph); | |
| 627 } | |
| 628 | |
| 629 void visitBasicBlock(HBasicBlock block) { | |
| 630 VariableNamer namer = new VariableNamer( | |
| 631 liveInstructions[block], names, compiler); | |
| 632 | |
| 633 block.forEachPhi((HPhi phi) { | |
| 634 handlePhi(phi, namer); | |
| 635 }); | |
| 636 | |
| 637 block.forEachInstruction((HInstruction instruction) { | |
| 638 handleInstruction(instruction, namer); | |
| 639 }); | |
| 640 } | |
| 641 | |
| 642 /** | |
| 643 * Returns whether [instruction] needs a name. Instructions that | |
| 644 * have no users or that are generated at use site do not need a name. | |
| 645 */ | |
| 646 bool needsName(instruction) { | |
| 647 if (instruction is HThis) return false; | |
| 648 if (instruction is HParameterValue) return true; | |
| 649 if (instruction.usedBy.isEmpty) return false; | |
| 650 if (generateAtUseSite.contains(instruction)) return false; | |
| 651 return !instruction.nonCheck().isCodeMotionInvariant(); | |
| 652 } | |
| 653 | |
| 654 /** | |
| 655 * Returns whether [instruction] dies at the instruction [at]. | |
| 656 */ | |
| 657 bool diesAt(HInstruction instruction, HInstruction at) { | |
| 658 LiveInterval atInterval = liveIntervals[at]; | |
| 659 LiveInterval instructionInterval = liveIntervals[instruction]; | |
| 660 int start = atInterval.start; | |
| 661 return instructionInterval.diesAt(start); | |
| 662 } | |
| 663 | |
| 664 void freeUsedNamesAt(HInstruction instruction, | |
| 665 HInstruction at, | |
| 666 VariableNamer namer) { | |
| 667 if (needsName(instruction)) { | |
| 668 if (diesAt(instruction, at)) { | |
| 669 namer.freeName(instruction); | |
| 670 } | |
| 671 } else if (generateAtUseSite.contains(instruction)) { | |
| 672 // If the instruction is generated at use site, then all its | |
| 673 // inputs may also die at [at]. | |
| 674 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | |
| 675 HInstruction input = instruction.inputs[i]; | |
| 676 freeUsedNamesAt(input, at, namer); | |
| 677 } | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 void handleInstruction(HInstruction instruction, VariableNamer namer) { | |
| 682 if (generateAtUseSite.contains(instruction)) { | |
| 683 assert(!liveIntervals.containsKey(instruction)); | |
| 684 return; | |
| 685 } | |
| 686 | |
| 687 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | |
| 688 HInstruction input = instruction.inputs[i]; | |
| 689 freeUsedNamesAt(input, instruction, namer); | |
| 690 } | |
| 691 | |
| 692 if (needsName(instruction)) { | |
| 693 namer.allocateName(instruction); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 void handlePhi(HPhi phi, VariableNamer namer) { | |
| 698 if (!needsName(phi)) return; | |
| 699 | |
| 700 for (int i = 0; i < phi.inputs.length; i++) { | |
| 701 HInstruction input = phi.inputs[i]; | |
| 702 HBasicBlock predecessor = phi.block.predecessors[i]; | |
| 703 // A [HTypeKnown] instruction never has a name, but its checked | |
| 704 // input might, therefore we need to do a copy instead of an | |
| 705 // assignment. | |
| 706 while (input is HTypeKnown) input = input.inputs[0]; | |
| 707 if (!needsName(input)) { | |
| 708 names.addAssignment(predecessor, input, phi); | |
| 709 } else { | |
| 710 names.addCopy(predecessor, input, phi); | |
| 711 } | |
| 712 } | |
| 713 | |
| 714 namer.allocateName(phi); | |
| 715 } | |
| 716 } | |
| OLD | NEW |