Version 1.0.26

Merge: Ensure that there are materializing instructions before long add/sub operations.
CL: https://r8-review.googlesource.com/c/r8/+/20741
Change-Id: I8834f3d1727607e9872bd3dd2f233a69869a0668
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 91cc6d2..71bba11 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.0.25";
+  public static final String LABEL = "v1.0.26";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
new file mode 100644
index 0000000..bff16af
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
@@ -0,0 +1,63 @@
+// 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.ir.code;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.code.Const16;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class AlwaysMaterializingDefinition extends ConstInstruction {
+
+  public AlwaysMaterializingDefinition(Value out) {
+    super(out);
+  }
+
+  @Override
+  public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+    // This instruction may never be considered dead as it must remain.
+    return false;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int register = builder.allocatedRegister(outValue, getNumber());
+    builder.add(
+        this, (register & 0xf) == register ? new Const4(register, 0) : new Const16(register, 0));
+  }
+
+  @Override
+  public void buildCf(CfBuilder builder) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean identicalNonValueNonPositionParts(Instruction other) {
+    return false;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return 0;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+    throw new Unreachable();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
new file mode 100644
index 0000000..4530040
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.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.ir.code;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class AlwaysMaterializingUser extends Instruction {
+
+  public AlwaysMaterializingUser(Value src) {
+    super(null, src);
+    // The user instruction never materializes so ensure it has position none.
+    setPosition(Position.none());
+  }
+
+  @Override
+  public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+    // This instruction may never be considered dead as it must remain.
+    return false;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addNop(this);
+  }
+
+  @Override
+  public void buildCf(CfBuilder builder) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean identicalNonValueNonPositionParts(Instruction other) {
+    return false;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return 0;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert inValues.get(0).definition instanceof AlwaysMaterializingDefinition;
+    return inValues.get(0).definition.maxOutValueRegister();
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public Constraint inliningConstraint(AppInfoWithLiveness info, DexType invocationContext) {
+    return Constraint.ALWAYS;
+  }
+
+  @Override
+  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+    throw new Unreachable();
+  }
+}
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 f75000b..ed3ad4d 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
@@ -22,7 +22,14 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
+import com.android.tools.r8.ir.code.AlwaysMaterializingDefinition;
+import com.android.tools.r8.ir.code.AlwaysMaterializingUser;
+import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.desugar.StringConcatRewriter;
@@ -674,6 +681,7 @@
     // Always perform dead code elimination before register allocation. The register allocator
     // does not allow dead code (to make sure that we do not waste registers for unneeded values).
     DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+    materializeInstructionBeforeLongOperationsWorkaround(code, options);
     LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code, options);
     registerAllocator.allocateRegisters(options.debug);
     printMethod(code, "After register allocation (non-SSA)");
@@ -689,6 +697,65 @@
     return registerAllocator;
   }
 
+  private static void materializeInstructionBeforeLongOperationsWorkaround(
+      IRCode code, InternalOptions options) {
+    if (!options.canHaveDex2OatLinkedListBug()) {
+      return;
+    }
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator it = block.listIterator();
+      Instruction firstMaterializing =
+          it.nextUntil(IRConverter::isMaterializingInstructionOnArtArmVersionM);
+      if (needsInstructionBeforeLongOperation(firstMaterializing)) {
+        ensureInstructionBeforeLongOperation(code, block, firstMaterializing, it);
+      }
+    }
+  }
+
+  private static void ensureInstructionBeforeLongOperation(
+      IRCode code, BasicBlock block, Instruction firstMaterializing, InstructionListIterator it) {
+    // Force materialize a constant-zero before the long operation.
+    Instruction check = it.previous();
+    assert firstMaterializing == check;
+    Value fixitValue = code.createValue(ValueType.INT);
+    // Forced definition of const-zero
+    Instruction fixitDefinition = new AlwaysMaterializingDefinition(fixitValue);
+    fixitDefinition.setBlock(block);
+    fixitDefinition.setPosition(firstMaterializing.getPosition());
+    it.add(fixitDefinition);
+    // Forced user of the forced definition to ensure it has a user and thus live range.
+    Instruction fixitUser = new AlwaysMaterializingUser(fixitValue);
+    fixitUser.setBlock(block);
+    it.add(fixitUser);
+  }
+
+  private static boolean needsInstructionBeforeLongOperation(Instruction instruction) {
+    // The cortex fixup will only trigger on long sub and long add instructions.
+    if (!((instruction.isAdd() || instruction.isSub()) && instruction.outType().isWide())) {
+      return false;
+    }
+    // If the block with the instruction is a fallthrough block, then it can't end up being
+    // preceded by the incorrectly linked prologue/epilogue..
+    BasicBlock block = instruction.getBlock();
+    for (BasicBlock pred : block.getPredecessors()) {
+      if (pred.exit().fallthroughBlock() == block) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private static boolean isMaterializingInstructionOnArtArmVersionM(Instruction instruction) {
+    return !instruction.isDebugInstruction()
+        && !instruction.isMove()
+        && !isPossiblyNonMaterializingLongOperationOnArtArmVersionM(instruction);
+  }
+
+  private static boolean isPossiblyNonMaterializingLongOperationOnArtArmVersionM(
+      Instruction instruction) {
+    return (instruction.isMul() || instruction.isDiv()) && instruction.outType().isWide();
+  }
+
   private void printC1VisualizerHeader(DexEncodedMethod method) {
     if (printer != null) {
       printer.begin("compilation");
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index cf0c1e2..daf657d 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -515,4 +515,13 @@
   public boolean canHaveMul2AddrBug() {
     return minApiLevel < AndroidApiLevel.M.getLevel();
   }
+
+  // Some Marshmallow VMs create an incorrect doubly-linked list of instructions. When the VM
+  // attempts to create a fixup for a Cortex 53 long add/sub issue, it may diverge due to the cyclic
+  // list.
+  //
+  // See b/77842465.
+  public boolean canHaveDex2OatLinkedListBug() {
+    return minApiLevel < AndroidApiLevel.N.getLevel();
+  }
 }