Merge "Add benchmarking ability to the sample app tool"
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index cb5aad0..3d30b23 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -138,13 +138,10 @@
       if (predecessors.size() <= 1) {
         continue;
       }
-      // If any of the edges to the block are critical, we need to insert new blocks on each
-      // containing the move-exception instruction which must remain the first instruction.
-      if (block.entry() instanceof MoveException) {
-        nextBlockNumber = block.splitCriticalExceptionEdges(
-            nextBlockNumber, valueNumberGenerator, newBlocks::add);
-        continue;
-      }
+
+      // Exceptional edges are given unique header blocks and can have at most one predecessor.
+      assert !block.entry().isMoveException();
+
       for (int predIndex = 0; predIndex < predecessors.size(); predIndex++) {
         BasicBlock pred = predecessors.get(predIndex);
         if (!pred.hasOneNormalExit()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 309e1d0..4cbebcf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -586,7 +586,7 @@
   }
 
   @Override
-  public int getMoveExceptionRegister() {
+  public int getMoveExceptionRegister(int instructionIndex) {
     return CfState.Slot.STACK_OFFSET;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 31ee706..b59794b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -21,12 +21,12 @@
 import com.android.tools.r8.code.InvokeSuperRange;
 import com.android.tools.r8.code.InvokeVirtual;
 import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.MoveException;
 import com.android.tools.r8.code.MoveResult;
 import com.android.tools.r8.code.MoveResultObject;
 import com.android.tools.r8.code.MoveResultWide;
 import com.android.tools.r8.code.SwitchPayload;
 import com.android.tools.r8.code.Throw;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexCode.Try;
@@ -185,14 +185,19 @@
   }
 
   @Override
-  public int getMoveExceptionRegister() {
-    // No register, move-exception is manually entered during construction.
+  public int getMoveExceptionRegister(int instructionIndex) {
+    Instruction instruction = code.instructions[instructionIndex];
+    if (instruction instanceof MoveException) {
+      MoveException moveException = (MoveException) instruction;
+      return moveException.AA;
+    }
     return -1;
   }
 
   @Override
   public Position getDebugPositionAtOffset(int offset) {
-    throw new Unreachable();
+    DexDebugEntry entry = getDebugEntryAtOffset(offset);
+    return entry == null ? preamblePosition : getCanonicalPositionAppendCaller(entry);
   }
 
   @Override
@@ -225,23 +230,30 @@
     }
   }
 
+  private DexDebugEntry getDebugEntryAtOffset(int offset) {
+    DexDebugEntry current = null;
+    if (debugEntries != null) {
+      for (DexDebugEntry entry : debugEntries) {
+        if (entry.address > offset) {
+          break;
+        }
+        current = entry;
+      }
+    }
+    return current;
+  }
+
   private void updateDebugPosition(int instructionIndex, IRBuilder builder) {
     if (debugEntries == null || debugEntries.isEmpty()) {
       return;
     }
-    DexDebugEntry current = null;
     int offset = instructionOffset(instructionIndex);
-    for (DexDebugEntry entry : debugEntries) {
-      if (entry.address > offset) {
-        break;
-      }
-      current = entry;
-    }
-    if (current == null) {
+    DexDebugEntry entry = getDebugEntryAtOffset(offset);
+    if (entry == null) {
       currentPosition = preamblePosition;
     } else {
-      currentPosition = getCanonicalPositionAppendCaller(current);
-      if (current.lineEntry && current.address == offset) {
+      currentPosition = getCanonicalPositionAppendCaller(entry);
+      if (entry.lineEntry && entry.address == offset) {
         builder.addDebugPosition(currentPosition);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 5be54f5..6735863 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -296,7 +296,6 @@
   private final LinkedList<BasicBlock> blocks = new LinkedList<>();
 
   private BasicBlock currentBlock = null;
-  private final List<BasicBlock.Pair> needGotoToCatchBlocks = new ArrayList<>();
   final private ValueNumberGenerator valueNumberGenerator;
   private final DexEncodedMethod method;
   private final AppInfo appInfo;
@@ -411,9 +410,6 @@
     // Check that the last block is closed and does not fall off the end.
     assert currentBlock == null;
 
-    // Handle where a catch handler hits the same block as the fallthrough.
-    handleFallthroughToCatchBlock();
-
     // Verify that we have properly filled all blocks
     // Must be after handle-catch (which has delayed edges),
     // but before handle-exit (which does not maintain predecessor counts).
@@ -571,14 +567,15 @@
 
   private void processMoveExceptionItem(MoveExceptionWorklistItem moveExceptionItem) {
     // TODO(zerny): Link with outer try-block handlers, if any. b/65203529
-    int moveExceptionDest = source.getMoveExceptionRegister();
-    assert moveExceptionDest >= 0;
     int targetIndex = source.instructionIndex(moveExceptionItem.targetOffset);
-    Value out = writeRegister(moveExceptionDest, ValueType.OBJECT, ThrowingInfo.NO_THROW, null);
-    Position position = source.getDebugPositionAtOffset(moveExceptionItem.targetOffset);
-    MoveException moveException = new MoveException(out);
-    moveException.setPosition(position);
-    currentBlock.add(moveException);
+    int moveExceptionDest = source.getMoveExceptionRegister(targetIndex);
+    if (moveExceptionDest >= 0) {
+      Value out = writeRegister(moveExceptionDest, ValueType.OBJECT, ThrowingInfo.NO_THROW, null);
+      Position position = source.getDebugPositionAtOffset(moveExceptionItem.targetOffset);
+      MoveException moveException = new MoveException(out);
+      moveException.setPosition(position);
+      currentBlock.add(moveException);
+    }
     Goto exit = new Goto();
     currentBlock.add(exit);
     BasicBlock targetBlock = getTarget(moveExceptionItem.targetOffset);
@@ -976,11 +973,8 @@
   public void addGoto(int targetOffset) {
     addInstruction(new Goto());
     BasicBlock targetBlock = getTarget(targetOffset);
-    if (currentBlock.hasCatchSuccessor(targetBlock)) {
-      needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, targetBlock));
-    } else {
-      currentBlock.link(targetBlock);
-    }
+    assert !currentBlock.hasCatchSuccessor(targetBlock);
+    currentBlock.link(targetBlock);
     addToWorklist(targetBlock, source.instructionIndex(targetOffset));
     closeCurrentBlock();
   }
@@ -1292,25 +1286,20 @@
   }
 
   public void addMoveException(int dest) {
-    Value out = writeRegister(dest, ValueType.OBJECT, ThrowingInfo.NO_THROW);
-    assert !out.hasLocalInfo();
-    MoveException instruction = new MoveException(out);
-    assert !instruction.instructionTypeCanThrow();
-    if (currentBlock.getInstructions().size() == 1 && currentBlock.entry().isDebugPosition()) {
-      InstructionListIterator it = currentBlock.listIterator();
-      Instruction entry = it.next();
-      assert entry.getPosition().equals(source.getCurrentPosition());
-      attachLocalValues(instruction);
-      it.replaceCurrentInstruction(instruction);
-      return;
+    assert !currentBlock.getPredecessors().isEmpty();
+    assert currentBlock.getPredecessors().stream().allMatch(b -> b.entry().isMoveException());
+    assert verifyValueIsMoveException(readRegister(dest, ValueType.OBJECT));
+  }
+
+  private static boolean verifyValueIsMoveException(Value value) {
+    if (value.isPhi()) {
+      for (Value operand : value.asPhi().getOperands()) {
+        assert operand.definition.isMoveException();
+      }
+    } else {
+      assert value.definition.isMoveException();
     }
-    if (!currentBlock.getInstructions().isEmpty()) {
-      throw new CompilationError("Invalid MoveException instruction encountered. "
-          + "The MoveException instruction is not the first instruction in the block in "
-          + method.qualifiedName()
-          + ".");
-    }
-    addInstruction(instruction);
+    return true;
   }
 
   public void addMoveResult(int dest) {
@@ -1843,29 +1832,19 @@
         assert !throwingInstructionInCurrentBlock;
         throwingInstructionInCurrentBlock = true;
         List<BasicBlock> targets = new ArrayList<>(catchHandlers.getAllTargets().size());
-        int moveExceptionDest = source.getMoveExceptionRegister();
-        if (moveExceptionDest < 0) {
-          for (int targetOffset : catchHandlers.getAllTargets()) {
-            BasicBlock target = getTarget(targetOffset);
-            addToWorklist(target, source.instructionIndex(targetOffset));
-            targets.add(target);
+        // Construct unique move-exception header blocks for each unique target.
+        Map<BasicBlock, BasicBlock> moveExceptionHeaders =
+            new IdentityHashMap<>(catchHandlers.getUniqueTargets().size());
+        for (int targetOffset : catchHandlers.getAllTargets()) {
+          BasicBlock target = getTarget(targetOffset);
+          BasicBlock header = moveExceptionHeaders.get(target);
+          if (header == null) {
+            header = new BasicBlock();
+            header.incrementUnfilledPredecessorCount();
+            moveExceptionHeaders.put(target, header);
+            ssaWorklist.add(new MoveExceptionWorklistItem(header, targetOffset));
           }
-        } else {
-          // If there is a well-defined move-exception destination register (eg, compiling from
-          // Java-bytecode) then we construct move-exception header blocks for each unique target.
-          Map<BasicBlock, BasicBlock> moveExceptionHeaders =
-              new IdentityHashMap<>(catchHandlers.getUniqueTargets().size());
-          for (int targetOffset : catchHandlers.getAllTargets()) {
-            BasicBlock target = getTarget(targetOffset);
-            BasicBlock header = moveExceptionHeaders.get(target);
-            if (header == null) {
-              header = new BasicBlock();
-              header.incrementUnfilledPredecessorCount();
-              moveExceptionHeaders.put(target, header);
-              ssaWorklist.add(new MoveExceptionWorklistItem(header, targetOffset));
-            }
-            targets.add(header);
-          }
+          targets.add(header);
         }
         currentBlock.linkCatchSuccessors(catchHandlers.getGuards(), targets);
       }
@@ -1997,45 +1976,11 @@
   private void closeCurrentBlockWithFallThrough(BasicBlock nextBlock) {
     assert currentBlock != null;
     addInstruction(new Goto());
-    if (currentBlock.hasCatchSuccessor(nextBlock)) {
-      needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, nextBlock));
-    } else {
-      currentBlock.link(nextBlock);
-    }
+    assert !currentBlock.hasCatchSuccessor(nextBlock);
+    currentBlock.link(nextBlock);
     closeCurrentBlock();
   }
 
-  private void handleFallthroughToCatchBlock() {
-    // When a catch handler for a block goes to the same block as the fallthrough for that
-    // block the graph only has one edge there. In these cases we add an additional block so the
-    // catch edge goes through that and then make the fallthrough go through a new direct edge.
-    for (BasicBlock.Pair pair : needGotoToCatchBlocks) {
-      BasicBlock source = pair.first;
-      BasicBlock target = pair.second;
-
-      // New block with one unfilled predecessor.
-      BasicBlock newBlock = BasicBlock.createGotoBlock(nextBlockNumber++, target);
-      blocks.add(newBlock);
-      newBlock.incrementUnfilledPredecessorCount();
-
-      // Link blocks.
-      source.replaceSuccessor(target, newBlock);
-      newBlock.getPredecessors().add(source);
-      source.getSuccessors().add(target);
-      target.getPredecessors().add(newBlock);
-
-      // Check that the successor indexes are correct.
-      assert source.hasCatchSuccessor(newBlock);
-      assert !source.hasCatchSuccessor(target);
-
-      // Mark the filled predecessors to the blocks.
-      if (source.isFilled()) {
-        newBlock.filledPredecessor(this);
-      }
-      target.filledPredecessor(this);
-    }
-  }
-
   /**
    * Change to control-flow graph to avoid repeated phi operands when all the same values
    * flow in from multiple predecessors.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 5c25fba..53bbb7e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -594,7 +594,12 @@
   }
 
   @Override
-  public int getMoveExceptionRegister() {
+  public int getMoveExceptionRegister(int instructionIndex) {
+    return getMoveExceptionRegister();
+  }
+
+  // In classfiles the register is always on top of stack.
+  private int getMoveExceptionRegister() {
     return state.startOfStack;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index e95790a..bf2b51f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -59,7 +59,8 @@
   void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset, IRBuilder builder);
 
   CatchHandlers<Integer> getCurrentCatchHandlers();
-  int getMoveExceptionRegister();
+
+  int getMoveExceptionRegister(int instructionIndex);
 
   // For debugging/verification purpose.
   boolean verifyRegister(int register);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 6cb8f6e..e749479 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1099,7 +1099,7 @@
     }
 
     @Override
-    public int getMoveExceptionRegister() {
+    public int getMoveExceptionRegister(int instructionIndex) {
       throw new Unreachable();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 80a439a..c64d8ed 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -209,7 +209,7 @@
   }
 
   @Override
-  public int getMoveExceptionRegister() {
+  public int getMoveExceptionRegister(int instructionIndex) {
     throw new Unreachable();
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index e7344a4..0cf2f53 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -196,7 +196,7 @@
 
   public void runCatchHandlerTest(boolean codeThrows, boolean twoGuards) throws Exception {
     final int secondBlockInstructions = 4;
-    final int initialBlockCount = 5;
+    final int initialBlockCount = 6;
     // Try split between all instructions in second block.
     for (int i = 1; i < secondBlockInstructions; i++) {
       TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
@@ -235,7 +235,7 @@
   public void runCatchHandlerSplitThreeTest(boolean codeThrows, boolean twoGuards)
       throws Exception {
     final int secondBlockInstructions = 4;
-    final int initialBlockCount = 5;
+    final int initialBlockCount = 6;
     // Try split out all instructions in second block.
     for (int i = 1; i < secondBlockInstructions - 1; i++) {
       TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 4214a67..101bf06 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -766,7 +766,7 @@
     }
 
     @Override
-    public int getMoveExceptionRegister() {
+    public int getMoveExceptionRegister(int instructionIndex) {
       throw new Unreachable();
     }