Merge "Do not shared too much constants in dominator block of usages"
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index f0ff57c..0c4697d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -97,11 +97,12 @@
 
   public abstract void buildDex(DexBuilder builder);
 
-  public void replacePhi(Phi phi, Value value) {
+  public void replaceValue(Value oldValue, Value newValue) {
     for (int i = 0; i < inValues.size(); i++) {
-      if (phi == inValues.get(i)) {
-        inValues.set(i, value);
-        value.addUser(this);
+      if (oldValue == inValues.get(i)) {
+        inValues.set(i, newValue);
+        newValue.addUser(this);
+        oldValue.removeUser(this);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index cef1394..1abdb1d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -196,7 +196,7 @@
     }
     // Replace this phi with the unique value in all users.
     for (Instruction user : uniqueUsers()) {
-      user.replacePhi(this, same);
+      user.replaceValue(this, same);
     }
     for (Phi user : uniquePhiUsers()) {
       user.replaceTrivialPhi(this, same);
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 4573768..764beb5 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
@@ -63,6 +63,7 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -73,6 +74,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public class CodeRewriter {
 
@@ -80,6 +82,8 @@
   private static final int CAN_THROW = 1;
   private static final int CANNOT_THROW = 2;
   private static final int MAX_FILL_ARRAY_SIZE = 8 * Constants.KILOBYTE;
+  // This constant was determined by experimentation.
+  private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
 
   private final AppInfo appInfo;
   private final DexItemFactory dexItemFactory;
@@ -876,6 +880,7 @@
     // Currently, we are only shortening the live range of constants in the entry block.
     // TODO(ager): Generalize this to shorten live ranges for more instructions? Currently
     // doing so seems to make things worse.
+    Map<BasicBlock, List<Instruction>> addConstantInBlock = new HashMap<>();
     DominatorTree dominatorTree = new DominatorTree(code);
     BasicBlock block = code.blocks.get(0);
     InstructionListIterator it = block.listIterator();
@@ -904,15 +909,55 @@
         // Move the const instruction as close to its uses as possible.
         it.detach();
         if (dominator != block) {
-          insertConstantInBlock(instruction, dominator);
+          // Post-pone constant insertion in order to use a global heuristics.
+          List<Instruction> csts = addConstantInBlock.get(dominator);
+          if (csts == null) {
+            csts = new ArrayList<>();
+            addConstantInBlock.put(dominator, csts);
+          }
+          csts.add(instruction);
         } else {
           toInsertInThisBlock.add(instruction);
         }
       }
     }
+
+    // Heuristic to decide if constant instructions are shared in dominator block of usages or move
+    // to the usages.
+    for (Map.Entry<BasicBlock, List<Instruction>> entry : addConstantInBlock.entrySet()) {
+      if (entry.getValue().size() > STOP_SHARED_CONSTANT_THRESHOLD) {
+        // Too much constants in the same block, do not longer shared them except if they are used
+        // by phi instructions.
+        for (Instruction instruction : entry.getValue()) {
+          if (instruction.outValue().numberOfPhiUsers() != 0) {
+            // Add constant into the dominator block of usages.
+            insertConstantInBlock(instruction, entry.getKey());
+          } else {
+            assert instruction.outValue().numberOfUsers() != 0;
+            ConstNumber constNumber = instruction.asConstNumber();
+            Value constantValue = instruction.outValue();
+            for (Instruction user : constantValue.uniqueUsers()) {
+              ConstNumber newCstNum = ConstNumber.copyOf(code, constNumber);
+              InstructionListIterator iterator = user.getBlock().listIterator();
+              iterator.nextUntil((x) -> x == user);
+              iterator.previous();
+              iterator.add(newCstNum);
+              user.replaceValue(constantValue, newCstNum.outValue());
+            }
+          }
+        }
+      } else {
+        // Add constant into the dominator block of usages.
+        for (Instruction inst : entry.getValue()) {
+          insertConstantInBlock(inst, entry.getKey());
+        }
+      }
+    }
+
     for (Instruction toInsert : toInsertInThisBlock) {
       insertConstantInBlock(toInsert, block);
     }
+    assert code.isConsistentSSA();
   }
 
   private void insertConstantInBlock(Instruction instruction, BasicBlock block) {