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();
+ }
}