Refactor util to add null throwing code.
Bug: 72443802, 124246610
Change-Id: I4dbc555f8399f60dd45cc4b9a631f8a0d72f0ded
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 5f10bd1..9aa0cba 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
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -182,6 +183,68 @@
}
@Override
+ public void replaceCurrentInstructionWithThrowNull(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Set<BasicBlock> blocksToRemove) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ BasicBlock block = current.getBlock();
+ assert !blocksToRemove.contains(block);
+
+ BasicBlock normalSuccessorBlock = split(code, blockIterator);
+ previous();
+
+ // Unlink all blocks that are dominated by successor.
+ {
+ DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
+ blocksToRemove.addAll(block.unlink(normalSuccessorBlock, dominatorTree));
+ }
+
+ // Insert constant null before the instruction.
+ previous();
+ ConstNumber constNumberInstruction = code.createConstNull();
+ // Note that we only keep position info for throwing instructions in release mode.
+ constNumberInstruction.setPosition(
+ appView.options().debug ? current.getPosition() : Position.none());
+ add(constNumberInstruction);
+ next();
+
+ // Replace the instruction by throw.
+ Throw throwInstruction = new Throw(constNumberInstruction.outValue());
+ for (Value inValue : current.inValues()) {
+ if (inValue.hasLocalInfo()) {
+ // Add this value as a debug value to avoid changing its live range.
+ throwInstruction.addDebugValue(inValue);
+ }
+ }
+ replaceCurrentInstruction(throwInstruction);
+ next();
+ remove();
+
+ // Remove all catch handlers where the guard does not include NullPointerException.
+ if (block.hasCatchHandlers()) {
+ CatchHandlers<BasicBlock> catchHandlers = block.getCatchHandlers();
+ catchHandlers.forEach(
+ (guard, target) -> {
+ if (blocksToRemove.contains(target)) {
+ // Already removed previously. This may happen if two catch handlers have the same
+ // target.
+ return;
+ }
+ if (!appView.appInfo().isSubtype(appView.dexItemFactory().npeType, guard)) {
+ // TODO(christofferqa): Consider updating previous dominator tree instead of
+ // rebuilding it from scratch.
+ DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
+ blocksToRemove.addAll(block.unlink(target, dominatorTree));
+ }
+ });
+ }
+ }
+
+ @Override
public BasicBlock split(IRCode code, ListIterator<BasicBlock> blocksIterator) {
List<BasicBlock> blocks = code.blocks;
assert blocksIterator == null || IteratorUtils.peekPrevious(blocksIterator) == block;
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 0a1bf67..8186ca2 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
@@ -5,11 +5,13 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Set;
public interface InstructionListIterator
extends InstructionIterator, PreviousUntilIterator<Instruction> {
@@ -49,6 +51,25 @@
}
/**
+ * Replace the current instruction with null throwing instructions.
+ *
+ * @param appView with subtype info through which we can test if the guard is subtype of NPE.
+ * @param code the IR code for the block this iterator originates from.
+ * @param blockIterator basic block iterator used to iterate the blocks.
+ * @param blocksToRemove set passed where blocks that were detached from the graph, but not
+ * removed yet are added. When inserting `throw null`, catch handlers whose guard does not
+ * catch NPE will be removed, but not yet removed using the passed block
+ * <code>blockIterator</code>. When iterating using <code>blockIterator</code> after then
+ * method returns the blocks in this set must be skipped when iterating with the active
+ * <code>blockIterator</code> and ultimately removed.
+ */
+ void replaceCurrentInstructionWithThrowNull(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Set<BasicBlock> blocksToRemove);
+
+ /**
* Split the block into two blocks at the point of the {@link ListIterator} cursor. The existing
* block will have all the instructions before the cursor, and the new block all the
* instructions after the cursor.
@@ -65,7 +86,6 @@
*/
BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator);
-
default BasicBlock split(IRCode code) {
return split(code, null);
}
@@ -117,9 +137,9 @@
* @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
* just after the block for which this is the instruction iterator. After this method returns
* it will be positioned just after the basic block returned.
- * @param blocksToRemove list passed where blocks that where detached from the graph, but not
+ * @param blocksToRemove list passed where blocks that were detached from the graph, but not
* removed are added. When inlining an inlinee that always throws blocks in the <code>code
- * </code> can be detached, and not simply removed unsing the passed <code>blockIterator
+ * </code> can be detached, and not simply removed using the passed <code>blockIterator
* </code>. When iterating using <code>blockIterator</code> after then method returns the
* blocks in this list must be skipped when iterating with the active <code>blockIterator
* </code> and ultimately removed.
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
index 01c66a8..12de1ce 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionIterator.java
@@ -5,10 +5,12 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import java.util.List;
import java.util.ListIterator;
+import java.util.Set;
public class LinearFlowInstructionIterator implements InstructionIterator, InstructionListIterator {
@@ -36,6 +38,16 @@
}
@Override
+ public void replaceCurrentInstructionWithThrowNull(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Set<BasicBlock> blocksToRemove) {
+ currentBlockIterator.replaceCurrentInstructionWithThrowNull(
+ appView, code, blockIterator, blocksToRemove);
+ }
+
+ @Override
public BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator) {
return currentBlockIterator.split(code, blockIterator);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 7ca48ed..31ae148 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import static com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.Strategy.ALLOW_ARGUMENT_REMOVAL;
import static com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.Strategy.DISALLOW_ARGUMENT_REMOVAL;
@@ -27,16 +26,11 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.FieldInstruction;
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.InvokeMethod;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.logging.Log;
@@ -425,8 +419,8 @@
// would lead to an IncompatibleClassChangeError (see MemberResolutionTest#lookupStaticField-
// WithFieldGetFromNullReferenceDirectly).
if (!receiver.getTypeLattice().isDefinitelyNull()) {
- replaceCurrentInstructionWithThrowNull(
- instruction, blockIterator, instructionIterator, code, blocksToBeRemoved);
+ instructionIterator.replaceCurrentInstructionWithThrowNull(
+ appView, code, blockIterator, blocksToBeRemoved);
++numberOfInstanceGetOrInstancePutWithNullReceiver;
replacedByThrowNull = true;
}
@@ -512,8 +506,8 @@
if (invoke.isInvokeMethodWithReceiver()) {
Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
if (receiver.isAlwaysNull(appView)) {
- replaceCurrentInstructionWithThrowNull(
- invoke, blockIterator, instructionIterator, code, blocksToBeRemoved);
+ instructionIterator.replaceCurrentInstructionWithThrowNull(
+ appView, code, blockIterator, blocksToBeRemoved);
++numberOfInvokesWithNullReceiver;
return;
}
@@ -530,8 +524,8 @@
for (int i = 0; i < invoke.arguments().size(); i++) {
Value argument = invoke.arguments().get(i);
if (argument.isAlwaysNull(appView) && facts.get(i)) {
- replaceCurrentInstructionWithThrowNull(
- invoke, blockIterator, instructionIterator, code, blocksToBeRemoved);
+ instructionIterator.replaceCurrentInstructionWithThrowNull(
+ appView, code, blockIterator, blocksToBeRemoved);
++numberOfInvokesWithNullArgument;
return;
}
@@ -539,63 +533,4 @@
}
}
- private void replaceCurrentInstructionWithThrowNull(
- Instruction instruction,
- ListIterator<BasicBlock> blockIterator,
- InstructionListIterator instructionIterator,
- IRCode code,
- Set<BasicBlock> blocksToBeRemoved) {
- BasicBlock block = instruction.getBlock();
- assert !blocksToBeRemoved.contains(block);
-
- BasicBlock normalSuccessorBlock = instructionIterator.split(code, blockIterator);
- instructionIterator.previous();
-
- // Unlink all blocks that are dominated by successor.
- {
- DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
- blocksToBeRemoved.addAll(block.unlink(normalSuccessorBlock, dominatorTree));
- }
-
- // Insert constant null before the instruction.
- instructionIterator.previous();
- ConstNumber constNumberInstruction = code.createConstNull();
- // Note that we only keep position info for throwing instructions in release mode.
- constNumberInstruction.setPosition(
- appView.options().debug ? instruction.getPosition() : Position.none());
- instructionIterator.add(constNumberInstruction);
- instructionIterator.next();
-
- // Replace the instruction by throw.
- Throw throwInstruction = new Throw(constNumberInstruction.outValue());
- for (Value inValue : instruction.inValues()) {
- if (inValue.hasLocalInfo()) {
- // Add this value as a debug value to avoid changing its live range.
- throwInstruction.addDebugValue(inValue);
- }
- }
- instructionIterator.replaceCurrentInstruction(throwInstruction);
- instructionIterator.next();
- instructionIterator.remove();
-
- // Remove all catch handlers where the guard does not include NullPointerException.
- if (block.hasCatchHandlers()) {
- CatchHandlers<BasicBlock> catchHandlers = block.getCatchHandlers();
- catchHandlers.forEach(
- (guard, target) -> {
- if (blocksToBeRemoved.contains(target)) {
- // Already removed previously. This may happen if two catch handlers have the same
- // target.
- return;
- }
- if (!appView.appInfo().isSubtype(appView.dexItemFactory().npeType, guard)) {
- // TODO(christofferqa): Consider updating previous dominator tree instead of
- // rebuilding it from scratch.
- DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
- blocksToBeRemoved.addAll(block.unlink(target, dominatorTree));
- }
- });
- }
- }
-
}
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 b30a9cf..0f32501 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
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexType;
@@ -22,6 +23,8 @@
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Set;
+
import org.junit.Test;
public class RegisterMoveSchedulerTest {
@@ -45,6 +48,15 @@
}
@Override
+ public void replaceCurrentInstructionWithThrowNull(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Set<BasicBlock> blocksToRemove) {
+ throw new Unimplemented();
+ }
+
+ @Override
public boolean hasNext() {
return it.hasNext();
}