Split dex constant optimizer from the CodeRewriter

Bug: b/284304606
Change-Id: I4663cafed4e79b930986e9803ceb3e90e8a0528c
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index cba30d9..bc82bc4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.ir.conversion.passes.BinopRewriter;
 import com.android.tools.r8.ir.conversion.passes.BranchSimplifier;
 import com.android.tools.r8.ir.conversion.passes.CommonSubexpressionElimination;
+import com.android.tools.r8.ir.conversion.passes.DexConstantOptimizer;
 import com.android.tools.r8.ir.conversion.passes.ParentConstructorHoistingCodeRewriter;
 import com.android.tools.r8.ir.conversion.passes.SplitBranch;
 import com.android.tools.r8.ir.conversion.passes.ThrowCatchOptimizer;
@@ -883,14 +884,8 @@
       constantCanonicalizer.canonicalize();
       timing.end();
       previous = printMethod(code, "IR after constant canonicalization (SSA)", previous);
-      timing.begin("Create constants for literal instructions");
-      codeRewriter.useDedicatedConstantForLitInstruction(code);
-      timing.end();
-      previous = printMethod(code, "IR after constant literals (SSA)", previous);
-      timing.begin("Shorten live ranges");
-      codeRewriter.shortenLiveRanges(code, constantCanonicalizer);
-      timing.end();
-      previous = printMethod(code, "IR after shorten live ranges (SSA)", previous);
+      new DexConstantOptimizer(appView, constantCanonicalizer).run(context, code, timing);
+      previous = printMethod(code, "IR after dex constant optimization (SSA)", previous);
     }
 
     if (removeVerificationErrorForUnknownReturnedValues != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
new file mode 100644
index 0000000..1e00a5e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
@@ -0,0 +1,493 @@
+// Copyright (c) 2023, 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.ir.conversion.passes;
+
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.Binop;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InstructionOrPhi;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.ConstantCanonicalizer;
+import com.android.tools.r8.utils.LazyBox;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+public class DexConstantOptimizer extends CodeRewriterPass<AppInfo> {
+
+  // This constant was determined by experimentation.
+  private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
+
+  private final ConstantCanonicalizer constantCanonicalizer;
+
+  public DexConstantOptimizer(AppView<?> appView, ConstantCanonicalizer constantCanonicalizer) {
+    super(appView);
+    this.constantCanonicalizer = constantCanonicalizer;
+  }
+
+  @Override
+  String getTimingId() {
+    return "DexConstantOptimizer";
+  }
+
+  @Override
+  void rewriteCode(ProgramMethod method, IRCode code) {
+    useDedicatedConstantForLitInstruction(code);
+    shortenLiveRanges(code, constantCanonicalizer);
+  }
+
+  @Override
+  boolean shouldRewriteCode(ProgramMethod method, IRCode code) {
+    return true;
+  }
+
+  /**
+   * If an instruction is known to be a binop/lit8 or binop//lit16 instruction, update the
+   * instruction to use its own constant that will be defined just before the instruction. This
+   * transformation allows to decrease pressure on register allocation by defining the shortest
+   * range of constant used by this kind of instruction. D8 knowns at build time that constant will
+   * be encoded directly into the final Dex instruction.
+   */
+  public void useDedicatedConstantForLitInstruction(IRCode code) {
+    if (!code.metadata().mayHaveArithmeticOrLogicalBinop()) {
+      return;
+    }
+
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator instructionIterator = block.listIterator(code);
+      // Collect all the non constant in values for binop/lit8 or binop/lit16 instructions.
+      Set<Value> binopsWithLit8OrLit16NonConstantValues = Sets.newIdentityHashSet();
+      while (instructionIterator.hasNext()) {
+        Instruction currentInstruction = instructionIterator.next();
+        if (!isBinopWithLit8OrLit16(currentInstruction)) {
+          continue;
+        }
+        Value value = binopWithLit8OrLit16NonConstant(currentInstruction.asBinop());
+        assert value != null;
+        binopsWithLit8OrLit16NonConstantValues.add(value);
+      }
+      if (binopsWithLit8OrLit16NonConstantValues.isEmpty()) {
+        continue;
+      }
+      // Find last use in block of all the non constant in values for binop/lit8 or binop/lit16
+      // instructions.
+      Reference2IntMap<Value> lastUseOfBinopsWithLit8OrLit16NonConstantValues =
+          new Reference2IntOpenHashMap<>();
+      lastUseOfBinopsWithLit8OrLit16NonConstantValues.defaultReturnValue(-1);
+      int currentInstructionNumber = block.getInstructions().size();
+      while (instructionIterator.hasPrevious()) {
+        Instruction currentInstruction = instructionIterator.previous();
+        currentInstructionNumber--;
+        for (Value value :
+            Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
+          if (!binopsWithLit8OrLit16NonConstantValues.contains(value)) {
+            continue;
+          }
+          if (!lastUseOfBinopsWithLit8OrLit16NonConstantValues.containsKey(value)) {
+            lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(value, currentInstructionNumber);
+          }
+        }
+      }
+      // Do the transformation except if the binop can use the binop/2addr format.
+      currentInstructionNumber--;
+      assert currentInstructionNumber == -1;
+      while (instructionIterator.hasNext()) {
+        Instruction currentInstruction = instructionIterator.next();
+        currentInstructionNumber++;
+        if (!isBinopWithLit8OrLit16(currentInstruction)) {
+          continue;
+        }
+        Binop binop = currentInstruction.asBinop();
+        if (!canBe2AddrInstruction(
+            binop, currentInstructionNumber, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
+          Value constValue = binopWithLit8OrLit16Constant(currentInstruction);
+          if (constValue.numberOfAllUsers() > 1) {
+            // No need to do the transformation if the const value is already used only one time.
+            ConstNumber newConstant =
+                ConstNumber.copyOf(code, constValue.definition.asConstNumber());
+            newConstant.setPosition(currentInstruction.getPosition());
+            newConstant.setBlock(currentInstruction.getBlock());
+            currentInstruction.replaceValue(constValue, newConstant.outValue());
+            constValue.removeUser(currentInstruction);
+            instructionIterator.previous();
+            instructionIterator.add(newConstant);
+            instructionIterator.next();
+          }
+        }
+      }
+    }
+
+    assert code.isConsistentSSA(appView);
+  }
+
+  // Check if a binop can be represented in the binop/lit8 or binop/lit16 form.
+  private static boolean isBinopWithLit8OrLit16(Instruction instruction) {
+    if (!instruction.isArithmeticBinop() && !instruction.isLogicalBinop()) {
+      return false;
+    }
+    Binop binop = instruction.asBinop();
+    // If one of the values does not need a register it is implicitly a binop/lit8 or binop/lit16.
+    boolean result =
+        !binop.needsValueInRegister(binop.leftValue())
+            || !binop.needsValueInRegister(binop.rightValue());
+    assert !result || binop.leftValue().isConstNumber() || binop.rightValue().isConstNumber();
+    return result;
+  }
+
+  // Return the constant in-value of a binop/lit8 or binop/lit16 instruction.
+  private static Value binopWithLit8OrLit16Constant(Instruction instruction) {
+    assert isBinopWithLit8OrLit16(instruction);
+    Binop binop = instruction.asBinop();
+    if (binop.leftValue().isConstNumber()) {
+      return binop.leftValue();
+    } else if (binop.rightValue().isConstNumber()) {
+      return binop.rightValue();
+    } else {
+      throw new Unreachable();
+    }
+  }
+
+  // Return the non-constant in-value of a binop/lit8 or binop/lit16 instruction.
+  private static Value binopWithLit8OrLit16NonConstant(Binop binop) {
+    if (binop.leftValue().isConstNumber()) {
+      return binop.rightValue();
+    } else if (binop.rightValue().isConstNumber()) {
+      return binop.leftValue();
+    } else {
+      throw new Unreachable();
+    }
+  }
+
+  /**
+   * Estimate if a binary operation can be a binop/2addr form or not. It can be a 2addr form when an
+   * argument is no longer needed after the binary operation and can be overwritten. That is
+   * definitely the case if there is no path between the binary operation and all other usages.
+   */
+  private static boolean canBe2AddrInstruction(
+      Binop binop, int binopInstructionNumber, Reference2IntMap<Value> lastUseOfRelevantValue) {
+    Value value = binopWithLit8OrLit16NonConstant(binop);
+    assert value != null;
+    int lastUseInstructionNumber = lastUseOfRelevantValue.getInt(value);
+    // The binop instruction is a user, so there is always a last use in the block.
+    assert lastUseInstructionNumber != -1;
+    if (lastUseInstructionNumber > binopInstructionNumber) {
+      return false;
+    }
+
+    Set<BasicBlock> noPathTo = Sets.newIdentityHashSet();
+    BasicBlock binopBlock = binop.getBlock();
+    Iterable<InstructionOrPhi> users =
+        value.debugUsers() != null
+            ? Iterables.concat(value.uniqueUsers(), value.debugUsers(), value.uniquePhiUsers())
+            : Iterables.concat(value.uniqueUsers(), value.uniquePhiUsers());
+    for (InstructionOrPhi user : users) {
+      BasicBlock userBlock = user.getBlock();
+      if (userBlock == binopBlock) {
+        // All users in the current block are either before the binop instruction or the
+        // binop instruction itself.
+        continue;
+      }
+      if (noPathTo.contains(userBlock)) {
+        continue;
+      }
+      if (binopBlock.hasPathTo(userBlock)) {
+        return false;
+      }
+      noPathTo.add(userBlock);
+    }
+
+    return true;
+  }
+
+  public void shortenLiveRanges(IRCode code, ConstantCanonicalizer canonicalizer) {
+    if (options.testing.disableShortenLiveRanges) {
+      return;
+    }
+    LazyBox<DominatorTree> dominatorTreeMemoization = new LazyBox<>(() -> new DominatorTree(code));
+    Map<BasicBlock, LinkedHashMap<Value, Instruction>> addConstantInBlock = new IdentityHashMap<>();
+    LinkedList<BasicBlock> blocks = code.blocks;
+    for (BasicBlock block : blocks) {
+      shortenLiveRangesInsideBlock(
+          code,
+          block,
+          dominatorTreeMemoization,
+          addConstantInBlock,
+          canonicalizer::isConstantCanonicalizationCandidate);
+    }
+
+    // Heuristic to decide if constant instructions are shared in dominator block
+    // of usages or moved to the usages.
+
+    // Process all blocks in stable order to avoid non-determinism of hash map iterator.
+    BasicBlockIterator blockIterator = code.listIterator();
+    while (blockIterator.hasNext()) {
+      BasicBlock block = blockIterator.next();
+      Map<Value, Instruction> movedInstructions = addConstantInBlock.get(block);
+      if (movedInstructions == null) {
+        continue;
+      }
+      assert !movedInstructions.isEmpty();
+      if (!block.isEntry() && movedInstructions.size() > STOP_SHARED_CONSTANT_THRESHOLD) {
+        // If there are too many constant numbers in the same block they are copied rather than
+        // shared unless used by a phi.
+        movedInstructions
+            .values()
+            .removeIf(
+                movedInstruction -> {
+                  if (movedInstruction.outValue().hasPhiUsers()
+                      || !movedInstruction.isConstNumber()) {
+                    return false;
+                  }
+                  ConstNumber constNumber = movedInstruction.asConstNumber();
+                  Value constantValue = movedInstruction.outValue();
+                  for (Instruction user : constantValue.uniqueUsers()) {
+                    ConstNumber newCstNum = ConstNumber.copyOf(code, constNumber);
+                    newCstNum.setPosition(user.getPosition());
+                    InstructionListIterator iterator = user.getBlock().listIterator(code, user);
+                    iterator.previous();
+                    iterator.add(newCstNum);
+                    user.replaceValue(constantValue, newCstNum.outValue());
+                  }
+                  constantValue.clearUsers();
+                  return true;
+                });
+      }
+
+      // Add constant into the dominator block of usages.
+      boolean hasCatchHandlers = block.hasCatchHandlers();
+      InstructionListIterator instructionIterator = block.listIterator(code);
+      while (instructionIterator.hasNext()) {
+        Instruction insertionPoint = instructionIterator.next();
+        if (insertionPoint.isJumpInstruction()
+            || (hasCatchHandlers && insertionPoint.instructionTypeCanThrow())
+            || (options.canHaveCmpIfFloatBug() && insertionPoint.isCmp())) {
+          break;
+        }
+        for (Value use :
+            Iterables.concat(insertionPoint.inValues(), insertionPoint.getDebugValues())) {
+          Instruction movedInstruction = movedInstructions.remove(use);
+          if (movedInstruction != null) {
+            instructionIterator =
+                insertInstructionWithShortenedLiveRange(
+                    code, blockIterator, instructionIterator, movedInstruction, insertionPoint);
+          }
+        }
+      }
+
+      // Insert remaining constant instructions prior to the "exit".
+      Instruction insertionPoint = instructionIterator.peekPrevious();
+      for (Instruction movedInstruction : movedInstructions.values()) {
+        instructionIterator =
+            insertInstructionWithShortenedLiveRange(
+                code, blockIterator, instructionIterator, movedInstruction, insertionPoint);
+      }
+    }
+
+    assert code.isConsistentSSA(appView);
+  }
+
+  private InstructionListIterator insertInstructionWithShortenedLiveRange(
+      IRCode code,
+      BasicBlockIterator blockIterator,
+      InstructionListIterator instructionIterator,
+      Instruction movedInstruction,
+      Instruction insertionPoint) {
+    Instruction previous = instructionIterator.previous();
+    assert previous == insertionPoint;
+    movedInstruction.setPosition(
+        getPositionForMovedNonThrowingInstruction(movedInstruction, insertionPoint));
+    if (movedInstruction.instructionTypeCanThrow()
+        && insertionPoint.getBlock().hasCatchHandlers()) {
+      // Split the block and reset the block iterator.
+      BasicBlock splitBlock =
+          instructionIterator.splitCopyCatchHandlers(code, blockIterator, appView.options());
+      BasicBlock previousBlock = blockIterator.previousUntil(b -> b == splitBlock);
+      assert previousBlock == splitBlock;
+      blockIterator.next();
+
+      // Add the constant instruction before the exit instruction.
+      assert !instructionIterator.hasNext();
+      instructionIterator.previous();
+      instructionIterator.add(movedInstruction);
+
+      // Continue insertion at the entry of the split block.
+      instructionIterator = splitBlock.listIterator(code);
+    } else {
+      instructionIterator.add(movedInstruction);
+    }
+    Instruction next = instructionIterator.next();
+    assert next == insertionPoint;
+    return instructionIterator;
+  }
+
+  private Position getPositionForMovedNonThrowingInstruction(
+      Instruction movedInstruction, Instruction insertionPoint) {
+    // If the type of the moved instruction is throwing and we don't have a position at the
+    // insertion point, we use the special synthetic-none position, which is OK as the moved
+    // instruction instance is known not to throw (or we would not be allowed the move it).
+    if (movedInstruction.instructionTypeCanThrow() && !insertionPoint.getPosition().isSome()) {
+      return Position.syntheticNone();
+    }
+    return insertionPoint.getPosition();
+  }
+
+  private void shortenLiveRangesInsideBlock(
+      IRCode code,
+      BasicBlock block,
+      LazyBox<DominatorTree> dominatorTreeMemoization,
+      Map<BasicBlock, LinkedHashMap<Value, Instruction>> addConstantInBlock,
+      Predicate<Instruction> selector) {
+    InstructionListIterator iterator = block.listIterator(code);
+    boolean seenCompareExit = false;
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (options.canHaveCmpIfFloatBug() && instruction.isCmp()) {
+        seenCompareExit = true;
+      }
+
+      if (instruction.hasUnusedOutValue() || instruction.outValue().hasLocalInfo()) {
+        continue;
+      }
+
+      if (!selector.test(instruction)) {
+        continue;
+      }
+
+      // Here we try to stop wasting time in the common case where constants are used immediately
+      // after their definition.
+      //
+      // This is especially important for the creation of large arrays, which has the following code
+      // pattern repeated many times, where the two loaded constants are only used by the ArrayPut
+      // instruction.
+      //
+      //   Const number (the array index)
+      //   Const (the array entry value)
+      //   ArrayPut
+      //
+      // The heuristic is therefore to check for constants used only once if the use is within the
+      // next two instructions, and only swap them if that is the case (cannot shorten the live
+      // range anyway).
+      if (instruction.outValue().hasSingleUniqueUser() && !instruction.outValue().hasPhiUsers()) {
+        Instruction uniqueUse = instruction.outValue().singleUniqueUser();
+        Instruction next = iterator.next();
+        if (uniqueUse == next) {
+          iterator.previous();
+          continue;
+        }
+        if (next.hasOutValue()
+            && next.outValue().hasSingleUniqueUser()
+            && !next.outValue().hasPhiUsers()
+            && iterator.hasNext()) {
+          Instruction nextNext = iterator.peekNext();
+          Instruction uniqueUseNext = next.outValue().singleUniqueUser();
+          if (uniqueUse == nextNext && uniqueUseNext == nextNext) {
+            iterator.previous();
+            continue;
+          }
+        }
+        iterator.previous();
+        // The call to removeOrReplaceByDebugLocalRead() at the end of this method will remove the
+        // last returned element of this iterator. Therefore, we re-read this element from the
+        // iterator.
+        iterator.previous();
+        iterator.next();
+      }
+      // Collect the blocks for all users of the constant.
+      Set<BasicBlock> userBlocks = new LinkedHashSet<>();
+      for (Instruction user : instruction.outValue().uniqueUsers()) {
+        userBlocks.add(user.getBlock());
+      }
+      for (Phi phi : instruction.outValue().uniquePhiUsers()) {
+        int predecessorIndex = 0;
+        for (Value operand : phi.getOperands()) {
+          if (operand == instruction.outValue()) {
+            userBlocks.add(phi.getBlock().getPredecessors().get(predecessorIndex));
+          }
+          predecessorIndex++;
+        }
+      }
+      // Locate the closest dominator block for all user blocks.
+      DominatorTree dominatorTree = dominatorTreeMemoization.computeIfAbsent();
+      BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
+
+      if (instruction.instructionTypeCanThrow()) {
+        if (block.hasCatchHandlers() || dominator.hasCatchHandlers()) {
+          // Do not move the constant if the constant instruction can throw
+          // and the dominator or the original block has catch handlers.
+          continue;
+        }
+      }
+
+      // If the dominator block has a potential compare exit we will chose that as the insertion
+      // point. Uniquely for instructions having invalues this can be before the definition of them.
+      // Bail-out when this is the case. See b/251015885 for more information.
+      if (seenCompareExit
+          && Iterables.any(instruction.inValues(), x -> x.getBlock() == dominator)) {
+        continue;
+      }
+
+      Instruction copy;
+      switch (instruction.opcode()) {
+        case CONST_CLASS:
+          copy = ConstClass.copyOf(code, instruction.asConstClass());
+          break;
+        case CONST_NUMBER:
+          copy = ConstNumber.copyOf(code, instruction.asConstNumber());
+          break;
+        case CONST_STRING:
+          copy = ConstString.copyOf(code, instruction.asConstString());
+          break;
+        case DEX_ITEM_BASED_CONST_STRING:
+          copy = DexItemBasedConstString.copyOf(code, instruction.asDexItemBasedConstString());
+          break;
+        case INSTANCE_GET:
+          copy = InstanceGet.copyOf(code, instruction.asInstanceGet());
+          break;
+        case STATIC_GET:
+          copy = StaticGet.copyOf(code, instruction.asStaticGet());
+          break;
+        default:
+          throw new Unreachable();
+      }
+      instruction.outValue().replaceUsers(copy.outValue());
+      addConstantInBlock
+          .computeIfAbsent(dominator, k -> new LinkedHashMap<>())
+          .put(copy.outValue(), copy);
+      assert iterator.peekPrevious() == instruction;
+      iterator.removeOrReplaceByDebugLocalRead();
+    }
+  }
+}
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 3a60f68..af0023f 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
@@ -6,12 +6,6 @@
 
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
-import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING;
-import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
-import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
@@ -31,23 +25,17 @@
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-import com.android.tools.r8.ir.code.Binop;
-import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DebugLocalsChange;
-import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.IfType;
-import com.android.tools.r8.ir.code.InstanceGet;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InstructionOrPhi;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeInterface;
 import com.android.tools.r8.ir.code.InvokeMethod;
