1 // Copyright (c) 2016, the R8 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 package com.android.tools.r8.ir.code; 5 6 import com.android.tools.r8.errors.CompilationError; 7 import com.android.tools.r8.graph.DebugLocalInfo; 8 import com.android.tools.r8.graph.DexType; 9 import com.android.tools.r8.ir.conversion.DexBuilder; 10 import com.android.tools.r8.ir.conversion.IRBuilder; 11 import com.android.tools.r8.utils.CfgPrinter; 12 import com.android.tools.r8.utils.ListUtils; 13 import com.android.tools.r8.utils.StringUtils; 14 import com.android.tools.r8.utils.StringUtils.BraceType; 15 import com.google.common.collect.ImmutableList; 16 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; 17 import java.util.ArrayList; 18 import java.util.Arrays; 19 import java.util.Collection; 20 import java.util.Collections; 21 import java.util.HashMap; 22 import java.util.LinkedList; 23 import java.util.List; 24 import java.util.ListIterator; 25 import java.util.Map; 26 import java.util.Map.Entry; 27 import java.util.Set; 28 import java.util.TreeSet; 29 import java.util.function.Consumer; 30 import java.util.function.Function; 31 32 /** 33 * Basic block abstraction. 34 */ 35 public class BasicBlock { 36 37 private Int2ReferenceMap<DebugLocalInfo> localsAtEntry; 38 setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry)39 public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) { 40 this.localsAtEntry = localsAtEntry; 41 } 42 getLocalsAtEntry()43 public Int2ReferenceMap<DebugLocalInfo> getLocalsAtEntry() { 44 return localsAtEntry; 45 } 46 47 public enum ThrowingInfo { 48 NO_THROW, CAN_THROW 49 } 50 51 public enum EdgeType { 52 NON_EDGE, 53 NORMAL, 54 EXCEPTIONAL 55 } 56 57 public static class Pair implements Comparable<Pair> { 58 59 public BasicBlock first; 60 public BasicBlock second; 61 Pair(BasicBlock first, BasicBlock second)62 public Pair(BasicBlock first, BasicBlock second) { 63 this.first = first; 64 this.second = second; 65 } 66 67 @Override compareTo(Pair o)68 public int compareTo(Pair o) { 69 if (first != o.first) { 70 return first.getNumber() - o.first.getNumber(); 71 } 72 if (second != o.second) { 73 return second.getNumber() - o.second.getNumber(); 74 } 75 return 0; 76 } 77 } 78 79 private final List<BasicBlock> successors = new ArrayList<>(); 80 private final List<BasicBlock> predecessors = new ArrayList<>(); 81 82 // Catch handler information about which successors are catch handlers and what their guards are. 83 private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES; 84 85 private LinkedList<Instruction> instructions = new LinkedList<>(); 86 private int number = -1; 87 private List<Phi> phis = new ArrayList<>(); 88 89 // State used during SSA construction. The SSA construction is based on the paper: 90 // 91 // "Simple and Efficient Construction of Static Single Assignment Form" 92 // http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf 93 // 94 // A basic block is filled when local value numbering is complete for that block. 95 // A basic block is sealed when all predecessor blocks have been filled. 96 // 97 // Therefore, for a sealed block we can always search backwards to find reaching values 98 // in predecessor blocks. 99 private boolean filled = false; 100 private boolean sealed = false; 101 private Map<Integer, Phi> incompletePhis = new HashMap<>(); 102 private int estimatedPredecessorsCount = 0; 103 private int unfilledPredecessorsCount = 0; 104 105 // State used for basic block sorting and tracing. 106 private int color = 0; 107 108 // Map of registers to current SSA value. Used during SSA numbering and cleared once filled. 109 private Map<Integer, Value> currentDefinitions = new HashMap<>(); 110 getSuccessors()111 public List<BasicBlock> getSuccessors() { 112 return successors; 113 } 114 getNormalSucessors()115 public List<BasicBlock> getNormalSucessors() { 116 if (!hasCatchHandlers()) { 117 return successors; 118 } 119 Set<Integer> handlers = catchHandlers.getUniqueTargets(); 120 ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder(); 121 for (int i = 0; i < successors.size(); i++) { 122 if (!handlers.contains(i)) { 123 normals.add(successors.get(i)); 124 } 125 } 126 return normals.build(); 127 } 128 getPredecessors()129 public List<BasicBlock> getPredecessors() { 130 return predecessors; 131 } 132 getNormalPredecessors()133 public List<BasicBlock> getNormalPredecessors() { 134 ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder(); 135 for (BasicBlock predecessor : predecessors) { 136 if (!predecessor.hasCatchSuccessor(this)) { 137 normals.add(predecessor); 138 } 139 } 140 return normals.build(); 141 } 142 removeSuccessor(BasicBlock block)143 public void removeSuccessor(BasicBlock block) { 144 int index = successors.indexOf(block); 145 assert index >= 0 : "removeSuccessor did not find the successor to remove"; 146 removeSuccessorsByIndex(Arrays.asList(index)); 147 } 148 removePredecessor(BasicBlock block)149 public void removePredecessor(BasicBlock block) { 150 int index = predecessors.indexOf(block); 151 assert index >= 0 : "removePredecessor did not find the predecessor to remove"; 152 predecessors.remove(index); 153 if (phis != null) { 154 for (Phi phi : getPhis()) { 155 phi.removeOperand(index); 156 } 157 // Collect and remove trivial phis after block removal. 158 List<Phi> trivials = new ArrayList<>(); 159 for (Phi phi : getPhis()) { 160 if (phi.isTrivialPhi()) { 161 trivials.add(phi); 162 } 163 } 164 for (Phi phi : trivials) { 165 phi.removeTrivialPhi(); 166 } 167 } 168 } 169 swapSuccessors(int x, int y)170 private void swapSuccessors(int x, int y) { 171 assert x != y; 172 if (hasCatchHandlers()) { 173 List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets()); 174 for (int i = 0; i < targets.size(); i++) { 175 if (targets.get(i) == x) { 176 targets.set(i, y); 177 } else if (targets.get(i) == y) { 178 targets.set(i, x); 179 } 180 } 181 catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets); 182 } 183 BasicBlock tmp = successors.get(x); 184 successors.set(x, successors.get(y)); 185 successors.set(y, tmp); 186 } 187 replaceSuccessor(BasicBlock block, BasicBlock newBlock)188 public void replaceSuccessor(BasicBlock block, BasicBlock newBlock) { 189 assert successors.contains(block) : "attempt to replace non-existent successor"; 190 191 if (successors.contains(newBlock)) { 192 int indexOfOldBlock = successors.indexOf(block); 193 int indexOfNewBlock = successors.indexOf(newBlock); 194 195 // Always rewrite catch handlers. 196 if (hasCatchHandlers()) { 197 List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets()); 198 for (int i = 0; i < targets.size(); i++) { 199 if (targets.get(i) == indexOfOldBlock) { 200 targets.set(i, indexOfNewBlock); 201 } 202 if (targets.get(i) > indexOfOldBlock) { 203 targets.set(i, targets.get(i) - 1); 204 } 205 } 206 catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets); 207 } 208 209 // Check if the replacement influences jump targets and rewrite as needed. 210 if (exit().isGoto()) { 211 if (indexOfOldBlock == successors.size() - 1 && indexOfNewBlock != successors.size() - 2) { 212 // Replacing the goto target and the new block will not become the goto target. 213 // We perform a swap to get the new block into the goto target position. 214 swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock); 215 } 216 } else if (exit().isIf()) { 217 if (indexOfNewBlock >= successors.size() - 2 && indexOfOldBlock >= successors.size() - 2) { 218 // New and old are true target and fallthrough, replace last instruction with a goto. 219 Instruction instruction = getInstructions().removeLast(); 220 for (Value value : instruction.inValues()) { 221 if (value.hasUsersInfo()) { 222 value.removeUser(instruction); 223 } 224 } 225 Instruction exit = new Goto(); 226 exit.setBlock(this); 227 getInstructions().addLast(exit); 228 } else if (indexOfOldBlock >= successors.size() - 2) { 229 // Old is either true or fallthrough and we need to swap the new block into the right 230 // position to become that target. 231 swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock); 232 } 233 } else if (exit().isSwitch()) { 234 // Rewrite fallthrough and case target indices. 235 Switch exit = exit().asSwitch(); 236 if (exit.getFallthroughBlockIndex() == indexOfOldBlock) { 237 exit.setFallthroughBlockIndex(indexOfNewBlock); 238 } 239 if (exit.getFallthroughBlockIndex() > indexOfOldBlock) { 240 exit.setFallthroughBlockIndex(exit.getFallthroughBlockIndex() - 1); 241 } 242 int[] indices = exit.targetBlockIndices(); 243 for (int i = 0; i < indices.length; i++) { 244 if (indices[i] == indexOfOldBlock) { 245 indices[i] = indexOfNewBlock; 246 } 247 if (indices[i] > indexOfOldBlock) { 248 indices[i] = indices[i] - 1; 249 } 250 } 251 } 252 253 // Remove the replaced successor. 254 boolean removed = successors.remove(block); 255 assert removed; 256 } else { 257 // If the new block is not a successor we don't have to rewrite indices or instructions 258 // and we can just replace the old successor with the new one. 259 for (int i = 0; i < successors.size(); i++) { 260 if (successors.get(i) == block) { 261 successors.set(i, newBlock); 262 return; 263 } 264 } 265 } 266 } 267 replacePredecessor(BasicBlock block, BasicBlock newBlock)268 public void replacePredecessor(BasicBlock block, BasicBlock newBlock) { 269 for (int i = 0; i < predecessors.size(); i++) { 270 if (predecessors.get(i) == block) { 271 predecessors.set(i, newBlock); 272 return; 273 } 274 } 275 assert false : "replaceSuccessor did not find the predecessor to replace"; 276 } 277 swapSuccessorsByIndex(int index1, int index2)278 public void swapSuccessorsByIndex(int index1, int index2) { 279 BasicBlock t = successors.get(index1); 280 successors.set(index1, successors.get(index2)); 281 successors.set(index2, t); 282 } 283 removeSuccessorsByIndex(List<Integer> successorsToRemove)284 public void removeSuccessorsByIndex(List<Integer> successorsToRemove) { 285 if (successorsToRemove.isEmpty()) { 286 return; 287 } 288 List<BasicBlock> copy = new ArrayList<>(successors); 289 successors.clear(); 290 int current = 0; 291 for (int i : successorsToRemove) { 292 successors.addAll(copy.subList(current, i)); 293 current = i + 1; 294 } 295 successors.addAll(copy.subList(current, copy.size())); 296 297 if (hasCatchHandlers()) { 298 int size = catchHandlers.size(); 299 List<DexType> guards = new ArrayList<>(size); 300 List<Integer> targets = new ArrayList<>(size); 301 current = 0; 302 for (int i = 0; i < catchHandlers.getAllTargets().size(); i++) { 303 if (successorsToRemove.contains(catchHandlers.getAllTargets().get(i))) { 304 guards.addAll(catchHandlers.getGuards().subList(current, i)); 305 targets.addAll(catchHandlers.getAllTargets().subList(current, i)); 306 current = i + 1; 307 } 308 } 309 if (guards.isEmpty()) { 310 catchHandlers = CatchHandlers.EMPTY_INDICES; 311 } else { 312 catchHandlers = new CatchHandlers<>(guards, targets); 313 } 314 } 315 } 316 removePredecessorsByIndex(List<Integer> predecessorsToRemove)317 public void removePredecessorsByIndex(List<Integer> predecessorsToRemove) { 318 if (predecessorsToRemove.isEmpty()) { 319 return; 320 } 321 List<BasicBlock> copy = new ArrayList<>(predecessors); 322 predecessors.clear(); 323 int current = 0; 324 for (int i : predecessorsToRemove) { 325 predecessors.addAll(copy.subList(current, i)); 326 current = i + 1; 327 } 328 predecessors.addAll(copy.subList(current, copy.size())); 329 } 330 removePhisByIndex(List<Integer> predecessorsToRemove)331 public void removePhisByIndex(List<Integer> predecessorsToRemove) { 332 for (Phi phi : phis) { 333 phi.removeOperandsByIndex(predecessorsToRemove); 334 } 335 } 336 getPhis()337 public List<Phi> getPhis() { 338 return phis; 339 } 340 setPhis(List<Phi> phis)341 public void setPhis(List<Phi> phis) { 342 this.phis = phis; 343 } 344 isFilled()345 public boolean isFilled() { 346 return filled; 347 } 348 setFilledForTesting()349 void setFilledForTesting() { 350 filled = true; 351 } 352 hasCatchHandlers()353 public boolean hasCatchHandlers() { 354 assert catchHandlers != null; 355 return !catchHandlers.isEmpty(); 356 } 357 getNumber()358 public int getNumber() { 359 assert number >= 0; 360 return number; 361 } 362 setNumber(int number)363 public void setNumber(int number) { 364 assert number >= 0; 365 this.number = number; 366 } 367 getInstructions()368 public LinkedList<Instruction> getInstructions() { 369 return instructions; 370 } 371 entry()372 public Instruction entry() { 373 return instructions.get(0); 374 } 375 exit()376 public JumpInstruction exit() { 377 assert filled; 378 assert instructions.get(instructions.size() - 1).isJumpInstruction(); 379 return instructions.get(instructions.size() - 1).asJumpInstruction(); 380 } 381 clearUserInfo()382 public void clearUserInfo() { 383 phis = null; 384 instructions.forEach(Instruction::clearUserInfo); 385 } 386 buildDex(DexBuilder builder)387 public void buildDex(DexBuilder builder) { 388 for (Instruction instruction : instructions) { 389 instruction.buildDex(builder); 390 } 391 } 392 mark()393 public void mark() { 394 assert color == 0; 395 color = 1; 396 } 397 clearMark()398 public void clearMark() { 399 color = 0; 400 } 401 isMarked()402 public boolean isMarked() { 403 return color == 1; 404 } 405 setColor(int color)406 public void setColor(int color) { 407 this.color = color; 408 } 409 getColor()410 public int getColor() { 411 return color; 412 } 413 hasColor(int color)414 public boolean hasColor(int color) { 415 return this.color == color; 416 } 417 incrementUnfilledPredecessorCount()418 public void incrementUnfilledPredecessorCount() { 419 ++unfilledPredecessorsCount; 420 ++estimatedPredecessorsCount; 421 } 422 decrementUnfilledPredecessorCount(int n)423 public void decrementUnfilledPredecessorCount(int n) { 424 unfilledPredecessorsCount -= n; 425 estimatedPredecessorsCount -= n; 426 } 427 decrementUnfilledPredecessorCount()428 public void decrementUnfilledPredecessorCount() { 429 --unfilledPredecessorsCount; 430 --estimatedPredecessorsCount; 431 } 432 verifyFilledPredecessors()433 public boolean verifyFilledPredecessors() { 434 assert estimatedPredecessorsCount == predecessors.size(); 435 assert unfilledPredecessorsCount == 0; 436 return true; 437 } 438 addPhi(Phi phi)439 public void addPhi(Phi phi) { 440 phis.add(phi); 441 } 442 removePhi(Phi phi)443 public void removePhi(Phi phi) { 444 phis.remove(phi); 445 } 446 add(Instruction next)447 public void add(Instruction next) { 448 assert !isFilled(); 449 instructions.add(next); 450 next.setBlock(this); 451 } 452 close(IRBuilder builder)453 public void close(IRBuilder builder) { 454 assert !isFilled(); 455 assert !instructions.isEmpty(); 456 filled = true; 457 sealed = unfilledPredecessorsCount == 0; 458 assert exit().isJumpInstruction(); 459 assert verifyNoValuesAfterThrowingInstruction(); 460 for (BasicBlock successor : successors) { 461 successor.filledPredecessor(builder); 462 } 463 } 464 link(BasicBlock successor)465 public void link(BasicBlock successor) { 466 assert !successors.contains(successor); 467 assert !successor.predecessors.contains(this); 468 successors.add(successor); 469 successor.predecessors.add(this); 470 } 471 allPredecessorsDominated(BasicBlock block, DominatorTree dominator)472 private boolean allPredecessorsDominated(BasicBlock block, DominatorTree dominator) { 473 for (BasicBlock pred : block.predecessors) { 474 if (!dominator.dominatedBy(pred, block)) { 475 return false; 476 } 477 } 478 return true; 479 } 480 blocksClean(List<BasicBlock> blocks)481 private boolean blocksClean(List<BasicBlock> blocks) { 482 blocks.forEach((b) -> { 483 assert b.predecessors.size() == 0; 484 assert b.successors.size() == 0; 485 }); 486 return true; 487 } 488 489 /** 490 * Unlinks this block from a single predecessor. 491 * 492 * @return returns the unlinked predecessor. 493 */ unlinkSinglePredecessor()494 public BasicBlock unlinkSinglePredecessor() { 495 assert predecessors.size() == 1; 496 assert predecessors.get(0).successors.size() == 1; 497 BasicBlock unlinkedBlock = predecessors.get(0); 498 predecessors.get(0).successors.clear(); 499 predecessors.clear(); 500 return unlinkedBlock; 501 } 502 503 /** 504 * Unlinks this block from a single normal successor. 505 * 506 * @return Returns the unlinked successor. 507 */ unlinkSingleSuccessor()508 public BasicBlock unlinkSingleSuccessor() { 509 assert !hasCatchHandlers(); 510 assert successors.size() == 1; 511 assert successors.get(0).predecessors.size() == 1; 512 BasicBlock unlinkedBlock = successors.get(0); 513 successors.get(0).predecessors.clear(); 514 successors.clear(); 515 return unlinkedBlock; 516 } 517 518 /** 519 * Unlinks this block from a single predecessor and successor. 520 * 521 * @return Returns the unlinked successor 522 */ unlinkSingle()523 public BasicBlock unlinkSingle() { 524 unlinkSinglePredecessor(); 525 return unlinkSingleSuccessor(); 526 } 527 unlink(BasicBlock successor, DominatorTree dominator)528 public List<BasicBlock> unlink(BasicBlock successor, DominatorTree dominator) { 529 assert successors.contains(successor); 530 assert successor.predecessors.contains(this); 531 List<BasicBlock> removedBlocks = new ArrayList<>(); 532 TreeSet<Pair> worklist = new TreeSet<>(); 533 worklist.add(new Pair(this, successor)); 534 while (!worklist.isEmpty()) { 535 Pair pair = worklist.pollFirst(); 536 BasicBlock pred = pair.first; 537 BasicBlock succ = pair.second; 538 assert pred.successors.contains(succ); 539 assert succ.predecessors.contains(pred); 540 int size = pred.successors.size(); 541 pred.removeSuccessor(succ); 542 assert size == pred.successors.size() + 1; 543 size = succ.predecessors.size(); 544 succ.removePredecessor(pred); 545 assert size == succ.predecessors.size() + 1; 546 // A predecessor has been removed. If all remaining predecessors are dominated by this block 547 // schedule it for removal, as it is no longer reachable. 548 if (allPredecessorsDominated(succ, dominator)) { 549 removedBlocks.add(succ); 550 for (BasicBlock block : succ.successors) { 551 worklist.add(new Pair(succ, block)); 552 } 553 for (Instruction instruction : succ.getInstructions()) { 554 for (Value value : instruction.inValues) { 555 value.removeUser(instruction); 556 } 557 for (Value value : instruction.getDebugValues()) { 558 value.removeDebugUser(instruction); 559 } 560 Value previousLocalValue = instruction.getPreviousLocalValue(); 561 if (previousLocalValue != null) { 562 previousLocalValue.removeDebugUser(instruction); 563 } 564 } 565 } 566 } 567 assert blocksClean(removedBlocks); 568 return removedBlocks; 569 } 570 linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets)571 public void linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets) { 572 List<Integer> successorIndexes = new ArrayList<>(targets.size()); 573 for (BasicBlock target : targets) { 574 int index = successors.indexOf(target); 575 if (index < 0) { 576 index = successors.size(); 577 link(target); 578 } 579 successorIndexes.add(index); 580 } 581 catchHandlers = new CatchHandlers<>(guards, successorIndexes); 582 } 583 clearCurrentDefinitions()584 public void clearCurrentDefinitions() { 585 currentDefinitions = null; 586 for (Phi phi : getPhis()) { 587 phi.clearDefinitionsUsers(); 588 } 589 } 590 591 // The proper incoming register for a catch successor (that is otherwise shadowed by the out-value 592 // of a throwing instruction) is stored at the negative register-index in the definitions map. 593 // (See readCurrentDefinition/writeCurrentDefinition/updateCurrentDefinition). onThrowValueRegister(int register)594 private int onThrowValueRegister(int register) { 595 return -(register + 1); 596 } 597 readOnThrowValue(int register, EdgeType readingEdge)598 private Value readOnThrowValue(int register, EdgeType readingEdge) { 599 if (readingEdge == EdgeType.EXCEPTIONAL) { 600 return currentDefinitions.get(onThrowValueRegister(register)); 601 } 602 return null; 603 } 604 isOnThrowValue(int register, EdgeType readingEdge)605 private boolean isOnThrowValue(int register, EdgeType readingEdge) { 606 return readOnThrowValue(register, readingEdge) != null; 607 } 608 readCurrentDefinition(int register, EdgeType readingEdge)609 public Value readCurrentDefinition(int register, EdgeType readingEdge) { 610 // If the block reading the current definition is a catch successor, then we must return the 611 // previous value of the throwing-instructions outgoing register if any. 612 Value result = readOnThrowValue(register, readingEdge); 613 if (result != null) { 614 return result == Value.UNDEFINED ? null : result; 615 } 616 return currentDefinitions.get(register); 617 } 618 updateCurrentDefinition(int register, Value value, EdgeType readingEdge)619 public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) { 620 // If the reading/writing block is a catch successor, possibly update the on-throw value. 621 if (isOnThrowValue(register, readingEdge)) { 622 register = onThrowValueRegister(register); 623 } 624 // We keep track of all users of phis so that we can update all users during 625 // trivial phi elimination. We only rewrite phi values during IR construction, so 626 // we only need to record definition users for phis. 627 Value previousValue = currentDefinitions.get(register); 628 if (value.isPhi()) { 629 value.asPhi().addDefinitionsUser(currentDefinitions); 630 } 631 assert verifyOnThrowWrite(register); 632 currentDefinitions.put(register, value); 633 // We have replaced one occurrence of value in currentDefinitions. There could be 634 // other occurrences. We only remove currentDefinitions from the set of users 635 // of the phi if we have removed all occurrences. 636 if (previousValue != null && 637 previousValue.isPhi() && 638 !currentDefinitions.values().contains(previousValue)) { 639 previousValue.asPhi().removeDefinitionsUser(currentDefinitions); 640 } 641 } 642 writeCurrentDefinition(int register, Value value, ThrowingInfo throwing)643 public void writeCurrentDefinition(int register, Value value, ThrowingInfo throwing) { 644 // If this write is dependent on not throwing, we move the existing value to its negative index 645 // so that it can be read by catch successors. 646 if (throwing == ThrowingInfo.CAN_THROW) { 647 Value previous = currentDefinitions.get(register); 648 assert verifyOnThrowWrite(register); 649 currentDefinitions.put(onThrowValueRegister(register), 650 previous == null ? Value.UNDEFINED : previous); 651 } 652 updateCurrentDefinition(register, value, EdgeType.NON_EDGE); 653 } 654 filledPredecessor(IRBuilder builder)655 public void filledPredecessor(IRBuilder builder) { 656 assert unfilledPredecessorsCount > 0; 657 if (--unfilledPredecessorsCount == 0) { 658 assert estimatedPredecessorsCount == predecessors.size(); 659 for (Entry<Integer, Phi> entry : incompletePhis.entrySet()) { 660 int register = entry.getKey(); 661 if (register < 0) { 662 register = onThrowValueRegister(register); 663 } 664 entry.getValue().addOperands(builder, register); 665 } 666 sealed = true; 667 incompletePhis.clear(); 668 } 669 } 670 getEdgeType(BasicBlock successor)671 public EdgeType getEdgeType(BasicBlock successor) { 672 assert successors.indexOf(successor) >= 0; 673 return hasCatchSuccessor(successor) ? EdgeType.EXCEPTIONAL : EdgeType.NORMAL; 674 } 675 hasCatchSuccessor(BasicBlock block)676 public boolean hasCatchSuccessor(BasicBlock block) { 677 if (!hasCatchHandlers()) { 678 return false; 679 } 680 return catchHandlers.getAllTargets().contains(successors.indexOf(block)); 681 } 682 guardsForCatchSuccessor(BasicBlock block)683 public int guardsForCatchSuccessor(BasicBlock block) { 684 assert hasCatchSuccessor(block); 685 int index = successors.indexOf(block); 686 int count = 0; 687 for (int handler : catchHandlers.getAllTargets()) { 688 if (handler == index) { 689 count++; 690 } 691 } 692 assert count > 0; 693 return count; 694 } 695 isSealed()696 public boolean isSealed() { 697 return sealed; 698 } 699 addIncompletePhi(int register, Phi phi, EdgeType readingEdge)700 public void addIncompletePhi(int register, Phi phi, EdgeType readingEdge) { 701 if (isOnThrowValue(register, readingEdge)) { 702 register = onThrowValueRegister(register); 703 } 704 assert !incompletePhis.containsKey(register); 705 incompletePhis.put(register, phi); 706 } 707 hasIncompletePhis()708 public boolean hasIncompletePhis() { 709 return !incompletePhis.isEmpty(); 710 } 711 getIncompletePhiRegisters()712 public Collection<Integer> getIncompletePhiRegisters() { 713 return incompletePhis.keySet(); 714 } 715 appendBasicBlockList( StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix)716 private void appendBasicBlockList( 717 StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) { 718 if (list.size() > 0) { 719 for (BasicBlock block : list) { 720 builder.append(block.number >= 0 ? block.number : "<unknown>"); 721 builder.append(postfix.apply(block)); 722 builder.append(" "); 723 } 724 } else { 725 builder.append("-"); 726 } 727 } 728 729 @Override toString()730 public String toString() { 731 return toDetailedString(); 732 } 733 toSimpleString()734 public String toSimpleString() { 735 return number < 0 ? super.toString() : ("block " + number); 736 } 737 predecessorPostfix(BasicBlock block)738 private String predecessorPostfix(BasicBlock block) { 739 if (hasCatchSuccessor(block)) { 740 return new String(new char[guardsForCatchSuccessor(block)]).replace("\0", "*"); 741 } 742 return ""; 743 } 744 toDetailedString()745 public String toDetailedString() { 746 StringBuilder builder = new StringBuilder(); 747 builder.append("block "); 748 builder.append(number); 749 builder.append(" ("); 750 builder.append(System.identityHashCode(this)); 751 builder.append(")"); 752 builder.append(", pred-counts: " + predecessors.size()); 753 if (unfilledPredecessorsCount > 0) { 754 builder.append(" (" + unfilledPredecessorsCount + " unfilled)"); 755 } 756 builder.append(", succ-count: " + successors.size()); 757 builder.append(", filled: " + isFilled()); 758 builder.append(", sealed: " + isSealed()); 759 builder.append("\n"); 760 builder.append("predecessors: "); 761 appendBasicBlockList(builder, predecessors, b -> ""); 762 builder.append("\n"); 763 builder.append("successors: "); 764 appendBasicBlockList(builder, successors, this::predecessorPostfix); 765 if (successors.size() > 0) { 766 builder.append(" ("); 767 if (hasCatchHandlers()) { 768 builder.append(catchHandlers.size()); 769 } else { 770 builder.append("no"); 771 } 772 builder.append(" try/catch successors)"); 773 } 774 builder.append("\n"); 775 if (phis != null && phis.size() > 0) { 776 for (Phi phi : phis) { 777 builder.append(phi.printPhi()); 778 if (incompletePhis.values().contains(phi)) { 779 builder.append(" (incomplete)"); 780 } 781 builder.append("\n"); 782 } 783 } else { 784 builder.append("no phis\n"); 785 } 786 if (localsAtEntry != null) { 787 builder.append("locals: "); 788 StringUtils.append(builder, localsAtEntry.int2ReferenceEntrySet(), ", ", BraceType.NONE); 789 builder.append("\n"); 790 } 791 for (Instruction instruction : instructions) { 792 StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6); 793 builder.append(": "); 794 StringUtils.appendRightPadded(builder, instruction.toString(), 20); 795 builder.append("\n"); 796 } 797 return builder.toString(); 798 } 799 print(CfgPrinter printer)800 public void print(CfgPrinter printer) { 801 printer.begin("block"); 802 printer.print("name \"B").append(number).append("\"\n"); 803 printer.print("from_bci -1\n"); 804 printer.print("to_bci -1\n"); 805 printer.print("predecessors"); 806 printBlockList(printer, predecessors); 807 printer.ln(); 808 printer.print("successors"); 809 printBlockList(printer, successors); 810 printer.ln(); 811 printer.print("xhandlers\n"); 812 printer.print("flags\n"); 813 printer.print("first_lir_id ").print(instructions.get(0).getNumber()).ln(); 814 printer.print("last_lir_id ").print(instructions.get(instructions.size() - 1).getNumber()).ln(); 815 printer.begin("HIR"); 816 if (phis != null) { 817 for (Phi phi : phis) { 818 phi.print(printer); 819 printer.append(" <|@\n"); 820 } 821 } 822 for (Instruction instruction : instructions) { 823 instruction.print(printer); 824 printer.append(" <|@\n"); 825 } 826 printer.end("HIR"); 827 printer.begin("LIR"); 828 for (Instruction instruction : instructions) { 829 instruction.printLIR(printer); 830 printer.append(" <|@\n"); 831 } 832 printer.end("LIR"); 833 printer.end("block"); 834 } 835 printBlockList(CfgPrinter printer, List<BasicBlock> blocks)836 private static void printBlockList(CfgPrinter printer, List<BasicBlock> blocks) { 837 for (BasicBlock block : blocks) { 838 printer.append(" \"B").append(block.number).append("\""); 839 } 840 } 841 addPhiMove(Move move)842 public void addPhiMove(Move move) { 843 // TODO(ager): Consider this more, is it always the case that we should add it before the 844 // exit instruction? 845 Instruction branch = exit(); 846 instructions.set(instructions.size() - 1, move); 847 instructions.add(branch); 848 } 849 setInstructions(LinkedList<Instruction> instructions)850 public void setInstructions(LinkedList<Instruction> instructions) { 851 this.instructions = instructions; 852 } 853 854 /** 855 * Remove a number of instructions. The instructions to remove are given as indexes in the 856 * instruction stream. 857 */ removeInstructions(List<Integer> toRemove)858 public void removeInstructions(List<Integer> toRemove) { 859 if (!toRemove.isEmpty()) { 860 LinkedList<Instruction> newInstructions = new LinkedList<>(); 861 int nextIndex = 0; 862 for (Integer index : toRemove) { 863 assert index >= nextIndex; // Indexes in toRemove must be sorted ascending. 864 newInstructions.addAll(instructions.subList(nextIndex, index)); 865 instructions.get(index).clearBlock(); 866 nextIndex = index + 1; 867 } 868 if (nextIndex < instructions.size()) { 869 newInstructions.addAll(instructions.subList(nextIndex, instructions.size())); 870 } 871 assert instructions.size() == newInstructions.size() + toRemove.size(); 872 setInstructions(newInstructions); 873 } 874 } 875 876 /** 877 * Remove an instruction. 878 */ removeInstruction(Instruction toRemove)879 public void removeInstruction(Instruction toRemove) { 880 int index = instructions.indexOf(toRemove); 881 assert index >= 0; 882 removeInstructions(Collections.singletonList(index)); 883 } 884 885 /** 886 * Create a new basic block with a single goto instruction. 887 * 888 * <p>The constructed basic block has no predecessors and has one 889 * successors which is the target block. 890 * 891 * @param target the target of the goto block 892 * @param blockNumber the block number of the goto block 893 */ createGotoBlock(BasicBlock target, int blockNumber)894 public static BasicBlock createGotoBlock(BasicBlock target, int blockNumber) { 895 BasicBlock block = createGotoBlock(blockNumber); 896 block.getSuccessors().add(target); 897 return block; 898 } 899 900 /** 901 * Create a new basic block with a single goto instruction. 902 * 903 * <p>The constructed basic block has no predecessors and no successors. 904 * 905 * @param blockNumber the block number of the goto block 906 */ createGotoBlock(int blockNumber)907 public static BasicBlock createGotoBlock(int blockNumber) { 908 BasicBlock block = new BasicBlock(); 909 block.add(new Goto()); 910 block.close(null); 911 block.setNumber(blockNumber); 912 return block; 913 } 914 915 /** 916 * Create a new basic block with a single if instruction. 917 * 918 * <p>The constructed basic block has no predecessors and no successors. 919 * 920 * @param blockNumber the block number of the goto block 921 */ createIfBlock(int blockNumber, If theIf)922 public static BasicBlock createIfBlock(int blockNumber, If theIf) { 923 BasicBlock block = new BasicBlock(); 924 block.add(theIf); 925 block.close(null); 926 block.setNumber(blockNumber); 927 return block; 928 } 929 isTrivialGoto()930 public boolean isTrivialGoto() { 931 return instructions.size() == 1 && exit().isGoto(); 932 } 933 hasOneNormalExit()934 public boolean hasOneNormalExit() { 935 return successors.size() == 1 && exit().isGoto(); 936 } 937 getCatchHandlers()938 public CatchHandlers<BasicBlock> getCatchHandlers() { 939 if (!hasCatchHandlers()) { 940 return CatchHandlers.EMPTY_BASIC_BLOCK; 941 } 942 List<BasicBlock> targets = ListUtils.map(catchHandlers.getAllTargets(), successors::get); 943 return new CatchHandlers<>(catchHandlers.getGuards(), targets); 944 } 945 getCatchHandlersWithSuccessorIndexes()946 public CatchHandlers<Integer> getCatchHandlersWithSuccessorIndexes() { 947 return catchHandlers; 948 } 949 clearCatchHandlers()950 public void clearCatchHandlers() { 951 catchHandlers = CatchHandlers.EMPTY_INDICES; 952 } 953 transferCatchHandlers(BasicBlock other)954 public void transferCatchHandlers(BasicBlock other) { 955 catchHandlers = other.catchHandlers; 956 other.catchHandlers = CatchHandlers.EMPTY_INDICES; 957 } 958 canThrow()959 public boolean canThrow() { 960 for (Instruction instruction : instructions) { 961 if (instruction.instructionTypeCanThrow()) { 962 return true; 963 } 964 } 965 return false; 966 } 967 968 // A block can have at most one "on throw" value. verifyOnThrowWrite(int register)969 private boolean verifyOnThrowWrite(int register) { 970 if (register >= 0) { 971 return true; 972 } 973 for (Integer other : currentDefinitions.keySet()) { 974 assert other >= 0 || other == register; 975 } 976 return true; 977 } 978 979 // Verify that if this block has a throwing instruction none of the following instructions may 980 // introduce an SSA value. Such values can lead to invalid uses since the values are not actually 981 // visible to exceptional successors. verifyNoValuesAfterThrowingInstruction()982 private boolean verifyNoValuesAfterThrowingInstruction() { 983 if (hasCatchHandlers()) { 984 ListIterator<Instruction> iterator = listIterator(instructions.size()); 985 while (iterator.hasPrevious()) { 986 Instruction instruction = iterator.previous(); 987 if (instruction.instructionTypeCanThrow()) { 988 return true; 989 } 990 assert instruction.outValue() == null; 991 } 992 } 993 return true; 994 } 995 iterator()996 public InstructionIterator iterator() { 997 return new BasicBlockInstructionIterator(this); 998 } 999 listIterator()1000 public InstructionListIterator listIterator() { 1001 return new BasicBlockInstructionIterator(this); 1002 } 1003 listIterator(int index)1004 public InstructionListIterator listIterator(int index) { 1005 return new BasicBlockInstructionIterator(this, index); 1006 } 1007 1008 /** 1009 * Creates an instruction list iterator starting at <code>instruction</code>. 1010 * 1011 * The cursor will be positioned after <code>instruction</code>. Calling <code>next</code> on 1012 * the returned iterator will return the instruction after <code>instruction</code>. Calling 1013 * <code>previous</code> will return <code>instruction</code>. 1014 */ listIterator(Instruction instruction)1015 public InstructionListIterator listIterator(Instruction instruction) { 1016 return new BasicBlockInstructionIterator(this, instruction); 1017 } 1018 1019 /** 1020 * Creates a new empty block as a successor for this block. 1021 * 1022 * The new block will have all the normal successors of the original block. 1023 * 1024 * The catch successors are either on the original block or the new block depending on the 1025 * value of <code>keepCatchHandlers</code>. 1026 * 1027 * The current block still has all the instructions, and the new block is empty instruction-wise. 1028 * 1029 * @param blockNumber block number for new block 1030 * @param keepCatchHandlers keep catch successors on the original block 1031 * @return the new block 1032 */ createSplitBlock(int blockNumber, boolean keepCatchHandlers)1033 BasicBlock createSplitBlock(int blockNumber, boolean keepCatchHandlers) { 1034 boolean hadCatchHandlers = hasCatchHandlers(); 1035 BasicBlock newBlock = new BasicBlock(); 1036 newBlock.setNumber(blockNumber); 1037 1038 // Copy all successors including catch handlers to the new block, and update predecessors. 1039 successors.forEach(newBlock.successors::add); 1040 for (BasicBlock successor : newBlock.getSuccessors()) { 1041 successor.replacePredecessor(this, newBlock); 1042 } 1043 successors.clear(); 1044 newBlock.catchHandlers = catchHandlers; 1045 catchHandlers = CatchHandlers.EMPTY_INDICES; 1046 1047 // If the catch handlers should be kept on the original block move them back. 1048 if (keepCatchHandlers && hadCatchHandlers) { 1049 moveCatchHandlers(newBlock); 1050 } 1051 1052 // Link the two blocks 1053 link(newBlock); 1054 1055 // Mark the new block filled and sealed. 1056 newBlock.filled = true; 1057 newBlock.sealed = true; 1058 1059 return newBlock; 1060 } 1061 1062 /** 1063 * Moves catch successors from `fromBlock` into this block. 1064 */ moveCatchHandlers(BasicBlock fromBlock)1065 public void moveCatchHandlers(BasicBlock fromBlock) { 1066 List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock); 1067 for (BasicBlock successor : catchSuccessors) { 1068 fromBlock.successors.remove(successor); 1069 successor.removePredecessor(fromBlock); 1070 } 1071 fromBlock.catchHandlers = CatchHandlers.EMPTY_INDICES; 1072 } 1073 1074 /** 1075 * Clone catch successors from `fromBlock` into this block. 1076 */ copyCatchHandlers( IRCode code, ListIterator<BasicBlock> blockIterator, BasicBlock fromBlock)1077 public void copyCatchHandlers( 1078 IRCode code, ListIterator<BasicBlock> blockIterator, BasicBlock fromBlock) { 1079 if (catchHandlers != null && catchHandlers.hasCatchAll()) { 1080 return; 1081 } 1082 List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock); 1083 1084 // After cloning is done all catch handler targets are referenced from both the 1085 // original and the newly created catch handlers. Thus, since we keep both of 1086 // them, we need to split appropriate edges to make sure every catch handler 1087 // target block has only one predecessor. 1088 // 1089 // Note that for each catch handler block target block we actually create two new blocks: 1090 // a copy of the original block and a new block to serve as a merging point for 1091 // the original and its copy. This actually simplifies things since we only need 1092 // one new phi to merge the two exception values, and all other phis don't need 1093 // to be changed. 1094 for (BasicBlock catchSuccessor : catchSuccessors) { 1095 catchSuccessor.splitCriticalExceptionEdges( 1096 code.valueNumberGenerator, 1097 newBlock -> { 1098 newBlock.setNumber(code.blocks.size()); 1099 blockIterator.add(newBlock); 1100 }); 1101 } 1102 } 1103 1104 /** 1105 * Assumes that `this` block is a catch handler target (note that it does not have to 1106 * start with MoveException instruction, since the instruction can be removed by 1107 * optimizations like dead code remover. 1108 * 1109 * Introduces new blocks on all incoming edges and clones MoveException instruction to 1110 * these blocks if it exists. All exception values introduced in newly created blocks 1111 * are combined in a phi added to `this` block. 1112 * 1113 * Note that if there are any other phis defined on this block, they remain valid, since 1114 * this method does not affect incoming edges in any way, and just adds new blocks with 1115 * MoveException and Goto. 1116 * 1117 * NOTE: onNewBlock must assign block number to the newly created block. 1118 */ splitCriticalExceptionEdges( ValueNumberGenerator valueNumberGenerator, Consumer<BasicBlock> onNewBlock)1119 public void splitCriticalExceptionEdges( 1120 ValueNumberGenerator valueNumberGenerator, Consumer<BasicBlock> onNewBlock) { 1121 List<BasicBlock> predecessors = this.getPredecessors(); 1122 boolean hasMoveException = entry().isMoveException(); 1123 MoveException move = null; 1124 if (hasMoveException) { 1125 // Remove the move-exception instruction. 1126 move = entry().asMoveException(); 1127 assert move.getPreviousLocalValue() == null; 1128 this.getInstructions().remove(0); 1129 } 1130 // Create new predecessor blocks. 1131 List<BasicBlock> newPredecessors = new ArrayList<>(); 1132 List<Value> values = new ArrayList<>(predecessors.size()); 1133 for (BasicBlock predecessor : predecessors) { 1134 if (!predecessor.hasCatchSuccessor(this)) { 1135 throw new CompilationError( 1136 "Invalid block structure: catch block reachable via non-exceptional flow."); 1137 } 1138 BasicBlock newBlock = new BasicBlock(); 1139 newPredecessors.add(newBlock); 1140 if (hasMoveException) { 1141 Value value = new Value( 1142 valueNumberGenerator.next(), MoveType.OBJECT, move.getDebugInfo()); 1143 values.add(value); 1144 newBlock.add(new MoveException(value)); 1145 } 1146 newBlock.add(new Goto()); 1147 newBlock.close(null); 1148 newBlock.getSuccessors().add(this); 1149 newBlock.getPredecessors().add(predecessor); 1150 predecessor.replaceSuccessor(this, newBlock); 1151 onNewBlock.accept(newBlock); 1152 assert newBlock.getNumber() >= 0 : "Number must be assigned by `onNewBlock`"; 1153 } 1154 // Replace the blocks predecessors with the new ones. 1155 predecessors.clear(); 1156 predecessors.addAll(newPredecessors); 1157 // Insert a phi for the move-exception value. 1158 if (hasMoveException) { 1159 Phi phi = new Phi(valueNumberGenerator.next(), 1160 this, MoveType.OBJECT, move.getLocalInfo()); 1161 phi.addOperands(values); 1162 move.outValue().replaceUsers(phi); 1163 } 1164 } 1165 1166 /** 1167 * Append catch handlers from another block <code>fromBlock</code> (which must have catch 1168 * handlers) to the catch handlers of this block. 1169 * 1170 * Note that after appending catch handlers their targets are referenced by both 1171 * <code>fromBlock</code> and <code>this</code> block, but no phis are inserted. For this reason 1172 * this method should only be called from either {@link #moveCatchHandlers} or 1173 * {@link #copyCatchHandlers} which know how to handle phis. 1174 * 1175 * @returns the catch successors that are reused in both blocks after appending. 1176 */ appendCatchHandlers(BasicBlock fromBlock)1177 private List<BasicBlock> appendCatchHandlers(BasicBlock fromBlock) { 1178 assert fromBlock.hasCatchHandlers(); 1179 1180 List<Integer> prevCatchTargets = fromBlock.catchHandlers.getAllTargets(); 1181 List<DexType> prevCatchGuards = fromBlock.catchHandlers.getGuards(); 1182 List<BasicBlock> catchSuccessors = new ArrayList<>(); 1183 List<DexType> newCatchGuards = new ArrayList<>(); 1184 List<Integer> newCatchTargets = new ArrayList<>(); 1185 1186 // First add existing catch handlers to the catch handler list. 1187 if (hasCatchHandlers()) { 1188 newCatchGuards.addAll(catchHandlers.getGuards()); 1189 newCatchTargets.addAll(catchHandlers.getAllTargets()); 1190 for (int newCatchTarget : newCatchTargets) { 1191 BasicBlock catchSuccessor = successors.get(newCatchTarget); 1192 if (!catchSuccessors.contains(catchSuccessor)) { 1193 catchSuccessors.add(catchSuccessor); 1194 } 1195 int index = catchSuccessors.indexOf(catchSuccessor); 1196 assert index == newCatchTarget; 1197 } 1198 } 1199 1200 // This is the number of catch handlers which are already successors of this block. 1201 int formerCatchHandlersCount = catchSuccessors.size(); 1202 1203 // Then add catch handlers from the other block to the catch handler list. 1204 for (int i = 0; i < prevCatchTargets.size(); i++) { 1205 int prevCatchTarget = prevCatchTargets.get(i); 1206 DexType prevCatchGuard = prevCatchGuards.get(i); 1207 // TODO(sgjesse): Check sub-types of guards. Will require AppInfoWithSubtyping. 1208 BasicBlock catchSuccessor = fromBlock.successors.get(prevCatchTarget); 1209 // We assume that all the catch handlers targets has only one 1210 // predecessor and, thus, no phis. 1211 assert catchSuccessor.getPredecessors().size() == 1; 1212 assert catchSuccessor.getPhis().isEmpty(); 1213 1214 int index = catchSuccessors.indexOf(catchSuccessor); 1215 if (index == -1) { 1216 catchSuccessors.add(catchSuccessor); 1217 index = catchSuccessors.size() - 1; 1218 } 1219 newCatchGuards.add(prevCatchGuard); 1220 newCatchTargets.add(index); 1221 } 1222 1223 // Create the new successors list and link things up. 1224 List<BasicBlock> formerSuccessors = new ArrayList<>(successors); 1225 successors.clear(); 1226 List<BasicBlock> sharedCatchSuccessors = new ArrayList<>(); 1227 for (int i = 0; i < catchSuccessors.size(); i++) { 1228 if (i < formerCatchHandlersCount) { 1229 // Former catch successors are just copied, as they are already linked. 1230 assert catchSuccessors.get(i).getPredecessors().contains(this); 1231 successors.add(catchSuccessors.get(i)); 1232 } else { 1233 // New catch successors are linked properly. 1234 assert !catchSuccessors.get(i).getPredecessors().contains(this); 1235 link(catchSuccessors.get(i)); 1236 sharedCatchSuccessors.add(catchSuccessors.get(i)); 1237 } 1238 } 1239 catchHandlers = new CatchHandlers<>(newCatchGuards, newCatchTargets); 1240 1241 // Finally add the normal successor if any. 1242 int catchSuccessorsCount = successors.size(); 1243 for (BasicBlock formerSuccessor : formerSuccessors) { 1244 if (!successors.contains(formerSuccessor)) { 1245 assert !exit().isThrow(); 1246 successors.add(formerSuccessor); 1247 } 1248 } 1249 assert successors.size() == catchSuccessorsCount || !exit().isThrow(); 1250 1251 return sharedCatchSuccessors; 1252 } 1253 } 1254