Merge "Support for -adaptresourcefilecontents"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index a8059bd..df6e65a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2448,16 +2448,73 @@
           // Zero test with a value range, or comparison between between two values,
           // each with a value ranges.
           if (theIf.isZeroTest()) {
-            if (!inValues.get(0).isValueInRange(0)) {
-              int cond = Long.signum(inValues.get(0).getValueRange().getMin());
-              simplifyIfWithKnownCondition(code, block, theIf, cond);
+            LongInterval interval = inValues.get(0).getValueRange();
+            if (!interval.containsValue(0)) {
+              // Interval doesn't contain zero at all.
+              int sign = Long.signum(interval.getMin());
+              simplifyIfWithKnownCondition(code, block, theIf, sign);
+            } else {
+              // Interval contains zero.
+              switch (theIf.getType()) {
+                case GE:
+                case LT:
+                  // [a, b] >= 0 is always true if a >= 0.
+                  // [a, b] < 0 is always false if a >= 0.
+                  // In both cases a zero condition takes the right branch.
+                  if (interval.getMin() == 0) {
+                    simplifyIfWithKnownCondition(code, block, theIf, 0);
+                  }
+                  break;
+                case LE:
+                case GT:
+                  // [a, b] <= 0 is always true if b <= 0.
+                  // [a, b] > 0 is always false if b <= 0.
+                  if (interval.getMax() == 0) {
+                    simplifyIfWithKnownCondition(code, block, theIf, 0);
+                  }
+                  break;
+                case EQ:
+                case NE:
+                  // Only a single element interval [0, 0] can be dealt with here.
+                  // Such intervals should have been replaced by constants.
+                  assert !interval.isSingleValue();
+                  break;
+              }
             }
           } else {
             LongInterval leftRange = inValues.get(0).getValueRange();
             LongInterval rightRange = inValues.get(1).getValueRange();
+            // Two overlapping ranges. Check for single point overlap.
             if (!leftRange.overlapsWith(rightRange)) {
+              // No overlap.
               int cond = Long.signum(leftRange.getMin() - rightRange.getMin());
               simplifyIfWithKnownCondition(code, block, theIf, cond);
+            } else {
+              // The two intervals overlap. We can simplify if they overlap at the end points.
+              switch (theIf.getType()) {
+                case LT:
+                case GE:
+                  // [a, b] < [c, d] is always false when a == d.
+                  // [a, b] >= [c, d] is always true when a == d.
+                  // In both cases 0 condition will choose the right branch.
+                  if (leftRange.getMin() == rightRange.getMax()) {
+                    simplifyIfWithKnownCondition(code, block, theIf, 0);
+                  }
+                  break;
+                case GT:
+                case LE:
+                  // [a, b] > [c, d] is always false when b == c.
+                  // [a, b] <= [c, d] is always true when b == c.
+                  // In both cases 0 condition will choose the right branch.
+                  if (leftRange.getMax() == rightRange.getMin()) {
+                    simplifyIfWithKnownCondition(code, block, theIf, 0);
+                  }
+                  break;
+                case EQ:
+                case NE:
+                  // Since there is overlap EQ and NE cannot be determined.
+                  break;
+              }
             }
           }
         } else if (theIf.isZeroTest() && !inValues.get(0).isConstNumber()
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index a9df6a3..8fd09d0 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -40,8 +40,10 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntArraySet;
 import it.unimi.dsi.fastutil.ints.IntIterator;
+import it.unimi.dsi.fastutil.ints.IntList;
 import it.unimi.dsi.fastutil.ints.IntSet;
 import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -144,6 +146,10 @@
   // List of intervals that no register has been allocated to sorted by first live range.
   protected PriorityQueue<LiveIntervals> unhandled = new PriorityQueue<>();
 
+  // The registers that have been released as a result of advancing to the next live intervals.
+  // A register is released if an active or inactive interval becomes handled.
+  private IntList expiredHere = new IntArrayList();
+
   // List of intervals for the result of move-exception instructions.
   // Always empty in mode ALLOW_ARGUMENT_REUSE.
   private List<LiveIntervals> moveExceptionIntervals = new ArrayList<>();
@@ -826,6 +832,7 @@
     // Go through each unhandled live interval and find a register for it.
     while (!unhandled.isEmpty()) {
       assert invariantsHold(mode);
+      expiredHere.clear();
 
       LiveIntervals unhandledInterval = unhandled.poll();
 
@@ -849,6 +856,12 @@
         if (start >= activeIntervals.getEnd()) {
           activeIterator.remove();
           freeOccupiedRegistersForIntervals(activeIntervals);
+          if (start == activeIntervals.getEnd()) {
+            expiredHere.add(activeIntervals.getRegister());
+            if (activeIntervals.getType().isWide()) {
+              expiredHere.add(activeIntervals.getRegister() + 1);
+            }
+          }
         } else if (!activeIntervals.overlapsPosition(start)) {
           activeIterator.remove();
           assert activeIntervals.getRegister() != NO_REGISTER;
@@ -863,6 +876,12 @@
         LiveIntervals inactiveIntervals = inactiveIterator.next();
         if (start >= inactiveIntervals.getEnd()) {
           inactiveIterator.remove();
+          if (start == inactiveIntervals.getEnd()) {
+            expiredHere.add(inactiveIntervals.getRegister());
+            if (inactiveIntervals.getType().isWide()) {
+              expiredHere.add(inactiveIntervals.getRegister() + 1);
+            }
+          }
         } else if (inactiveIntervals.overlapsPosition(start)) {
           inactiveIterator.remove();
           assert inactiveIntervals.getRegister() != NO_REGISTER;
@@ -1153,17 +1172,137 @@
     return false;
   }
 
-  private int getSpillRegister(LiveIntervals intervals) {
+  private int getNewSpillRegister(LiveIntervals intervals) {
     if (intervals.isArgumentInterval()) {
       return intervals.getSplitParent().getRegister();
     }
 
     int register = maxRegisterNumber + 1;
     increaseCapacity(maxRegisterNumber + intervals.requiredRegisters());
+    return register;
+  }
+
+  private int getSpillRegister(LiveIntervals intervals, IntList excludedRegisters) {
+    if (intervals.isArgumentInterval()) {
+      return intervals.getSplitParent().getRegister();
+    }
+
+    TreeSet<Integer> previousFreeRegisters = new TreeSet<>(freeRegisters);
+    int previousMaxRegisterNumber = maxRegisterNumber;
+    freeRegisters.removeAll(expiredHere);
+    if (excludedRegisters != null) {
+      freeRegisters.removeAll(excludedRegisters);
+    }
+
+    // Check if we can use a register that was previously used as a register for intervals.
+    // This could lead to fewer moves during resolution.
+    int register = -1;
+    for (LiveIntervals split : intervals.getSplitParent().getSplitChildren()) {
+      int candidate = split.getRegister();
+      if (candidate != NO_REGISTER
+          && registersAreFreeAndConsecutive(candidate, intervals.getType().isWide())
+          && maySpillLiveIntervalsToRegister(intervals, candidate, previousMaxRegisterNumber)) {
+        register = candidate;
+        break;
+      }
+    }
+
+    if (register == -1) {
+      do {
+        // If the register needs to fit in 4 bits at the next use, then prioritize a small register.
+        // If we can find a small register, we do not need to insert a move at the next use.
+        boolean prioritizeSmallRegisters =
+            !intervals.getUses().isEmpty()
+                && intervals.getUses().first().getLimit() == Constants.U4BIT_MAX;
+        register =
+            getFreeConsecutiveRegisters(intervals.requiredRegisters(), prioritizeSmallRegisters);
+      } while (!maySpillLiveIntervalsToRegister(intervals, register, previousMaxRegisterNumber));
+    }
+
+    // Going to spill to the register (pair).
+    freeRegisters = previousFreeRegisters;
+    // If getFreeConsecutiveRegisters had to increment |maxRegisterNumber|, we need to update
+    // freeRegisters.
+    for (int i = previousMaxRegisterNumber + 1; i <= maxRegisterNumber; ++i) {
+      freeRegisters.add(i);
+    }
     assert registersAreFree(register, intervals.getType().isWide());
     return register;
   }
 
+  private boolean maySpillLiveIntervalsToRegister(
+      LiveIntervals intervals, int register, int previousMaxRegisterNumber) {
+    if (register > previousMaxRegisterNumber) {
+      // Nothing can prevent us from spilling to an entirely fresh register.
+      return true;
+    }
+
+    // If we are about to spill to an argument register, we need to be careful that the live range
+    // that is being spilled does not overlap with the live range of the corresponding argument.
+    //
+    // Note that this is *not* guaranteed when overlapsInactiveIntervals is null, because it is
+    // possible that some live ranges of the argument are still in the unhandled set.
+    if (register < numberOfArgumentRegisters) {
+      // Find the first argument value that uses the given register.
+      LiveIntervals argumentLiveIntervals = firstArgumentValue.getLiveIntervals();
+      while (!argumentLiveIntervals.usesRegister(register, intervals.getType().isWide())) {
+        argumentLiveIntervals = argumentLiveIntervals.getNextConsecutive();
+        assert argumentLiveIntervals != null;
+      }
+      do {
+        if (argumentLiveIntervals.anySplitOverlaps(intervals)) {
+          // Remove so that next invocation of getFreeConsecutiveRegisters does not consider this.
+          freeRegisters.remove(register);
+          // We have just established that there is an overlap between the live range of the
+          // current argument and the live range we need to find a register for. Therefore, if
+          // the argument is wide, and the current register corresponds to the low register of the
+          // argument, we know that the subsequent register will not work either.
+          if (register == argumentLiveIntervals.getRegister()
+              && argumentLiveIntervals.getType().isWide()) {
+            freeRegisters.remove(register + 1);
+          }
+          return false;
+        }
+        // The next argument live interval may also use the register, if it is a wide register pair.
+        argumentLiveIntervals = argumentLiveIntervals.getNextConsecutive();
+      } while (argumentLiveIntervals != null
+          && argumentLiveIntervals.usesRegister(register, intervals.getType().isWide()));
+    }
+
+    // Check for overlap with inactive intervals.
+    LiveIntervals overlapsInactiveIntervals = null;
+    for (LiveIntervals inactiveIntervals : inactive) {
+      if (inactiveIntervals.usesRegister(register, intervals.getType().isWide())
+          && intervals.overlaps(inactiveIntervals)) {
+        overlapsInactiveIntervals = inactiveIntervals;
+        break;
+      }
+    }
+    if (overlapsInactiveIntervals != null) {
+      // Remove so that next invocation of getFreeConsecutiveRegisters does not consider this.
+      freeRegisters.remove(register);
+      if (register == overlapsInactiveIntervals.getRegister()
+          && overlapsInactiveIntervals.getType().isWide()) {
+        freeRegisters.remove(register + 1);
+      }
+      return false;
+    }
+
+    // Check for overlap with the move exception interval.
+    boolean overlapsMoveExceptionInterval =
+        hasDedicatedMoveExceptionRegister()
+            && (register == getMoveExceptionRegister()
+                || (intervals.getType().isWide() && register + 1 == getMoveExceptionRegister()))
+            && overlapsMoveExceptionInterval(intervals);
+    if (overlapsMoveExceptionInterval) {
+      // Remove so that next invocation of getFreeConsecutiveRegisters does not consider this.
+      freeRegisters.remove(register);
+      return false;
+    }
+
+    return true;
+  }
+
   private int toInstructionPosition(int position) {
     return position % 2 == 0 ? position : position + 1;
   }
@@ -1496,7 +1635,7 @@
       // of finding another candidate to spill via allocateBlockedRegister.
       if (!unhandledInterval.getUses().first().hasConstraint()) {
         int nextConstrainedPosition = unhandledInterval.firstUseWithConstraint().getPosition();
-        int register = getSpillRegister(unhandledInterval);
+        int register = getSpillRegister(unhandledInterval, null);
         LiveIntervals split = unhandledInterval.splitBefore(nextConstrainedPosition);
         assignFreeRegisterToUnhandledInterval(unhandledInterval, register);
         unhandled.add(split);
@@ -1839,7 +1978,8 @@
       int splitPosition = unhandledInterval.getFirstUse();
       LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
       assert split != unhandledInterval;
-      int registerNumber = getSpillRegister(unhandledInterval);
+      // Experiments show that it has a positive impact on code size to use a fresh register here.
+      int registerNumber = getNewSpillRegister(unhandledInterval);
       assignFreeRegisterToUnhandledInterval(unhandledInterval, registerNumber);
       unhandledInterval.setSpilled(true);
       unhandled.add(split);
@@ -1926,6 +2066,18 @@
       LiveIntervals unhandledInterval, int candidate, boolean candidateIsWide) {
     assert unhandledInterval.getRegister() == NO_REGISTER;
     assert atLeastOneOfRegistersAreTaken(candidate, candidateIsWide);
+    // Registers that we cannot choose for spilling.
+    IntList excludedRegisters = new IntArrayList(candidateIsWide ? 2 : 1);
+    excludedRegisters.add(candidate);
+    if (candidateIsWide) {
+      excludedRegisters.add(candidate + 1);
+    }
+    if (unhandledInterval.isArgumentInterval()
+        && unhandledInterval != unhandledInterval.getSplitParent()) {
+      // This live interval will become active in its original argument register and in the
+      // candidate register simultaneously.
+      unhandledInterval.getSplitParent().forEachRegister(excludedRegisters::add);
+    }
     // Spill overlapping active intervals.
     List<LiveIntervals> newActive = new ArrayList<>();
     Iterator<LiveIntervals> activeIterator = active.iterator();
@@ -1934,7 +2086,7 @@
       assert registersForIntervalsAreTaken(intervals);
       if (intervals.usesRegister(candidate, candidateIsWide)) {
         activeIterator.remove();
-        int registerNumber = getSpillRegister(intervals);
+        int registerNumber = getSpillRegister(intervals, excludedRegisters);
         // Important not to free the registers for intervals before finding a spill register,
         // because we might otherwise end up spilling to the current registers of intervals,
         // depending on getSpillRegister.
@@ -2662,8 +2814,33 @@
   }
 
   private int getFreeConsecutiveRegisters(int numberOfRegisters) {
+    return getFreeConsecutiveRegisters(numberOfRegisters, false);
+  }
+
+  private int getFreeConsecutiveRegisters(int numberOfRegisters, boolean prioritizeSmallRegisters) {
     int oldMaxRegisterNumber = maxRegisterNumber;
-    Iterator<Integer> freeRegistersIterator = freeRegisters.iterator();
+    TreeSet<Integer> freeRegistersWithDesiredOrdering = this.freeRegisters;
+    if (prioritizeSmallRegisters) {
+      freeRegistersWithDesiredOrdering =
+          new TreeSet<>(
+              (Integer x, Integer y) -> {
+                boolean xIsArgument = x < numberOfArgumentRegisters;
+                boolean yIsArgument = y < numberOfArgumentRegisters;
+                // If x is an argument and y is not, then prioritize y.
+                if (xIsArgument && !yIsArgument) {
+                  return 1;
+                }
+                // If x is not an argument and y is, then prioritize x.
+                if (!xIsArgument && yIsArgument) {
+                  return -1;
+                }
+                // Otherwise use their normal ordering.
+                return x - y;
+              });
+      freeRegistersWithDesiredOrdering.addAll(this.freeRegisters);
+    }
+
+    Iterator<Integer> freeRegistersIterator = freeRegistersWithDesiredOrdering.iterator();
     int first = getNextFreeRegister(freeRegistersIterator);
     int current = first;
     while (current - first + 1 != numberOfRegisters) {
@@ -2692,6 +2869,22 @@
     return first;
   }
 
+  private boolean registersAreFreeAndConsecutive(int register, boolean registerIsWide) {
+    if (!freeRegisters.contains(register)) {
+      return false;
+    }
+    if (registerIsWide) {
+      if (!freeRegisters.contains(register + 1)) {
+        return false;
+      }
+      if (register == numberOfArgumentRegisters - 1) {
+        // Will not be consecutive after reordering the arguments and temporaries.
+        return false;
+      }
+    }
+    return true;
+  }
+
   private int getNextFreeRegister(Iterator<Integer> freeRegistersIterator) {
     if (freeRegistersIterator.hasNext()) {
       return freeRegistersIterator.next();
diff --git a/src/test/examples/assumevalues6/Assumevalues.java b/src/test/examples/assumevalues6/Assumevalues.java
new file mode 100644
index 0000000..b3ad1de
--- /dev/null
+++ b/src/test/examples/assumevalues6/Assumevalues.java
@@ -0,0 +1,45 @@
+// 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 assumevalues6;
+
+public class Assumevalues {
+  public static int field = 0;
+  public static int field2 = 2;
+  public static int field3 = 2;
+
+  public static void main(String[] args) {
+    if (0 > field) {
+      System.out.println("NOPE1");
+    }
+    if (field < 0) {
+      System.out.println("NOPE2");
+    }
+    if (field3 == 0) {
+      System.out.println("NOPE3");
+    }
+    if (field3 != 0) {
+      System.out.println("YUP1");
+    }
+    if (2 < field) {
+      System.out.println("NOPE4");
+    }
+    if (field > 2) {
+      System.out.println("NOPE5");
+    }
+    if (field2 < field) {
+      System.out.println("NOPE6");
+    }
+    if (field > field2) {
+      System.out.println("NOPE7");
+    }
+    if (field <= field2) {
+      System.out.println("YUP2");
+    }
+    if (field2 >= field) {
+      System.out.println("YUP3");
+    }
+    System.out.println("OK");
+  }
+}
diff --git a/src/test/examples/assumevalues6/keep-rules.txt b/src/test/examples/assumevalues6/keep-rules.txt
new file mode 100644
index 0000000..4910a9d
--- /dev/null
+++ b/src/test/examples/assumevalues6/keep-rules.txt
@@ -0,0 +1,16 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep class assumevalues6.Assumevalues {
+  void main(...);
+}
+
+# Mark some fields with value ranges.
+-assumevalues public class assumevalues6.Assumevalues {
+  int field return 0..2;
+  int field2 return 2..4;
+  int field3 return 2..2;
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues6Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues6Test.java
new file mode 100644
index 0000000..e093f00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues6Test.java
@@ -0,0 +1,69 @@
+// 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumevalues6Test extends TreeShakingTest {
+
+  @Parameters(name = "mode:{0}-{1} minify:{2}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (MinifyMode minify : MinifyMode.values()) {
+      parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+      parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+      parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+    }
+    return parameters;
+  }
+
+  public TreeShakingAssumevalues6Test(Frontend frontend, Backend backend, MinifyMode minify) {
+    super("examples/assumevalues6", "assumevalues6.Assumevalues", frontend, backend, minify);
+  }
+
+  @Test
+  public void test() throws Exception {
+    runTest(
+        getBackend() == Backend.DEX ? TreeShakingAssumevalues6Test::assumevalues6CheckCode : null,
+        TreeShakingAssumevalues6Test::assumevalues6CheckOutput,
+        null,
+        ImmutableList.of("src/test/examples/assumevalues6/keep-rules.txt"));
+  }
+
+  private static void assumevalues6CheckCode(CodeInspector inspector) {
+    inspector.forAllClasses(c -> {
+      c.forAllMethods(m -> {
+        if (m.getFinalName().equals("main")) {
+          m.iterateInstructions().forEachRemaining(i -> {
+            if (i.isConstString(JumboStringMode.ALLOW)) {
+              ConstStringInstructionSubject str = (ConstStringInstructionSubject) i;
+              assert !str.getString().toASCIIString().contains("NOPE");
+            }
+          });
+        }
+      });
+    });
+  }
+
+  private static void assumevalues6CheckOutput(String output1, String output2) {
+    String expected = StringUtils.lines("YUP1", "YUP2", "YUP3", "OK");
+    Assert.assertEquals(expected, output1);
+    Assert.assertEquals(expected, output2);
+  }
+}
\ No newline at end of file