Timing for collision detection in class merger

This CL also prints timing for the collision detection in the vertical class merging if options.printTimes is enabled.

To accomplish this, this CL extends the Timing class, such that it is possible to restart an existing timer. This is needed since the collission detection runs as one out of many checks inside a loop.

Change-Id: I8777d80d20f102072dc16634c8febc83ec3c1a70
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 222f979..b5137de 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1168,7 +1168,7 @@
     }
   }
 
-  private static class CollisionDetector {
+  private class CollisionDetector {
 
     private static final int NOT_FOUND = 1 << (Integer.SIZE - 1);
 
@@ -1193,27 +1193,30 @@
     }
 
     boolean mayCollide() {
+      timing.begin("collision detection");
       fillSeenPositions(invokes);
+      boolean result = false;
       // If the type is not used in methods at all, there cannot be any conflict.
-      if (seenPositions.isEmpty()) {
-        return false;
-      }
-      for (DexMethod method : invokes) {
-        Int2IntMap positionsMap = seenPositions.get(method.name);
-        if (positionsMap != null) {
-          int arity = method.getArity();
-          int previous = positionsMap.get(arity);
-          if (previous != NOT_FOUND) {
-            assert previous != 0;
-            int positions = computePositionsFor(method.proto, source, sourceProtoCache,
-                substituions);
-            if ((positions & previous) != 0) {
-              return true;
+      if (!seenPositions.isEmpty()) {
+        for (DexMethod method : invokes) {
+          Int2IntMap positionsMap = seenPositions.get(method.name);
+          if (positionsMap != null) {
+            int arity = method.getArity();
+            int previous = positionsMap.get(arity);
+            if (previous != NOT_FOUND) {
+              assert previous != 0;
+              int positions =
+                  computePositionsFor(method.proto, source, sourceProtoCache, substituions);
+              if ((positions & previous) != 0) {
+                result = true;
+                break;
+              }
             }
           }
         }
       }
-      return false;
+      timing.end();
+      return result;
     }
 
     private void fillSeenPositions(Collection<DexMethod> invokes) {
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index ff2aa68..e8cf59b 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -13,6 +13,8 @@
 // Finally a report is printed by:
 //     t.report();
 
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Stack;
 
 public class Timing {
@@ -27,23 +29,28 @@
   static class Node {
     final String title;
 
-    final Stack<Node> sons = new Stack<>();
-    final long start_time;
-    long stop_time;
+    final Map<String, Node> children = new LinkedHashMap<>();
+    long duration = 0;
+    long start_time;
 
     Node(String title) {
       this.title = title;
       this.start_time = System.nanoTime();
-      this.stop_time = -1;
+    }
+
+    void restart() {
+      assert start_time == -1;
+      start_time = System.nanoTime();
     }
 
     void end() {
-      stop_time = System.nanoTime();
+      duration += System.nanoTime() - start_time;
+      start_time = -1;
       assert duration() >= 0;
     }
 
     long duration() {
-      return stop_time - start_time;
+      return duration;
     }
 
     @Override
@@ -66,15 +73,22 @@
         System.out.print("- ");
       }
       System.out.println(toString(top));
-      sons.forEach(p -> { p.report(depth + 1, top); });
+      children.values().forEach(p -> p.report(depth + 1, top));
     }
   }
 
 
   public void begin(String title) {
-    Node n = new Node(title);
-    stack.peek().sons.add(n);
-    stack.push(n);
+    Node parent = stack.peek();
+    Node child;
+    if (parent.children.containsKey(title)) {
+      child = parent.children.get(title);
+      child.restart();
+    } else {
+      child = new Node(title);
+      parent.children.put(title, child);
+    }
+    stack.push(child);
   }
 
   public void end() {