Merge "Don't probe local state in addDebugLocalStart."
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 5bed061..3cf9b8c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -581,7 +581,7 @@
assert predecessors.size() == 1;
assert predecessors.get(0).successors.size() == 1;
BasicBlock unlinkedBlock = predecessors.get(0);
- predecessors.get(0).successors.clear();
+ unlinkedBlock.successors.clear();
predecessors.clear();
return unlinkedBlock;
}
@@ -604,7 +604,7 @@
assert successors.size() == 1;
assert successors.get(0).predecessors.size() == 1;
BasicBlock unlinkedBlock = successors.get(0);
- successors.get(0).predecessors.clear();
+ unlinkedBlock.predecessors.clear();
successors.clear();
return unlinkedBlock;
}
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 1adcd11..81e79e6 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
@@ -291,21 +291,19 @@
private void appendCatchHandlers(IRCode code, BasicBlock invokeBlock,
IRCode inlinee, ListIterator<BasicBlock> blocksIterator) {
- BasicBlock inlineeBlock = null;
- // Move back through the inlinee blocks added (they are now in the basic blocks list).
+ // Position right after the empty invoke block, by moving back through the newly added inlinee
+ // blocks (they are now in the basic blocks list).
for (int i = 0; i < inlinee.blocks.size(); i++) {
- inlineeBlock = blocksIterator.previous();
+ blocksIterator.previous();
}
- assert inlineeBlock == inlinee.blocks.getFirst();
- // Position right after the empty invoke block.
- inlineeBlock = blocksIterator.next();
- assert inlineeBlock == inlinee.blocks.getFirst();
+ assert IteratorUtils.peekNext(blocksIterator) == inlinee.blocks.getFirst();
- // Iterate through the inlined blocks (they are now in the basic blocks list).
+ // Iterate through the inlined blocks.
Iterator<BasicBlock> inlinedBlocksIterator = inlinee.blocks.iterator();
while (inlinedBlocksIterator.hasNext()) {
BasicBlock inlinedBlock = inlinedBlocksIterator.next();
- assert inlineeBlock == inlinedBlock; // Iterators must be in sync.
+ BasicBlock expected = blocksIterator.next();
+ assert inlinedBlock == expected; // Iterators must be in sync.
if (inlinedBlock.hasCatchHandlers()) {
// The block already has catch handlers, so it has only one throwing instruction, and no
// splitting is required.
@@ -316,23 +314,17 @@
// handlers must be added to each of these blocks.
splitBlockAndCopyCatchHandlers(code, invokeBlock, inlinedBlock, blocksIterator);
}
- // Iterate to the next inlined block (if more inlined blocks).
- inlineeBlock = blocksIterator.next();
}
}
- private void removeArgumentInstructions(IRCode inlinee) {
- int index = 0;
- InstructionListIterator inlineeIterator = inlinee.blocks.getFirst().listIterator();
- List<Value> arguments = inlinee.collectArguments();
- while (inlineeIterator.hasNext()) {
- Instruction instruction = inlineeIterator.next();
- if (instruction.isArgument()) {
- assert !instruction.outValue().isUsed();
- assert instruction.outValue() == arguments.get(index++);
- inlineeIterator.remove();
- }
- }
+ private static void removeArgumentInstruction(
+ InstructionListIterator iterator, Value expectedArgument) {
+ assert iterator.hasNext();
+ Instruction instruction = iterator.next();
+ assert instruction.isArgument();
+ assert !instruction.outValue().isUsed();
+ assert instruction.outValue() == expectedArgument;
+ iterator.remove();
}
@Override
@@ -341,47 +333,77 @@
List<BasicBlock> blocksToRemove, DexType downcast) {
assert blocksToRemove != null;
boolean inlineeCanThrow = canThrow(inlinee);
+
+ // Split the block with the invocation into three blocks, where the first block contains all
+ // instructions before the invocation, the second block contains only the invocation, and the
+ // third block contains all instructions that follow the invocation.
BasicBlock invokeBlock = split(code, 1, blocksIterator);
assert invokeBlock.getInstructions().size() == 2;
assert invokeBlock.getInstructions().getFirst().isInvoke();
+ Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
+ BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
+ BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
+
// Invalidate position-on-throwing-instructions property if it does not hold for the inlinee.
if (!inlinee.doAllThrowingInstructionsHavePositions()) {
code.setAllThrowingInstructionsHavePositions(false);
}
- // Split the invoke instruction into a separate block.
- Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
- BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
- BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
-
- CheckCast castInstruction = null;
- // Map all argument values, and remove the arguments instructions in the inlinee.
+ // Map all argument values. The first one needs special handling if there is a downcast type.
List<Value> arguments = inlinee.collectArguments();
assert invoke.inValues().size() == arguments.size();
- for (int i = 0; i < invoke.inValues().size(); i++) {
+
+ BasicBlock entryBlock = inlinee.blocks.getFirst();
+ InstructionListIterator entryBlockIterator;
+
+ int i = 0;
+ if (downcast != null) {
+ CheckCast castInstruction =
+ new CheckCast(code.createValue(ValueType.OBJECT), invoke.inValues().get(0), downcast);
+ castInstruction.setPosition(invoke.getPosition());
+
+ // Splice in the check cast operation.
+ if (entryBlock.canThrow() || invokeBlock.hasCatchHandlers()) {
+ // Since the cast-instruction may also fail we need to create a new block for the cast.
+ //
+ // Note that the downcast of the receiver is made at the call site, so we need to copy the
+ // catch handlers from the invoke block to the block with the cast. This is already being
+ // done when we copy the catch handlers of the invoke block (if any) to all the blocks in
+ // the inlinee (by the call to appendCatchHandlers() later in this method), so we don't
+ // need to do anything about that here.
+ BasicBlock inlineEntry = entryBlock;
+ entryBlock = entryBlock.listIterator().split(inlinee);
+ entryBlockIterator = entryBlock.listIterator();
+ // Insert cast instruction into the new block.
+ inlineEntry.getInstructions().addFirst(castInstruction);
+ castInstruction.setBlock(inlineEntry);
+ assert castInstruction.getBlock().getInstructions().size() == 2;
+ } else {
+ castInstruction.setBlock(entryBlock);
+ entryBlockIterator = entryBlock.listIterator();
+ entryBlockIterator.add(castInstruction);
+ }
+
+ // Map the argument value that has been cast.
+ Value argument = arguments.get(i);
+ argument.replaceUsers(castInstruction.outValue);
+ removeArgumentInstruction(entryBlockIterator, argument);
+ i++;
+ } else {
+ entryBlockIterator = entryBlock.listIterator();
+ }
+
+ // Map the remaining argument values.
+ for (; i < invoke.inValues().size(); i++) {
// TODO(zerny): Support inlining in --debug mode.
assert !arguments.get(i).hasLocalInfo();
- if ((i == 0) && (downcast != null)) {
- Value invokeValue = invoke.inValues().get(0);
- Value receiverValue = arguments.get(0);
- Value value = code.createValue(ValueType.OBJECT);
- castInstruction = new CheckCast(value, invokeValue, downcast);
- castInstruction.setPosition(invoke.getPosition());
- receiverValue.replaceUsers(value);
- } else {
- arguments.get(i).replaceUsers(invoke.inValues().get(i));
- }
+ Value argument = arguments.get(i);
+ argument.replaceUsers(invoke.inValues().get(i));
+ removeArgumentInstruction(entryBlockIterator, argument);
}
- removeArgumentInstructions(inlinee);
- if (castInstruction != null) {
- // Splice in the check cast operation.
- inlinee.blocks.getFirst().listIterator().split(inlinee);
- BasicBlock newBlock = inlinee.blocks.getFirst();
- assert newBlock.getInstructions().size() == 1;
- newBlock.getInstructions().addFirst(castInstruction);
- castInstruction.setBlock(newBlock);
- }
+
+ assert entryBlock.getInstructions().stream().noneMatch(Instruction::isArgument);
// The inline entry is the first block now the argument instructions are gone.
BasicBlock inlineEntry = inlinee.blocks.getFirst();
@@ -421,6 +443,7 @@
invokeBlockIterator.next();
invokeBlockIterator.remove();
invokeSuccessor = invokeBlock;
+ assert invokeBlock.getInstructions().getFirst().isGoto();
}
// Link the inlinee into the graph.
@@ -433,15 +456,14 @@
if (blocksIterator == null) {
// If no block iterator was passed create one for the insertion of the inlinee blocks.
blocksIterator = code.blocks.listIterator(code.blocks.indexOf(invokeBlock));
- blocksIterator.next();
} else {
- // If a blocks iterator was passed, back up to the block with the invoke instruction and
- // remove it.
+ // If a block iterator was passed, back up to the block with the invoke instruction.
blocksIterator.previous();
blocksIterator.previous();
}
+ assert IteratorUtils.peekNext(blocksIterator) == invokeBlock;
- // Insert inlinee blocks into the IR code.
+ // Insert inlinee blocks into the IR code of the callee, before the invoke block.
int blockNumber = code.getHighestBlockNumber() + 1;
for (BasicBlock bb : inlinee.blocks) {
bb.setNumber(blockNumber++);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 04cd083..106d537 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -605,7 +605,7 @@
if (!strategy.isValidTarget(invoke, target, inlinee)) {
continue;
}
- DexType downcast = createDowncastIfNeeded(strategy, invoke, target);
+ DexType downcast = getDowncastTypeIfNeeded(strategy, invoke, target);
// Inline the inlinee code in place of the invoke instruction
// Back up before the invoke instruction.
iterator.previous();
@@ -635,7 +635,7 @@
assert code.isConsistentSSA();
}
- private DexType createDowncastIfNeeded(
+ private static DexType getDowncastTypeIfNeeded(
InliningStrategy strategy, InvokeMethod invoke, DexEncodedMethod target) {
if (invoke.isInvokeMethodWithReceiver()) {
// If the invoke has a receiver but the actual type of the receiver is different
diff --git a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
index 4cfb71a..6ddc9db 100644
--- a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
@@ -5,18 +5,24 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderOrEqualThan;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
import java.util.stream.Stream;
import org.junit.Assume;
import org.junit.Test;
+import org.junit.runner.RunWith;
// See b/80385846
+@RunWith(VmTestRunner.class)
public class PostIncrementTestRunner extends DebugTestBase {
private static final Class CLASS = PostIncrementTest.class;
private static final String NAME = CLASS.getCanonicalName();
@Test
+ @IgnoreIfVmOlderOrEqualThan(Version.V5_1_1)
public void test() throws Exception {
Assume.assumeTrue("Older runtimes cause some kind of debug streaming issues",
ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1_HOST));