@@ -63,7 +51,6 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LazyBox;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
@@ -75,25 +62,16 @@
 import it.unimi.dsi.fastutil.ints.IntSet;
 import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
 import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Predicate;
 
 public class CodeRewriter {
 
-  // This constant was determined by experimentation.
-  private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
-
   private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
   private final InternalOptions options;
@@ -366,422 +344,9 @@
     assert code.isConsistentSSA(appView);
   }
 
-  /**
-   * If an instruction is known to be a binop/lit8 or binop//lit16 instruction, update the
-   * instruction to use its own constant that will be defined just before the instruction. This
-   * transformation allows to decrease pressure on register allocation by defining the shortest
-   * range of constant used by this kind of instruction. D8 knowns at build time that constant will
-   * be encoded directly into the final Dex instruction.
-   */
-  public void useDedicatedConstantForLitInstruction(IRCode code) {
-    if (!code.metadata().mayHaveArithmeticOrLogicalBinop()) {
-      return;
-    }
 
-    for (BasicBlock block : code.blocks) {
-      InstructionListIterator instructionIterator = block.listIterator(code);
-      // Collect all the non constant in values for binop/lit8 or binop/lit16 instructions.
-      Set<Value> binopsWithLit8OrLit16NonConstantValues = Sets.newIdentityHashSet();
-      while (instructionIterator.hasNext()) {
-        Instruction currentInstruction = instructionIterator.next();
-        if (!isBinopWithLit8OrLit16(currentInstruction)) {
-          continue;
-        }
-        Value value = binopWithLit8OrLit16NonConstant(currentInstruction.asBinop());
-        assert value != null;
-        binopsWithLit8OrLit16NonConstantValues.add(value);
-      }
-      if (binopsWithLit8OrLit16NonConstantValues.isEmpty()) {
-        continue;
-      }
-      // Find last use in block of all the non constant in values for binop/lit8 or binop/lit16
-      // instructions.
-      Reference2IntMap<Value> lastUseOfBinopsWithLit8OrLit16NonConstantValues =
-          new Reference2IntOpenHashMap<>();
-      lastUseOfBinopsWithLit8OrLit16NonConstantValues.defaultReturnValue(-1);
-      int currentInstructionNumber = block.getInstructions().size();
-      while (instructionIterator.hasPrevious()) {
-        Instruction currentInstruction = instructionIterator.previous();
-        currentInstructionNumber--;
-        for (Value value :
-            Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
-          if (!binopsWithLit8OrLit16NonConstantValues.contains(value)) {
-            continue;
-          }
-          if (!lastUseOfBinopsWithLit8OrLit16NonConstantValues.containsKey(value)) {
-            lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(value, currentInstructionNumber);
-          }
-        }
-      }
-      // Do the transformation except if the binop can use the binop/2addr format.
-      currentInstructionNumber--;
-      assert currentInstructionNumber == -1;
-      while (instructionIterator.hasNext()) {
-        Instruction currentInstruction = instructionIterator.next();
-        currentInstructionNumber++;
-        if (!isBinopWithLit8OrLit16(currentInstruction)) {
-          continue;
-        }
-        Binop binop = currentInstruction.asBinop();
-        if (!canBe2AddrInstruction(
-            binop, currentInstructionNumber, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
-          Value constValue = binopWithLit8OrLit16Constant(currentInstruction);
-          if (constValue.numberOfAllUsers() > 1) {
-            // No need to do the transformation if the const value is already used only one time.
-            ConstNumber newConstant = ConstNumber
-                .copyOf(code, constValue.definition.asConstNumber());
-            newConstant.setPosition(currentInstruction.getPosition());
-            newConstant.setBlock(currentInstruction.getBlock());
-            currentInstruction.replaceValue(constValue, newConstant.outValue());
-            constValue.removeUser(currentInstruction);
-            instructionIterator.previous();
-            instructionIterator.add(newConstant);
-            instructionIterator.next();
-          }
-        }
-      }
-    }
 
-    assert code.isConsistentSSA(appView);
-  }
 
-  // Check if a binop can be represented in the binop/lit8 or binop/lit16 form.
-  private static boolean isBinopWithLit8OrLit16(Instruction instruction) {
-    if (!instruction.isArithmeticBinop() && !instruction.isLogicalBinop()) {
-      return false;
-    }
-    Binop binop = instruction.asBinop();
-    // If one of the values does not need a register it is implicitly a binop/lit8 or binop/lit16.
-    boolean result =
-        !binop.needsValueInRegister(binop.leftValue())
-            || !binop.needsValueInRegister(binop.rightValue());
-    assert !result || binop.leftValue().isConstNumber() || binop.rightValue().isConstNumber();
-    return result;
-  }
-
-  // Return the constant in-value of a binop/lit8 or binop/lit16 instruction.
-  private static Value binopWithLit8OrLit16Constant(Instruction instruction) {
-    assert isBinopWithLit8OrLit16(instruction);
-    Binop binop = instruction.asBinop();
-    if (binop.leftValue().isConstNumber()) {
-      return binop.leftValue();
-    } else if (binop.rightValue().isConstNumber()) {
-      return binop.rightValue();
-    } else {
-      throw new Unreachable();
-    }
-  }
-
-  // Return the non-constant in-value of a binop/lit8 or binop/lit16 instruction.
-  private static Value binopWithLit8OrLit16NonConstant(Binop binop) {
-    if (binop.leftValue().isConstNumber()) {
-      return binop.rightValue();
-    } else if (binop.rightValue().isConstNumber()) {
-      return binop.leftValue();
-    } else {
-      throw new Unreachable();
-    }
-  }
-
-  /**
-   * Estimate if a binary operation can be a binop/2addr form or not. It can be a 2addr form when an
-   * argument is no longer needed after the binary operation and can be overwritten. That is
-   * definitely the case if there is no path between the binary operation and all other usages.
-   */
-  private static boolean canBe2AddrInstruction(
-      Binop binop, int binopInstructionNumber, Reference2IntMap<Value> lastUseOfRelevantValue) {
-    Value value = binopWithLit8OrLit16NonConstant(binop);
-    assert value != null;
-    int lastUseInstructionNumber = lastUseOfRelevantValue.getInt(value);
-    // The binop instruction is a user, so there is always a last use in the block.
-    assert lastUseInstructionNumber != -1;
-    if (lastUseInstructionNumber > binopInstructionNumber) {
-      return false;
-    }
-
-    Set<BasicBlock> noPathTo = Sets.newIdentityHashSet();
-    BasicBlock binopBlock = binop.getBlock();
-    Iterable<InstructionOrPhi> users =
-        value.debugUsers() != null
-            ? Iterables.concat(value.uniqueUsers(), value.debugUsers(), value.uniquePhiUsers())
-            : Iterables.concat(value.uniqueUsers(), value.uniquePhiUsers());
-    for (InstructionOrPhi user : users) {
-      BasicBlock userBlock = user.getBlock();
-      if (userBlock == binopBlock) {
-        // All users in the current block are either before the binop instruction or the
-        // binop instruction itself.
-        continue;
-      }
-      if (noPathTo.contains(userBlock)) {
-        continue;
-      }
-      if (binopBlock.hasPathTo(userBlock)) {
-        return false;
-      }
-      noPathTo.add(userBlock);
-    }
-
-    return true;
-  }
-
-  public void shortenLiveRanges(IRCode code, ConstantCanonicalizer canonicalizer) {
-    if (options.testing.disableShortenLiveRanges) {
-      return;
-    }
-    LazyBox<DominatorTree> dominatorTreeMemoization = new LazyBox<>(() -> new DominatorTree(code));
-    Map<BasicBlock, LinkedHashMap<Value, Instruction>> addConstantInBlock = new IdentityHashMap<>();
-    LinkedList<BasicBlock> blocks = code.blocks;
-    for (BasicBlock block : blocks) {
-      shortenLiveRangesInsideBlock(
-          code,
-          block,
-          dominatorTreeMemoization,
-          addConstantInBlock,
-          canonicalizer::isConstantCanonicalizationCandidate);
-    }
-
-    // Heuristic to decide if constant instructions are shared in dominator block
-    // of usages or moved to the usages.
-
-    // Process all blocks in stable order to avoid non-determinism of hash map iterator.
-    BasicBlockIterator blockIterator = code.listIterator();
-    while (blockIterator.hasNext()) {
-      BasicBlock block = blockIterator.next();
-      Map<Value, Instruction> movedInstructions = addConstantInBlock.get(block);
-      if (movedInstructions == null) {
-        continue;
-      }
-      assert !movedInstructions.isEmpty();
-      if (!block.isEntry() && movedInstructions.size() > STOP_SHARED_CONSTANT_THRESHOLD) {
-        // If there are too many constant numbers in the same block they are copied rather than
-        // shared unless used by a phi.
-        movedInstructions
-            .values()
-            .removeIf(
-                movedInstruction -> {
-                  if (movedInstruction.outValue().hasPhiUsers()
-                      || !movedInstruction.isConstNumber()) {
-                    return false;
-                  }
-                  ConstNumber constNumber = movedInstruction.asConstNumber();
-                  Value constantValue = movedInstruction.outValue();
-                  for (Instruction user : constantValue.uniqueUsers()) {
-                    ConstNumber newCstNum = ConstNumber.copyOf(code, constNumber);
-                    newCstNum.setPosition(user.getPosition());
-                    InstructionListIterator iterator = user.getBlock().listIterator(code, user);
-                    iterator.previous();
-                    iterator.add(newCstNum);
-                    user.replaceValue(constantValue, newCstNum.outValue());
-                  }
-                  constantValue.clearUsers();
-                  return true;
-                });
-      }
-
-      // Add constant into the dominator block of usages.
-      boolean hasCatchHandlers = block.hasCatchHandlers();
-      InstructionListIterator instructionIterator = block.listIterator(code);
-      while (instructionIterator.hasNext()) {
-        Instruction insertionPoint = instructionIterator.next();
-        if (insertionPoint.isJumpInstruction()
-            || (hasCatchHandlers && insertionPoint.instructionTypeCanThrow())
-            || (options.canHaveCmpIfFloatBug() && insertionPoint.isCmp())) {
-          break;
-        }
-        for (Value use :
-            Iterables.concat(insertionPoint.inValues(), insertionPoint.getDebugValues())) {
-          Instruction movedInstruction = movedInstructions.remove(use);
-          if (movedInstruction != null) {
-            instructionIterator =
-                insertInstructionWithShortenedLiveRange(
-                    code, blockIterator, instructionIterator, movedInstruction, insertionPoint);
-          }
-        }
-      }
-
-      // Insert remaining constant instructions prior to the "exit".
-      Instruction insertionPoint = instructionIterator.peekPrevious();
-      for (Instruction movedInstruction : movedInstructions.values()) {
-        instructionIterator =
-            insertInstructionWithShortenedLiveRange(
-                code, blockIterator, instructionIterator, movedInstruction, insertionPoint);
-      }
-    }
-
-    assert code.isConsistentSSA(appView);
-  }
-
-  private InstructionListIterator insertInstructionWithShortenedLiveRange(
-      IRCode code,
-      BasicBlockIterator blockIterator,
-      InstructionListIterator instructionIterator,
-      Instruction movedInstruction,
-      Instruction insertionPoint) {
-    Instruction previous = instructionIterator.previous();
-    assert previous == insertionPoint;
-    movedInstruction.setPosition(
-        getPositionForMovedNonThrowingInstruction(movedInstruction, insertionPoint));
-    if (movedInstruction.instructionTypeCanThrow()
-        && insertionPoint.getBlock().hasCatchHandlers()) {
-      // Split the block and reset the block iterator.
-      BasicBlock splitBlock =
-          instructionIterator.splitCopyCatchHandlers(code, blockIterator, appView.options());
-      BasicBlock previousBlock = blockIterator.previousUntil(b -> b == splitBlock);
-      assert previousBlock == splitBlock;
-      blockIterator.next();
-
-      // Add the constant instruction before the exit instruction.
-      assert !instructionIterator.hasNext();
-      instructionIterator.previous();
-      instructionIterator.add(movedInstruction);
-
-      // Continue insertion at the entry of the split block.
-      instructionIterator = splitBlock.listIterator(code);
-    } else {
-      instructionIterator.add(movedInstruction);
-    }
-    Instruction next = instructionIterator.next();
-    assert next == insertionPoint;
-    return instructionIterator;
-  }
-
-  private Position getPositionForMovedNonThrowingInstruction(
-      Instruction movedInstruction, Instruction insertionPoint) {
-    // If the type of the moved instruction is throwing and we don't have a position at the
-    // insertion point, we use the special synthetic-none position, which is OK as the moved
-    // instruction instance is known not to throw (or we would not be allowed the move it).
-    if (movedInstruction.instructionTypeCanThrow() && !insertionPoint.getPosition().isSome()) {
-      return Position.syntheticNone();
-    }
-    return insertionPoint.getPosition();
-  }
-
-  private void shortenLiveRangesInsideBlock(
-      IRCode code,
-      BasicBlock block,
-      LazyBox<DominatorTree> dominatorTreeMemoization,
-      Map<BasicBlock, LinkedHashMap<Value, Instruction>> addConstantInBlock,
-      Predicate<Instruction> selector) {
-    InstructionListIterator iterator = block.listIterator(code);
-    boolean seenCompareExit = false;
-    while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (options.canHaveCmpIfFloatBug() && instruction.isCmp()) {
-        seenCompareExit = true;
-      }
-
-      if (instruction.hasUnusedOutValue() || instruction.outValue().hasLocalInfo()) {
-        continue;
-      }
-
-      if (!selector.test(instruction)) {
-        continue;
-      }
-
-      // Here we try to stop wasting time in the common case where constants are used immediately
-      // after their definition.
-      //
-      // This is especially important for the creation of large arrays, which has the following code
-      // pattern repeated many times, where the two loaded constants are only used by the ArrayPut
-      // instruction.
-      //
-      //   Const number (the array index)
-      //   Const (the array entry value)
-      //   ArrayPut
-      //
-      // The heuristic is therefore to check for constants used only once if the use is within the
-      // next two instructions, and only swap them if that is the case (cannot shorten the live
-      // range anyway).
-      if (instruction.outValue().hasSingleUniqueUser() && !instruction.outValue().hasPhiUsers()) {
-        Instruction uniqueUse = instruction.outValue().singleUniqueUser();
-        Instruction next = iterator.next();
-        if (uniqueUse == next) {
-          iterator.previous();
-          continue;
-        }
-        if (next.hasOutValue()
-            && next.outValue().hasSingleUniqueUser()
-            && !next.outValue().hasPhiUsers()
-            && iterator.hasNext()) {
-          Instruction nextNext = iterator.peekNext();
-          Instruction uniqueUseNext = next.outValue().singleUniqueUser();
-          if (uniqueUse == nextNext && uniqueUseNext == nextNext) {
-            iterator.previous();
-            continue;
-          }
-        }
-        iterator.previous();
-        // The call to removeOrReplaceByDebugLocalRead() at the end of this method will remove the
-        // last returned element of this iterator. Therefore, we re-read this element from the
-        // iterator.
-        iterator.previous();
-        iterator.next();
-      }
-      // Collect the blocks for all users of the constant.
-      Set<BasicBlock> userBlocks = new LinkedHashSet<>();
-      for (Instruction user : instruction.outValue().uniqueUsers()) {
-        userBlocks.add(user.getBlock());
-      }
-      for (Phi phi : instruction.outValue().uniquePhiUsers()) {
-        int predecessorIndex = 0;
-        for (Value operand : phi.getOperands()) {
-          if (operand == instruction.outValue()) {
-            userBlocks.add(phi.getBlock().getPredecessors().get(predecessorIndex));
-          }
-          predecessorIndex++;
-        }
-      }
-      // Locate the closest dominator block for all user blocks.
-      DominatorTree dominatorTree = dominatorTreeMemoization.computeIfAbsent();
-      BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
-
-      if (instruction.instructionTypeCanThrow()) {
-        if (block.hasCatchHandlers() || dominator.hasCatchHandlers()) {
-          // Do not move the constant if the constant instruction can throw
-          // and the dominator or the original block has catch handlers.
-          continue;
-        }
-      }
-
-      // If the dominator block has a potential compare exit we will chose that as the insertion
-      // point. Uniquely for instructions having invalues this can be before the definition of them.
-      // Bail-out when this is the case. See b/251015885 for more information.
-      if (seenCompareExit
-          && Iterables.any(instruction.inValues(), x -> x.getBlock() == dominator)) {
-        continue;
-      }
-
-      Instruction copy;
-      switch (instruction.opcode()) {
-        case CONST_CLASS:
-          copy = ConstClass.copyOf(code, instruction.asConstClass());
-          break;
-        case CONST_NUMBER:
-          copy = ConstNumber.copyOf(code, instruction.asConstNumber());
-          break;
-        case CONST_STRING:
-          copy = ConstString.copyOf(code, instruction.asConstString());
-          break;
-        case DEX_ITEM_BASED_CONST_STRING:
-          copy = DexItemBasedConstString.copyOf(code, instruction.asDexItemBasedConstString());
-          break;
-        case INSTANCE_GET:
-          copy = InstanceGet.copyOf(code, instruction.asInstanceGet());
-          break;
-        case STATIC_GET:
-          copy = StaticGet.copyOf(code, instruction.asStaticGet());
-          break;
-        default:
-          throw new Unreachable();
-      }
-      instruction.outValue().replaceUsers(copy.outValue());
-      addConstantInBlock
-          .computeIfAbsent(dominator, k -> new LinkedHashMap<>())
-          .put(copy.outValue(), copy);
-      assert iterator.peekPrevious() == instruction;
-      iterator.removeOrReplaceByDebugLocalRead();
-    }
-  }
 
   // TODO(mikaelpeltier) Manage that from and to instruction do not belong to the same block.
   private static boolean hasLocalOrLineChangeBetween(