Merge "Assert against removal of instructions with debug users/values."
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 3d68ace..7020f4b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -91,7 +91,7 @@
// If this is the end of the block clear out the pending state.
pendingLocals = null;
pendingLocalChanges = false;
- } else if (pc != emittedPc) {
+ } else if (pc != emittedPc && !instruction.isNop()) {
// For non-exit / pc-advancing instructions emit any pending changes once possible.
emitLocalChanges(pc);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 40a4776..8a2de9b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -108,6 +108,7 @@
throw new IllegalStateException();
}
assert current.outValue() == null || !current.outValue().isUsed();
+ assert current.getDebugValues().isEmpty();
for (int i = 0; i < current.inValues().size(); i++) {
Value value = current.inValues().get(i);
value.removeUser(current);
@@ -115,11 +116,28 @@
for (Value value : current.getDebugValues()) {
value.removeDebugUser(current);
}
+ if (current.getLocalInfo() != null) {
+ for (Instruction user : current.outValue().debugUsers()) {
+ user.removeDebugValue(current.outValue());
+ }
+ }
listIterator.remove();
current = null;
}
@Override
+ public void removeOrReplaceByNop() {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ if (current.getDebugValues().isEmpty()) {
+ remove();
+ } else {
+ replaceCurrentInstruction(new Nop());
+ }
+ }
+
+ @Override
public void detach() {
if (current == null) {
throw new IllegalStateException();
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
index d9ce088..74c2768 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -81,7 +81,8 @@
// Remove all instructions from the block before removing the block.
Iterator<Instruction> iterator = current.iterator();
while (iterator.hasNext()) {
- iterator.next();
+ Instruction instruction = iterator.next();
+ instruction.clearDebugValues();
iterator.remove();
}
listIterator.remove();
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 f14d66a..de198c9 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
@@ -131,9 +131,19 @@
}
public void removeDebugValue(Value value) {
- assert debugValues.contains(value);
- value.removeDebugUser(this);
- debugValues.remove(value);
+ assert value.getLocalInfo() != null;
+ if (debugValues != null) {
+ assert debugValues.contains(value);
+ debugValues.remove(value);
+ return;
+ }
+ assert false;
+ }
+
+ public void clearDebugValues() {
+ if (debugValues != null) {
+ debugValues.clear();
+ }
}
/**
@@ -824,6 +834,14 @@
return null;
}
+ public boolean isNop() {
+ return false;
+ }
+
+ public Nop asNop() {
+ return null;
+ }
+
public boolean canBeFolded() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 97f1885..ef49910 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -60,6 +60,12 @@
}
/**
+ * Safe removal function that will insert a Nop to take over the debug values if any are assocated
+ * with the current instruction.
+ */
+ void removeOrReplaceByNop();
+
+ /**
* Remove the current instruction (aka the {@link Instruction} returned by the previous call to
* {@link #next}) without updating its def/use chains.
* <p>
diff --git a/src/main/java/com/android/tools/r8/ir/code/Nop.java b/src/main/java/com/android/tools/r8/ir/code/Nop.java
new file mode 100644
index 0000000..dc378de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Nop.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2017, 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.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class Nop extends Instruction {
+
+ public Nop() {
+ super(null);
+ }
+
+ @Override
+ public boolean isNop() {
+ return true;
+ }
+
+ @Override
+ public Nop asNop() {
+ return this;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addNop(this);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+ return Constraint.ALWAYS;
+ }
+
+ @Override
+ public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+ return getDebugValues().isEmpty();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 2880c87..0af8f63 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -260,9 +260,7 @@
}
public boolean isUsed() {
- return !users.isEmpty()
- || !phiUsers.isEmpty()
- || ((debugData != null) && !debugData.users.isEmpty());
+ return !users.isEmpty() || !phiUsers.isEmpty() || numberOfAllDebugUsers() > 0;
}
public boolean usedInMonitorOperation() {
@@ -291,6 +289,7 @@
uniquePhiUsers = null;
if (debugData != null) {
debugData.users.clear();
+ debugData.phiUsers.clear();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index da067d8..b35c1a0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -317,7 +317,8 @@
}
private boolean isNopInstruction(com.android.tools.r8.ir.code.Instruction instruction) {
- return instruction.isDebugLocalsChange()
+ return instruction.isNop()
+ || instruction.isDebugLocalsChange()
|| (instruction.isConstNumber() && !instruction.outValue().needsRegister());
}
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 b698fc5..52c9eb4 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
@@ -50,9 +50,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeNewArray;
import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.JumpInstruction;
import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.Move;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -72,7 +70,6 @@
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
@@ -423,7 +420,6 @@
private int right;
private BasicBlock target;
private BasicBlock fallthrough;
- private int blockNumber;
public IfBuilder(IRCode code) {
this.code = code;
@@ -483,7 +479,6 @@
// Extract the information from the switch before removing it.
Int2ReferenceSortedMap<BasicBlock> keyToTarget = theSwitch.getKeyToTargetMap();
- Set<Value> originalDebugValues = ImmutableSet.copyOf(theSwitch.getDebugValues());
// Keep track of the current fallthrough, starting with the original.
BasicBlock fallthroughBlock = theSwitch.fallthroughBlock();
@@ -493,6 +488,8 @@
BasicBlock originalSwitchBlock = iterator.split(code, blocksIterator);
assert !originalSwitchBlock.hasCatchHandlers();
assert originalSwitchBlock.getInstructions().size() == 1;
+ assert originalBlock.exit().isGoto();
+ theSwitch.moveDebugValues(originalBlock.exit());
blocksIterator.remove();
theSwitch.getBlock().detachAllSuccessors();
BasicBlock block = theSwitch.getBlock().unlinkSinglePredecessor();
@@ -537,9 +534,6 @@
fallthroughBlock = ifBlock;
}
- // Attach the debug values from the original switch to the first of the new instructions,
- originalDebugValues.forEach(fallthroughBlock.exit()::addDebugValue);
-
// Finally link the block before the original switch to the new block sequence.
originalBlock.link(fallthroughBlock);
@@ -1770,16 +1764,15 @@
Instruction current = iterator.next();
if (current.isInvokeMethod()) {
DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
-
if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
// Remove Throwable::addSuppressed(Throwable) call.
- iterator.remove();
+ iterator.removeOrReplaceByNop();
} else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
Value destValue = current.outValue();
if (destValue == null) {
// If the result of the call was not used we don't create
// an empty array and just remove the call.
- iterator.remove();
+ iterator.removeOrReplaceByNop();
continue;
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 0e24dbd..8ab2de5 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -74,6 +74,11 @@
}
@Override
+ public void removeOrReplaceByNop() {
+ remove();
+ }
+
+ @Override
public void detach() {
remove();
}