Merge "Also rematerialize constant string values"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 7b3eef0..2734ff4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.DebugLocalsChange;
 import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
@@ -266,9 +266,10 @@
       // Mapping from register number to const number instructions for this basic block.
       // Used to remove redundant const instructions that reloads the same constant into
       // the same register.
-      Map<Integer, ConstNumber> registerToNumber = new HashMap<>();
+      Map<Integer, ConstInstruction> registerToConstant = new HashMap<>();
       MoveEliminator moveEliminator = new MoveEliminator(allocator);
       ListIterator<Instruction> iterator = block.getInstructions().listIterator();
+      boolean mayNoLongerThrow = false;
       while (iterator.hasNext()) {
         Instruction current = iterator.next();
         if (moveEliminator.shouldBeEliminated(current)) {
@@ -276,28 +277,30 @@
         } else if (current.outValue() != null && current.outValue().needsRegister()) {
           Value outValue = current.outValue();
           int instructionNumber = current.getNumber();
-          if (outValue.isConstant() && current.isConstNumber()) {
-            if (constantSpilledAtDefinition(current.asConstNumber(), allocator)) {
+          if (!outValue.hasLocalInfo() && (current.isConstNumber() || current.isConstString())) {
+            if (constantSpilledAtDefinition(current.asConstInstruction(), allocator)) {
               // Remove constant instructions that are spilled at their definition and are
               // therefore unused.
               iterator.remove();
-              continue;
-            }
-            int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
-            ConstNumber numberInRegister = registerToNumber.get(outRegister);
-            if (numberInRegister != null
-                && numberInRegister.identicalNonValueNonPositionParts(current)) {
-              // This instruction is not needed, the same constant is already in this register.
-              // We don't consider the positions of the two (non-throwing) instructions.
-              iterator.remove();
+              mayNoLongerThrow |= current.instructionTypeCanThrow();
             } else {
-              // Insert the current constant in the mapping. Make sure to clobber the second
-              // register if wide and register-1 if that defines a wide value.
-              registerToNumber.put(outRegister, current.asConstNumber());
-              if (current.outType().isWide()) {
-                registerToNumber.remove(outRegister + 1);
+              int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+              ConstInstruction constantInRegister = registerToConstant.get(outRegister);
+              if (constantInRegister != null
+                  && constantInRegister.identicalNonValueNonPositionParts(current)) {
+                // This instruction is not needed, the same constant is already in this register.
+                // We don't consider the positions of the two (non-throwing) instructions.
+                iterator.remove();
+                mayNoLongerThrow |= current.instructionTypeCanThrow();
               } else {
-                removeWideConstantCovering(registerToNumber, outRegister);
+                // Insert the current constant in the mapping. Make sure to clobber the second
+                // register if wide and register-1 if that defines a wide value.
+                registerToConstant.put(outRegister, current.asConstNumber());
+                if (current.outType().isWide()) {
+                  registerToConstant.remove(outRegister + 1);
+                } else {
+                  removeWideConstantCovering(registerToConstant, outRegister);
+                }
               }
             }
           } else {
@@ -305,32 +308,40 @@
             // from the mapping.
             int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
             for (int i = 0; i < outValue.requiredRegisters(); i++) {
-              registerToNumber.remove(outRegister + i);
+              registerToConstant.remove(outRegister + i);
             }
             // Check if the first register written is the second part of a wide value. If so
             // the wide value is no longer active.
-            removeWideConstantCovering(registerToNumber, outRegister);
+            removeWideConstantCovering(registerToConstant, outRegister);
           }
         }
       }
+
+      if (mayNoLongerThrow && block.hasCatchHandlers() && !block.canThrow()) {
+        block.clearCatchHandlers();
+      }
     }
   }
 
   private static void removeWideConstantCovering(
-      Map<Integer, ConstNumber> registerToNumber, int register) {
-    ConstNumber number = registerToNumber.get(register - 1);
-    if (number != null && number.outType().isWide()) {
-      registerToNumber.remove(register - 1);
+      Map<Integer, ConstInstruction> registerToConstant, int register) {
+    ConstInstruction constant = registerToConstant.get(register - 1);
+    if (constant != null && constant.outType().isWide()) {
+      registerToConstant.remove(register - 1);
     }
   }
 
   private static boolean constantSpilledAtDefinition(
-      ConstNumber constNumber, LinearScanRegisterAllocator allocator) {
-    if (constNumber.outValue().isFixedRegisterValue()) {
+      ConstInstruction constInstruction, LinearScanRegisterAllocator allocator) {
+    assert constInstruction.isConstNumber() || constInstruction.isConstString();
+    if (constInstruction.outValue().isFixedRegisterValue()) {
       return false;
     }
     LiveIntervals definitionIntervals =
-        constNumber.outValue().getLiveIntervals().getSplitCovering(constNumber.getNumber());
+        constInstruction
+            .outValue()
+            .getLiveIntervals()
+            .getSplitCovering(constInstruction.getNumber());
     return definitionIntervals.isSpilledAndRematerializable(allocator);
   }
 }
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 1c0e761..aca1f85 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
@@ -673,7 +673,7 @@
       // const number instructions are for values that can be rematerialized instead of
       // spilled.
       assert instruction.getNumber() == -1;
-      assert instruction.isMove() || instruction.isConstNumber();
+      assert instruction.isMove() || instruction.isConstNumber() || instruction.isConstString();
       assert !instruction.isDebugInstruction();
       return true;
     }
@@ -1928,8 +1928,11 @@
         newActive.add(splitChild);
         // If the constant is split before its first actual use, mark the constant as being
         // spilled. That will allows us to remove it afterwards if it is rematerializable.
-        if (intervals.getValue().isConstNumber()
-            && intervals.getStart() == intervals.getValue().definition.getNumber()
+        Value intervalsValue = intervals.getValue();
+        boolean isRematerializableConstantValue =
+            intervalsValue.isConstNumber() || intervalsValue.isConstString();
+        if (isRematerializableConstantValue
+            && intervals.getStart() == intervalsValue.definition.getNumber()
             && intervals.getUses().size() == 1) {
           intervals.setSpilled(true);
         }
@@ -1939,9 +1942,7 @@
             LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
             splitOfSplit.setRegister(intervals.getRegister());
             inactive.add(splitOfSplit);
-          } else if (intervals.getValue().isConstNumber()) {
-            // TODO(ager): Do this for all constants. Currently we only rematerialize const
-            // number and therefore we only do it for numbers at this point.
+          } else if (isRematerializableConstantValue) {
             splitRangesForSpilledConstant(splitChild, registerNumber);
           } else if (intervals.isArgumentInterval()) {
             splitRangesForSpilledArgument(splitChild);
@@ -1972,6 +1973,7 @@
     // register for as long as possible to avoid further moves.
     assert spilled.isSpilled();
     assert !spilled.getValue().isConstNumber();
+    assert !spilled.getValue().isConstString();
     assert !spilled.isLinked() || spilled.isArgumentInterval();
     boolean isSpillingToArgumentRegister =
         (spilled.isArgumentInterval() || registerNumber < numberOfArgumentRegisters);
@@ -2004,7 +2006,7 @@
     // spill we are running low on registers and this constant should get out of the way
     // as much as possible.
     assert spilled.isSpilled();
-    assert spilled.getValue().isConstNumber();
+    assert spilled.getValue().isConstNumber() || spilled.getValue().isConstString();
     assert !spilled.isLinked() || spilled.isArgumentInterval();
     // Do not split range if constant is reused by one of the eleven following instruction.
     int maxGapSize = 11 * INSTRUCTION_NUMBER_DELTA;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 7e398e6..d7c6588 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -99,8 +99,8 @@
     if (value.isArgument()) {
       return true;
     }
-    // TODO(ager): rematerialize const string as well.
-    if (!value.isConstNumber()) {
+    boolean isRematerializableConstantValue = value.isConstNumber() || value.isConstString();
+    if (!isRematerializableConstantValue) {
       return false;
     }
     // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index 645552f..afec916 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -4,8 +4,10 @@
 package com.android.tools.r8.ir.regalloc;
 
 import com.android.tools.r8.code.MoveType;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.FixedRegisterValue;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -130,16 +132,22 @@
   private Integer createMove(RegisterMove move) {
     Instruction instruction;
     if (move.definition != null) {
-      if (move.definition.isArgument()) {
-        Argument argument = move.definition.asArgument();
+      Instruction definition = move.definition;
+      if (definition.isArgument()) {
+        Argument argument = definition.asArgument();
         int argumentRegister = argument.outValue().getLiveIntervals().getRegister();
         Value to = new FixedRegisterValue(argument.outType(), move.dst);
         Value from = new FixedRegisterValue(argument.outType(), argumentRegister);
         instruction = new Move(to, from);
       } else {
-        ConstNumber number = move.definition.asConstNumber();
-        Value to = new FixedRegisterValue(number.outType(), move.dst);
-        instruction = new ConstNumber(to, number.getRawValue());
+        Value to = new FixedRegisterValue(definition.outType(), move.dst);
+        if (definition.isConstNumber()) {
+          instruction = new ConstNumber(to, definition.asConstNumber().getRawValue());
+        } else if (definition.isConstString()) {
+          instruction = new ConstString(to, definition.asConstString().getValue());
+        } else {
+          throw new Unreachable("Unexpected definition");
+        }
       }
     } else {
       Value to = new FixedRegisterValue(move.type.toValueType(), move.dst);