Merge commit '99cf2c2ea56f74a1626daf535043d14b43e1862a' into dev-release
Change-Id: Icfbf7264b44ba092c8c263b30e8907c308dcb8c2
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 240933c..2bb729c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -621,6 +621,7 @@
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
public final DexType androidOsBundleType = createStaticallyKnownType("Landroid/os/Bundle;");
+ public final DexType androidOsHandlerType = createStaticallyKnownType("Landroid/os/Handler;");
public final DexType androidOsParcelableCreatorType =
createStaticallyKnownType("Landroid/os/Parcelable$Creator;");
public final DexType androidSystemOsConstantsType =
@@ -1053,6 +1054,7 @@
ImmutableSet.<DexType>builder()
.add(
androidAppActivity,
+ androidOsHandlerType,
callableType,
enumType,
npeType,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/ConstSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/ConstSimpleInliningConstraint.java
new file mode 100644
index 0000000..4ca4fa3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/ConstSimpleInliningConstraint.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2024, 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.analysis.inlining;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ConstSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private ConstSimpleInliningConstraint(int argumentIndex) {
+ super(argumentIndex);
+ }
+
+ static ConstSimpleInliningConstraint create(
+ int argumentIndex, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new ConstSimpleInliningConstraint(argumentIndex);
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argumentRoot = invoke.getArgument(getArgumentIndex()).getAliasedValue();
+ return argumentRoot.isDefinedByInstructionSatisfying(Instruction::isConstInstruction);
+ }
+
+ @Override
+ public SimpleInliningConstraint fixupAfterParametersChanged(
+ AppView<AppInfoWithLiveness> appView,
+ ArgumentInfoCollection changes,
+ SimpleInliningConstraintFactory factory) {
+ if (changes.isArgumentRemoved(getArgumentIndex())) {
+ RemovedArgumentInfo removedArgumentInfo =
+ changes.getArgumentInfo(getArgumentIndex()).asRemovedArgumentInfo();
+ if (!removedArgumentInfo.hasSingleValue()) {
+ // We should never have constraints for unused arguments.
+ assert false;
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ SingleValue singleValue = removedArgumentInfo.getSingleValue();
+ return singleValue.isSingleConstValue()
+ ? AlwaysSimpleInliningConstraint.getInstance()
+ : NeverSimpleInliningConstraint.getInstance();
+ } else {
+ assert !changes.hasArgumentInfo(getArgumentIndex());
+ }
+ return withArgumentIndex(changes.getNewArgumentIndex(getArgumentIndex()), factory);
+ }
+
+ @Override
+ SimpleInliningArgumentConstraint withArgumentIndex(
+ int argumentIndex, SimpleInliningConstraintFactory factory) {
+ return factory.createConstConstraint(argumentIndex);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
index a207c8d..cbeb329 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
@@ -64,11 +64,12 @@
return new SimpleInliningConstraintConjunction(ImmutableList.of(this, other));
}
- public final SimpleInliningConstraint lazyMeet(Supplier<SimpleInliningConstraint> supplier) {
+ public final SimpleInliningConstraintWithDepth lazyMeet(
+ Supplier<SimpleInliningConstraintWithDepth> supplier) {
if (isNever()) {
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
- return meet(supplier.get());
+ return supplier.get().meet(this);
}
public final SimpleInliningConstraint join(SimpleInliningConstraint other) {
@@ -96,4 +97,8 @@
AppView<AppInfoWithLiveness> appView,
ArgumentInfoCollection changes,
SimpleInliningConstraintFactory factory);
+
+ public final SimpleInliningConstraintWithDepth withDepth(int instructionDepth) {
+ return new SimpleInliningConstraintWithDepth(this, instructionDepth);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
index 49d5674..63a2886 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
@@ -7,17 +7,23 @@
import static com.android.tools.r8.ir.code.Opcodes.GOTO;
import static com.android.tools.r8.ir.code.Opcodes.IF;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.STRING_SWITCH;
import static com.android.tools.r8.ir.code.Opcodes.THROW;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.IfType;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.StringSwitch;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -40,7 +46,8 @@
*/
public class SimpleInliningConstraintAnalysis {
- private final SimpleInliningConstraintFactory factory;
+ private final SimpleInliningConstraintFactory constraintFactory;
+ private final DexItemFactory dexItemFactory;
private final ProgramMethod method;
private final InternalOptions options;
private final int simpleInliningConstraintThreshold;
@@ -49,21 +56,22 @@
public SimpleInliningConstraintAnalysis(
AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
- this.factory = appView.simpleInliningConstraintFactory();
+ this.constraintFactory = appView.simpleInliningConstraintFactory();
+ this.dexItemFactory = appView.dexItemFactory();
this.method = method;
this.options = appView.options();
this.simpleInliningConstraintThreshold = appView.options().simpleInliningConstraintThreshold;
}
- public SimpleInliningConstraint analyzeCode(IRCode code) {
+ public SimpleInliningConstraintWithDepth analyzeCode(IRCode code) {
if (method.getReference().getArity() == 0) {
// The method does not have any parameters, so there is no need to analyze the method.
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
if (options.debug) {
// Inlining is not enabled in debug mode.
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
// Run a bounded depth-first traversal to collect the path constraints that lead to early
@@ -73,25 +81,33 @@
return analyzeInstructionsInBlock(code.entryBlock(), 0, instructionIterator);
}
- private SimpleInliningConstraint analyzeInstructionsInBlock(BasicBlock block, int depth) {
+ private SimpleInliningConstraintWithDepth analyzeInstructionsInBlock(
+ BasicBlock block, int depth) {
return analyzeInstructionsInBlock(block, depth, block.iterator());
}
- private SimpleInliningConstraint analyzeInstructionsInBlock(
+ private SimpleInliningConstraintWithDepth analyzeInstructionsInBlock(
BasicBlock block, int instructionDepth, InstructionIterator instructionIterator) {
// If we reach a block that has already been seen, give up.
if (!seen.add(block)) {
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
// Move the instruction iterator forward to the block's jump instruction, while incrementing the
// instruction depth of the depth-first traversal.
Instruction instruction = instructionIterator.next();
+ SimpleInliningConstraint blockConstraint = AlwaysSimpleInliningConstraint.getInstance();
while (!instruction.isJumpInstruction()) {
assert !instruction.isArgument();
assert !instruction.isDebugInstruction();
- if (!instruction.isAssume()) {
- instructionDepth += 1;
+ SimpleInliningConstraint instructionConstraint =
+ computeConstraintForInstructionNotToMaterialize(instruction);
+ if (instructionConstraint.isAlways()) {
+ assert instruction.isAssume();
+ } else if (instructionConstraint.isNever()) {
+ instructionDepth++;
+ } else {
+ blockConstraint = blockConstraint.meet(instructionConstraint);
}
instruction = instructionIterator.next();
}
@@ -99,11 +115,39 @@
// If we have exceeded the threshold, then all paths from this instruction will not lead to any
// early exits, so return 'never'.
if (instructionDepth > simpleInliningConstraintThreshold) {
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
- // Analyze the jump instruction.
- // TODO(b/132600418): Extend to switch and throw instructions.
+ SimpleInliningConstraintWithDepth jumpConstraint =
+ computeConstraintForJumpInstruction(instruction.asJumpInstruction(), instructionDepth);
+ return jumpConstraint.meet(blockConstraint);
+ }
+
+ private SimpleInliningConstraint computeConstraintForInstructionNotToMaterialize(
+ Instruction instruction) {
+ if (instruction.isAssume()) {
+ return AlwaysSimpleInliningConstraint.getInstance();
+ }
+ if (instruction.isInvokeVirtual()) {
+ InvokeVirtual invoke = instruction.asInvokeVirtual();
+ if (invoke.getInvokedMethod().isIdenticalTo(dexItemFactory.objectMembers.getClass)
+ && invoke.hasUnusedOutValue()) {
+ Value receiver = invoke.getReceiver();
+ if (receiver.getType().isDefinitelyNotNull()) {
+ return AlwaysSimpleInliningConstraint.getInstance();
+ }
+ Value receiverRoot = receiver.getAliasedValue();
+ if (receiverRoot.isDefinedByInstructionSatisfying(Instruction::isArgument)) {
+ Argument argument = receiverRoot.getDefinition().asArgument();
+ return constraintFactory.createNotEqualToNullConstraint(argument.getIndex());
+ }
+ }
+ }
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ private SimpleInliningConstraintWithDepth computeConstraintForJumpInstruction(
+ JumpInstruction instruction, int instructionDepth) {
switch (instruction.opcode()) {
case IF:
If ifInstruction = instruction.asIf();
@@ -125,7 +169,7 @@
// Compute the constraint for which paths through the true target are guaranteed to exit
// early.
- SimpleInliningConstraint trueTargetConstraint =
+ SimpleInliningConstraintWithDepth trueTargetConstraint =
computeConstraintFromIfTest(
argumentIndex, argumentType, otherOperand, ifInstruction.getType())
// Only recurse into the true target if the constraint from the if-instruction
@@ -135,7 +179,7 @@
// Compute the constraint for which paths through the false target are guaranteed to
// exit early.
- SimpleInliningConstraint fallthroughTargetConstraint =
+ SimpleInliningConstraintWithDepth fallthroughTargetConstraint =
computeConstraintFromIfTest(
argumentIndex, argumentType, otherOperand, ifInstruction.getType().inverted())
// Only recurse into the false target if the constraint from the if-instruction
@@ -152,19 +196,42 @@
return analyzeInstructionsInBlock(instruction.asGoto().getTarget(), instructionDepth);
case RETURN:
- return AlwaysSimpleInliningConstraint.getInstance();
+ return AlwaysSimpleInliningConstraint.getInstance().withDepth(instructionDepth);
+
+ case STRING_SWITCH:
+ // Require that all cases including the default case are simple. In that case we can
+ // guarantee simpleness by requiring that the switch value is constant.
+ StringSwitch stringSwitch = instruction.asStringSwitch();
+ Value valueRoot = stringSwitch.value().getAliasedValue();
+ if (!valueRoot.isDefinedByInstructionSatisfying(Instruction::isArgument)) {
+ return SimpleInliningConstraintWithDepth.getNever();
+ }
+ int maxInstructionDepth = instructionDepth;
+ for (BasicBlock successor : stringSwitch.getBlock().getNormalSuccessors()) {
+ SimpleInliningConstraintWithDepth successorConstraintWithDepth =
+ analyzeInstructionsInBlock(successor, instructionDepth);
+ if (!successorConstraintWithDepth.getConstraint().isAlways()) {
+ return SimpleInliningConstraintWithDepth.getNever();
+ }
+ maxInstructionDepth =
+ Math.max(maxInstructionDepth, successorConstraintWithDepth.getInstructionDepth());
+ }
+ Argument argument = valueRoot.getDefinition().asArgument();
+ ConstSimpleInliningConstraint simpleConstraint =
+ constraintFactory.createConstConstraint(argument.getIndex());
+ return simpleConstraint.withDepth(maxInstructionDepth);
case THROW:
- return block.hasCatchHandlers()
- ? NeverSimpleInliningConstraint.getInstance()
- : AlwaysSimpleInliningConstraint.getInstance();
+ return instruction.getBlock().hasCatchHandlers()
+ ? SimpleInliningConstraintWithDepth.getNever()
+ : SimpleInliningConstraintWithDepth.getAlways(instructionDepth);
default:
break;
}
// Give up.
- return NeverSimpleInliningConstraint.getInstance();
+ return SimpleInliningConstraintWithDepth.getNever();
}
private SimpleInliningConstraint computeConstraintFromIfTest(
@@ -174,15 +241,16 @@
case EQ:
if (isZeroTest) {
if (argumentType.isReferenceType()) {
- return factory.createEqualToNullConstraint(argumentIndex);
+ return constraintFactory.createEqualToNullConstraint(argumentIndex);
}
if (argumentType.isBooleanType()) {
- return factory.createEqualToFalseConstraint(argumentIndex);
+ return constraintFactory.createEqualToFalseConstraint(argumentIndex);
}
} else if (argumentType.isPrimitiveType()) {
OptionalLong rawValue = getRawNumberValue(otherOperand);
if (rawValue.isPresent()) {
- return factory.createEqualToNumberConstraint(argumentIndex, rawValue.getAsLong());
+ return constraintFactory.createEqualToNumberConstraint(
+ argumentIndex, rawValue.getAsLong());
}
}
return NeverSimpleInliningConstraint.getInstance();
@@ -190,15 +258,16 @@
case NE:
if (isZeroTest) {
if (argumentType.isReferenceType()) {
- return factory.createNotEqualToNullConstraint(argumentIndex);
+ return constraintFactory.createNotEqualToNullConstraint(argumentIndex);
}
if (argumentType.isBooleanType()) {
- return factory.createEqualToTrueConstraint(argumentIndex);
+ return constraintFactory.createEqualToTrueConstraint(argumentIndex);
}
} else if (argumentType.isPrimitiveType()) {
OptionalLong rawValue = getRawNumberValue(otherOperand);
if (rawValue.isPresent()) {
- return factory.createNotEqualToNumberConstraint(argumentIndex, rawValue.getAsLong());
+ return constraintFactory.createNotEqualToNumberConstraint(
+ argumentIndex, rawValue.getAsLong());
}
}
return NeverSimpleInliningConstraint.getInstance();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
index 5b60ccd..39a8164 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNull;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.utils.ArrayUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -15,16 +16,29 @@
public class SimpleInliningConstraintFactory {
// Immutable argument constraints for low argument indices to avoid overhead of ConcurrentHashMap.
+ private final ConstSimpleInliningConstraint[] lowConstConstraints =
+ ArrayUtils.initialize(
+ new ConstSimpleInliningConstraint[5], i -> ConstSimpleInliningConstraint.create(i, this));
private final EqualToBooleanSimpleInliningConstraint[] lowEqualToFalseConstraints =
- new EqualToBooleanSimpleInliningConstraint[5];
+ ArrayUtils.initialize(
+ new EqualToBooleanSimpleInliningConstraint[5],
+ i -> EqualToBooleanSimpleInliningConstraint.create(i, false, this));
private final EqualToBooleanSimpleInliningConstraint[] lowEqualToTrueConstraints =
- new EqualToBooleanSimpleInliningConstraint[5];
+ ArrayUtils.initialize(
+ new EqualToBooleanSimpleInliningConstraint[5],
+ i -> EqualToBooleanSimpleInliningConstraint.create(i, true, this));
private final NullSimpleInliningConstraint[] lowNotEqualToNullConstraints =
- new NullSimpleInliningConstraint[5];
+ ArrayUtils.initialize(
+ new NullSimpleInliningConstraint[5],
+ i -> NullSimpleInliningConstraint.create(i, definitelyNotNull(), this));
private final NullSimpleInliningConstraint[] lowEqualToNullConstraints =
- new NullSimpleInliningConstraint[5];
+ ArrayUtils.initialize(
+ new NullSimpleInliningConstraint[5],
+ i -> NullSimpleInliningConstraint.create(i, definitelyNull(), this));
// Argument constraints for high argument indices.
+ private final Map<Integer, ConstSimpleInliningConstraint> highConstConstraints =
+ new ConcurrentHashMap<>();
private final Map<Integer, EqualToBooleanSimpleInliningConstraint> highEqualToFalseConstraints =
new ConcurrentHashMap<>();
private final Map<Integer, EqualToBooleanSimpleInliningConstraint> highEqualToTrueConstraints =
@@ -34,20 +48,12 @@
private final Map<Integer, NullSimpleInliningConstraint> highEqualToNullConstraints =
new ConcurrentHashMap<>();
- public SimpleInliningConstraintFactory() {
- for (int i = 0; i < lowEqualToFalseConstraints.length; i++) {
- lowEqualToFalseConstraints[i] = EqualToBooleanSimpleInliningConstraint.create(i, false, this);
- }
- for (int i = 0; i < lowEqualToTrueConstraints.length; i++) {
- lowEqualToTrueConstraints[i] = EqualToBooleanSimpleInliningConstraint.create(i, true, this);
- }
- for (int i = 0; i < lowNotEqualToNullConstraints.length; i++) {
- lowNotEqualToNullConstraints[i] =
- NullSimpleInliningConstraint.create(i, definitelyNotNull(), this);
- }
- for (int i = 0; i < lowEqualToNullConstraints.length; i++) {
- lowEqualToNullConstraints[i] = NullSimpleInliningConstraint.create(i, definitelyNull(), this);
- }
+ public ConstSimpleInliningConstraint createConstConstraint(int argumentIndex) {
+ return createArgumentConstraint(
+ argumentIndex,
+ lowConstConstraints,
+ highConstConstraints,
+ () -> ConstSimpleInliningConstraint.create(argumentIndex, this));
}
public EqualToBooleanSimpleInliningConstraint createEqualToFalseConstraint(int argumentIndex) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintWithDepth.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintWithDepth.java
new file mode 100644
index 0000000..391f5fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintWithDepth.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2024, 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.analysis.inlining;
+
+public class SimpleInliningConstraintWithDepth {
+
+ private static final SimpleInliningConstraintWithDepth NEVER =
+ new SimpleInliningConstraintWithDepth(NeverSimpleInliningConstraint.getInstance(), 0);
+
+ private final SimpleInliningConstraint constraint;
+ private final int instructionDepth;
+
+ public SimpleInliningConstraintWithDepth(
+ SimpleInliningConstraint constraint, int instructionDepth) {
+ this.constraint = constraint;
+ this.instructionDepth = instructionDepth;
+ }
+
+ public static SimpleInliningConstraintWithDepth getAlways(int instructionDepth) {
+ return AlwaysSimpleInliningConstraint.getInstance().withDepth(instructionDepth);
+ }
+
+ public static SimpleInliningConstraintWithDepth getNever() {
+ return NEVER;
+ }
+
+ public SimpleInliningConstraint getConstraint() {
+ return constraint;
+ }
+
+ public SimpleInliningConstraint getNopConstraint() {
+ return instructionDepth == 0 ? constraint : NeverSimpleInliningConstraint.getInstance();
+ }
+
+ public int getInstructionDepth() {
+ return instructionDepth;
+ }
+
+ public SimpleInliningConstraintWithDepth join(SimpleInliningConstraintWithDepth other) {
+ SimpleInliningConstraint joinConstraint = constraint.join(other.constraint);
+ return joinConstraint.withDepth(Math.max(instructionDepth, other.instructionDepth));
+ }
+
+ public SimpleInliningConstraintWithDepth meet(SimpleInliningConstraint other) {
+ return new SimpleInliningConstraintWithDepth(constraint.meet(other), instructionDepth);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 994ef53..b95529b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -742,10 +742,6 @@
new TypeAnalysis(appView, ir).narrowing();
}
- if (conversionOptions.isStringSwitchConversionEnabled()) {
- StringSwitchConverter.convertToStringSwitchInstructions(ir, appView.dexItemFactory());
- }
-
ir.removeRedundantBlocks();
assert ir.isConsistentSSABeforeTypesAreCorrect(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 9f18e74..9d6a8b6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.ir.conversion.passes.FilledNewArrayRewriter;
import com.android.tools.r8.ir.conversion.passes.MoveResultRewriter;
import com.android.tools.r8.ir.conversion.passes.ParentConstructorHoistingCodeRewriter;
+import com.android.tools.r8.ir.conversion.passes.StringSwitchConverter;
import com.android.tools.r8.ir.conversion.passes.StringSwitchRemover;
import com.android.tools.r8.ir.conversion.passes.ThrowCatchOptimizer;
import com.android.tools.r8.ir.conversion.passes.TrivialPhiSimplifier;
@@ -120,7 +121,6 @@
protected final IdentifierNameStringMarker identifierNameStringMarker;
private final Devirtualizer devirtualizer;
protected final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
- private final StringSwitchRemover stringSwitchRemover;
private final TypeChecker typeChecker;
protected EnumUnboxer enumUnboxer;
protected final NumberUnboxer numberUnboxer;
@@ -202,7 +202,6 @@
this.identifierNameStringMarker = null;
this.devirtualizer = null;
this.typeChecker = null;
- this.stringSwitchRemover = null;
this.methodOptimizationInfoCollector = null;
this.enumUnboxer = EnumUnboxer.empty();
this.numberUnboxer = NumberUnboxer.empty();
@@ -275,10 +274,6 @@
this.enumUnboxer = EnumUnboxer.empty();
this.numberUnboxer = NumberUnboxer.empty();
}
- this.stringSwitchRemover =
- options.isStringSwitchConversionEnabled()
- ? new StringSwitchRemover(appView, identifierNameStringMarker)
- : null;
}
public IRConverter(AppInfo appInfo) {
@@ -590,6 +585,13 @@
return timing;
}
+ // In R8, StringSwitch instructions are introduced when entering the LIR phase. In D8, we don't
+ // use LIR, so we explicitly introduce StringSwitch instructions here.
+ if (!options.getTestingOptions().isSupportedLirPhase()) {
+ new StringSwitchConverter(appView)
+ .run(code, methodProcessor, methodProcessingContext, timing);
+ }
+
if (options.canHaveArtStringNewInitBug()) {
timing.begin("Check for new-init issue");
TrivialPhiSimplifier.ensureDirectStringNewToInit(appView, code);
@@ -773,11 +775,10 @@
.run(code, methodProcessor, methodProcessingContext, timing);
}
- if (code.getConversionOptions().isStringSwitchConversionEnabled()) {
- // Remove string switches prior to canonicalization to ensure that the constants that are
- // being introduced will be canonicalized if possible.
- stringSwitchRemover.run(code, methodProcessor, methodProcessingContext, timing);
- }
+ // Remove string switches prior to canonicalization to ensure that the constants that are
+ // being introduced will be canonicalized if possible.
+ new StringSwitchRemover(appView, identifierNameStringMarker)
+ .run(code, methodProcessor, methodProcessingContext, timing);
// TODO(mkroghj) Test if shorten live ranges is worth it.
if (options.isGeneratingDex()) {
@@ -967,9 +968,7 @@
IRCode code, OptimizationFeedback feedback, Timing timing) {
if (!code.getConversionOptions().isGeneratingLir()) {
new FilledNewArrayRewriter(appView).run(code, timing);
- }
- if (stringSwitchRemover != null) {
- stringSwitchRemover.run(code, timing);
+ new StringSwitchRemover(appView, identifierNameStringMarker).run(code, timing);
}
code.removeRedundantBlocks();
deadCodeRemover.run(code, timing);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 6da063b..25df747 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.conversion.passes.DexItemBasedConstStringRemover;
import com.android.tools.r8.ir.conversion.passes.FilledNewArrayRewriter;
import com.android.tools.r8.ir.conversion.passes.InitClassRemover;
+import com.android.tools.r8.ir.conversion.passes.StringSwitchConverter;
import com.android.tools.r8.ir.conversion.passes.StringSwitchRemover;
import com.android.tools.r8.ir.optimize.ConstantCanonicalizer;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
@@ -42,8 +43,9 @@
assert appView.testing().canUseLir(appView);
assert appView.testing().isPreLirPhase();
appView.testing().enterLirSupportedPhase();
- ConstResourceNumberRewriter constResourceNumberRewriter =
- new ConstResourceNumberRewriter(appView);
+ CodeRewriterPassCollection codeRewriterPassCollection =
+ new CodeRewriterPassCollection(
+ new ConstResourceNumberRewriter(appView), new StringSwitchConverter(appView));
// Convert code objects to LIR.
ThreadUtils.processItems(
appView.appInfo().classes(),
@@ -53,7 +55,7 @@
method -> {
assert !method.getDefinition().getCode().hasExplicitCodeLens();
IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
- constResourceNumberRewriter.run(code, Timing.empty());
+ codeRewriterPassCollection.run(code, null, null, Timing.empty());
LirCode<Integer> lirCode =
IR2LirConverter.translate(
code,
@@ -143,11 +145,12 @@
new CodeRewriterPassCollection(
new AdaptClassStringsRewriter(appView),
new ConstResourceNumberRemover(appView),
+ // Must run before DexItemBasedConstStringRemover.
+ new StringSwitchRemover(appView),
new DexItemBasedConstStringRemover(appView),
new InitClassRemover(appView),
new RecordInvokeDynamicInvokeCustomRewriter(appView),
- new FilledNewArrayRewriter(appView),
- new StringSwitchRemover(appView));
+ new FilledNewArrayRewriter(appView));
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz ->
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
index 24f7613..fd3c580 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
-import com.android.tools.r8.utils.InternalOptions;
public abstract class MethodConversionOptions {
@@ -16,7 +15,7 @@
return forD8(appView);
}
assert appView.testing().isPreLirPhase();
- return new MutableMethodConversionOptions(Target.CF, appView.options());
+ return new MutableMethodConversionOptions(Target.CF);
}
public static MutableMethodConversionOptions forPostLirPhase(AppView<?> appView) {
@@ -25,8 +24,7 @@
}
assert appView.testing().isPostLirPhase();
Target target = appView.options().isGeneratingClassFiles() ? Target.CF : Target.DEX;
- return new MutableMethodConversionOptions(target, appView.options())
- .disableStringSwitchConversion();
+ return new MutableMethodConversionOptions(target);
}
public static MutableMethodConversionOptions forLirPhase(AppView<?> appView) {
@@ -34,12 +32,12 @@
return forD8(appView);
}
assert appView.testing().isSupportedLirPhase();
- return new MutableMethodConversionOptions(determineTarget(appView), appView.options());
+ return new MutableMethodConversionOptions(determineTarget(appView));
}
public static MutableMethodConversionOptions forD8(AppView<?> appView) {
assert !appView.enableWholeProgramOptimizations();
- return new MutableMethodConversionOptions(determineTarget(appView), appView.options());
+ return new MutableMethodConversionOptions(determineTarget(appView));
}
public static MutableMethodConversionOptions nonConverting() {
@@ -82,24 +80,16 @@
public abstract boolean isPeepholeOptimizationsEnabled();
- public abstract boolean isStringSwitchConversionEnabled();
-
public abstract boolean shouldFinalizeAfterLensCodeRewriter();
public static class MutableMethodConversionOptions extends MethodConversionOptions {
- private Target target;
+ private final Target target;
private boolean enablePeepholeOptimizations = true;
- private boolean enableStringSwitchConversion;
private boolean finalizeAfterLensCodeRewriter;
- private MutableMethodConversionOptions(Target target, boolean enableStringSwitchConversion) {
+ private MutableMethodConversionOptions(Target target) {
this.target = target;
- this.enableStringSwitchConversion = enableStringSwitchConversion;
- }
-
- private MutableMethodConversionOptions(Target target, InternalOptions options) {
- this(target, options.isStringSwitchConversionEnabled());
}
public void disablePeepholeOptimizations(MethodProcessor methodProcessor) {
@@ -107,11 +97,6 @@
enablePeepholeOptimizations = false;
}
- public MutableMethodConversionOptions disableStringSwitchConversion() {
- enableStringSwitchConversion = false;
- return this;
- }
-
public MutableMethodConversionOptions setFinalizeAfterLensCodeRewriter() {
finalizeAfterLensCodeRewriter = true;
return this;
@@ -138,11 +123,6 @@
}
@Override
- public boolean isStringSwitchConversionEnabled() {
- return enableStringSwitchConversion;
- }
-
- @Override
public boolean shouldFinalizeAfterLensCodeRewriter() {
return finalizeAfterLensCodeRewriter;
}
@@ -151,7 +131,7 @@
public static class ThrowingMethodConversionOptions extends MutableMethodConversionOptions {
private ThrowingMethodConversionOptions() {
- super(null, true);
+ super(null);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index f22f61a..abe0324 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -72,6 +72,8 @@
void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts);
+ void setNopInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint);
+
void setSimpleInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint);
void classInitializerMayBePostponed(DexEncodedMethod method);
@@ -117,6 +119,8 @@
void unsetReturnValueOnlyDependsOnArguments(ProgramMethod method);
+ void unsetNopInliningConstraint(ProgramMethod method);
+
void unsetSimpleInliningConstraint(ProgramMethod method);
void unsetUnusedArguments(ProgramMethod method);
@@ -139,6 +143,7 @@
unsetNonNullParamOrThrow(method);
unsetReturnedArgument(method);
unsetReturnValueOnlyDependsOnArguments(method);
+ unsetNopInliningConstraint(method);
unsetSimpleInliningConstraint(method);
unsetUnusedArguments(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
index 98a92e8..87fc847 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
@@ -59,7 +59,7 @@
return noChange();
}
- private boolean verifyConsistentCode(IRCode code, boolean ssa, String preposition) {
+ protected boolean verifyConsistentCode(IRCode code, boolean ssa, String preposition) {
boolean result;
String message = "Invalid code " + preposition + " " + getRewriterId();
try {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ConstResourceNumberRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ConstResourceNumberRewriter.java
index e6806fc..7783c5c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ConstResourceNumberRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ConstResourceNumberRewriter.java
@@ -62,4 +62,10 @@
}
return CodeRewriterResult.hasChanged(hasChanged);
}
+
+ @Override
+ protected boolean verifyConsistentCode(IRCode code, boolean ssa, String preposition) {
+ // Skip verification since this runs prior to the removal of invalid code.
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchConverter.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
rename to src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchConverter.java
index 5d7faef..a49b93b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchConverter.java
@@ -1,12 +1,15 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2024, 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.conversion;
+package com.android.tools.r8.ir.conversion.passes;
import static com.android.tools.r8.ir.code.ConstNumber.asConstNumberOrNull;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -24,6 +27,8 @@
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StringSwitch;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -105,10 +110,28 @@
* }
* </pre>
*/
-public class StringSwitchConverter {
+public class StringSwitchConverter extends CodeRewriterPass<AppInfo> {
- public static boolean convertToStringSwitchInstructions(
- IRCode code, DexItemFactory dexItemFactory) {
+ public StringSwitchConverter(AppView<?> appView) {
+ super(appView);
+ }
+
+ @Override
+ protected String getRewriterId() {
+ return "StringSwitchConverter";
+ }
+
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ return options.shouldCompileMethodInReleaseMode(appView, code.context())
+ && options.enableStringSwitchConversion;
+ }
+
+ @Override
+ protected CodeRewriterResult rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
List<BasicBlock> rewritingCandidates = getRewritingCandidates(code, dexItemFactory);
if (rewritingCandidates != null) {
boolean changed = false;
@@ -121,9 +144,15 @@
code.removeAllDeadAndTrivialPhis();
code.removeUnreachableBlocks();
}
- return changed;
+ return CodeRewriterResult.hasChanged(changed);
}
- return false;
+ return CodeRewriterResult.NO_CHANGE;
+ }
+
+ @Override
+ protected boolean verifyConsistentCode(IRCode code, boolean ssa, String preposition) {
+ // Skip verification since this runs prior to the removal of invalid code.
+ return true;
}
private static List<BasicBlock> getRewritingCandidates(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchRemover.java
index f35f820..74fa808 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/StringSwitchRemover.java
@@ -84,7 +84,7 @@
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
StringSwitch theSwitch = block.exit().asStringSwitch();
- if (theSwitch != null) {
+ if (theSwitch != null && shouldBeRemoved(code, theSwitch)) {
try {
SingleStringSwitchRemover remover;
if (theSwitch.numberOfKeys() < appView.options().minimumStringSwitchSize
@@ -132,7 +132,7 @@
BasicBlock block = blockIterator.next();
for (BasicBlock predecessor : block.getNormalPredecessors()) {
StringSwitch exit = predecessor.exit().asStringSwitch();
- if (exit != null) {
+ if (exit != null && shouldBeRemoved(code, exit)) {
hasStringSwitch = true;
if (block == exit.fallthroughBlock()) {
// After the elimination of this string-switch instruction, there will be two
@@ -164,6 +164,15 @@
return hasStringSwitch;
}
+ private boolean shouldBeRemoved(IRCode code, StringSwitch theSwitch) {
+ // We only support retaining StringSwitch instructions in LIR. However, even when compiling to
+ // LIR, we (currently) need to remove StringSwitch instructions where the keys may be class
+ // names, so that these DexItemBasedConstStrings are correctly lens code rewritten.
+ // (Note this could be avoided by introducing a separate DexItemBasedStringSwitch instruction.)
+ return !code.getConversionOptions().isGeneratingLir()
+ || isClassNameValue(theSwitch.value(), dexItemFactory);
+ }
+
@Override
protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
return code.metadata().mayHaveStringSwitch();
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 92da768..a166867 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
@@ -221,18 +221,21 @@
int estimatedSizeForInlining =
code.getEstimatedSizeForInliningIfLessThanOrEquals(
instructionLimit + estimatedMaxIncrement);
- if (estimatedSizeForInlining < 0) {
- return false;
- }
- if (estimatedSizeForInlining <= instructionLimit) {
- return true;
- }
- int actualIncrement =
- getInliningInstructionLimitIncrement(invoke, target, inliningIRProvider);
- if (estimatedSizeForInlining <= instructionLimit + actualIncrement) {
- return true;
+ if (estimatedSizeForInlining >= 0) {
+ if (estimatedSizeForInlining <= instructionLimit) {
+ return true;
+ }
+ int actualIncrement =
+ getInliningInstructionLimitIncrement(invoke, target, inliningIRProvider);
+ if (estimatedSizeForInlining <= instructionLimit + actualIncrement) {
+ return true;
+ }
}
}
+ return satisfiesSimpleInliningConstraint(invoke, target);
+ }
+
+ private boolean satisfiesSimpleInliningConstraint(InvokeMethod invoke, ProgramMethod target) {
// Even if the inlinee is big it may become simple after inlining. We therefore check if the
// inlinee's simple inlining constraint is satisfied by the invoke.
SimpleInliningConstraint simpleInliningConstraint =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 6c10cc6..8b2f63d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -147,7 +147,7 @@
}
@Override
- public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) {
+ public SimpleInliningConstraint getNopInliningConstraint() {
return NeverSimpleInliningConstraint.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 9c7fb22..c23876d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -87,7 +87,7 @@
public abstract AbstractValue getAbstractReturnValue();
- public abstract SimpleInliningConstraint getNopInliningConstraint(InternalOptions options);
+ public abstract SimpleInliningConstraint getNopInliningConstraint();
public abstract SimpleInliningConstraint getSimpleInliningConstraint();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 382be09..76616ba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -57,7 +57,9 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.DeterminismAnalysis;
import com.android.tools.r8.ir.analysis.InitializedClassesOnNormalExitAnalysis;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintAnalysis;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintWithDepth;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis.ClassInitializerSideEffect;
import com.android.tools.r8.ir.analysis.type.DynamicType;
@@ -827,8 +829,14 @@
private void computeSimpleInliningConstraint(
ProgramMethod method, IRCode code, OptimizationFeedback feedback) {
- feedback.setSimpleInliningConstraint(
- method, new SimpleInliningConstraintAnalysis(appView, method).analyzeCode(code));
+ SimpleInliningConstraintWithDepth simpleInliningConstraintWithDepth =
+ new SimpleInliningConstraintAnalysis(appView, method).analyzeCode(code);
+ SimpleInliningConstraint nopInliningConstraint =
+ simpleInliningConstraintWithDepth.getNopConstraint();
+ SimpleInliningConstraint simpleInliningConstraint =
+ simpleInliningConstraintWithDepth.getConstraint();
+ feedback.setNopInliningConstraint(method, nopInliningConstraint);
+ feedback.setSimpleInliningConstraint(method, simpleInliningConstraint);
}
private void computeDynamicReturnType(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
index 06ef441..808f2c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
@@ -40,6 +40,13 @@
public abstract int fixupReturnedArgumentIndex(int returnedArgumentIndex);
+ public final SimpleInliningConstraint fixupNopInliningConstraint(
+ AppView<AppInfoWithLiveness> appView,
+ SimpleInliningConstraint constraint,
+ SimpleInliningConstraintFactory factory) {
+ return fixupSimpleInliningConstraint(appView, constraint, factory);
+ }
+
public abstract SimpleInliningConstraint fixupSimpleInliningConstraint(
AppView<AppInfoWithLiveness> appView,
SimpleInliningConstraint constraint,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
index 4831f7f..6c59889 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
@@ -201,7 +201,7 @@
}
@Override
- public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) {
+ public SimpleInliningConstraint getNopInliningConstraint() {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 3c46cf1..3c03079 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -78,6 +78,8 @@
private BitSet nonNullParamOnNormalExits =
DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS;
+ private SimpleInliningConstraint nopInliningConstraint =
+ NeverSimpleInliningConstraint.getInstance();
private SimpleInliningConstraint simpleInliningConstraint =
NeverSimpleInliningConstraint.getInstance();
@@ -144,6 +146,7 @@
abstractReturnValue = template.abstractReturnValue;
setDynamicType(template.dynamicType);
inlining = template.inlining;
+ nopInliningConstraint = template.nopInliningConstraint;
simpleInliningConstraint = template.simpleInliningConstraint;
bridgeInfo = template.bridgeInfo;
instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
@@ -169,6 +172,7 @@
.fixupNonNullParamOrThrow(fixer)
.fixupReturnedArgumentIndex(fixer)
.fixupParametersWithBitwiseOperations(fixer)
+ .fixupNopInliningConstraint(appView, fixer)
.fixupSimpleInliningConstraint(appView, fixer)
.fixupUnusedArguments(fixer);
}
@@ -486,12 +490,8 @@
}
@Override
- public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) {
- // We currently require that having a simple inlining constraint implies that the method becomes
- // empty after inlining. Therefore, an invoke is a nop if the simple inlining constraint is
- // satisfied (if the invoke does not trigger other side effects, such as class initialization).
- assert options.simpleInliningConstraintThreshold == 0;
- return getSimpleInliningConstraint();
+ public SimpleInliningConstraint getNopInliningConstraint() {
+ return nopInliningConstraint;
}
@Override
@@ -595,7 +595,7 @@
if (!mayHaveSideEffects()) {
return false;
}
- if (getNopInliningConstraint(options).isSatisfied(invoke)) {
+ if (getNopInliningConstraint().isSatisfied(invoke)) {
return false;
}
return true;
@@ -606,6 +606,22 @@
return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
+ void setNopInliningConstraint(SimpleInliningConstraint constraint) {
+ this.nopInliningConstraint = constraint;
+ }
+
+ void unsetNopInliningConstraint() {
+ nopInliningConstraint = NeverSimpleInliningConstraint.getInstance();
+ }
+
+ public MutableMethodOptimizationInfo fixupNopInliningConstraint(
+ AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
+ nopInliningConstraint =
+ fixer.fixupNopInliningConstraint(
+ appView, nopInliningConstraint, appView.simpleInliningConstraintFactory());
+ return this;
+ }
+
void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
this.simpleInliningConstraint = constraint;
}
@@ -811,6 +827,7 @@
&& instanceInitializerInfoCollection.isEmpty()
&& nonNullParamOrThrow == top.getNonNullParamOrThrow()
&& nonNullParamOnNormalExits == top.getNonNullParamOnNormalExits()
+ && nopInliningConstraint == top.getNopInliningConstraint()
&& simpleInliningConstraint == top.getSimpleInliningConstraint()
&& maxRemovedAndroidLogLevel == top.getMaxRemovedAndroidLogLevel()
&& parametersWithBitwiseOperations == top.getParametersWithBitwiseOperations()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 413d343..54a0fc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -271,6 +271,11 @@
}
@Override
+ public void setNopInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint) {
+ getMethodOptimizationInfoForUpdating(method).setNopInliningConstraint(constraint);
+ }
+
+ @Override
public synchronized void setSimpleInliningConstraint(
ProgramMethod method, SimpleInliningConstraint constraint) {
getMethodOptimizationInfoForUpdating(method).setSimpleInliningConstraint(constraint);
@@ -381,6 +386,11 @@
}
@Override
+ public void unsetNopInliningConstraint(ProgramMethod method) {
+ getMethodOptimizationInfoForUpdating(method).unsetNopInliningConstraint();
+ }
+
+ @Override
public synchronized void unsetSimpleInliningConstraint(ProgramMethod method) {
getMethodOptimizationInfoForUpdating(method).unsetSimpleInliningConstraint();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 61fe21d..3c32b83 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -122,6 +122,9 @@
public void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts) {}
@Override
+ public void setNopInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint) {}
+
+ @Override
public void setSimpleInliningConstraint(
ProgramMethod method, SimpleInliningConstraint constraint) {}
@@ -189,6 +192,9 @@
public void unsetReturnValueOnlyDependsOnArguments(ProgramMethod method) {}
@Override
+ public void unsetNopInliningConstraint(ProgramMethod method) {}
+
+ @Override
public void unsetSimpleInliningConstraint(ProgramMethod method) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 9edabc2..c015121 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -204,6 +204,11 @@
}
@Override
+ public void setNopInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint) {
+ method.getDefinition().getMutableOptimizationInfo().setNopInliningConstraint(constraint);
+ }
+
+ @Override
public void setSimpleInliningConstraint(
ProgramMethod method, SimpleInliningConstraint constraint) {
method.getDefinition().getMutableOptimizationInfo().setSimpleInliningConstraint(constraint);
@@ -339,6 +344,12 @@
}
@Override
+ public void unsetNopInliningConstraint(ProgramMethod method) {
+ withMutableMethodOptimizationInfo(
+ method, MutableMethodOptimizationInfo::unsetNopInliningConstraint);
+ }
+
+ @Override
public void unsetSimpleInliningConstraint(ProgramMethod method) {
withMutableMethodOptimizationInfo(
method, MutableMethodOptimizationInfo::unsetSimpleInliningConstraint);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationInfoRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationInfoRemover.java
index 1ad49fd..db23102 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationInfoRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationInfoRemover.java
@@ -59,6 +59,7 @@
optimizationInfo.unsetDynamicType();
optimizationInfo.unsetInitializedClassesOnNormalExit();
optimizationInfo.unsetInstanceInitializerInfoCollection();
+ optimizationInfo.unsetNopInliningConstraint();
optimizationInfo.unsetSimpleInliningConstraint();
if (optimizationInfo.isEffectivelyDefault()) {
method.unsetOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index e8deff4..42c94e5 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -100,7 +100,6 @@
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.StringSwitchConverter;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
@@ -146,13 +145,6 @@
IRCode irCode = parser.getIRCode(method, conversionOptions);
// Some instructions have bottom types (e.g., phis). Compute their actual types by widening.
new TypeAnalysis(appView, irCode).widening();
-
- if (conversionOptions.isStringSwitchConversionEnabled()) {
- if (StringSwitchConverter.convertToStringSwitchInstructions(
- irCode, appView.dexItemFactory())) {
- irCode.removeRedundantBlocks();
- }
- }
return irCode;
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
index b2331cc..de48365 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -366,11 +366,7 @@
@SuppressWarnings("unchecked")
private LirCode<EV> removeUnreachableBlocks(LirCode<EV> rewritten) {
- IRCode code =
- rewritten.buildIR(
- context,
- appView,
- MethodConversionOptions.forLirPhase(appView).disableStringSwitchConversion());
+ IRCode code = rewritten.buildIR(context, appView, MethodConversionOptions.forLirPhase(appView));
AffectedValues affectedValues = code.removeUnreachableBlocks();
affectedValues.narrowingWithAssumeRemoval(appView, code);
new DeadCodeRemover(appView).run(code, Timing.empty());
@@ -386,7 +382,6 @@
context.buildIR(
appView,
MethodConversionOptions.forLirPhase(appView)
- .disableStringSwitchConversion()
.setFinalizeAfterLensCodeRewriter());
// MethodProcessor argument is only used by unboxing lenses.
MethodProcessor methodProcessor = null;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 8df07a0..aa82269 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -538,12 +538,17 @@
ProgramMethod context,
ConcretePolymorphicMethodStateOrBottom existingMethodState) {
DynamicTypeWithUpperBound dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
+ // TODO(b/331587404): Investigate if we can replace the receiver by null before entering this
+ // pass, so that this special case is not needed.
if (dynamicReceiverType.isNullType()) {
+ assert appView.testing().allowNullDynamicTypeInCodeScanner : "b/250634405";
// This can happen if we were unable to determine that the receiver is a phi value where null
- // information has not been propagated down. See if we can improve the test here or ensure
- // that all phi's are normalized before computing the optimization info.
- assert appView.checkForTesting(() -> false) : "b/250634405";
- return MethodState.unknown();
+ // information has not been propagated down. Ideally this case would never happen as it should
+ // be possible to replace the receiver by the null constant in this case.
+ //
+ // Since the receiver is known to be null, no argument information should be propagated to the
+ // callees, so we return bottom here.
+ return MethodState.bottom();
}
ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
index 856ff9d..758bd34 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
@@ -68,8 +68,7 @@
(ignore, existingMethodState) -> {
if (existingMethodState == null) {
MethodState newMethodState = methodStateSupplier.apply(MethodState.bottom());
- assert !newMethodState.isBottom();
- return newMethodState;
+ return newMethodState.isBottom() ? null : newMethodState;
}
assert !existingMethodState.isBottom();
timing.begin("Join temporary method state");
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
index 126960b..10731fb 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
@@ -116,10 +116,7 @@
.setApiLevelForCode(
appView.apiLevelCompute().computeInitialMinApiLevel(appView.options()));
}
- IRCode code =
- method.buildIR(
- appView,
- MethodConversionOptions.forLirPhase(appView).disableStringSwitchConversion());
+ IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
inliner.performInlining(
method, code, getSimpleFeedback(), methodProcessor, Timing.empty());
CodeRewriter.removeAssumeInstructions(appView, code);
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
index ba3abda..192178b 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
@@ -176,8 +176,7 @@
private void instrumentMethod(ProgramMethod method, boolean skipMethodLogging) {
// Disable StringSwitch conversion to avoid having to run the StringSwitchRemover before
// finalizing the code.
- MutableMethodConversionOptions conversionOptions =
- MethodConversionOptions.forD8(appView).disableStringSwitchConversion();
+ MutableMethodConversionOptions conversionOptions = MethodConversionOptions.forD8(appView);
IRCode code = method.buildIR(appView, conversionOptions);
InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 759ebd7..1eacba7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -266,7 +266,6 @@
mode.isInitialTreeShaking()
? MethodConversionOptions.forPreLirPhase(appView)
: MethodConversionOptions.forLirPhase(appView);
- conversionOptions.disableStringSwitchConversion();
IRCode ir = method.buildIR(appView, conversionOptions);
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 28f377b..be887b7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -408,7 +408,7 @@
public boolean enableDevirtualization = true;
public boolean enableEnumUnboxing = true;
public boolean enableSimpleInliningConstraints = true;
- public final int simpleInliningConstraintThreshold = 0;
+ public final int simpleInliningConstraintThreshold = 1;
public boolean enableClassInlining = true;
public boolean enableClassStaticizer = true;
public boolean enableInitializedClassesAnalysis = true;
@@ -2333,6 +2333,7 @@
public boolean allowClassInliningOfSynthetics = true;
public boolean allowInjectedAnnotationMethods = false;
public boolean allowInliningOfSynthetics = true;
+ public boolean allowNullDynamicTypeInCodeScanner = true;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/DexItemBasedStringSwitchTest.java b/src/test/java/com/android/tools/r8/ir/conversion/DexItemBasedStringSwitchTest.java
new file mode 100644
index 0000000..6b92f2a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/conversion/DexItemBasedStringSwitchTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2024, 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.conversion;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DexItemBasedStringSwitchTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-repackageclasses")
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Object o = System.currentTimeMillis() > 0 ? new A() : new B();
+ switch (o.getClass().getName()) {
+ case "com.android.tools.r8.ir.conversion.DexItemBasedStringSwitchTest$A":
+ System.out.println("A");
+ return;
+ case "com.android.tools.r8.ir.conversion.DexItemBasedStringSwitchTest$B":
+ System.out.println("B");
+ break;
+ default:
+ System.out.println("Neither");
+ break;
+ }
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {}
+
+ @NoHorizontalClassMerging
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningTest.java
index 44eb259..3d8006e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningTest.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
@@ -46,8 +46,9 @@
MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
assertThat(mainMethodSubject, isPresent());
- // TODO(b/331337747): Should be true.
- assertFalse(
+ // TODO(b/331337747): Account for constant canonicalization in constraint analysis.
+ assertEquals(
+ parameters.isCfRuntime(),
mainMethodSubject
.streamInstructions()
.filter(InstructionSubject::isConstString)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/ConvertRemovedStringSwitchTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/ConvertRemovedStringSwitchTest.java
index 1239133..fc490c64 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/ConvertRemovedStringSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/ConvertRemovedStringSwitchTest.java
@@ -13,8 +13,14 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.conversion.passes.StringSwitchConverter;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -52,7 +58,7 @@
.assertSuccessWithOutputLines("A", "B", "C", "D", "E!");
}
- private void inspect(CodeInspector inspector) {
+ private void inspect(CodeInspector inspector) throws Exception {
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
@@ -69,7 +75,12 @@
assertEquals(1, stringCounts.getInt("E!"));
// Verify that we can rebuild the StringSwitch instruction.
- IRCode code = mainMethodSubject.buildIR();
+ AppView<?> appView =
+ computeAppView(
+ AndroidApp.builder().build(),
+ new InternalOptions(inspector.getFactory(), new Reporter()));
+ IRCode code = mainMethodSubject.buildIR(appView);
+ new StringSwitchConverter(appView).run(code, Timing.empty());
assertTrue(code.streamInstructions().anyMatch(Instruction::isStringSwitch));
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 867f9c9..181c0c5 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -151,11 +151,6 @@
}
@Override
- public boolean isStringSwitchConversionEnabled() {
- return false;
- }
-
- @Override
public boolean shouldFinalizeAfterLensCodeRewriter() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 0556718..d4e6cfd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -81,8 +81,7 @@
inspector ->
inspector
.applyIf(
- kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_8_0)
- || splitGroup,
+ splitGroup,
i -> {
if (kotlinParameters.getLambdaGeneration().isClass()) {
i.assertIsCompleteMergeGroup(
@@ -123,12 +122,7 @@
ClassSubject mergeTarget =
codeInspector.clazz(
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1");
- assertThat(
- mergeTarget,
- isPresentIf(
- kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_8_0)
- || splitGroup));
-
+ assertThat(mergeTarget, isPresentIf(splitGroup));
if (mergeTarget.isAbsent()) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java
index 776cf11..45f163e 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java
@@ -23,11 +23,11 @@
* This is an attempt on a regression test for b/250634405. What happens in the input program is
* that we determine a phi value to be always null in Phi.getDynamicUpperBoundType. That information
* has not been propagated to the receiver during optimizing of the IR. Therefor the check at {@link
- * ArgumentPropagatorCodeScanner.scan} for receiver always being null returns false.
+ * ArgumentPropagatorCodeScanner#scan} for receiver always being null returns false.
*
* <p>Getting the exact IR to match was difficult so this test short circuit this by disabling IR
* processing of a simple method (by specifying pass-through) and disabling the check in {@link
- * ArgumentPropagatorCodeScanner.scan}.
+ * ArgumentPropagatorCodeScanner#scan}.
*/
@RunWith(Parameterized.class)
public class PolymorphicMethodWithNullReceiverBoundTest extends TestBase {
@@ -44,24 +44,23 @@
public void testR8WithTestAssertionsEnabled() {
assertThrows(
CompilationFailedException.class,
- () -> {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(Main.class)
- .enableNoHorizontalClassMergingAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .setMinApi(parameters)
- .addOptionsModification(
- options -> {
- options.testing.cfByteCodePassThrough =
- method -> method.getName().startsWith("main");
- options.testing.checkReceiverAlwaysNullInCallSiteOptimization = false;
- })
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(containsString("b/250634405"));
- });
- });
+ () ->
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters)
+ .addOptionsModification(
+ options -> {
+ options.testing.allowNullDynamicTypeInCodeScanner = false;
+ options.testing.cfByteCodePassThrough =
+ method -> method.getName().startsWith("main");
+ options.testing.checkReceiverAlwaysNullInCallSiteOptimization = false;
+ })
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorMessageThatMatches(containsString("b/250634405"))));
}
@Test
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index a0dbfa4..5512419 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -793,9 +793,14 @@
}
protected static AppView<AppInfo> computeAppView(AndroidApp app) throws Exception {
+ return computeAppView(app, new InternalOptions());
+ }
+
+ protected static AppView<AppInfo> computeAppView(AndroidApp app, InternalOptions options)
+ throws Exception {
AppInfo appInfo =
AppInfo.createInitialAppInfo(
- readApplicationForDexOutput(app, new InternalOptions()),
+ readApplicationForDexOutput(app, options),
GlobalSyntheticsStrategy.forNonSynthesizing());
return AppView.createForD8(appInfo);
}
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 7e0c692..6800813 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -15,6 +15,7 @@
GOLEM_BUILD_TARGETS_TESTS = [
utils.GRADLE_TASK_ALL_TESTS_WITH_APPLY_MAPPING_JAR,
+ utils.GRADLE_TASK_TESTBASE_WITH_APPLY_MAPPING_JAR,
utils.GRADLE_TASK_TEST_DEPS_JAR
]
GOLEM_BUILD_TARGETS = [utils.GRADLE_TASK_R8LIB] + GOLEM_BUILD_TARGETS_TESTS
@@ -95,14 +96,15 @@
]
buildTargets = [utils.GRADLE_TASK_R8] + testBuildTargets
r8jar = utils.R8_JAR
- testjars = [utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR]
+ testjars = [utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR, utils.R8_TESTBASE_JAR]
else:
testBuildTargets = GOLEM_BUILD_TARGETS_TESTS
buildTargets = GOLEM_BUILD_TARGETS
r8jar = utils.R8LIB_JAR
testjars = [
os.path.join(utils.R8LIB_TESTS_JAR),
- os.path.join(utils.R8LIB_TESTS_DEPS_JAR)
+ os.path.join(utils.R8LIB_TESTS_DEPS_JAR),
+ os.path.join(utils.R8LIB_TESTBASE_JAR)
]
if options.version:
diff --git a/tools/utils.py b/tools/utils.py
index b250d53..6faecc8 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -53,6 +53,7 @@
GRADLE_TASK_SWISS_ARMY_KNIFE = ':main:swissArmyKnife'
GRADLE_TASK_TEST = ':test:test'
GRADLE_TASK_ALL_TESTS_WITH_APPLY_MAPPING_JAR = ':test:rewriteTestsForR8LibWithRelocatedDeps'
+GRADLE_TASK_TESTBASE_WITH_APPLY_MAPPING_JAR = ':test:rewriteTestBaseForR8LibWithRelocatedDeps'
GRADLE_TASK_TEST_DEPS_JAR = ':test:packageTestDeps'
GRADLE_TASK_TEST_JAR = ':test:relocateTestsForR8LibWithRelocatedDeps'
@@ -69,6 +70,8 @@
THREADING_MODULE_BLOCKING_JAR = os.path.join(LIBS, 'threading-module-blocking.jar')
THREADING_MODULE_SINGLE_THREADED_JAR = os.path.join(LIBS, 'threading-module-single-threaded.jar')
R8_TESTS_JAR = os.path.join(LIBS, 'r8tests.jar')
+R8_TESTBASE_JAR = os.path.join(LIBS, 'r8test_base.jar')
+R8LIB_TESTBASE_JAR = os.path.join(LIBS, 'r8libtestbase-cf.jar')
R8LIB_TESTS_JAR = os.path.join(LIBS, 'r8libtestdeps-cf.jar')
R8_TESTS_DEPS_JAR = os.path.join(LIBS, 'test_deps_all.jar')
R8LIB_TESTS_DEPS_JAR = R8_TESTS_DEPS_JAR