Support catch handlers in conversion to filled-array-data.
Bug: b/246971330
Change-Id: I064cb8aa0020f47b261ff245e47323db4c4bff8b
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 a214f7c..0a79887 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
@@ -1921,8 +1921,11 @@
newBlock.getMutableSuccessors().add(this);
newBlock.getMutablePredecessors().add(predecessor);
predecessor.replaceSuccessor(this, newBlock);
- blockIterator.add(newBlock);
- assert newBlock.getNumber() >= 0 : "Number must be assigned by `onNewBlock`";
+ if (blockIterator == null) {
+ code.blocks.add(newBlock);
+ } else {
+ blockIterator.add(newBlock);
+ }
}
// Replace the blocks predecessors with the new ones.
predecessors.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 1d40f99..afe8e0b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -129,7 +129,7 @@
}
@Override
- public void addThrowingInstructionToPossiblyThrowingBlock(
+ public BasicBlock addThrowingInstructionToPossiblyThrowingBlock(
IRCode code,
ListIterator<BasicBlock> blockIterator,
Instruction instruction,
@@ -140,11 +140,15 @@
assert !block.hasCatchHandlers();
assert splitBlock.hasCatchHandlers();
block.copyCatchHandlers(code, blockIterator, splitBlock, options);
- while (IteratorUtils.peekPrevious(blockIterator) != splitBlock) {
- blockIterator.previous();
+ if (blockIterator != null) {
+ while (IteratorUtils.peekPrevious(blockIterator) != splitBlock) {
+ blockIterator.previous();
+ }
}
+ return splitBlock;
} else {
add(instruction);
+ return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index ae400dc..8b519ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -207,12 +207,12 @@
}
@Override
- public void addThrowingInstructionToPossiblyThrowingBlock(
+ public BasicBlock addThrowingInstructionToPossiblyThrowingBlock(
IRCode code,
ListIterator<BasicBlock> blockIterator,
Instruction instruction,
InternalOptions options) {
- instructionIterator.addThrowingInstructionToPossiblyThrowingBlock(
+ return instructionIterator.addThrowingInstructionToPossiblyThrowingBlock(
code, blockIterator, instruction, options);
}
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 224b457..8a69912 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
@@ -26,7 +26,7 @@
public interface InstructionListIterator
extends InstructionIterator, ListIterator<Instruction>, PreviousUntilIterator<Instruction> {
- void addThrowingInstructionToPossiblyThrowingBlock(
+ BasicBlock addThrowingInstructionToPossiblyThrowingBlock(
IRCode code,
ListIterator<BasicBlock> blockIterator,
Instruction instruction,
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index ed95689..819ba43 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -183,12 +183,12 @@
}
@Override
- public void addThrowingInstructionToPossiblyThrowingBlock(
+ public BasicBlock addThrowingInstructionToPossiblyThrowingBlock(
IRCode code,
ListIterator<BasicBlock> blockIterator,
Instruction instruction,
InternalOptions options) {
- currentBlockIterator.addThrowingInstructionToPossiblyThrowingBlock(
+ return currentBlockIterator.addThrowingInstructionToPossiblyThrowingBlock(
code, blockIterator, instruction, options);
}
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 d7e5fc1..5002bac 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
@@ -2174,8 +2174,6 @@
// optimization so that we do not transform half-initialized arrays into fully initialized
// arrays on exceptional edges. If the block has no handlers it is not observable so
// we perform the rewriting.
- // TODO(b/246971330): Allow simplification when all users of the array are in the same
- // try/catch.
if (block.hasCatchHandlers() && instruction.instructionInstanceCanThrow()) {
return null;
}
@@ -2280,8 +2278,7 @@
}
DexType arrayType = newArrayEmpty.type;
boolean encodeAsFilledNewArray = canUseFilledNewArray(arrayType, size, options);
- if (!encodeAsFilledNewArray
- && !canUseFilledArrayData(arrayType, size, instruction.getBlock(), options)) {
+ if (!encodeAsFilledNewArray && !canUseFilledArrayData(arrayType, size, options)) {
return null;
}
return new FilledArrayCandidate(newArrayEmpty, size, encodeAsFilledNewArray);
@@ -2316,23 +2313,13 @@
return false;
}
- private boolean canUseFilledArrayData(
- DexType arrayType, int size, BasicBlock block, RewriteArrayOptions options) {
+ private boolean canUseFilledArrayData(DexType arrayType, int size, RewriteArrayOptions options) {
// If there is only one element it is typically smaller to generate the array put
// instruction instead of fill array data.
if (size < options.minSizeForFilledArrayData || size > options.maxSizeForFilledArrayData) {
return false;
}
- if (!arrayType.isPrimitiveArrayType()) {
- return false;
- }
- // NewArrayFilledData can throw, so creating one as done below would add a second
- // throwing instruction to the same block (the first one being NewArrayEmpty).
- // TODO(b/246971330): Support splitting the block when inserting the additional instruction.
- if (block.hasCatchHandlers()) {
- return false;
- }
- return true;
+ return arrayType.isPrimitiveArrayType();
}
/**
@@ -2394,15 +2381,16 @@
WorkList<BasicBlock> worklist = WorkList.newIdentityWorkList(code.blocks);
while (worklist.hasNext()) {
BasicBlock block = worklist.next();
- simplifyArrayConstructionBlock(block, worklist, code, options.rewriteArrayOptions());
+ simplifyArrayConstructionBlock(block, worklist, code, options);
}
}
private void simplifyArrayConstructionBlock(
- BasicBlock block, WorkList<BasicBlock> worklist, IRCode code, RewriteArrayOptions options) {
+ BasicBlock block, WorkList<BasicBlock> worklist, IRCode code, InternalOptions options) {
+ RewriteArrayOptions rewriteOptions = options.rewriteArrayOptions();
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
- FilledArrayCandidate candidate = computeFilledArrayCandiate(it.next(), options);
+ FilledArrayCandidate candidate = computeFilledArrayCandiate(it.next(), rewriteOptions);
if (candidate == null) {
continue;
}
@@ -2431,7 +2419,7 @@
newArrayEmpty.outValue().replaceUsers(invokeValue);
instructionsToRemove.add(newArrayEmpty);
- boolean originalAllocationPointHasHandlers = newArrayEmpty.getBlock().hasCatchHandlers();
+ boolean originalAllocationPointHasHandlers = block.hasCatchHandlers();
boolean insertionPointHasHandlers = lastArrayPut.getBlock().hasCatchHandlers();
if (!insertionPointHasHandlers && !originalAllocationPointHasHandlers) {
@@ -2462,7 +2450,11 @@
NewArrayFilledData fillArray =
new NewArrayFilledData(newArrayEmpty.outValue(), elementSize, size, contents);
fillArray.setPosition(newArrayEmpty.getPosition());
- it.add(fillArray);
+ BasicBlock newBlock =
+ it.addThrowingInstructionToPossiblyThrowingBlock(code, null, fillArray, options);
+ if (newBlock != null) {
+ worklist.addIfNotSeen(newBlock);
+ }
}
instructionsToRemove.addAll(info.arrayPutsToRemove);
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 8983d39..4d2d2d2 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
@@ -191,7 +191,7 @@
}
@Override
- public void addThrowingInstructionToPossiblyThrowingBlock(
+ public BasicBlock addThrowingInstructionToPossiblyThrowingBlock(
IRCode code,
ListIterator<BasicBlock> blockIterator,
Instruction instruction,
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataWithCatchHandlerTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataWithCatchHandlerTest.java
new file mode 100644
index 0000000..3e04138
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataWithCatchHandlerTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2022, 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.rewrite.arrays;
+
+import static com.android.tools.r8.cf.methodhandles.fields.ClassFieldMethodHandleTest.Main.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dex.code.DexFillArrayData;
+import com.android.tools.r8.dex.code.DexNewArray;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionOffsetSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FilledArrayDataWithCatchHandlerTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("1");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public FilledArrayDataWithCatchHandlerTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(FilledArrayDataWithCatchHandlerTest.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testReleaseD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(FilledArrayDataWithCatchHandlerTest.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkInstructions);
+ }
+
+ private void checkInstructions(CodeInspector inspector) {
+ MethodSubject foo = inspector.clazz(TestClass.class).uniqueMethodWithFinalName("foo");
+ List<InstructionSubject> newArrays =
+ foo.streamInstructions()
+ .filter(i -> i.asDexInstruction().getInstruction() instanceof DexNewArray)
+ .collect(Collectors.toList());
+ assertEquals(1, newArrays.size());
+
+ List<InstructionSubject> fillArrays =
+ foo.streamInstructions()
+ .filter(i -> i.asDexInstruction().getInstruction() instanceof DexFillArrayData)
+ .collect(Collectors.toList());
+ assertEquals(1, fillArrays.size());
+
+ InstructionOffsetSubject offsetNew = newArrays.get(0).getOffset(foo);
+ InstructionOffsetSubject offsetFill = newArrays.get(0).getOffset(foo);
+ assertTrue(
+ foo.streamTryCatches()
+ .allMatch(r -> r.getRange().includes(offsetNew) && r.getRange().includes(offsetFill)));
+ }
+
+ static class TestClass {
+
+ public static int foo() {
+ int value = 1;
+ int[] array = null;
+ try {
+ array = new int[6];
+ } catch (RuntimeException e) {
+ return array[0];
+ }
+ array[0] = value;
+ array[1] = value;
+ array[2] = value;
+ array[3] = value;
+ array[4] = value;
+ array[5] = value;
+ return array[5];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(foo());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
index 939753f..611ed3f 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
@@ -201,6 +201,7 @@
assertArrayTypes(arraysThatUseNewArrayEmpty, DexNewArray.class);
assertArrayTypes(intsThatUseFilledNewArray, DexFilledNewArray.class);
assertFilledArrayData(arraysThatUseFilledData);
+ assertFilledArrayData(catchHandlerNonThrowingFillArrayData);
if (compilationMode == CompilationMode.DEBUG) {
// The explicit assignments can't be collapsed without breaking the debugger's ability to
@@ -256,7 +257,6 @@
assertArrayTypes(arrayWithCorrectCountButIncompleteCoverage, DexNewArray.class);
assertArrayTypes(catchHandlerThrowing, DexNewArray.class);
- assertArrayTypes(catchHandlerNonThrowingFillArrayData, DexNewArray.class);
assertArrayTypes(catchHandlerNonThrowingFilledNewArray, DexFilledNewArray.class);
}