Version 1.6.34
Cherry-pick: Do not inline methods on definitely null receivers
CL: https://r8-review.googlesource.com/c/r8/+/43609
Bug: 140851070
Change-Id: If5c1caef21aefb174a3cd6184df80388c93d2aa5
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 753d1b1..203966b 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 = "1.6.33";
+ public static final String LABEL = "1.6.34";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 3e3ed2b..90c2a58 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -600,11 +600,6 @@
return this;
}
- public IRCode buildEmptyThrowingIRCode(AppView<?> appView, Origin origin) {
- DexCode emptyThrowingDexCode = buildEmptyThrowingDexCode();
- return emptyThrowingDexCode.buildIR(this, appView, origin);
- }
-
/**
* Generates a {@link DexCode} object for the given instructions.
*/
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 0c4e409..f90c1b6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -351,46 +351,37 @@
info.include(invoke.getType(), candidate);
}
+ Value receiver = invoke.getReceiver();
+ if (receiver.getTypeLattice().isDefinitelyNull()) {
+ // A definitely null receiver will throw an error on call site.
+ return null;
+ }
InlineAction action = new InlineAction(candidate, invoke, reason);
- Value receiver = invoke.getReceiver();
if (receiver.getTypeLattice().isNullable()) {
- InternalOptions options = appView.options();
- if (receiver.getTypeLattice().isDefinitelyNull()) {
- if (!options.enableInliningOfInvokesWithDefinitelyNullReceivers) {
+ assert !receiver.getTypeLattice().isDefinitelyNull();
+ // When inlining an instance method call, we need to preserve the null check for the
+ // receiver. Therefore, if the receiver may be null and the candidate inlinee does not
+ // throw if the receiver is null before any other side effect, then we must synthesize a
+ // null check.
+ if (!candidate.getOptimizationInfo().checksNullReceiverBeforeAnySideEffect()) {
+ InternalOptions options = appView.options();
+ if (!options.enableInliningOfInvokesWithNullableReceivers) {
+ return null;
+ }
+ if (!options.nullableReceiverInliningFilter.isEmpty()
+ && !options.nullableReceiverInliningFilter.contains(
+ invoke.getInvokedMethod().toSourceString())) {
return null;
}
if (Log.ENABLED && Log.isLoggingEnabledFor(Inliner.class)) {
Log.debug(
Inliner.class,
- "Inlining method `%s` with definitely null receiver into `%s`",
+ "Inlining method `%s` with nullable receiver into `%s`",
invoke.getInvokedMethod().toSourceString(),
invocationContext.toSourceString());
}
- action.setShouldReturnEmptyThrowingCode();
- } else {
- // When inlining an instance method call, we need to preserve the null check for the
- // receiver. Therefore, if the receiver may be null and the candidate inlinee does not
- // throw if the receiver is null before any other side effect, then we must synthesize a
- // null check.
- if (!candidate.getOptimizationInfo().checksNullReceiverBeforeAnySideEffect()) {
- if (!options.enableInliningOfInvokesWithNullableReceivers) {
- return null;
- }
- if (!options.nullableReceiverInliningFilter.isEmpty()
- && !options.nullableReceiverInliningFilter.contains(
- invoke.getInvokedMethod().toSourceString())) {
- return null;
- }
- if (Log.ENABLED && Log.isLoggingEnabledFor(Inliner.class)) {
- Log.debug(
- Inliner.class,
- "Inlining method `%s` with nullable receiver into `%s`",
- invoke.getInvokedMethod().toSourceString(),
- invocationContext.toSourceString());
- }
- action.setShouldSynthesizeNullCheckForReceiver();
- }
+ action.setShouldSynthesizeNullCheckForReceiver();
}
}
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 c35cd11..b9c16e7 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
@@ -536,7 +536,6 @@
public final Invoke invoke;
final Reason reason;
- private boolean shouldReturnEmptyThrowingCode;
private boolean shouldSynthesizeNullCheckForReceiver;
InlineAction(DexEncodedMethod target, Invoke invoke, Reason reason) {
@@ -545,10 +544,6 @@
this.reason = reason;
}
- void setShouldReturnEmptyThrowingCode() {
- shouldReturnEmptyThrowingCode = true;
- }
-
void setShouldSynthesizeNullCheckForReceiver() {
shouldSynthesizeNullCheckForReceiver = true;
}
@@ -561,53 +556,48 @@
LensCodeRewriter lensCodeRewriter) {
Origin origin = appView.appInfo().originFor(target.method.holder);
- IRCode code;
- if (shouldReturnEmptyThrowingCode) {
- code = target.buildEmptyThrowingIRCode(appView, origin);
- } else {
- // Build the IR for a yet not processed method, and perform minimal IR processing.
- code = target.buildInliningIR(context, appView, generator, callerPosition, origin);
+ // Build the IR for a yet not processed method, and perform minimal IR processing.
+ IRCode code = target.buildInliningIR(context, appView, generator, callerPosition, origin);
- // Insert a null check if this is needed to preserve the implicit null check for the
- // receiver.
- if (shouldSynthesizeNullCheckForReceiver) {
- List<Value> arguments = code.collectArguments();
- if (!arguments.isEmpty()) {
- Value receiver = arguments.get(0);
- assert receiver.isThis();
+ // Insert a null check if this is needed to preserve the implicit null check for the
+ // receiver.
+ if (shouldSynthesizeNullCheckForReceiver) {
+ List<Value> arguments = code.collectArguments();
+ if (!arguments.isEmpty()) {
+ Value receiver = arguments.get(0);
+ assert receiver.isThis();
- BasicBlock entryBlock = code.entryBlock();
+ BasicBlock entryBlock = code.entryBlock();
- // Insert a new block between the last argument instruction and the first actual
- // instruction of the method.
- BasicBlock throwBlock =
- entryBlock.listIterator(code, arguments.size()).split(code, 0, null);
- assert !throwBlock.hasCatchHandlers();
+ // Insert a new block between the last argument instruction and the first actual
+ // instruction of the method.
+ BasicBlock throwBlock =
+ entryBlock.listIterator(code, arguments.size()).split(code, 0, null);
+ assert !throwBlock.hasCatchHandlers();
- // Link the entry block to the successor of the newly inserted block.
- BasicBlock continuationBlock = throwBlock.unlinkSingleSuccessor();
- entryBlock.link(continuationBlock);
+ // Link the entry block to the successor of the newly inserted block.
+ BasicBlock continuationBlock = throwBlock.unlinkSingleSuccessor();
+ entryBlock.link(continuationBlock);
- // Replace the last instruction of the entry block, which is now a goto instruction,
- // with an `if-eqz` instruction that jumps to the newly inserted block if the receiver
- // is null.
- If ifInstruction = new If(If.Type.EQ, receiver);
- entryBlock.replaceLastInstruction(ifInstruction, code);
- assert ifInstruction.getTrueTarget() == throwBlock;
- assert ifInstruction.fallthroughBlock() == continuationBlock;
+ // Replace the last instruction of the entry block, which is now a goto instruction,
+ // with an `if-eqz` instruction that jumps to the newly inserted block if the receiver
+ // is null.
+ If ifInstruction = new If(If.Type.EQ, receiver);
+ entryBlock.replaceLastInstruction(ifInstruction, code);
+ assert ifInstruction.getTrueTarget() == throwBlock;
+ assert ifInstruction.fallthroughBlock() == continuationBlock;
- // Replace the single goto instruction in the newly inserted block by `throw null`.
- InstructionListIterator iterator = throwBlock.listIterator(code);
- Value nullValue = iterator.insertConstNullInstruction(code, appView.options());
- iterator.next();
- iterator.replaceCurrentInstruction(new Throw(nullValue));
- } else {
- assert false : "Unable to synthesize a null check for the receiver";
- }
+ // Replace the single goto instruction in the newly inserted block by `throw null`.
+ InstructionListIterator iterator = throwBlock.listIterator(code);
+ Value nullValue = iterator.insertConstNullInstruction(code, appView.options());
+ iterator.next();
+ iterator.replaceCurrentInstruction(new Throw(nullValue));
+ } else {
+ assert false : "Unable to synthesize a null check for the receiver";
}
- if (!target.isProcessed()) {
- lensCodeRewriter.rewrite(code, target);
- }
+ }
+ if (!target.isProcessed()) {
+ lensCodeRewriter.rewrite(code, target);
}
return new InlineeWithReason(code, reason);
}
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 dbd4989..faf5a9e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -196,9 +196,6 @@
public boolean enableNonNullTracking = true;
public boolean enableInlining =
!Version.isDev() || System.getProperty("com.android.tools.r8.disableinlining") == null;
- public boolean enableInliningOfInvokesWithDefinitelyNullReceivers =
- System.getProperty("com.android.tools.r8.disableInliningOfInvokesWithDefinitelyNullReceivers")
- == null;
public boolean enableInliningOfInvokesWithNullableReceivers = true;
public boolean disableInliningOfLibraryMethodOverrides = true;
public boolean enableClassInlining = true;