Merge "Dump timing when system proterty com.android.tools.r8.printtimes is set"
diff --git a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
index 00cb542..0a633f3 100644
--- a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
+++ b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
@@ -316,14 +316,14 @@
     @Override
     public void writeResult() throws IOException {
       if (dexPath != null) {
-        Segment[] segments = DexFileReader.parseMapFrom(dexPath);
-        for (Segment segment : segments) {
-          int y = segment.offset / width;
+        DexSection[] sections = DexParser.parseMapFrom(dexPath);
+        for (DexSection section : sections) {
+          int y = section.offset / width;
           for (int x = 0; x < width; x++) {
             int val = (x / 10) % 2 == 0 ? 0 : 0xffffff;
             image.setRGB(x, y, val);
           }
-          System.out.println(segment);
+          System.out.println(section);
         }
       }
       ImageIO.write(image, "png", output.toFile());
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
index 908372a..8c6574c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -11,6 +11,7 @@
 
 public class DexDebugEntry {
 
+  public final boolean lineEntry;
   public final int address;
   public final int line;
   public final DexString sourceFile;
@@ -21,6 +22,7 @@
   public final Position callerPosition;
 
   public DexDebugEntry(
+      boolean lineEntry,
       int address,
       int line,
       DexString sourceFile,
@@ -29,6 +31,7 @@
       ImmutableMap<Integer, DebugLocalInfo> locals,
       DexMethod method,
       Position callerPosition) {
+    this.lineEntry = lineEntry;
     this.address = address;
     this.line = line;
     this.sourceFile = sourceFile;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index a1bc201..b672e26 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -98,6 +98,7 @@
   @Override
   public void visit(DexDebugEvent.AdvancePC advancePC) {
     positionState.visit(advancePC);
+    entryEventReceived(false);
   }
 
   @Override
@@ -113,7 +114,7 @@
   @Override
   public void visit(DexDebugEvent.Default defaultEvent) {
     positionState.visit(defaultEvent);
-    defaultEventReceived();
+    entryEventReceived(true);
   }
 
   @Override
@@ -153,11 +154,12 @@
     getEntry(restartLocal.registerNum).reset();
   }
 
-  public void defaultEventReceived() {
+  private void entryEventReceived(boolean lineEntry) {
     if (pending != null) {
       // Local changes contribute to the pending position entry.
       entries.add(
           new DexDebugEntry(
+              pending.lineEntry,
               pending.address,
               pending.line,
               pending.sourceFile,
@@ -169,6 +171,7 @@
     }
     pending =
         new DexDebugEntry(
+            lineEntry,
             positionState.getCurrentPc(),
             positionState.getCurrentLine(),
             positionState.getCurrentFile(),
@@ -184,7 +187,7 @@
   public List<DexDebugEntry> build() {
     // Flush any pending entry.
     if (pending != null) {
-      defaultEventReceived(); // To flush 'pending'.
+      entryEventReceived(false); // To flush 'pending'.
       pending = null;
     }
     List<DexDebugEntry> result = entries;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index bee0910..69fb348 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -216,7 +216,9 @@
   private void emitLocalChanges(int pc) {
     // If pc advanced since the locals changed and locals indeed have changed, emit the changes.
     if (localsChanged()) {
-      emitAdvancementEvents(emittedPc, emittedPosition, pc, emittedPosition, events, factory);
+      assert emittedPc != pc;
+      int pcDelta = emittedPc == NO_PC_INFO ? pc : pc - emittedPc;
+      events.add(factory.createAdvancePC(pcDelta));
       emittedPc = pc;
       emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
       pendingLocalChanges = false;
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 bb4891b..ff8bd02 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
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -44,11 +45,15 @@
 
   public final boolean hasDebugPositions;
 
+  private final InternalOptions options;
+
   public IRCode(
+      InternalOptions options,
       DexEncodedMethod method,
       LinkedList<BasicBlock> blocks,
       ValueNumberGenerator valueNumberGenerator,
       boolean hasDebugPositions) {
+    this.options = options;
     this.method = method;
     this.blocks = blocks;
     this.valueNumberGenerator = valueNumberGenerator;
@@ -251,7 +256,10 @@
     ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
     BasicBlock entryBlock = blocks.getFirst();
     depthFirstSorting(visitedBlock, entryBlock, builder);
-    return builder.build().reverse();
+    ImmutableList<BasicBlock> ordered = builder.build().reverse();
+    return options.testing.placeExceptionalBlocksLast
+        ? reorderExceptionalBlocksLastForTesting(ordered)
+        : ordered;
   }
 
   private void depthFirstSorting(Set<BasicBlock> visitedBlock, BasicBlock block,
@@ -265,6 +273,23 @@
     }
   }
 
+  // Reorder the blocks forcing all exceptional blocks to be at the end.
+  private static ImmutableList<BasicBlock> reorderExceptionalBlocksLastForTesting(
+      ImmutableList<BasicBlock> blocks) {
+    ImmutableList.Builder<BasicBlock> reordered = ImmutableList.builder();
+    for (BasicBlock block : blocks) {
+      if (!block.entry().isMoveException()) {
+        reordered.add(block);
+      }
+    }
+    for (BasicBlock block : blocks) {
+      if (block.entry().isMoveException()) {
+        reordered.add(block);
+      }
+    }
+    return reordered.build();
+  }
+
   public void print(CfgPrinter printer) {
     ensureBlockNumbering();
     for (BasicBlock block : blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index baf97f6..82840f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -52,6 +52,7 @@
 
   @Override
   public void buildDex(DexBuilder builder) {
+    assert builder.getOptions().canUseNotInstruction();
     com.android.tools.r8.code.Instruction instruction;
     int dest = builder.allocatedRegister(dest(), getNumber());
     int src = builder.allocatedRegister(source(), getNumber());
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 140184d..55e4b83 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
@@ -234,7 +234,7 @@
       currentPosition = Position.none();
     } else {
       currentPosition = getCanonicalPositionAppendCaller(current);
-      if (current.address == offset) {
+      if (current.lineEntry && current.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 0a27017..98c1015 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
@@ -390,7 +390,7 @@
     joinPredecessorsWithIdenticalPhis();
 
     // Package up the IR code.
-    IRCode ir = new IRCode(method, blocks, valueNumberGenerator, hasDebugPositions);
+    IRCode ir = new IRCode(options, method, blocks, valueNumberGenerator, hasDebugPositions);
 
     // Split critical edges to make sure that we have a place to insert phi moves if
     // necessary.
@@ -1258,7 +1258,13 @@
   public void addNot(NumericType type, int dest, int value) {
     Value in = readNumericRegister(value, type);
     Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
-    Not instruction = new Not(type, out, in);
+    Instruction instruction;
+    if (options.canUseNotInstruction()) {
+      instruction = new Not(type, out, in);
+    } else {
+      Value minusOne = readLiteral(ValueType.fromNumericType(type), -1);
+      instruction = new Xor(type, out, in, minusOne);
+    }
     assert !instruction.instructionTypeCanThrow();
     addInstruction(instruction);
   }
@@ -1616,6 +1622,22 @@
     return readRegister(register, ValueType.fromNumericType(type));
   }
 
+  public Value readLiteral(ValueType type, long constant) {
+    if (type == ValueType.INT) {
+      return readIntLiteral(constant);
+    } else {
+      assert type == ValueType.LONG;
+      return readLongLiteral(constant);
+    }
+  }
+
+  public Value readLongLiteral(long constant) {
+    Value value = new Value(valueNumberGenerator.next(), ValueType.LONG, null);
+    ConstNumber number = new ConstNumber(value, constant);
+    add(number);
+    return number.outValue();
+  }
+
   public Value readIntLiteral(long constant) {
     Value value = new Value(valueNumberGenerator.next(), ValueType.INT, null);
     ConstNumber number = new ConstNumber(value, constant);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b851352..6fc9e17 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -392,6 +392,7 @@
         Function.identity();
 
     public boolean invertConditionals = false;
+    public boolean placeExceptionalBlocksLast = false;
   }
 
   public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
new file mode 100644
index 0000000..0142dba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+public class BreakAtTryAndCatchTest {
+  int i = 0;
+
+  void foo() {
+    try { bar(); } catch (RuntimeException e) { baz(); }
+    baz();
+  }
+
+  int bar() {
+    if (i++ % 2 == 0) {
+      System.out.println("bar return " + i);
+      return i;
+    }
+    System.out.println("bar throw " + i);
+    throw new RuntimeException("" + i);
+  }
+
+  void baz() {
+    System.out.println("baz");
+  }
+
+  public static void main(String[] args) {
+    BreakAtTryAndCatchTest test = new BreakAtTryAndCatchTest();
+    test.foo();
+    test.foo();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
new file mode 100644
index 0000000..54c906b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BreakAtTryAndCatchTestRunner extends DebugTestBase {
+
+  private static final Class CLASS = BreakAtTryAndCatchTest.class;
+  private static final String FILE = CLASS.getSimpleName() + ".java";
+  private static final String NAME = CLASS.getCanonicalName();
+
+  private final String name;
+  private final DebugTestConfig config;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Collection<Object[]> setup() {
+    DelayedDebugTestConfig cf =
+        temp -> new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
+    DelayedDebugTestConfig d8 =
+        temp -> new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
+    DelayedDebugTestConfig d8Reordered =
+        temp -> new D8DebugTestConfig().compileAndAdd(
+            temp,
+            Collections.singletonList(ToolHelper.getClassFileForTestClass(CLASS)),
+            options -> options.testing.placeExceptionalBlocksLast = true);
+    return ImmutableList.of(
+        new Object[]{"CF", cf},
+        new Object[]{"D8", d8},
+        new Object[]{"D8/reorder", d8Reordered}
+    );
+  }
+
+  public BreakAtTryAndCatchTestRunner(String name, DelayedDebugTestConfig config) {
+    this.name = name;
+    this.config = config.getConfig(temp);
+  }
+
+  @Test
+  public void testHitOnEntryOnly() throws Throwable {
+    Assume.assumeFalse("b/72933440", name.equals("D8/reorder"));
+    runDebugTest(
+        config,
+        NAME,
+        breakpoint(NAME, "foo", 10),
+        run(),
+        checkLine(FILE, 10), // hit line entry, bar does not throw
+        run(),
+        checkLine(FILE, 10), // hit line entry, bar does throw
+        breakpoint(NAME, "main", 31),
+        run(),
+        checkLine(FILE, 31), // No more hits on line, continue to main.
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
index b588590..002635f 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -36,7 +35,6 @@
   /** Test that only hit the break point at line 15 once. */
   @Test
   public void testHitBreakpointOnce() throws Throwable {
-    Assume.assumeFalse("b/72933440 : invalid line info table", config instanceof D8DebugTestConfig);
     runDebugTest(
         config,
         NAME,
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 5d15735..4ce3b5a 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -125,14 +125,14 @@
   }
 
   private void checkConsistentEntries() {
-    DexDebugEntry previousEntry = null;
+    DexDebugEntry previousLineEntry = null;
     for (DexDebugEntry entry : entries) {
-      if (previousEntry != null) {
+      if (entry.lineEntry) {
         assertTrue(
             "More than one entry defined for PC " + StringUtils.hexString(entry.address, 2),
-            entry.address > previousEntry.address);
+            previousLineEntry == null || entry.address > previousLineEntry.address);
+        previousLineEntry = entry;
       }
-      previousEntry = entry;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index d6f86d8..2da7d8c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -123,9 +123,10 @@
     LinkedList<BasicBlock> blocks = new LinkedList<>();
     blocks.add(block);
 
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    InternalOptions options = new InternalOptions();
+    IRCode code = new IRCode(options, null, blocks, new ValueNumberGenerator(), false);
     PeepholeOptimizer.optimize(code,
-        new MockLinearScanRegisterAllocator(code, new InternalOptions()));
+        new MockLinearScanRegisterAllocator(code, options));
 
     // Check that all four constant number instructions remain.
     assertEquals(4,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 699ebb4..6e02a17 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -62,7 +62,7 @@
     // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
     // that ended up removing that goto changing the code to start with the unreachable
     // throw.
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
     CodeRewriter.collapsTrivialGotos(null, code);
     assertTrue(code.blocks.get(0).isTrivialGoto());
     assertTrue(blocks.contains(block0));
@@ -130,7 +130,7 @@
     // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
     // that ended up removing that goto changing the code to start with the unreachable
     // throw.
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
     CodeRewriter.collapsTrivialGotos(null, code);
     assertTrue(block0.getInstructions().get(1).isIf());
     assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index af8382e..0623f01 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -116,9 +116,8 @@
 
   static final Map<String, TestPredicate> FAILING_TESTS =
       ImmutableMap.<String, TestPredicate>builder()
-          // ART line-table currently returns too many line entries.
-          .put("LineTableDuplicatesTest", RunJdwpTests::isJava)
           // Other failures on various older runtimes.
+          .put("LineTableDuplicatesTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ArrayReference.GetValuesTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ArrayReference.LengthTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
diff --git a/tools/asmifier.py b/tools/asmifier.py
new file mode 100755
index 0000000..4d2b973
--- /dev/null
+++ b/tools/asmifier.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import gradle
+import os
+import subprocess
+import sys
+import utils
+
+def run(args, build=True):
+  if build:
+    gradle.RunGradle(['copyMavenDeps'])
+  cmd = []
+  cmd.append('java')
+  cmd.extend(['-cp', 'build/deps/asm-6.0.jar:build/deps/asm-util-6.0.jar'])
+  cmd.append('org.objectweb.asm.util.ASMifier')
+  cmd.extend(args)
+  utils.PrintCmd(cmd)
+  result = subprocess.check_output(cmd)
+  print(result)
+  return result
+
+def main():
+  build = True
+  args = []
+  for arg in sys.argv[1:]:
+    if arg in ("--build", "--no-build"):
+      build = arg == "--build"
+    else:
+      args.append(arg)
+  try:
+    run(args, build)
+  except subprocess.CalledProcessError as e:
+    # In case anything relevant was printed to stdout, normally this is already
+    # on stderr.
+    print(e.output)
+    return e.returncode
+
+if __name__ == '__main__':
+  sys.exit(main())