[LIR] Use LIR in R8 conversion and inlining
Bug: b/225838009
Change-Id: I02f5df60c983fed94122939721d59d90917eace6
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index a76bdf5..3332852 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.PositionBuilder;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -148,6 +149,14 @@
public abstract int estimatedDexCodeSizeUpperBoundInBytes();
+ public final boolean isLirCode() {
+ return asLirCode() != null;
+ }
+
+ public LirCode<Integer> asLirCode() {
+ return null;
+ }
+
public CfCode asCfCode() {
throw new Unreachable(getClass().getCanonicalName() + ".asCfCode()");
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index d871616..ca741c9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -36,8 +36,14 @@
this.fieldBitAccessAnalysis =
options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
this.fieldAssignmentTracker = new FieldAssignmentTracker(appView);
- this.fieldReadForInvokeReceiverAnalysis = new FieldReadForInvokeReceiverAnalysis(appView);
- this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
+ if (options.testing.canUseLir(appView)) {
+ // When using LIR the bytecode metadata is computed later during finalization via IR.
+ this.fieldReadForInvokeReceiverAnalysis = null;
+ this.fieldReadForWriteAnalysis = null;
+ } else {
+ this.fieldReadForInvokeReceiverAnalysis = new FieldReadForInvokeReceiverAnalysis(appView);
+ this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
+ }
}
@VisibleForTesting
@@ -111,4 +117,32 @@
}
}
}
+
+ public static BytecodeMetadataProvider computeBytecodeMetadata(
+ IRCode irCode, AppView<AppInfoWithLiveness> appView) {
+ // This rebuilding of metadata should only be used in the LIR pipeline where the info is
+ // discarded when translating from IR to LIR.
+ assert appView.options().testing.canUseLir(appView);
+ BytecodeMetadataProvider bytecodeMetadataProvider = BytecodeMetadataProvider.empty();
+ if (irCode.metadata().mayHaveFieldInstruction()) {
+ BytecodeMetadataProvider.Builder builder = BytecodeMetadataProvider.builder();
+ FieldReadForInvokeReceiverAnalysis fieldReadForInvokeReceiverAnalysis =
+ new FieldReadForInvokeReceiverAnalysis(appView);
+ FieldReadForWriteAnalysis fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
+ for (Instruction instruction : irCode.instructions()) {
+ if (instruction.isFieldInstruction()) {
+ FieldInstruction fieldInstruction = instruction.asFieldInstruction();
+ ProgramField field =
+ appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
+ if (field != null) {
+ fieldReadForInvokeReceiverAnalysis.recordFieldAccess(
+ fieldInstruction, field, builder, irCode.context());
+ fieldReadForWriteAnalysis.recordFieldAccess(fieldInstruction, field, builder);
+ }
+ }
+ }
+ bytecodeMetadataProvider = builder.build();
+ }
+ return bytecodeMetadataProvider;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java
index 976020c6..6289b5d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java
@@ -23,7 +23,7 @@
private final AppView<AppInfoWithLiveness> appView;
- FieldReadForInvokeReceiverAnalysis(AppView<AppInfoWithLiveness> appView) {
+ public FieldReadForInvokeReceiverAnalysis(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
index 6a5c420..2155676 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
@@ -21,7 +21,7 @@
private final AppView<AppInfoWithLiveness> appView;
- FieldReadForWriteAnalysis(AppView<AppInfoWithLiveness> appView) {
+ public FieldReadForWriteAnalysis(AppView<AppInfoWithLiveness> appView) {
this.appView = 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 a7d54cab..e943123 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
@@ -616,12 +616,14 @@
assertionsRewriter.run(method, code, deadCodeRemover, timing);
CheckNotNullConverter.runIfNecessary(appView, code);
+ previous = printMethod(code, "IR after disable assertions (SSA)", previous);
if (serviceLoaderRewriter != null) {
assert appView.appInfo().hasLiveness();
timing.begin("Rewrite service loaders");
serviceLoaderRewriter.rewrite(code, methodProcessor, methodProcessingContext);
timing.end();
+ previous = printMethod(code, "IR after service rewriting (SSA)", previous);
}
if (identifierNameStringMarker != null) {
@@ -629,12 +631,14 @@
identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(code);
timing.end();
assert code.isConsistentSSA(appView);
+ previous = printMethod(code, "IR after identifier-name strings (SSA)", previous);
}
if (memberValuePropagation != null) {
timing.begin("Propagate member values");
memberValuePropagation.run(code);
timing.end();
+ previous = printMethod(code, "IR after member-value propagation (SSA)", previous);
}
if (enumValueOptimizer != null) {
@@ -642,16 +646,16 @@
timing.begin("Remove switch maps");
enumValueOptimizer.removeSwitchMaps(code);
timing.end();
+ previous = printMethod(code, "IR after enum-value optimization (SSA)", previous);
}
if (instanceInitializerOutliner != null) {
instanceInitializerOutliner.rewriteInstanceInitializers(
code, context, methodProcessor, methodProcessingContext);
assert code.verifyTypes(appView);
+ previous = printMethod(code, "IR after instance initializer outlining (SSA)", previous);
}
- previous = printMethod(code, "IR after disable assertions (SSA)", previous);
-
// Update the IR code if collected call site optimization info has something useful.
// While aggregation of parameter information at call sites would be more precise than static
// types, those could be still less precise at one single call site, where specific arguments
@@ -1061,7 +1065,11 @@
if (options.testing.roundtripThroughLir) {
code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing);
}
- if (options.isGeneratingClassFiles()) {
+ if (options.testing.canUseLir(appView)) {
+ timing.begin("IR->LIR");
+ finalizeToLir(code, feedback, bytecodeMetadataProvider, timing);
+ timing.end();
+ } else if (options.isGeneratingClassFiles()) {
timing.begin("IR->CF");
finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
timing.end();
@@ -1101,7 +1109,7 @@
IRCode code, S strategy, String name, Timing timing) {
timing.begin("IR->LIR (" + name + ")");
LirCode<EV> lirCode =
- IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
+ IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.options());
timing.end();
// Check that printing does not fail.
String lirString = lirCode.toString();
@@ -1109,11 +1117,26 @@
timing.begin("LIR->IR (" + name + ")");
IRCode irCode =
Lir2IRConverter.translate(
- code.context(), lirCode, strategy.getDecodingStrategy(lirCode), appView);
+ code.context(), lirCode, strategy.getDecodingStrategy(lirCode, null), appView);
timing.end();
return irCode;
}
+ private void finalizeToLir(
+ IRCode code,
+ OptimizationFeedback feedback,
+ BytecodeMetadataProvider bytecodeMetadataProvider,
+ Timing timing) {
+ assert deadCodeRemover.verifyNoDeadCode(code);
+ assert BytecodeMetadataProvider.empty() == bytecodeMetadataProvider;
+ LirCode<Integer> lirCode =
+ IR2LirConverter.translate(
+ code, LirStrategy.getDefaultStrategy().getEncodingStrategy(), appView.options());
+ ProgramMethod method = code.context();
+ method.setCode(lirCode, appView);
+ markProcessed(code, feedback);
+ }
+
private void finalizeToCf(
IRCode code,
OptimizationFeedback feedback,
@@ -1251,4 +1274,32 @@
inliner.onMethodCodePruned(method);
}
}
+
+ public void finalizeLirMethodToOutputFormat(ProgramMethod method) {
+ Code code = method.getDefinition().getCode();
+ if (!(code instanceof LirCode)) {
+ return;
+ }
+ Timing onThreadTiming = Timing.empty();
+ IRCode irCode = method.buildIR(appView);
+ BytecodeMetadataProvider bytecodeMetadataProvider =
+ FieldAccessAnalysis.computeBytecodeMetadata(irCode, appView.withLiveness());
+ // During processing optimization info may cause previously live code to become dead.
+ // E.g., we may now have knowledge that an invoke does not have side effects.
+ // Thus, we re-run the dead-code remover now as it is assumed complete by CF/DEX finalization.
+ deadCodeRemover.run(irCode, onThreadTiming);
+ if (options.isGeneratingClassFiles()) {
+ method.setCode(
+ new IRToCfFinalizer(appView, deadCodeRemover)
+ .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
+ appView);
+ } else {
+ assert options.isGeneratingDex();
+ method.setCode(
+ new IRToDexFinalizer(appView, deadCodeRemover)
+ .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
+ appView);
+ updateHighestSortingStrings(method.getDefinition());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index f8946d4..1187f29 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.io.IOException;
@@ -100,6 +101,7 @@
lastWaveDone(postMethodProcessorBuilder, executorService);
eventConsumer.finished(appView);
assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+ finalizeLirToOutputFormat(timing, executorService);
timing.end();
}
@@ -176,6 +178,7 @@
eventConsumer.finished(appView);
assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
}
+ finalizeLirToOutputFormat(timing, executorService);
timing.end();
}
@@ -207,9 +210,28 @@
// Assure that no more optimization feedback left after post processing.
assert feedback.noUpdatesLeft();
+ finalizeLirToOutputFormat(timing, executorService);
return builder.build();
}
+ private void finalizeLirToOutputFormat(Timing timing, ExecutorService executorService)
+ throws ExecutionException {
+ if (!options.testing.canUseLir(appView)) {
+ return;
+ }
+ String output = options.isGeneratingClassFiles() ? "CF" : "DEX";
+ timing.begin("LIR->IR->" + output);
+ ThreadUtils.processItems(
+ appView.appInfo().classes(),
+ clazz -> clazz.forEachProgramMethod(this::finalizeLirMethodToOutputFormat),
+ executorService);
+ appView
+ .getSyntheticItems()
+ .getPendingSyntheticClasses()
+ .forEach(clazz -> clazz.forEachProgramMethod(this::finalizeLirMethodToOutputFormat));
+ timing.end();
+ }
+
private void clearDexMethodCompilationState() {
appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 1653ae2..6646ce0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -85,6 +85,7 @@
assert done;
irConverter.removeDeadCodeAndFinalizeIR(
irCode, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
+ irConverter.finalizeLirMethodToOutputFormat(programMethod);
}
public void rewriteRecordFieldArray(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
index 12028fb..cf4a6fe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
@@ -136,7 +136,7 @@
break;
case RETURN:
- // Wil not materialize after class inlining.
+ // Will not materialize after class inlining.
if (appView.options().isGeneratingClassFiles()) {
result++;
} else {
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
index ee53ceb..3bc2fe6 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.CatchHandlers;
@@ -12,6 +11,7 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -24,24 +24,22 @@
public class IR2LirConverter<EV> {
- private final DexItemFactory factory;
private final IRCode irCode;
private final LirEncodingStrategy<Value, EV> strategy;
private final LirBuilder<Value, EV> builder;
private IR2LirConverter(
- DexItemFactory factory, IRCode irCode, LirEncodingStrategy<Value, EV> strategy) {
- this.factory = factory;
+ InternalOptions options, IRCode irCode, LirEncodingStrategy<Value, EV> strategy) {
this.irCode = irCode;
this.strategy = strategy;
this.builder =
- new LirBuilder<>(irCode.context().getReference(), strategy, factory)
+ new LirBuilder<>(irCode.context().getReference(), strategy, options)
.setMetadata(irCode.metadata());
}
public static <EV> LirCode<EV> translate(
- IRCode irCode, LirEncodingStrategy<Value, EV> strategy, DexItemFactory factory) {
- return new IR2LirConverter<>(factory, irCode, strategy).internalTranslate();
+ IRCode irCode, LirEncodingStrategy<Value, EV> strategy, InternalOptions options) {
+ return new IR2LirConverter<>(options, irCode, strategy).internalTranslate();
}
private void recordBlock(BasicBlock block, int blockIndex) {
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 8777089..4315a85 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -15,6 +15,11 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -25,6 +30,7 @@
import com.android.tools.r8.ir.code.ArrayLength;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CanonicalPositions;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.Cmp;
@@ -116,7 +122,36 @@
LirCode<EV> lirCode,
LirDecodingStrategy<Value, EV> strategy,
AppView<?> appView) {
- Parser<EV> parser = new Parser<>(lirCode, method.getReference(), appView, strategy);
+ return translate(
+ method,
+ lirCode,
+ strategy,
+ appView,
+ new NumberGenerator(),
+ null,
+ RewrittenPrototypeDescription.none(),
+ appView.graphLens().getOriginalMethodSignature(method.getReference()));
+ }
+
+ public static <EV> IRCode translate(
+ ProgramMethod method,
+ LirCode<EV> lirCode,
+ LirDecodingStrategy<Value, EV> strategy,
+ AppView<?> appView,
+ NumberGenerator valueNumberGenerator,
+ Position callerPosition,
+ RewrittenPrototypeDescription protoChanges,
+ DexMethod originalMethod) {
+ Parser<EV> parser =
+ new Parser<>(
+ lirCode,
+ originalMethod,
+ method.getDefinition().isD8R8Synthesized(),
+ appView,
+ strategy,
+ valueNumberGenerator,
+ callerPosition,
+ protoChanges);
parser.parseArguments(method);
parser.ensureDebugInfo();
lirCode.forEach(view -> view.accept(parser));
@@ -134,8 +169,9 @@
private final AppView<?> appView;
private final LirCode<EV> code;
private final LirDecodingStrategy<Value, EV> strategy;
- private final NumberGenerator valueNumberGenerator = new NumberGenerator();
+ private final NumberGenerator valueNumberGenerator;
private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator();
+ private final RewrittenPrototypeDescription protoChanges;
private final Int2ReferenceMap<BasicBlock> blocks = new Int2ReferenceOpenHashMap<>();
@@ -145,18 +181,62 @@
private Position currentPosition;
private PositionEntry nextPositionEntry = null;
private int nextIndexInPositionsTable = 0;
+ private final PositionEntry[] positionTable;
+
+ private final boolean buildForInlining;
public Parser(
LirCode<EV> code,
DexMethod method,
+ boolean isD8R8Synthesized,
AppView<?> appView,
- LirDecodingStrategy<Value, EV> strategy) {
+ LirDecodingStrategy<Value, EV> strategy,
+ NumberGenerator valueNumberGenerator,
+ Position callerPosition,
+ RewrittenPrototypeDescription protoChanges) {
super(code);
this.appView = appView;
this.code = code;
this.strategy = strategy;
- // Recreate the preamble position. This is active for arguments and code with no positions.
- currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ this.valueNumberGenerator = valueNumberGenerator;
+ this.protoChanges = protoChanges;
+ assert protoChanges != null;
+ if (callerPosition == null) {
+ buildForInlining = false;
+ positionTable = code.getPositionTable();
+ // Recreate the preamble position. This is active for arguments and code with no positions.
+ currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ } else {
+ buildForInlining = true;
+ PositionEntry[] inlineePositions = code.getPositionTable();
+ Position inlineePreamble = null;
+ if (inlineePositions.length > 0 && inlineePositions[0].fromInstructionIndex == 0) {
+ inlineePreamble = inlineePositions[0].position;
+ }
+ CanonicalPositions canonicalPositions =
+ new CanonicalPositions(
+ callerPosition,
+ inlineePositions.length,
+ method,
+ isD8R8Synthesized,
+ inlineePreamble);
+ currentPosition = canonicalPositions.getPreamblePosition();
+ positionTable = new PositionEntry[inlineePositions.length];
+ for (int i = 0; i < inlineePositions.length; i++) {
+ PositionEntry inlineeEntry = inlineePositions[i];
+ Position inlineePosition = inlineeEntry.position;
+ positionTable[i] =
+ new PositionEntry(
+ inlineeEntry.fromInstructionIndex,
+ canonicalPositions.getCanonical(
+ inlineePosition
+ .builderWithCopy()
+ .setCallerPosition(
+ canonicalPositions.canonicalizeCallerPosition(
+ inlineePosition.getCallerPosition()))
+ .build()));
+ }
+ }
}
@Override
@@ -197,23 +277,47 @@
private void advanceNextPositionEntry() {
nextPositionEntry =
- nextIndexInPositionsTable < code.getPositionTable().length
- ? code.getPositionTable()[nextIndexInPositionsTable++]
+ nextIndexInPositionsTable < positionTable.length
+ ? positionTable[nextIndexInPositionsTable++]
: null;
}
public void parseArguments(ProgramMethod method) {
+ ArgumentInfoCollection argumentsInfo = protoChanges.getArgumentInfoCollection();
currentBlock = getBasicBlock(ENTRY_BLOCK_INDEX);
boolean hasReceiverArgument = !method.getDefinition().isStatic();
- assert code.getArgumentCount()
- == method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
+
+ int index = 0;
if (hasReceiverArgument) {
+ assert argumentsInfo.getNewArgumentIndex(0) == 0;
addThisArgument(method.getHolderType());
+ index++;
}
- int index = hasReceiverArgument ? 1 : 0;
- for (DexType parameter : method.getParameters()) {
- addArgument(parameter, index++);
+
+ int originalNumberOfArguments =
+ method.getParameters().size()
+ + argumentsInfo.numberOfRemovedArguments()
+ + method.getDefinition().getFirstNonReceiverArgumentIndex()
+ - protoChanges.numberOfExtraParameters();
+
+ int numberOfRemovedArguments = 0;
+ while (index < originalNumberOfArguments) {
+ ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(index);
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ RemovedArgumentInfo removedArgumentInfo = argumentInfo.asRemovedArgumentInfo();
+ addArgument(removedArgumentInfo.getType(), index++);
+ numberOfRemovedArguments++;
+ } else if (argumentInfo.isRewrittenTypeInfo()) {
+ RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+ int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments);
+ assert method.getArgumentType(newArgumentIndex) == rewrittenTypeInfo.getNewType();
+ addArgument(rewrittenTypeInfo.getOldType(), index++);
+ } else {
+ int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments);
+ addArgument(method.getArgumentType(newArgumentIndex), index++);
+ }
}
+
// Set up position state after adding arguments.
advanceNextPositionEntry();
}
@@ -252,8 +356,14 @@
}
}
}
- for (int i = 0; i < peekNextInstructionIndex(); ++i) {
- valueNumberGenerator.next();
+ if (!buildForInlining) {
+ // The decoding strategy will increment this on demand when built for inlining.
+ // Not incrementing for normal building results in nice order of instruction index and
+ // value number.
+ int lastValueIndex = getCurrentValueIndex();
+ for (int i = 0; i < lastValueIndex; ++i) {
+ valueNumberGenerator.next();
+ }
}
return new IRCode(
appView.options(),
@@ -262,7 +372,7 @@
blockList,
valueNumberGenerator,
basicBlockNumberGenerator,
- code.getMetadata(),
+ code.getMetadataForIR(),
method.getOrigin(),
new MutableMethodConversionOptions(appView.options()));
}
@@ -365,7 +475,7 @@
index, typeElement, code::getDebugLocalInfo);
Argument argument = new Argument(dest, index, type.isBooleanType());
assert currentBlock != null;
- assert currentPosition.isSyntheticPosition();
+ assert currentPosition.isSyntheticPosition() || buildForInlining;
argument.setPosition(currentPosition);
currentBlock.getInstructions().add(argument);
argument.setBlock(currentBlock);
@@ -512,7 +622,7 @@
public void onConstClass(DexType type, boolean ignoreCompatRules) {
Value dest =
getOutValueForNextInstruction(
- type.toTypeElement(appView, Nullability.definitelyNotNull()));
+ TypeElement.classClassType(appView, Nullability.definitelyNotNull()));
addInstruction(new ConstClass(dest, type, ignoreCompatRules));
}
@@ -727,8 +837,12 @@
@Override
public void onReturn(EV value) {
- addInstruction(new Return(getValue(value)));
- closeCurrentBlock();
+ if (protoChanges.hasBeenChangedToReturnVoid()) {
+ onReturnVoid();
+ } else {
+ addInstruction(new Return(getValue(value)));
+ closeCurrentBlock();
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index f6ca030..8c8e4c5 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.lightir.LirCode.TryCatchTable;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -60,6 +61,7 @@
private static final long DOUBLE_0 = Double.doubleToRawLongBits(0);
private static final long DOUBLE_1 = Double.doubleToRawLongBits(1);
+ private final boolean useDexEstimationStrategy;
private final DexItemFactory factory;
private final ByteArrayWriter byteWriter = new ByteArrayWriter();
private final LirWriter writer = new LirWriter(byteWriter);
@@ -140,8 +142,10 @@
}
}
- public LirBuilder(DexMethod method, LirEncodingStrategy<V, EV> strategy, DexItemFactory factory) {
- this.factory = factory;
+ public LirBuilder(
+ DexMethod method, LirEncodingStrategy<V, EV> strategy, InternalOptions options) {
+ useDexEstimationStrategy = options.isGeneratingDex();
+ factory = options.dexItemFactory();
constants = new Reference2IntOpenHashMap<>();
positionTable = new ArrayList<>();
this.strategy = strategy;
@@ -174,7 +178,9 @@
public LirBuilder<V, EV> setCurrentPosition(Position position) {
assert position != null;
- currentPosition = position;
+ if (!position.isNone()) {
+ currentPosition = position;
+ }
return this;
}
@@ -721,7 +727,8 @@
instructionCount,
tryCatchTable,
debugTable,
- strategy.getStrategyInfo());
+ strategy.getStrategyInfo(),
+ useDexEstimationStrategy);
}
private int getCmpOpcode(NumericType type, Cmp.Bias bias) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index f1c04ec..75a1aa3 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -3,20 +3,43 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.dex.code.CfOrDexInstruction;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ArgumentUse;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
-public class LirCode<EV> implements Iterable<LirInstructionView> {
+public class LirCode<EV> extends Code implements Iterable<LirInstructionView> {
public static class PositionEntry {
final int fromInstructionIndex;
@@ -72,7 +95,9 @@
private final LirStrategyInfo<EV> strategyInfo;
- private final IRMetadata metadata;
+ private final boolean useDexEstimationStrategy;
+
+ private final IRMetadata irMetadata;
/** Constant pool of items. */
private final DexItem[] constants;
@@ -95,13 +120,13 @@
private final DebugLocalInfoTable<EV> debugLocalInfoTable;
public static <V, EV> LirBuilder<V, EV> builder(
- DexMethod method, LirEncodingStrategy<V, EV> strategy, DexItemFactory factory) {
- return new LirBuilder<>(method, strategy, factory);
+ DexMethod method, LirEncodingStrategy<V, EV> strategy, InternalOptions options) {
+ return new LirBuilder<>(method, strategy, options);
}
/** Should be constructed using {@link LirBuilder}. */
LirCode(
- IRMetadata metadata,
+ IRMetadata irMetadata,
DexItem[] constants,
PositionEntry[] positions,
int argumentCount,
@@ -109,8 +134,9 @@
int instructionCount,
TryCatchTable tryCatchTable,
DebugLocalInfoTable<EV> debugLocalInfoTable,
- LirStrategyInfo<EV> strategyInfo) {
- this.metadata = metadata;
+ LirStrategyInfo<EV> strategyInfo,
+ boolean useDexEstimationStrategy) {
+ this.irMetadata = irMetadata;
this.constants = constants;
this.positionTable = positions;
this.argumentCount = argumentCount;
@@ -119,6 +145,24 @@
this.tryCatchTable = tryCatchTable;
this.debugLocalInfoTable = debugLocalInfoTable;
this.strategyInfo = strategyInfo;
+ this.useDexEstimationStrategy = useDexEstimationStrategy;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public LirCode<Integer> asLirCode() {
+ // TODO(b/225838009): Unchecked cast will be removed once the encoding strategy is definitive.
+ return (LirCode<Integer>) this;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ throw new Unreachable("LIR code should not be subject to hashing.");
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ throw new Unreachable("LIR code should not be subject to equality checks.");
}
public EV decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
@@ -143,8 +187,8 @@
return instructionCount;
}
- public IRMetadata getMetadata() {
- return metadata;
+ public IRMetadata getMetadataForIR() {
+ return irMetadata;
}
public DexItem getConstantItem(int index) {
@@ -172,12 +216,184 @@
}
@Override
+ public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
+ // Bytecode metadata is recomputed when finalizing via IR.
+ throw new Unreachable();
+ }
+
+ @Override
+ public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
+ // Bytecode metadata is recomputed when finalizing via IR.
+ throw new Unreachable();
+ }
+
+ @Override
public LirIterator iterator() {
return new LirIterator(new ByteArrayIterator(instructions));
}
@Override
+ public IRCode buildIR(
+ ProgramMethod method,
+ AppView<?> appView,
+ Origin origin,
+ MutableMethodConversionOptions conversionOptions) {
+ LirCode<Integer> typedLir = asLirCode();
+ return Lir2IRConverter.translate(
+ method,
+ typedLir,
+ LirStrategy.getDefaultStrategy().getDecodingStrategy(typedLir, null),
+ appView);
+ }
+
+ @Override
+ public IRCode buildInliningIR(
+ ProgramMethod context,
+ ProgramMethod method,
+ AppView<?> appView,
+ GraphLens codeLens,
+ NumberGenerator valueNumberGenerator,
+ Position callerPosition,
+ Origin origin,
+ RewrittenPrototypeDescription protoChanges) {
+ assert valueNumberGenerator != null;
+ assert callerPosition != null;
+ assert protoChanges != null;
+ LirCode<Integer> typedLir = asLirCode();
+ IRCode irCode =
+ Lir2IRConverter.translate(
+ method,
+ typedLir,
+ LirStrategy.getDefaultStrategy().getDecodingStrategy(typedLir, valueNumberGenerator),
+ appView,
+ valueNumberGenerator,
+ callerPosition,
+ protoChanges,
+ appView.graphLens().getOriginalMethodSignature(method.getReference()));
+ // TODO(b/225838009): Should we keep track of which code objects need to be narrowed?
+ // In particular, the encoding of phis does not maintain interfaces.
+ new TypeAnalysis(appView).narrowing(irCode);
+ return irCode;
+ }
+
+ @Override
+ public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+ assert registry.getTraversalContinuation().shouldContinue();
+ LirUseRegistryCallback<EV> registryCallbacks = new LirUseRegistryCallback<>(this, registry);
+ for (LirInstructionView view : this) {
+ registryCallbacks.onInstructionView(view);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ if (tryCatchTable != null) {
+ for (CatchHandlers<Integer> handler : tryCatchTable.tryCatchHandlers.values()) {
+ for (DexType guard : handler.getGuards()) {
+ registry.registerExceptionGuard(guard);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Int2ReferenceMap<DebugLocalInfo> collectParameterInfo(
+ DexEncodedMethod encodedMethod, AppView<?> appView) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public void registerArgumentReferences(DexEncodedMethod method, ArgumentUse registry) {
+ throw new Unimplemented();
+ }
+
+ @Override
public String toString() {
return new LirPrinter<>(this).prettyPrint();
}
+
+ @Override
+ public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) {
+ // TODO(b/225838009): Add retracing to printer.
+ return toString();
+ }
+
+ @Override
+ public int estimatedDexCodeSizeUpperBoundInBytes() {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public int estimatedSizeForInlining() {
+ if (useDexEstimationStrategy) {
+ LirSizeEstimation<EV> estimation = new LirSizeEstimation<>(this);
+ for (LirInstructionView view : this) {
+ estimation.onInstructionView(view);
+ }
+ return estimation.getSizeEstimate();
+ } else {
+ // TODO(b/225838009): Currently the size estimation for CF has size one for each instruction
+ // (even switches!) and ignores stack instructions, thus loads to arguments are not included.
+ // The result is a much smaller estimate than for DEX. Once LIR is in place we should use the
+ // same estimate for both.
+ return instructionCount;
+ }
+ }
+
+ @Override
+ public boolean estimatedSizeForInliningAtMost(int threshold) {
+ if (useDexEstimationStrategy) {
+ LirSizeEstimation<EV> estimation = new LirSizeEstimation<>(this);
+ for (LirInstructionView view : this) {
+ estimation.onInstructionView(view);
+ if (estimation.getSizeEstimate() > threshold) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return estimatedSizeForInlining() <= threshold;
+ }
+ }
+
+ @Override
+ public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ for (LirInstructionView view : this) {
+ int opcode = view.getOpcode();
+ if (opcode != LirOpcodes.RETURN && opcode != LirOpcodes.DEBUGPOS) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean hasMonitorInstructions() {
+ for (LirInstructionView view : this) {
+ int opcode = view.getOpcode();
+ if (opcode == LirOpcodes.MONITORENTER || opcode == LirOpcodes.MONITOREXIT) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void forEachPosition(Consumer<Position> positionConsumer) {
+ for (PositionEntry entry : positionTable) {
+ positionConsumer.accept(entry.position);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
index 0b0a51d..f0eaeef 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Phi;
import java.util.function.Function;
import java.util.function.IntFunction;
@@ -13,6 +14,16 @@
/** Abstraction for how to decode SSA values (and basic blocks) when reading LIR. */
public abstract class LirDecodingStrategy<V, EV> {
+ private final NumberGenerator valueNumberGenerator;
+
+ public LirDecodingStrategy(NumberGenerator valueNumberGenerator) {
+ this.valueNumberGenerator = valueNumberGenerator;
+ }
+
+ public final int getValueNumber(int encodedValueIndex) {
+ return valueNumberGenerator == null ? encodedValueIndex : valueNumberGenerator.next();
+ }
+
public abstract V getValue(EV encodedValue, LirStrategyInfo<EV> strategyInfo);
public abstract V getValueDefinitionForInstructionIndex(
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index 6803d0b..fc1ccc6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -151,7 +151,7 @@
// int JSR = 168;
// int RET = 169;
int TABLESWITCH = 170;
- int LOOKUPSWITCH = 171;
+ // int LOOKUPSWITCH = 171;
// int IRETURN = 172;
// int LRETURN = 173;
// int FRETURN = 174;
@@ -447,8 +447,7 @@
// case RET: return "RET";
case TABLESWITCH:
return "TABLESWITCH";
- case LOOKUPSWITCH:
- return "LOOKUPSWITCH";
+ // case LOOKUPSWITCH:
case ARETURN:
return "ARETURN";
case RETURN:
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index 444df4e..d979ada 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -448,13 +448,21 @@
onFieldInstruction(field);
}
- public abstract void onInstanceGet(DexField field, EV object);
+ public void onInstanceGet(DexField field, EV object) {
+ onFieldInstruction(field);
+ }
- public abstract void onInstancePut(DexField field, EV object, EV value);
+ public void onInstancePut(DexField field, EV object, EV value) {
+ onFieldInstruction(field);
+ }
- public abstract void onNewArrayEmpty(DexType type, EV size);
+ public void onNewArrayEmpty(DexType type, EV size) {
+ onInstruction();
+ }
- public abstract void onThrow(EV exception);
+ public void onThrow(EV exception) {
+ onInstruction();
+ }
public void onReturnVoid() {
onInstruction();
@@ -493,9 +501,17 @@
onInstruction();
}
- public abstract void onMonitorEnter(EV value);
+ public void onMonitorInstruction(EV value) {
+ onInstruction();
+ }
- public abstract void onMonitorExit(EV value);
+ public void onMonitorEnter(EV value) {
+ onMonitorInstruction(value);
+ }
+
+ public void onMonitorExit(EV value) {
+ onMonitorInstruction(value);
+ }
public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
onInstruction();
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index 96d9cef..cfe1a08 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
import com.android.tools.r8.ir.code.IfType;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
@@ -61,7 +62,7 @@
}
private String fmtValueIndex(EV valueIndex) {
- return valueIndex.toString();
+ return "v" + valueIndex.toString();
}
private String fmtInsnIndex(int instructionIndex) {
@@ -86,6 +87,22 @@
advanceToNextValueIndex();
}
code.forEach(this::onInstructionView);
+ if (code.getTryCatchTable() != null) {
+ builder.append("try-catch-handlers:\n");
+ code.getTryCatchTable()
+ .tryCatchHandlers
+ .forEach(
+ (index, handlers) -> {
+ builder.append(index).append(":\n");
+ for (CatchHandler<Integer> handler : handlers) {
+ builder
+ .append(handler.getGuard())
+ .append(" -> ")
+ .append(handler.getTarget())
+ .append('\n');
+ }
+ });
+ }
return builder.toString();
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
new file mode 100644
index 0000000..eaa93d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
@@ -0,0 +1,339 @@
+// Copyright (c) 2023, 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.lightir;
+
+import com.android.tools.r8.dex.code.DexAget;
+import com.android.tools.r8.dex.code.DexAput;
+import com.android.tools.r8.dex.code.DexArrayLength;
+import com.android.tools.r8.dex.code.DexBase1Format;
+import com.android.tools.r8.dex.code.DexBase2Format;
+import com.android.tools.r8.dex.code.DexBase3Format;
+import com.android.tools.r8.dex.code.DexCheckCast;
+import com.android.tools.r8.dex.code.DexConst16;
+import com.android.tools.r8.dex.code.DexConst4;
+import com.android.tools.r8.dex.code.DexConstClass;
+import com.android.tools.r8.dex.code.DexConstString;
+import com.android.tools.r8.dex.code.DexConstWide16;
+import com.android.tools.r8.dex.code.DexFillArrayData;
+import com.android.tools.r8.dex.code.DexFillArrayDataPayload;
+import com.android.tools.r8.dex.code.DexFilledNewArray;
+import com.android.tools.r8.dex.code.DexGoto;
+import com.android.tools.r8.dex.code.DexInstanceOf;
+import com.android.tools.r8.dex.code.DexInvokeCustom;
+import com.android.tools.r8.dex.code.DexMonitorEnter;
+import com.android.tools.r8.dex.code.DexMonitorExit;
+import com.android.tools.r8.dex.code.DexMove;
+import com.android.tools.r8.dex.code.DexMoveException;
+import com.android.tools.r8.dex.code.DexNewArray;
+import com.android.tools.r8.dex.code.DexNewInstance;
+import com.android.tools.r8.dex.code.DexNotInt;
+import com.android.tools.r8.dex.code.DexNotLong;
+import com.android.tools.r8.dex.code.DexPackedSwitch;
+import com.android.tools.r8.dex.code.DexPackedSwitchPayload;
+import com.android.tools.r8.dex.code.DexSget;
+import com.android.tools.r8.dex.code.DexThrow;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+
+public class LirSizeEstimation<EV> extends LirParsedInstructionCallback<EV> implements LirOpcodes {
+
+ private int sizeEstimate = 0;
+
+ LirSizeEstimation(LirCode<EV> code) {
+ super(code);
+ }
+
+ @Override
+ public int getCurrentValueIndex() {
+ // We don't use value information.
+ return 0;
+ }
+
+ public int getSizeEstimate() {
+ return sizeEstimate;
+ }
+
+ /**
+ * Most size information can be found just using opcode.
+ *
+ * <p>We overwrite the base view callback and only in the few payload instruction cases do we make
+ * use of the parsed-instruction callbacks.
+ */
+ @Override
+ public void onInstructionView(LirInstructionView view) {
+ sizeEstimate += instructionSize(view.getOpcode(), view);
+ }
+
+ @Override
+ public void onIntSwitch(EV unusedValue, IntSwitchPayload payload) {
+ sizeEstimate +=
+ DexPackedSwitch.SIZE
+ + DexPackedSwitchPayload.SIZE
+ + (2 * payload.keys.length)
+ + (2 * payload.targets.length);
+ }
+
+ @Override
+ public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV unusedSrc) {
+ sizeEstimate += DexFillArrayData.SIZE + DexFillArrayDataPayload.SIZE + 4 + data.length;
+ }
+
+ private int instructionSize(int opcode, LirInstructionView view) {
+ switch (opcode) {
+ case TABLESWITCH:
+ case NEWARRAYFILLEDDATA:
+ // The payload instructions use the "parsed callback" to compute the payloads.
+ super.onInstructionView(view);
+ // The full size is added by the callbacks so return zero here.
+ return 0;
+
+ case ACONST_NULL:
+ case ICONST_M1:
+ case ICONST_0:
+ case ICONST_1:
+ case ICONST_2:
+ case ICONST_3:
+ case ICONST_4:
+ case ICONST_5:
+ return DexConst4.SIZE;
+
+ case LCONST_0:
+ case LCONST_1:
+ case FCONST_0:
+ case FCONST_1:
+ case FCONST_2:
+ case DCONST_0:
+ case DCONST_1:
+ return DexConstWide16.SIZE;
+
+ case LDC:
+ // Most of the const loads are the same size (2).
+ return DexConstString.SIZE;
+
+ case IALOAD:
+ case LALOAD:
+ case FALOAD:
+ case DALOAD:
+ case AALOAD:
+ case BALOAD:
+ case CALOAD:
+ case SALOAD:
+ // The loads are all size 2.
+ return DexAget.SIZE;
+
+ case IASTORE:
+ case LASTORE:
+ case FASTORE:
+ case DASTORE:
+ case AASTORE:
+ case BASTORE:
+ case CASTORE:
+ case SASTORE:
+ // The loads are all size 2.
+ return DexAput.SIZE;
+
+ case IADD:
+ case LADD:
+ case FADD:
+ case DADD:
+ case ISUB:
+ case LSUB:
+ case FSUB:
+ case DSUB:
+ case IMUL:
+ case LMUL:
+ case FMUL:
+ case DMUL:
+ case IDIV:
+ case LDIV:
+ case FDIV:
+ case DDIV:
+ case IREM:
+ case LREM:
+ case FREM:
+ case DREM:
+ // The binary ops are all size 2.
+ return DexBase2Format.SIZE;
+
+ case INEG:
+ case LNEG:
+ case FNEG:
+ case DNEG:
+ // The negs are all size 1.
+ return DexBase1Format.SIZE;
+
+ case ISHL:
+ case LSHL:
+ case ISHR:
+ case LSHR:
+ case IUSHR:
+ case LUSHR:
+ case IAND:
+ case LAND:
+ case IOR:
+ case LOR:
+ case IXOR:
+ case LXOR:
+ // The binary ops are all size 2.
+ return DexBase2Format.SIZE;
+
+ case I2L:
+ case I2F:
+ case I2D:
+ case L2I:
+ case L2F:
+ case L2D:
+ case F2I:
+ case F2L:
+ case F2D:
+ case D2I:
+ case D2L:
+ case D2F:
+ case I2B:
+ case I2C:
+ case I2S:
+ // Number conversions are all size 1.
+ return DexBase1Format.SIZE;
+
+ case LCMP:
+ case FCMPL:
+ case FCMPG:
+ case DCMPL:
+ case DCMPG:
+ return DexBase2Format.SIZE;
+
+ case IFEQ:
+ case IFNE:
+ case IFLT:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ case IF_ICMPEQ:
+ case IF_ICMPNE:
+ case IF_ICMPLT:
+ case IF_ICMPGE:
+ case IF_ICMPGT:
+ case IF_ICMPLE:
+ case IF_ACMPEQ:
+ case IF_ACMPNE:
+ return DexBase2Format.SIZE;
+
+ case GOTO:
+ return DexGoto.SIZE;
+
+ case ARETURN:
+ case RETURN:
+ return DexBase1Format.SIZE;
+
+ case GETSTATIC:
+ case PUTSTATIC:
+ case GETFIELD:
+ case PUTFIELD:
+ return DexBase2Format.SIZE;
+
+ case INVOKEVIRTUAL:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ case INVOKEINTERFACE:
+ return DexBase3Format.SIZE;
+
+ case INVOKEDYNAMIC:
+ return DexInvokeCustom.SIZE;
+
+ case NEW:
+ return DexNewInstance.SIZE;
+ case NEWARRAY:
+ return DexNewArray.SIZE;
+ case ARRAYLENGTH:
+ return DexArrayLength.SIZE;
+ case ATHROW:
+ return DexThrow.SIZE;
+ case CHECKCAST:
+ return DexCheckCast.SIZE;
+ case INSTANCEOF:
+ return DexInstanceOf.SIZE;
+ case MONITORENTER:
+ return DexMonitorEnter.SIZE;
+ case MONITOREXIT:
+ return DexMonitorExit.SIZE;
+ case MULTIANEWARRAY:
+ return DexFilledNewArray.SIZE;
+
+ case IFNULL:
+ case IFNONNULL:
+ return DexBase1Format.SIZE;
+
+ // Non-CF instructions.
+ case ICONST:
+ case LCONST:
+ case FCONST:
+ case DCONST:
+ return DexConst16.SIZE;
+
+ case INVOKESTATIC_ITF:
+ case INVOKEDIRECT:
+ case INVOKEDIRECT_ITF:
+ case INVOKESUPER:
+ case INVOKESUPER_ITF:
+ return DexBase3Format.SIZE;
+
+ case DEBUGPOS:
+ // Often debug positions will be associated with instructions so assume size 0.
+ return 0;
+
+ case PHI:
+ // Assume a move per phi.
+ return DexMove.SIZE;
+
+ case FALLTHROUGH:
+ // Hopefully fallthrough points will not materialize as instructions.
+ return 0;
+
+ case MOVEEXCEPTION:
+ return DexMoveException.SIZE;
+
+ case DEBUGLOCALWRITE:
+ return DexMove.SIZE;
+
+ case INVOKENEWARRAY:
+ return DexFilledNewArray.SIZE;
+
+ case ITEMBASEDCONSTSTRING:
+ return DexConstString.SIZE;
+
+ case NEWUNBOXEDENUMINSTANCE:
+ return DexConst16.SIZE;
+
+ case INOT:
+ return DexNotInt.SIZE;
+ case LNOT:
+ return DexNotLong.SIZE;
+
+ case DEBUGLOCALREAD:
+ // These reads do not materialize after register allocation.
+ return 0;
+
+ case INITCLASS:
+ return DexSget.SIZE;
+
+ case INVOKEPOLYMORPHIC:
+ return DexBase3Format.SIZE;
+
+ case RECORDFIELDVALUES:
+ // Rewritten to new-array in DEX.
+ return DexNewArray.SIZE;
+
+ case CHECKCAST_SAFE:
+ case CHECKCAST_IGNORE_COMPAT:
+ return DexCheckCast.SIZE;
+
+ case CONSTCLASS_IGNORE_COMPAT:
+ return DexConstClass.SIZE;
+
+ default:
+ throw new Unreachable("Unexpected LIR opcode: " + opcode);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
index 108586f..07ac579 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
@@ -29,9 +30,15 @@
* the possible encoding for phi and non-phi values.
*/
public abstract class LirStrategy<V, EV> {
+
+ public static LirStrategy<Value, Integer> getDefaultStrategy() {
+ return new PhiInInstructionsStrategy();
+ }
+
public abstract LirEncodingStrategy<V, EV> getEncodingStrategy();
- public abstract LirDecodingStrategy<V, EV> getDecodingStrategy(LirCode<EV> code);
+ public abstract LirDecodingStrategy<V, EV> getDecodingStrategy(
+ LirCode<EV> code, NumberGenerator valueNumberGenerator);
/**
* Encoding of a value with a phi-bit.
@@ -139,8 +146,9 @@
}
@Override
- public LirDecodingStrategy<Value, PhiOrValue> getDecodingStrategy(LirCode<PhiOrValue> code) {
- return new DecodingStrategy(code);
+ public LirDecodingStrategy<Value, PhiOrValue> getDecodingStrategy(
+ LirCode<PhiOrValue> code, NumberGenerator valueNumberGenerator) {
+ return new DecodingStrategy(code, valueNumberGenerator);
}
private static class StrategyInfo extends LirStrategyInfo<PhiOrValue> {
@@ -234,7 +242,8 @@
private final Value[] values;
private final int firstPhiValueIndex;
- DecodingStrategy(LirCode<PhiOrValue> code) {
+ DecodingStrategy(LirCode<PhiOrValue> code, NumberGenerator valueNumberGenerator) {
+ super(valueNumberGenerator);
values = new Value[code.getArgumentCount() + code.getInstructionCount()];
int phiValueIndex = -1;
for (LirInstructionView view : code) {
@@ -270,7 +279,7 @@
int index = decode(encodedValue, strategyInfo);
Value value = values[index];
if (value == null) {
- value = new Value(index, TypeElement.getBottom(), null);
+ value = new Value(getValueNumber(index), TypeElement.getBottom(), null);
values[index] = value;
}
return value;
@@ -284,7 +293,7 @@
DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
Value value = values[index];
if (value == null) {
- value = new Value(index, type, localInfo);
+ value = new Value(getValueNumber(index), type, localInfo);
values[index] = value;
} else {
value.setType(type);
@@ -306,7 +315,8 @@
PhiOrValue encodedValue = getEncodedPhiForAbsoluteValueIndex(valueIndex, strategyInfo);
BasicBlock block = getBlock.apply(encodedValue.getBlockIndex());
DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
- Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+ Phi phi =
+ new Phi(getValueNumber(valueIndex), block, type, localInfo, RegisterReadType.NORMAL);
Value value = values[valueIndex];
if (value != null) {
// A fake ssa value has already been created, replace the users by the actual phi.
diff --git a/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java b/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java
new file mode 100644
index 0000000..67f7a4e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2023, 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.lightir;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
+import java.util.List;
+
+public class LirUseRegistryCallback<EV> extends LirParsedInstructionCallback<EV> {
+
+ private final UseRegistry registry;
+
+ public LirUseRegistryCallback(LirCode<EV> code, UseRegistry registry) {
+ super(code);
+ this.registry = registry;
+ }
+
+ @Override
+ public int getCurrentValueIndex() {
+ // The registry of instructions does not require knowledge of value indexes.
+ return 0;
+ }
+
+ @Override
+ public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
+ registry.registerCheckCast(type, ignoreCompatRules);
+ }
+
+ @Override
+ public void onSafeCheckCast(DexType type, EV value) {
+ registry.registerSafeCheckCast(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onConstClass(DexType type, boolean ignoreCompatRules) {
+ registry.registerConstClass(type, null, ignoreCompatRules);
+ }
+
+ @Override
+ public void onConstMethodHandle(DexMethodHandle methodHandle) {
+ registry.registerMethodHandle(methodHandle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+ }
+
+ @Override
+ public void onConstMethodType(DexProto methodType) {
+ registry.registerProto(methodType);
+ }
+
+ @Override
+ public void onDexItemBasedConstString(
+ DexReference item, NameComputationInfo<?> nameComputationInfo) {
+ if (nameComputationInfo.needsToRegisterReference()) {
+ assert item.isDexType();
+ registry.registerTypeReference(item.asDexType());
+ }
+ }
+
+ @Override
+ public void onInitClass(DexType clazz) {
+ registry.registerInitClass(clazz);
+ }
+
+ @Override
+ public void onInstanceGet(DexField field, EV object) {
+ registry.registerInstanceFieldRead(field);
+ }
+
+ @Override
+ public void onInstancePut(DexField field, EV object, EV value) {
+ registry.registerInstanceFieldWrite(field);
+ }
+
+ @Override
+ public void onStaticGet(DexField field) {
+ registry.registerStaticFieldRead(field);
+ }
+
+ @Override
+ public void onStaticPut(DexField field, EV value) {
+ registry.registerStaticFieldWrite(field);
+ }
+
+ @Override
+ public void onInstanceOf(DexType type, EV value) {
+ registry.registerInstanceOf(type);
+ }
+
+ @Override
+ public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
+ registry.registerCallSite(callSite);
+ }
+
+ @Override
+ public void onInvokeDirect(DexMethod method, List<EV> arguments, boolean isInterface) {
+ registry.registerInvokeDirect(method);
+ }
+
+ @Override
+ public void onInvokeInterface(DexMethod method, List<EV> arguments) {
+ registry.registerInvokeInterface(method);
+ }
+
+ @Override
+ public void onInvokeStatic(DexMethod method, List<EV> arguments, boolean isInterface) {
+ registry.registerInvokeStatic(method);
+ }
+
+ @Override
+ public void onInvokeSuper(DexMethod method, List<EV> arguments, boolean isInterface) {
+ registry.registerInvokeSuper(method);
+ }
+
+ @Override
+ public void onInvokeVirtual(DexMethod method, List<EV> arguments) {
+ registry.registerInvokeVirtual(method);
+ }
+
+ @Override
+ public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
+ registry.registerTypeReference(type);
+ }
+
+ @Override
+ public void onInvokeNewArray(DexType type, List<EV> arguments) {
+ registry.registerTypeReference(type);
+ }
+
+ @Override
+ public void onNewArrayEmpty(DexType type, EV size) {
+ registry.registerTypeReference(type);
+ }
+
+ @Override
+ public void onNewInstance(DexType clazz) {
+ registry.registerNewInstance(clazz);
+ }
+
+ @Override
+ public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
+ registry.registerNewUnboxedEnumInstance(type);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
index 0428126..6e9366f 100644
--- a/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
@@ -23,8 +24,9 @@
}
@Override
- public LirDecodingStrategy<Value, Integer> getDecodingStrategy(LirCode<Integer> code) {
- return new DecodingStrategy(code);
+ public LirDecodingStrategy<Value, Integer> getDecodingStrategy(
+ LirCode<Integer> code, NumberGenerator valueNumberGenerator) {
+ return new DecodingStrategy(code, valueNumberGenerator);
}
private static class EncodingStrategy extends LirEncodingStrategy<Value, Integer> {
@@ -94,7 +96,8 @@
private final Value[] values;
- DecodingStrategy(LirCode<Integer> code) {
+ DecodingStrategy(LirCode<Integer> code, NumberGenerator valueNumberGenerator) {
+ super(valueNumberGenerator);
values = new Value[code.getArgumentCount() + code.getInstructionCount()];
}
@@ -103,7 +106,7 @@
int index = encodedValue;
Value value = values[index];
if (value == null) {
- value = new Value(index, TypeElement.getBottom(), null);
+ value = new Value(getValueNumber(index), TypeElement.getBottom(), null);
values[index] = value;
}
return value;
@@ -115,7 +118,7 @@
DebugLocalInfo localInfo = getLocalInfo.apply(index);
Value value = values[index];
if (value == null) {
- value = new Value(index, type, localInfo);
+ value = new Value(getValueNumber(index), type, localInfo);
values[index] = value;
} else {
value.setType(type);
@@ -138,7 +141,8 @@
LirStrategyInfo<Integer> strategyInfo) {
BasicBlock block = getBlock.apply(valueIndex);
DebugLocalInfo localInfo = getLocalInfo.apply(valueIndex);
- Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+ Phi phi =
+ new Phi(getValueNumber(valueIndex), block, type, localInfo, RegisterReadType.NORMAL);
Value value = values[valueIndex];
if (value != null) {
// A fake ssa value has already been created, replace the users by the actual phi.
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 6259f3a..9afb2e9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2077,10 +2077,24 @@
public static class TestingOptions {
+ public boolean roundtripThroughLir = false;
+ private boolean useLir = false;
+
+ public void enableLir() {
+ useLir = true;
+ }
+
+ public void disableLir() {
+ useLir = false;
+ }
+
+ public boolean canUseLir(AppView<?> appView) {
+ return useLir && appView.enableWholeProgramOptimizations();
+ }
+
// If false, use the desugared library implementation when desugared library is enabled.
public boolean alwaysBackportListSetMapMethods = true;
public boolean neverReuseCfLocalRegisters = false;
- public boolean roundtripThroughLir = false;
public boolean checkReceiverAlwaysNullInCallSiteOptimization = true;
public boolean forceInlineAPIConversions = false;
public boolean ignoreValueNumbering = false;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 22f879d..fa1d579 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -132,10 +132,11 @@
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+ .withOptionConsumer(o -> o.testing.enableLir())
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 1, "lambdadesugaring"))
.run();
}
@@ -172,9 +173,10 @@
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
+ .withOptionConsumer(opts -> opts.testing.enableLir())
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 1, "lambdadesugaring"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java b/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
index f0bc1e5..173efa1 100644
--- a/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
+++ b/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
@@ -68,7 +68,7 @@
}
private void inspect(CodeInspector inspector) {
- assertEquals(parameters.isCfRuntime() ? 1 : 2, inspector.allClasses().size());
+ assertEquals(1, inspector.allClasses().size());
}
@Test
@@ -77,6 +77,7 @@
.addProgramFiles(getProgramFiles(target))
.setMinApi(parameters)
.addKeepMainRule(MAIN_CLASS)
+ .addOptionsModification(o -> o.testing.enableLir())
.run(parameters.getRuntime(), MAIN_CLASS)
.inspect(this::inspect)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerPhiDirectUserAfterInlineTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerPhiDirectUserAfterInlineTest.java
index 84f5003..ec1f304 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerPhiDirectUserAfterInlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerPhiDirectUserAfterInlineTest.java
@@ -172,14 +172,14 @@
assertNotNull(argument);
Value argumentValue = argument.outValue();
- BasicBlock block1 = irCode.blocks.get(1);
+ BasicBlock block1 = irCode.blocks.stream().filter(b -> b.getNumber() == 1).findFirst().get();
assertTrue(block1.exit().isIf());
- BasicBlock block3 = irCode.blocks.get(3);
+ BasicBlock block3 = irCode.blocks.stream().filter(b -> b.getNumber() == 3).findFirst().get();
assertTrue(block1.getSuccessors().contains(block3));
assertTrue(block3.exit().isGoto());
- BasicBlock block4 = irCode.blocks.get(4);
+ BasicBlock block4 = irCode.blocks.stream().filter(b -> b.getNumber() == 4).findFirst().get();
assertSame(block3.getUniqueNormalSuccessor(), block4);
Phi firstPhi =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 5ece012..483ed33 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -12,7 +12,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
@@ -42,7 +41,6 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.Sets;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Assume;
@@ -312,6 +310,8 @@
.allowAccessModification()
.enableInliningAnnotations()
.addDontObfuscate()
+ // Using LIR changes the inlining heuristics so enable it consistently.
+ .addOptionsModification(o -> o.testing.enableLir())
.setMinApi(parameters)
.run(parameters.getRuntime(), main)
.assertSuccessWithOutput(javaOutput);
@@ -324,16 +324,14 @@
.filter(FoundClassSubject::isSynthesizedJavaLambdaClass)
.map(FoundClassSubject::getFinalName)
.collect(Collectors.toList());
+ assertEquals(Collections.emptyList(), synthesizedJavaLambdaClasses);
- // TODO(b/120814598): Should only be "java.lang.StringBuilder".
assertEquals(
- new HashSet<>(synthesizedJavaLambdaClasses),
+ Collections.singleton("java.lang.StringBuilder"),
collectTypes(clazz.uniqueMethodWithOriginalName("testStatelessLambda")));
- assertTrue(
- inspector.allClasses().stream().anyMatch(ClassSubject::isSynthesizedJavaLambdaClass));
assertEquals(
- Sets.newHashSet("java.lang.StringBuilder"),
+ Collections.singleton("java.lang.StringBuilder"),
collectTypes(clazz.uniqueMethodWithOriginalName("testStatefulLambda")));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
index 8f8541c..37e6c57 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -39,7 +39,7 @@
HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
- .addOptionsModification(o -> o.testing.roundtripThroughLir = true)
+ .addOptionsModification(o -> o.testing.enableLir())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("42")
.inspect(inspector -> assertThat(inspector.clazz(anim.class), isAbsent()));
diff --git a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
index abd980d..909c083 100644
--- a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
@@ -12,12 +12,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.InternalOptions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -74,10 +74,13 @@
@Test
public void test() {
- DexItemFactory factory = new DexItemFactory();
- DexMethod method = factory.createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V"));
+ InternalOptions options = new InternalOptions();
+ DexMethod method =
+ options
+ .dexItemFactory()
+ .createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V"));
LirCode<?> code =
- LirCode.builder(method, new ThrowingStrategy(), factory)
+ LirCode.builder(method, new ThrowingStrategy(), options)
.setMetadata(IRMetadata.unknown())
.addConstNull()
.addConstInt(42)
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
index c4fa84b..a39c770 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.regress;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
@@ -51,6 +50,7 @@
.addKeepMainRule(TestClass.class)
.addInnerClasses(Regress160394262Test.class)
.setMinApi(parameters)
+ .addOptionsModification(o -> o.testing.enableLir())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(this::checkJoinerIsClassInlined);
@@ -58,14 +58,7 @@
private void checkJoinerIsClassInlined(CodeInspector inspector) {
assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), isAbsent());
- // TODO(b/160640028): Joiner should be class inlined.
- // When line info tables are kept we appear to successfully inline Joiner. Reason unknown.
- if (parameters.isCfRuntime()
- || parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
- assertThat(inspector.clazz(Joiner.class), isPresent());
- } else {
- assertThat(inspector.clazz(Joiner.class), isAbsent());
- }
+ assertThat(inspector.clazz(Joiner.class), isAbsent());
}
static class TestClass {